Overview
Visual testing catches unintentional layout or style regressions that functional tests miss. It operates at two levels:
- •Visual regression baselines — Playwright's
toHaveScreenshot()compares against committed PNGs - •AI-readable screenshots — saved to
e2e/screenshots/for Claude Code to inspect via the Read tool
Screenshot Types
Regression Baselines
Committed in e2e/__snapshots__/. Playwright compares each test run pixel-by-pixel (with maxDiffPixelRatio: 0.01 tolerance).
- •Generated with
bun run test:visual:update - •Verified with
bun run test:visual - •Must be updated after intentional visual changes
AI-Readable Screenshots
Saved to e2e/screenshots/ (gitignored, ephemeral). Regenerated on every test run.
- •Used by Claude Code:
Read e2e/screenshots/home--initial-load.png - •Produced by
takeAIScreenshot()andtakeStepScreenshot()frome2e/utils/screenshots.ts - •Useful for autonomous review of UI changes without running the app
Workflow
Adding Visual Tests for a New Page
- •Add test in
e2e/visual.spec.tsusingtoHaveScreenshot() - •Optionally add
takeStepScreenshot()for AI-readable copies - •Run
bun run test:visual:updateto generate baseline - •Commit the new baseline PNGs in
e2e/__snapshots__/
After Intentional Visual Changes
- •Run
bun run test:visual— expect failures on changed pages - •Review the diff in
test-results/to confirm changes are intentional - •Run
bun run test:visual:updateto regenerate baselines - •Commit updated baselines
CI Behavior
- •Visual tests run as part of
bun run test:e2e(all Playwright tests) - •Failure screenshots and AI-readable PNGs uploaded as CI artifacts
- •Baselines are committed, so CI compares against the same reference
When to Consult This
Consult this process when:
- •Adding a new page or route
- •Changing layout or CSS significantly
- •Refactoring component structure that affects rendering
- •Reviewing visual QA for a feature
See Also
- •Testing Engineer — owns visual regression suite
- •Verification and Testing —
visualTEST<sub>PLAN</sub> value