Debug
A test failed. Before touching the code, look at what actually happened in the browser. Playwright records enough that you can usually see the cause in a minute. This is the Debug step of the JMeter track, with better tooling.
Read the Error First
Playwright errors are specific. This one tells you the locator, what it expected, what it found, and how long it waited:
Error: expect(locator).toContainText(expected) failed
Locator: getByTestId('error')
Expected substring: "Username is required"
Timeout: 5000ms
Error: element(s) not found
"Element(s) not found" after a full timeout almost always means the locator is wrong, not that the app is slow. In the sample project this exact error appeared because saucedemo uses data-test and the config still had the default data-testid.
Next to the error the runner also writes a screenshot (if screenshot: 'only-on-failure' is set) and an error-context.md file with an accessibility snapshot of the page at the moment of failure. Open that file: it shows you what the page really contained.
UI Mode
The fastest way to work on a test locally.
npx playwright test --ui
A window opens with:
| Panel | What it shows |
|---|---|
| Test list (left) | Every file and test. Click the play icon to run one. Filter by name, @tag, project or status |
| Timeline (top) | The test as a strip of screenshots. Hover to scrub through time |
| Actions | Each action with its locator and duration. Click one to jump to that moment |
| Source | The test code with the current action highlighted |
| Call, Log | What the action did and the wait steps it went through |
| Errors | The failure message |
| Console, Network | Browser console and every request |
| Pick locator | Hover the page snapshot to get the locator for any element |
Turn on watch mode (the eye icon) and the test re-runs every time you save the file. This is how I write a new test: UI mode on the side, edit, save, watch it run.
Trace Viewer
UI mode is for local work. The trace is what you use when a test failed somewhere else: in CI, on a colleague's machine, last night.
Record a Trace
The generated config already records a trace on the first retry:
use: {
trace: 'on-first-retry',
},
| Value | When a trace is kept |
|---|---|
'on-first-retry' | Only when a test fails and is retried. Recommended for CI |
'retain-on-failure' | Every test is traced, only failures keep it. Use when retries are 0 |
'on' | Always. Slow, big files. Only for a short debugging session |
'off' | Never |
To force a trace for one local run:
npx playwright test --trace on
Open a Trace
Traces are saved as test-results/<test-name>/trace.zip.
npx playwright show-trace test-results/login-Login-empty-form-chromium/trace.zip
Or open the HTML report (npx playwright show-report) and click the trace icon next to the failed test. Or drag the zip onto trace.playwright.dev, which runs entirely in your browser and uploads nothing.
The trace viewer shows the same panels as UI mode: actions, before/after DOM snapshots you can inspect, network, console, and the source line for each step. The DOM snapshots are live: you can hover elements and use Pick locator on the page as it was at that moment.
Tip: Since 1.59 there is also
npx playwright trace <trace.zip>for inspecting a trace from the terminal. Handy when you are on a server without a browser.
Step Through with the Inspector
For a test you want to walk through action by action:
npx playwright test tests/login.spec.ts --debug
The browser opens headed with the Playwright Inspector. Step over each action, see the locator highlighted on the page, and edit locators in the Inspector's locator box to try alternatives live.
To stop at a specific line instead of the start, put await page.pause(); in the test and run headed:
npx playwright test tests/login.spec.ts --headed
In VS Code, set a breakpoint and click Debug test in the Testing sidebar. Same thing, inside the editor.
Useful Runs While Debugging
| Command | What it does |
|---|---|
npx playwright test tests/login.spec.ts | One file |
npx playwright test -g "locked out" | Tests whose name matches |
npx playwright test --last-failed | Only what failed in the previous run |
npx playwright test --headed | Watch the browser |
npx playwright test --repeat-each 5 | Run each test five times to expose flakiness |
npx playwright test --workers 1 | Serial, to rule out parallel interference |
npx playwright test --project chromium | One project only |
Common Failures and What They Mean
| Symptom | Usual cause | Fix |
|---|---|---|
element(s) not found after a full timeout | Wrong locator, or wrong test id attribute | Pick locator in UI mode, compare with the aria snapshot |
strict mode violation: resolved to N elements | Locator matches more than one element | Filter by container or add name |
Test timeout of 30000ms exceeded | An action waited forever, often an element covered by an overlay or a navigation that never finished | Look at the trace timeline for the last action |
| Passes locally, fails in CI | Different viewport, slower machine, or state left by another test | Compare the CI trace, set an explicit viewport, check test isolation |
| Passes alone, fails with others | Tests share state: same account, same record | Give each test its own data, or use test.describe.configure({ mode: 'serial' }) as a last resort |
| Flaky on the same assertion | The assertion is not web-first, or something is asserted before the page settled | Use await expect(locator), drop isVisible() checks |
${var} style literal text in the page | Not a Playwright issue. That is the JMeter track. Different problem, same smell: something was never resolved | See JMeter Section 7 |
Tips
-
Trace first, code second. The trace answers "what did the page look like" without guessing.
-
Turn retries off while debugging.
--retries 0makes a flaky test fail loudly instead of quietly passing on the second try. -
error-context.mdis underrated. It is an aria snapshot of the failing page, which is exactly what you need to fix a locator. -
Do not add
waitForTimeout. If a test needs a fixed sleep, it is waiting for something you can assert on instead.