4.3 KiB
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.PageObjectbase class for the typed page-object pattern.- Local
TestingE2eErrorhierarchy with stableTESTING_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: falseby default (WSL2 stability)forbidOnly: !!process.env.CIretries: process.env.CI ? 2 : 0workers: process.env.CI ? 1 : undefineduse.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
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: arequestinstance auto-created per testfreshPage: 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 requestsscreenshotOnFailure(testInfo, page)— manual fallback for non-fixture-driven capturesdismissDialog(page)— registers a one-shot dialog handlermockGeolocation(page, coords)— install a geolocation override via context.grantPermissions + setGeolocationmockClipboard(page)— install a programmatic clipboard viapage.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
PlaywrightTestConfigshapes. Mitigation: pin via catalog; bump deliberately. - Page-object generality — a one-size base class can't anticipate every consumer's pattern. Mitigation: keep
PageObjectminimal; 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.