UNPKG

appium-xcuitest-driver

Version:

Appium driver for iOS using XCUITest for backend

85 lines (76 loc) 3.06 kB
import _ from 'lodash'; import {Devicectl} from 'node-devicectl'; import {utilities} from 'appium-ios-device'; import {log} from '../logger'; import {isIos18OrNewer} from '../utils'; import type {XCUITestDriverOpts} from '../driver'; import {tryGetRemoteXPCServices} from './remotexpc-utils'; import type {RemoteXPCServices} from './remotexpc-utils'; export class ConnectedDevicesClient { private constructor(private readonly services: RemoteXPCServices | null) {} /** * Create a connected devices client instance. * When opts indicate iOS/tvOS 18+, loads and stores the remotexpc Services * instance for tunnel registry listing; otherwise uses legacy listing only. */ static async create(opts: XCUITestDriverOpts): Promise<ConnectedDevicesClient> { let services: RemoteXPCServices | null = null; if (isIos18OrNewer(opts)) { services = await tryGetRemoteXPCServices(); if (!services) { log.warn('Could not load appium-ios-remotexpc, using legacy devices listing instead'); } } return new ConnectedDevicesClient(services); } /** * Returns the list of connected real device UDIDs. * Only considers tunnel registry UDIDs when remotexpc is loaded and tunnels are running; * otherwise returns the legacy list only. */ async getConnectedDevices(): Promise<string[]> { const [tunnelSettled, legacySettled] = await Promise.allSettled([ this.listUdidsFromTunnelsRegistry(), this.listLegacyUdids(), ]); // Tunnels succeeded → short-circuit: return tunnel UDIDs only (legacy not used) if (tunnelSettled.status === 'fulfilled') { return tunnelSettled.value; } // Tunnels rejected (only other status after fulfilled) → use legacy; throw if legacy failed const err = tunnelSettled.reason; log.info( 'Tunnel registry unavailable; using legacy devices listing instead. ' + `Original error: ${err instanceof Error ? err.message : String(err)}`, ); if (legacySettled.status === 'rejected') { throw legacySettled.reason instanceof Error ? legacySettled.reason : new Error(String(legacySettled.reason)); } return legacySettled.value; } private isPreferDevicectlEnabled(): boolean { return ['yes', 'true', '1'].includes( _.toLower(process.env.APPIUM_XCUITEST_PREFER_DEVICECTL ?? ''), ); } /** * Fetches UDIDs from the tunnel registry. * @throws When remotexpc is not loaded or when the tunnel registry is unreachable. */ private async listUdidsFromTunnelsRegistry(): Promise<string[]> { if (!this.services) { throw new Error('appium-ios-remotexpc module cannot be loaded'); } return await this.services.getAvailableDevices(); } private async listLegacyUdids(): Promise<string[]> { if (this.isPreferDevicectlEnabled()) { return (await new Devicectl('').listDevices()) .map(({hardwareProperties}) => hardwareProperties?.udid) .filter(Boolean); } return await utilities.getConnectedDevices(); } }