Traces, screenshots and debugging a failure
A test failed. Now what? A UI test failure that just says "expected visible, got hidden" is not much to go on — and if it failed in CI, on a machine you cannot see, it is worse. Playwright's answer is some of the best debugging in any test tool: it can record a trace of exactly what happened, take screenshots and video, and let you step through a test interactively. Knowing these turns a baffling failure into a five-minute fix. This lesson is debugging a failing Playwright test.
First, read the failure
Before any tool, read what Playwright already tells you. A failure prints the assertion that failed, the
locator involved, the expected and actual, and a snippet of the page. Often that is enough:
getByRole('button', { name: 'Sign in' }) "resolved to 0 elements" means the button was not found — a wrong
name, or the page did not load. expect(...).toHaveCount(11) "received 1" means only the header row was
present — the data did not load. Read the message and the line before reaching for heavier tools; half the
time it names the problem.
Traces: a recording of the whole test
A trace is Playwright's most powerful feature. It records, for every step, a snapshot of the DOM, the action taken, the network requests, the console, and screenshots — a complete timeline you can scrub through after the fact. Turn it on in the config:
use: { trace: 'on-first-retry' } // AgentPay: record a trace when a test retries
'on-first-retry' (what AgentPay uses) records only when a test fails and retries — cheap, and you get a
trace exactly when you need one. Then open it:
npx playwright show-trace trace.zip
The trace viewer shows the timeline, and for each step a before/after snapshot of the page you can inspect — hover the failing step and see exactly what the page looked like, what was on it, what the network was doing. For a CI failure you cannot reproduce locally, the trace is often the only way to see what happened — download it from the CI artifacts (the CI module wires this up) and scrub through. Traces are the single biggest reason a Playwright failure is diagnosable.
Screenshots and video
Simpler artifacts, useful at a glance:
- Screenshot on failure —
use: { screenshot: 'only-on-failure' }(AgentPay sets this) captures the page at the moment of failure. A picture instantly shows "the page is on the login screen, not the dashboard" or "an error banner is showing". - Video —
use: { video: 'retain-on-failure' }records the whole test; watch the run to see where it went wrong. Heavier than a trace but sometimes clearer for a timing issue.
These are automatic once configured, and they attach to the HTML report (below), so a CI failure comes with a picture without any extra work.
Debugging interactively
When you can run locally, step through the test:
- UI mode —
npx playwright test --uiopens a visual runner: pick a test, watch it run step by step, see the DOM snapshot at each step, and time-travel. The best way to develop and debug tests. - Debug mode / Inspector —
npx playwright test --debugruns headed and pauses so you can step through, and opens the Inspector where you can try locators live against the real page (invaluable when a locator is not matching). - Headed —
--headedsimply shows the browser so you watch it happen. page.pause()— drop it into a test to pause execution there and open the Inspector at that point.
Developing a new test in UI mode, and reaching for --debug when a locator will not match, is the normal
workflow — far faster than adding console.logs.
The HTML report
Playwright generates an HTML report of a run — every test, its status, and for failures the error, the screenshot, and the trace, all in one page:
npx playwright show-report
AgentPay's config produces this report in CI (reporter: [['github'], ['html', …]]) and uploads it as an
artifact, so when the CI module runs the suite on every change, a failure comes with a browsable report:
click the failed test, see the screenshot, open the trace. That is what makes a CI failure actionable rather
than a dead end — which, as the whole automation part insisted, is the difference between a suite people act
on and one they ignore.
A debugging routine
Put it together into a routine for any failing test:
- Read the error — the assertion, locator, expected vs actual. Often enough.
- Look at the screenshot — what state was the page actually in?
- Open the trace — scrub to the failing step, inspect the DOM snapshot and network at that moment.
- Reproduce in UI/debug mode locally — step through, try the locator live in the Inspector.
- Fix the cause — a wrong locator, a missing wait (add a web-first assertion, never a sleep), or a real bug in the app (the best outcome — the test did its job).
Most failures fall out at step 1 or 3. And note the distinction that matters: sometimes the test is wrong (fix the test), and sometimes the app is wrong (you found a bug — file it, per the bug-reports lesson). A good debugging habit is what lets you tell the two apart quickly.
Check your work
Read the failure first — Playwright names the failed assertion, the locator, expected vs actual, and a page snippet; "resolved to 0 elements" = not found, "received 1" for a row count = data did not load. Half of failures are solved here.
Traces (trace: 'on-first-retry') record a full timeline — DOM snapshots, actions, network, console —
scrubbable in show-trace. Often the only way to diagnose a CI-only failure (download from artifacts). The
biggest reason Playwright failures are diagnosable.
Screenshots (screenshot: 'only-on-failure') and video (retain-on-failure) capture the failure
state automatically and attach to the report.
Interactive: UI mode (--ui, step through + time-travel — best for developing), debug mode (--debug,
pause + live-try locators in the Inspector), --headed, and page.pause().
HTML report (show-report) collects every test with errors, screenshots and traces; AgentPay uploads it
in CI so a failure is browsable and actionable.
Routine: read error → screenshot → trace → reproduce in UI/debug → fix (wrong locator, missing wait, or a real app bug — tell test-wrong from app-wrong).
Practice
- Make an AgentPay test fail (wrong button name) and read the error; identify the cause from the message alone.
- Enable traces, cause a failure, and open the trace with
show-trace; scrub to the failing step and inspect the DOM snapshot. - Turn on
screenshot: 'only-on-failure'and confirm the failure screenshot shows the page state. - Run the suite in
--uimode and step through a test; then use--debugto try a locator live in the Inspector. - Generate and open the HTML report; find a failed test's screenshot and trace in it.
- Take one failure and decide whether the test or the app is wrong, and justify which fix is correct.
Official documentation
- Playwright — Trace viewer — Recording and scrubbing traces.
- Playwright — Debugging tests — UI mode, debug mode, the Inspector and
page.pause(). - Playwright — Reporters — The HTML report, screenshots and video.
Next: the api-automation module — automating the API tests you designed by hand.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship