Skip to content

H.5 — Playwright essentials

Status: drafted · Time: 3 min · Audience: engineer Outcome: Everyday Playwright commands plus four debugging moves.

Printable card · Companion to G.12 — E2E testing with Playwright + Claude Code. The everyday commands plus four debugging moves.


CommandWhat it does
npx playwright testRuns all tests
npx playwright test <file>Runs tests in a single file
npx playwright test --grep "<name>"Runs only tests whose name matches the pattern
npx playwright test --uiOpens the Playwright UI for interactive runs
npx playwright test --debugRuns tests in debug mode with the inspector
npx playwright test --headedRuns tests with the browser visible (default is headless)
npx playwright codegen <url>Records actions into a test script
npx playwright show-reportOpens the HTML report from the last run

When a test is failing and you cannot tell why:

MoveWhat it does
Run with --headedSee the browser. Most “why is this failing” questions resolve when you can see what is happening.
Run with --uiTime-travel through the test steps. Inspect the DOM at each point.
Add page.pause() in the testThe test stops at this line; you can interact with the browser and the inspector manually.
Read the traceAfter a failure, the trace file in test-results/ has screenshots, network logs, and DOM snapshots.

A typical Playwright test:

import { test, expect } from '@playwright/test';
test('user can sign in and see dashboard', async ({ page }) => {
await page.goto('https://example.com/login');
await page.getByLabel('Email').fill('user@example.com');
await page.getByLabel('Password').fill('test-password');
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});

The shape: navigate, interact, assert. Three steps. A test that does more than that is usually doing too much.


Playwright’s locators are stable. Prefer accessible ones:

BestWorse
page.getByRole('button', { name: 'Sign in' })page.locator('.btn.btn-primary')
page.getByLabel('Email')page.locator('#email-input')
page.getByText('Welcome back')page.locator('div.welcome-msg')

Why: role and label locators survive CSS refactors. Class-name locators do not.


For tests that need authenticated state, use tests/seed.spec.ts (see G.14). The seed pre-establishes state once; downstream tests skip the setup. This saves 10,000 tokens of agent context per test run.


Not a Playwright manual. The full Playwright docs are at playwright.dev. This card covers what you actually need for G.12.

Not a substitute for the chapter. G.12 covers the patterns at the level of why they work. G.13 covers using a skill to author tests one-shot.

Not the right tool for unit tests. Playwright is for end-to-end tests against a running app. For unit tests, use the project’s unit testing framework.


Remember: when a test is failing, run with --headed or --ui first. Most questions resolve when you can see the browser.

Up to: ↑ Appendix H · Companion: G.12 — E2E testing