Playwright Masters

Playwright Visual Testing: Screenshot Comparisons and CI Guide

Playwright visual testing compares a page or component screenshot with an approved baseline to identify unexpected appearance changes. It can reveal broken spacing, clipped text, missing images, and layout shifts. Reliable tests need predictable content, consistent rendering environments, and reviewed reference images. Use visual checks alongside functional and accessibility tests.

Imagine visiting an online shop where the checkout button works, but its label is clipped. A functional test might confirm that clicking the button opens checkout. The customer still sees an unfinished interface.

Visual testing helps teams investigate these appearance problems before users encounter them.

This guide explains how to create screenshot comparisons, review failures, manage dynamic content, test responsive layouts, and maintain useful baselines.

If you are beginning your automation journey, explore the Playwright Automation Testing Course in Hyderabad for course information and a structured learning path.

Playwright visual testing

What Is Playwright Visual Testing?

Playwright visual testing checks the rendered appearance of an interface against an accepted reference image.

That reference is called a baseline screenshot.

A difference means the interface changed. It does not automatically mean the change is a defect. A reviewer must decide whether the new appearance matches the intended design.

For example:

  • A heading wraps onto an extra line.
  • A pricing card becomes narrower.
  • A form label disappears.
  • A navigation menu overlaps another section.
  • A button loses its padding.

These changes can affect usability even when the underlying application functions correctly.

Visual Testing vs Functional and Accessibility Testing

Each testing approach answers a different question.

Approach

What it checks

Example

Functional testing

Behaviour and outcomes

Submitting a valid form shows confirmation

Visual testing

Appearance against an approved reference

The confirmation panel has the expected layout

Accessibility testing

Access and interaction requirements

A keyboard user can reach and operate the form

A screenshot cannot confirm that the server stored an enquiry. A successful submission cannot confirm that all text is readable.

Also, normal Playwright clicks perform actionability checks, including whether the element receives pointer events. A button covered by an overlay can therefore fail a functional test too. official actionability documentation

Use overlapping checks where they protect different requirements.

How Does Screenshot Comparison Work?

A useful workflow has five steps:

  1. Prepare a predictable page state.
  2. Confirm that the intended content has loaded.
  3. Capture the page or component.
  4. Compare it with the approved baseline.
  5. Investigate unexpected differences.

Playwright Test provides toHaveScreenshot() for these comparisons. It waits for two consecutive matching captures before comparing the result with the reference. This reduces capture instability, but does not prove that your application reached the intended state. official screenshot assertion documentation

A stable loading screen is still the wrong baseline if your requirement concerns the completed page.

Which Screens Should You Test First?

Start with interfaces where appearance affects an important user task.

Interface

State worth checking

User risk

Login form

Validation error displayed

Users cannot understand why login failed

Pricing card

All fee details visible

Users miss important information

Mobile navigation

Menu expanded

Links overlap or disappear

Checkout summary

Items and total displayed

Important amounts become unreadable

Enquiry form

Successful submission

Users miss confirmation

Avoid collecting screenshots without a clear purpose. Every additional baseline creates review and maintenance work.

Set Up Your First Playwright Visual Test

In a new practice folder, run:

npm init playwright@latest

Choose TypeScript during setup. The initializer installs the test package and creates a starter configuration. Replace the generated configuration with the one below. Then install Chromium:

npx playwright install chromium

Keep the generated lockfile in version control. Use npm ci when installing the same project in CI.

Record the installed version:

npx playwright –version

Use that exact package version when selecting the Docker image later in this guide. Updating the package, browser, or container is a change that requires baseline review.

Create or update playwright.config.ts:

import { defineConfig } from ‘@playwright/test’;

 

export default defineConfig({

  testDir: ‘./tests’,

 

  reporter: [

    [‘list’],

    [‘html’, { open: ‘never’ }],

  ],

 

  updateSnapshots: ‘none’,

 

  use: {

    browserName: ‘chromium’,

    viewport: { width: 1000, height: 700 },

    deviceScaleFactor: 1,

    colorScheme: ‘light’,

    locale: ‘en-US’,

    timezoneId: ‘UTC’,

  },

});

Setting updateSnapshots: ‘none’ makes ordinary comparison runs fail when a reference is missing; baseline generation remains a deliberate command. This configuration makes several rendering choices explicit. Fonts and operating-system differences can still affect the output.

For guidance on organizing configuration and reusable helpers, read the Playwright framework guide.

A Self-Contained Visual Testing Example

Create tests/course-card.visual.spec.ts:

import { test, expect } from ‘@playwright/test’;

 

