Traces, Screenshots & Videos
Capture everything for post-mortem debugging
Traces, Screenshots & Videos
In the previous tutorial, we learned how to debug tests interactively. But what about when tests fail in CI at 3 AM? You can't step through with a debugger.
That's where traces, screenshots, and videos come in. They capture everything that happened so you can debug failures after the fact ā like a flight recorder for your tests.
The Trace Viewer
"What exactly is a trace?"
Traces are Playwright's superpower for debugging. A trace captures:
- Screenshot at every step
- DOM snapshot at every step
- Network requests and responses
- Console logs
- Action timings
It's like time-travel debugging, but for test runs you weren't watching.
Recording Traces
Configure tracing in playwright.config.ts:
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
// Record trace for each test
trace: 'on', // 'on' | 'off' | 'on-first-retry' | 'retain-on-failure'
},
});
Trace options:
'on'ā always record (large files, good for debugging)'off'ā never record'on-first-retry'ā record only when retrying a failed test'retain-on-failure'ā record always, but keep only for failed tests
For CI, 'retain-on-failure' is the sweet spot ā you get traces when you need them without wasting storage.
Viewing Traces
After a test runs, traces are saved in test-results/. View them with:
npx playwright show-trace test-results/my-test-chromium/trace.zip
Or just run:
npx playwright show-trace
And pick from available traces.
What You See in Trace Viewer
The Trace Viewer opens in your browser with:
Timeline ā shows every action, click, keystroke, navigation
Screenshot at each step ā see exactly what the page looked like
Before/After DOM snapshots ā compare state before and after actions
Network tab ā all requests with timing, headers, and bodies
Console tab ā browser console logs
Source tab ā your test code, highlighted at each step
Click any action in the timeline to time-travel to that moment. How cool is that?
Recording Traces Programmatically
For more control, start and stop traces in your test:
test('debug with custom trace', async ({ page, context }) => {
// Start tracing
await context.tracing.start({ screenshots: true, snapshots: true });
await page.goto('/checkout');
await page.getByLabel('Email').fill('user@example.com');
// ... more actions
// Stop and save trace
await context.tracing.stop({ path: 'checkout-trace.zip' });
});
This is useful when you want traces for specific complex flows.
Screenshots
Screenshots capture the page at a specific moment. Use them for debugging, visual regression testing, or documentation.
Automatic Screenshots on Failure
The easiest setup ā Playwright takes a screenshot whenever a test fails:
// playwright.config.ts
export default defineConfig({
use: {
screenshot: 'only-on-failure',
},
});
Screenshot options:
'off'ā no automatic screenshots'on'ā screenshot after every test'only-on-failure'ā screenshot only when test fails (recommended for CI)
Failed test screenshots land in test-results/ alongside the test.
Manual Screenshots
Take screenshots anywhere in your test:
test('screenshot examples', async ({ page }) => {
await page.goto('/dashboard');
// Basic screenshot
await page.screenshot({ path: 'dashboard.png' });
// Full page (scrolls and stitches)
await page.screenshot({ path: 'full-page.png', fullPage: true });
// Specific element only
await page.locator('.chart').screenshot({ path: 'chart.png' });
// With options
await page.screenshot({
path: 'custom.png',
fullPage: true,
animations: 'disabled', // Stop CSS animations
mask: [page.locator('.user-id')], // Hide sensitive data
});
});
Screenshot Comparison (Visual Testing)
"Can I catch visual regressions automatically?"
Playwright can compare screenshots against baselines:
test('visual regression', async ({ page }) => {
await page.goto('/pricing');
// Compares against saved snapshot
await expect(page).toHaveScreenshot('pricing-page.png');
// Element screenshot comparison
await expect(page.locator('.pricing-table')).toHaveScreenshot('pricing-table.png');
});
First run creates the baseline. Subsequent runs compare against it. Differences fail the test.
Update baselines with:
npx playwright test --update-snapshots
Screenshot Options
await page.screenshot({
path: 'screenshot.png',
fullPage: true,
clip: { x: 0, y: 0, width: 800, height: 600 }, // Specific region
omitBackground: true, // Transparent background
type: 'jpeg', // or 'png' (default)
quality: 80, // For JPEG only
scale: 'css', // 'css' or 'device'
});
Video Recording
"Can I record a video of my test running?"
Yep! Videos capture the entire test run. They're bigger than traces but sometimes easier to share with non-technical folks.
Configuring Video Recording
// playwright.config.ts
export default defineConfig({
use: {
video: 'retain-on-failure',
},
});
Video options:
'off'ā no video'on'ā always record'on-first-retry'ā record when retrying failed tests'retain-on-failure'ā record always, keep only for failures (recommended)
Video Size
Videos can get large. Control the size:
export default defineConfig({
use: {
video: {
mode: 'retain-on-failure',
size: { width: 1280, height: 720 }, // Smaller than default
},
},
});
Recording Video Manually
test('manual video recording', async ({ browser }) => {
const context = await browser.newContext({
recordVideo: {
dir: './videos/',
size: { width: 1280, height: 720 },
},
});
const page = await context.newPage();
await page.goto('/app');
// ... test actions
// Video is saved when context closes
await context.close();
// Get the video path
const video = page.video();
if (video) {
console.log('Video saved:', await video.path());
}
});
Configuring Artifacts for CI
Here's a production-ready config that captures everything you need when tests fail:
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
// Retry failed tests once in CI
retries: process.env.CI ? 1 : 0,
// Capture artifacts
use: {
// Trace on retry (captures the retry, not the original failure)
trace: 'on-first-retry',
// Screenshot when test fails
screenshot: 'only-on-failure',
// Video when test fails
video: 'retain-on-failure',
},
// Where to store artifacts
outputDir: 'test-results/',
// Reporter configuration
reporter: [
['html', { open: 'never' }], // HTML report
['list'], // Console output
],
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
],
});
Viewing Artifacts
Local Development
Artifacts go to test-results/ by default. Structure looks like:
test-results/
āāā my-test-chromium/
ā āāā trace.zip
ā āāā test-failed-1.png
ā āāā video.webm
āāā another-test-firefox/
āāā trace.zip
View them with:
# Open trace
npx playwright show-trace test-results/my-test-chromium/trace.zip
# HTML report (includes screenshots, traces)
npx playwright show-report
GitHub Actions
Upload artifacts in your workflow:
# .github/workflows/tests.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run tests
run: npx playwright test
- name: Upload test artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: |
playwright-report/
test-results/
retention-days: 7
When tests fail, download artifacts from the GitHub Actions run page. The HTML report includes traces you can view directly.
Playwright HTML Report
The HTML report is the easiest way to review failures. It includes:
- Test results summary
- Embedded screenshots
- Trace viewer built-in
- Video playback
- Error messages and stack traces
Generate and view:
npx playwright test
npx playwright show-report
The report opens in your browser. Click any failed test to see its trace, screenshots, and video.
Combining Artifacts Strategically
Different situations call for different artifacts:
CI Debugging
// Focus on traces ā most detail, reasonable size
use: {
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'off', // Save storage
}
Bug Reports / Demos
// Video is easiest to share with stakeholders
use: {
trace: 'off',
screenshot: 'on',
video: 'on',
}
Development
// Traces give you everything for debugging
use: {
trace: 'on',
screenshot: 'off',
video: 'off',
}
Storage Considerations
Artifacts add up. A single trace can be 5-20MB. Videos can be 50MB+.
Tips for managing storage:
- Use
'retain-on-failure'in CI ā only keep what you need - Set retention days on artifact uploads
- Clean
test-results/before runs:rm -rf test-results/ - Add
test-results/andplaywright-report/to.gitignore
# .gitignore
test-results/
playwright-report/
Quick Reference
| Artifact | Config | Best For |
|---|---|---|
| Trace | trace: 'retain-on-failure' | Detailed debugging |
| Screenshot | screenshot: 'only-on-failure' | Quick visual check |
| Video | video: 'retain-on-failure' | Sharing with non-devs |
What's Next?
You now have a complete debugging artifact toolkit:
- Traces capture everything: screenshots, DOM, network, console
- Use Trace Viewer to time-travel through test runs
- Automatic screenshots on failure for quick debugging
- Video recording for demos and sharing
- Configure artifacts differently for CI vs local development
- HTML report bundles everything in one place
Time for real-world patterns. Let's tackle one of the trickiest challenges in E2E testing: handling authentication. Let's go!