Skip to content

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

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

feat(lambdatest): Integrate TestMU hub connection with tunnel, mirroring SauceLabs/BrowserStack#116
Winify wants to merge 3 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 adds TestMu (LambdaTest) as a third cloud provider alongside BrowserStack and SauceLabs, following the same SessionProvider interface pattern. It covers browser, mobile browser/emulator, and native app sessions; tunnel lifecycle via @lambdatest/node-tunnel; dual-platform app listing (Android + iOS merged); and a companion wdio://testmu/local-binary resource.

  • TestMuProvider (src/providers/cloud/testmu.provider.ts): implements getConnectionConfig, buildCapabilities, startTunnel/stopTunnel, and onSessionClose with REST PATCH for browser sessions and browser.execute('lambda-status=…') for mobile. osVersion is accepted by the schema and shown in the README browser example but never placed in lt:options.platformVersion, so it is silently dropped.
  • list_apps / upload_app (src/tools/cloud-provider.tool.ts): TestMu fetches both ?type=android and ?type=ios and merges results; returns an error only when both platforms fail, which matches the PR's stated design intent.
  • Code generator and resource (src/recording/code-generator.ts, src/resources/testmu-local.resource.ts): LambdaTest try/catch/finally code generation added; binary download resource mirrors the BrowserStack/SauceLabs pattern.

Confidence Score: 4/5

Safe to merge after fixing the osVersion gap — no other correctness issues found.

The osVersion parameter is advertised in the README browser session example (osVersion: '11') and accepted by the Zod schema, but buildCapabilities never writes it into lt:options.platformVersion. Any user following the documentation will silently get an unconstrained OS version from LambdaTest. Everything else — tunnel lifecycle, session status reporting, dual-platform app listing, code generation — is correctly implemented and well-tested.

src/providers/cloud/testmu.provider.ts — the browser-platform branch of buildCapabilities needs to forward osVersion into lt:options.platformVersion.

Important Files Changed

Filename Overview
src/providers/cloud/testmu.provider.ts New TestMuProvider implementing SessionProvider. osVersion is accepted by the schema and shown in the README but never placed in lt:options.platformVersion for browser sessions, causing silent capability mismatch.
src/tools/cloud-provider.tool.ts Adds TestMu to list_apps/upload_app with dual-platform fetch for Android+iOS. Error surfacing when both platforms fail is correct; one-platform-success gracefully drops the failed-platform error (intentional by design per PR description).
src/resources/testmu-local.resource.ts New resource exposing TestMu tunnel binary download URL and setup steps, matching the BrowserStack/SauceLabs pattern. macOS always returns the Intel x64 binary (with a Rosetta 2 note for ARM64).
src/recording/code-generator.ts Adds LambdaTest code-gen branch (try/catch/finally with REST status update or execute-based for mobile). Minor: the fallback tunnel name in generated code ('wdio-mcp-tunnel') differs from the one session.tool.ts generates at runtime, but this only triggers for edge-case histories where lt:options.tunnelName isn't recorded.
src/tools/session.tool.ts Adds testmu to provider enum and testmuLocal legacy flag. Normalized effectiveTunnel correctly handles 'external' (does not auto-start tunnel; capabilities still carry lt:options.tunnel: true).
src/providers/cloud/saucelabs.provider.ts Minor refactor: SauceLabs import changed from dynamic await import('saucelabs') to a static top-level import. No behavioral change; module now loads at startup rather than lazily.
tests/providers/testmu.provider.test.ts Comprehensive test coverage for getConnectionConfig, buildCapabilities (browser, mobile browser, native app), getSessionType, and onSessionClose (REST PATCH + execute paths). All edge cases covered.
tests/tools/cloud-provider.tool.test.ts Adds TestMu list_apps and upload_app tests covering happy-path, dual-platform fetch, credential errors, and HTTP errors. All verified with mock fetch.

Sequence Diagram

sequenceDiagram
    participant C as MCP Client
    participant ST as session.tool.ts
    participant TMP as TestMuProvider
    participant LT as LambdaTest Hub
    participant LTAPI as LambdaTest REST API

    C->>ST: start_session(provider:'testmu', tunnel:true, ...)
    ST->>TMP: getConnectionConfig(options)
    TMP-->>ST: "{hostname:'hub.lambdatest.com', user, key}"
    ST->>TMP: "buildCapabilities({...args, tunnelName})"
    TMP-->>ST: "{browserName, platformName, lt:options:{tunnel:true,...}}"
    ST->>TMP: "startTunnel({tunnelName})"
    TMP->>LT: LambdaTunnel.start(user, key, tunnelName)
    LT-->>TMP: tunnel handle
    TMP-->>ST: tunnelHandle
    ST->>LT: "remote({...config, capabilities})"
    LT-->>ST: sessionId

    C->>ST: close_session()
    ST->>TMP: onSessionClose(sessionId, 'browser', result)
    TMP->>LTAPI: "PATCH /automation/api/v1/sessions/{sessionId}"
    LTAPI-->>TMP: 200 OK
    ST->>LT: browser.deleteSession()
    ST->>TMP: stopTunnel(tunnelHandle)
    TMP->>LT: tunnel.stop()
Loading

Reviews (3): Last reviewed commit: "fix(testmu): Address code review finding..." | 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
Comment thread src/providers/cloud/testmu.provider.ts Outdated

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thank you! Could not get a trial yet, so this code was "best effort guessing" based on the other 2

Comment thread src/providers/cloud/testmu.provider.ts Outdated
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 thread src/recording/code-generator.ts Outdated
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 thread src/providers/cloud/testmu.provider.ts Outdated
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.

@Winify Winify requested a review from navin772 June 6, 2026 09:18
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