test(‘course card matches its approved appearance’, async ({ page }) => {

  await page.setContent(`

    <!doctype html>

    <html lang=”en”>

      <head>

        <meta charset=”utf-8″>

        <style>

          body {

            margin: 0;

            padding: 24px;

            background: #f3f5f7;

            font-family: Arial, sans-serif;

          }

 

          article {

            box-sizing: border-box;

            width: 100%;

            max-width: 360px;

            padding: 24px;

            border-radius: 12px;

            background: #fff;

          }

 

          h1 {

            margin: 0 0 12px;

            font-size: 24px;

          }

 

          p {

            line-height: 1.5;

          }

 

          button {

            padding: 12px 18px;

            border: 0;

            border-radius: 6px;

            background: #126b42;

            color: #fff;

          }

        </style>

      </head>

 

      <body>

        <article aria-label=”Course card”>

          <h1>Learn Playwright</h1>

          <p>Practise browser automation with a sample project.</p>

          <button type=”button”>View syllabus</button>

        </article>

      </body>

    </html>

  `);

 

  const card = page.getByRole(‘article’, {

    name: ‘Course card’,

  });

 

  await expect(card.getByRole(‘heading’))

    .toHaveText(‘Learn Playwright’);

 

  await expect(card).toHaveScreenshot(‘course-card.png’);

});

The text assertion checks content. The screenshot assertion checks appearance.

This practice example avoids external images, live data, and remote fonts so the first exercise stays focused.

Create, Review, and Challenge the Baseline

Generate the reference screenshot deliberately:

npx playwright test tests/course-card.visual.spec.ts –update-snapshots

Open the resulting image. Check the heading, paragraph, spacing, and button before accepting it.

Run the normal comparison:

npx playwright test tests/course-card.visual.spec.ts

Now change only the article padding from 24px to 8px. Keep the body padding unchanged. Rerun the test without updating snapshots.

The card still has the same maximum width, but its content moves closer to the edges. Its inner width increases by 32 pixels. Text may wrap differently, and the card height can change. The heading text assertion can still pass because the words remain unchanged. The screenshot assertion is expected to detect the altered appearance.

This is a controlled exercise, not a claim that an execution result has been observed.

Inspect the report:

npx playwright show-report

Review the expected, actual, and difference images:

Evidence

What it represents

What to inspect

Expected image

The reviewed card with 24px article padding

Original spacing, text wrapping, and height

Actual image

The newly rendered card with 8px article padding

Content shifted toward the card edges

Difference image

The comparison evidence produced by the test runner

Changed pixels around text, button, and card boundaries

If image dimensions differ, inspect that mismatch too. A changed height is useful evidence, not something to hide with a larger tolerance.

For your project report, retain all three images and the test output. Label the actual image as an intentionally introduced defect. Record whether the test failed and why. Use screenshots generated by your own execution rather than a decorative illustration of a test result.

Restore the original padding if the change was accidental. Update the baseline only when the new design is correct and approved.

Commit reviewed baseline images to version control. Keep baseline generation and comparison environments consistent. official visual comparison guide

Handle Dynamic Content Without Hiding Defects

Changing dates, advertisements, prices, and account records can cause differences unrelated to a design change.

Prefer predictable test data for important content.

For an application with a matching product endpoint:

await page.route(‘**/api/product’, route =>

  route.fulfill({

    json: {

      name: ‘Practice Notebook’,

      price: 250,

    },

  })

);

 

await page.goto(‘/product’);

This fragment assumes a configured baseURL and the response shape your application expects.

Mask content only when it falls outside the requirement:

await expect(page).toHaveScreenshot(‘dashboard.png’, {

  mask: [page.getByTestId(‘live-clock’)],

});

Masking the checkout total would remove evidence needed to check its appearance. The masked region can also produce differences if its position or dimensions change.

Reuse stable setup through Playwright fixtures when several tests need the same preparation.

Wait for the Intended Page State

Use meaningful readiness checks:

await expect(

  page.getByRole(‘heading’, { name: ‘Order summary’ })

).toBeVisible();

 

await expect(

  page.getByTestId(‘loading-indicator’)

).toBeHidden();

 

await page.evaluate(() => document.fonts.ready);

Adapt these selectors to your application. Fonts being ready does not guarantee that images, charts, or API-driven content have finished loading.

Avoid arbitrary sleeps as the main readiness strategy.

Understand Animations and Screenshot Tolerances

Screenshot assertions default to animations: ‘disabled’. You can specify it explicitly to document the choice. Finite animations are fast-forwarded; infinite animations are cancelled for capture and resumed afterward. official screenshot assertion documentation

