The JavaScript client
It speaks to the same tool layer as the CLI and the MCP server, by spawning
mobium pipe, so the mobium binary has to be on PATH or named by
MOBIUM_BIN_PATH. The quick start goes from nothing to a
running script.
Install
git clone https://github.com/mobiumdev/mobium.git
npm install ./mobium/clients/javascript
Connect
import { start } from 'mobium'
const device = await start({ platform: 'android', app: 'com.android.settings' })
for (const el of await device.map()) console.log(el.ref, el.label)
await device.quit()
device below is what start() or connect() resolved to: a Device. Every method returns a promise.
Every tool, by method
Connecting, ending, and errors
start
export function start(options?: StartOptions): Promise<Device>
Connect and open the session on the device.
End it with quit().
Open a session on the one running device
import { start } from 'mobium'
const device = await start()
try {
console.log(device.session?.platform, device.session?.driver)
} finally {
await device.quit()
}
Pick the device and platform, and launch an app
import { start } from 'mobium'
const device = await start({ platform: 'ios', device: 'booted', app: 'com.apple.Preferences' })
connect
export function connect(options?: ConnectOptions): Promise<Device>
Connect to mobium without touching the device. close() leaves the session open.
Connect without opening a session, and leave it open
import { connect } from 'mobium'
const device = await connect({ device: 'emulator-5554' })
console.log(await device.current())
await device.close()
Give each parallel run a daemon of its own
import { connect } from 'mobium'
const device = await connect({ device: 'emulator-5556', session: 'run2', callTimeoutMs: 60000 })
findBinary
export function findBinary(explicit?: string): string
Locate the mobium binary: the explicit path, then MOBIUM_BIN_PATH, then PATH.
Find the mobium executable a connection would run
import { findBinary } from 'mobium'
console.log(findBinary()) // MOBIUM_BIN_PATH, else the first on PATH
errorFrom
export function errorFrom(text: string, structured?: unknown): MobiumError
The exception for a failed tool call, from its text and structured content.
Turn a tool's error result into the matching exception
import { errorFrom, NoSuchElementError } from 'mobium'
const err = errorFrom('no element matches text=Sign in', { code: 'no_such_element', remedy: 'run app_map' })
console.log(err instanceof NoSuchElementError, err.remedy)
session
session?: string
session: Session | null
A daemon of this connection's own, by name, as MOBIUM_SESSION sets one. One daemon serves one call at a time across every device, so parallel runs on different devices should each name one. Keep it short; it is part of a socket path.
See what start() opened
const { device: id, platform, driver, reused } = device.session ?? {}
console.log(id, platform, driver, reused)
sessions
sessions(): Promise<{ device: string; platform: string; driver: string }[]>
The sessions open on the daemon.
List the sessions open on the daemon
for (const s of await device.sessions()) console.log(s.device, s.platform, s.driver)
quit
quit(): Promise<void>
End the session on the device and close the connection. The app start() launched, if any, is stopped too.
End the session, whatever happens
try {
await device.tap('text=Network & internet')
} finally {
await device.quit()
}
close
close(): Promise<void>
Close the connection; the session stays open. Resolves once mobium has exited.
Close the connection and keep the session for the CLI
await device.close()
MobiumError
export class MobiumError extends Error
Decide by the error's code, never its message
import { MobiumError } from 'mobium'
try {
await device.tap('text=Sign in')
} catch (e) {
if (!(e instanceof MobiumError)) throw e
console.log(e.code, e.remedy, e.retryable, e.details)
}
NoSuchElementError
export class NoSuchElementError extends MobiumError {}
Treat a missing element as an answer
import { NoSuchElementError } from 'mobium'
try {
await device.tap('text=Skip')
} catch (e) {
if (!(e instanceof NoSuchElementError)) throw e // no Skip button this time
}
TimedOutError
export class TimedOutError extends MobiumError {}
Wait, and say what happened if it never came
import { TimedOutError } from 'mobium'
try {
await device.waitFor('text=Welcome back', { timeoutMs: 5000 })
} catch (e) {
if (e instanceof TimedOutError) console.log(e.message) // what was on screen instead
else throw e
}
UnsupportedError
export class UnsupportedError extends MobiumError {}
Skip what a platform cannot do
import { UnsupportedError } from 'mobium'
try {
await device.press('back')
} catch (e) {
if (!(e instanceof UnsupportedError)) throw e
console.log(e.remedy) // iOS has no back button
}
Types
| Type | What it is |
|---|---|
Bounds | An on-screen rectangle, in device pixels on both platforms. |
Element | One actionable thing on screen, from map() or find(). |
MapChange | One element that is on both maps and differs between them, from mapDiff(). |
MapDiff | What changed since the last map of this device, from mapDiff(). |
DeviceInfo | One attached device or simulator. |
Session | What start() opened. |
Cookie | One of a page's cookies, with Vibium's keys. |
StorageState | A page's cookies and web storage, in the shape Vibium saves. |
Point | A point in device pixels. |
Trace | What traceStart(), traceStop() and trace() resolve to. |
Data | A tool's structured answer, for the calls that return one as is. |
BatchStep | One call in a batch: a tool and the arguments it takes on its own. |
BatchStepResult | One step's answer in a batch. |
ConnectOptions | |
StartOptions | |
WaitOptions | |
Device | |
MobiumError | |
NoDeviceError | |
DeviceNotReadyError | |
ToolchainMissingError | |
NoSuchElementError | |
AmbiguousLocatorError | |
ElementNotReachableError | |
NoSuchContextError | |
NoSuchAlertError | |
UnsupportedError | |
NotConfirmedError | |
TimedOutError | |
InvalidArgumentError | |
DeviceServerError | |
InternalError |