IKivan-kozenko -aqa
Try yourself as a QA tester
All topics·Advanced·16 / 24

Snapshot testing

ARIA snapshots capture the accessibility tree of a page as YAML and compare it on re-run. Unlike HTML snapshots, they survive CSS/class refactors — they only break when the meaningful structure changes.

What gets captured

An ARIA snapshot is not HTML. It's the accessibility tree — the same view a screen reader sees. It captures roles, accessible names, and attributes like checked, expanded, disabled. Implementation details like CSS classes, data attributes, or HTML tags don't appear.

This is the key advantage: a developer can rewrite the component's internals, change the markup, rename classes — the snapshot won't break unless the component's accessible structure actually changes.

Write and match a snapshot

The template is a YAML-like string where each line is - role "accessible name". You can match the whole page with expect(page).toMatchAriaSnapshot() or scope to a specific element with a locator.

ts
test('order detail page structure', async ({ page }) => {
  await page.goto('/orders/42')

  // Перевіряємо структуру всієї сторінки
  await expect(page).toMatchAriaSnapshot(`
    - heading "Order #42" [level=1]
    - region "Order details":
      - text: Customer: Ivan Kozenko
      - text: Status: Pending
    - region "Actions":
      - button "Approve order"
      - button "Cancel order"
  `)
})

// Або лише конкретну частину сторінки
test('action buttons are correct', async ({ page }) => {
  await page.goto('/orders/42')

  await expect(page.getByRole('region', { name: 'Actions' })).toMatchAriaSnapshot(`
    - button "Approve order"
    - button "Cancel order"
  `)
})

Partial matching — check what matters

By default, a snapshot template matches a subset — you don't need to list every element. Only what you put in the template is checked. If the page has 20 nav links but you only care that the Orders link exists, include just that one.

You can also omit the accessible name to match any element with that role, or use regex for dynamic text.

ts
// Перевірити що кнопка з роллю button існує (без вказання назви)
await expect(page.getByRole('dialog')).toMatchAriaSnapshot(`
  - dialog:
    - button
`)

// Regex для динамічного тексту
await expect(page).toMatchAriaSnapshot(`
  - heading /Order #\d+/ [level=1]
  - text: /\d+ items/
`)

// Перевірити що список містить хоча б ці елементи (порядок — не важливий)
await expect(page.getByRole('navigation')).toMatchAriaSnapshot(`
  - navigation:
    - link "Dashboard"
    - link "Orders"
`)

Strict children — exact list

When you need to assert that the element has EXACTLY these children and no others, add /children: equal to the template. This is useful for navigation menus, action toolbars, or select options where unexpected extra items are a bug.

ts
// Точно ці 3 кнопки, не більше і не менше
await expect(page.getByRole('toolbar')).toMatchAriaSnapshot(`
  - toolbar "Order actions":
    - /children: equal
    - button "Approve"
    - button "Reject"
    - button "Archive"
`)

// Глобально в конфізі — щоб всі snapshots перевіряли рівно
// playwright.config.ts
export default defineConfig({
  expect: {
    toMatchAriaSnapshot: {
      children: 'equal',
    },
  },
})

Auto-generate snapshot templates

You don't have to write snapshot templates by hand. Two ways to generate them: run with --update-snapshots flag (updates in-test inline strings), or use the VS Code extension's "Update snapshot" button next to a failing test.

On the first run when there's no existing snapshot, Playwright auto-generates it and the test passes. After that, any deviation fails the test until you explicitly update.

bash
# Оновити всі застарілі snapshots
npx playwright test --update-snapshots

# Або лише конкретний файл
npx playwright test tests/orders.spec.ts --update-snapshots
ts
// При першому запуску — пустий шаблон, Playwright заповнить
test('nav structure', async ({ page }) => {
  await page.goto('/dashboard')

  // Перший запуск: Playwright запише шаблон
  await expect(page.getByRole('navigation')).toMatchAriaSnapshot(``)
})

// Після першого запуску шаблон буде заповнений:
// await expect(page.getByRole('navigation')).toMatchAriaSnapshot(`
//   - navigation:
//     - link "Dashboard"
//     - link "Orders"
//     - link "Customers"
// `)