---
title: 'Debugging: UI Mode, Trace Viewer, Codegen | TestingBot'
description: 'Sooner or later a test fails and the message alone does not explain
  why. Playwright ships four debugging tools, and each fits a different moment: UI
  mode…'
source_url:
  html: https://testingbot.com/resources/courses/playwright/debugging-tests
  md: https://testingbot.com/resources/courses/playwright/debugging-tests.md
---

Course

[Playwright Tutorial: Zero to CI](https://testingbot.com/resources/courses/playwright)

Chapter 6 of 10

1. [1 Playwright Tutorial for Beginners](https://testingbot.com/resources/courses/playwright)
2. [2 Install Playwright and Run a First Test](https://testingbot.com/resources/courses/playwright/install-and-first-test)
3. [3 Locators: Find Elements the Way Users Do](https://testingbot.com/resources/courses/playwright/locators)
4. [4 Auto-Waiting and Web-First Assertions](https://testingbot.com/resources/courses/playwright/auto-waiting-and-assertions)
5. [5 Fixtures, Hooks and Logging In Once](https://testingbot.com/resources/courses/playwright/fixtures-hooks-and-authentication)
6. [6 Debugging: UI Mode, Trace Viewer, Codegen](https://testingbot.com/resources/courses/playwright/debugging-tests)
7. [7 Multiple Browsers, Workers and Sharding](https://testingbot.com/resources/courses/playwright/browsers-parallelism-and-sharding)
8. [8 Run Playwright in CI with GitHub Actions](https://testingbot.com/resources/courses/playwright/github-actions-ci)
9. [9 Run Your Playwright Suite on a Cloud Grid](https://testingbot.com/resources/courses/playwright/run-on-a-cloud-grid)
10. [10 Knowledge check](https://testingbot.com/resources/courses/playwright/knowledge-check)

[Exit course](https://testingbot.com/resources/courses)

 Chapter 6 of 10 Beginner 7 min read 2 quick checks Updated Nov 2026 

# Debugging: UI Mode, Trace Viewer, Codegen
On this page
1. [UI mode: the everyday tool](https://testingbot.com#ui-mode-the-everyday-tool)
2. [The Inspector: step through a test](https://testingbot.com#the-inspector-step-through-a-test)
3. [Trace viewer: debugging failures after the fact](https://testingbot.com#trace-viewer-debugging-failures-after-the-fact)
4. [Make traces readable with test.step](https://testingbot.com#make-traces-readable-with-test-step)
5. [Codegen: discover selectors by clicking](https://testingbot.com#codegen-discover-selectors-by-clicking)
6. [A debugging routine that works](https://testingbot.com#a-debugging-routine-that-works)

Sooner or later a test fails and the message alone does not explain why. Playwright ships four debugging tools, and each fits a different moment: UI mode while you write tests, the Inspector when you need to stop at a line, the trace viewer for failures you did not watch happen (above all on CI), and codegen when you need to discover how to reach an element.

## UI mode: the everyday tool[#](https://testingbot.com#ui-mode-the-everyday-tool)

```bash
npx playwright test --ui
```

UI mode opens a desktop window with your test tree on the left. For whichever test you run, it shows:

- an **action timeline** : every `goto`, `click`, `fill` and assertion, each with a before/after DOM snapshot you can inspect with the browser's element tools;
- the **source** line for the selected action, plus console messages, network requests and errors;
- a **Pick locator** button: hover over the snapshot and it shows the locator it would use, which you can copy straight into your test;
- **watch mode** (the eye icon next to a test or file): the test re-runs every time you save.

Because the snapshots are real DOM, not screenshots, you can inspect an element at the exact moment an assertion failed and see, for example, that the text was "2 item left" rather than "2 items left".

UI mode runs the same projects as the command line, so you can filter to `chromium` while iterating and run all browsers before committing.

## The Inspector: step through a test[#](https://testingbot.com#the-inspector-step-through-a-test)

When you want to stop a test at a particular line and poke at the live browser, use debug mode:

```bash
npx playwright test tests/todo.spec.ts:14 --debug
```

The `:14` suffix runs only the test declared on that line. `--debug` is a shortcut for headed mode, one worker, no timeout, and the Playwright Inspector window. The Inspector pauses before each action, highlights the target element in the browser and lets you step forward one action at a time, or edit a locator and see live what it matches.

To stop at a specific point instead of stepping from the beginning, put a breakpoint in the code:

```typescript
test('completes one todo', async ({ page }) => {
  await page.goto('https://demo.playwright.dev/todomvc/');
  await page.pause(); // execution stops here when run with --debug or --headed
  // ...
});
```

`page.pause()` only does something in a headed browser, but remove it before committing anyway; a forgotten breakpoint is confusing for the next person. Setting the environment variable `PWDEBUG=1` has the same effect as `--debug` and is useful when tests are launched by another script.

If you use VS Code, the official Playwright extension gives you the same powers inside the editor: run or debug a single test from the gutter, set normal breakpoints, and pick locators.

## Trace viewer: debugging failures after the fact[#](https://testingbot.com#trace-viewer-debugging-failures-after-the-fact)

UI mode and the Inspector need you at the keyboard. Failures on CI happen while nobody is watching, and that is what traces are for. A trace is a zip file containing every action, DOM snapshots before and after each one, network traffic, console output and the test source.

The generated config already records one when a test is retried:

```typescript
use: {
  trace: 'on-first-retry',
},
```

The options are `'off'`, `'on'`, `'on-first-retry'`, `'on-all-retries'` and `'retain-on-failure'`. Recording on every test (`'on'`) is heavy and rarely needed. If you do not use retries, `'retain-on-failure'` records every test but keeps the trace only when the test fails.

To produce a trace locally on demand, override the mode from the command line:

```bash
npx playwright test --trace on
npx playwright show-report
```

In the HTML report, open a test and click its trace. You can also open a trace file directly:

```bash
npx playwright show-trace test-results/todo-adds-a-todo-item-chromium/trace.zip
```

or drag the zip onto [trace.playwright.dev](https://trace.playwright.dev), which processes the file in your browser rather than uploading it. That is the quickest way to open a trace a colleague attached to a bug.

How to read one efficiently:

1. Start at the failed action (marked red) and read the error and the action log, which says what Playwright was waiting for.
2. Look at the **Before** snapshot of that action. Is the element there? Is something covering it?
3. Step back through earlier actions to find where the page diverged from what the test expected.
4. Check the **Network** tab for failed or slow API calls around that moment; many "UI" failures are a backend returning a 500.

Screenshots and video can be captured too, with the same retention logic:

```typescript
use: {
  trace: 'on-first-retry',
  screenshot: 'only-on-failure',
  video: 'retain-on-failure',
},
```

All of these land in `test-results/` and are linked from the HTML report, which is why chapter 7 uploads that report from CI.

Your CI runs with `retries: 0`. Which `trace` setting gives you a trace for every failing test without keeping traces for passing ones?

 `'on-first-retry'` `'on'` `'retain-on-failure'` `'off'` plus `screenshot: 'only-on-failure'`

Without retries, `'on-first-retry'` never records anything. `'retain-on-failure'` records each test and discards the trace when it passes; `'on'` keeps them all.

## Make traces readable with test.step[#](https://testingbot.com#make-traces-readable-with-test-step)

Long tests produce long, flat action lists. `test.step` groups actions under a name that appears in the report, in UI mode and in the trace:

```typescript
import { test, expect } from '@playwright/test';

test('completes a todo', async ({ page }) => {
  await test.step('add two todos', async () => {
    await page.goto('https://demo.playwright.dev/todomvc/');
    const input = page.getByPlaceholder('What needs to be done?');
    await input.fill('Buy milk');
    await input.press('Enter');
    await input.fill('Pay rent');
    await input.press('Enter');
  });

  await test.step('complete one', async () => {
    await page.getByTestId('todo-item').filter({ hasText: 'Pay rent' })
      .getByRole('checkbox', { name: 'Toggle Todo' }).check();
    await expect(page.getByTestId('todo-count')).toHaveText('1 item left');
  });
});
```

A failure now reads "complete one" rather than "locator.check at line 14".

## Codegen: discover selectors by clicking[#](https://testingbot.com#codegen-discover-selectors-by-clicking)

```bash
npx playwright codegen https://demo.playwright.dev/todomvc/
```

Codegen opens a browser plus a window that writes test code as you interact. It prefers role, label and test-id locators, and its toolbar can insert assertions (visibility, text, value) for the element you click. Useful flags:

```bash
npx playwright codegen --device="iPhone 13" https://example.com # emulate a device
npx playwright codegen --save-storage=auth.json https://example.com # save login state when you close it
npx playwright codegen --load-storage=auth.json https://example.com # start already logged in
npx playwright codegen --target=python https://example.com # generate Python instead
```

Treat recorded code as a draft. It has no notion of which values are test data, it records every incidental click, and it does not know which waits matter. Our article on the [Playwright recorder](https://testingbot.com/resources/articles/playwright-recorder) goes through cleaning a recording into a maintainable test.

Codegen recorded `getByRole('link', { name: 'Order #4821' })`. The order number comes from whatever data the test environment holds today. What should you do with it?

 Keep it: codegen prefers role locators, so it is stable Replace it with the XPath of the link Make the test create the order (or locate the row by data the test controls) instead of relying on that number

Recorded code has no idea which values are test data. A role locator is only stable if its name is; XPath would be stable in neither sense.

## A debugging routine that works[#](https://testingbot.com#a-debugging-routine-that-works)

1. Read the error. Playwright's messages usually name the locator and the condition that was not met.
2. Reproduce in UI mode with the failing project selected.
3. If it only fails on CI, download the report artifact and open the trace.
4. If it passes on retry, it is flaky: compare the failing attempt's trace with the passing one and find the state you did not wait for.

When tests run on a remote grid, you get additional artifacts per session, such as a video of the whole screen and the browser's logs; our guide to [debugging Playwright tests on TestingBot](https://testingbot.com/support/web-automate/playwright/debug-tests) lists them.

[Previous · Chapter 5 Fixtures, Hooks and Logging In Once](https://testingbot.com/resources/courses/playwright/fixtures-hooks-and-authentication) [Next · Chapter 7 Multiple Browsers, Workers and Sharding](https://testingbot.com/resources/courses/playwright/browsers-parallelism-and-sharding)

On this page

1. [UI mode: the everyday tool](https://testingbot.com#ui-mode-the-everyday-tool)
2. [The Inspector: step through a test](https://testingbot.com#the-inspector-step-through-a-test)
3. [Trace viewer: debugging failures after the fact](https://testingbot.com#trace-viewer-debugging-failures-after-the-fact)
4. [Make traces readable with test.step](https://testingbot.com#make-traces-readable-with-test-step)
5. [Codegen: discover selectors by clicking](https://testingbot.com#codegen-discover-selectors-by-clicking)
6. [A debugging routine that works](https://testingbot.com#a-debugging-routine-that-works)

Certification

This course has a free, hands-on certificate.

[How it works](https://testingbot.com/resources/courses/playwright/certification)