Test animation behaviour separately when it forms part of the requirement.

Comparison options also have distinct purposes:

Option

Meaning

threshold

Per-pixel perceived colour difference

maxDiffPixels

Allowed number of differing pixels

maxDiffPixelRatio

Allowed proportion of differing pixels

These options are documented in the screenshot assertion reference.

For example:

await expect(page).toHaveScreenshot(‘dashboard.png’, {

  maxDiffPixelRatio: 0.01,

});

A 1% allowance is not a universal recommendation. It can conceal an important small defect on a large image.

Stabilize the environment first, then justify any tolerance against the requirement.

Test Responsive Layouts and Browser Engines

Add projects to the earlier configuration:

projects: [

  {

    name: ‘desktop-chromium’,

    use: {

      browserName: ‘chromium’,

      viewport: { width: 1280, height: 800 },

    },

  },

  {

    name: ‘narrow-chromium’,

    use: {

      browserName: ‘chromium’,

      viewport: { width: 390, height: 844 },

    },

  },

  {

    name: ‘desktop-firefox’,

    use: {

      browserName: ‘firefox’,

      viewport: { width: 1280, height: 800 },

    },

  },

  {

    name: ‘desktop-webkit’,

    use: {

      browserName: ‘webkit’,

      viewport: { width: 1280, height: 800 },

    },

  },

],

Install the required browsers:

npx playwright install chromium firefox webkit

Review the baselines for every selected project.

A narrow Chromium viewport tests responsive layout. It does not reproduce every characteristic of a physical phone. Choose additional device coverage based on your users.

Check Authentication Before Protected Screenshots

For logged-in pages, verify that the intended dashboard loaded before capture.

Use a dedicated test account with predictable permissions and records. Reusable authentication state does not guarantee stable application data.

Follow the Playwright authentication guide for related setup.

Keep mutable records isolated when tests run in parallel.

Run Visual Checks in CI/CD

The central rule is simple: generate and compare baselines in the same rendering environment.

A reference created on Windows or macOS may differ from a Linux screenshot even when the application code is unchanged. Fonts, browser builds, and operating systems all matter.

Generate Linux baselines deliberately

For a trusted practice project, use the official Playwright container. The image supplies browsers and system dependencies; your project still needs its npm dependencies.

The following commands use a Bash-compatible shell. Replace YOUR_VERSION with the exact version shown by npx playwright –version, without the word “Version.” Select the corresponding supported image tag from the official Docker guide.

export VISUAL_PW_VERSION=YOUR_VERSION

 

docker run –rm –init –ipc=host \

  -v “$PWD:/work” \

  -w /work \

  “mcr.microsoft.com/playwright:v${VISUAL_PW_VERSION}-noble” \

  /bin/sh -c ‘npm ci && npx playwright test tests/course-card.visual.spec.ts –update-snapshots’

The mounted folder makes the generated PNG files available in your project. The container may create files owned by a different user; use a suitable user and writable dependency directory for your development environment when necessary.

Review the PNG files inside the test’s snapshots folder. Confirm that the correct content appears and the layout matches the intended design. Commit only approved references.

Run a normal comparison in the same container:

docker run –rm –init –ipc=host \

  -v “$PWD:/work” \

  -w /work \

  “mcr.microsoft.com/playwright:v${VISUAL_PW_VERSION}-noble” \

  /bin/sh -c ‘npm ci && npx playwright test tests/course-card.visual.spec.ts’

If you use multiple projects, create and review the reference for every selected project. Match the container architecture too where possible; using the same tag on different CPU architectures does not guarantee identical rendering.

Compare approved screenshots in GitHub Actions

Create .github/workflows/visual-tests.yml:

name: Visual tests

 

on:

  push:

  pull_request:

 

permissions:

  contents: read

 

jobs:

  visual:

    runs-on: ubuntu-24.04

    container:

      # Replace YOUR_VERSION with your exact Playwright package version.

      image: mcr.microsoft.com/playwright:vYOUR_VERSION-noble

      options: –ipc=host

 

    steps:

      – uses: actions/checkout@v6

 

      – uses: actions/setup-node@v6

        with:

          node-version: 22

          cache: npm

 

      – name: Install project dependencies

        run: npm ci

 

      – name: Compare approved screenshots

        run: npx playwright test tests/course-card.visual.spec.ts

 

      – name: Save reports and failure evidence

        if: ${{ !cancelled() }}

        uses: actions/upload-artifact@v5

        with:

          name: visual-test-evidence

          path: |

            playwright-report/

            test-results/

          retention-days: 14

