Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/microsoft/playwright/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Playwright can capture screenshots and record videos of your tests, providing visual evidence of test execution and helping debug failures.

Screenshots

Full Page Screenshot

Capture a screenshot of the entire page:
import { test, expect } from '@playwright/test';

test('capture full page', async ({ page }) => {
  await page.goto('https://example.com');
  await page.screenshot({ 
    path: 'screenshot.png',
    fullPage: true 
  });
});

Element Screenshot

Capture a screenshot of a specific element:
test('capture element', async ({ page }) => {
  await page.goto('https://example.com');
  
  const element = page.locator('.main-content');
  await element.screenshot({ path: 'element.png' });
});

Screenshot Options

await page.screenshot({
  path: 'screenshot.png',
  fullPage: true,
  type: 'png', // 'png' or 'jpeg'
  quality: 100, // 0-100 (jpeg only)
});

Visual Regression Testing

Compare Screenshots

Playwright includes built-in visual comparison:
import { test, expect } from '@playwright/test';

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

Update Baseline Screenshots

When intentional UI changes are made, update baselines:
npx playwright test --update-snapshots

Configure Visual Comparison

Customize comparison tolerance in playwright.config.ts:
import { defineConfig } from '@playwright/test';

export default defineConfig({
  expect: {
    toHaveScreenshot: {
      maxDiffPixels: 100,
      maxDiffPixelRatio: 0.1,
      threshold: 0.2,
    },
  },
});

Video Recording

Configure Video Recording

Enable video recording in your configuration:
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    video: 'on', // Record all tests
    // video: 'retain-on-failure', // Only failed tests
    // video: 'on-first-retry', // Record on retry
    videoSize: { width: 1280, height: 720 },
  },
});

Video Recording Options

1

Always record

use: {
  video: 'on',
}
Records video for all tests.
2

Record failures only

use: {
  video: 'retain-on-failure',
}
Only keeps videos of failed tests.
3

Record on retry

use: {
  video: 'on-first-retry',
}
Records when tests are retried.
4

Never record

use: {
  video: 'off',
}
Disables video recording.

Access Video Path

Get the video path programmatically:
import { test } from '@playwright/test';

test('with video', async ({ page }) => {
  await page.goto('https://example.com');
  // ... test actions ...
});

test.afterEach(async ({ page }, testInfo) => {
  if (testInfo.status === 'failed') {
    const videoPath = await page.video()?.path();
    console.log('Video saved to:', videoPath);
  }
});

Screenshot on Failure

Automatic Screenshots

Configure automatic screenshots for failed tests:
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    screenshot: 'only-on-failure',
    // screenshot: 'on', // Always capture
    // screenshot: 'off', // Never capture
  },
});

Custom Failure Handler

Implement custom screenshot logic:
import { test } from '@playwright/test';

test.afterEach(async ({ page }, testInfo) => {
  if (testInfo.status !== testInfo.expectedStatus) {
    const screenshot = await page.screenshot();
    await testInfo.attach('screenshot', { 
      body: screenshot, 
      contentType: 'image/png' 
    });
  }
});

test('example test', async ({ page }) => {
  await page.goto('https://example.com');
  // Test actions...
});

Artifacts Location

Configure where screenshots and videos are saved:
import { defineConfig } from '@playwright/test';

export default defineConfig({
  // Output directory for test artifacts
  outputDir: 'test-results/',
  
  use: {
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
});

Practical Examples

Debug Failed Tests

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

test('checkout flow', async ({ page }) => {
  await page.goto('https://example.com/shop');
  
  // Take screenshot before critical action
  await page.screenshot({ path: 'before-checkout.png' });
  
  await page.click('text=Add to Cart');
  await page.click('text=Checkout');
  
  // Take screenshot after critical action
  await page.screenshot({ path: 'after-checkout.png' });
  
  await expect(page.locator('.success-message')).toBeVisible();
});

Visual Testing Workflow

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

test.describe('homepage visual tests', () => {
  test('desktop view', async ({ page }) => {
    await page.goto('https://example.com');
    await expect(page).toHaveScreenshot('homepage-desktop.png');
  });

  test('mobile view', async ({ page }) => {
    await page.setViewportSize({ width: 375, height: 667 });
    await page.goto('https://example.com');
    await expect(page).toHaveScreenshot('homepage-mobile.png');
  });

  test('dark mode', async ({ page }) => {
    await page.emulateMedia({ colorScheme: 'dark' });
    await page.goto('https://example.com');
    await expect(page).toHaveScreenshot('homepage-dark.png');
  });
});

Hide Dynamic Content

test('stable screenshot', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Hide elements that change frequently
  await page.screenshot({
    path: 'stable.png',
    mask: [
      page.locator('.timestamp'),
      page.locator('.ad'),
      page.locator('.random-content'),
    ],
  });
});

CI/CD Integration

Store artifacts in CI pipelines for easy access:
name: Playwright Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build
      - run: npx playwright install --with-deps
      - run: npx playwright test
      
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

Best Practices

Selective Recording: Use retain-on-failure for videos to save disk space while still capturing failures.
  • Use visual regression for stable UIs: Only apply visual testing to components with predictable rendering
  • Mask dynamic content: Hide timestamps, ads, and random content to reduce false positives
  • Set appropriate tolerances: Configure pixel difference thresholds based on your needs
  • Store artifacts in CI: Upload screenshots and videos to CI artifacts for easy debugging
  • Clean up old artifacts: Implement retention policies to manage disk space
Video recording increases test execution time and storage requirements. Use it judiciously in CI/CD pipelines.

Troubleshooting

Screenshots appear blank

Wait for content to load before capturing:
await page.goto('https://example.com');
await page.waitForLoadState('networkidle');
await page.screenshot({ path: 'loaded.png' });

Visual comparison failing unexpectedly

Increase tolerance or update baselines:
await expect(page).toHaveScreenshot('homepage.png', {
  maxDiffPixels: 500,
  threshold: 0.3,
});

Videos not being saved

Ensure video path is accessible and verify configuration:
test.afterEach(async ({ page }, testInfo) => {
  const video = page.video();
  if (video) {
    const path = await video.path();
    console.log('Video path:', path);
  }
});

Build docs developers (and LLMs) love