Sign inSign up

How to visual regression test with Playwright (with examples)

Seamless walkthrough for Playwright visual testing

loading
Kyle Gach

Updated: Feb 16, 2024

🎭
Chromatic’s Playwright integration is now available to try in public beta. Try our Playwright visual testing integration

Playwright is a Microsoft NodeJS framework and test runner that's perfect for verifying your app's user flows and functionality. However, did you know you can also configure Playwright to validate your application's appearance?

In this guide, learn how to unlock visual regression testing in Playwright through Playwright’s visual comparison. Explore how to run visual tests with Playwright's default configuration, and how to capture interactive snapshots of your test cases at any test run stage.

What is visual testing?

Visual regression testing (or visual testing for short) involves checking and verifying the visual elements of your application's UI, such as responsive design, cross-browser/device differences, localization, dynamic content, and more. Visual testing tools conduct these tests through snapshot comparison, diffing baselines of how your UI should look against new snapshots of UI changes.

This test methodology is crucial to modern developer teams because most testing tools that specialize in application functionality are inefficient for catching visual changes. You'd need hundreds or thousands of assertions to cover visual testing across your entire application. It's like playing the piano with gloves on: hard work and inefficient.

Can you visual test in Playwright?

Yes! Playwright offers visual comparison and snapshot testing features, which allow developers to capture screenshots of web pages or individual elements in a test run and compare them to baseline images to catch unexpected visual regressions.

Get started with Playwright snapshot testing

To begin snapshot testing in Playwright, let’s start with an example end-to-end test that opens a dialog and then clicks the close button:

Dashboard of our app, with an open dialog showing a chart.
// tests/dashboard.spec.ts

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

test("Dashboard", async ({ page }) => {
  await page.goto("/dashboard/acme");

  await expect(page).toHaveTitle(/Acme Dashboard/);

  const expandButton = await page.locator(
    ".main .card:nth-child(0) .btn-expand",
  );

  await expandButton.click();

  const dialog = await page.locator(".dialog");

  const closeButton = await dialog.locator(".btn-close");

  await closeButton.click();
});

Playwright provides the page.screenshot API to take screenshots of the page you’re testing. To use it, add a line that calls the function at the part of the flow where a screenshot should be taken:

// tests/dashboard.spec.ts
import { test, expect } from "@playwright/test";

test("Dashboard", async ({ page }) => {
  await page.goto("/dashboard/acme");

  await expect(page).toHaveTitle(/Acme Dashboard/);

  const expandButton = await page.locator(
    ".main .card:nth-child(0) .btn-expand",
  );

  await expandButton.click();

  const dialog = await page.locator(".dialog");

  // 👇 Take a screenshot once the dialog is located
  page.screenshot({ path: "latencyExpanded.png" });

  const closeButton = await dialog.locator(".btn-close");

  await closeButton.click();
});

page.screenshot has a couple of options that may be useful to you. You can capture the full page height (instead of just the viewport height) and you can configure where the screenshot is saved:

await page.screenshot({
  fullPage: true, // Capture full page height
  path: 'screenshot.png', // Provide save location
});

You can also screenshot a particular element instead of the entire page:

await page.locator('.dialog').screenshot(...);

Once you’ve updated your test, run your test command to save a screenshot:

yarn playwright test

In our example, the screenshot will save in the root of the project:

VS Code, showing the contents of dashboard.spec.ts from the snippet above and the newly-created latencyExpanded.png in the file explorer sidebar

Open the screenshot to confirm that it looks correct:

VSCode showing the latencyExpanded.png screenshot

Now, commit both your code change and the newly-created screenshot. Then push your commit, making the screenshot your baseline.

Run Playwright visual tests

Next, we’ll use that baseline screenshot to verify a change.

First, create a new branch and make a code change. For this example, we’ll change our primary chart color:

- chartPrimary: '#FD7748',
+ chartPrimary: '#1EA7FD',

After making the change, run your test command again to save a new screenshot. Once more, open the screenshot to confirm it looks correct.

Commit your changes and make a PR for your new branch.

Now, when we review that PR, we can compare the previous baseline screenshot and the modified one:

Automate tests in CI

In the workflow so far, we’ve created the screenshots on a local machine and then committed them to your repo. This can cause problems if you're working with other developers on the same E2E tests, as device differences like installed fonts could cause unnecessary image differences.

