# Pathrule Pattern: Testing (Vitest + Playwright) (1.0.0)
# ::pathrule:package:testing-vitest-playwright

### [RULE] Select by role and accessible name, not CSS  (path: /e2e)
<!-- scope: folder | priority: high | advisory -->

End-to-end tests must locate elements the way a user or screen reader does, so they survive markup and styling refactors.

- Reach for `getByRole(name)` first; it is Playwright's recommended locator and doubles as an accessibility check.
- Fall back in order to `getByLabel`, `getByPlaceholder`, `getByText`, then `getByTestId` only when no semantic handle exists.
- Do not select by CSS class, tag chains, or XPath; these break on every redesign.
- Add `data-testid` to the element, not a wrapper, when a test id is genuinely needed.

---

### [RULE] Test observable behavior, not implementation  (path: /src)
<!-- scope: folder | priority: high | advisory -->

Unit and component tests must verify what a user or caller observes, so they catch regressions without breaking on internal refactors.

- Assert on rendered DOM, returned values, and emitted events; query with `@testing-library` role and label helpers.
- Do not assert on component internal state, private methods, or spy call counts when an observable effect exists.
- Prefer `await screen.findByRole(...)` and `userEvent` interactions over reaching into instances.
- Reserve mocks for true boundaries (network, time, randomness); never mock the unit under test.

---

### [MEMORY] Two-layer test stack: Vitest units, Playwright e2e  (path: /)

We run two distinct layers so each test sits at the right altitude. Keep them separate; do not drive full app flows from Vitest or unit-test pure logic through Playwright.

- Unit and component tests live in `/src` next to source or in `/tests`, run under Vitest 4.1 with `environment: 'jsdom'` (or `happy-dom`) and `@testing-library/react`.
- End-to-end tests live in `/e2e`, run under `@playwright/test` against a real browser via the `webServer` config that boots the app.
- Vitest 4 defaults coverage to `v8`; set `coverage.include` explicitly since `coverage.all` was removed in v4.
- Heavy component-interaction tests can use Vitest Browser Mode (stable since v4) instead of jsdom when DOM fidelity matters.

See /e2e for the Playwright selector rule and /src for the behavior-testing rule.

---

### [MEMORY] Playwright CI config: parallel, retries, traces, sharding  (path: /e2e)

Our `playwright.config.ts` is tuned so CI failures are reproducible and fast. Match these settings when adding projects or jobs.

- `fullyParallel: true`, `workers` left to default locally and capped in CI, `retries: process.env.CI ? 2 : 0`.
- `trace: 'on-first-retry'` so a failing test ships a full trace (network, DOM snapshots, timeline) without slowing green runs.
- `webServer` boots the app with `reuseExistingServer: !process.env.CI` so local runs reuse a running dev server and CI always starts clean.
- Large suites split across runners with `--shard=index/total`; merge the blob reports afterward for one HTML report.
- Open the Trace Viewer (`npx playwright show-trace`) before editing any flaky test.

---

### [SKILL] testing-vitest-playwright-review  (path: /)

---
name: testing-vitest-playwright-review
description: Review checklist for any change that adds or edits Vitest unit/component tests or Playwright e2e tests. Confirms tests assert behavior, use stable role-based selectors, and run reliably in CI.
---

# Testing (Vitest + Playwright) review

- [ ] New logic and components have Vitest tests; new user flows have a Playwright e2e test.
- [ ] Assertions target observable output (rendered DOM, return values, events), not internal state or private methods.
- [ ] Component queries use `@testing-library` role/label helpers and `userEvent`, not container DOM traversal.
- [ ] Playwright locators follow the hierarchy: `getByRole` first, then label/placeholder/text, `getByTestId` last, no raw CSS or XPath.
- [ ] Mocks are limited to real boundaries (network, time, randomness); the unit under test is never mocked.
- [ ] Async work is awaited via `findBy*` / web-first `expect` assertions, with no fixed `sleep`/`waitForTimeout` waits.
- [ ] Vitest `coverage.include` is set and coverage does not regress; provider is `v8`.
- [ ] Playwright config keeps `trace: 'on-first-retry'`, CI `retries: 2`, `fullyParallel`, and a `webServer` entry.
- [ ] Tests are deterministic and isolated: one context per e2e test, no shared mutable fixtures, no order dependence.
- [ ] CI runs both suites and uploads Playwright traces/reports as artifacts.
