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

10 KiB

testing-e2e — delta (swap-testing-e2e-cloakbrowser)

ADDED Requirements

Requirement: Cloakserve CDP connect helper

The package MUST expose @suaveplan/testing-e2e/cloakserve (also re-exported from the package root) providing connectCloakServe(options, chromium?) and buildCloakServeUrl(options). The URL builder MUST map camelCase option keys to cloakserve's kebab-case query-string keys (hardwareConcurrency → hardware-concurrency, etc.), stringify booleans, skip undefined/null values, and default the base URL to http://localhost:9222. connectCloakServe MUST inject @playwright/test's chromium by default with a lazy dynamic-import cache, and wrap any connectOverCDP rejection in a CloakLaunchError whose context carries { cloakServeUrl }.

Scenario: buildCloakServeUrl maps keys and base URL

  • WHEN buildCloakServeUrl({ fingerprint: 42, hardwareConcurrency: 8, geoip: true }) is called
  • THEN the result equals http://localhost:9222/?fingerprint=42&hardware-concurrency=8&geoip=true (param order is preserved)

Verified by: src/cloakserve/build-cloakserve-url.test.ts::"appends fingerprint seed" AND src/cloakserve/build-cloakserve-url.test.ts::"maps camelCase to kebab-case" AND src/cloakserve/build-cloakserve-url.test.ts::"stringifies booleans"

Scenario: connectCloakServe forwards options and returns the Browser

  • WHEN connectCloakServe({ fingerprint: 11111, timezone: "America/New_York" }, injectedDriver) is called
  • THEN injectedDriver.connectOverCDP is called with a URL containing fingerprint=11111 and timezone=America%2FNew_York, and the returned Browser is the injected fake

Verified by: src/cloakserve/cloakserve-connect.test.ts::"uses injected chromium and builds the cloakserve URL"

Scenario: connectCloakServe wraps errors

  • WHEN the underlying connectOverCDP rejects with ECONNREFUSED
  • THEN connectCloakServe rethrows a CloakLaunchError with code === TESTING_E2E.CLOAK.PROXY_INVALID and context.cloakServeUrl set to the URL that was attempted

Verified by: src/cloakserve/cloakserve-connect.test.ts::"wraps connection errors in CloakLaunchError" AND src/cloakserve/cloakserve-connect.test.ts::"classifies generic connect errors as LAUNCH_FAILED"

Requirement: Stealth presets

The package MUST expose @suaveplan/testing-e2e/presets (also re-exported from the package root) providing frozen STEALTH_DEFAULT_CLOAK ({ headless: true }) and STEALTH_HEADED_CLOAK ({ headless: false, humanize: true }) plus stealthMax(proxy) and stealthMaxHeadless(proxy) factories that return fresh objects on every call. stealthMax(proxy) MUST return { headless: false, humanize: true, geoip: true, proxy } — exactly matching the CloakBrowser README's "Recommended config for anti-bot sites" block.

Scenario: stealthMax matches the upstream recommendation

  • WHEN stealthMax("http://proxy:8080") is called
  • THEN the result is { headless: false, humanize: true, geoip: true, proxy: "http://proxy:8080" }

Verified by: src/presets/stealth-presets.test.ts::"matches the upstream recommended config"

Scenario: static presets are frozen

  • WHEN Object.isFrozen(STEALTH_DEFAULT_CLOAK) is checked
  • THEN it is true (and same for STEALTH_HEADED_CLOAK)

Verified by: src/presets/stealth-presets.test.ts::"is headless and frozen" AND src/presets/stealth-presets.test.ts::"is headed + humanized and frozen"

Requirement: waitForTimeout lint guard

The package's source-guard test MUST flag waitForTimeout and wait_for_timeout calls anywhere under src/ (excluding .test.ts and .bench.ts). Both forms emit CDP protocol traffic that reCAPTCHA v3 / Enterprise behavioural scorers detect — the upstream CloakBrowser README documents this as a primary cause of low scores.

Scenario: guard fires on a synthetic violation

  • WHEN the no-disallowed source guard test runs against the current src/ tree
  • THEN the test passes (no violations) AND the PATTERNS array contains entries named waitForTimeout and wait_for_timeout

Verified by: tests/no-disallowed.test.ts::"src/ contains no disallowed constructs"

Requirement: Docker bundle

The package MUST ship a docker/ directory containing Dockerfile.cloakserve (extending cloakhq/cloakbrowser, installing the Kasada / Akamai font set, running cloakserve on 9222), Dockerfile.manager (extending cloakhq/cloakbrowser-manager, exposing 8080 with a cloakprofiles named volume), docker-compose.yml (both services bound to 127.0.0.1, manager behind the manager compose profile, named volume defined), .env.example (CLOAKBROWSER_TAG, MANAGER_TAG, MANAGER_AUTH_TOKEN), and a docker/README.md operator playbook. Both Dockerfiles MUST declare a healthcheck.

Scenario: cloakserve Dockerfile builds on the upstream image and exposes 9222

  • WHEN docker build -f docker/Dockerfile.cloakserve is run
  • THEN the resulting image is layered on cloakhq/cloakbrowser, exposes port 9222, defaults its CMD to cloakserve, and runs the documented font-install step