To mitigate this, take your screenshots within your continuous integration (CI) environment, and let your CI job handle the screenshot and committing process for you. Then, you can review the results as part of the PR review, as before.

Here’s an example using GitHub Actions:

# .github/workflows/e2e.yml
name: E2E tests
on: push
permissions:
  contents: write
jobs:
  E2E:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v3
        with:
          node-version: '16'
      - uses: actions/checkout@v3
      - name: Install dependencies
        run: yarn
      - name: Install Playwright Browsers
        run: yarn playwright install --with-deps
      - name: Run tests
        run: yarn playwright test

      - name: Update E2E screenshots
        run: |
          git config --global user.name 'Your Name'
          git config --global user.email 'you@example.com'
          git commit -am "Update screenshots"
          git push

The screenshots are still part of your repo (which can take up a lot of space if you have many tests), but now they’re created in a shared, consistent environment, eliminating any device differences.

Debugging an issue can be difficult in this flow. A static screenshot often doesn’t provide enough information, and debugging the real page requires navigating through a staging environment and recreating any interactions that took place.

Reviewing and collaborating with non-developers can also be tricky, because it all happens within the PR experience, which many are unfamiliar with.

Debuggable snapshots and collaborative review

You can take this workflow even further when you integrate Playwright with Chromatic, a cloud-based visual testing platform from the creators of Storybook. It gives you interactive snapshots of your UI for significantly improved test results and collaborative review, like GitHub for visual testing.

Here’s how to use Chromatic in our previous example:

// tests/dashboard.spec.ts
// ➖ Remove this line
// import { test, expect } from '@playwright/test';
// ➕ Add this line
import { test, expect, takeArchive } from "@chromaui/test-archiver";

test("Dashboard", async ({ page }, testInfo) => {
 await page.goto("/dashboard/acme");

 await expect(page).toHaveTitle(/Acme Dashboard/);

 const expandButton = await page.locator(
   ".main .card:nth-child(0) .btn-expand"
 );

 await expandButton.click();

 const dialog = await page.locator(".dialog");

 // 👇 Take a screenshot once the dialog is located
 // ➖ Remove this line
 // page.screenshot();
 // ➕ Add this line
 await takeArchive(page, testInfo);

 const closeButton = await dialog.locator(".btn-close");

 await closeButton.click();
});

And then the CI job (using GitHub Actions here) would be:

# .github/workflows/e2e.yml
name: E2E tests
on: push
jobs:
  E2E:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - name: Install dependencies
        run: yarn
      - name: Install Playwright Browsers
        run: yarn playwright install --with-deps

      # 👇 Run your E2E tests *before* running Chromatic for your E2E test archives
      - name: Run tests
        run: yarn playwright test

      # 👇 Run Chromatic for your E2E test archives
      - name: Publish E2E Archives to Chromatic
        uses: chromaui/action@v1
       with:
         projectToken: ${{ secrets.CHROMATIC_ARCHIVE_PROJECT_TOKEN }}
         buildScriptName: build-archive-storybook

Now, instead of creating a static screenshot, running your test will create a fully debuggable snapshot of the page that you can open in the browser.

These snapshots are created in a dedicated, specialized environment —making the process fast and consistent —and saved in Chromatic’s database instead of cluttering your project’s repo.

Once in Chromatic, snapshots can be easily shared with all of your project's stakeholders and contributors. There’s a purpose-built web app for reviewing visual tests, which integrates with your git provider.

Right now, Chromatic's Playwright E2E integration is available in beta. Try visual testing for Playwright today! Alternatively, check out our breakdown of other visual testing tools.

Did this article help you?

Get free UI development guides and tutorials like this emailed to you.

4,472 developers and counting

We’re hiring!

Join the team behind Storybook and Chromatic. Build tools that are used in production by 100s of thousands of developers. Remote-first.

View jobs

Popular posts

E2E Visual Tests public beta

Specialized visual testing for Cypress and Playwright
loading
Dominic Nguyen
Product
PricingAboutJobsTerms of ServicePrivacyStatusSecurity • SOC 2Contact Sales
Chromatic
© Chroma Software Inc. Made by the maintainers of Storybook.