Files
suaveplan/openspec/changes/enterprise-testing-operational/specs/testing-e2e/spec.md
2026-05-27 23:29:53 +01:00

4.1 KiB

ADDED Requirements

Requirement: Default network sandboxing

createE2ETestConfig() SHALL default to blocking external origins via a per-test page.route('**/*') handler that aborts requests with 'blockedbyclient' unless their origin is allow-listed. The auto-allowed set SHALL include localhost, 127.0.0.1, and the configured testbed origin (read from the TESTBED_ORIGIN env var, falling back to the factory's baseURL). The factory SHALL accept an allowedOrigins: string[] option (default []) which lifts the block per-origin. Origin matching SHALL be performed via URL constructor parsing on full origin (<scheme>://<host>:<port>), never substring or regex matching.

Scenario: External origin blocked by default

  • WHEN a spec configured by createE2ETestConfig({ baseURL: 'http://localhost:3200' }) issues a request to https://example.com/data
  • THEN the request is aborted with reason 'blockedbyclient' AND the assertion library surfaces the abort as a network failure

Scenario: localhost auto-allowed

  • WHEN a spec under the same config issues a request to http://localhost:3200/api/users or http://127.0.0.1:9000/
  • THEN the request proceeds normally AND no abort is raised

Scenario: Testbed origin auto-allowed

  • WHEN TESTBED_ORIGIN=https://testbed.internal:8443 is set in the environment
  • THEN requests to that exact origin proceed normally

Scenario: allowedOrigins lifts the block

  • WHEN createE2ETestConfig({ allowedOrigins: ['https://api.partner.test'] }) is used and a spec issues GET https://api.partner.test/v1/foo
  • THEN the request proceeds normally AND requests to any other external origin remain blocked

Scenario: Origin matching is exact

  • WHEN allowedOrigins: ['https://api.partner.test'] is set and a spec issues GET https://evil.api.partner.test.attacker.example/
  • THEN the request is blocked AND the substring overlap does NOT lift the block

Requirement: Spec-level escape hatch with warning

The package SHALL expose a test.use({ allowExternalNetwork: true }) Playwright fixture extension that disables the default block for the enclosing spec only. Use of this escape hatch SHALL emit a warning at suite start naming the offending spec file path, encouraging migration to explicit allowedOrigins. The escape hatch MUST be implemented as a Playwright test.extend fixture and MUST NOT mutate other specs' route handlers.

Scenario: Escape hatch lifts the block locally

  • WHEN a spec declares test.use({ allowExternalNetwork: true }) and issues a request to https://example.com/
  • THEN the request proceeds normally AND no abort is raised within that spec

Scenario: Escape-hatch warning emitted

  • WHEN the suite starts and at least one spec opts into allowExternalNetwork: true
  • THEN a warning line is written to stderr naming each offending spec file path AND the warning persists in the test report

Scenario: Escape hatch is spec-local

  • WHEN spec A declares test.use({ allowExternalNetwork: true }) and spec B does not
  • THEN spec B's external requests are still blocked AND spec A's opt-in does not leak across

Requirement: Origin-matching algorithm is documented and tested

The origin-matching algorithm used by the network sandbox SHALL be exported as a pure function (e.g. isOriginAllowed(url, allowedOrigins, autoAllowed)) so it is testable in isolation. The function MUST: parse url via the URL constructor and reject inputs that fail to parse with TESTING_E2E.NETWORK.URL_INVALID; compare on the <scheme>://<host>:<port> triple, treating the default port for the scheme as equivalent to an explicit port; return a boolean. fast-check property tests SHALL cover round-trip parsing and the substring-attack scenario above.

Scenario: Default-port equivalence

  • WHEN isOriginAllowed('https://api.partner.test/', ['https://api.partner.test:443'], []) is called
  • THEN the function returns true

Scenario: Invalid URL rejected

  • WHEN isOriginAllowed('not a url', [], []) is called
  • THEN the function throws TESTING_E2E.NETWORK.URL_INVALID