When an E2E test fails, especially on CI you cannot enter by hand, the question is not "what was on the screen" but "what was happening step by step". A screenshot is a photo of the crash site: it shows the final state but not the path to it. Playwright gives something far more powerful - a trace, a full recording of the run by which a failure can be taken apart after the fact as if you had stood next to it. It is the trace, not the screenshot, that is the main diagnostic tool.
There are several debugging tools, each with its role. npx playwright test --ui opens the UI Mode - an interactive mode showing the test tree, steps and snapshots, handy for running and analyzing locally. The --debug flag launches the Inspector with step-by-step execution. --headed shows the real browser instead of a background one. show-report opens the run's HTML report, and show-trace a specific trace file. Each command answers its own debugging question.
codegen stands apart - the scenario generator. npx playwright codegen with an address opens a browser and records your actions into test code, picking locators along the way. This is a quick way to feel out the right locator and sketch a path, but it is precisely a sketch: the generated scenario almost always needs refinement, which we discuss below. Codegen saves time at the start rather than producing a finished professional test.
The heart of diagnostics is the Trace Viewer. It shows the run's timeline, DOM snapshots at every step, network requests, console output, the test source and the steps themselves - all synchronized in time. From a failed test's trace you see not only where it failed but what preceded it: what the DOM was, what came over the network, what the console wrote. This turns taking apart a mysterious CI failure from guessing into reading a recording.
It helps to gather the debugging commands in one place once, so as not to recall them under the pressure of a broken run. Below is a set: the UI Mode, debugging a specific file, headed mode, opening the report and a trace, the generator. You return to this map when a test failed and you need to quickly pick a tool for a concrete question - take apart a trace, reproduce locally or feel out a locator.
The key trace setting on CI is a balance between benefit and cost. Recording a trace for every test is expensive: it noticeably slows the run and bloats artifacts, while a trace is mostly needed for failures. So the optimal start is trace: 'on-first-retry': a trace is recorded only when a test failed and went for a retry. So the detailed recording is exactly where it is needed for analysis and is not spent on thousands of passing runs.
About codegen it is important to remember it gives a draft, not a result. The generated scenario contains extra clicks, random locators and fragile data; leaving it as is means adding a brittle test tied to the markup. It must be turned into a readable business test: remove the excess, replace locators with semantic ones, stabilize the data, give the scenario an intent name. Codegen finds the path, but a professional test is made from it by a human.
The typical diagnostic failures come down to working blind. Relying on a single failure screenshot without enabling a trace - and guessing about the cause instead of reading a recording. Writing traces of all tests in a row, slowing the run for data needed only for failed ones. And committing raw codegen output as a finished test - a brittle scenario breaking at the first edit. Enable a trace on the retry, take apart failures in the Trace Viewer, and use codegen as a draft.
npx playwright test --ui # interactive UI Mode
npx playwright test e2e/checkout.spec.ts --debug # Inspector, step by step
npx playwright test --headed # the real browser on screen
npx playwright show-report # the run's HTML report
npx playwright show-trace test-results/.../trace.zip # take apart a trace
npx playwright codegen http://localhost:3000 # a scenario draft