appium-xcuitest-driver
Version:
Appium driver for iOS using XCUITest for backend
263 lines • 11 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSim = createSim;
exports.getExistingSim = getExistingSim;
exports.shutdownSimulator = shutdownSimulator;
exports.runSimulatorReset = runSimulatorReset;
exports.installToSimulator = installToSimulator;
exports.shutdownOtherSimulators = shutdownOtherSimulators;
exports.setSafariPrefs = setSafariPrefs;
exports.setLocalizationPrefs = setLocalizationPrefs;
const appium_ios_simulator_1 = require("appium-ios-simulator");
const node_simctl_1 = require("node-simctl");
const appium_webdriveragent_1 = require("appium-webdriveragent");
const lodash_1 = __importDefault(require("lodash"));
const support_1 = require("appium/support");
const utils_1 = require("../utils");
const app_utils_1 = require("../app-utils");
const APPIUM_SIM_PREFIX = 'appiumTest';
/**
* Create a new simulator with `appiumTest-` prefix and return the object.
*
* @returns Simulator object associated with the udid passed in.
*/
async function createSim() {
const { simulatorDevicesSetPath: devicesSetPath, deviceName, platformVersion } = this.opts;
const platform = (0, utils_1.normalizePlatformName)(this.opts.platformName);
const simctl = new node_simctl_1.Simctl({ devicesSetPath });
if (!deviceName) {
let deviceNames = [];
try {
const devices = platformVersion
? await simctl.getDevices(platformVersion, platform)
: await simctl.getDevices(null, platform);
const nameMapper = (device) => device.name;
deviceNames = Array.isArray(devices)
? devices.map(nameMapper)
: lodash_1.default.flatMap(lodash_1.default.values(devices)).map(nameMapper);
}
catch { }
throw new Error(`'deviceName' must be provided in order to create a new Simulator for ${platform} platform. ` +
`Currently available device names: ${deviceNames}`);
}
if (!platformVersion) {
throw new Error(`'platformVersion' is required.`);
}
const simName = `${APPIUM_SIM_PREFIX}-${support_1.util.uuidV4().toUpperCase()}-${deviceName}`;
this.log.debug(`Creating a temporary Simulator device '${simName}'`);
const udid = await simctl.createDevice(simName, deviceName, platformVersion, { platform });
return await (0, appium_ios_simulator_1.getSimulator)(udid, {
platform,
checkExistence: false,
devicesSetPath,
// @ts-ignore This is ok
logger: this.log,
});
}
/**
* Get an existing simulator matching the provided capabilities.
*
* @returns The matched Simulator instance or `null` if no matching device is found.
*/
async function getExistingSim() {
const { platformVersion, deviceName, udid, simulatorDevicesSetPath: devicesSetPath, platformName, } = this.opts;
const platform = (0, utils_1.normalizePlatformName)(platformName);
const selectSim = async (dev) => await (0, appium_ios_simulator_1.getSimulator)(dev.udid, {
platform,
checkExistence: false,
devicesSetPath,
// @ts-ignore This is ok
logger: this.log,
});
const simctl = new node_simctl_1.Simctl({ devicesSetPath });
let devicesMap;
if (udid && lodash_1.default.toLower(udid) !== utils_1.UDID_AUTO) {
this.log.debug(`Looking for an existing Simulator with UDID '${udid}'`);
devicesMap = await simctl.getDevices(null, platform);
for (const device of lodash_1.default.flatMap(lodash_1.default.values(devicesMap))) {
if (device.udid === udid) {
return await selectSim(device);
}
}
return null;
}
if (!platformVersion) {
this.log.debug(`Provide 'platformVersion' capability if you prefer an existing Simulator to be selected`);
return null;
}
const devices = devicesMap?.[platformVersion] ?? (await simctl.getDevices(platformVersion, platform));
this.log.debug(`Looking for an existing Simulator with platformName: ${platform}, ` +
`platformVersion: ${platformVersion}, deviceName: ${deviceName}`);
for (const device of devices) {
if ((deviceName && device.name === deviceName) || !deviceName) {
if (!deviceName) {
this.log.debug(`The 'deviceName' capability value is empty. ` +
`Selecting the first matching device '${device.name}' having the ` +
`'platformVersion' set to ${platformVersion}`);
}
return await selectSim(device);
}
}
return null;
}
/**
* Shutdown simulator
*/
async function shutdownSimulator() {
const device = this.device;
// stop XCTest processes if running to avoid unexpected side effects
await (0, appium_webdriveragent_1.resetTestProcesses)(device.udid, true);
await device.shutdown();
}
/**
* Run simulator reset
* @param enforceSimulatorShutdown Whether to enforce simulator shutdown
*/
async function runSimulatorReset(enforceSimulatorShutdown = false) {
const { noReset, fullReset, keychainsExcludePatterns, keepKeyChains, bundleId, app, browserName, } = this.opts;
if (noReset && !fullReset) {
// noReset === true && fullReset === false
this.log.debug('Reset: noReset is on. Leaving simulator as is');
return;
}
const device = this.device;
if (!this.device) {
this.log.debug('Reset: no device available. Skipping');
return;
}
if (fullReset) {
this.log.debug('Reset: fullReset is on. Cleaning simulator');
await shutdownSimulator.bind(this)();
const isKeychainsBackupSuccessful = (keychainsExcludePatterns || keepKeyChains) && (await device.backupKeychains());
await device.clean();
if (isKeychainsBackupSuccessful) {
await device.restoreKeychains(keychainsExcludePatterns?.split(',')?.map(lodash_1.default.trim) || []);
this.log.info(`Successfully restored keychains after full reset`);
}
else if (keychainsExcludePatterns || keepKeyChains) {
this.log.warn('Cannot restore keychains after full reset, because ' +
'the backup operation did not succeed');
}
}
else if (bundleId) {
// fastReset or noReset
// Terminate the app under test if it is still running on Simulator
try {
await device.terminateApp(bundleId);
}
catch {
this.log.warn(`Reset: failed to terminate Simulator application with id "${bundleId}"`);
}
if (app) {
this.log.info('Not scrubbing third party app in anticipation of uninstall');
}
else {
const isSafari = lodash_1.default.toLower(browserName) === 'safari';
try {
if (isSafari) {
await device.scrubSafari(true);
}
else {
await device.scrubApp(bundleId);
}
}
catch (err) {
this.log.debug(err.stack);
this.log.warn(err.message);
this.log.warn(`Reset: could not scrub ${isSafari ? 'Safari browser' : 'application with id "' + this.opts.bundleId + '"'}. ` + `Leaving as is.`);
}
}
if (enforceSimulatorShutdown && (await device.isRunning())) {
await shutdownSimulator.bind(this)();
}
}
}
/**
* Install app to simulator
* @param app The app to the path
* @param bundleId The bundle id to ensure it is already installed and uninstall it
* @param opts Install options
*/
async function installToSimulator(app, bundleId, opts = {}) {
if (!app) {
this.log.debug('No app path is given. Nothing to install.');
return;
}
const { skipUninstall, newSimulator = false } = opts;
const device = this.device;
if (!skipUninstall && !newSimulator && bundleId && (await device.isAppInstalled(bundleId))) {
this.log.debug(`Reset requested. Removing app with id '${bundleId}' from the device`);
await device.removeApp(bundleId);
}
this.log.debug(`Installing '${app}' on Simulator with UUID '${device.udid}'...`);
const timer = new support_1.timing.Timer().start();
await device.installApp(app);
this.log.info(`The app has been successfully installed in ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
}
/**
* Shutdown other simulators
*/
async function shutdownOtherSimulators() {
const device = this.device;
const simctl = new node_simctl_1.Simctl({
devicesSetPath: device.devicesSetPath,
});
const allDevices = lodash_1.default.flatMap(lodash_1.default.values(await simctl.getDevices()));
const otherBootedDevices = allDevices
.filter(({ udid, state }) => udid !== device.udid && state === 'Booted');
if (lodash_1.default.isEmpty(otherBootedDevices)) {
this.log.info('No other running simulators have been detected');
return;
}
this.log.info(`Detected ${support_1.util.pluralize('other running Simulator', otherBootedDevices.length, true)}. Shutting them down...`);
for (const { udid } of otherBootedDevices) {
// It is necessary to stop the corresponding xcodebuild process before killing
// the simulator, otherwise it will be automatically restarted
await (0, appium_webdriveragent_1.resetTestProcesses)(udid, true);
simctl.udid = udid;
await simctl.shutdownDevice();
}
}
/**
* Configures Safari options based on the given session capabilities
*
* @returns true if any preferences have been updated
*/
async function setSafariPrefs() {
const prefs = (0, app_utils_1.buildSafariPreferences)(this.opts);
if (lodash_1.default.isEmpty(prefs)) {
return false;
}
this.log.debug(`About to update Safari preferences: ${JSON.stringify(prefs)}`);
await this.device.updateSafariSettings(prefs);
return true;
}
/**
* Changes Simulator localization preferences
*
* @returns True if preferences were changed
*/
async function setLocalizationPrefs() {
const { language, locale, calendarFormat, skipSyncUiDialogTranslation } = this.opts;
const l10nConfig = {};
if (language) {
l10nConfig.language = { name: language, skipSyncUiDialogTranslation };
}
if (locale) {
l10nConfig.locale = { name: locale };
if (calendarFormat) {
l10nConfig.locale.calendar = calendarFormat;
}
}
if (lodash_1.default.isEmpty(l10nConfig)) {
return false;
}
this.log.debug(`About to update localization preferences: ${JSON.stringify(l10nConfig)}`);
await this.device.configureLocalization(l10nConfig);
return true;
}
//#endregion
//# sourceMappingURL=simulator-management.js.map