Alternative Browser Engines
Playwright and Puppeteer drive full Chromium — the most accurate way to test what users see. But a new class of lightweight, agent-era browsers has appeared, and CodeceptJS can drive them through dedicated helpers:
- Obscura — an open-source Rust browser with a real
V8 engine. From v0.2.0, the default release build also renders — real layout, computed styles,
and screenshots — with
-no-renderbuilds still available for pure-speed, nothing-painted scraping mode. Release archives are available for Linux, macOS, and Windows. - Kitesurf — Cloudflare’s browser that runs in V8 isolates on Cloudflare Workers, with a real layout and rendering pipeline. Cloud-only, free in beta, planned to be open-sourced.
Both speak Chrome DevTools Protocol. CodeceptJS drives them with raw CDP — one round-trip per action, no stale element handles — which is why suites on these browsers run fast and never hang on navigation races.
When are they better than Playwright?
Section titled “When are they better than Playwright?”Smoke suites where startup and execution time matter. Obscura is distributed as a standalone binary and is designed for lightweight browser automation. Benchmark it against your own pages and CI environment before choosing it for a PR gate.
Massive parallel scale. Kitesurf sessions are Cloudflare Workers — they spawn in about a
second, cost nothing while idle, and there is no practical ceiling on how many you run at once.
Combined with run-workers, every worker acquires its own cloud browser:
// codecept.conf.js — each worker independently loads the config,// so each one gets its own Kitesurf session automaticallyexport const config = { helpers: { Kitesurf: { url: 'https://staging.myapp.com', }, },}
npx codeceptjs run-workers 16Sixteen cloud browsers, zero local resources, feedback in the time of your slowest test. Scale the number up as far as your suite can split — the browsers are no longer the bottleneck, and your CI runner only coordinates.
Testing the DOM, not the pixels. Most functional assertions — text appears, form submits, redirect happens, cookie is set — do not need a GPU raster pipeline. Obscura executes your app’s real JavaScript in real V8; it only skips painting. For API-adjacent flows (login → dashboard data appears), that is exactly the right amount of browser.
Constrained environments. ARM CI runners, thin containers, air-gapped machines: a static binary with no system dependencies goes where Chromium will not.
Optional stealth builds. Obscura publishes separate -stealth archives. Treat their behaviour
as an Obscura capability rather than a browser-compatibility guarantee from CodeceptJS.
When to stay with Playwright
Section titled “When to stay with Playwright”- Anything visual: visual regression, PDF (neither helper exposes PDF output). Obscura’s v0.2.0+ rendering/CSS engine is new and independently implemented — expect edge cases and gaps versus a real browser, especially around inherited properties and less common computed-style values.
- Visibility semantics on
-no-renderObscura builds (and v0.1.x): every element reports as visible —seeElement/dontSeeElementthrow and point you toseeElementInDOM. On v0.2.0+ default builds,CDPBrowserdetects the real layout engine per binary and visibility works normally. - Complex input: drag-and-drop, hover chains, file uploads, iframes, multi-tab, service workers.
- Cross-browser coverage (Firefox, WebKit).
- Testing local apps with Kitesurf: the cloud browser must reach your app; use a tunnel
(
cloudflared tunnel --url http://localhost:3000) or a deployed environment.
Configuration
Section titled “Configuration”CodeceptJS 4.2 is tested in CI with Obscura 0.2.2. Obscura 0.2.x is recommended; 0.1.x and
-no-render builds operate without layout, visibility assertions, or screenshots. See
Installation for platform-specific archive names.
helpers: { Obscura: { url: 'http://localhost:3000', },}
helpers: { Kitesurf: { url: 'https://staging.myapp.com', accountId: process.env.CF_ACCOUNT_ID, apiToken: process.env.CF_API_TOKEN, },}Obscura’s three connection modes
Section titled “Obscura’s three connection modes”Obscura manages its own obscura serve process, the same way Playwright manages its own browser
process — there is nothing to start by hand in the common case:
-
Self-launch (default) — leave
endpointunset. The helper resolves a binary (binaryPathin the config, thenOBSCURA_PATH, thenobscuraonPATH), spawnsobscura serveon a free port, and kills it when the run ends. Not settingportis intentional: a free port is picked automatically, which is what makesrun-workerscollision-free — every worker gets its own instance without any config.helpers: {Obscura: {url: 'http://localhost:3000',binaryPath: '/usr/local/bin/obscura', // optional override, like Playwright's executablePath},} -
Attach — set
endpointexplicitly to connect to an Obscura instance you manage yourself (already running locally, in a container, or on a remote host). The helper only connects; it never spawns or kills anything.helpers: {Obscura: {url: 'http://localhost:3000',endpoint: 'http://127.0.0.1:9222',},} -
Courtesy-attach — only relevant when
endpointis unset and no binary can be resolved either. If something is already answering on the conventionalhttp://127.0.0.1:9222, the helper attaches to it (and, again, never kills it) instead of failing outright. This exists so that “start Obscura by hand and just run the tests” keeps working without any config, while self-launch is still the default for everyone else.
Capability matrix
Section titled “Capability matrix”| Playwright | Obscura | Kitesurf | |
|---|---|---|---|
| Real JS execution (V8) | yes | yes | yes |
| Layout / getBoundingClientRect | yes | yes (v0.2.0+ default builds); synthetic on -no-render/v0.1.x | yes |
| Screenshots | yes | yes (v0.2.0+ default builds); no on -no-render/v0.1.x | yes |
| Visibility assertions | yes | yes (v0.2.0+ default builds); no, DOM-presence only, on -no-render/v0.1.x | yes |
Screencast / video (screencast plugin) | yes — WebM via page.screencast, with caption burn-in | yes — APNG via CDP Page.startScreencast, assembled in-process (v0.2.0+ default builds; verified PNG frames on the live server); no caption burn-in | untested |
| Startup model | local browser process | standalone local binary | remote cloud session |
| Parallel scale | machine-bound | machine-bound (light) | near-unlimited (cloud) |
| Where it runs | local/grid | local | Cloudflare only |
| License / cost | open source | Apache-2.0 | proprietary, free beta |