Trace Viewer — The Time Machine for Flaky Tests
CI says "Element not visible." But it works on your machine. Stop guessing. Playwright's Trace Viewer gives you a complete, step-by-step replay of exactly what the browser saw and did.
📖 The Story: The CI Ghost
David was losing his mind. A test named "Checkout Flow" was failing in GitHub Actions. The CI logs simply said: TimeoutError: Locator expected to be visible.
David ran the test locally. It passed. He ran it 50 times locally. It passed 50 times. He pushed a "fix" that just increased the timeout. CI failed again the next day.
Then he remembered Playwright Traces. He added trace: 'retain-on-failure' to his config. The next CI failure produced a trace.zip file. David downloaded it and ran npx playwright show-trace trace.zip.
A local web server popped up. He could see a timeline of every action. He clicked on the failed step. On the left, the DOM snapshot *before* the click. On the right, the DOM snapshot *after*. He suddenly saw it: a promotional pop-up was appearing over the checkout button for exactly 500 milliseconds, blocking the click. It only triggered on CI because the CI server was slightly slower at rendering the header.
He added a line to dismiss the pop-up. The test passed 100 times in CI. The ghost was gone.
Logs tell you what failed. Traces tell you why.
🎯 Why Trace Viewer?
Time Travel
Hover over the timeline to see the exact state of the DOM at every millisecond of the test.
Snapshots
Automatic before/after screenshots for every single action without writing extra code.
Network Waterfall
See every API call, its payload, response, and timing.
Console & Errors
All browser console logs and JavaScript errors captured perfectly in context.
🌍 Real World Example
Imagine a test that adds an item to a cart, but the "Add to Cart" API fails intermittently. The UI test sees a spinner forever. Without traces, you just see "Timeout on View Cart button." With traces, you open the Network tab, see the POST request, and notice it returned a 500 Internal Server Error. You've just diagnosed a backend bug using a frontend test.
🧒 Explain Like I'm 10
Imagine a plane crashes. Investigators don't just look at the wreckage; they look for the "Black Box." The Black Box records everything the pilots said and everything the plane's sensors felt right up until the crash.
Playwright's Trace Viewer is the Black Box for your test. When the test "crashes" (fails), you open the Black Box and listen to exactly what the browser was "saying" and "seeing" right before it broke.
🎓 Professional Explanation
Trace Viewer is a local Progressive Web App (PWA) generated by the Playwright test runner. When enabled, Playwright instruments the browser to record a highly compressed timeline of events. This includes DOM mutations (via a snapshot diffing algorithm), network traffic via CDP, console output, and Playwright action metadata.
The trace is saved as a .zip archive. You can open it via the Playwright CLI, which spins up a local HTTP server to render the interactive timeline UI.
📊 The Trace Timeline
What a Trace File Captures
⚙️ Enabling Traces
You configure tracing in playwright.config.ts. You should NOT run traces on every test, as it slows down execution.
import { defineConfig } from '@playwright/test'; export default defineConfig({ use: { /* Options: 'off', 'on', 'retain-on-failure', 'on-first-retry', 'on-all-retries' */ trace: 'retain-on-failure' } });
Trace Options Breakdown:
'on': Records a trace for every test. Very slow. Only use for local debugging.'retain-on-failure': Records a trace for every test, but deletes it if the test passes. Perfect for CI.'on-first-retry': Only records a trace if the test fails and is retrying. Excellent for flaky tests.
Running via CLI
# Force tracing for a local debug run
npx playwright test --trace=on
👁️ Viewing the Trace
When a test fails in CI, Playwright outputs a link to download the trace. Once you have the .zip file:
# Opens an interactive local web app
npx playwright show-trace trace.zip
Alternative: You can also drag and drop the .zip file directly into the hosted web app at https://trace.playwright.dev. This is entirely client-side; your trace data is not uploaded to any server.
🔍 Exploring the Trace UI
- Timeline (Top): A waterfall of every action, network request, and browser event. Colors indicate type (green=action, blue=network, red=error).
- Actions (Left Panel): A list of every Playwright command executed. Click one to jump to that moment.
- Snapshots (Center): The actual browser UI. Hover over the timeline to "scrub" through the DOM like a video. Highlight elements to inspect their locators.
- Source (Right Panel): The exact line of TypeScript/JavaScript code that was executing at that moment.
- Console & Network (Bottom): Tabs to view all console logs and the complete network waterfall (similar to Chrome DevTools).
⚠️ Common Mistakes
Mistake 1: Tracing 'on' in CI
// ❌ BAD: Tracing every test slows down CI drastically trace: 'on' // ✅ GOOD: Only trace failures trace: 'retain-on-failure'
Mistake 2: Ignoring the "Before" Snapshot
When an action fails, engineers often look at the "After" snapshot (the error state). The real clue is usually in the "Before" snapshot—what did the screen look like the millisecond *before* Playwright tried to click?
✅ Best Practices
- Use
'retain-on-failure'in CI. It gives you the diagnostic power without slowing down passing tests. - Always download traces from CI. Set up your CI to upload the
test-results/folder as an artifact. - Scrub the timeline. Don't just click through actions. Hover your mouse across the timeline to see subtle UI changes (like spinners or overlays).
- Check the Console. 90% of frontend bugs print a warning or error to the console. The Trace captures all of it.
In the Trace Viewer Snapshots view, click the "Eye" icon to reveal the locator Playwright was targeting. You will visually see a highlighted box over the element. If the box is over the wrong element, your locator is bad!
⚡ Performance Tips
- Trace files are surprisingly small. Playwright uses a highly optimized format. A 30-second test with heavy network usage usually results in a 1-3 MB zip file.
- Clean up old traces. In CI, don't keep trace artifacts for more than 7 days to save cloud storage.
- Avoid
console.logfor large objects. If youconsole.loga massive JSON object, it gets recorded in the trace and bloats the file size.
🏗️ Senior Engineer Deep Dive
How Trace Data is Captured
Playwright uses the Chrome DevTools Protocol (CDP) to hook into the browser. For DOM snapshots, it doesn't save a full HTML document for every frame. Instead, it takes an initial snapshot and then records DOM mutations (via DOM.setChildNodes and similar events). When you view the trace, the UI reconstructs the DOM at any given timestamp by replaying those mutations. This is why the file size remains small despite recording thousands of frames.
Trace vs. Video vs. Screenshots
Screenshots capture a single moment. Video captures a visual timeline but lacks data (you can't see network calls or code). Traces capture everything: visual, network, code, and console. If you have traces, you generally don't need videos.
💼 Interview Questions
Show Answer
trace: 'retain-on-failure'. When the test fails in CI, the trace.zip file is saved. I download it and run npx playwright show-trace trace.zip to inspect the timeline, DOM snapshots, and network calls to see exactly what went wrong.'retain-on-failure' and 'on-first-retry'?Show Answer
'retain-on-failure' records a trace for every run but deletes it if the test passes. 'on-first-retry' does not record a trace on the first attempt; it only starts recording if the test fails and is automatically retried by the runner.Show Answer
Show Answer
🏋️ Practical Exercise
Generate and Explore a Trace
- Run a test locally with tracing forced on:
npx playwright test --trace=on - When the test finishes, click the generated URL in the terminal, or run
npx playwright show-traceon the output file. - Hover your mouse over the Timeline at the top.
- Watch the DOM snapshot change in the center.
- Click on a network request in the bottom panel and inspect its JSON response.
🚀 Mini Project
🏗️ The Intentional Bug
- Write a test that navigates to a site.
- Add a line of code that intentionally targets a non-existent element (e.g.,
await page.getByRole('button', { name: 'DoesNotExist' }).click();). - Run with
--trace=on. - Open the trace. Go to the failed step.
- Use the "Pick Locator" tool in the Trace UI to find a locator for an element that *does* exist on the page.
📝 Quiz
Test your understanding. Click an option to check your answer.
npx playwright open-trace trace.zipnpx playwright show-trace trace.zipnpx playwright trace trace.zipnpx playwright debug trace.zipnpx playwright show-trace spins up a local server to render the trace UI.'on''off''retain-on-failure''always''retain-on-failure' ensures you have traces for debugging without slowing down passing tests.❓ FAQ
Q: Are trace files secure to upload to cloud tools?
A: Trace files contain DOM snapshots and network payloads. If those contain sensitive PII or secrets, do not upload them to public tools. trace.playwright.dev processes everything client-side and doesn't upload data, but always verify your company policy.
Q: Can I view traces on my mobile phone?
A: Technically yes, via the web app, but the UI is heavily optimized for desktop screens with multiple panels.
Q: Does tracing work with sharded tests?
A: Yes. Each shard generates its own trace files for failed tests. You just need to ensure your CI uploads artifacts from all shard jobs.
📦 Summary
🎯 Key Takeaways
- Trace Viewer is the ultimate debugging tool for flaky CI tests.
- Use
'retain-on-failure'in CI to balance speed and diagnostics. - Traces contain DOM snapshots, network, console, and source code.
- Hover over the timeline to time-travel through the DOM.
- Open traces via CLI or drag-and-drop into the web app.
- Always check the "Before" snapshot to see what blocked the action.
