Resource library

QA How-To

How to Use Playwright aria snapshot (2026)

Learn Playwright aria snapshot testing in 2026 with YAML templates, partial matching, stored snapshots, debugging, CI review, and accessible test design.

19 min read | 2,603 words

TL;DR

Use `expect(locator).toMatchAriaSnapshot()` to compare a stable accessibility subtree with readable YAML. Scope the region, include only meaningful semantics, and review every generated baseline change as a product contract change.

Key Takeaways

  • Use toMatchAriaSnapshot for readable assertions across roles, names, states, URLs, and hierarchy.
  • Scope snapshots to a meaningful region instead of capturing a dynamic full page.
  • Choose contain, equal, or deep-equal child matching according to the exact product contract.
  • Control dynamic data or use narrow regular expressions rather than weakening the whole template.
  • Generate snapshot updates locally and review semantic diffs before committing them.
  • Combine ARIA snapshots with focus, keyboard, behavior, automated scan, and manual accessibility testing.

playwright aria snapshot testing compares a page or locator's accessible structure with a YAML template. It is useful when one user-facing region contains several related roles, names, states, and relationships that would be verbose to assert one at a time. In 2026, the main assertion is expect(pageOrLocator).toMatchAriaSnapshot().

ARIA snapshots are not screenshots and are not a complete accessibility audit. They represent the accessibility tree that browsers and assistive technology consume, so they are especially good at catching missing headings, renamed controls, broken lists, incorrect expanded states, and structural regressions. This guide shows inline and file snapshots, partial and strict matching, generation, review, debugging, and durable test design.

TL;DR

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

test('account navigation exposes the expected structure', async ({ page }) => {
  await page.goto('/account');

  await expect(page.getByRole('navigation', { name: 'Account' }))
    .toMatchAriaSnapshot(`
      - list:
        - listitem:
          - link "Profile"
        - listitem:
          - link "Security"
    `);
});
Goal Use
Verify one value toHaveText, toBeChecked, or another focused assertion
Verify a small semantic subtree Inline toMatchAriaSnapshot()
Review a larger stable region Named .aria.yml snapshot file
Inspect current accessible YAML locator.ariaSnapshot()
Accept intentional changes --update-snapshots, then review the diff

1. What playwright aria snapshot Represents

An ARIA snapshot is a YAML representation of accessible nodes. A node can include a role, accessible name, supported state or property, text, URL, and children. Indentation represents hierarchy. The structure comes from native HTML semantics and ARIA computation, not from DOM tag names or CSS selectors.

For example, this HTML:

<nav aria-label="Primary">
  <a href="/docs">Docs</a>
  <button aria-expanded="false">Products</button>
</nav>

has a snapshot shape like this:

- navigation "Primary":
  - link "Docs":
    - /url: /docs
  - button "Products" [expanded=false]

The accessible name is quoted when matched exactly. A regular expression can replace a dynamic name. State markers can include properties such as checked, disabled, expanded, invalid, level, pressed, and selected. Text that is not represented by a more specific role can appear as text content.

The assertion compares a template with the current accessibility tree. Matching is case-sensitive and order-sensitive, while whitespace is normalized. By default, templates can be partial, so omitting a name, state, or unrelated child can make the test deliberately flexible.

This representation makes a snapshot readable during review. A developer can see that a link became a generic clickable element or that a heading level changed. Unlike pixel snapshots, it does not fail because of color, spacing, antialiasing, or a one-pixel layout difference.

2. Set Up a Runnable ARIA Snapshot Test

Initialize a Playwright Test project and save the test as tests/aria-snapshot.spec.ts. The example uses page.setContent(), so it runs without an application server.

npm init playwright@latest
npx playwright test tests/aria-snapshot.spec.ts
import { test, expect } from '@playwright/test';

test('settings panel has accessible controls', async ({ page }) => {
  await page.setContent(`
    <main>
      <h1>Notification settings</h1>
      <label>
        <input type="checkbox" checked>
        Product updates
      </label>
      <button>Save changes</button>
      <p role="status">All changes saved</p>
    </main>
  `);

  await expect(page.getByRole('main')).toMatchAriaSnapshot(`
    - heading "Notification settings" [level=1]
    - checkbox "Product updates" [checked]
    - button "Save changes"
    - status: All changes saved
  `);
});

Scope the assertion to the smallest meaningful region. A full-page tree includes navigation, banners, experiments, and footers that may be irrelevant to a settings requirement. A named main, dialog, form, navigation, list, or article gives the snapshot a clear owner.

