Visual Testing & Screenshots
Detect visual regressions automatically with Playwright's screenshot comparison, and generate PDF reports from your tests.
Screenshot-Based Visual Testing
Playwright's toHaveScreenshot() assertion compares the current appearance of a page or element against a stored baseline image. On the first run it creates the baseline; on subsequent runs it diffs the live screenshot against the baseline pixel by pixel and fails if the difference exceeds a configurable threshold. This catches unintended visual regressions that functional assertions miss.
npx playwright test --update-snapshots or pytest --update-snapshots to regenerate all baselines when you intentionally change the UI. Commit the updated snapshots alongside your code changes so reviewers can see the visual diff.Controlling Screenshot Scope
You can screenshot the entire page (fullPage: true), a single element (locator.screenshot()), or a clipped region. Masking dynamic regions — such as timestamps, ads, or user avatars — prevents spurious failures by replacing those areas with solid rectangles before comparison. Set a maxDiffPixelRatio to tolerate minor anti-aliasing differences across operating systems.
PDF Generation
Playwright can also print a page to PDF using page.pdf(). This is useful for generating reproducible PDF reports during test runs or as a side-effect of UI tests that exercise a "Print" or "Export" feature. PDF generation requires a Chromium browser and works only in headless mode.
import { test, expect } from '@playwright/test';
test('homepage matches visual baseline', async ({ page }) => {
await page.goto('/');
// Full-page screenshot comparison
await expect(page).toHaveScreenshot('homepage.png', {
fullPage: true,
maxDiffPixelRatio: 0.02,
});
});
test('product card matches baseline', async ({ page }) => {
await page.goto('/products');
// Mask dynamic price badge to avoid false failures
const card = page.getByTestId('product-card').first();
await expect(card).toHaveScreenshot('product-card.png', {
mask: [page.getByTestId('price-badge')],
});
});
test('element screenshot saved to disk', async ({ page }) => {
await page.goto('/chart');
await page.getByTestId('revenue-chart')
.screenshot({ path: 'test-results/revenue-chart.png' });
});
test('export page to PDF', async ({ page }) => {
await page.goto('/invoice/42');
await page.emulateMedia({ media: 'print' });
await page.pdf({
path: 'test-results/invoice-42.pdf',
format: 'A4',
printBackground: true,
});
});
import com.microsoft.playwright.*;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import java.nio.file.Paths;
import java.util.List;
class VisualTest {
void screenshotTest(Page page) {
page.navigate("/");
// Full-page screenshot
page.screenshot(new Page.ScreenshotOptions()
.setFullPage(true)
.setPath(Paths.get("test-results/homepage.png")));
// Element screenshot
page.navigate("/products");
Locator card = page.getByTestId("product-card").first();
card.screenshot(new Locator.ScreenshotOptions()
.setPath(Paths.get("test-results/product-card.png")));
// PDF generation
page.navigate("/invoice/42");
page.emulateMedia(new Page.EmulateMediaOptions()
.setMedia(Media.PRINT));
page.pdf(new Page.PdfOptions()
.setPath(Paths.get("test-results/invoice-42.pdf"))
.setFormat("A4")
.setPrintBackground(true));
}
}
from playwright.sync_api import Page, expect
def test_homepage_visual(page: Page, assert_snapshot):
page.goto("/")
# assert_snapshot from pytest-playwright-snapshot plugin
assert_snapshot(page.screenshot(full_page=True), name="homepage.png")
def test_element_screenshot(page: Page):
page.goto("/products")
card = page.get_by_test_id("product-card").first()
# Save element screenshot
card.screenshot(path="test-results/product-card.png")
def test_export_pdf(page: Page):
page.goto("/invoice/42")
page.emulate_media(media="print")
page.pdf(
path="test-results/invoice-42.pdf",
format="A4",
print_background=True,
)
def test_masked_screenshot(page: Page, assert_snapshot):
page.goto("/dashboard")
# Mask dynamic areas before comparison
screenshot = page.screenshot(
mask=[page.get_by_test_id("live-clock")]
)
assert_snapshot(screenshot, name="dashboard.png")
Written by PV
© 2026 All Rights Reserved