Writing selectors and actions gets you halfway. Assertions verify that your application behaves correctly, and debugging tools help you understand why tests fail. Playwright provides both in abundance.
Expect Matchers
Playwright extends the expect function with web-specific matchers that auto-retry until the condition is met or the timeout expires. This is different from Jest — Playwright assertions are async and will wait for the expected state.
Page-Level Assertions
// Check the page title
await expect(page).toHaveTitle('Dashboard — My App');
await expect(page).toHaveTitle(/Dashboard/);
// Check the current URL
await expect(page).toHaveURL('/dashboard');
await expect(page).toHaveURL(/\/dashboard\?tab=.+/);Element Visibility and Text
const heading = page.getByRole('heading', { name: 'Welcome' });
// Is the element visible?
await expect(heading).toBeVisible();
await expect(heading).not.toBeVisible();
// Check text content
await expect(heading).toHaveText('Welcome Back');
await expect(heading).toContainText('Welcome');Form State Assertions
const emailInput = page.getByLabel('Email');
const submitButton = page.getByRole('button', { name: 'Submit' });
// Check input values
await expect(emailInput).toHaveValue('user@example.com');
await expect(emailInput).toBeEmpty();
// Check enabled/disabled state
await expect(submitButton).toBeEnabled();
await expect(submitButton).toBeDisabled();
// Check checkbox state
await expect(page.getByLabel('Remember me')).toBeChecked();CSS and Attribute Assertions
// Check CSS classes
await expect(page.locator('.alert')).toHaveClass(/alert-success/);
// Check element count
await expect(page.getByRole('listitem')).toHaveCount(5);
// Check attributes
await expect(page.getByAltText('Logo')).toHaveAttribute('src', /logo\.png/);Soft Assertions
By default, a failed assertion stops the test immediately. Soft assertions let you collect multiple failures before stopping:
await expect.soft(page.getByTestId('status')).toHaveText('Active');
await expect.soft(page.getByTestId('role')).toHaveText('Admin');
await expect.soft(page.getByTestId('email')).toHaveText('a@b.com');
// Test continues even if the first assertion fails
// All failures are reported at the end
Debugging Failing Tests
When a test fails, you need to understand what the page looked like at the moment of failure. Playwright offers several debugging tools.
Playwright Inspector
The inspector is an interactive debugger that lets you step through your test line by line:
npx playwright test --debugThis opens a browser window alongside an inspector panel. You can pause execution, inspect the DOM, try selectors in real time, and step through each action. It is the fastest way to diagnose why a locator is not finding an element.
Trace Viewer
The trace viewer is a post-mortem tool that records everything during test execution — DOM snapshots, network requests, console logs, and action timelines:
// In playwright.config.ts
export default defineConfig({
use: {
trace: 'on-first-retry', // Record trace on retry
},
});After a failed test, open the trace:
npx playwright show-trace trace.zipThe trace viewer shows a timeline of every action, a DOM snapshot at each step, network requests, and console output. You can scrub through the timeline to see exactly what the page looked like when the assertion failed.
Screenshots on Failure
Configure Playwright to capture a screenshot when any test fails:
export default defineConfig({
use: {
screenshot: 'only-on-failure',
},
});Screenshots are saved to the test results directory. You can also take screenshots manually within a test:
await page.screenshot({ path: 'debug-checkout.png' });
await page.screenshot({ path: 'full-page.png', fullPage: true });Console Logs
Capture browser console messages in your test to catch runtime errors:
page.on('console', (msg) => {
if (msg.type() === 'error') {
console.log('Browser error:', msg.text());
}
});Key Takeaways
- Playwright assertions auto-retry, so you do not need manual waits.
- Use
toBeVisible(),toHaveText(), andtoHaveURL()for common checks. - Use soft assertions when you want to collect multiple failures in one test.
- The
--debugflag opens the interactive inspector for step-by-step debugging. - Configure
trace: 'on-first-retry'andscreenshot: 'only-on-failure'for automated diagnostics.