Skip to main content

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:

PanelWhat 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
ActionsEach action with its locator and duration. Click one to jump to that moment
SourceThe test code with the current action highlighted
Call, LogWhat the action did and the wait steps it went through
ErrorsThe failure message
Console, NetworkBrowser console and every request
Pick locatorHover 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',
},
ValueWhen 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​

CommandWhat it does
npx playwright test tests/login.spec.tsOne file
npx playwright test -g "locked out"Tests whose name matches
npx playwright test --last-failedOnly what failed in the previous run
npx playwright test --headedWatch the browser
npx playwright test --repeat-each 5Run each test five times to expose flakiness
npx playwright test --workers 1Serial, to rule out parallel interference
npx playwright test --project chromiumOne project only

Common Failures and What They Mean​

SymptomUsual causeFix
element(s) not found after a full timeoutWrong locator, or wrong test id attributePick locator in UI mode, compare with the aria snapshot
strict mode violation: resolved to N elementsLocator matches more than one elementFilter by container or add name
Test timeout of 30000ms exceededAn action waited forever, often an element covered by an overlay or a navigation that never finishedLook at the trace timeline for the last action
Passes locally, fails in CIDifferent viewport, slower machine, or state left by another testCompare the CI trace, set an explicit viewport, check test isolation
Passes alone, fails with othersTests share state: same account, same recordGive each test its own data, or use test.describe.configure({ mode: 'serial' }) as a last resort
Flaky on the same assertionThe assertion is not web-first, or something is asserted before the page settledUse await expect(locator), drop isVisible() checks
${var} style literal text in the pageNot a Playwright issue. That is the JMeter track. Different problem, same smell: something was never resolvedSee 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 0 makes a flaky test fail loudly instead of quietly passing on the second try.

  • error-context.md is 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.