--- name: playwright description: Use when writing, debugging, or configuring E2E tests with Playwright. Trigger for any mention of end-to-end testing, browser automation, page objects, visual regression, storageState auth, playwright.config, or cross-browser testing. Also use when setting up E2E in CI, testing critical user flows, or debugging flaky browser tests. --- # Playwright E2E Testing ## Overview The definitive E2E testing reference for web apps built with Next.js, FastAPI, Django, NestJS, Express, and React. Covers test structure, locator strategy, authentication reuse, API mocking, visual regression, accessibility, CI sharding, and framework-specific setup. ## When to Use - Testing critical user flows end-to-end (login, checkout, onboarding) - Cross-browser testing (Chromium, Firefox, WebKit) - Visual regression testing with `toHaveScreenshot()` - Accessibility auditing with `@axe-core/playwright` - Testing Server Components, SSR pages, or full-stack flows - Mobile/responsive testing via device emulation ## When NOT to Use - **Unit testing** isolated functions — use `pytest` or `vitest` - **Component testing** React components in isolation — use `vitest` + Testing Library (faster feedback loop) - **API-only testing** with no browser interaction — use `httpx` / `supertest` directly - **Load/performance testing** — use k6, Artillery, or Locust --- ## Quick Reference | I need... | Go to | |-----------|-------| | Production-grade config to copy | [templates/playwright.config.ts](templates/playwright.config.ts) | | Page Object, auth, mocking patterns | [references/e2e-patterns.md](references/e2e-patterns.md) | | Locator strategy | § Locators below | | Auth reuse with storageState | § Authentication below | | CI setup (GitHub Actions + sharding) | § CI Integration below | | Framework-specific webServer | § Framework Integration below | --- ## Core Patterns ### Test Structure ```typescript import { test, expect } from '@playwright/test'; test.describe('Checkout flow', () => { test('guest can complete purchase', async ({ page }) => { await page.goto('/products/widget-pro'); await page.getByRole('button', { name: 'Add to cart' }).click(); await page.getByRole('link', { name: 'Cart' }).click(); await page.getByRole('button', { name: 'Checkout' }).click(); await page.getByLabel('Email').fill('guest@example.com'); await page.getByRole('button', { name: 'Place order' }).click(); await expect(page.getByText('Order confirmed')).toBeVisible(); }); }); ``` ### Locators — the priority order Always prefer **role-based and user-visible locators**. They survive refactors and match how users interact with the page. | Priority | Locator | When | |----------|---------|------| | 1 | `getByRole('button', { name: '...' })` | Interactive elements with accessible names | | 2 | `getByLabel('...')` | Form fields with `