| auto-generated | true |
| name | visual-testing |
| description | When and how to use visual regression tests and AI-readable screenshots. |
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() and takeStepScreenshot() from e2e/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.ts using toHaveScreenshot()
- Optionally add
takeStepScreenshot() for AI-readable copies
- Run
bun run test:visual:update to 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:update to 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