appium-remote-debugger
Version:
Appium proxy for Remote Debugger protocol
134 lines (124 loc) • 4.27 kB
text/typescript
import {checkParams} from '../utils';
import B, {TimeoutError as BTimeoutError} from 'bluebird';
import {getAppIdKey, getPageIdKey} from './property-accessors';
import type {RemoteDebugger} from '../remote-debugger';
const SAFARI_BUNDLE_ID = 'com.apple.mobilesafari';
const GARBAGE_COLLECT_TIMEOUT_MS = 5000;
/**
* Launches Safari application on the device by sending a launch command
* to the remote debugger.
*/
export async function launchSafari(this: RemoteDebugger): Promise<void> {
await this.requireRpcClient().send('launchApplication', {
bundleId: SAFARI_BUNDLE_ID,
});
}
/**
* Starts recording the timeline by registering an event listener and
* sending the Timeline.start command to the remote debugger.
*
* @param fn - Event listener function that will be called when timeline events are recorded.
* @returns A promise that resolves when the timeline recording has started.
*/
export async function startTimeline(
this: RemoteDebugger,
fn: import('../types').EventListener,
): Promise<any> {
this.log.debug('Starting to record the timeline');
this.requireRpcClient().on('Timeline.eventRecorded', fn);
return await this.requireRpcClient().send('Timeline.start', {
appIdKey: getAppIdKey(this),
pageIdKey: getPageIdKey(this),
});
}
/**
* Stops recording the timeline by sending the Timeline.stop command
* to the remote debugger.
*/
export async function stopTimeline(this: RemoteDebugger): Promise<any> {
this.log.debug('Stopping to record the timeline');
await this.requireRpcClient().send('Timeline.stop', {
appIdKey: getAppIdKey(this),
pageIdKey: getPageIdKey(this),
});
}
/**
* Overrides the user agent string for the current page.
* Note: This may not work for mobile Safari.
*
* @param value - The user agent string to set.
* @returns A promise that resolves when the user agent has been overridden.
*/
export async function overrideUserAgent(this: RemoteDebugger, value: string): Promise<any> {
this.log.debug('Setting overrideUserAgent');
return await this.requireRpcClient().send('Page.overrideUserAgent', {
appIdKey: getAppIdKey(this),
pageIdKey: getPageIdKey(this),
value,
});
}
/**
* Checks whether JavaScript execution is currently blocked on the page
* by attempting to execute a simple JavaScript command with a timeout.
*
* @param timeoutMs - The maximum amount of milliseconds to wait for a JavaScript
* command response. Defaults to 1000ms.
* @returns A promise that resolves to true if JavaScript execution is blocked,
* false if it is not blocked.
*/
export async function isJavascriptExecutionBlocked(
this: RemoteDebugger,
timeoutMs: number = 1000,
): Promise<boolean> {
try {
await B.resolve(
this.requireRpcClient().send('Runtime.evaluate', {
expression: '1+1;',
returnByValue: true,
appIdKey: getAppIdKey(this),
pageIdKey: getPageIdKey(this),
}),
).timeout(timeoutMs);
return false;
} catch {
return true;
}
}
/**
* Triggers garbage collection on the page's JavaScript heap.
* This method will gracefully handle cases where garbage collection cannot
* be performed (e.g., when not connected to a page).
*
* @param timeoutMs - Maximum time in milliseconds to wait for garbage collection
* to complete. Defaults to GARBAGE_COLLECT_TIMEOUT_MS (5000ms).
*/
export async function garbageCollect(
this: RemoteDebugger,
timeoutMs: number = GARBAGE_COLLECT_TIMEOUT_MS,
): Promise<void> {
this.log.debug(`Garbage collecting with ${timeoutMs}ms timeout`);
try {
checkParams({
appIdKey: getAppIdKey(this),
pageIdKey: getPageIdKey(this),
});
} catch {
this.log.debug(`Unable to collect garbage at this time`);
return;
}
try {
await B.resolve(
this.requireRpcClient().send('Heap.gc', {
appIdKey: getAppIdKey(this),
pageIdKey: getPageIdKey(this),
}),
).timeout(timeoutMs);
this.log.debug(`Garbage collection successful`);
} catch (e: any) {
if (e instanceof BTimeoutError) {
this.log.debug(`Garbage collection timed out after ${timeoutMs}ms`);
} else {
this.log.debug(`Unable to collect garbage: ${e.message}`);
}
}
}