level 02 / assertions
Assertions
expect() in depth — visible/hidden, text, URL, count, attribute, and soft assertions that don't abort the test on first failure.
practice site: SauceDemo
How expect() works in Playwright
Playwright’s expect() is not Jest’s expect. It is locator-aware and
retries automatically until the assertion passes or the timeout expires.
// This retries for up to 5s (configurable) checking visibility
await expect(page.getByText('Products')).toBeVisible();
// NOT like Jest: does not throw immediately on failure
// It polls the DOM until the condition is met
This means you can write assertions immediately after an action without manual waits — the assertion’s retry covers the async UI update.
Visibility
// Element is visible (in DOM + visible in viewport)
await expect(page.getByRole('button', { name: 'Checkout' })).toBeVisible();
// Element is hidden or detached from DOM
await expect(page.getByTestId('loading-spinner')).toBeHidden();
// Element is in DOM (even if hidden)
await expect(page.locator('.modal')).toBeAttached();
// Element is not in DOM at all
await expect(page.locator('.old-element')).not.toBeAttached();
Text content
// Contains this text (substring match)
await expect(page.getByRole('heading')).toContainText('Welcome');
// Exact text match
await expect(page.getByRole('heading')).toHaveText('Welcome back, Alice');
// Match with regex
await expect(page.getByTestId('item-count')).toHaveText(/\d+ items?/);
// Inner text (all text nodes concatenated)
const text = await page.getByRole('heading').innerText();
expect(text).toBe('Welcome back, Alice'); // synchronous — value already retrieved
URL and navigation
// Current URL matches string exactly
await expect(page).toHaveURL('https://saucedemo.com/inventory.html');
// URL matches regex
await expect(page).toHaveURL(/inventory/);
// Page title
await expect(page).toHaveTitle('Swag Labs');
await expect(page).toHaveTitle(/Swag/);
Count
// Exactly N elements match the locator
await expect(page.locator('.inventory_item')).toHaveCount(6);
// At least one match
await expect(page.locator('.notification')).not.toHaveCount(0);
Attributes and properties
// Element has attribute with value
await expect(page.getByRole('textbox')).toHaveAttribute('placeholder', 'Username');
await expect(page.getByRole('textbox')).toHaveAttribute('disabled', '');
// CSS class
await expect(page.getByRole('button', { name: 'Selected' })).toHaveClass(/selected/);
// Input value
await expect(page.getByLabel('Username')).toHaveValue('standard_user');
// Checkbox state
await expect(page.getByRole('checkbox', { name: 'Remember me' })).toBeChecked();
Negation
Every assertion has a .not counterpart:
await expect(page.getByText('Error')).not.toBeVisible();
await expect(page.locator('.spinner')).not.toBeAttached();
await expect(page).not.toHaveURL(/login/);
Soft assertions
Soft assertions do not abort the test on failure. All soft assertions are collected and reported together at the end of the test.
test('checkout page summary', async ({ page }) => {
await page.goto('/checkout-step-two.html');
// These collect failures but don't stop the test
await expect.soft(page.getByTestId('subtotal')).toHaveText('Item total: $29.99');
await expect.soft(page.getByTestId('tax')).toHaveText('Tax: $2.40');
await expect.soft(page.getByTestId('total')).toHaveText('Total: $32.39');
// This hard assertion runs regardless of the soft failures above
await expect(page.getByRole('button', { name: 'Finish' })).toBeVisible();
});
// If soft assertions fail, the test is marked failed after the test body completes
Reference
expect(locator).toBeVisible()
Element visible in viewport
expect(locator).toBeHidden()
Element hidden or not in DOM
expect(locator).toHaveText(str)
Exact text match
expect(locator).toContainText(str)
Substring match
expect(locator).toHaveValue(str)
Input current value
expect(locator).toHaveCount(n)
Number of matching elements
expect(locator).toHaveAttribute(k, v)
HTML attribute value
expect(locator).toBeChecked()
Checkbox/radio is checked
expect(page).toHaveURL(url)
Current page URL
expect(page).toHaveTitle(str)
Page text
expect.soft(...)
Non-aborting assertion