Keep focused assertions for critical actions and outcomes. The snapshot can prove the panel's semantic structure, while await button.click() and await expect(status).toHaveText('Saved') prove behavior. Snapshot testing and action testing complement each other.

For semantic locator fundamentals, review Playwright getByRole. The same role and accessible-name concepts explain both locator matches and ARIA snapshots.

3. Read the YAML Syntax and Accessible States

The usual node form is - role "name" [attribute=value]. The role may stand alone for partial matching, the name can be exact or a regex, and supported states appear in brackets. A colon introduces text or nested content depending on the YAML value.

- heading "Billing" [level=2]
- textbox "Cardholder name": Jamie Lee
- checkbox "Use billing address" [checked]
- button "Edit payment method" [disabled]
- tab "Invoices" [selected]

Boolean states can use shorthand where supported, such as [checked] for true. Values such as [pressed=false], [invalid=spelling], or [level=3] express more detail. Only include a property when it matters to the requirement. Omitting it permits partial matching on that dimension.

Links can expose their URL through a property:

- link "View invoice":
  - /url: /invoices/INV-42

Regular expressions use slash syntax for dynamic accessible names or text. Escape backslashes correctly inside a TypeScript template literal.

await expect(page.getByRole('main')).toMatchAriaSnapshot(`
  - heading /Order #\\d+/ [level=1]
  - status: /Updated \\d+ seconds ago/
`);

Do not use a broad regex to silence meaningful changes. /Order.*/ could accept an error heading or unwanted suffix. Match the stable grammar and ignore only the variable segment.

Inspect the browser accessibility tree when the YAML is unexpected. Native HTML usually produces better semantics than adding redundant ARIA. A snapshot should reveal product behavior, not encourage markup designed only to satisfy automation.

4. Choose Partial, Exact, and Deep Child Matching

ARIA snapshot templates support partial matching. By default, specified children must appear in order, but the actual tree can contain other children between or around them. This is useful when a region includes optional content that is outside the test's purpose.

The /children property controls strictness:

Children mode Meaning Good use
contain Specified children appear in order, extras allowed Stable milestones in a dynamic region
equal Direct children exactly match in order Strict list or toolbar contract
deep-equal Children and nested descendants exactly match Small, highly controlled component
await expect(page.getByRole('list', { name: 'Plan features' }))
  .toMatchAriaSnapshot(`
    - list "Plan features":
      - /children: equal
      - listitem: API access
      - listitem: Audit logs
      - listitem: SSO
  `);

Use strict modes sparingly. A deep-equal full-page snapshot can become a maintenance burden because an unrelated accessible helper, description, or nested link changes the baseline. Strictness is valuable when the exact ordered structure is the requirement, such as a menu, stepper, or regulated confirmation summary.

You can set the default children mode in configuration and override individual templates:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  expect: {
    toMatchAriaSnapshot: {
      children: 'equal',
    },
  },
});

Before choosing a global strict mode, inspect the suite's component stability. Many teams get better signal by keeping contain as the default and adding /children: equal only to important bounded regions.

5. Handle Dynamic Text Without Making Snapshots Weak

Dynamic identifiers, counters, dates, user names, and localized copy can make an exact snapshot brittle. First ask whether the value can be deterministic through test data or a controlled clock. Determinism produces stronger tests than a broad regex.

When variation is the intended contract, use a precise expression or omit only the irrelevant name. This snapshot expects an order heading with digits, a stable button, and a status that names the expected workflow.

await expect(page.getByRole('main')).toMatchAriaSnapshot(`
  - heading /Order #\\d+/ [level=1]
  - button "Cancel order"
  - status: /Order is (processing|shipped)/
`);

Omitting a name matches the role regardless of name:

- button

That is appropriate only if the accessible name is irrelevant. For a primary Submit button, the name is usually part of the user contract and should remain exact. For a generated chart graphic whose label contains a constantly changing timestamp, checking the role and a stable prefix may be enough.

Snapshot order is meaningful. If cards can legitimately reorder based on live data, assert the structure of one card rather than snapshotting the whole feed. Use a locator filter to choose a deterministic item, then capture its subtree.

const card = page.getByRole('article')
  .filter({ has: page.getByRole('heading', { name: 'Starter plan' }) });

await expect(card).toMatchAriaSnapshot(`
  - heading "Starter plan" [level=2]
  - text: $19 per month
  - button "Choose Starter"
`);

The test is now sensitive to the plan's accessible contract without coupling to feed order or unrelated plans.

