Skip to content

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

Merged
Winify merged 5 commits into
mainfrom
feature/testmu-integration
Jun 7, 2026
Merged

feat(lambdatest): Integrate TestMU hub connection with tunnel, mirroring SauceLabs/BrowserStack#116
Winify merged 5 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

@Winify Winify linked an issue Jun 5, 2026 that may be closed by this pull request
@Winify Winify added the enhancement New feature or request label Jun 5, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Jun 5, 2026

Greptile Summary

This PR integrates LambdaTest (exposed as the testmu provider) into the existing cloud-provider framework, mirroring the BrowserStack and Sauce Labs patterns already present in the codebase.

  • New TestMuProvider (src/providers/cloud/testmu.provider.ts) covers three capability modes (browser, mobile browser/emulator, mobile native app), handles tunnel lifecycle via @lambdatest/node-tunnel, and reports session status via the LambdaTest REST API using environment-variable credentials (TESTMU_USERNAME / TESTMU_ACCESS_KEY).
  • cloud-provider.tool.ts adds TestMu to list_apps (dual Android + iOS fetch with merged results) and upload_app (targeting manual-api.lambdatest.com).
  • testmu-local.resource.ts provides the wdio://testmu/local-binary MCP resource with platform-specific download URLs, matching the BrowserStack/Sauce Labs resource pattern. Resource requirement wording conflates auto-start (tunnel: true) with manual-start (tunnel: 'external').

Confidence Score: 5/5

Safe to merge — the new provider is well-isolated, mirrors established patterns, and the two findings are minor defensive-fallback improvements that do not affect the happy path.

The core provider logic (capability building, tunnel lifecycle, REST status reporting) is correct and covered by a thorough test suite. The two findings relate to edge-case error fallbacks (an unexpected upload API response producing a bare 'unknown' string) and a resource description that misstates the manual-setup requirement for auto-started tunnels; neither affects normal operation.

src/tools/cloud-provider.tool.ts (parseUploadResponse fallback) and src/resources/testmu-local.resource.ts (requirement wording)

Important Files Changed

Filename Overview
src/providers/cloud/testmu.provider.ts New LambdaTest provider with browser/mobile browser/native app capability modes, tunnel lifecycle via @lambdatest/node-tunnel, and REST API session status reporting. REST response is now checked before logging success.
src/tools/cloud-provider.tool.ts Adds TestMu to list_apps and upload_app tools. TestMu list_apps fetches both android and ios endpoints and merges; upload_app targets manual-api.lambdatest.com. The parseUploadResponse fallback produces a bare 'unknown' string (no lt:// prefix) if neither app_id nor app_url is present in the response.
src/resources/testmu-local.resource.ts New MCP resource providing LambdaTest Tunnel binary download URLs and setup instructions per platform. Resource requirement text says MUST manually start before tunnel:true, but tunnel:true auto-starts via @lambdatest/node-tunnel without a manual binary download.
src/recording/code-generator.ts Adds LambdaTest code generation branch detecting lt:options in capabilities; generates correct hub hostname, env var references, and tunnel setup code using @lambdatest/node-tunnel.
src/tools/session.tool.ts Adds testmu to provider enum and testmuLocal as deprecated tunnel param; region description correctly scoped to 'saucelabs' only. Tunnel lifecycle correctly gated on effectiveTunnel === true.
src/providers/registry.ts Registers testMuProvider in the provider map alongside browserstack and saucelabs.
tests/providers/testmu.provider.test.ts Comprehensive unit tests covering all three capability modes, tunnel, reporting labels, credential handling, and onSessionClose REST/execute paths.

Sequence Diagram

sequenceDiagram
    participant C as MCP Client
    participant S as session.tool.ts
    participant P as TestMuProvider
    participant LT as LambdaTest API
    participant T as @lambdatest/node-tunnel

    C->>S: start_session(provider:'testmu', tunnel:true)
    S->>S: compute tunnelName (auto-generated)
    S->>P: buildCapabilities(options + tunnelName)
    P-->>S: "caps with lt:options.tunnel=true"
    S->>P: "startTunnel({tunnelName})"
    P->>T: "tunnel.start({user,key,tunnelName})"
    T-->>P: tunnel handle
    P-->>S: tunnelHandle
    S->>LT: remote(hub.lambdatest.com, caps)
    LT-->>S: sessionId
    S-->>C: session started

    C->>S: close_session()
    S->>P: onSessionClose(sessionId, 'browser', result)
    P->>LT: "PATCH /automation/api/v1/sessions/{id}"
    LT-->>P: 200 OK
    S->>P: stopTunnel(tunnelHandle)
    P->>T: tunnel.stop()
    T-->>P: stopped
    S-->>C: session closed
Loading

Reviews (4): Last reviewed commit: "fix(mobile): Allow mobile browser sessio..." | 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
@Winify
Copy link
Copy Markdown
Collaborator Author

Winify commented Jun 7, 2026

I addressed the issues and the greptile comments.

@Winify Winify merged commit 53a0e3c into main Jun 7, 2026
6 checks passed
@Winify Winify deleted the feature/testmu-integration branch June 7, 2026 20:08
@navin772
Copy link
Copy Markdown

navin772 commented Jun 8, 2026

Thank you @Winify ! Above changes worked well in my testing

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