# Playwright Cheat Sheet

Course: [Playwright](https://www.devtutorials.ai/learn/playwright) · Section: Advanced Playwright and AI
Source: https://www.devtutorials.ai/learn/playwright/advanced/playwright-cheat-sheet

> A one-page reference for the whole course: CLI commands, test structure, locators, actions, assertions, configuration, fixtures, authentication, network mocking, API testing, debugging, reporting and CI, with runnable examples and three review exercises.

## Playwright Cheat Sheet

- This page collects the most used commands and APIs from the whole course, in one place.
- Each section has a short table and a box you can run. The lesson that teaches it in depth is named at the start.
- Keep it open while you write tests.

**Simple lesson:** look things up here. Learn them in the lesson named next to each section.

### A Real-World Example

- One test can use almost everything from this course: a logged-in user, a mocked API, locators, actions and web-first assertions.

### What You Will Find

<figure className="plw-fig">
<svg viewBox="0 0 640 210" role="img" aria-labelledby="fig-plw-map" style={{ width: "100%", maxWidth: 640, height: "auto" }}>
  <title id="fig-plw-map">Animation: the eight chapters of the course light up one after another, from Foundations to Advanced and AI</title>
  {[["Foundations", "1–3"], ["Core Test Writing", "4–6"], ["Test Structure", "7–9"], ["Browser Control", "10–11"], ["Auth, Network, API", "12–14"], ["Test Design", "15–16"], ["Debug, Report, CI", "17–19"], ["Advanced and AI", "20–23"]].map(([name, lessons], i) => (
    <g key={i}>
      <rect x={15 + (i % 4) * 155} y={20 + Math.floor(i / 4) * 95} width="145" height="80" rx="10" style={{ fill: "var(--plw-blue-soft)", stroke: "var(--plw-blue)" }} opacity="0.4">
        <animate attributeName="opacity" values={`0.4;0.4;1;1;0.4`} keyTimes={`0;${(i * 0.1).toFixed(2)};${(i * 0.1 + 0.04).toFixed(2)};0.95;1`} dur="9s" repeatCount="indefinite" />
      </rect>
      <text x={87 + (i % 4) * 155} y={55 + Math.floor(i / 4) * 95} textAnchor="middle" style={{ fill: "var(--plw-blue)", fontFamily: "var(--sans)", fontSize: 12, fontWeight: 700 }}>{name}</text>
      <text x={87 + (i % 4) * 155} y={75 + Math.floor(i / 4) * 95} textAnchor="middle" style={{ fill: "var(--ink2)", fontFamily: "var(--sans)", fontSize: 11 }}>Lessons {lessons}</text>
    </g>
  ))}
</svg>
<figcaption>Eight chapters, 23 lessons. Each section below points back to the lesson that teaches it.</figcaption>
</figure>

1. CLI commands and test structure.
2. Locators and actions.
3. Assertions.
4. Configuration and fixtures.
5. Authentication, network and API.
6. Debugging, reporting and CI.

## Commands and Structure

### CLI Commands

| Command | What it does |
|---|---|
| `npm init playwright@latest` | Create a project |
| `npx playwright install --with-deps` | Install browsers and Linux libraries |
| `npx playwright test` | Run all tests |
| `npx playwright test cart.spec.ts:12` | Run the test on line 12 |
| `--project=chromium` | One project only |
| `-g "@smoke"` / `--grep-invert` | Filter by title or tag |
| `--headed`, `--debug`, `--ui` | Watch, step through, or open UI Mode |
| `--workers=2`, `--shard=1/3` | Parallel workers, split across machines |
| `--repeat-each=10`, `--retries=2` | Hunt flaky tests, retry failures |
| `--trace on`, `-u` | Record traces, update snapshots |
| `--last-failed`, `--list` | Rerun failures, list tests |
| `npx playwright show-report` | Open the HTML report |
| `npx playwright show-trace trace.zip` | Open a trace |
| `npx playwright codegen <url>` | Record a test |

### Test Structure

| Code | Use |
|---|---|
| `test('title', async ({ page }) => { ... })` | A test |
| `test.describe('group', () => { ... })` | Group tests |
| `test.beforeEach` / `afterEach` / `beforeAll` / `afterAll` | Hooks |
| `test.step('name', async () => { ... })` | Named steps in reports |
| `test.only`, `test.skip`, `test.fixme`, `test.fail`, `test.slow` | Focus and annotations |
| `test('title', { tag: '@smoke' }, ...)` | Tags |
| `test.use({ ... })` | Options for a file or group |

**Simple lesson:** `npx playwright test` with flags runs anything. `test`, `describe`, hooks and steps shape a file.

## Locators and Actions

### Locators (best first)

| Locator | Example |
|---|---|
| Role and name | `page.getByRole('button', { name: 'Log in' })` |
| Label | `page.getByLabel('Email')` |
| Placeholder | `page.getByPlaceholder('Search by name')` |
| Text | `page.getByText('Your cart is empty')` |
| Alt text, title | `page.getByAltText('Steel water bottle')`, `page.getByTitle('...')` |
| Test id | `page.getByTestId('cart-count')` |
| CSS (last resort) | `page.locator('.price')` |
| Filter | `.filter({ hasText: 'Yoga Mat' })`, `.filter({ has: locator })` |
| Chain | `card.getByRole('button', { name: 'Add to cart' })` |
| Pick one | `.first()`, `.last()`, `.nth(2)` |
| Frames | `page.frameLocator('iframe[name="payment"]').getByLabel('Card number')` |

### Actions

| Action | Example |
|---|---|
| Click | `.click()`, `.dblclick()`, `.click({ button: 'right' })` |
| Type | `.fill('text')`, `.pressSequentially('abc')`, `.clear()` |
| Keys | `.press('Enter')`, `page.keyboard.press('Control+K')` |
| Choose | `.check()`, `.uncheck()`, `.selectOption('Shoes')` |
| Other | `.hover()`, `.focus()`, `.dragTo(target)`, `.setInputFiles(path)` |

**Simple lesson:** prefer roles, labels and test ids. Filter and chain to reach one element.

## Assertions

### Web-First Assertions (they retry)

| Page or locator check | Example |
|---|---|
| Visible, hidden | `toBeVisible()`, `toBeHidden()` |
| Enabled, checked, focused | `toBeEnabled()`, `toBeChecked()`, `toBeFocused()` |
| Text | `toHaveText('...')`, `toContainText('...')`, `toHaveText([...])` |
| Count, value, attribute | `toHaveCount(3)`, `toHaveValue('...')`, `toHaveAttribute('href', '/cart')` |
| Page | `toHaveURL('/account')`, `toHaveTitle(/Shop/)` |
| Structure, look | `toMatchAriaSnapshot(...)`, `toHaveScreenshot('x.png')` |
| API response | `await expect(response).toBeOK()` |

- Always `await` them. Plain value checks (`toBe`, `toEqual`, `toContain`) do not retry.
- `expect.soft()` keeps going after a failure. `expect.poll()` and `toPass()` retry your own code.

`expect(await locator.textContent()).toBe('...')` reads once and does not retry. Write `await expect(locator).toHaveText('...')` instead.

## Configuration and Fixtures

### Config Options You Will Use

| Option | Example |
|---|---|
| Where tests live | `testDir: './tests'` |
| Server and timeouts | `use: { baseURL, actionTimeout: 5_000 }`, `timeout: 30_000`, `expect: { timeout: 5_000 }` |
| CI behaviour | `forbidOnly: !!process.env.CI`, `retries: process.env.CI ? 2 : 0`, `workers` |
| Debug data | `use: { trace: 'on-first-retry', screenshot: 'only-on-failure', video: 'retain-on-failure' }` |
| Browsers and devices | `projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }]` |
| Setup first | `{ name: 'setup', testMatch: /.*\.setup\.ts/ }`, `dependencies: ['setup']` |
| Reports | `reporter: [['list'], ['html', { open: 'never' }]]` |
| Start the app | `webServer: { command: 'npm start', url: 'http://localhost:3000' }` |

### Fixtures

```ts
export const test = base.extend<{ cartPage: CartPage }>({
  cartPage: async ({ page }, use) => {
    const cartPage = new CartPage(page); // setup
    await use(cartPage);                // the test runs here
    // teardown goes after use()
  },
});
```

- Built-in fixtures: `page`, `context`, `browser`, `request`, `browserName`, `baseURL`.
- `{ scope: 'worker' }` shares one value per worker. `{ auto: true }` runs without being asked for.

**Simple lesson:** the config sets defaults for every test. Fixtures give each test what it needs.

## Authentication, Network and API

### Authentication

| Task | Code |
|---|---|
| Save a login | `await page.context().storageState({ path: 'playwright/.auth/user.json' })` |
| Use it | `use: { storageState: 'playwright/.auth/user.json' }` |
| Logged out | `test.use({ storageState: { cookies: [], origins: [] } })` |

### Network

| Task | Code |
|---|---|
| Mock JSON | `await page.route('**/api/products*', r => r.fulfill({ json: [] }))` |
| Fail a call | `route.fulfill({ status: 500 })` or `route.abort()` |
| Change a call | `route.continue({ headers: { ... } })` |
| Edit real data | `const res = await route.fetch(); route.fulfill({ response: res, json })` |
| Wait for a call | `const res = page.waitForResponse('**/api/x'); await click; await res` |
| Offline | `await context.setOffline(true)` |

### API

| Task | Code |
|---|---|
| GET | `const res = await request.get('/api/products/2')` |
| POST JSON | `await request.post('/api/orders', { data: { productId: 1 } })` |
| Check | `await expect(res).toBeOK()`, `res.status()`, `await res.json()` |
| Token for all calls | `test.use({ extraHTTPHeaders: { Authorization: 'Bearer ...' } })` |
| Shares page cookies | `page.request.post(...)` |

**Simple lesson:** storage state for logins, `page.route` for mocks, `request` for the API.

## Debugging, Reporting and CI

| Need | Use |
|---|---|
| Why did it fail? | Error, call log, code frame, then the trace |
| Step through | `--debug`, `await page.pause()`, VS Code breakpoints |
| All-in-one | `npx playwright test --ui` |
| Record what happened | `trace: 'on-first-retry'`, then `show-trace` |
| Page messages | `page.on('console', m => ...)`, `page.on('pageerror', e => ...)` |
| Reports | `list` or `dot` in the terminal, `html` for people, `junit` and `json` for tools |
| CI | `npm ci`, `npx playwright install --with-deps`, `npx playwright test`, upload the report |
| Faster CI | `fullyParallel: true`, `--shard=x/y`, blob reports and `merge-reports` |
| Time-based pages | `page.clock.setFixedTime(...)`, `install()`, `fastForward()` |
| Accessibility | `new AxeBuilder({ page }).analyze()`, `toMatchAriaSnapshot()` |
| AI agents | `@playwright/mcp`, `playwright-cli`, `npx playwright init-agents --loop=claude` |

Stuck on a test? Run it once with `--trace on` and open the trace. Most mysteries are solved by looking at the page snapshot at the failing step.

Playwright changes every month. When an option here does not work, check the release notes for your version in the Playwright docs.

**Simple lesson:** the trace answers most questions. Reports and CI share the answers with your team.

## TL;DR

- CLI: `npx playwright test` plus flags. `show-report` and `show-trace` open results.
- Locators: roles, labels and test ids first. Filter and chain.
- Assertions: `await expect(locator)...` retries. Plain `expect` does not.
- Config and fixtures set up every test. Storage state logs in once.
- `page.route` mocks, `request` calls the API, traces explain failures.
- CI: install, test, upload. Shard for speed.

## Common mistakes

| Mistake | What happens | Fix |
|---|---|---|
| CSS and XPath first | Tests break when the design changes | Roles, labels, test ids |
| Missing `await` | Flaky tests and lost failures | Await every action and web-first assertion |
| `waitForTimeout()` | Slow and still flaky | Web-first assertions |
| Logging in through the form everywhere | Slow suites | Storage state and a setup project |
| No traces or reports on CI | Failures nobody can explain | Upload `playwright-report` and traces |

## Check Yourself

## Exercises

## What to Remember

- This page is a map. The named lessons are the territory.
- Roles and labels, web-first assertions, no fixed waits.
- Log in once, mock what is rare, test the API directly.
- Traces, reports and CI turn failures into fixes.
- You now know Playwright from the first test to AI agents. Keep practising on real projects.