6. Generate, Update, and Store Snapshots Safely

Playwright can generate snapshots through Codegen, an empty template, or the test runner's update workflow. An empty inline template gives the updater a place to write a baseline.

await expect(page.getByRole('main')).toMatchAriaSnapshot('');

Run the target test with snapshot updates:

npx playwright test tests/account.spec.ts --update-snapshots

The update workflow can produce patch-based changes for source snapshots. Supported source update methods include patch, 3way, and overwrite.

npx playwright test --update-snapshots --update-source-method=patch

Treat generated content as a draft. Review whether roles, names, states, hierarchy, and included regions reflect requirements. Remove noisy or variable nodes rather than accepting an oversized baseline. Never update snapshots automatically on the main branch simply because a test failed.

For larger snapshots, store YAML separately with a .aria.yml name:

await expect(page.getByRole('main')).toMatchAriaSnapshot({
  name: 'account-main.aria.yml',
});

By default, the snapshot is stored under the test's snapshot directory. You can configure its path template:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  expect: {
    toMatchAriaSnapshot: {
      pathTemplate: '__snapshots__/{testFilePath}/{arg}{ext}',
    },
  },
});

Commit intended baselines and review their diffs like code. A snapshot update is a product-contract change, not routine test output.

7. Inspect with ariaSnapshot() and Debug Mismatches

locator.ariaSnapshot() returns the current YAML representation as a string. It is an inspection API, not an assertion. Use it during diagnosis, to build focused tooling, or to attach safe evidence.

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

test('inspect checkout accessibility tree', async ({ page }, testInfo) => {
  await page.goto('/checkout');
  const checkout = page.getByRole('main');

  const yaml = await checkout.ariaSnapshot();
  await testInfo.attach('checkout-aria.yml', {
    body: yaml,
    contentType: 'text/yaml',
  });

  await expect(checkout).toMatchAriaSnapshot(`
    - heading "Checkout" [level=1]
    - button "Place order"
  `);
});

Before attaching a large snapshot, consider whether accessible names contain customer data, account details, or other sensitive text. Test reports can have broader access and longer retention than the application.

When an assertion fails, classify the diff:

  • Missing node: element absent, hidden from the accessibility tree, or outside locator scope.
  • Changed role: native element or ARIA role changed.
  • Changed name: visible copy, label relationship, or ARIA naming changed.
  • Changed state: application did not update checked, expanded, selected, or another property.
  • Changed order or hierarchy: DOM semantics or grouping changed.
  • Extra node under strict children mode: baseline is strict or product added content.

Use the trace, locator picker, and browser accessibility panel to confirm the rendered semantics. If the mismatch accompanies a waiting failure, the visible, enabled, and stable Playwright guide helps diagnose the interaction side.

8. Combine ARIA Snapshots with Focused Assertions

Snapshot tests provide broad semantic coverage, but focused assertions explain critical requirements more directly. Use a snapshot to verify a dialog's structure, then assert its focus, enabled state, action, and outcome separately.

test('delete dialog exposes and performs confirmation', async ({ page }) => {
  await page.goto('/projects/42');
  await page.getByRole('button', { name: 'Delete project' }).click();

  const dialog = page.getByRole('dialog', { name: 'Delete project' });
  await expect(dialog).toMatchAriaSnapshot(`
    - heading "Delete project" [level=2]
    - text: This action cannot be undone.
    - button "Cancel"
    - button "Confirm delete"
  `);

  const confirm = dialog.getByRole('button', { name: 'Confirm delete' });
  await expect(confirm).toBeFocused();
  await confirm.click();
  await expect(page.getByRole('status')).toHaveText('Project deleted');
});

The snapshot alone does not prove focus moved into the dialog, the keyboard is trapped appropriately, Escape works, or the button triggers deletion. It also does not evaluate color contrast, target size, labels in context, announcement timing, or every accessibility standard.

Combine layers according to risk:

  • Semantic snapshot for role, name, state, and hierarchy.
  • Locator assertions for focused behavior and live state transitions.
  • Keyboard tests for tab order and widget operation.
  • Automated accessibility scanning for rule-based violations.
  • Manual assistive-technology and usability review for critical journeys.

Use the accessibility testing checklist to plan coverage beyond snapshots. Treat a clean snapshot as evidence for a defined semantic contract, not a certificate of accessibility.

9. Review Snapshot Changes in CI and Pull Requests

CI should compare committed baselines and fail on unexpected differences. It should not accept updates automatically. A pull request that changes an ARIA snapshot needs an explanation of the corresponding product requirement, especially when a role, accessible name, or state disappears.

