---
description: Playwright end-to-end testing conventions
globs: ["e2e/**/*.ts","tests/**/*.spec.ts","playwright.config.ts"]
alwaysApply: false
---

# Playwright Test

- Tests live in `e2e/` (or `tests/`) as `*.spec.ts`. Run `npx playwright test`; debug with `--ui` or `--debug`.
- Locators by user-facing role and text: `page.getByRole('button', { name: 'Save' })`, `getByLabel`, `getByText`. Use `getByTestId` only when there is no accessible name. No CSS/XPath chains tied to layout.
- Web-first assertions only: `await expect(locator).toBeVisible()`, `toHaveText`, `toHaveURL`. They retry automatically.
- Never use `page.waitForTimeout()` or fixed sleeps. Wait for a visible state, a response (`page.waitForResponse`), or a URL.
- Each test is independent: create its own data through API calls or fixtures, not by depending on another test.
- Log in once in a setup project and reuse `storageState`; don't log in through the UI in every test.
- Put repeated flows in page objects or custom fixtures (`test.extend`), not copy-pasted steps.
- Mock third-party services with `page.route`; hit your own backend for real.
- Keep `retries` low in CI (1-2) and keep `trace: 'on-first-retry'` so failures can be debugged.
- Use `test.step` to label long flows.

## Don't
- Don't use `force: true` to get past a covered element; fix the UI state.
- Don't assert on exact pixel positions or auto-generated class names.

## Done when
The new tests pass three times in a row: `npx playwright test <file> --repeat-each=3`.
