Initial Commit
This commit is contained in:
88
openspec/archive/2026-04-19-enterprise-testing-e2e/design.md
Normal file
88
openspec/archive/2026-04-19-enterprise-testing-e2e/design.md
Normal file
@@ -0,0 +1,88 @@
|
||||
## 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.
|
||||
Reference in New Issue
Block a user