11.17 — Visual & a11y testing (bonus)
Bonus lesson — two specialist tools for the gaps everything else misses: visual testing (a pixel-level toEqual, because functional tests can’t see a destroyed layout) and the automated accessibility scan. Know they exist, know their honest limits, reach for them when the question is theirs.
// visual: a pixel-level toEqual
await expect(page).toHaveScreenshot("checkout.png");
// 1st run: SAVES the baseline image
// later runs: pixel-compare → fail on diff
// intended redesign? update the expected value:
// $ npx playwright test --update-snapshots
// tolerance for pixel weather:
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
});
// a11y: the automated floor
import AxeBuilder from "@axe-core/playwright";
const results = await new AxeBuilder({ page })
.analyze();
expect(results.violations).toEqual([]);The gap first: a CSS regression rotates the total and shrinks the Pay button to unreadable — and every functional test PASSES, because toHaveText("₹900") only asks whether the text exists (11.6). The words are all there. The page is destroyed. Text assertions are blind to LOOKS.
The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”
Screenshot scope composes: expect(locator).toHaveScreenshot() snapshots ONE component instead of the page — much less brittle (only that widget’s pixels can fail), and usually the better default. fullPage: true, mask: [locator] (hide dynamic regions like timestamps), and stylePath (inject test-only CSS) handle the classic instabilities.
Baselines are stored per-platform and per-project (11.12) — checkout-chromium-linux.png — which is exactly why teams generate them on CI and treat local visual runs as advisory. The --update-snapshots flag exists on CI reruns for that reason.
The axe engine behind AxeBuilder is the industry’s shared standard (the same one inside browser devtools’ accessibility audits). Its rules map to WCAG — the Web Content Accessibility Guidelines — the spec accessibility law references worldwide. “Passes axe” is the floor; “meets WCAG AA” is the certificate humans verify toward.
Job note: teams increasingly staff “quality” to cover both of these — and interviewers probe with “how would you catch a CSS regression?” (answer: component-scoped toHaveScreenshot on key screens, baselines governed like spec changes) and “what does automated a11y testing miss?” (answer: everything needing judgment — focus order sensibility, alt-text QUALITY, screen-reader flow — the scan is a floor). Both answers are now yours.
⌨️ build the pixel differ
toHaveScreenshot is a comparison engine with a tolerance dial. Build it on strings-as-pixel-rows: count differences position by position, then let maxDiffPixels decide pass or fail.
requirements:
- Write
diffCount(baseline, actual)for two same-length strings: split each into characters (1.6’s train, car by car) and count positions where they differ — filter’s callback accepts the INDEX as its second argument (4.9’s fine print, finally used). - Write
compare(baseline, actual, maxDiffPixels): compute the count, printdiff pixels: N, then printwithin tolerance: passorbeyond tolerance: FAIL. - Run twice:
"##########"vs"#########_"with tolerance2(antialiasing dust — passes), then"##########"vs"____######"with tolerance2(a real regression — fails).
when you press RUN, the console must show exactly:
✏️ Quick check 1
The first-ever run of toHaveScreenshot("checkout.png") — what does it do?
✏️ Quick check 2
A teammate runs --update-snapshots to make a red visual test green, without knowing why it was red. Which 10.3 trap is that?
✏️ Quick check 3
Does a passing axe scan certify the page as fully accessible? Type yes or no.
🗣️ Now teach it back
Explain both bonus tools to a friend: the gap visual testing fills, the baseline/compare/diff cycle (and who governs baseline updates), the pixel-weather kit, and what the axe scan is — including what it honestly can’t do.
Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.