← Back to libraryQuestion 181 of 273
🎭PlaywrightIntermediate

Visual Regression / Screenshot Testing

📌 Definition:

Playwright's toHaveScreenshot() assertion captures a screenshot and compares it pixel-by-pixel against a committed baseline, failing when the rendered UI drifts beyond a configured threshold. It's built in — no third-party visual service required for basic coverage.

📖 Detailed Explanation:

Functional assertions can't catch a broken layout, a wrong color, or an overlapping element that still has the right text. Visual testing fills that gap. On first run, toHaveScreenshot() writes a baseline; later runs diff against it and produce an actual/expected/diff triplet on mismatch. Because rendering varies by OS and browser, baselines are stored per platform/project, and CI should generate them on the same OS it runs on (often via a Linux container or the official Playwright Docker image) to avoid font/anti-aliasing noise. You reduce false positives by masking dynamic regions (timestamps, avatars), setting maxDiffPixels/threshold, and disabling animations. Update baselines intentionally with --update-snapshots when a change is legitimate.

🔑 Key Points:
  • expect(page).toHaveScreenshot() diffs against a committed baseline image
  • Catches layout/color/overflow bugs that text assertions miss
  • Baselines are platform/browser-specific — generate them on the CI OS to avoid noise
  • Reduce flakiness: mask dynamic areas, set threshold/maxDiffPixels, disable animations
  • Update baselines deliberately with --update-snapshots on intended changes
🌍 Real-World Example:

A CSS refactor accidentally pushed the checkout total off-screen on mobile widths. Every functional test still passed (the text was present), but the mobile visual snapshot failed with a clear diff highlighting the misplaced total — catching a bug that assertions alone would have shipped.

📎 Code Example:
// First run creates the baseline; later runs diff against it
await expect(page).toHaveScreenshot('checkout.png', {
  maxDiffPixels: 100,
  mask: [page.getByTestId('order-timestamp')], // ignore dynamic text
  animations: 'disabled',
});

// Component-level snapshot
await expect(page.getByRole('dialog')).toHaveScreenshot('dialog.png');

# Regenerate baselines after an intended UI change
npx playwright test --update-snapshots
🎯 Scenario-Based Interview Question:

Scenario: A newly added visual test passes on a developer's Mac but fails on Linux CI with thousands of pixel diffs, even though the UI looks identical. What's wrong, and how do you make visual testing reliable across environments?