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.connectOverCDPis called with a URL containingfingerprint=11111andtimezone=America%2FNew_York, and the returnedBrowseris 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
connectOverCDPrejects withECONNREFUSED - THEN
connectCloakServerethrows aCloakLaunchErrorwithcode === TESTING_E2E.CLOAK.PROXY_INVALIDandcontext.cloakServeUrlset 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 forSTEALTH_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 guardtest runs against the currentsrc/tree - THEN the test passes (no violations) AND the
PATTERNSarray contains entries namedwaitForTimeoutandwait_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.cloakserveis run - THEN the resulting image is layered on
cloakhq/cloakbrowser, exposes port9222, defaults its CMD tocloakserve, 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_CODESis 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, orFailed to download - THEN the calling fixture rethrows a
CloakLaunchErrorwith codeTESTING_E2E.CLOAK.BINARY_MISSING, the original error ascause, and a remediation hint pointing tonpx 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 ANDcloakbrowseris 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
extendedTestwith no per-project override - THEN the
browserfixture is produced by the cloakbrowserlaunchfunction (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
CloakLaunchErrorwith a classified code (LAUNCH_FAILED,BINARY_MISSING, orPROXY_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.launchPersistentContextis invoked with the expecteduserDataDirand 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
spyis invoked exactly once with the expecteduserDataDirand 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
launchWithExtensionrethrows aCloakLaunchErrorwithcode === TESTING_E2E.CLOAK.BINARY_MISSING
Verified by: src/extension-fixture/launch-with-extension.test.ts::"wraps launcher errors with CloakLaunchError"