Use this review sequence:

  1. Confirm the test reached the intended UI state before snapshotting.
  2. Read the semantic diff rather than only approving generated text.
  3. Decide whether the product change is intentional and accessible.
  4. Remove unstable detail that is not part of the contract.
  5. Run the affected test across configured browser projects when semantics may differ.
  6. Commit the test, external snapshot, and product change together.

ARIA snapshots are generally shared across browser projects rather than duplicated per browser. Still, browser engines and platform accessibility mappings can expose differences in edge cases. Keep markup standards-based and investigate unexpected cross-browser results instead of maintaining arbitrary per-browser semantics.

Avoid full-page snapshots for pages with ads, experiments, generated recommendations, or live activity. Scope stable regions and use partial matching. This keeps CI signal focused and reduces update fatigue.

When a baseline changes because a heading was removed or an icon button lost its name, prefer fixing the application. Updating the snapshot would record the regression as the new expected state. Snapshot review is therefore part QA analysis and part accessibility review, not a mechanical test-maintenance task.

10. Best Practices for Maintainable playwright aria snapshot Tests

Name each snapshot around user intent, such as checkout summary, account navigation, or delete confirmation. A semantic region with a stable purpose is a better boundary than a framework component or CSS container.

Keep inline templates short enough to understand beside the test. Move a larger stable baseline to a named .aria.yml file. In both cases, include only the roles, names, states, URLs, and children that matter. Use exact children matching for a deliberately closed structure and partial matching for a region with acceptable additions.

Make the UI state deterministic before asserting. Seed controlled data, stop at the correct route, wait through web-first assertions, and avoid snapshotting during a loading transition. ARIA snapshot assertions use the expect timeout, but waiting longer cannot repair a state that never settles.

Prefer native HTML and correct labeling. Do not add arbitrary roles, hidden labels, or aria-label values only to satisfy a snapshot. The accessible tree is a product interface used by real people.

Finally, keep a written update policy:

  • Developers generate a candidate baseline locally.
  • Reviewers compare the semantic diff with the requirement.
  • Unexpected lost names, roles, states, or hierarchy block the change.
  • Dynamic text is controlled or narrowly matched, never broadly ignored.
  • CI verifies snapshots but does not rewrite them.
  • Accessibility specialists review high-risk workflow changes.

These rules preserve the main advantage of ARIA snapshots: broad, readable semantic coverage with less noise than DOM or pixel snapshots.

Interview Questions and Answers

Q: What is a Playwright ARIA snapshot?

It is a YAML representation of the accessible tree for a page or locator. The template can describe roles, accessible names, supported states, text, URLs, and hierarchy. toMatchAriaSnapshot() compares the current tree with that template.

Q: How is an ARIA snapshot different from a screenshot snapshot?

An ARIA snapshot validates semantic structure exposed to assistive technology. A screenshot validates rendered pixels. ARIA snapshots ignore color and layout detail but can detect lost roles, names, hierarchy, and state, while screenshots cover visual regressions that the accessibility tree cannot express.

Q: Are ARIA snapshots a replacement for accessibility testing?

No. They cover a useful semantic contract but do not prove keyboard behavior, focus management, contrast, announcement timing, target size, or overall usability. I combine them with focused assertions, scans, keyboard tests, and manual assistive-technology review.

Q: What is the difference between ariaSnapshot() and toMatchAriaSnapshot()?

ariaSnapshot() returns the current YAML string for inspection or tooling. toMatchAriaSnapshot() is a retrying Playwright assertion that compares current state with a template or stored snapshot. One captures representation, the other verifies a contract.

Q: How do you handle dynamic values in a snapshot?

I first make data deterministic when possible. If variation is part of the requirement, I use a narrowly bounded regular expression, omit an irrelevant property, or scope the locator to a stable item. I avoid broad expressions that accept unrelated changes.

Q: What do contain, equal, and deep-equal children modes mean?

contain allows extra children while requiring specified children in order. equal requires the direct child list to match exactly. deep-equal also requires exact nested descendants, so I reserve it for small and tightly controlled components.

Q: How should teams update ARIA snapshots?

Generate the candidate locally with the snapshot update workflow, then review the semantic diff against the product requirement. Commit only intentional changes. CI should verify committed baselines and should not automatically accept a failing snapshot.

Q: Why should snapshots be scoped to a locator?

