Visual Testing with Playwright: The Complete Tutorial

Published: (April 23, 2026 at 04:05 AM EDT)
3 min read
Source: Dev.to

Source: Dev.to

Since version 1.22, Microsoft’s Playwright includes a native visual testing feature: the toHaveScreenshot() method. It captures screenshots and automatically compares them to reference images, with no external plugin needed. This is a strong option for development teams that want to add visual testing to their existing stack. The tutorial covers installation, configuration, best practices, and CI/CD integration.

Installation and first test

Setup is quick with an existing Node.js project:

npm install -D @playwright/test
npx playwright install

Create your first visual test in tests/visual.spec.ts:

import { test, expect } from '@playwright/test';

test('homepage visual test', async ({ page }) => {
  await page.goto('https://your-site.com');
  await expect(page).toHaveScreenshot('homepage.png');
});

The first run generates the baseline. Subsequent runs compare against it.

Configuring tolerance

By default, Playwright flags any single‑pixel difference. In practice, configure thresholds to avoid false positives:

// playwright.config.ts
export default defineConfig({
  expect: {
    toHaveScreenshot: {
      maxDiffPixelRatio: 0.01,
      animations: 'disabled',
      scale: 'device',
    },
  },
});

Handling dynamic content

Three common solutions:

  1. Mask dynamic zones – use the mask option to ignore changing parts of the page.
  2. Replace content via page.evaluate() – inject deterministic data before taking the screenshot.
  3. Hide with injected CSS – add a style block that hides or stabilizes animated elements.

Each approach balances reliability and maintenance effort.

Stabilizing tests

  • Wait for network idle, font loading, and critical element visibility before capturing.
  • Disable animations globally in the Playwright config (as shown above).
  • Use explicit waits or page.waitForLoadState('networkidle') when needed.

Multi‑resolution testing

Leverage Playwright projects to run the same test suite across different viewports and devices, each with its own baseline:

// playwright.config.ts
export default defineConfig({
  projects: [
    {
      name: 'Desktop',
      use: { viewport: { width: 1920, height: 1080 } },
    },
    {
      name: 'Tablet',
      use: { viewport: { width: 768, height: 1024 } },
    },
    {
      name: 'Mobile',
      use: { device: 'iPhone 13' },
    },
  ],
});

CI/CD integration

GitHub Actions (or any CI system) can run visual tests automatically. When a test fails, Playwright generates three images:

  • Baseline – the stored reference screenshot.
  • Actual – the newly captured screenshot.
  • Diff – a red‑highlighted image showing pixel differences.

The HTML report displays these side‑by‑side, making it easy to review failures in the pipeline.

Limitations

  • Requires TypeScript/JavaScript knowledge.
  • No built‑in review dashboard; you rely on the generated HTML report.
  • Pixel comparison is basic—anti‑aliasing and font‑rendering differences between browsers can cause noise.
  • Managing baselines for many tests across multiple browsers can lead to a large number of files (e.g., 200 + tests × 3 browsers = 600 + baseline images).

For teams that prefer a no‑code approach, consider Delta‑QA, which offers visual regression testing without writing code, managing baselines, or dealing with false positives.

FAQ

Is Playwright free for visual testing?

Yes. toHaveScreenshot() is built into Playwright, which is open‑source and free to use.

Playwright or Cypress for visual testing?

Playwright provides native visual testing, while Cypress requires an external plugin. Playwright also supports three browser engines (Chromium, Firefox, WebKit) versus Cypress’s single‑engine focus, making Playwright the stronger choice for visual testing.

Can you use Playwright and Delta‑QA together?

Absolutely. Use Playwright for complex developer‑focused tests and Delta‑QA for routine visual checks performed by QA teams.

0 views
Back to Blog

Related posts

Read more »