Files
suaveplan/openspec/archive/2026-04-19-enterprise-testing-e2e/design.md
2026-05-27 23:29:53 +01:00

89 lines
4.3 KiB
Markdown

## Context
`@suaveplan/testing-e2e` is the third Layer 0 testing-infrastructure package on genesis. It wraps Playwright with conventions, page objects, and a config factory.
Layer 0 dependency profile: external catalog deps only. No `@suaveplan/*` workspace deps in runtime closure. `@suaveplan/testing` permitted as `devDependency` for the package's own tests.
## Goals / Non-Goals
**Goals:**
- Thin, conventional Playwright wrapper. No forking of Playwright internals.
- `createPlaywrightConfig()` absorbs WSL2 + CI defaults so every consumer gets stable runs without copy-paste.
- `PageObject` base class for the typed page-object pattern.
- Local `TestingE2eError` hierarchy with stable `TESTING_E2E.<DOMAIN>.<REASON>` codes.
- ≥ 95% coverage on every dimension; co-located unit tests + cross-cutting `tests/` per the genesis test-placement hard rule.
**Non-Goals:**
- A full Playwright re-implementation. We re-export, we don't rebuild.
- Browser launching automation. Playwright already handles that; we expose its config.
- React-specific helpers. Those live in `@suaveplan/testing-dom`.
- Real-browser E2E tests of the package itself — the unit tests for our helpers run under vitest with happy-dom; real Playwright E2E of the package would be circular and impractical.
## Decisions
### D1: Local `TestingE2eError` hierarchy
Mirrors the shape from `@suaveplan/testing` and `@suaveplan/testing-dom`. Subclasses: `NavigationError`, `WaitTimeoutError`, `ConfigError`, `FixtureError`, `AbortedError`. Codes follow `TESTING_E2E.<DOMAIN>.<REASON>` pattern.
### D2: `createPlaywrightConfig(options)`
Factory accepting `{ baseURL, projects, reporter, ...overrides }` and returning a `PlaywrightTestConfig`:
- `fullyParallel: false` by default (WSL2 stability)
- `forbidOnly: !!process.env.CI`
- `retries: process.env.CI ? 2 : 0`
- `workers: process.env.CI ? 1 : undefined`
- `use.trace: "on-first-retry"`
- `use.screenshot: "only-on-failure"`
- `use.video: "retain-on-failure"`
- Caller can override every default via `options`.
### D3: `PageObject` base class
```ts
export abstract class PageObject {
constructor(protected readonly page: Page) {}
abstract readonly path: string;
async navigate(): Promise<void> { ... }
async waitForReady(opts?: { signal? }): Promise<void> { ... }
findByRole(role, options?): Locator { ... }
findByLabel(text): Locator { ... }
findByText(text): Locator { ... }
}
```
Extension by composition: subclasses add typed locators and domain methods.
### D4: Test fixtures via Playwright's `test.extend`
`extendedTest` exposes:
- `apiContext`: a `request` instance auto-created per test
- `freshPage`: a new browser context + page (isolated from any auth fixtures)
- `defaultTimeout`: configurable per-suite default
### D5: Helpers
- `waitForNetworkIdle(page, opts?)` — wait until N consecutive ticks with no in-flight requests
- `screenshotOnFailure(testInfo, page)` — manual fallback for non-fixture-driven captures
- `dismissDialog(page)` — registers a one-shot dialog handler
- `mockGeolocation(page, coords)` — install a geolocation override via context.grantPermissions + setGeolocation
- `mockClipboard(page)` — install a programmatic clipboard via `page.evaluate`
### D6: OTEL gated behind env flag
Same pattern as testing-dom: counters/histograms behind `process.env.TESTING_E2E_OTEL_ENABLED === "1"`.
### D7: Tests run under vitest (not Playwright)
Counter-intuitive but correct: testing-e2e is a LIBRARY exposing Playwright helpers. Its OWN unit tests run under vitest because vitest is faster and the helpers themselves don't need a real browser. We test the typed shapes, error throws, config defaults, etc. Real Playwright tests live in app packages that consume testing-e2e.
## Risks / Trade-offs
- **Playwright version churn** — major Playwright bumps occasionally change `PlaywrightTestConfig` shapes. Mitigation: pin via catalog; bump deliberately.
- **Page-object generality** — a one-size base class can't anticipate every consumer's pattern. Mitigation: keep `PageObject` minimal; document the intended subclassing pattern.
- **Helpers tested under vitest, not Playwright** — covers shape but not real-browser behavior. Mitigation: mark helpers as "best effort" in docs; recommend integration tests in consumer apps.
- **`devDependency on @suaveplan/testing`** — same as testing-dom; documented exception.