Good news. Playwright has visual testing built right in. No plugins. No third-party services. No extra dependencies. One method: toHaveScreenshot(). That is all you need to start.
First run -- no baseline exists. Playwright takes a screenshot and saves it. Test fails with "missing snapshot" message. This is expected.
Second run -- baseline exists. Playwright takes a new screenshot and compares it pixel by pixel against the baseline. If they match, test passes.
Future runs -- same comparison. Any visual change causes the test to fail with a diff image showing exactly what changed.
import { test, expect } from '@playwright/test';
test('login page looks correct', async ({ page }) => {
await page.goto('/banking/login');
// That is it. One line. Playwright handles everything.
await expect(page).toHaveScreenshot();
});When you run this the first time, Playwright creates a folder structure like this:
tests/
visual-basic.spec.ts
visual-basic.spec.ts-snapshots/
login-page-looks-correct-1-chromium-darwin.png ← baseline
login-page-looks-correct-1-chromium-linux.png ← different per OSNotice the OS name in the file (darwin, linux, win32). Font rendering is different across operating systems. A screenshot taken on macOS will NOT match one taken on Linux. This matters hugely for CI/CD. We will cover the solution in lesson 9.
By default, Playwright auto-generates the snapshot name from the test title. You can pass a custom name for clarity.
test('banking portal pages', async ({ page }) => {
await page.goto('/banking/login');
// Custom name -- easier to find in the snapshots folder
await expect(page).toHaveScreenshot('banking-login.png');
await page.goto('/banking');
await expect(page).toHaveScreenshot('banking-dashboard.png');
});| File | Purpose | Location |
|---|---|---|
| Baseline (golden image) | The approved screenshot | test-file-snapshots/ folder |
| Actual screenshot | What the test just captured | test-results/ folder (on failure) |
| Diff image | Highlights differences in pink | test-results/ folder (on failure) |
| Expected image | Copy of baseline for comparison | test-results/ folder (on failure) |
When a visual test fails, check the test-results/ folder. Playwright generates three images: expected (baseline), actual (what it saw), and diff (differences highlighted in pink). The diff image is gold -- it shows you exactly which pixels changed.
Q: How does Playwright store and compare visual snapshots?
A: Playwright stores baseline screenshots in a snapshots folder next to the test file, with the OS and browser name in the filename. On each run, it takes a new screenshot and compares it pixel by pixel against the baseline. If the diff exceeds the configured threshold, the test fails. It generates three output images -- expected, actual, and diff -- so you can see exactly what changed.
Key Point: toHaveScreenshot() handles everything -- baseline creation, pixel comparison, and diff generation. One method, zero plugins.