appium-xcuitest-driver
Version:
Appium driver for iOS using XCUITest for backend
94 lines (86 loc) • 3.22 kB
text/typescript
import type {AppiumLogger} from '@appium/types';
import type {EventEmitter} from 'node:events';
import type {DVTServiceWithConnection} from 'appium-ios-remotexpc';
import {makeNetworkMonitorEvent} from '../commands/bidi/models';
import {BIDI_EVENT_NAME} from '../commands/bidi/constants';
import {getRemoteXPCServices} from './remotexpc-utils';
/**
* Active DVT NetworkMonitor session: streams instrument events to the driver BiDi event bus.
*/
export class NetworkMonitorSession {
private dvt: DVTServiceWithConnection | null = null;
private runPromise: Promise<void> | null = null;
private stopped = false;
/**
* @param log - Logger for this session (typically the driver logger).
* @param udid - Target device UDID for `startDVTService`.
*/
constructor(
private readonly log: AppiumLogger,
private readonly udid: string,
) {}
/**
* @returns `true` only while the consume loop may still be receiving events (`this.dvt` is set).
* After normal completion, error, or {@link interrupt}, this becomes `false`.
*/
isRunning(): boolean {
return this.dvt !== null;
}
/**
* Opens `startDVTService` and begins iterating `networkMonitor.events()`, emitting each payload on `eventEmitter`
* using `BIDI_EVENT_NAME` and `makeNetworkMonitorEvent` (BiDi `appium:xcuitest.networkMonitor`).
*
* @param eventEmitter - Typically the session driver's `eventEmitter` (WebDriver BiDi bus).
*/
async start(eventEmitter: EventEmitter): Promise<void> {
this.stopped = false;
const Services = await getRemoteXPCServices();
const dvt = await Services.startDVTService(this.udid);
this.dvt = dvt;
this.runPromise = this.consumeEvents(dvt, eventEmitter);
}
/**
* Stops monitoring: closes Remote XPC (same pattern as other `startDVTService` call sites in this driver).
* Safe to call more than once.
*/
async interrupt(): Promise<void> {
if (this.stopped) {
return;
}
this.stopped = true;
if (this.dvt) {
const dvt = this.dvt;
this.dvt = null;
try {
await dvt.networkMonitor.stop();
} catch (err: any) {
this.log.debug(`Error stopping network monitor: ${err?.message ?? err}`);
}
// Same as other `startDVTService` paths in this driver (condition inducer, terminateAppRemoteXPC):
// close RemoteXPC only. Remotexpc's DVT integration tests often call `dvtService.close()` first for
// explicit channel teardown; we can add that if shutdown issues show up in the field.
try {
await dvt.remoteXPC.close();
} catch (err: any) {
this.log.debug(`Error closing RemoteXPC for network monitor: ${err?.message ?? err}`);
}
}
if (this.runPromise) {
this.runPromise = null;
}
}
private async consumeEvents(
dvt: DVTServiceWithConnection,
eventEmitter: EventEmitter,
): Promise<void> {
try {
for await (const event of dvt.networkMonitor.events()) {
eventEmitter.emit(BIDI_EVENT_NAME, makeNetworkMonitorEvent(event as object));
}
} catch (err: any) {
if (!this.stopped) {
this.log.error('Network monitor stream ended unexpectedly', err);
}
}
}
}