Verified by: manual operator verification (docker compose up cloakserve + smoke connect via connectCloakServe); source-level invariants are enforced by reading the Dockerfile in CI is out of scope for this change

Requirement: CLOAK_* error codes

The package MUST emit CloakLaunchError extends TestingE2eError with codes TESTING_E2E.CLOAK.LAUNCH_FAILED, TESTING_E2E.CLOAK.BINARY_MISSING, and TESTING_E2E.CLOAK.PROXY_INVALID. All three codes MUST appear in the exported TESTING_E2E_ERROR_CODES registry and conform to TESTING_E2E_ERROR_CODE_PATTERN.

Scenario: codes registered

  • WHEN TESTING_E2E_ERROR_CODES is iterated
  • THEN the three CLOAK codes are present and each matches the registry pattern

Verified by: src/errors/testing-e2e-error.test.ts::"registers CLOAK_* codes" AND src/errors/testing-e2e-error.test.ts::"CloakLaunchError exposes the LAUNCH_FAILED code by default"

Scenario: BINARY_MISSING raised when cloakbrowser binary not present

  • WHEN the underlying cloakbrowser launcher throws an error whose message includes binary not found, ENOENT, or Failed to download
  • THEN the calling fixture rethrows a CloakLaunchError with code TESTING_E2E.CLOAK.BINARY_MISSING, the original error as cause, and a remediation hint pointing to npx cloakbrowser install

Verified by: src/errors/testing-e2e-error.test.ts::"classifyCloakError maps ENOENT to BINARY_MISSING"

MODIFIED Requirements

Requirement: Layer 0 dep-graph isolation

The @suaveplan/testing-e2e dependencies and peerDependencies MUST NOT contain any @suaveplan/* workspace package. dependencies MUST include cloakbrowser (any valid range — direct or catalog:testing) alongside the existing @playwright/test, @opentelemetry/api, tslib, and zod entries. devDependencies MAY include @suaveplan/testing for the package's own tests.

Scenario: runtime closure clean and contains cloakbrowser

  • WHEN bunx turbo ls --filter=@suaveplan/testing-e2e... is run
  • THEN no @suaveplan/* package appears in the runtime closure AND cloakbrowser is present as a direct dependency

Verified by: tests/dep-graph.test.ts::"no @suaveplan/* in dependencies or peerDependencies" AND tests/dep-graph.test.ts::"cloakbrowser is a direct dependency"

Requirement: extendedTest default browser is stealth

extendedTest.browser MUST be overridden so the default browser used by every fixture downstream (freshPage, consumer-extended fixtures) launches via cloakbrowser.launch() (drop-in for stock chromium). The override MUST honour testInfo.project.use.cloak if defined, otherwise launch with cloakbrowser defaults (headless: true, stealth args on). The launcher MUST be injectable for unit testing — the fixture takes an internal default but exposes the launcher symbol on the cloak worker fixture so tests can supply a spy.

Scenario: default browser is cloakbrowser

  • WHEN a test runs under extendedTest with no per-project override
  • THEN the browser fixture is produced by the cloakbrowser launch function (identity check via injected spy)

Verified by: src/fixtures/extended-test.test.ts::"browser fixture is launched via cloakbrowser launch" AND src/fixtures/extended-test.test.ts::"cloak options from testInfo.project.use.cloak flow into launch"

Scenario: errors map to CloakLaunchError

  • WHEN the underlying cloakbrowser launcher throws
  • THEN the fixture rethrows a CloakLaunchError with a classified code (LAUNCH_FAILED, BINARY_MISSING, or PROXY_INVALID)

Verified by: src/fixtures/extended-test.test.ts::"wraps launch errors in CloakLaunchError"

Requirement: Extension fixture uses cloakbrowser launchPersistentContext

launchWithExtension MUST accept a launchPersistentContext function parameter (default: cloakbrowser's). The legacy chromium: BrowserType parameter is removed. Extension args (--disable-extensions-except, --load-extension) and userDataDir behaviour are unchanged.

Scenario: default launcher is cloakbrowser

  • WHEN launchWithExtension({ extensionPath, userDataDir }) is called with no explicit launcher
  • THEN cloakbrowser.launchPersistentContext is invoked with the expected userDataDir and Chromium args

Verified by: src/extension-fixture/launch-with-extension.test.ts::"defaults to cloakbrowser launchPersistentContext"

Scenario: launcher is injectable

  • WHEN launchWithExtension({ launchPersistentContext: spy, extensionPath, userDataDir }) is called
  • THEN the provided spy is invoked exactly once with the expected userDataDir and Chromium args

Verified by: src/extension-fixture/launch-with-extension.test.ts::"uses injected launcher when provided"

Scenario: launchPersistentContext errors map to CloakLaunchError

  • WHEN the underlying launcher throws an error matching the ENOENT/binary classifier
  • THEN launchWithExtension rethrows a CloakLaunchError with code === TESTING_E2E.CLOAK.BINARY_MISSING

Verified by: src/extension-fixture/launch-with-extension.test.ts::"wraps launcher errors with CloakLaunchError"