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

Wait for the right thing

In order of preference:

  1. wait.selector: an element that only exists once the page is ready (#late, [data-loaded]).
  2. wait.network_idle_ms: no new resources for N ms; good for image-heavy pages.
  3. 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:

  1. Find the cause with the diff image (diff-only view in the report): moving shadows, sub-pixel layout, lazy images.
  2. Fix it with a mask or wait.
  3. Only then allow a small max_diff_percent for that page.

Raising threshold hides subtle colour changes everywhere; raising max_diff_percent hides small localised changes. Prefer neither.

Workflow tips