feat(lambdatest): Integrate TestMU hub connection with tunnel, mirroring SauceLabs/BrowserStack#116
feat(lambdatest): Integrate TestMU hub connection with tunnel, mirroring SauceLabs/BrowserStack#116Winify wants to merge 2 commits into
Conversation
Greptile SummaryThis PR integrates LambdaTest (branded as "TestMu") as a third cloud provider, mirroring the BrowserStack and Sauce Labs patterns already in the codebase.
Confidence Score: 5/5Safe to merge — the provider implementation is structurally sound and closely mirrors the existing BrowserStack/SauceLabs patterns; no runtime logic bugs were found in the changed paths. The core provider, capability building, session status reporting, and code generation are all correct. The two findings in testmu-local.resource.ts are documentation/labeling issues that do not affect functionality: the tunnel auto-management path works regardless of the misleading resource description, and the ARM64 Linux label mismatch only affects the informational resource output for the tunnel: 'external' scenario. src/resources/testmu-local.resource.ts — the resource description and requirement field overstate what is required for tunnel: true, and the Linux ARM64 arch label is inconsistent with the macOS pattern.
|
| Filename | Overview |
|---|---|
| src/providers/cloud/testmu.provider.ts | New TestMuProvider implementing SessionProvider for LambdaTest — covers browser, mobile browser/emulator, and native app modes with correct capability construction, auto-managed tunnel via npm package, and proper session status PATCH with response check. |
| src/resources/testmu-local.resource.ts | New resource for TestMu tunnel binary setup — description and requirement field incorrectly state that manual daemon start is required for tunnel: true (the npm package handles this automatically); Linux ARM64 arch label is misleading since the served binary is x64. |
| src/tools/cloud-provider.tool.ts | TestMu support added to list_apps and upload_app — both platform error paths now correctly surfaced; the two per-platform list fetches run sequentially rather than in parallel, adding unnecessary latency. |
| src/recording/code-generator.ts | LambdaTest code generation added — try/catch/finally scaffold mirrors BrowserStack and SauceLabs patterns, tunnel setup emitted as top-level ESM imports with correct async tunnel.start/stop wiring. |
| src/tools/session.tool.ts | testmu added to provider enum; testmuLocal legacy tunnel param added and wired into effectiveTunnel fallback chain consistently across both browser and mobile session paths. |
| tests/providers/testmu.provider.test.ts | New test suite with good coverage of capabilities, session type, tunnel flags, and onSessionClose — including error/missing-credentials paths. |
Sequence Diagram
sequenceDiagram
participant Client as MCP Client
participant Tool as session.tool.ts
participant Reg as providers/registry.ts
participant TM as TestMuProvider
participant LT as @lambdatest/node-tunnel
participant Hub as hub.lambdatest.com
participant API as api.lambdatest.com
Client->>Tool: start_session(provider:testmu, tunnel:true)
Tool->>Reg: getProvider('testmu', platform)
Reg-->>Tool: TestMuProvider
Tool->>TM: startTunnel(options)
TM->>LT: "tunnel.start({user, key, tunnelName})"
LT-->>TM: tunnel handle
TM-->>Tool: tunnelHandle
Tool->>TM: getConnectionConfig(options)
TM-->>Tool: "{hostname, user, key}"
Tool->>TM: buildCapabilities(options)
TM-->>Tool: caps with lt:options
Tool->>Hub: new remote(caps)
Hub-->>Tool: browser + sessionId
Tool-->>Client: session started
Client->>Tool: stop_session(status)
Tool->>TM: onSessionClose(sessionId, result)
TM->>API: "PATCH /sessions/{id} {status_ind}"
API-->>TM: 200 OK
Tool->>TM: stopTunnel(tunnelHandle)
TM->>LT: tunnel.stop()
LT-->>TM: stopped
Tool-->>Client: session closed
Reviews (2): Last reviewed commit: "docs(provider): Update README documentat..." | Re-trigger Greptile
…ing SauceLabs/BrowserStack - Adding LambdaTunnel declaration to types - Intorducing resources for tunnel setup
6ec8417 to
c76e4da
Compare
|
|
||
| // Mobile browser/emulator mode (e.g. Chrome on Android emulator) | ||
| if (mobileBrowser) { | ||
| ltOptions.appiumVersion = '2.11.0'; |
There was a problem hiding this comment.
Don't hardcode appiumVersion for the virtual mobile-browser path. TestMu AI rejects a pinned version here with The Device/Appium version combination is not supported, I got this while testing this PR locally. Both 2.11.0 and latest fail, while omitting it succeeds (hub auto-selects).
Though, native real-device mode below accepts latest.
| const body = { status_ind: result.status === 'passed' ? 'passed' : 'failed' }; | ||
| const apiUrl = `https://api.lambdatest.com/automation/api/v1/sessions/${sessionId}`; |
There was a problem hiding this comment.
This REST PATCH only works for web sessions. For mobile, the WebDriver sessionId isn't addressable here - it 404s (Either resource not found or already deleted); mobile sessions live under mobile-api.lambdatest.com keyed by an internal test_id, so status is silently never recorded.
Branch by sessionType: web keeps this PATCH; mobile (ios/android) should use the live handle - await browser.execute('lambda-status=' + status) (the hub intercepts it server-side; verified). The hook doesn't work for web, hence the split.
| " const ltAuth = Buffer.from(`${process.env.TESTMU_USERNAME}:${process.env.TESTMU_ACCESS_KEY}`).toString('base64');", | ||
| " await fetch('https://api.lambdatest.com/automation/api/v1/sessions/' + browser.sessionId, {", | ||
| " method: 'PATCH',", | ||
| " headers: { Authorization: 'Basic ' + ltAuth, 'Content-Type': 'application/json' },", | ||
| ' body: JSON.stringify({ status_ind: ltStatus })', |
There was a problem hiding this comment.
Same issue as the provider's onSessionClose, in the generated script: this REST PATCH 404s for real-device mobile sessions. Branch on history.type - emit await browser.execute('lambda-status=' + ltStatus) for ios/android, and keep the REST PATCH only for browser.
Something like:
const isMobile = history.type !== 'browser';
const statusUpdate = isMobile
? " await browser.execute('lambda-status=' + ltStatus);"
: [
" const ltAuth = Buffer.from(`${process.env.TESTMU_USERNAME}:${process.env.TESTMU_ACCESS_KEY}`).toString('base64');",
" await fetch('https://api.lambdatest.com/automation/api/v1/sessions/' + browser.sessionId, {",
" method: 'PATCH',",
" headers: { Authorization: 'Basic ' + ltAuth, 'Content-Type': 'application/json' },",
' body: JSON.stringify({ status_ind: ltStatus })',
' });',
].join('\n');| if (process.env.TESTMU_USERNAME) ltOptions.username = process.env.TESTMU_USERNAME; | ||
| if (process.env.TESTMU_ACCESS_KEY) ltOptions.accessKey = process.env.TESTMU_ACCESS_KEY; |
There was a problem hiding this comment.
I think we can drop these 2 as getConnectionConfig() already passes user/key at the connection level, I verified that it works fine without passing them as caps and just having them as env vars.
It could also leak the creds wherever the capabilities is parsed.
Proposed changes
New Provider (src/providers/cloud/testmu.provider.ts)
TestMuProvider implements the SessionProvider interface with three capability modes (browser, mobile browser/emulator, mobile native app), tunnel lifecycle via
@lambdatest/node-tunnel, and session status reporting via LambdaTest REST API. UsesTESTMU_USERNAME/TESTMU_ACCESS_KEYenvironment variables, credentials never appear in tool call parameters.App Management (src/tools/cloud-provider.tool.ts)
Tunnel Binary Resource (src/resources/testmu-local.resource.ts)
wdio://testmu/local-binaryresource with platform-specific LambdaTest Tunnel binary download URLs and setup commands, matching the BrowserStack/Sauce Labs pattern.Closing #104
Types of changes
Checklist
Further comments
Reviewers: @webdriverio/project-committers