# Capability Spec Delta: testing-e2e (testing-e2e-async-and-fixtures-hardening) **Package**: `@suaveplan/testing-e2e` **Status**: PROPOSED ## ADDED Requirements ### Requirement: `extendedTest.apiContext` is browser-free The `apiContext` fixture in `extendedTest` SHALL NOT request the `browser` fixture from Playwright's dependency graph. Playwright spins up requested deps eagerly, so destructuring `browser` would launch a Chromium instance for every API-only test, blowing the per-suite startup budget. #### Scenario: Source-level guard - **WHEN** `src/fixtures/extended-test.ts` is parsed - **THEN** the apiContext fixture body destructures an empty object AND its destructure list contains no `browser` token #### Scenario: extendedTest export shape - **WHEN** a consumer imports `extendedTest` from `@suaveplan/testing-e2e/fixtures` - **THEN** the value is a callable Playwright test extension exposing `.extend(...)` **Verified by:** `src/fixtures/extended-test.test.ts::"apiContext fixture does not destructure \`browser\`"` AND `src/fixtures/extended-test.test.ts::"extendedTest is exported as a Playwright test extension"` --- ### Requirement: `instrumented()` ends spans only after async fns settle `instrumented(name, fn)` SHALL detect when the wrapped function returns a PromiseLike value and SHALL defer `span.end()` and the duration histogram record until the returned promise settles. On rejection, the span SHALL `recordException` (wrapping non-Error rejections in `new Error(String(reason))`) before ending. On resolution, the duration recorded SHALL reflect the wall-clock interval between invocation and settlement. #### Scenario: async resolve - **WHEN** the wrapped fn awaits ≥10ms before returning - **THEN** the awaited result equals the inner return value AND the elapsed wall-clock time is ≥10ms #### Scenario: async reject - **WHEN** the wrapped async fn throws an `Error` - **THEN** the returned promise rejects with the same error #### Scenario: async reject with non-Error reason - **WHEN** the wrapped async fn rejects with a string - **THEN** the returned promise rejects with the same string AND `recordException` is invoked with a wrapped `Error` **Verified by:** `src/observability/instrumented.test.ts::"ends span only after async fn settles (resolved)"` AND `src/observability/instrumented.test.ts::"ends span only after async fn settles (rejected)"` AND `src/observability/instrumented.test.ts::"ends span only after async fn settles (rejected with non-Error)"` ## MODIFIED Requirements ### Requirement: Coverage and docs gates Istanbul statement + branch + function + line coverage across `packages/dev/testing-e2e/src/` (excluding `*.bench.ts`, `*.schemas.ts`, barrel `index.ts` files) MUST be 100% on every dimension. Every implementation file MUST have a sibling `.md` (≥ 200 words, simple + advanced examples). #### Scenario: CI coverage gate - **WHEN** `bunx turbo test:coverage --filter=@suaveplan/testing-e2e` runs - **THEN** statement / branch / function / line coverage all = 100% **Verified by:** CI gate (`bunx turbo test:coverage --filter=@suaveplan/testing-e2e` exits non-zero on regression)