Writing stable visual tests
A visual test is only useful if an unchanged page produces an identical screenshot every time. Most of the work is removing sources of nondeterminism.
Same machine, same Chrome, same fonts
Text rendering depends on the OS, the Chrome version and the installed fonts. Baselines made on a laptop will not match CI. Generate baselines where you compare, ideally in CI with a pinned Chrome and a font set you control, and commit .snapdiff/baseline/. Use web fonts you ship (and document.fonts.ready, which snapdiff awaits) or fonts you know are installed; a CSS stack that falls through to "whatever the system has" will drift. snapdiff already pins the colour profile and disables hinting and LCD anti-aliasing.
Remove time and randomness
- Dates, clocks, relative times ("3 minutes ago"), random content, ads, carousels:
maskthem (a solid box is painted over the region, so layout is kept) orhidethem (visibility:hidden). - Prefer masking when the element's size is stable and hiding when only its pixels vary.
- Animations and transitions are disabled automatically, so you capture the end state. Videos, canvases and GIFs still need masking.
- If the app reads the date or random numbers, give it a test mode (a query parameter or a fixed seed) and put it in
path.
Wait for the right thing
In order of preference:
wait.selector: an element that only exists once the page is ready (#late,[data-loaded]).wait.network_idle_ms: no new resources for N ms; good for image-heavy pages.wait.delay_ms: last resort, and keep it small.
Pick the smallest useful screenshot
Use mode: element with a selector for components; a full-page shot of a long page changes whenever anything above the area of interest moves. Use several viewports only where the layout actually differs (the mobile viewport with mobile: true and device_scale_factor: 2 exercises responsive CSS).
Tune tolerance last
Defaults (threshold: 0.1, max_diff_percent: 0) are strict on purpose and already ignore anti-aliasing. If a page still flickers:
- Find the cause with the diff image (diff-only view in the report): moving shadows, sub-pixel layout, lazy images.
- Fix it with a mask or wait.
- Only then allow a small
max_diff_percentfor that page.
Raising threshold hides subtle colour changes everywhere; raising max_diff_percent hides small localised changes. Prefer neither.
Workflow tips
snapdiff runlocally, review the report,snapdiff approve NAMEfor intended changes, commit the baseline with the code change.- In CI run
snapdiff run --junit report.xml --markdown summary.md --strictand upload.snapdiff/report.htmlas an artifact.--strictalso fails on screenshots with no baseline, so new pages cannot slip in unreviewed. - Interactions: trigger state with
actions(hover,click,type) and capture the element, not the page.