Q10 of 42 · Playwright

How do you assert text content in Playwright?

PlaywrightJuniorplaywrightassertionsfundamentalsjunior

Short answer

Short answer: Use `expect(locator).toHaveText(...)` for exact / regex match, `toContainText(...)` for substring, `toHaveValue(...)` for inputs, `toHaveTitle(...)` for the page title. All assertions auto-retry until the timeout.

Detail

Playwright's expect (from @playwright/test) has built-in matchers that auto-retry. They differ from Jest's expect — these are Locator-aware.

Common matchers:

// Exact text
await expect(page.getByRole('heading', { level: 1 })).toHaveText('Welcome');

// Regex
await expect(page.getByTestId('total')).toHaveText(/£\d+\.\d{2}/);

// Substring
await expect(page.getByTestId('cart-banner')).toContainText('items in your cart');

// Array match (multiple elements)
await expect(page.getByRole('listitem')).toHaveText(['Apples', 'Bread', 'Cheese']);

// Input value
await expect(page.getByLabel('Email')).toHaveValue('alice@x.com');

// Page title / URL
await expect(page).toHaveTitle('qa.codes');
await expect(page).toHaveURL(/dashboard/);

The retry behaviour means you don't write waitForText boilerplate — expect(...).toHaveText(...) waits up to the test timeout for the assertion to pass.

toHaveText vs toContainText: exact-match vs substring. Exact is stricter and catches unexpected text additions; substring is forgiving and survives copy tweaks. Default to exact unless the surrounding text is genuinely unstable.

Negative assertions: await expect(locator).not.toBeVisible() etc. — same auto-retry but inverted.

Custom timeout per assertion:

await expect(locator).toHaveText('Loaded', { timeout: 10_000 });

// EXAMPLE

assertions.spec.ts

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

test('cart assertions', async ({ page }) => {
  await page.goto('/cart');

  // Page-level
  await expect(page).toHaveTitle('Your cart — qa.codes');
  await expect(page).toHaveURL(/\/cart/);

  // Single element
  await expect(page.getByTestId('total')).toHaveText('£42.50');
  await expect(page.getByRole('heading')).toContainText('cart');

  // Multiple elements at once
  await expect(page.getByTestId('item-name')).toHaveText([
    'Apples', 'Bread', 'Cheese',
  ]);

  // Negative
  await expect(page.getByTestId('empty-state')).not.toBeVisible();

  // Custom timeout for slow updates
  await expect(page.getByTestId('total')).toHaveText('£0.00', { timeout: 10_000 });
});

// WHAT INTERVIEWERS LOOK FOR

Knowing Playwright's `expect` is Locator-aware and auto-retries, the difference between `toHaveText` and `toContainText`, and array assertions for multiple elements.

// COMMON PITFALL

Reaching for `toBe` / `toEqual` with `textContent()` — those don't retry. Use `toHaveText` instead.