Effective device management is crucial for scaling your mobile automation. Whether you’re testing on a single device or orchestrating dozens simultaneously, understanding device management patterns will help you build robust automation workflows.
Understanding devices
In Uplink, a device (also called a worker ) is a mobile phone or tablet that connects to your session and can host browsers. Devices can join sessions through either:
Connect app : Users scan a QR code or enter a session code
SDK integration : Your custom app connects programmatically
Connecting devices
Waiting for devices
The most common pattern is to wait for a device to connect before starting automation:
const client = await uplink . client . connect ( 'wss://relay.uplink.build/session/<jwt>' )
// Wait for any device to connect
const device = await client . worker ()
console . log ( 'Device connected:' , device . address )
// Now you can use the device
const browser = await device . launch ()
Listing connected devices
Get all currently connected devices:
const devices = client . workers ()
console . log ( ` ${ devices . length } devices connected` )
devices . forEach ( device => {
console . log ( 'Device:' , device . address )
})
Device connection events
React to devices connecting and disconnecting in real-time:
client . on ( 'worker-connected' , async ( device ) => {
console . log ( 'New device connected:' , device . address )
// Automatically start automation on new devices
const browser = await device . launch ()
const page = await browser . newPage ()
await page . goto ( 'https://example.com' )
})
client . on ( 'worker-disconnected' , ( device ) => {
console . log ( 'Device disconnected:' , device . address )
})
Retrieve information about a device’s capabilities and specifications:
const info = await device . getDeviceInfo ()
console . log ( 'Device model:' , info . deviceModel ) // e.g., "iPhone 14 Pro"
console . log ( 'Platform:' , info . platform ) // "iOS" or "Android"
console . log ( 'OS version:' , info . platformVersion ) // e.g., "17.2"
console . log ( 'Type:' , info . deviceType ) // "phone" or "tablet"
Using device info for targeting
const devices = client . workers ()
// Find iOS devices
const iosDevices = []
for ( const device of devices ) {
const info = await device . getDeviceInfo ()
if ( info . platform === 'iOS' ) {
iosDevices . push ( device )
}
}
// Test on a specific device type
const iphone = devices . find ( async d => {
const info = await d . getDeviceInfo ()
return info . deviceModel . includes ( 'iPhone' )
})
Multi-device patterns
Parallel execution
Run automation on multiple devices simultaneously:
const client = await uplink . client . connect ( 'wss://relay.uplink.build/session/<jwt>' )
// Wait for multiple devices
const device1 = await client . worker ()
const device2 = await client . worker ()
const device3 = await client . worker ()
// Run tests in parallel
await Promise . all ([
runTest ( device1 ),
runTest ( device2 ),
runTest ( device3 )
])
async function runTest ( device ) {
const browser = await device . launch ()
const page = await browser . newPage ()
await page . goto ( 'https://example.com' )
// ... test logic
await browser . close ()
}
Device pool management
Manage a pool of devices for load distribution:
class DevicePool {
constructor ( client ) {
this . client = client
this . available = []
this . busy = new Set ()
client . on ( 'worker-connected' , ( device ) => {
this . available . push ( device )
})
}
async acquire () {
// Wait for an available device
while ( this . available . length === 0 ) {
await new Promise ( resolve => setTimeout ( resolve , 100 ))
}
const device = this . available . shift ()
this . busy . add ( device )
return device
}
release ( device ) {
this . busy . delete ( device )
this . available . push ( device )
}
}
// Usage
const pool = new DevicePool ( client )
async function runAutomation () {
const device = await pool . acquire ()
try {
const browser = await device . launch ()
// ... automation logic
await browser . close ()
} finally {
pool . release ( device )
}
}
// Run multiple automations
await Promise . all ([
runAutomation (),
runAutomation (),
runAutomation ()
])
Run different tests based on platform:
client . on ( 'worker-connected' , async ( device ) => {
const info = await device . getDeviceInfo ()
if ( info . platform === 'iOS' ) {
await runIOSTest ( device )
} else if ( info . platform === 'Android' ) {
await runAndroidTest ( device )
}
})
async function runIOSTest ( device ) {
const browser = await device . launch ()
// iOS-specific test logic
await browser . close ()
}
async function runAndroidTest ( device ) {
const browser = await device . launch ()
// Android-specific test logic
await browser . close ()
}
Browser management per device
Each device can host multiple browsers. Manage them effectively:
Launching browsers
// Launch a new browser on a specific device
const browser = await device . launch ()
// Or let the client pick a device
const browser = await client . launch ()
Listing browsers
// Get all browsers on a device
const browsers = await device . browsers ()
console . log ( ` ${ browsers . length } browsers running on device` )
Connecting to existing browsers
Browsers are persistent and can be reconnected using their handle:
// Save the browser handle
const browser = await device . launch ()
const handle = browser . handle
// Later, reconnect to the same browser
const sameBrowser = await device . connect ( handle )
// Or from the client level
const sameBrowser = await client . connect ( handle )
Cleaning up browsers
Always close browsers when done to free device resources:
// Close all browsers on a device
const browsers = await device . browsers ()
await Promise . all ( browsers . map ( b => b . close ()))
Device lifecycle
Terminating devices
When necessary, you can terminate a device connection:
// Terminate a specific device
await device . terminate ()
// Or from the client level
await client . terminate ( device . address )
Terminating a device closes all browsers and disconnects the device from the session. Only do this when you’re certain you’re done with the device.
Handling disconnections
Devices can disconnect unexpectedly (network issues, app closure, etc.). Handle disconnections gracefully:
const activeDevices = new Map ()
client . on ( 'worker-connected' , ( device ) => {
activeDevices . set ( device . address , device )
})
client . on ( 'worker-disconnected' , ( device ) => {
console . warn ( 'Device disconnected:' , device . address )
activeDevices . delete ( device . address )
// Implement reconnection logic or failover
handleDeviceFailure ( device . address )
})
async function handleDeviceFailure ( address ) {
// Retry logic, notification, or reassignment
}
Addressing devices
Devices have unique addresses for identification and targeting:
const devices = client . workers ()
devices . forEach ( device => {
console . log ( 'Address:' , device . address )
})
// Use a specific device by address
const targetDevice = devices . find ( d => d . address === 'device-123' )
if ( targetDevice ) {
const browser = await targetDevice . launch ()
}
Best practices
Always close browsers and pages when done to free device resources: try {
const browser = await device . launch ()
const page = await browser . newPage ()
// ... automation
} finally {
await page . close ()
await browser . close ()
}
Handle device variability
Different devices have different capabilities. Test your automation across various device types and OS versions. const info = await device . getDeviceInfo ()
if ( parseFloat ( info . platformVersion ) < 15 ) {
console . warn ( 'Old OS version, some features may not work' )
}
Keep track of device connection stability and browser success rates: const stats = {
connected: 0 ,
disconnected: 0 ,
failures: 0
}
client . on ( 'worker-connected' , () => stats . connected ++ )
client . on ( 'worker-disconnected' , () => stats . disconnected ++ )
Set appropriate timeouts when waiting for devices or operations: const timeout = 30000 // 30 seconds
const device = await Promise . race ([
client . worker (),
new Promise (( _ , reject ) =>
setTimeout (() => reject ( new Error ( 'Device connection timeout' )), timeout )
)
])
Next steps