Codegen command syntax for test generation
Run the `codegen` command followed by the URL of the website to generate tests for. The URL is optional and can be added directly in the browser window if omitted. Example: `npx playwright codegen demo.playwright.dev/todomvc`
Codegen opens browser and Inspector for test generation
Codegen opens a browser window for interaction and the Playwright Inspector for recording, copying, and managing generated tests.
Actions recordable in Codegen
With the test generator you can record actions like click or fill by interacting with the page.
Codegen Inspector buttons for test management
The Codegen Inspector provides 'record' button to stop recording, 'copy' button to copy generated code to editor, and 'clear' button to clear code and start recording again. Once finished, close the Playwright Inspector window or stop the terminal command.
Pick Locator workflow in Codegen
To generate locators: press the 'Record' button to stop recording and the 'Pick Locator' button will appear. Click 'Pick Locator' and hover over elements in the browser window to see the locator highlighted underneath each element. Click the element you want to locate and the code for that locator will appear in the locator playground. Edit the locator in the playground to fine-tune it and see the matching element highlighted in the browser window. Use the copy button to copy the locator and paste it into your code.
Codegen emulation capabilities
Codegen can generate tests using emulation for specific viewports, devices, color schemes, geolocation, language, or timezone. The test generator can also preserve authenticated state.
Show Browser option in VS Code
Enable the 'Show Browser' option in the Playwright sidebar to watch tests execute in a live browser window. Disable it to run tests in headless mode, where tests run in the background without opening a visible browser window.
Recording a new test with CodeGen in VS Code
Click 'Record new' in the Playwright sidebar. A browser window opens. As you interact with the page, Playwright automatically generates test code. You can also generate assertions from the recording toolbar.
Record at cursor feature in VS Code
Place your cursor inside an existing test and click 'Record at cursor' to add new actions at that specific point in the test.
Switching between multiple configuration files in VS Code
If you have multiple playwright.config.ts files, use the gear icon in the Playwright sidebar to switch between them. This allows you to easily work with different test suites or environments.
Opening Test Explorer and Playwright sidebar in VS Code
Click the Testing icon in the VS Code Activity Bar to open the Test Explorer. This view shows your tests and contains the Playwright sidebar for managing projects, tools, and settings.
Running a single test in VS Code
Click the green 'play' icon next to any test in the Test Explorer to run it. The play button will change to a green checkmark if the test passes or a red X if it fails. The test name displays how long the test took to run. The Test Results panel automatically opens at the bottom showing a summary including the number of tests that ran, passed, failed, or were skipped, along with total execution time.
Running all tests in VS Code at different levels
Click the play icon next to a specific test file to run all tests within that file. Click the play icon at the very top of the Test Explorer to run all tests across the entire project.
Running tests on multiple browsers (projects) in VS Code
In the Playwright sidebar, check the boxes for the projects (browsers) you want to test against. Projects represent different browser configurations—each typically corresponds to a specific browser like Chromium, Firefox, or WebKit with its own settings such as viewport size or device emulation. When you run a test, it executes across all selected projects, allowing verification that the application works consistently across different browsers and configurations.
Codegen --user-data-dir for existing browser profile
Use the --user-data-dir option to set a fixed user data directory for the browser session, allowing codegen to use an existing browser profile with authentication state already present. Example: npx playwright codegen --user-data-dir=/path/to/your/browser/data/ github.com/microsoft/playwright. As of Chrome 136, the default user data directory cannot be accessed via automated tooling like Playwright; a separate user data directory must be created for testing.
Page.pause() enables custom setup codegen recording
Call page.pause() to open a codegen controls window for recording in non-standard setups, such as when using custom context routing. The browser must be launched in headed mode (headless: false).
Custom setup codegen example with BrowserContext.route
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext({ /* pass any options */ });
await context.route('**/*', route => route.continue());
const page = await context.newPage();
await page.pause();
})();
Codegen assertion types: visibility, text, value
The test generator can create three types of assertions: 'assert visibility' to check that an element is visible, 'assert text' to check that an element contains specific text, and 'assert value' to check that an element has a specific value.
Codegen command syntax
Run codegen with: npx playwright codegen [URL]. The URL is optional; you can run without it and add the URL directly in the browser window instead.
Codegen locator selection prioritizes role, text, test id
Playwright's test generator prioritizes role, text, and test id locators when generating tests. If multiple elements match the locator, the generator improves the locator to make it resilient and uniquely identify the target element.
VS Code extension for test recording
Playwright provides a VS Code extension available on the VS Code Marketplace (ms-playwright.playwright) that allows generating tests directly from VS Code.
Record new test in VS Code creates test-1.spec.ts
Clicking the Record new button from the Testing sidebar in VS Code creates a test-1.spec.ts file and opens a browser window for recording.
Record at Cursor in VS Code extension
The Record at cursor button in the VS Code Testing sidebar allows recording actions from a specific point in a test. If the browser window is not already open, first run the test with 'Show browser' checked before clicking Record at cursor.
Pick locator feature in test generator
Click the Pick locator button from the testing sidebar, hover over elements in the browser to see the locator highlighted, click the element to select it, then press Enter to copy the locator to clipboard or press Escape to cancel.
Codegen opens browser and Inspector windows
When running the codegen command, two windows are opened: a browser window where you interact with the website to test, and the Playwright Inspector window where you can record tests and copy the generated code.
Codegen viewport size emulation with --viewport-size
Use the --viewport-size option to generate tests with a specific viewport size, for example: npx playwright codegen --viewport-size="800,600" playwright.dev. Playwright opens the browser with the specified width and height in non-responsive mode since tests must run under the same conditions.
Codegen device emulation with --device
Use the --device option to record tests while emulating a mobile device, which sets viewport size, user agent, and other properties. Example: npx playwright codegen --device="iPhone 13" playwright.dev
Codegen color scheme emulation with --color-scheme
Use the --color-scheme option to record tests in a specific color scheme. Example: npx playwright codegen --color-scheme=dark playwright.dev
Codegen emulation options: timezone, geolocation, language
Use --timezone, --geolocation, and --lang options to emulate timezone, location, and language. Example: npx playwright codegen --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" bing.com/maps
Codegen --save-storage saves authentication state
Use the --save-storage option to save cookies, localStorage, and IndexedDB data at the end of a codegen session. Example: npx playwright codegen github.com/microsoft/playwright --save-storage=auth.json. This file contains sensitive information and should be added to .gitignore or deleted after use.
Codegen --load-storage reuses authentication state
Use the --load-storage option to restore cookies, localStorage, and IndexedDB data from a previously saved storage file, bringing web apps to an authenticated state without needing to login again. Example: npx playwright codegen --load-storage=auth.json github.com/microsoft/playwright
Use codegen to record and generate Playwright scripts
You can use the `npx playwright codegen` command-line tool to record user interactions and automatically generate JavaScript code. For example: `npx playwright codegen wikipedia.org`.
Record and generate tests with Codegen
Use `npx playwright codegen [options] [url]` to record actions and generate tests. This supports multiple languages for code generation.
Codegen options table
Options for `npx playwright codegen`:
| Option | Description |
| -b, --browser <name> | Browser to use: chromium, firefox, or webkit (default: chromium) |
| -o, --output <file> | Output file for the generated script |
| --target <language> | Language to use: javascript, playwright-test, python, etc. |
| --test-id-attribute <attr> | Attribute to use for test IDs |
Codegen generates locators and frame locators
Playwright Codegen now generates locators and frame locators for recorded interactions.
Example: generated test with describe and comments
Example showing generated test structure with describe, numbered step comments, and assertions:
```ts
// spec: specs/basic-operations.plan.md
// seed: tests/seed.spec.ts
import { test, expect } from './fixtures'; // or '@playwright/test' if no fixtures file
test.describe('Signing in and out', () => {
test('should sign in', async ({ page }) => {
// 1. Navigate to the application
// (handled by the seed fixture)
// 2. Type 'John Doe' into the username field
await page.getByRole('textbox', { name: 'username' }).fill('John Doe');
// 3. Type password
await page.getByRole('textbox', { name: 'password' }).fill('TestPassword');
// 4. Press Enter to submit
await page.getByRole('textbox', { name: 'password' }).press('Enter');
await expect(page.getByRole('heading')).toContainText('Welcome, John Doe!');
});
});
```
playwright-cli test generation workflow: plan → generate → heal
The end-to-end workflow for authoring and maintaining Playwright tests with `playwright-cli` consists of three phases: Plan (explore the app, produce a spec file describing what to test), Generate (turn a spec into Playwright test files, update the spec if it is vague or stale), and Heal (diagnose failing tests, fix the code, reconcile the spec with reality). All three phases lean on the same mechanic: run `npx playwright test --debug=cli` in the background, then `playwright-cli attach tw-XXXX` to drive the paused page interactively.
Every playwright-cli action emits equivalent Playwright TypeScript code
Every action performed with `playwright-cli` generates corresponding Playwright TypeScript code. This code appears in the output and can be copied directly into test files. For example, `playwright-cli fill e1 'user@example.com'` generates `await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');`.
Use semantic locators in generated test code
Generated code from `playwright-cli` uses role-based locators when possible, which are more resilient. Prefer `page.getByRole('button', { name: 'Submit' }).click()` over fragile CSS selectors like `page.locator('#submit-btn').click()`.
Explore page structure with playwright-cli snapshot before recording
Take snapshots to understand the page structure before recording actions. Use `playwright-cli snapshot` to review the element structure and get element references (e.g., e1, e2, e3) that can be used with other commands like `playwright-cli click e5`.
Add assertions manually after generating actions
Generated code captures actions but not assertions. Add expectations in the test using recommended matchers: `toBeVisible()` (element is rendered and visible), `toHaveText(text)` (element text content matches), `toHaveValue(value) / toBeEmpty()` (input/select value matches), `toBeChecked() / toBeUnchecked()` (checkbox state matches), `toMatchAriaSnapshot(snapshot)` (page or locator matches a partial accessibility snapshot). When asserting text content, ensure the generated locator does not contain text from the element itself; `getByTestId()` or `getByLabel()` usually work well. When locator is text-based, prefer `toBeVisible()` instead.
Generate locators and capture assertion values with playwright-cli commands
Use `playwright-cli --raw generate-locator <target>` to produce the locator expression for an assertion. Use `playwright-cli --raw eval "el => el.textContent" <ref>` to capture expected text content for `toHaveText`. Use `playwright-cli --raw eval "el => el.value" <ref>` to capture expected input value for `toHaveValue`/`toBeEmpty`. Use `playwright-cli --raw snapshot` or `playwright-cli --raw snapshot <ref>` to capture expected aria snapshot for `toMatchAriaSnapshot` or `toBeChecked`.
Seed test is minimal test landing page in starting state
A seed test is a minimal test that lands the page in the state every scenario starts from: navigation to the app, any required login, feature flags, etc. Scenarios assume a fresh start after the seed. The `--debug=cli` pause occurs inside this test, so the seed is where every planning and generation session begins. Minimum viable seed is a test that calls `page.goto()` to the app URL. Preferred approach is to push navigation into a fixture so scenario tests reuse it.
Check workspace has Playwright installed before codegen
Before anything else, check the workspace has Playwright installed by running `test -f playwright.config.ts || test -f playwright.config.js` or `npx --no-install playwright --version`. If there is no Playwright install, bootstrap one with `npm init playwright@latest` and let the user pick the defaults.
Spec file structure for test planning
Save spec files under `specs/<feature>.plan.md`. Structure includes: heading with feature name, Application Overview section (one paragraph describing what the feature does and why it matters), Test Scenarios section with numbered groups. Each group contains Seed reference, and numbered scenarios with kebab-case names, File path, and Steps (numbered list with concrete user steps and expect bullets for observable outcomes).
Spec file naming and scenario naming conventions
Scenario names are kebab-case and must match the test file name. For example, `should-add-single-todo` scenario name corresponds to `should-add-single-todo.spec.ts` test file. Each scenario is independent and starts from the seed's fresh state; never chain scenarios.
Write spec file steps at user level, not API level
In spec file steps, write at the user level (example: 'Type "Buy milk" into the input'), not the API level (example: 'call fill'). Put observable outcomes in `- expect:` bullets; each becomes an assertion during generation.
Generate scenarios one at a time, never in parallel
Generate each target scenario sequentially, never in parallel. Scenarios share the seed session, so parallel generation is unsafe. For each scenario: launch the seed in debug mode with `PLAYWRIGHT_HTML_OPEN=never npx playwright test <seed-file> --debug=cli` in the background, then `playwright-cli attach tw-XXXX`.
Always use seed test via --debug=cli, not direct playwright-cli open
Always go through the seed test to capture any custom setup done there. Do not just open the app URL directly with `playwright-cli open`; launch via `PLAYWRIGHT_HTML_OPEN=never npx playwright test <seed-file> --debug=cli` instead to ensure the seed setup is executed.
Walk spec steps with playwright-cli treating app as source of truth
Walk each scenario's Steps one by one with `playwright-cli`, treating the spec as the plan and the live app as the source of truth. If a step is vague ('click the button' — which button?), references an element that no longer exists, or contradicts the app's actual behaviour, use judgement: update the spec to match what the app really does, then keep going. Editing the spec mid-generation is expected.
Generated test file structure and naming
Generated test file should be located at path given in spec, with one test per file. File path, describe name, and test name come verbatim from the spec (minus the ordinal). Prefix each numbered step with a `// N. <step text>` comment before its actions. Use the describe group name verbatim from the spec (no ordinal prefix). Import from `./fixtures` if the project has a fixtures file; otherwise import from `@playwright/test`.
Close CLI session and stop background test between scenarios
After generating one scenario's test code, close the CLI session and stop the background test before moving to the next scenario. This ensures each test run starts from a clean page.
Run generated tests after generation to check for failures
After generation, run the new tests once with `PLAYWRIGHT_HTML_OPEN=never npx playwright test tests/<group>/<scenario>.spec.ts`. Any failure goes to the Heal phase.
Fix failing tests one at a time, not in parallel
Record the list of failing `<file>:<line>` entries and process them one at a time. Do not attempt parallel fixes — shared state and the single CLI session make that fragile.
Debug failing tests with playwright-cli in background
Run the single failing test in debug mode in the background with `PLAYWRIGHT_HTML_OPEN=never npx playwright test tests/<group>/<scenario>.spec.ts:<line> --debug=cli`, wait for 'Debugging Instructions' and the `tw-XXXX` session name, then `playwright-cli attach tw-XXXX`. The test is paused at the start. Step forward or run until just before the failing action or assertion, then diagnose using `playwright-cli snapshot` (check if element changed/moved/renamed), `playwright-cli console` (app-side errors), `playwright-cli requests` (failed request/wrong payload), or `playwright-cli show --annotate` (ask user to point somewhere).
Common causes of test failures during healing
Common causes of failing tests include: selector drift, new wrapper element, label/ARIA rename, timing (transition, async load), assertion text updated in the app, and test data leaking between runs.
Reconcile test changes with spec file
After fixing a test, open the spec file referenced by the `// spec:` header in the test file and locate the matching scenario. If the fix was purely technical (locator drift, better assertion shape) and the spec's user-level behaviour still matches the app, leave the spec alone. If the fix changed user-visible steps, inputs, order, or expected outcomes that the spec describes, update the spec to match reality; keep the scenario id and file path stable, only the step/expect lines change. If unclear whether the app change is intentional (spec is stale) or a regression (test was right, app is wrong), stop and ask the user, providing the scenario id, the spec lines that no longer match, and the observed app behaviour.
Never use sleeps or networkidle as fix for failing tests
Never skip hooks or add sleeps as a fix for failing tests. Never use `networkidle`.
Mark tests as fixme when app has confirmed bug
If after thorough investigation you are confident the test is correct but the app is wrong and the user has confirmed it's a bug, mark the test `test.fixme(...)` with a comment pointing at the user's decision or issue link. Never silently skip.
Example: generated test with manual assertions
Example showing how to combine generated actions with manual assertions:
```typescript
// Generated action
await page.getByRole('button', { name: 'Submit' }).click();
// Manual assertions:
await expect(page.getByRole('alert', { name: 'Success' })).toBeVisible();
await expect(page.getByTestId('main-header')).toHaveText('Welcome, user');
await expect(page.getByRole('textbox', { name: 'Email' })).toHaveValue('user@example.com');
await expect(page.getByRole('checkbox', { name: 'Enable notifications' })).toBeChecked();
// toMatchAriaSnapshot on the whole page, finds a matching region
await expect(page).toMatchAriaSnapshot(`
- heading "Welcome, user"
- link /\\d+ new messages?/
- button "Sign out"
`);
// toMatchAriaSnapshot scoped to a region
await expect(page.getByRole('navigation')).toMatchAriaSnapshot(`
- link "Home"
- link /\\d+ new messages?/
- link "Profile"
`);
```