playwright-automation

Write production-grade Playwright tests in TypeScript: Page Object Model, fixtures, auto-waiting, user-facing locators, parallel execution, CI integration, sharding, and 2025-2026 feature awareness. Includes an explicit "do not" list for AI agents. Use when: "Playwright," "write E2E test," "page obj

By petrkindlmann · 1,023 installs

npx skills add petrkindlmann/qa-skills --skill playwright-automation

Source repository · Upstream listing

<objective How an expert agent writes stable, maintainable, production grade Playwright tests in TypeScript. The failure this prevents: AI agents reflexively reach for the three patterns that produce suites which pass once and flake forever — never use waitForTimeout , never default to CSS selectors, and avoid the legacy page.click() family. This skill encodes the auto waiting, user facing locator, fixture based discipline that makes a suite survive a refactor. </objective Discovery Questions Check .agents/qa project context.md first — if it exists, use it and skip any question answered there. Then ask only what's missing: 1. TypeScript or JavaScript? TypeScript is strongly recommended — it catches locator and assertion mistakes at compile time, and every example here assumes it. 2. Which browsers? Chromium for local dev; add Firefox and WebKit in CI. Mobile viewports are separate Playwright projects, not separate test files — they change the device descriptor. 3. Existing suite or fresh start? Migrating from Cypress/Selenium, rewrite the flakiest tests first; never big bang. Changes the sequencing entirely. 4. Single site or multi site? Multi site needs shared fixtures and per site config objects — see references/multi site architecture.md . Core Principles 1. User facing locators first. getByRole getByLabel getByTestId CSS (last resort). Locators must reflect what the user sees, not how the DOM is structured. See references/selector strategies.md . 2. Auto waiting — NEVER use waitForTimeout . Every Playwright action and web first assertion auto waits. If you think you need a timeout, you need a better locator or assertion. 3. Test isolation. Each test gets a fresh BrowserContext . Tests must never depend on other tests' state or execution order. 4. Parallel by default, serial only when necessary. Use fullyParallel: true . Reserve test.describe.serial for flows that genuinely cannot be isolated (rare). 5. Fixtures for setup, not hooks. Fixtures compose, provide type safety, and tear down automatically. Prefer them over beforeEach / afterEach for anything non trivial. See references/fixtures and projects.md . Calibrate to your team maturity (set team maturity in .agents/qa project context.md ): startup — Chromium only, 5–10 critical path tests, basic CI run on PR. Skip sharding and visual baselines until the suite is stable. growing — Chromium + Firefox, POM structure, parallel execution, sharding in CI, HTML report artifacts. established — Full browser matrix, auth fixtures, API mocking layer, visual regression baseline, trace on failure, flakiness tracking. Project Structure playwright.config.ts The blob reporter in CI is what makes sharded runs mergeable — see the sharding section. The setup project writes storageState once before the browser projects depend on it. Global setup (storageState) This is the setup project pattern: the setup project (or a globalSetup file) runs UI login once, and every browser project replays the saved cookies/localStorage via storageState in config. For multi role auth (admin/user/guest) and token seeding, see references/auth patterns.md . Page Object Model Component objects represent reusable UI fragments (modals, tables, nav). They take a root Locator , not a Page : Compose, don't inherit deep. A page holds its components; it does not extend a five level hierarchy: Inject page objects via fixtures , not constructors in test files: POM methods return state (locators, values); they do not assert. Assertions live in the test so failures point at the test, not the page object. Test Patterns Form interactions with test.step Wrap logical action groups in test.step() for readable trace viewer output: API mocking See references/network and mocking.md for HAR replay and conditional routing. Authenticated APIRequestContext fixture For seeding data or asserting backend state without driving the UI, inject a pre authenticated APIRequestContext . Acquire the token in the fixture; never hardcode it: Tags and annotations Assertions Always prefer web first assertions — they auto retry until the condition holds or the timeout expires: Soft assertions collect all failures instead of stopping at the first: ARIA snapshots verify accessibility tree structure and catch semantic regressions: Visual regression (one liner; defer the workflow) Playwright's built in toHaveScreenshot auto retries and writes a baseline on first run. Mask dynamic regions; do not precede it with waitForTimeout : For baseline management, thresholds ( maxDiffPixelRatio , maskColor , stylePath ), and review workflows, use visual testing — that is where visual baselines belong. Accessibility scan (axe; deep audits live elsewhere) ARIA snapshots above check structure, not WCAG rules. For rule based scanning, add @axe core/playwright : For WCAG levels, rule tuning, and remediation guidance, use accessibility testing . Parallel Execution & CI Sharding across CI nodes Split the suite across matrix jobs, then merge the shard reports into one HTML report. Sharding earns its place at growing + maturity; a startup suite of 5–10 tests should not shard. Each shard uploads its blob report/ ; a final job runs npx playwright merge reports reporter=html ./all blob reports . The blob reporter (set in the config above) is what makes merge work — shard alone produces fragmented HTML reports. See references/ci recipes.md for the full GitHub Actions workflow, blob upload/download, and artifact patterns. Debugging Trace viewer: npx playwright show trace test results/.../trace.zip — timeline of actions, network, DOM snapshots, console. UI mode: npx playwright test ui — live, step by step, time travel. Debug flag: npx playwright test my test.spec.ts debug — headed, pauses each action. VS Code extension ms playwright.playwright — run/debug from gutter, pick locators, watch mode. page.pause() opens the Inspector mid test. Local only — never commit it. See references/debugging and triage.md for flaky test triage and artifact analysis. New Features (2025 2026) Current latest is Playwright 1.60.0 (May 2026). Pin the same version in package.json and your CI Docker image. Recent additions worth knowing: Version Feature What it does v1.45 Clock API page.clock.install() / fastForward() — control time without monkey patching Date v1.45 fail on flaky tests Fail the CI run if any test needed a retry to pass v1.46 only changed Run only tests affected by changed files (git diff aware) v1.46 ARIA snapshots toMatchAriaSnapshot() for accessibility tree assertions v1.48 routeWebSocket First class WebSocket interception (replaces CDP hacks) v1.55 Test Migrator Automated Cypress→/Selenium→Playwright via npx playwright migrate v1.56 Test Agents npx playwright init agents loop=claude\ vscode\ opencode — planner/generator/healer agents inside the coding agent's loop v1.57 Chrome for Testing default Headed uses chrome , headless uses chrome headless shell instead of bundled Chromium. Caveat: a high memory regression was reported (microsoft/playwright 38489) — pin a known good image tag for CI. v1.57 toHaveScreenshot options maskColor , stylePath , pathTemplate for masking color, custom stylesheet, and output path control v1.59 Screencast API page.screencast.start() / .stop() for mid test video with start/stop control — an alternative to recordVideo , not a replacement. Adds action annotations, chapter markers, custom HTML overlays, and screencast.showOverlays() / hideOverlays() . Useful for agent self verification: a coding agent can hand off a reviewable video receipt. v1.59 debug=cli Pause and attach so an agent can step through a test v1.60 locator.drop() Simulate an external file/clipboard drag and drop onto an element v1.60 tracing.startHar() HAR recording as a first class tracing API AI augmented authoring (Test Agents vs MCP) Two integration paths — pick based on whether the agent runs inside your editor loop or drives a real browser remotely. Path A — Test Agents ( npx playwright init agents loop=claude ) : scaffolds planner/generator/healer agents the coding agent loads during its loop. Token efficient — no MCP server, no inter process traffic. Best for "Claude/VS Code/opencode writes Playwright tests for me." Path B — @playwright/mcp : an MCP server exposing browser actions to any MCP aware agent. Higher overhead (process boundary, JSON marshalling) but the right choice when the agent must drive a live browser interactively rather than author tests offline. Config: { "mcpServers": { "playwright": { "command": "npx", "args": ["@playwright/mcp@latest"] } } } in .mcp.json . For test failure repair, see test reliability . For first time generation from PRDs/specs, see ai test generation . Anti Patterns Design time mistakes that quietly rot a suite. The code level "never do X" list lives in references/anti patterns.md with BAD/GOOD pairs — load it when writing test bodies. 1. The God Page Object One class for the whole app turns into a 2000 line file every test imports and nothing can refactor safely. Split by page/feature and compose component objects. 2. POM methods that assert A page object whose methods call expect hides the assertion from the test. When it fails, the stack points at the page object, not the failing scenario. Return locators/state; assert in the test. 3. Asserting on implementation detail Tests keyed to CSS classes, DOM nesting, or internal IDs break on every refactor without a real behavior change. Assert what the user perceives — visible text, roles, URLs. 4. Fixtures that depend on test order A fixture that mutates shared module state, or assumes another test ran first, fails the moment tests parallelize or run in isolation. Each fixture must stand alone. 5. data testid where getByRole would work Sprinkling test ids onto buttons and headings that already have an accessible name skips the cheapest accessibility signal you get for free. Reserve getByTestId for elements with no stable role/label. The most damaging runtime mistake — synchronizing with waitForTimeout instead of an auto waiting locator: The other nine code level offenders (CSS over roles, page. over locators, force: true , shared state, per test login, locator.all() without a stability check, allTextContents() over toHaveText() , hitting real third party services, committed test.only ) are in references/anti patterns.md . Verification Run these against the generated artifact, smallest first: Enforce the "never do X" rules in CI with eslint plugin playwright — rules no wait for timeout , no force option , no element handle , no page pause turn this skill's prose bans into a failing lint. Done When playwright.config.ts exists with projects for at least Chromium (Firefox + WebKit added when targeting CI), and forbidOnly: !!process.env.CI . Page Object Model files live in e2e/pages/ (or equivalent), with component objects composed via a root Locator and no expect inside POM methods. grep rn 'waitForTimeout' e2e/ returns nothing, and eslint plugin playwright 's no wait for timeout is enabled. Every locator uses getByRole / getByLabel / getByTestId — grep rn 'page.locator(\ xpath=\ css=' e2e/ returns nothing (or only justified, commented exceptions). CI runs the suite on PR; at growing + maturity it shards across matrix jobs with the blob reporter and a merge reports step, uploading the HTML report as an artifact on failure. Related Skills visual