feat(testmu): hosted Chrome and Edge for the web engine with testmuBrowsers()
Merged
SA
21d ago+689+689+012345678901234567890123456789-8-8-0123456789
What and why
The upstream maintainer closed tester-army/e2e#765 (TestMu AI desktop browsers) and asked to consolidate the TestMu AI integration in tester-army/e2e#772, which covers devices only. This PR adds the browser side to @e2e-dev/testmu so #772 carries both.
testmuBrowsers(), exported from@e2e-dev/testmu/web, is aBrowserProviderfor@e2e-dev/web: TestMu AI hosted Chrome and Edge on Windows and macOS, one session per worker slot, or one per attempt withscope: 'attempt'.- A session starts when its CDP websocket opens and ends when the engine closes it, so the provider only builds the CDP URL. There's no API call and no SDK. Credentials come from the package's existing
testmuCredentials()(LT_USERNAME/LT_ACCESS_KEY) and never appear in a log line. - Follows
testmu()'s conventions:build(default the run id) andsessionName(defaulte2e-<run id>-<target>-<slot or attempt id>) name sessions as the device provider does, so a run's device and browser sessions land in one build.project(defaulte2e) is sent asLT:Options.project, astestmu()sends its own.geoLocationandtimezoneare top-level options, as ontestmu().- Optional strings must be non-empty, defaults apply only to
undefined(sonullfails), and unknown keys are refused. These are the same rules cubic asked for on #772.
- Other options:
browserName(Chrome/MicrosoftEdge),browserVersion,platform,route,hub,scope,capabilities(furtherLT:Options).idleTimeoutdefaults to 600 s.capabilitiescannot set what the provider sets (user,accessKey,build,name,project,platform,geoLocation,timezone,browserName,browserVersion, or a nestedLT:Options). route:/puppeteer(default) serves raw CDP on every machine./playwright-cdplabels sessions as Playwright but serves raw CDP only on machines with TestMu AI's newest backend, which is still rolling out, and fails to connect elsewhere.- The package root stays device-only (unchanged
index.ts). The browser provider lives only at@e2e-dev/testmu/web, so a web-only project never loads agent-device or@e2e-dev/mobile, and the root's types never reference@e2e-dev/web. - Optional peers:
@e2e-dev/mobile,@e2e-dev/webandagent-deviceare now marked optional. Device users already install agent-device explicitly (no published release satisfies the range yet), so nothing changes for them. Without this, a web-onlynpm install @e2e-dev/testmuwould try to resolve the unpublished agent-device range.
import type { E2EConfig } from 'e2e';
import { web } from '@e2e-dev/web';
import { testmuBrowsers } from '@e2e-dev/testmu/web';
export default {
targets: [
{
name: 'testmu-web',
engine: web({ browser: testmuBrowsers({ platform: 'Windows 11', capabilities: { video: true } }), viewport: null }),
app: { url: 'https://staging.example.com' },
},
],
} satisfies E2EConfig;
sequenceDiagram
participant W as web engine
participant P as testmuBrowsers()
participant H as cdp.lambdatest.com
W->>P: acquire(request)
P-->>W: { id, cdpEndpoint } (caps + LT_* from request.env)
W->>H: connectOverCDP(cdpEndpoint)
Note over H: socket open = session starts
W->>H: close
Note over H: socket closed = session ends
W->>P: release() (no-op)
- Docs: new
docs/integrations/testmu-browsers.mdx(options, scopes, routes, recordings, downloads, Limits), with its config sample checked asdocs/examples/web/testmu-browsers.config.ts(registered inscripts/check-docs-examples.ts, like the mobile example), plus the nav, integrations card, environment table, security page,browser.mdx, package README, AGENTS.md and theskills/e2esetup row. Both TestMu AI pages now say where to get the credentials (Account Settings → Password & Security → Username and Access Key).
Verified
Ran it locally: yes
pnpm checkpasses (lint, dead code, typecheck, error codes, peer ranges, install scripts, docs).pnpm --filter @e2e-dev/testmu run test: 108/108, of which 35 are new for the browser provider. They cover the URL and capabilities, credentials fromrequest.envonly, everyINVALID_CONFIGcase (includingnullvalues and the reservedcapabilitieskeys), the 600 sidleTimeoutdefault,testmu()-style project/build/session naming, log lines without the key, the/webexport with a device-only root, and 24 worker leases fitting the engine's 16 KB hand-off.- Packed
@e2e-dev/testmu(pnpm pack) and installed it browser-only into a scratch project one2e0.15.1: npm pulled neitheragent-devicenor@e2e-dev/mobile, and the import came from@e2e-dev/testmu/web. - Real runs on TestMu AI production:
| Route | Platform | Result |
|---|---|---|
/puppeteer (default) | Windows 11 + Chrome | ✅ 1/1, recorded with build <run id> and session e2e-<run id>-<target>-1 |
/puppeteer, scope: 'attempt' | Windows 11 + Chrome | ✅ 2/2, one session per test |
/playwright-cdp | macOS Sequoia + Chrome, pinned to a machine with the backend fix | ✅ 2/2, session labelled Playwright |
timezone: 'UTC+05:30' | Windows 11 + Chrome | ✅ browser reports Asia/Calcutta (+330 min) |
- No report, log or artifact file from any run contains the access key.
projectis sent inLT:Optionsthe waytestmu()sends it. The desktop sessions API doesn't return a project field, so the docs only say it is sent, not that the dashboard groups by it.- Platform issues seen in the sanity run. Both reproduce with plain Puppeteer/Playwright, without this provider, and are being reported to TestMu AI:
- Edge over CDP: on Windows 11 the hub answers
500after about 4 minutes (Chrome connects in about 11 s), so Edge sessions hit e2e's 60 s connect limit. - Geo exits:
geoLocation: 'DE'exits in Portugal and'FR'in Spain. The option is applied (the default exit is Mumbai), but those exits sit in neighbouring countries.
- Edge over CDP: on Windows 11 the hub answers
- Not included: linking TestMu AI's recording through
record(), astestmu()does for devices. The web engine supports it, but it needs the desktop sessions API, so it's left for a follow-up. - In
workerscope about 24–30 workers per target fit the engine's 16 KB lease hand-off (documented under Limits).scope: 'attempt'has no such ceiling. - A session that waits longer than 60 s for a machine (queued, or pinned to a busy one) fails with
LAUNCH_TIMEOUT. The web engine's CDP connect is fixed at 60 s, as the Limits section documents.
🤖 Generated with Claude Code