Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Visual regressions

crates/ui/tests/visual_regression.rs renders real components and independent reference painters with the same offscreen wgpu renderer and compares the results. The reference painters never call the components: they draw literal geometry with plain egui painter calls. Together they define the visual contract of each control; they are not screenshots of an app.

Coverage

SuitePairsWhat is compared
Controls372icon button, single-shape button, tab, horizontal and vertical split × light/dark × scales 1, 1.5, 2 × normal and compact sizes × the states normal, hover, press, disabled, focus, plus selected and selected-press for buttons and tabs
Controls and content kit282controls::Button (primary, secondary, ghost, destructive), Switch, a RadioGroup option, Progress::bar (25 %, 100 %, the reduced-motion indeterminate pose), the content::Collapsible header and a dismissible content::Banner (info, warning, danger) × light/dark × scales 1, 1.5, 2 × the states normal, hover, press, disabled, focus, plus selected (on, chosen, open) and selected-press for the switch, radio and collapsible; drawn with the built-in token sets and no labels
Toolbar overflow18three widths × two themes × three scales against literal rects
Widget palette12cells painted from WidgetPalette::apply_to against literal painting
SVG icons12catalog output rasterized by the SVG loader against literal, pre-substituted SVGs

All 696 positive pairs must give identical RGBA pixels. The 684 pairs rendered with wgpu must also give identical meshes: vertices (position, UV, colour), indices, texture ids and clip rects. The SVG pairs compare the loader’s rasters and must contain visible pixels.

Each state is reached with real input, and the test asserts it was reached: the control is hovered, held down (and dragging, for split handles), or focused. Disabled controls must stay unhovered and unfocused under the same input. Selection wins over press. Split handles are driven in both orientations. The compact fixtures repeat every contract at a smaller size.

48 negative comparisons change one thing in the reference (fill colour, spacing, stroke width; the fill of a kit button, switch, radio, bar and banner, the collapsible’s chevron) and must differ in both pixels and meshes, which shows the comparison can fail. There is no tolerance, mask, crop, perceptual metric or automatic update. The toolbar fitting itself (malformed input, reachable overflow, groups kept in order) is covered by unit tests in the layout module.

Running

cargo test -p ashlar-ui --test visual_regression

With ASHLAR_UI_VISUAL_DIR=/absolute/path, each suite writes into its own subdirectory: <case>-before.png (reference), <case>-after.png (component), <case>-difference.png (changed pixels in magenta, 255,0,128) and comparison.tsv with the columns case, width, height, changed_pixels, mesh_equal and expected_change. Rows are appended as each pair finishes, so the file is useful even when a later pair fails. Images are at most 256×144 pixels. A renderer failure fails the test.

Limits

The comparison is same-machine only: both sides go through the same driver in the same run, so it says nothing about pixel identity across GPUs or drivers. It does not replace app screenshots, complete themes, text layout checks, command dispatch, window resizing, tests against real accessibility services, or cross-platform runtime tests. Apps that keep their own baseline images keep their own record of where those came from.