# @suaveplan/testing-e2e - Stealth Playwright E2E utilities backed by cloakbrowser — config factory, page objects, fixtures, helpers, structured errors. Drop-in browser-launch swap that passes Cloudflare Turnstile, reCAPTCHA v3, FingerprintJS. Layer 0; no @suaveplan/* runtime deps ## Summary Playwright E2E testing utilities for Suaveplan, with the default `browser` worker fixture launching [`cloakbrowser`](https://github.com/CloakHQ/cloakbrowser) — a real Chromium binary with 49 source-level C++ fingerprint patches — instead of the stock Playwright Chromium. Every spec that imports `extendedTest` from this package gets `navigator.webdriver === false`, real GPU/canvas/audio/screen/WebRTC fingerprints, a normal `window.chrome`, no `HeadlessChrome` UA leak, and no CDP detection signal. Cloudflare Turnstile, reCAPTCHA v3 (0.9 score), FingerprintJS, BrowserScan, and ShieldSquare see a normal browser. The rest of the package is unchanged: a config factory with WSL2/CI defaults, an abstract `PageObject` base, an `apiContext` fixture that deliberately does NOT depend on `browser`, network/console monitors, multi-session orchestration, OTel-instrumented entry points, and a structured `TestingE2eError` hierarchy (now with `CLOAK_*` codes). Layer 0 — no `@suaveplan/*` runtime dependencies. ### What the swap changes - `extendedTest.browser` is launched via `cloakbrowser.launch`. The returned object is still a Playwright `Browser` from `playwright-core` — every fixture, page object, helper, and monitor works unchanged. - A new worker option `cloak: CloakOptions` flows into the launcher. Set per project in `playwright.config.ts`: `use: { cloak: { humanize: true, geoip: true, proxy: "…" } }`. - `launchWithExtension` defaults to `cloakbrowser.launchPersistentContext` and exposes a `launchPersistentContext` injector for tests / profile managers. - Failures classify into `TESTING_E2E.CLOAK.{LAUNCH_FAILED,BINARY_MISSING,PROXY_INVALID}` via `classifyCloakError` + `wrapCloakError`, wrapped in `CloakLaunchError`. - `@playwright/test` stays as the test runner — cloakbrowser is a drop-in browser-launch replacement, not a runner replacement. - **No public re-exports of `cloakbrowser` symbols.** Per CLAUDE.md rule 4, when bypassing the fixture layer, consumers `import { launch } from "cloakbrowser"` directly. ### Binary footprint On first launch the stealth Chromium binary (~200 MB) is auto- downloaded to `~/.cache/cloakbrowser/`. CI runners pre-cache it via `npx cloakbrowser install` in the setup step (parity with `playwright install chromium`). ## Documentation tree - [build-cloakserve-url](./src/cloakserve/build-cloakserve-url.md) — `buildCloakServeUrl` constructs the WebSocket / HTTP endpoint URL for `cloakserve`, the cloakbrowser CDP multiplexer th… - [cloakserve-connect](./src/cloakserve/cloakserve-connect.md) — `connectCloakServe` connects Playwright to a running `cloakserve` CDP multiplexer over `chromium.connectOverCDP(...)`,… - [config](./src/config/create-playwright-config.md) — _(no submodules)_ - [create-playwright-config](./src/config/create-playwright-config.md) — _(no submodules)_ - [console-monitor](./src/console-monitor/console-monitor.md) — _(no submodules)_ - [console-monitor](./src/console-monitor/console-monitor.md) — _(no submodules)_ - [codes](./src/errors/codes.md) — `TESTING_E2E_ERROR_CODES` is the frozen registry of every failure code `@suaveplan/testing-e2e` emits. All codes carry… - [testing-e2e-error](./src/errors/testing-e2e-error.md) — `TestingE2eError` is the structured error class thrown by every helper, fixture, and page object in `@suaveplan/testing… - [extension-fixture](./src/extension-fixture/launch-with-extension.md) — `launchWithExtension` starts a stealth Chromium persistent browser context with an unpacked browser extension loaded, t… - [launch-with-extension](./src/extension-fixture/launch-with-extension.md) — `launchWithExtension` starts a stealth Chromium persistent browser context with an unpacked browser extension loaded, t… - [fixtures](./src/fixtures/extended-test.md) — `extendedTest` is the Playwright `test` function pre-extended with the fixtures this package ships, and — critically —… - [extended-test](./src/fixtures/extended-test.md) — `extendedTest` is the Playwright `test` function pre-extended with the fixtures this package ships, and — critically —… - [dismiss-dialog](./src/helpers/dismiss-dialog.md) — `dismissDialog` attaches a one-shot handler to the next `page.on("dialog")` event, with the caller choosing between `ac… - [mock-clipboard](./src/helpers/mock-clipboard.md) — `mockClipboard` installs a deterministic, in-memory clipboard implementation on the navigator object for a Playwright p… - [mock-geolocation](./src/helpers/mock-geolocation.md) — `mockGeolocation` pins a Playwright page to a fixed `{ latitude, longitude }` so tests that exercise location-aware fea… - [screenshot-on-failure](./src/helpers/screenshot-on-failure.md) — `screenshotOnFailure` captures a full-page screenshot when a Playwright test fails and attaches it to the `TestInfo` re… - [wait-for-network-idle](./src/helpers/wait-for-network-idle.md) — `waitForNetworkIdle` suspends until a Playwright page has had zero in-flight requests for at least `idleMs` consecutive… - [multi-session](./src/multi-session/create-multi-session.md) — _(no submodules)_ - [create-multi-session](./src/multi-session/create-multi-session.md) — _(no submodules)_ - [network-monitor](./src/network-monitor/network-monitor.md) — _(no submodules)_ - [network-monitor](./src/network-monitor/network-monitor.md) — _(no submodules)_ - [observability](./src/observability/instrumented.md) — _(no submodules)_ - [instrumented](./src/observability/instrumented.md) — _(no submodules)_ - [page-objects](./src/page-objects/page-object.md) — _(no submodules)_ - [page-object](./src/page-objects/page-object.md) — _(no submodules)_ - [presets](./src/presets/stealth-presets.md) — Ready-made `CloakOptions` bundles for the most common cloakbrowser deployment shapes. Pulled from the official CloakBro… - [stealth-presets](./src/presets/stealth-presets.md) — Ready-made `CloakOptions` bundles for the most common cloakbrowser deployment shapes. Pulled from the official CloakBro… - [create-playwright-config](./src/config/create-playwright-config.md) — _(no submodules)_- [console-monitor](./src/console-monitor/console-monitor.md) — _(no submodules)_- [console-monitor](./src/console-monitor/console-monitor.md) — _(no submodules)_- [codes](./src/errors/codes.md) — `TESTING_E2E_ERROR_CODES` is the frozen registry of every failure code `@suaveplan/testing-e2e` emits. All codes carry… - [testing-e2e-error](./src/errors/testing-e2e-error.md) — `TestingE2eError` is the structured error class thrown by every helper, fixture, and page object in `@suaveplan/testing… - [extension-fixture](./src/extension-fixture/launch-with-extension.md) — _(no submodules)_- [launch-with-extension](./src/extension-fixture/launch-with-extension.md) — _(no submodules)_- [fixtures](./src/fixtures/extended-test.md) — _(no submodules)_- [extended-test](./src/fixtures/extended-test.md) — _(no submodules)_- [dismiss-dialog](./src/helpers/dismiss-dialog.md) — `dismissDialog` attaches a one-shot handler to the next `page.on("dialog")` event, with the caller choosing between `ac… - [mock-clipboard](./src/helpers/mock-clipboard.md) — `mockClipboard` installs a deterministic, in-memory clipboard implementation on the navigator object for a Playwright p… - [mock-geolocation](./src/helpers/mock-geolocation.md) — `mockGeolocation` pins a Playwright page to a fixed `{ latitude, longitude }` so tests that exercise location-aware fea… - [screenshot-on-failure](./src/helpers/screenshot-on-failure.md) — `screenshotOnFailure` captures a full-page screenshot when a Playwright test fails and attaches it to the `TestInfo` re… - [wait-for-network-idle](./src/helpers/wait-for-network-idle.md) — `waitForNetworkIdle` suspends until a Playwright page has had zero in-flight requests for at least `idleMs` consecutive… - [multi-session](./src/multi-session/create-multi-session.md) — _(no submodules)_- [create-multi-session](./src/multi-session/create-multi-session.md) — _(no submodules)_- [network-monitor](./src/network-monitor/network-monitor.md) — _(no submodules)_- [network-monitor](./src/network-monitor/network-monitor.md) — _(no submodules)_- [observability](./src/observability/instrumented.md) — _(no submodules)_- [instrumented](./src/observability/instrumented.md) — _(no submodules)_- [page-objects](./src/page-objects/page-object.md) — _(no submodules)_- [page-object](./src/page-objects/page-object.md) — _(no submodules)_- [create-playwright-config](./src/config/create-playwright-config.md) — _(no submodules)_- [console-monitor](./src/console-monitor/console-monitor.md) — _(no submodules)_- [console-monitor](./src/console-monitor/console-monitor.md) — _(no submodules)_- [codes](./src/errors/codes.md) — `TESTING_E2E_ERROR_CODES` is the frozen registry of every failure code `@suaveplan/testing-e2e` emits. All codes carry… - [testing-e2e-error](./src/errors/testing-e2e-error.md) — `TestingE2eError` is the structured error class thrown by every helper, fixture, and page object in `@suaveplan/testing… - [extension-fixture](./src/extension-fixture/launch-with-extension.md) — _(no submodules)_- [launch-with-extension](./src/extension-fixture/launch-with-extension.md) — _(no submodules)_- [fixtures](./src/fixtures/extended-test.md) — _(no submodules)_- [extended-test](./src/fixtures/extended-test.md) — _(no submodules)_- [dismiss-dialog](./src/helpers/dismiss-dialog.md) — `dismissDialog` attaches a one-shot handler to the next `page.on("dialog")` event, with the caller choosing between `ac… - [mock-clipboard](./src/helpers/mock-clipboard.md) — `mockClipboard` installs a deterministic, in-memory clipboard implementation on the navigator object for a Playwright p… - [mock-geolocation](./src/helpers/mock-geolocation.md) — `mockGeolocation` pins a Playwright page to a fixed `{ latitude, longitude }` so tests that exercise location-aware fea… - [screenshot-on-failure](./src/helpers/screenshot-on-failure.md) — `screenshotOnFailure` captures a full-page screenshot when a Playwright test fails and attaches it to the `TestInfo` re… - [wait-for-network-idle](./src/helpers/wait-for-network-idle.md) — `waitForNetworkIdle` suspends until a Playwright page has had zero in-flight requests for at least `idleMs` consecutive… - [multi-session](./src/multi-session/create-multi-session.md) — _(no submodules)_- [create-multi-session](./src/multi-session/create-multi-session.md) — _(no submodules)_- [network-monitor](./src/network-monitor/network-monitor.md) — _(no submodules)_- [network-monitor](./src/network-monitor/network-monitor.md) — _(no submodules)_- [observability](./src/observability/instrumented.md) — _(no submodules)_- [instrumented](./src/observability/instrumented.md) — _(no submodules)_- [page-objects](./src/page-objects/page-object.md) — _(no submodules)_- [page-object](./src/page-objects/page-object.md) — _(no submodules)_- [create-playwright-config](./src/config/create-playwright-config.md) — _(no submodules)_- [console-monitor](./src/console-monitor/console-monitor.md) — _(no submodules)_- [console-monitor](./src/console-monitor/console-monitor.md) — _(no submodules)_- [codes](./src/errors/codes.md) — `TESTING_E2E_ERROR_CODES` is the frozen registry of every failure code `@suaveplan/testing-e2e` emits. All codes carry… - [testing-e2e-error](./src/errors/testing-e2e-error.md) — `TestingE2eError` is the structured error class thrown by every helper, fixture, and page object in `@suaveplan/testing… - [extension-fixture](./src/extension-fixture/launch-with-extension.md) — _(no submodules)_- [launch-with-extension](./src/extension-fixture/launch-with-extension.md) — _(no submodules)_- [fixtures](./src/fixtures/extended-test.md) — _(no submodules)_- [extended-test](./src/fixtures/extended-test.md) — _(no submodules)_- [dismiss-dialog](./src/helpers/dismiss-dialog.md) — `dismissDialog` attaches a one-shot handler to the next `page.on("dialog")` event, with the caller choosing between `ac… - [mock-clipboard](./src/helpers/mock-clipboard.md) — `mockClipboard` installs a deterministic, in-memory clipboard implementation on the navigator object for a Playwright p… - [mock-geolocation](./src/helpers/mock-geolocation.md) — `mockGeolocation` pins a Playwright page to a fixed `{ latitude, longitude }` so tests that exercise location-aware fea… - [screenshot-on-failure](./src/helpers/screenshot-on-failure.md) — `screenshotOnFailure` captures a full-page screenshot when a Playwright test fails and attaches it to the `TestInfo` re… - [wait-for-network-idle](./src/helpers/wait-for-network-idle.md) — `waitForNetworkIdle` suspends until a Playwright page has had zero in-flight requests for at least `idleMs` consecutive… - [multi-session](./src/multi-session/create-multi-session.md) — _(no submodules)_- [create-multi-session](./src/multi-session/create-multi-session.md) — _(no submodules)_- [network-monitor](./src/network-monitor/network-monitor.md) — _(no submodules)_- [network-monitor](./src/network-monitor/network-monitor.md) — _(no submodules)_- [observability](./src/observability/instrumented.md) — _(no submodules)_- [instrumented](./src/observability/instrumented.md) — _(no submodules)_- [page-objects](./src/page-objects/page-object.md) — _(no submodules)_- [page-object](./src/page-objects/page-object.md) — _(no submodules)_- [create-playwright-config](./src/config/create-playwright-config.md) — _(no submodules)_- [console-monitor](./src/console-monitor/console-monitor.md) — _(no submodules)_ - [console-monitor](./src/console-monitor/console-monitor.md) — _(no submodules)_- [codes](./src/errors/codes.md) — `TESTING_E2E_ERROR_CODES` is the frozen registry of every failure code `@suaveplan/testing-e2e` emits. All codes carry… - [testing-e2e-error](./src/errors/testing-e2e-error.md) — `TestingE2eError` is the structured error class thrown by every helper, fixture, and page object in `@suaveplan/testing… - [extension-fixture](./src/extension-fixture/launch-with-extension.md) — _(no submodules)_ - [launch-with-extension](./src/extension-fixture/launch-with-extension.md) — _(no submodules)_- [fixtures](./src/fixtures/extended-test.md) — _(no submodules)_ - [extended-test](./src/fixtures/extended-test.md) — _(no submodules)_- [dismiss-dialog](./src/helpers/dismiss-dialog.md) — `dismissDialog` attaches a one-shot handler to the next `page.on("dialog")` event, with the caller choosing between `ac… - [mock-clipboard](./src/helpers/mock-clipboard.md) — `mockClipboard` installs a deterministic, in-memory clipboard implementation on the navigator object for a Playwright p… - [mock-geolocation](./src/helpers/mock-geolocation.md) — `mockGeolocation` pins a Playwright page to a fixed `{ latitude, longitude }` so tests that exercise location-aware fea… - [screenshot-on-failure](./src/helpers/screenshot-on-failure.md) — `screenshotOnFailure` captures a full-page screenshot when a Playwright test fails and attaches it to the `TestInfo` re… - [wait-for-network-idle](./src/helpers/wait-for-network-idle.md) — `waitForNetworkIdle` suspends until a Playwright page has had zero in-flight requests for at least `idleMs` consecutive… - [multi-session](./src/multi-session/create-multi-session.md) — _(no submodules)_ - [create-multi-session](./src/multi-session/create-multi-session.md) — _(no submodules)_- [network-monitor](./src/network-monitor/network-monitor.md) — _(no submodules)_ - [network-monitor](./src/network-monitor/network-monitor.md) — _(no submodules)_- [observability](./src/observability/instrumented.md) — _(no submodules)_ - [instrumented](./src/observability/instrumented.md) — _(no submodules)_- [page-objects](./src/page-objects/page-object.md) — _(no submodules)_ - [page-object](./src/page-objects/page-object.md) — _(no submodules)_ ## Install ```bash bun add @suaveplan/testing-e2e ``` Requires `@playwright/test` and `cloakbrowser` (installed automatically as direct dependencies). On first launch cloakbrowser auto-downloads the stealth Chromium binary; pre-cache in CI with `npx cloakbrowser install`. ## Quick Start ### Playwright config ```ts // playwright.config.ts import { createPlaywrightConfig } from "@suaveplan/testing-e2e/config"; export default createPlaywrightConfig({ baseURL: "http://localhost:3000", testDir: "e2e", projects: [ { name: "chromium", use: { browserName: "chromium" } }, ], }); ``` ### Page object + stealth extended fixtures ```ts // playwright.config.ts — cloak options for the default browser fixture import { createPlaywrightConfig } from "@suaveplan/testing-e2e/config"; export default createPlaywrightConfig({ baseURL: "http://localhost:3000", testDir: "e2e", use: { cloak: { headless: true, humanize: true, geoip: true, proxy: "socks5://user:pass@residential:1080", }, }, }); ``` ```ts // e2e/pages/login.page.ts import { PageObject } from "@suaveplan/testing-e2e/page-objects"; export class LoginPage extends PageObject { public readonly path = "/login"; get emailInput() { return this.findByLabel("Email"); } get submitButton() { return this.findByRole("button", { name: /sign in/i }); } } // e2e/login.spec.ts — runs against a Cloudflare-walled app import { extendedTest as test, expect } from "@suaveplan/testing-e2e/fixtures"; import { waitForNetworkIdle } from "@suaveplan/testing-e2e/helpers"; import { LoginPage } from "./pages/login.page.js"; test("signs in through Cloudflare Turnstile", async ({ page }) => { const login = new LoginPage(page); await login.navigate(); // Turnstile lets us through await login.emailInput.fill("user@example.com"); await login.submitButton.click(); await waitForNetworkIdle(page); await expect(page).toHaveURL("/dashboard"); }); ``` ### Programmatic launch (outside the Playwright runner) ```ts import { launchBrowserWithCloak } from "@suaveplan/testing-e2e/fixtures"; const browser = await launchBrowserWithCloak({ humanize: true, geoip: true, proxy: "http://user:pass@proxy:8080", }); const ctx = await browser.newContext(); const page = await ctx.newPage(); await page.goto("https://bot.incolumitas.com"); console.log(await page.title()); await browser.close(); ``` ### Extension fixture (stealth + persistent profile) ```ts import { launchWithExtension } from "@suaveplan/testing-e2e/extension-fixture"; const { context, extensionId } = await launchWithExtension({ extensionPath: "/path/to/unpacked-extension", userDataDir: "/tmp/profile", launchOptions: { humanize: true, geoip: true }, }); ``` ### Cloakserve CDP server (shared container, N parallel workers) ```bash # One container, all your test workers share it cd packages/testing/testing-e2e/docker docker compose up -d cloakserve ``` ```ts import { connectCloakServe, stealthMax } from "@suaveplan/testing-e2e"; const browser = await connectCloakServe({ url: "http://localhost:9222", fingerprint: 11111, // per-worker seed timezone: "America/New_York", locale: "en-US", proxy: process.env.RESIDENTIAL_PROXY, geoip: true, }); ``` ### Stealth presets ```ts // playwright.config.ts import { defineConfig } from "@playwright/test"; import { stealthMax } from "@suaveplan/testing-e2e"; export default defineConfig({ use: { // Upstream-recommended config: residential proxy + geoip + headed + humanize. cloak: stealthMax(process.env.RESIDENTIAL_PROXY!), }, }); ``` `STEALTH_DEFAULT_CLOAK` / `STEALTH_HEADED_CLOAK` are also exported for the lighter shapes. See `src/presets/stealth-presets.md` for the full menu. ### Docker bundle `packages/testing/testing-e2e/docker/` ships: - `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 for persistence - `docker-compose.yml` — both services on `127.0.0.1`, with the manager gated behind the `manager` compose profile - `.env.example` — pinnable `CLOAKBROWSER_TAG`, `MANAGER_TAG`, and `MANAGER_AUTH_TOKEN` See `docker/README.md` for the full operator playbook. ## Modules | Subpath | Module | Description | |---|---|---| | `@suaveplan/testing-e2e/config` | [src/config/create-playwright-config.md](src/config/create-playwright-config.md) | `createPlaywrightConfig` — emits a `PlaywrightTestConfig` with WSL2-safe defaults (`fullyParallel: false`, trace/screenshot/video retention) and automatic CI mode (`retries: 2`, `workers: 1`) | | `@suaveplan/testing-e2e/page-objects` | [src/page-objects/page-object.md](src/page-objects/page-object.md) | Abstract `PageObject` base class — typed wrapper around a Playwright `Page` with `navigate()`, `waitForReady()`, and accessibility-first locator helpers (`findByRole`, `findByLabel`, `findByText`) | | `@suaveplan/testing-e2e/fixtures` | [src/fixtures/extended-test.md](src/fixtures/extended-test.md) | `extendedTest` — `test.extend(...)` with the `browser` worker fixture overridden to launch `cloakbrowser`; plus `apiContext` (isolated `APIRequestContext`, browser-free) and `freshPage` (isolated browser context + page); also exports `launchBrowserWithCloak` and `expect` | | `@suaveplan/testing-e2e/helpers` | [src/helpers/wait-for-network-idle.md](src/helpers/wait-for-network-idle.md) | `waitForNetworkIdle` — polls in-flight request count and resolves once the network has been idle for `idleMs` (default 500 ms); throws `WaitTimeoutError` on timeout | | | [src/helpers/mock-clipboard.md](src/helpers/mock-clipboard.md) | `mockClipboard` — installs a programmatic in-page clipboard so tests can assert `writeText`/`readText` without OS permissions | | | [src/helpers/mock-geolocation.md](src/helpers/mock-geolocation.md) | `mockGeolocation` — grants geolocation permission and sets static latitude/longitude/accuracy on a `BrowserContext` | | | [src/helpers/screenshot-on-failure.md](src/helpers/screenshot-on-failure.md) | `screenshotOnFailure` — captures a full-page screenshot named after the failing test; useful as a manual teardown hook alongside Playwright's built-in artifact retention | | | [src/helpers/dismiss-dialog.md](src/helpers/dismiss-dialog.md) | `dismissDialog` — registers a one-shot `dialog` handler that accepts or dismisses the next browser dialog (`alert`, `confirm`, `prompt`) | | `@suaveplan/testing-e2e/errors` | [src/errors/testing-e2e-error.md](src/errors/testing-e2e-error.md) | Typed error classes (`NavigationError`, `WaitTimeoutError`, `AbortedError`, `CloakLaunchError`) + `classifyCloakError` / `wrapCloakError` helpers | | | [src/errors/codes.md](src/errors/codes.md) | `TESTING_E2E_ERROR_CODES` registry | | _(observability)_ | [src/observability/instrumented.md](src/observability/instrumented.md) | `instrumented` — optional OTel wrapper (gated by `TESTING_E2E_OTEL_ENABLED=1`) that records call counts and duration histograms for package entrypoints |