Testing that a whole section of a page rendered correctly — the right headings, the right buttons, the right labels, in the right structure — usually means writing a pile of individual assertions:
await expect(page.getByRole('heading', { name: 'Checkout' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Place Order' })).toBeVisible();
await expect(page.getByRole('textbox', { name: 'Card Number' })).toBeVisible();
await expect(page.getByRole('textbox', { name: 'Expiry' })).toBeVisible();
// ...and so on, one line per element you actually care about
Each line is fine in isolation. Together they're verbose, they don't capture the structure — parent/child relationships, order — and every one is a separate point of maintenance when the page changes. toMatchAriaSnapshot() replaces the whole block with a single assertion against a YAML description of the accessibility tree:
await expect(page).toMatchAriaSnapshot(`
- heading "Checkout"
- textbox "Card Number"
- textbox "Expiry"
- button "Place Order"
`);
That's the accessible structure of the section, described the way a screen reader would encounter it — roles and accessible names, nested to match the real DOM hierarchy. One assertion, and it reads like a spec of what the UI should be rather than a checklist of individual toBeVisible() calls.
Generating the snapshot instead of hand-writing it
You don't write the YAML from scratch. locator.ariaSnapshot() produces it directly from a locator's current DOM state:
const snapshot = await page.locator('#checkout-form').ariaSnapshot();
console.log(snapshot);
// paste this into your toMatchAriaSnapshot() call as your baseline
Run it once against a known-good page state, review the output, and use it as your expected value. This is the same workflow as visual snapshot testing — generate a baseline, commit it, let future runs diff against it — applied to structure instead of pixels.
Where this beats manual assertions, and where it doesn't
Structural snapshots are strong for broad regression coverage — catching that a section's shape changed at all, even in ways you didn't specifically think to assert on. If a developer removes a form field or reorders two buttons, a snapshot assertion catches it without you having written an assertion for that specific field or that specific order.
They're the wrong tool when you want one precise, named check with a clear failure message — expect(saveButton).toBeEnabled() tells you exactly what was wrong when it fails. A failing snapshot assertion tells you the whole tree didn't match, and you have to read a diff to find what actually changed. For a test whose entire point is "does this one button become enabled after filling the form," keep the targeted assertion. For "did this settings panel render with everything it's supposed to have," reach for the snapshot.
In practice I've landed on using snapshots for full-section or full-page structural checks — the kind of test that exists to catch "something changed here" broadly — and keeping manual, named assertions for the specific behavioral checks that are the actual point of a given test.
