Files
suaveplan/openspec/archive/2026-04-27-testing-e2e-async-and-fixtures-hardening/proposal.md
2026-05-27 23:29:53 +01:00

2.1 KiB

testing-e2e: async-span + apiContext fixture hardening

Package: @suaveplan/testing-e2e Status: PROPOSED Type: bug-fix

Why

Two latent defects:

  1. extendedTest.apiContext requests a browser it never uses. src/fixtures/extended-test.ts destructures { browser: _browser } from Playwright's fixture deps. Playwright's fixture system spins up the requested deps eagerly; declaring browser triggers a Chromium launch even for API-only test files. API-only suites pay hundreds of ms per worker for nothing.
  2. instrumented() ends spans synchronously even for async fns. Same defect as in @suaveplan/testing-dom. Every histogram emitted by an async-wrapped helper records ~0 ms.

What changes

  • MODIFIED: src/fixtures/extended-test.ts#apiContext — changes the destructure from { browser: _browser } to {} so Playwright doesn't materialise a browser for API-only tests.
  • MODIFIED: src/observability/instrumented.ts — detects PromiseLike return values and ends the OTel span (and records the histogram) only after the promise settles.
  • MODIFIED: openspec/specs/testing-e2e/spec.md — coverage gate raised from ≥95% to 100% to match CLAUDE.md's pre-publish gate. Path reference updated from packages/universal/testing-e2e/ to packages/dev/testing-e2e/.

Out of scope

  • Refactoring instrumented() away from tracer.startActiveSpan — keeping the same OTel surface, only fixing span lifetime.
  • Adding new fixtures to extendedTest.

Verification approach

  • src/fixtures/extended-test.test.ts — source-level guard that the apiContext fixture body destructures an empty object, NOT one containing browser.
  • src/observability/instrumented.test.ts::"ends span only after async fn settles (resolved)" — observable timing assertion (elapsed >= 10ms).
  • src/observability/instrumented.test.ts::"ends span only after async fn settles (rejected)" and "… (rejected with non-Error)" — pin the rejection branch including recordException for non-Error throws.

Migration impact

None — bug-fix-only behavioural change. Consumers' existing tests continue to pass.