Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
systemCallMethods.waitForEmulatorReady = async function waitForEmulatorReady (timeoutMs = 20000) {
try {
await waitForCondition(async () => {
try {
if (!(await this.shell(['getprop', 'init.svc.bootanim'])).includes('stopped')) {
return false;
}
// Sometimes the package manager service might still being initialized
// on slow systems even after emulator booting is completed.
// The usual output of `pm get-install-location` command looks like `0[auto]`
return /\d+\[\w+\]/.test(await this.shell(['pm', 'get-install-location']));
} catch (err) {
log.debug(`Waiting for emulator startup. Intermediate error: ${err.message}`);
return false;
}
}, {
waitMs: timeoutMs,
intervalMs: 3000,
});
async verifyServicesAvailability () {
log.debug(`Waiting up to ${SERVICES_LAUNCH_TIMEOUT}ms for services to be available`);
let isPmServiceAvailable = false;
let pmOutput = '';
let pmError = null;
try {
await waitForCondition(async () => {
if (!isPmServiceAvailable) {
pmError = null;
pmOutput = '';
try {
pmOutput = await this.adb.shell(['pm', 'list', 'instrumentation']);
} catch (e) {
pmError = e;
}
if (pmOutput.includes('Could not access the Package Manager')) {
pmError = new Error(`Problem running Package Manager: ${pmOutput}`);
pmOutput = ''; // remove output, so it is not printed below
} else if (pmOutput.includes(INSTRUMENTATION_TARGET)) {
pmOutput = ''; // remove output, so it is not printed below
log.debug(`Instrumentation target '${INSTRUMENTATION_TARGET}' is available`);
// eslint-disable-next-line require-atomic-updates
isPmServiceAvailable = true;
commands.hideKeyboard = async function hideKeyboard () {
let {isKeyboardShown, canCloseKeyboard} = await this.adb.isSoftKeyboardPresent();
if (!isKeyboardShown) {
log.info('Keyboard has no UI; no closing necessary');
return;
}
// Try ESC then BACK if the first one fails
for (const keyCode of [111, 4]) {
if (canCloseKeyboard) {
await this.adb.keyevent(keyCode);
}
try {
return await waitForCondition(async () => {
({isKeyboardShown} = await this.adb.isSoftKeyboardPresent());
return !isKeyboardShown;
}, {waitMs: 1000, intervalMs: 500});
} catch (ign) {}
}
throw new Error(`The software keyboard cannot be closed`);
};
async waitForTarget (appIdKey, pageIdKey) {
if (!this.needsTarget()) {
return;
}
if (this.getTarget(appIdKey, pageIdKey)) {
return;
}
// otherwise waiting is necessary to see what the target is
try {
await waitForCondition(() => !_.isEmpty(this.getTarget(appIdKey, pageIdKey)), {
waitMs: WAIT_FOR_TARGET_TIMEOUT,
intervalMs: WAIT_FOR_TARGET_INTERVAL,
error: 'No targets found, unable to communicate with device',
});
} catch (err) {
if (!err.message.includes('Condition unmet')) {
throw err;
}
throw new Error('No targets found, unable to communicate with device');
}
}
let stopCmd = `${launchctlCmd} stop`;
await exec('bash', ['-c', stopCmd]);
} catch (err) {
log.warn(`Could not stop simulator daemons: ${err.message}`);
log.debug('Carrying on anyway!');
}
try {
let removeCmd = `${launchctlCmd} remove`;
await exec('bash', ['-c', removeCmd]);
} catch (err) {
log.warn(`Could not remove simulator daemons: ${err.message}`);
log.debug('Carrying on anyway!');
}
try {
// Waits 10 sec for the simulator launchd services to stop.
await waitForCondition(async () => {
let {stdout} = await exec('bash', ['-c',
`ps -e | grep ${this.udid} | grep launchd_sim | grep -v bash | grep -v grep | awk {'print$1'}`]);
return stdout.trim().length === 0;
}, {waitMs: 10000, intervalMs: 500});
} catch (err) {
log.warn(`Could not end simulator daemon for ${this.udid}: ${err.message}`);
log.debug('Carrying on anyway!');
}
}
}
try {
wasRoot = await this.isRoot();
} catch (ign) {}
if (wasRoot) {
throw e;
}
log.info(`Cannot kill PID ${pid} due to insufficient permissions. Retrying as root`);
let {isSuccessful} = await this.root();
becameRoot = isSuccessful;
await this.shell(['kill', '-0', pid]);
}
const timeoutMs = 1000;
let stdout;
try {
await waitForCondition(async () => {
try {
stdout = await this.shell(['kill', pid]);
return false;
} catch (e) {
// kill returns non-zero code if the process is already killed
return true;
}
}, {waitMs: timeoutMs, intervalMs: 300});
} catch (err) {
log.warn(`Cannot kill process ${pid} in ${timeoutMs} ms. Trying to force kill...`);
stdout = await this.shell(['kill', '-9', pid]);
}
return stdout;
} finally {
if (becameRoot) {
await this.unroot();
const certBuffer = Buffer.from(content, 'base64');
const cn = commonName || await extractCommonName(certBuffer);
const mobileConfig = toMobileConfig(certBuffer, cn);
try {
await plist.updatePlistFile(configPath, mobileConfig, false, false);
} catch (err) {
throw new Error(`Cannot store the generated config as '${configPath}'. ` +
`Original error: ${err.message}`);
}
try {
const host = os.hostname();
const certUrl = `http://${host}:${tmpPort}/${configName}`;
await tmpServer.listen(tmpPort);
try {
await waitForCondition(async () => {
try {
return (await checkPortStatus(tmpPort, host)) === 'open';
} catch (ign) {
return false;
}
}, {
waitMs: TMPSERVER_STARTUP_TIMEOUT,
intervalMs: 300,
});
log.debug(`The temporary web server is running at http://${host}:${tmpPort}`);
} catch (e) {
throw new Error(`The temporary web server cannot be started at http://${host}:${tmpPort}.`);
}
if (this.isRealDevice()) {
try {
await this.proxyCommand('/url', 'POST', {url: certUrl});
recordingProperties.currentTimeLimit = timeLimitInt - currentDuration;
const chunkDuration = recordingProperties.currentTimeLimit < MAX_RECORDING_TIME_SEC
? recordingProperties.currentTimeLimit
: MAX_RECORDING_TIME_SEC;
log.debug(`Starting the next ${chunkDuration}s-chunk ` +
`of screen recording in order to achieve ${timeLimitInt}s total duration`);
scheduleScreenRecord(adb, recordingProperties)
.catch((e) => {
log.error(e.stack);
recordingProperties.stopped = true;
});
});
await recordingProc.start(0);
try {
await waitForCondition(async () => await adb.fileExists(pathOnDevice),
{waitMs: RETRY_TIMEOUT, intervalMs: RETRY_PAUSE});
} catch (e) {
throw new Error(`The expected screen record file '${pathOnDevice}' does not exist after ${RETRY_TIMEOUT}ms. ` +
`Is ${SCREENRECORD_BINARY} utility available and operational on the device under test?`);
}
recordingProperties.records.push(pathOnDevice);
recordingProperties.recordingProcess = recordingProc;
}
} else {
logger.debug('Waiting for app source to contain elements');
condFn = async () => {
try {
let source = await this.getSourceForElementForXML();
source = JSON.parse(source || '{}');
let appEls = (source.UIAApplication || {})['>'];
return appEls && appEls.length > 0 && !IosDriver.isSpringBoard(source.UIAApplication);
} catch (e) {
logger.warn(`Couldn't extract app element from source, error was: ${e}`);
return false;
}
};
}
try {
await waitForCondition(condFn, {logger, waitMs: 10000, intervalMs: 500});
} catch (err) {
if (err.message && err.message.match(/Condition unmet/)) {
logger.warn('Initial spin timed out, continuing but the app might not be ready.');
logger.debug(`Initial spin error was: ${err}`);
} else {
throw err;
}
}
}
commands.terminateApp = async function terminateApp (appId, options = {}) {
log.info(`Terminating '${appId}'`);
if (!(await this.adb.processExists(appId))) {
log.info(`The app '${appId}' is not running`);
return false;
}
await this.adb.forceStop(appId);
const timeout = util.hasValue(options.timeout) && !isNaN(options.timeout) ? parseInt(options.timeout, 10) : 500;
try {
await waitForCondition(async () => await this.queryAppState(appId) <= APP_STATE_NOT_RUNNING,
{waitMs: timeout, intervalMs: 100});
} catch (e) {
log.errorAndThrow(`'${appId}' is still running after ${timeout}ms timeout`);
}
log.info(`'${appId}' has been successfully terminated`);
return true;
};