IntermediateSDET

Set up visual regression testing with Playwright screenshots

Build a visual regression test suite using Playwright's built-in screenshot comparison. Capture baseline screenshots for a web application's key views, introduce a deliberate visual change, verify the test detects it, and configure the suite to run in CI with a tolerance threshold and a diff image artifact on failure.

Why this matters

Functional tests verify that a button works; visual regression tests verify that it still looks right. CSS changes, dependency upgrades, and font loading issues cause visual regressions that no functional test catches. A screenshot-based regression suite is the closest thing to automated visual QA; it does not replace human review, but it catches the regressions nobody would notice until a user complained.

Before you start

Step-by-step guide

  1. 1

    Write the first screenshot test

    Use toHaveScreenshot() in a Playwright test. Run it once; Playwright creates the baseline PNG in __screenshots__. Commit it to the repository. Run again; the test should pass. This is the full flow: generate baseline, commit, assert on subsequent runs.

  2. 2

    Capture baselines for 5 key views

    Write screenshot tests for: homepage, a listing page, a detail page, a form in empty state, and a form showing a validation error. Run all 5 once to generate baselines. Name them clearly; the file name is what you read in the CI failure log.

  3. 3

    Set a pixel diff threshold

    Add { maxDiffPixels: 100 } to toHaveScreenshot() for views with dynamic content (timestamps, animated elements). For static views, use maxDiffPixelRatio: 0 to catch any change. Tight thresholds generate false positives; loose thresholds miss real regressions; calibrate per view.

  4. 4

    Introduce a deliberate regression

    Change a CSS value (background colour, font size, spacing) in the app. Run the screenshot tests. At least one should fail with a diff image. Open the diff image; the changed pixels are highlighted in red. Verify you can see the change clearly. Revert and verify all tests pass again.

  5. 5

    Handle dynamic content

    Some views contain content that changes on every load (timestamps, random data). Use page.evaluate to freeze time before screenshotting, or mask the dynamic region with toHaveScreenshot({ mask: [page.locator('.timestamp')] }). Run the test 3 times and verify it is stable.

  6. 6

    Add to CI

    Configure the Playwright test to run in GitHub Actions. Store the baseline screenshots in the repository. On failure, use the actions/upload-artifact step to capture diff images as build artifacts. The workflow is: PR opens, CI runs, screenshot fails, diff image is attached, reviewer decides if the change is intentional.

Relevant Axiom pages

What to do next

Back to Practice Lab