---
name: e2e-playwright
description: "Write a Playwright end-to-end test for a described user flow using stable selectors and web-first assertions."
allowed-tools: ["Read", "Grep", "Write", "Bash(npx playwright test:*)"]
output: "playwright spec file"
runner: "playwright"
---

# Playwright E2E Test Writer

## Musts

- Restate the user flow as ordered steps before writing the spec, so each assertion maps to a user-visible outcome.
- Select elements by role, label, or accessible text using `getByRole`, `getByLabel`, or `getByText`.
- Use web-first assertions such as `expect(locator).toBeVisible()` that auto-wait for the expected state.
- Assert the outcome of each step, not just that navigation happened.
- Keep tests independent so any one can run alone without relying on another's leftover state.

## Guidelines

- Never use `page.waitForTimeout` or fixed sleeps; let assertions and auto-waiting handle timing.
- Prefer a stable `data-testid` only when no accessible role or label fits.
- Reset or isolate test data per test so reruns are deterministic.
- Group steps with `test.step` when a flow has several stages, to make failures readable.
- Keep one user journey per test file section rather than one giant test.

## Output format

```typescript
import { test, expect } from '@playwright/test';

test('user can sign in and reach the dashboard', async ({ page }) => {
  await page.goto('/login');

  await page.getByLabel('Email').fill('user@example.com');
  await page.getByLabel('Password').fill('correct-horse');
  await page.getByRole('button', { name: 'Sign in' }).click();

  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
  await expect(page.getByText('Welcome back')).toBeVisible();
});
```
