@e2e-dev/testmu runs targets on TestMu AI (formerly LambdaTest). testmu() is a device provider for Android emulators, iOS simulators, and real devices. testmuBrowsers() from @e2e-dev/testmu/web is a browser provider for hosted Chrome and Edge. Tests stay the same.

Authenticate

Set LT_USERNAME and LT_ACCESS_KEY in the environment e2e run starts in, such as CI secrets. Copy them from Account Settings → Password & Security → Username and Access Key (accounts.lambdatest.com/security/username-accesskey). Without either, the lease fails before any test starts. testmuBrowsers() sends them only in each session’s CDP URL, never in a log line.

Run mobile targets on devices

Each worker slot leases one device when the run starts and releases it when the run ends.

No Mac, Xcode, or Android SDK on the machine that runs e2e.

Install the integration alongside the mobile engine:

npm install --save-dev e2e @e2e-dev/mobile @e2e-dev/testmu
e2e.config.ts
import type { E2EConfig } from 'e2e';
import { mobile } from '@e2e-dev/mobile';
import { testmu } from '@e2e-dev/testmu';

const apk = 'https://prod-mobile-artefacts.lambdatest.com/assets/docs/proverbial_android.apk';
const ipa = 'https://prod-mobile-artefacts.lambdatest.com/assets/docs/proverbial_ios.ipa';

export default {
  targets: [
    {
      name: 'android-emulator',
      engine: mobile({
        platform: 'android',
        device: testmu({ device: 'Galaxy S22 Ultra 5G', osVersion: '14', app: apk }),
      }),
      app: { bundleId: 'com.lambdatest.proverbial' },
    },
    {
      name: 'ios-real',
      engine: mobile({
        platform: 'ios',
        device: testmu({ device: 'iPhone 16', osVersion: '18', app: ipa, deviceType: 'real' }),
      }),
      app: { bundleId: 'proverbial' },
    },
    {
      name: 'android-real',
      engine: mobile({
        platform: 'android',
        device: testmu({ device: 'Pixel 6', osVersion: '14', app: apk, deviceType: 'real' }),
      }),
      app: { bundleId: 'com.lambdatest.proverbial' },
    },
  ],
  workers: 1,
} satisfies E2EConfig;

device and osVersion must match TestMu AI’s catalog exactly, as the capabilities generator lists them: 18.0 for a virtual iOS device, 18 for a real one, 14 for Android. Real and virtual devices have separate catalogs.

app is the build TestMu AI installs on every session: an lt:// app id, an https URL, or a local path, resolved against the directory of e2e.config.ts and uploaded. Use an .apk or .aab on Android, a zipped .app on an iOS simulator, and a signed .ipa on a real iOS device. Leave the target’s app.appPath out, and keep app.bundleId for app.open().

Example test

tests/home.e2e.ts
import { test } from '@e2e-dev/mobile';
import { expect } from 'e2e';

test('Proverbial home screen', async ({ app, screen }) => {
  await app.open();
  await expect(screen.getByText('Proverbial')).toBeVisible();
  // Android labels the button GEOLOCATION, iOS GeoLocation.
  await expect(screen.getByRole('button', /^geolocation$/i)).toBeVisible();
});

Run web targets in browsers

Each worker slot gets its own Chrome or Edge session for the run. Every attempt gets a new browser context in that session, as in a local run, so a test never sees another test’s cookies, storage, or permissions.

Install the integration alongside the web engine:

npm install --save-dev e2e @e2e-dev/web @e2e-dev/testmu

Set the target’s browser to testmuBrowsers():

e2e.config.ts
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(), viewport: null }),
      app: { url: 'https://staging.example.com' },
    },
  ],
} satisfies E2EConfig;

Device options

