UNPKG

appium-xcuitest-driver

Version:

Appium driver for iOS using XCUITest for backend

263 lines 11 kB
"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