Playwright’s Trace Viewer lets you step through your tests in a way that feels like time travel, showing you exactly what happened when a test failed.
Let’s see it in action. Imagine a simple test that tries to log in but fails because the password field isn’t found.
# test_login.py
from playwright.sync_api import sync_playwright
def test_login_failure():
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com/login") # Assume this page exists
page.fill("input[name='username']", "testuser")
page.fill("input[name='password']", "wrongpassword") # This field might be named differently
page.click("button[type='submit']")
browser.close()
When this test fails, you’d typically see a cryptic error message. But if you ran it with tracing enabled:
playwright test --trace on
Playwright will generate a .zip file in a test-results directory. Unzipping this and opening the index.html file in your browser launches the Trace Viewer.
The Trace Viewer Interface
You’re presented with a timeline of actions. On the left, you see each step: goto, fill, click. On the right, you see a snapshot of the page after that action. If an action failed, it’s highlighted in red. Clicking on a failed step shows you the DOM and console logs at that precise moment. This is where the magic happens – you can see the page as it was when it broke.
The Mental Model: What’s Happening Under the Hood?
Playwright isn’t just replaying your actions; it’s recording a detailed log. When trace on is active, Playwright intercepts browser events: DOM mutations, network requests, console messages, and user interactions. It captures screenshots at key moments and stores all this data, along with the executed test code, in the trace file.
Think of it like a flight recorder for your browser. Every significant event is logged chronologically. The Trace Viewer then reconstructs this sequence, allowing you to:
- See the DOM: Inspect the exact HTML structure of the page at any given point. This is crucial for understanding why selectors might have failed.
- Examine Network Activity: See all the HTTP requests and responses. Was an API call missing? Did it return an error?
- Review Console Logs: Catch JavaScript errors or application-level logging that might have occurred.
- Watch Video (if enabled): If you run
trace on --video on, you get a full-motion video of the test execution.
Debugging the Login Failure
Back to our login example. In the Trace Viewer, you’d click on the fill("input[name='password']", "wrongpassword") step. If it failed, you’d see the page. You’d then look at the DOM snapshot on the right. You might discover the password input is actually id="passwordField" or name="user_password". You’d update your test code:
# Corrected test
page.fill("input[name='user_password']", "wrongpassword")
The power here is that you’re not guessing. You’re seeing the problem. You can also inspect the click("button[type='submit']") step. Perhaps the button isn’t immediately visible or enabled. The trace would show you its state.
Common Trace Viewer Use Cases:
- Selector Failures: The most common. The trace shows the DOM, revealing typos, incorrect attributes, or dynamic changes you didn’t account for.
- Timing Issues: An element isn’t ready when your script tries to interact with it. The trace shows the DOM state before the element appeared. You’d then add
page.wait_for_selector()or use Playwright’s auto-waiting capabilities more effectively. - Unexpected Redirects/Navigations: The
gotoorclicksteps show the wrong URL or a blank page. - Application Errors: JavaScript errors in the console tab of the trace reveal client-side bugs.
- API Failures: Network tab shows failed XHR requests that your application relies on.
The One Thing Most People Don’t Know
The trace file itself is a static snapshot, but it contains everything Playwright knows about the execution. This includes not just DOM snapshots and network logs, but also the source code of your test file as it was executed. You can see your test code directly within the Trace Viewer, synchronized with the actions it’s performing. This means you don’t need to switch back and forth between your IDE and the trace; the entire debugging context is consolidated.
The next step in mastering Playwright’s debugging tools is understanding how to leverage page.pause() within your tests for interactive debugging sessions.