Files
2026-05-27 23:29:53 +01:00

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.
  • 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

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.