UNPKG

node-simctl

Version:

Wrapper around Apple's simctl binary

119 lines (102 loc) 4.04 kB
import _ from 'lodash'; import log, { LOG_PREFIX } from '../logger'; import { retryInterval } from 'asyncbox'; import { SIM_RUNTIME_NAME, normalizeVersion } from '../helpers'; const SIM_RUNTIME_NAME_SUFFIX_IOS = 'iOS'; const DEFAULT_CREATE_SIMULATOR_TIMEOUT = 10000; const commands = {}; /** * @typedef {Object} SimCreationOpts * @property {string} [platform='iOS'] - Platform name in order to specify runtime such as 'iOS', 'tvOS', 'watchOS' * @property {number} [timeout=10000] - The maximum number of milliseconds to wait * unit device creation is completed. */ /** * Create Simulator device with given name for the particular * platform type and version. * * @this {import('../simctl').Simctl} * @param {string} name - The device name to be created. * @param {string} deviceTypeId - Device type, for example 'iPhone 6'. * @param {string} platformVersion - Platform version, for example '10.3'. * @param {SimCreationOpts} [opts={}] - Simulator options for creating devices. * @return {Promise<string>} The UDID of the newly created device. * @throws {Error} If the corresponding simctl subcommand command * returns non-zero return code. */ commands.createDevice = async function createDevice (name, deviceTypeId, platformVersion, opts = {}) { const { platform = SIM_RUNTIME_NAME_SUFFIX_IOS, timeout = DEFAULT_CREATE_SIMULATOR_TIMEOUT } = opts; let runtimeIds = []; // Try getting runtimeId using JSON flag try { runtimeIds.push(await this.getRuntimeForPlatformVersionViaJson(platformVersion, platform)); } catch {} if (_.isEmpty(runtimeIds)) { // at first make sure that the runtime id is the right one // in some versions of Xcode it will be a patch version let runtimeId; try { runtimeId = await this.getRuntimeForPlatformVersion(platformVersion, platform); } catch { log.warn(`Unable to find runtime for iOS '${platformVersion}'. Continuing`); runtimeId = platformVersion; } // get the possible runtimes, which will be iterated over // start with major-minor version let potentialRuntimeIds = [normalizeVersion(runtimeId)]; if (runtimeId.split('.').length === 3) { // add patch version if it exists potentialRuntimeIds.push(runtimeId); } // add modified versions, since modern Xcodes use this, then the bare // versions, to accomodate older Xcodes runtimeIds.push( ...(potentialRuntimeIds.map((id) => `${SIM_RUNTIME_NAME}${platform}-${id.replace(/\./g, '-')}`)), ...potentialRuntimeIds ); } // go through the runtime ids and try to create a simulator with each let udid; for (const runtimeId of runtimeIds) { log.debug(LOG_PREFIX, `Creating simulator with name '${name}', device type id '${deviceTypeId}' and runtime id '${runtimeId}'`); try { const {stdout} = await this.exec('create', { args: [name, deviceTypeId, runtimeId] }); udid = stdout.trim(); break; } catch { // the error gets logged in `simExec` } } if (!udid) { throw new Error(`Could not create simulator with name '${name}', device ` + `type id '${deviceTypeId}', with runtime ids ` + `${runtimeIds.map((id) => `'${id}'`).join(', ')}`); } // make sure that it gets out of the "Creating" state const retries = parseInt(`${timeout / 1000}`, 10); await retryInterval(retries, 1000, async () => { const devices = _.values(await this.getDevices()); for (const deviceArr of _.values(devices)) { for (const device of deviceArr) { if (device.udid === udid) { if (device.state === 'Creating') { // need to retry throw new Error(`Device with udid '${udid}' still being created`); } else { // stop looking, we're done return; } } } } throw new Error(`Device with udid '${udid}' not yet created`); }); return udid; }; export default commands;