page.getByText('Success').isVisible() returns a boolean by checking the DOM exactly once: if the element hasn't rendered yet, you get false even if it appears 200ms later. expect(page.getByText('Success')).toBeVisible() retries for up to 5 seconds before failing. That retry behavior is the core of Playwright's assertion design, and most beginner mistakes come from not knowing which methods have it. This guide covers every assertion type, the not negation, soft assertions for collecting all failures, and the patterns that silently disable auto-retry.

How Playwright assertions work

Playwright assertions use the expect() function from @playwright/test. They're not the same as Jest's expect. Playwright's version is async and has built-in retry logic.

import { test, expect } from '@playwright/test';

The key difference from most testing frameworks: Playwright assertions retry automatically. When you write:

await expect(page.getByText('Welcome')).toBeVisible();

Playwright doesn't just check once. It checks repeatedly for up to 5 seconds (the default expect timeout), waiting for the condition to become true. This eliminates the need for manual waitFor calls in 90% of cases.

If the condition never becomes true within the timeout, the test fails with a clear message showing what was expected and what actually existed.

Element assertions (locator-based)

These check properties of a specific element on the page.

toBeVisible / toBeHidden

// Element is rendered and visible to the user
await expect(page.getByText('Dashboard')).toBeVisible();

// Element is not present, or present but hidden (display:none, visibility:hidden, opacity:0)
await expect(page.getByRole('dialog')).toBeHidden();

toBeHidden() is true if the element doesn't exist OR if it exists but is invisible. Use not.toBeAttached() if you specifically need to confirm the element isn't in the DOM at all.

toHaveText / toContainText

// Exact text match (trims whitespace automatically)
await expect(page.getByRole('heading', { level: 1 })).toHaveText('My Travel Items');

// Partial text match
await expect(page.getByRole('heading')).toContainText('Travel');

// Array: check text of multiple elements
await expect(page.getByRole('listitem')).toHaveText(['Tokyo', 'Paris', 'London']);

// Regex: pattern match
await expect(page.getByTestId('price')).toHaveText(/\$\d+\.\d{2}/);

toHaveText with an array matches the full text of each element in order. Very useful for verifying table rows or sorted lists.

toHaveValue

For ,