Visual regression
On this page
Visual regression compares a screenshot of the current page against a stored baseline and records the result as pass or fail on the run. Use it to guard against unintended visual changes on a page you have already validated.
Running it in CI? See Visual regression in CI for the baseline-cache pattern, Job Summary snippet and the two options to publish the HTML report.
#Take a checkpoint
Call visualCheckpoint($label) at any point in a step where you want to compare
against a baseline. The label identifies the checkpoint across runs.
1$this2->it('the checkout addresses step is unchanged', function () use ($frontOfficeCheckoutPage) {3 $frontOfficeCheckoutPage->confirmAddresses();4 5 $this->visualCheckpoint('checkout-tunnel-addresses');6});
The first time this checkpoint is seen, the screenshot is stored as the
baseline and the check reports pass. On subsequent runs, the current
screenshot is diffed against the baseline: identical → pass, above the
similarity threshold → pass, below the threshold → fail.
#Full-page or viewport
By default visualCheckpoint captures the full page (including content
scrolled out of view). Pass false as the second argument to capture only the
current viewport.
1// Full-page (default): great for landing pages and long tunnels.2$this->visualCheckpoint('cart');3 4// Viewport-only: great for a header, a toolbar, a modal.5$this->visualCheckpoint('order-shipping-modal', false);
#Frame a viewport capture on an anchor
Viewport-only captures show whatever happens to be at the top of the page — often
a header, a promo banner or a cookie strip, not the piece you actually want to
compare. CommonPage::scrollToAnchor($selector, $settleMs = 400) scrolls the
matching element to the top of the viewport before the capture, so the frame
lands on the business content.
1$this->frontOfficePage->waitFor('.login-form'); // guarantee presence2$this->frontOfficePage->scrollToAnchor('.login-form');3$this->frontOfficePage->visualCheckpoint('login', null, 0.98, false);
Best-effort by design: if the selector is absent, the call is a no-op — the
library doesn't know your project's DOM, so it's up to the suite to guarantee
the anchor is present (via waitFor or similar) before calling. The optional
$settleMs gives the browser a moment for a smooth-scroll animation to finish
before the screenshot is taken.
#Baselines
Baselines are stored in a persistent folder (outside the ephemeral screens/
folder), so they survive between runs. Commit the baseline files alongside the
suites that produce them so CI can compare against them.
To refresh a baseline after an intentional visual change, delete the file(s) for the affected checkpoint(s) and re-run the suite: the next run stores the new screenshot as the baseline.
#Report
Ask the CLI to write an HTML report at the end of the run:
1bin/prestaflow run tests --visual-report
Or pick a custom path:
1bin/prestaflow run tests --visual-report=reports/visual/2026-07-06.html
A visual-results.json side-file is written next to the HTML report. The report
shows the reference, the actual capture and the diff side by side for each
failing checkpoint.
#Time zone of the stamp
The report includes a "generated at" timestamp. By default it is in UTC; set
PRESTAFLOW_TZ (or pass --visual-report-tz=…) to render the human timestamp
in your local zone (the ISO attribute keeps its offset).
1PRESTAFLOW_TZ=Europe/Paris bin/prestaflow run tests --visual-report