Puppeteer ↔ Chrome DevTools Protocol: Is the AXTree snapshot a stable testing contract?
0 reputation · 29 May 2022, 11:33 UTC
0 reputation · 29 May 2022, 11:33 UTC
Use page.accessibility.snapshot() to assert accessible names, roles, and states of a single UI component during automated testing.
Accessibility.getFullAXTree domain; its shape is not versioned.interestingOnly heuristics change between releases, altering which nodes are returned.Should test suites treat the AXTree output as a reliable contract across browser upgrades, or rely on higher‑level assertions (e.g., role/name pairs) that tolerate structural drift?
30275 reputation · 29 May 2022, 12:47 UTC
No — the AXTree snapshot returned by page.accessibility.snapshot() is not a stable contract across Chromium upgrades, and your test suite should not treat it as one. Assert on semantic properties (role, accessible name, value, states) of the specific component you care about, not on tree structure, node counts, or ordering.
Confirmed by the architecture: Puppeteer's snapshot is a thin pass-through of the CDP Accessibility.getFullAXTree response. Puppeteer adds no normalization, filtering, or versioning layer. The CDP Accessibility domain is not versioned, and Chrome's accessibility tree is an internal implementation detail — it evolves as Chrome improves its accessibility support, even when the DOM and ARIA markup are identical.
The likely (but version-dependent) failure modes:
interestingOnly: true, Chrome filters out nodes it deems uninteresting (often generic containers). Those heuristics change between releases, so nodes appear and disappear without any markup change.interestingOnly: false returns far more data, can approach CDP message-size limits on complex pages, and still buys you no structural stability.Find the node for your component and assert only its semantic properties:
const snapshot = await page.accessibility.snapshot();
function findByRoleAndName(node, role, name) {
if (!node) return null;
if (node.role === role && node.name === name) return node;
return (node.children || [])
.map(c => findByRoleAndName(c, role, name))
.find(Boolean);
}
const toggle = findByRoleAndName(snapshot, 'switch', 'Dark mode');
expect(toggle.checked).toBe('true');This tolerates added/removed wrapper nodes, reordering, and heuristic changes, because the search is structural but the assertion is semantic. If you only need role/name checks, DOM-level assertions via getByRole-style queries (Testing Library) or computed ARIA attribute checks are even more insulated from AXTree drift.
In principle yes, but none exists: neither CDP nor Puppeteer exposes a versioned snapshot schema, and Chrome has no commitment to AXTree stability. Don't design around expecting one. If you need cross-version regression detection, snapshot the extracted semantic facts (a sorted list of role/name/value tuples for your component subtree) rather than the raw tree.
Because heuristic behavior is version-specific, confirm on your own fixture:
page.accessibility.snapshot({ interestingOnly: true }) on your current Chromium and on a newer build (e.g., via puppeteer/browsers install chrome@<version>), and diff node counts and role distributions.chrome://accessibility/ in both versions to see the live tree and confirm which nodes the heuristics dropped.One caveat: if your component lives inside shadow DOM or cross-origin iframes, AXTree behavior at those boundaries is especially version-sensitive, so run the two-version comparison before relying on any snapshot there.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.