This is the meat of Playwright assertions. Locator assertions check the state of individual elements -- visibility, text, values, attributes, CSS, count. All auto-retrying. You will use these in every single test you write. Let me walk through each one.
import { test, expect } from '@playwright/test';
test('visibility assertions', async ({ page }) => {
await page.goto('/banking');
// Element IS visible on screen
await expect(page.getByRole('heading', { name: 'Login' })).toBeVisible();
// Element is hidden (display:none, visibility:hidden, or not in DOM)
await expect(page.getByRole('alert')).toBeHidden();
// Element exists in DOM but may or may not be visible
await expect(page.getByTestId('modal-backdrop')).toBeAttached();
// Element does NOT exist in DOM at all
await expect(page.getByTestId('error-popup')).not.toBeAttached();
});import { test, expect } from '@playwright/test';
test('element state assertions', async ({ page }) => {
await page.goto('/banking');
// Button is enabled and clickable
await expect(page.getByRole('button', { name: 'Login' })).toBeEnabled();
// Button is disabled (cannot be clicked)
await expect(page.getByRole('button', { name: 'Submit' })).toBeDisabled();
// Checkbox is checked
await page.getByRole('checkbox', { name: 'Remember me' }).check();
await expect(page.getByRole('checkbox', { name: 'Remember me' })).toBeChecked();
// Checkbox is NOT checked
await expect(page.getByRole('checkbox', { name: 'Newsletter' })).not.toBeChecked();
// Radio button is selected
await page.getByRole('radio', { name: 'Savings' }).check();
await expect(page.getByRole('radio', { name: 'Savings' })).toBeChecked();
});import { test, expect } from '@playwright/test';
test('input value assertions', async ({ page }) => {
await page.goto('/banking');
// After filling an input, verify its value
await page.getByLabel('Username').fill('john_doe');
await expect(page.getByLabel('Username')).toHaveValue('john_doe');
// Regex match on value
await expect(page.getByLabel('Email')).toHaveValue(/@/);
// Empty input
await expect(page.getByLabel('Password')).toHaveValue('');
// Select dropdown value
await page.getByLabel('Account Type').selectOption('savings');
await expect(page.getByLabel('Account Type')).toHaveValue('savings');
});import { test, expect } from '@playwright/test';
test('attribute and CSS assertions', async ({ page }) => {
await page.goto('/shopping');
// Element has a specific attribute value
await expect(page.getByRole('link', { name: 'Help' }))
.toHaveAttribute('href', '/help');
// Attribute matches regex
await expect(page.getByRole('img', { name: 'Product' }))
.toHaveAttribute('src', /\.jpg$/);
// Element has a CSS class (regex matches partial class list)
await expect(page.getByRole('link', { name: 'Home' }))
.toHaveClass(/active/);
// Element has specific CSS property value
await expect(page.getByRole('alert'))
.toHaveCSS('background-color', 'rgb(255, 0, 0)');
// Font size check
await expect(page.getByRole('heading', { name: 'Welcome' }))
.toHaveCSS('font-size', '32px');
});import { test, expect } from '@playwright/test';
test('count assertions', async ({ page }) => {
await page.goto('/shopping');
// Exact number of product cards
await expect(page.locator('.product-card')).toHaveCount(12);
// Table has 5 data rows
await expect(page.getByRole('row')).toHaveCount(6); // 5 data + 1 header
// Cart is empty -- zero items
await expect(page.getByTestId('cart-item')).toHaveCount(0);
// After adding an item
await page.getByRole('button', { name: 'Add to Cart' }).first().click();
await expect(page.getByTestId('cart-item')).toHaveCount(1);
});| Assertion | What It Checks | Example |
|---|---|---|
| toBeVisible() | Element is visible on screen | expect(locator).toBeVisible() |
| toBeHidden() | Element is hidden or absent | expect(locator).toBeHidden() |
| toBeAttached() | Element exists in DOM | expect(locator).toBeAttached() |
| toBeEnabled() | Element is not disabled | expect(button).toBeEnabled() |
| toBeDisabled() | Element is disabled | expect(button).toBeDisabled() |
| toBeChecked() | Checkbox/radio is checked | expect(checkbox).toBeChecked() |
| toHaveText() | Text matches exactly | expect(locator).toHaveText("Hello") |
| toContainText() | Text contains substring | expect(locator).toContainText("Hel") |
| toHaveValue() | Input value matches | expect(input).toHaveValue("john") |
| toHaveAttribute() | Has attribute with value | expect(link).toHaveAttribute("href", "/home") |
| toHaveClass() | Has CSS class | expect(el).toHaveClass(/active/) |
| toHaveCSS() | Has CSS property value | expect(el).toHaveCSS("color", "red") |
| toHaveCount() | Number of matching elements | expect(locator).toHaveCount(5) |
| toHaveTitle() | Page title matches | expect(page).toHaveTitle(/Home/) |
| toHaveURL() | Page URL matches | expect(page).toHaveURL(/dashboard/) |
Key Point: All locator assertions auto-retry. You will use toBeVisible, toHaveText, toHaveValue, and toHaveCount the most. Learn these four cold -- they cover 80% of all assertion needs.
Q: What are the most commonly used assertions in Playwright?
A: The top five are: toBeVisible() to check if an element is on screen, toHaveText() to verify text content, toHaveValue() for input field values, toBeEnabled()/toBeDisabled() for button states, and toHaveCount() for checking the number of matching elements. All of these auto-retry for up to 5 seconds. I also frequently use toHaveURL() and toHaveTitle() for page-level verification. The key is that all of them take a locator as input, which enables the auto-retry mechanism.
Key Point: Locator assertions cover visibility, state, text, values, attributes, CSS, and count. All auto-retry. Learn the reference table -- you will use it daily.