Visual regression testing with Playwright

Visual regression testing with Playwright

Introduction

The Cypress visual post wired up cypress-image-snapshot. Playwright ships first-party screenshot assertions: expect(page).toHaveScreenshot() and expect(locator).toHaveScreenshot().

Previous post: Cross-browser testing.

First baseline

import { test, expect } from "@playwright/test"

test("home after login", async ({ page }) => {
  // seed + login steps from earlier articles
  await page.goto("/login")
  // ...
  await expect(page).toHaveScreenshot({ fullPage: true })
})

The first run stores golden images next to your tests (*-snapshots/ by default). Later runs diff pixels; failures drop artifacts under test-results/.

Component-level shots

await expect(page.getByTestId("sign-in-button")).toHaveScreenshot()

Use this when the full page is too noisy (animations, third-party widgets).

Refreshing baselines

After an intentional UI change:

npx playwright test --update-snapshots

Same workflow as Cypress’s updateSnapshots - review and commit PNGs deliberately.

Thresholds

Tune flakiness from fonts or GPU differences:

await expect(page).toHaveScreenshot({ maxDiffPixels: 100 })

Or set defaults through expect in playwright.config.ts.

Reference imagery

The Cypress article’s diff screenshots still illustrate the idea; only the API differs.

Historical Cypress diff example

Git (Cypress era)

https://github.com/12masta/react-redux-realworld-example-app/tree/7-cypress

https://github.com/12masta/react-redux-realworld-example-app/pull/9/files

Next: Playwright on Azure DevOps CI.