Files
suaveplan/openspec/changes/swap-testing-e2e-cloakbrowser/proposal.md
2026-05-27 23:29:53 +01:00

89 lines
4.8 KiB
Markdown

# swap-testing-e2e-cloakbrowser
## Why
`@playwright/test` ships a stock Chromium that is trivially fingerprinted as
automation (`navigator.webdriver === true`, `HeadlessChrome` UA, missing
`window.chrome`, GPU/canvas/audio leaks, CDP detection). E2E suites that
exercise production-like flows against bot-aware infrastructure
(Cloudflare Turnstile, reCAPTCHA v3, ShieldSquare, FingerprintJS,
BrowserScan) either fail outright or skew metrics on the first request.
[CloakBrowser](https://github.com/CloakHQ/cloakbrowser) (`cloakbrowser`,
v0.3.28) is a real Chromium binary with 49 source-level C++ patches
covering canvas, WebGL, audio, fonts, GPU, screen, WebRTC, network
timing, automation signals, and CDP input behaviour. It is a drop-in
replacement at the browser-launch layer: the returned object is a
`playwright-core` `Browser` and works with every existing Playwright
test, fixture, page-object, and helper without API changes.
This proposal swaps every browser-launch call inside
`@suaveplan/testing-e2e` to `cloakbrowser`, retains `@playwright/test`
as the test runner (CloakBrowser does not provide one), and exposes the
stealth-specific knobs (`humanize`, `proxy`, `geoip`,
`launchPersistentContext`, extension support) as first-class API surface.
## What Changes
- **ADDED:** `CLOAK_LAUNCH_FAILED`, `CLOAK_BINARY_MISSING`,
`CLOAK_PROXY_INVALID` error codes + `CloakLaunchError` class +
`classifyCloakError` helper that turns raw cloakbrowser errors into
classified `TestingE2eError` instances with remediation hints.
- **ADDED:** `@suaveplan/testing-e2e/cloakserve` subpath —
`connectCloakServe(options, chromium?)` + `buildCloakServeUrl(options)`
pair that wraps `chromium.connectOverCDP(...)` for the
[cloakserve](https://github.com/CloakHQ/CloakBrowser#cdp-server-mode)
CDP multiplexer. Strongly-typed query-parameter surface
(`fingerprint`, `timezone`, `locale`, `platform`, GPU vendor /
renderer, hardware concurrency, device memory, screen dimensions,
`proxy`, `geoip`). Errors wrapped in `CloakLaunchError`.
- **ADDED:** `@suaveplan/testing-e2e/presets` subpath — stealth
presets matching the upstream README's "Recommended config for
anti-bot sites" block. `STEALTH_DEFAULT_CLOAK`,
`STEALTH_HEADED_CLOAK`, `stealthMax(proxy)`,
`stealthMaxHeadless(proxy)`.
- **ADDED:** `tests/no-disallowed.test.ts` rule against
`waitForTimeout` / `wait_for_timeout`. Both forms emit CDP traffic
that reCAPTCHA v3 / Enterprise behavioural scorers detect; the
upstream README documents this explicitly. Test files excluded.
- **ADDED:** `docker/` bundle — `Dockerfile.cloakserve` (extends
`cloakhq/cloakbrowser`, adds the Kasada / Akamai font set, runs
`cloakserve` on 9222), `Dockerfile.manager` (extends
`cloakhq/cloakbrowser-manager`, exposes 8080 with a `cloakprofiles`
named volume), `docker-compose.yml` binding both to `127.0.0.1`
with the manager gated behind the `manager` compose profile,
`.env.example` for pinned tags + `AUTH_TOKEN`, operator `README.md`.
- **MODIFIED:** `extendedTest`'s `browser` fixture now launches via
`cloakbrowser.launch()` instead of the stock Playwright chromium
fixture. `freshPage` and any downstream fixture composition keep
the same signature. The launcher is unit-test-injectable via the
`launchBrowserWithCloak(opts, injectedLaunch?)` helper.
- **MODIFIED:** `launchWithExtension` accepts an injectable
`launchPersistentContext` function (default: cloakbrowser's). The
legacy `chromium: BrowserType` parameter is removed.
- **MODIFIED:** `packages/testing/testing-e2e/package.json` adds
`cloakbrowser: ^0.3.28` as a direct dependency. (The dep is pinned
in-package rather than added to `workspaces.catalogs.testing` to
keep this change isolated from concurrent in-flight work that also
touches the root catalog; promote to the testing catalog as a
follow-up if other packages start consuming `cloakbrowser`.)
- **MODIFIED:** Package description, README updated. **No public
re-exports of cloakbrowser symbols** — per CLAUDE.md rule 4,
consumers `import { launch } from "cloakbrowser"` directly when
bypassing the fixture layer.
## Impact
- **Affected specs:** `testing-e2e`.
- **Affected code:** all six consumer packages currently importing from
`@suaveplan/testing-e2e` continue to work unchanged — the browser
launcher swap is transparent to existing tests because the returned
`Browser` object is the same `playwright-core` `Browser` type.
- **Binary footprint:** CI runners gain a ~200MB stealth Chromium
download on first run (cached under `~/.cache/cloakbrowser/`).
Acceptable: the alternative is `playwright install chromium` (~170MB)
on every `@playwright/test` consumer.
- **Out of scope:** Python flavour (`cloakbrowser` PyPI), `cloakserve`
CDP multiplexer, `cloakbrowser-manager` Docker profile UI — none are
consumed by the genesis monorepo today.