@saucelabs/mobilewright-driver
v0.0.2
Published
Sauce Labs driver for Mobilewright
Readme
@saucelabs/mobilewright-driver
Run your Mobilewright tests on Sauce Labs real Android and iOS devices. Add the driver to your config; your tests don't change.
Requires mobilewright 0.0.67, Node.js 22.12 or later, and Android 13 or later on Android devices.
Get started
Install the driver:
npm i -D @saucelabs/mobilewright-driverSet the driver in
mobilewright.config.ts. This example runs Sauce Labs' demo app on a Pixel and an iPhone:import { defineConfig } from 'mobilewright'; import { sauceLabsDriver } from '@saucelabs/mobilewright-driver'; export default defineConfig({ testDir: './tests', use: { actionTimeout: 30_000 }, expect: { timeout: 30_000 }, driver: sauceLabsDriver({ region: 'us-west-1', build: 'my-first-build' }), // us-east-4, eu-central-1, asia-south-2 projects: [ { name: 'android', use: { platform: 'android', deviceName: /Google_Pixel(?!.*Fold)/, // no foldables: apps can open on the hidden screen osVersion: '>=13', bundleId: 'com.saucelabs.mydemoapp.android', installApps: 'https://github.com/saucelabs/my-demo-app-android/releases/download/2.3.0/mda-2.3.0-27.apk', }, }, { name: 'ios', use: { platform: 'ios', deviceName: /iPhone/, osVersion: '>=16.6', bundleId: 'com.saucelabs.mydemo.app.ios', installApps: 'https://github.com/saucelabs/my-demo-app-ios/releases/download/2.3.0/SauceLabs-Demo-App.ipa', }, }, ], });Write a test, as you would for a local device:
// tests/catalog.test.ts import { expect, test } from '@mobilewright/test'; test('the catalog lists the backpack', async ({ screen }) => { await expect(screen.getByText(/Sauce Labs Backpack/).first()).toBeVisible(); });Run it with your Sauce Labs credentials:
export SAUCE_USERNAME=… SAUCE_ACCESS_KEY=… npx mobilewright test --project android npx mobilewright test --project ios
Each test shows up on the Sauce Labs Test Results page with its video, screenshots and device logs.
Options
All options are optional.
| Option | Default | Description |
|---|---|---|
| username, accessKey | env vars | Sauce Labs credentials |
| region | us-west-1 | Data center: us-west-1, us-east-4, eu-central-1 or asia-south-2 |
| build, tags | — | Build name and tags shown on the Test Results page |
| reportTests | true | Report each test to the Test Results page |
| artifacts | video, screenshots, device logs | What Sauce Labs records per test: { video, screenshots, deviceLogs, networkCapture } |
| allocationTimeout | 300000 | How long to wait for a device, in ms |
| sessionDuration | Sauce Labs' default | Maximum session length, e.g. PT1H (limits) |
| sauceConnect | false | Start a Sauce Connect tunnel for the run |
| sauceConnectOpts | — | Tunnel options: any sc run flag, camelCased (e.g. tunnelDomains) |
| tunnelName, tunnelOwner | — | Use a tunnel that's already running, or one shared with you |
| appiumVersion | appium3-2026-07 | Hosted Appium version used for web views |
| iosWdaStorageRef, iosWdaBundleId | — | A custom WebDriverAgent (iOS, private devices only) |
Tips
Timeouts. Mobilewright's 5-second defaults are meant for local devices. Use 30 seconds for actionTimeout and
expect, as in the example above.
Parallel runs. Each worker uses one device. Set workers to your account's concurrency (the free
tier allows 1). Run Android and iOS in separate processes (--project android, --project ios): in one process,
Mobilewright's device pool can wait forever for the other platform.
Apps. installApps takes an https:// URL (Sauce Labs downloads it), a file in App Storage
(storage:<id> or storage:filename=<name>), or a local .apk, .ipa or .zip (uploaded once, then reused).
Test results. A retry shows up as … (retry 1). To skip a test without leaving an empty job on Sauce Labs, skip it
before it starts: test.skip(({ platform }) => platform === 'ios', 'reason'). Each test in Playwright's HTML report
links to its Sauce Labs job, where the video is. Mobilewright's own video option records nothing on Sauce Labs.
Local device or Sauce Labs. Use Sauce Labs only when the credentials are set:
const config: MobilewrightConfig = { /* ... */ };
if (process.env.SAUCE_USERNAME) config.driver = sauceLabsDriver();
export default defineConfig(config);Sauce Connect
Use Sauce Connect when your app talks to a server on your machine or on a private network. The driver downloads it, starts the tunnel before the first test and stops it at the end:
driver: sauceLabsDriver({
sauceConnect: true,
sauceConnectOpts: { tunnelDomains: ['.*\\.internal'] }, // optional: only these hosts use the tunnel
}),Good to know:
- Your organization must allow Sauce Connect on public devices.
- Devices don't send
localhostor127.0.0.1through the tunnel. Use another name for your server, e.g.localtest.mewithproxyLocalhost: 'allow'(localhost proxying). - Sauce Connect re-signs HTTPS. Chrome and Safari accept it; an Android app's web view may not. List
such hosts in
tlsPassthroughDomains. - Safari on iOS 18 and later tries HTTPS first, even for
http://URLs. For a plain HTTP server, list its host intlsPassthroughDomains.
Public and private devices
Both work. Public devices limit sessions to 1 hour, don't allow a custom WebDriverAgent, and may limit Android shell
commands to an allowlist. The driver needs uiautomator dump; if your account blocks it, use a
private device or ask Sauce Labs to allow it. See public and private devices.
Troubleshooting
- See what the driver does:
DEBUG=mw:mobilewright-driver*.SAUCE_LIVE_VIEW=1prints a link to watch each device live. - "Failed to reach Sauce Connect tunnel": the tunnel in
tunnelNameisn't running, or isn't shared with you (settunnelOwner). - "The application at path … cannot be installed": the device's OS is likely older than the app's minimum. Set
osVersion, e.g.'>=18.6'. - Web views on iOS: Safari needs Web Inspector on, and an app's web view must set
WKWebView.isInspectable = true.
Development
How the driver works: ARCHITECTURE.md. How to work on it: AGENTS.md.
npm install
npm run build
npm run lint
npm test # unit tests, no credentials neededLicense
Apache-2.0
