Skip to content

feat(lambdatest): Integrate TestMU hub connection with tunnel, mirroring SauceLabs/BrowserStack#116

Open
Winify wants to merge 2 commits into
mainfrom
feature/testmu-integration
Open

feat(lambdatest): Integrate TestMU hub connection with tunnel, mirroring SauceLabs/BrowserStack#116
Winify wants to merge 2 commits into
mainfrom
feature/testmu-integration

Conversation

@Winify
Copy link
Copy Markdown
Collaborator

@Winify Winify commented Jun 5, 2026

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. Uses TESTMU_USERNAME / TESTMU_ACCESS_KEY environment variables, credentials never appear in tool call parameters.

App Management (src/tools/cloud-provider.tool.ts)

  • list_apps for TestMu fetches both ?type=android and ?type=ios endpoints and merges results — no platform filter parameter needed
  • upload_app uploads to manual-api.lambdatest.com/app/upload/realDevice, returns lt://APP_ID refs
  • HTTP errors per-platform are collected; surfaces error only if both platforms fail

Tunnel Binary Resource (src/resources/testmu-local.resource.ts)

wdio://testmu/local-binary resource with platform-specific LambdaTest Tunnel binary download URLs and setup commands, matching the BrowserStack/Sauce Labs pattern.

Closing #104

Types of changes

  • Polish (an improvement for an existing feature)
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update (improvements to the project's docs)
  • Specification changes (updates to WebDriver command specifications)
  • Internal updates (everything related to internal scripts, governance documentation and CI files)

Checklist

  • I have squashed commits that belong together
  • I have tested with Claude (or another MCP-compatible client)
  • I have read the CONTRIBUTING doc
  • I have added the necessary documentation for new/changed tools (if appropriate)
  • I have added proper type definitions for new commands (if appropriate)

Further comments

Reviewers: @webdriverio/project-committers

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Jun 5, 2026

Greptile Summary

This PR integrates LambdaTest (branded as "TestMu") as a third cloud provider, mirroring the BrowserStack and Sauce Labs patterns already in the codebase.

  • TestMuProvider (src/providers/cloud/testmu.provider.ts) implements all SessionProvider methods: three capability modes (desktop browser, mobile browser/emulator, native app), tunnel lifecycle via @lambdatest/node-tunnel, and session status reporting via the LambdaTest REST API with proper res.ok checks.
  • cloud-provider.tool.ts adds TestMu to list_apps (fetching both ?type=android and ?type=ios and merging) and upload_app (posting to manual-api.lambdatest.com).
  • testmu-local.resource.ts exposes the wdio://testmu/local-binary resource for manual tunnel binary setup, but its requirement field incorrectly claims this step is mandatory for tunnel: true — the npm package auto-manages the binary in that case.

Confidence Score: 5/5

Safe 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.

Important Files Changed

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
Loading

Reviews (2): Last reviewed commit: "docs(provider): Update README documentat..." | Re-trigger Greptile

Comment thread src/tools/cloud-provider.tool.ts
Comment thread src/providers/cloud/testmu.provider.ts Outdated
Comment thread src/tools/session.tool.ts Outdated
Winify added 2 commits June 5, 2026 10:54
…ing SauceLabs/BrowserStack

- Adding LambdaTunnel declaration to types
- Intorducing resources for tunnel setup
@Winify Winify force-pushed the feature/testmu-integration branch from 6ec8417 to c76e4da Compare June 5, 2026 08:55

// Mobile browser/emulator mode (e.g. Chrome on Android emulator)
if (mobileBrowser) {
ltOptions.appiumVersion = '2.11.0';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +145 to +146
const body = { status_ind: result.status === 'passed' ? 'passed' : 'failed' };
const apiUrl = `https://api.lambdatest.com/automation/api/v1/sessions/${sessionId}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +294 to +298
" 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 })',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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');

Comment on lines +31 to +32
if (process.env.TESTMU_USERNAME) ltOptions.username = process.env.TESTMU_USERNAME;
if (process.env.TESTMU_ACCESS_KEY) ltOptions.accessKey = process.env.TESTMU_ACCESS_KEY;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Native LambdaTest Support as a Cloud Provider

2 participants