OptionDefaultMeaning
devicerequiredDevice name as TestMu AI’s catalog lists it ('Galaxy S22 Ultra 5G', 'iPhone 16').
osVersionrequiredOS version as the catalog lists it for that device.
apprequiredThe build TestMu AI installs: an lt:// app id, an https URL, or a local path.
deviceType'virtual''virtual' for an emulator or simulator, 'real' for a real device.
project'e2e'Dashboard project the sessions are grouped under.
buildthe run idDashboard build the sessions are grouped under.
sessionNamee2e-<run id>-<target>-<slot>Session name on the dashboard. With more than one worker slot, -<slot> is appended.
orientationthe device’s'portrait' or 'landscape'.
geoLocationTestMu AI’sCountry code the device’s IP geolocates to ('US').
timezonethe device’sTime zone ('UTC+05:30').
languagethe device’sLanguage code ('fr').
localethe device’sLocale ('fr_FR').
appiumVersionTestMu AI’sAppium version for the session ('2.16.2').

Device features are set when the session starts and hold for the whole session. An unknown option or an invalid value fails the config load with INVALID_CONFIG.

Browser options

OptionDefaultMeaning
browserName'Chrome''Chrome' or 'MicrosoftEdge'. The web engine attaches over CDP, so any other browser fails the config.
capabilitiesnonebrowserVersion, sent next to browserName, and TestMu AI’s LT:Options fields, such as platform, geoLocation, timezone, video, network, or tunnel and tunnelName for TestMu AI Tunnel.

The provider sends platform (Windows 11, since TestMu AI refuses a session without one), build (the run id), project (e2e), and idleTimeout (600 seconds) unless capabilities sets them. Edge sessions get --user-data-dir=remote-debug-profile-edge in browserOptions unless capabilities set one. It sets user, accessKey, and name itself, so capabilities that set any of them fail the config with INVALID_CONFIG. So do a nested LT:Options, a browserName (use the option), a value JSON cannot carry, and a misspelled testmuBrowsers() option. Other capabilities keys go to TestMu AI unchecked.

testmuBrowsers({
  browserName: 'MicrosoftEdge',
  capabilities: { browserVersion: '140', platform: 'Windows 10', build: `checkout ${process.env.GITHUB_SHA}`, video: true },
})

Find sessions and videos

By default, the dashboard names each session e2e-<run id>-<target>-<slot>.

An attempt that records video gets:

  • On a device, a link to TestMu AI’s recording of the session, found by its build and sessionName. The recording covers the whole session, so every attempt on a worker slot links the same video. Runs that overlap and share a fixed build and sessionName can link each other’s videos; the defaults never do.
  • In a browser, the web engine’s screencast of the page, as in a local run. TestMu AI records the session’s screen when you pass capabilities: { video: true }. Watch it in the TestMu AI dashboard.

Limits

  • Parallel sessions. Keep workers times the number of TestMu AI targets within your plan’s parallel sessions. A browser session beyond the plan waits in TestMu AI’s queue.
  • Device sessions are billed while leased. A device session starts when its slot is leased, which takes about 30 to 75 seconds. It is billed until the run releases it, even when no test runs on it.
  • Stopped runner on a device. If the runner is killed before it releases a device, nothing sends the session another command. TestMu AI’s idle timeout (120 seconds by default) ends it, about three minutes after its last command.
  • Device settings and logs. Device settings (permissions, location, network, appearance), system alerts, recording and device logs through agent-device, and port reverse are not available. Read logs on the TestMu AI dashboard, and use TestMu AI Tunnel to reach a server on your network.
  • 60-second browser connect. The web engine waits at most 60 seconds for a browser session to accept the CDP connection. A shorter launchTimeout ends the wait sooner, but a longer one does not extend it. A session still queued or provisioning after that fails with LAUNCH_TIMEOUT, or with ENGINE_FAILURE when launchTimeout is over 60 seconds.
  • Dropped browser connections. A browser session cannot be resumed, so a dropped connection fails the attempt.
  • Browser downloads. TestMu AI’s browsers save downloads on their own machines, so browser.waitForDownload fails with ENGINE_FAILURE. Assert on the download link or the request instead.

Next