Scoping creates a clear semantic owner and excludes unrelated navigation, experiments, and dynamic content. It reduces maintenance noise and produces more actionable diffs. Full-page snapshots are appropriate only when the full page structure is genuinely the contract.

Common Mistakes

  • Treating a passing ARIA snapshot as proof of complete accessibility.
  • Snapshotting the full page when only one dialog or region matters.
  • Updating baselines automatically after every failure.
  • Using broad regular expressions that hide changed labels or error text.
  • Applying deep-equal to large dynamic trees and creating update fatigue.
  • Omitting accessible names that are essential to the user task.
  • Snapshotting during loading or animation without establishing the intended state.
  • Adding incorrect ARIA to make the expected YAML appear.
  • Forgetting that order and case are meaningful during matching.
  • Attaching snapshots containing sensitive accessible text to public reports.

Conclusion

playwright aria snapshot testing is most effective for small, meaningful accessibility subtrees with stable semantic requirements. Use toMatchAriaSnapshot() to verify roles, names, states, and hierarchy, select partial or exact children deliberately, and use ariaSnapshot() when you need to inspect the current YAML.

Start with one high-risk dialog, form, or navigation region. Generate a candidate, remove irrelevant detail, add focused behavior assertions, and review the semantic baseline with the same care as application code. That workflow catches accessibility structure regressions without turning every harmless UI change into noise.

Interview Questions and Answers

What does toMatchAriaSnapshot verify?

It compares the current accessible subtree with a YAML template or stored snapshot. The comparison can cover roles, names, supported states, text, URLs, order, and hierarchy. The assertion retries within the expect timeout.

How does an ARIA snapshot differ from a screenshot?

An ARIA snapshot checks semantic structure exposed to accessibility APIs. A screenshot checks rendered pixels and visual appearance. Teams often need both because each catches a different class of regression.

What is partial matching in an ARIA snapshot?

A template can omit names, states, or children that are not part of the requirement. The default child behavior allows additional children while preserving the order of specified ones. This keeps a focused semantic contract resilient to irrelevant additions.

When would you use deep-equal children?

I use it for a small, tightly controlled component whose complete nested accessibility structure is the requirement. I avoid it for full pages or dynamic regions. Excessive strictness creates maintenance noise and hides the important diff.

How do you handle dynamic names in ARIA snapshots?

I first make the data deterministic through fixtures or a controlled clock. If variation is intentional, I use a narrow regular expression or scope to a stable item. I do not use broad wildcards that accept unrelated error text.

How should snapshot updates be reviewed?

The reviewer maps each semantic change to an intentional product requirement. Lost names, changed roles, and state regressions should block acceptance unless explicitly intended. CI verifies committed baselines but should not rewrite them.

Can a passing ARIA snapshot prove WCAG compliance?

No. It gives evidence about a chosen semantic subtree, not every accessibility requirement. Keyboard behavior, focus management, contrast, announcements, and assistive-technology usability need additional testing.

Why scope ARIA snapshots to locators?

A scoped region has clear ownership and excludes unrelated dynamic content. Its diffs are smaller and more actionable. I choose landmarks, dialogs, forms, lists, or articles that match a user task.

Frequently Asked Questions

What is a Playwright aria snapshot?

It is a YAML representation of a page or locator's accessible tree. It can express roles, accessible names, states, text, URLs, and hierarchy for snapshot comparison.

How do I assert an ARIA snapshot in Playwright?

Call `expect(pageOrLocator).toMatchAriaSnapshot()` with an inline YAML template or a named `.aria.yml` snapshot. Scope the locator to the smallest meaningful region before asserting.

How do I generate or update Playwright ARIA snapshots?

Use an empty template or a named snapshot and run Playwright Test with `--update-snapshots`. Review the generated semantic diff before committing, and do not auto-accept updates in CI.

What does locator.ariaSnapshot return?

It returns the current accessibility subtree as a YAML string. Use it for inspection, diagnostic attachments, or tooling, while `toMatchAriaSnapshot` is the retrying assertion API.

Can ARIA snapshots contain regular expressions?

Yes. Regex patterns can match dynamic accessible names or text. Keep patterns narrowly bounded so they ignore only intended variation and still catch meaningful label changes.

Are Playwright ARIA snapshots accessibility tests?

They are one useful accessibility testing layer, but they do not prove full accessibility. Add keyboard, focus, automated rule scans, contrast checks, and manual assistive-technology review.

Should I store ARIA snapshots inline or in files?

Keep small readable templates inline beside the test. Use a named `.aria.yml` file for a larger stable region that benefits from a dedicated semantic diff.

Related Guides