Q30 of 42 · Playwright
How would you architect a Playwright suite for cross-browser visual regression?
Short answer
Short answer: Use `expect(page).toHaveScreenshot()` with per-project baselines (Playwright auto-keys baselines by project name). Mask volatile elements, freeze time, disable animations. Store baselines in git (small set) or S3 (large set). Update intentionally via `--update-snapshots` in a dedicated PR-affecting workflow.
Detail
Playwright has built-in visual regression — no plugin needed. The architecture:
Step 1: Configure projects for the browsers you want visual coverage on:
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
]
Step 2: Snapshots are auto-keyed by project:
await expect(page).toHaveScreenshot('home.png', { maxDiffPixels: 50 });
Generates home-chromium.png, home-firefox.png, home-webkit.png. Each project compares against its own baseline.
Step 3: Stabilise the page before snapshotting:
await page.clock.setFixedTime(new Date('2026-01-01')); // freeze time-derived UI
await page.addStyleTag({ content: '* { animation: none !important; transition: none !important; }' });
await page.waitForLoadState('networkidle'); // wait for everything to load
await expect(page.getByTestId('spinner')).not.toBeVisible();
Step 4: Mask volatile elements that genuinely change per run:
await expect(page).toHaveScreenshot('home.png', {
mask: [
page.getByTestId('relative-time'),
page.getByTestId('user-avatar'),
],
});
Step 5: Storage strategy:
- Git: easy reviews (diffs show in PRs), but baselines bloat the repo. Use git LFS once you exceed ~50MB.
- S3 / object storage: scales better, but PR review of baseline changes is harder. Tools like Reg-Suit or homegrown scripts handle the upload/diff.
Step 6: Update workflow:
# PR fails because of an intentional UI change
npx playwright test --update-snapshots
git add tests/__screenshots__
git commit -m "Update visual baselines for new pricing card"
The new baselines ship in the same PR as the UI change, with the diff in code review.
Step 7: CI gating:
- Run visuals on chromium-only on PR (fast feedback).
- Full cross-browser visual on PR-to-main or nightly.
- Block merge on visual diffs above tolerance.
Watch-outs:
- Cross-browser will diverge. Even a stable page renders pixel-differently across engines (font hinting, scrollbar width). Set
maxDiffPixelsper project; webkit usually needs the highest tolerance. - CI vs local mismatch. Different OSs render fonts differently. Standardise on a Docker base image (
mcr.microsoft.com/playwright:v...) so local-vs-CI diffs go away. - Don't snapshot every page. 50 well-chosen snapshots beat 500 bot-generated ones.