Repository navigation
Add --aria: diff ARIA snapshots per route - #93
Merged
Merged
Conversation
Playwright's ARIA snapshot of both sides, diffed line by line after the shot, with asset hashes and the left origin normalized and masked elements left out. Pixels still decide the score and exit code. Fixes #45 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…mple config Co-Authored-By: Claude Opus 5.5 <[email protected]>
tmashara
force-pushed
the
claude/issue-45-aria
branch
from
October 29, 2026 19:39
9175d1a to
5433ffb
Compare
This was referenced Oct 29, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
--aria(configaria: true): after each shot, renderdiff takes Playwright's ARIA snapshot (locator.ariaSnapshot()) ofbodyor the--selectorelement on both sides and diffs it line by line, reusingtextDifffromcompare.ts.Why
A markup migration can keep every pixel and still break the page for a screen reader. The new
button-vs-divfixture shows it: a<div>styled like the<button>it replaced scores 100% at every width, and only the accessibility tree shows the lost role.Output
+is left only (your change),-is right only, the same convention astext.truncatedcounts the rest.aria: { added, removed, truncated }, where both lists empty means identical. When the snapshot fails it isaria: nullwithariaError. There is nosummary.aria.--progressrows showaria: N added, M removed. Report cards showaria identicaloraria differs (+a −r), and the summary has a collapsedARIA:block./url:values are normalized the same way as--computed(normalizeAssets's rewrite, split out asnormalizeUrl). The left origin is rewritten to the right's (rebase's regex, split out asrebaseText).Masked elements are excluded
ariaSnapshothas no exclude option, so each mask's elements getaria-hidden="true"right before the snapshot. That happens after all shot work for the job: the shot, the recheck,locateand--computed. Nothing restores the attribute, and nothing needs to: every next job reloads the page withgoto, and so do the retry and error paths.Limits
mainswapped for adiv), every line under it re-indents and shows up as changed. This is marked with aponytail:comment. The upgrade path is to key lines by role path.truncatedcounting what was cut.--computedtime budget (15s, raced at 20s).How tested
tests/fixtures/pages/button-vs-div. It is pixel-identical at all six equivalence widths on macOS; Linux CI will confirm.tests/visual/controls.test.tsruns/button-vs-div,/originand/marginwith--aria. Exit code is 1 because/margindiffers./button-vs-divscores 1 and shows exactly the lost-button lines at both widths, once in stdout./origin, which differs only in port, and/margin, which is CSS only, come out identical.unknown flag: --aria). With the change it passes./button-vs-divwith--mask .buy, and'/button-vs-div @.notes'. Both come out identical. It fails if thearia-hiddenmarking is removed, and it fails if the snapshot readsbodyinstead of the route's selector.tests/unit/computed.test.tscover hash and origin normalization, the cap andtruncated, and howfmtAriagroups and splits diffs across widths.--aria:ResolvedConfig.ariaistrue | undefined, so an unset or false value drops out of the hashed JSON.npm run typecheck,lint,format:check,npm test(348 pass) andnpm run test:visual(379 pass), rebased on main after Per-route selector in the route string: '/pricing @section.cta' #91 all pass locally.Fixes #45
🤖 Generated with Claude Code