Series Navigation
← Recorder Guide — SPAs, Shadow DOM, Iframes
Why Assertions Matter More Than Actions
Recording actions is the easy part — any recorder can capture a click sequence. Assertions are what turn a script into a test. Without them you're just replaying actions with no verification.
A test that clicks "Submit" but never checks whether the form actually submitted is worse than useless — it gives false confidence.
Using the Assertion Builder
- Record your actions (or type them manually)
- Click the Assertion Builder tab
- Move your mouse over the page — elements highlight as you hover
- Click the element you want to assert on
- The panel shows all available assertion types for that element
- Select one → code is appended to the editor
Element Assertions
Visibility
// Element is visible on screen
await expect(page.locator('.success-message')).toBeVisible();
// Element exists in DOM but is hidden
await expect(page.locator('.error-panel')).toBeHidden();
// Element does NOT exist in DOM at all
await expect(page.locator('.loading-spinner')).not.toBeAttached();
Text Content
// Exact text match
await expect(page.locator('h1')).toHaveText('Welcome to Dashboard');
// Partial text (contains)
await expect(page.locator('.notification')).toContainText('saved successfully');
// Text matches a regex
await expect(page.locator('.user-greeting')).toHaveText(/Hello, \w+/);
// Inner text (strips HTML tags)
await expect(page.locator('.price')).toHaveText('$29.99');
Input Values
// Input field value
await expect(page.locator('#email')).toHaveValue('user@example.com');
// Textarea value
await expect(page.locator('textarea#notes')).toHaveValue('My note content');
// Empty input
await expect(page.locator('#search')).toHaveValue('');
State Assertions
// Button is enabled / disabled
await expect(page.locator('#submit')).toBeEnabled();
await expect(page.locator('#submit')).toBeDisabled();
// Checkbox / radio is checked
await expect(page.locator('#terms-checkbox')).toBeChecked();
await expect(page.locator('#terms-checkbox')).not.toBeChecked();
// Input is editable
await expect(page.locator('#name')).toBeEditable();
// Element is focused
await expect(page.locator('#email')).toBeFocused();
Attribute and Class Assertions
// Specific attribute value
await expect(page.locator('img.logo')).toHaveAttribute('alt', 'Company Logo');
await expect(page.locator('a.docs-link')).toHaveAttribute('href', '/docs');
// Attribute matches regex
await expect(page.locator('input')).toHaveAttribute('type', /text|email/);
// CSS class present
await expect(page.locator('.status-badge')).toHaveClass(/active/);
await expect(page.locator('.status-badge')).toHaveClass('badge badge-active');
CSS and Style
// CSS property value
await expect(page.locator('.alert')).toHaveCSS('background-color', 'rgb(255, 0, 0)');
await expect(page.locator('.modal')).toHaveCSS('display', 'block');
// Inline style — note: checks computed style, not inline attribute
await expect(page.locator('#panel')).toHaveCSS('visibility', 'visible');
List and Count Assertions
// Exact count of matching elements
await expect(page.locator('table tbody tr')).toHaveCount(10);
await expect(page.locator('.product-card')).toHaveCount(24);
// At least one match
await expect(page.locator('.error-message')).not.toHaveCount(0);
// Check text of all items in a list
await expect(page.locator('ul.nav li')).toHaveText([
'Home', 'Products', 'About', 'Contact'
]);
// Partial match across list
await expect(page.locator('.tag')).toContainText(['JavaScript', 'TypeScript']);
Page-Level Assertions
// URL assertions
await expect(page).toHaveURL('https://app.example.com/dashboard');
await expect(page).toHaveURL(/dashboard/);
await expect(page).not.toHaveURL(/login/);
// Page title
await expect(page).toHaveTitle('Dashboard — My App');
await expect(page).toHaveTitle(/Dashboard/);
// Screenshot comparison (visual regression)
await expect(page).toHaveScreenshot('dashboard.png');
await expect(page.locator('.chart')).toHaveScreenshot('revenue-chart.png');
Soft Assertions — Check Multiple Without Stopping
By default, a failing assertion stops the test. Soft assertions collect all failures and report them at the end:
test('profile page validation', async ({ page }) => {
await page.goto('/profile');
// These all run even if one fails
await expect.soft(page.locator('h1')).toHaveText('My Profile');
await expect.soft(page.locator('.avatar')).toBeVisible();
await expect.soft(page.locator('.email')).toContainText('@');
await expect.soft(page.locator('.join-date')).toBeVisible();
// This assertion is hard — stops if fails
await expect(page.locator('#edit-button')).toBeEnabled();
});
Assertion Timeouts
Every expect() retries until the assertion passes or the timeout is reached (default: 5 seconds):
// Custom timeout for slow operations
await expect(page.locator('.report-generated')).toBeVisible({ timeout: 30000 });
// Global default in playwright.config.ts
export default defineConfig({
expect: { timeout: 10000 },
});
Discussion
Loading...Leave a Comment
All comments are reviewed before appearing. No links please.