Replace the image placeholder before running the workflow. The container package version must match your project. The workflow compares existing references; it does not approve new appearances automatically.

This example runs the course-card test across projects configured in your file. If you add Firefox or WebKit projects, generate and review their baselines in the same container first. See the official CI guide for platform-specific setup.

Review an intentional design update

When a designer approves a new appearance:

  1. Change the application or sample CSS.
  2. Regenerate only the affected test’s references in the agreed environment.
  3. Review old and new images alongside the requirement.
  4. Commit the code and approved baseline changes together.
  5. Run the ordinary comparison again.

Passing after a baseline update proves agreement with the new reference. The human review establishes whether that reference is correct.

Troubleshoot Visual Failures

Symptom

Investigate first

Text differs across many screenshots

Fonts, OS, browser version

Prices or names change

Uncontrolled test data

Loading state appears

Application readiness

Login form replaces dashboard

Authentication

Mobile text is clipped

Width and wrapping rules

Images are missing

Asset requests and loading

One browser fails

Browser-specific rendering

Failures follow an upgrade

Environment and dependency changes

Keep the failure evidence before changing anything.

A retry that passes can reveal instability. It does not explain or repair the cause.

Record Reproducible Test Evidence

A useful evidence note allows another tester to understand what was checked. Record:

  • Execution date and exact Playwright version.
  • Operating system or container tag and CPU architecture.
  • Browser project, viewport, and device scale factor.
  • Baseline-generation command and approval decision.
  • Unchanged comparison result.
  • Result after changing article padding from 24px to 8px.
  • Result after restoring the original padding.
  • Paths to the baseline, actual image, difference image, and report.

The expected sequence is an approved baseline, a passing unchanged comparison, a failing deliberate change, and a passing restored comparison. Record the observed sequence, including unexpected results. Do not turn an expected outcome into a claimed test result without running it.

The complete sample uses local HTML so it does not depend on a remote application or account. The routing, authentication, and readiness fragments elsewhere in this article need adaptation to your application.

Build a Useful Portfolio Project

Create a small course-enquiry page and cover:

  • Empty form.
  • Validation errors.
  • Successful submission.
  • Desktop and narrow-screen layouts.

Add functional assertions for required fields and confirmation.

Introduce deliberate layout defects, then record which checks detect them. Publish reviewed baselines, actual failure images, and an explanation of each test’s purpose.

Explore the Playwright projects guide for related practice ideas.

Frequently Asked Questions

What is the difference between visual testing and visual regression testing?

Visual testing evaluates appearance against a defined expectation. Visual regression testing uses an approved earlier appearance to detect unintended changes after an update. Playwright screenshot comparisons commonly support this regression workflow.

Can Playwright compare individual components?

Yes. A locator screenshot assertion can compare an element such as a card or form. This checks a rendered page element; it does not automatically set up a separate component-testing environment.

Is Playwright visual testing free?

Native screenshot comparison is included in Playwright Test. Optional hosted services may charge for additional review and collaboration features.

Is taking a screenshot enough?

No. A screenshot records appearance. A visual check evaluates it against a reference or another defined expectation.

Why does a test pass locally but fail in CI?

Rendering environments or page states may differ. Check fonts, operating system, browser versions, data, and loading conditions before increasing tolerances.

Should every test include a screenshot?

No. Add screenshots where appearance protects a meaningful requirement. Keep behaviour checks focused on outcomes.

Can screenshots prove accessibility compliance?

No. They can reveal visible problems, but cannot establish keyboard access or screen-reader behaviour. Use dedicated accessibility checks.

When should a baseline be updated?

When the appearance change is intentional, correct, and reviewed—not simply because a comparison failed.

Build Visual Tests Around User Needs

A useful visual test protects something a user depends on: readable text, visible controls, clear validation, or a stable layout.

Start with one important component. Prepare predictable data, review its baseline, introduce a defect, and examine the evidence. Expand coverage when another screenshot protects a clear requirement.

For a structured learning path covering assertions, fixtures, authentication, and practical automation projects, explore Playwright training at Playwright Masters.

Playwright Masters automation testing logo - White Back ground

Playwright Masters Team

Playwright Automation Testing Experts | Industry-Focused Training & Practical Learning

Playwright Masters is a dedicated automation testing training platform focused on helping learners build practical skills in Playwright automation testing. Our training covers real-world automation concepts, framework practices, coding, debugging, API testing, cross-browser testing, CI/CD, and interview preparation to help learners develop job-ready testing skills.

Scroll to Top

Start Your Playwright Automation Career Today

Get FREE Demo + Syllabus & Become Job-Ready in Playwright.