UNPKG

@expo/xdl

Version:
427 lines (333 loc) 12.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isSimulatorRunningAsync = isSimulatorRunningAsync; exports.getDefaultSimulatorDeviceUDIDAsync = getDefaultSimulatorDeviceUDIDAsync; exports.getContainerPathAsync = getContainerPathAsync; exports.openURLAsync = openURLAsync; exports.openBundleIdAsync = openBundleIdAsync; exports.bootAsync = bootAsync; exports.getBootedSimulatorsAsync = getBootedSimulatorsAsync; exports.isSimulatorBootedAsync = isSimulatorBootedAsync; exports.installAsync = installAsync; exports.uninstallAsync = uninstallAsync; exports.listAsync = listAsync; exports.listDevicesAsync = listDevicesAsync; exports.shutdownAsync = shutdownAsync; exports.updatePermissionsAsync = updatePermissionsAsync; exports.setAppearanceAsync = setAppearanceAsync; exports.eraseAsync = eraseAsync; exports.eraseAllAsync = eraseAllAsync; exports.addMediaAsync = addMediaAsync; exports.captureScreenAsync = captureScreenAsync; exports.deleteUnavailableAsync = deleteUnavailableAsync; exports.simctlAsync = simctlAsync; exports.isSimulatorAppRunningAsync = isSimulatorAppRunningAsync; exports.openSimulatorAppAsync = openSimulatorAppAsync; exports.killAllAsync = killAllAsync; exports.isLicenseOutOfDate = isLicenseOutOfDate; exports.isXcrunInstalledAsync = isXcrunInstalledAsync; exports.xcrunAsync = xcrunAsync; exports.xcrunWithLogging = xcrunWithLogging; function osascript() { const data = _interopRequireWildcard(require("@expo/osascript")); osascript = function () { return data; }; return data; } function _spawnAsync() { const data = _interopRequireDefault(require("@expo/spawn-async")); _spawnAsync = function () { return data; }; return data; } function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _Logger() { const data = _interopRequireDefault(require("./Logger")); _Logger = function () { return data; }; return data; } function _XDLError() { const data = _interopRequireDefault(require("./XDLError")); _XDLError = function () { return data; }; return data; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } async function isSimulatorRunningAsync() { try { const zeroMeansNo = (await osascript().execAsync('tell app "System Events" to count processes whose name is "Simulator"')).trim(); if (zeroMeansNo === '0') { return false; } } catch (_unused) { return false; } return true; } async function getDefaultSimulatorDeviceUDIDAsync() { try { const { stdout: defaultDeviceUDID } = await (0, _spawnAsync().default)('defaults', ['read', 'com.apple.iphonesimulator', 'CurrentDeviceUDID']); return defaultDeviceUDID.trim(); } catch (e) { return null; } } /** * Returns the local path for the installed tar.app. Returns null when the app isn't installed. * * @param udid * @param bundleIdentifier */ async function getContainerPathAsync(udid, bundleIdentifier) { try { const { stdout } = await xcrunAsync(['simctl', 'get_app_container', deviceUDIDOrBooted(udid), bundleIdentifier]); return stdout.trim(); } catch (error) { var _error$stderr; if ((_error$stderr = error.stderr) === null || _error$stderr === void 0 ? void 0 : _error$stderr.match(/No such file or directory/)) { return null; } throw error; } } async function openURLAsync(options) { return simctlAsync(['openurl', deviceUDIDOrBooted(options.udid), options.url]); } async function openBundleIdAsync(options) { return simctlAsync(['launch', deviceUDIDOrBooted(options.udid), options.bundleIdentifier]); } // This will only boot in headless mode if the Simulator app is not running. async function bootAsync({ udid }) { try { // Skip logging since this is likely to fail. await xcrunAsync(['simctl', 'boot', udid]); } catch (error) { var _error$stderr2; if (!((_error$stderr2 = error.stderr) === null || _error$stderr2 === void 0 ? void 0 : _error$stderr2.match(/Unable to boot device in current state: Booted/))) { throw error; } } return await isSimulatorBootedAsync({ udid }); } async function getBootedSimulatorsAsync() { const simulatorDeviceInfo = await listAsync('devices'); return Object.values(simulatorDeviceInfo.devices).reduce((prev, runtime) => { return prev.concat(runtime.filter(device => device.state === 'Booted')); }, []); } async function isSimulatorBootedAsync({ udid }) { // Simulators can be booted even if the app isn't running :( const devices = await getBootedSimulatorsAsync(); if (udid) { var _devices$find; return (_devices$find = devices.find(bootedDevice => bootedDevice.udid === udid)) !== null && _devices$find !== void 0 ? _devices$find : null; } else { var _devices$; return (_devices$ = devices[0]) !== null && _devices$ !== void 0 ? _devices$ : null; } } async function installAsync(options) { return simctlAsync(['install', deviceUDIDOrBooted(options.udid), options.dir]); } async function uninstallAsync(options) { return simctlAsync(['uninstall', deviceUDIDOrBooted(options.udid), options.bundleIdentifier]); } function parseSimControlJSONResults(input) { try { return JSON.parse(input); } catch (error) { // Nov 15, 2020: Observed this can happen when opening the simulator and the simulator prompts the user to update the XC command line tools. // Unexpected token I in JSON at position 0 if (error.message.match('Unexpected token')) { _Logger().default.global.error(`Apple's simctl returned malformed JSON:\n${input}`); } throw error; } } // TODO: Compare with // const results = await SimControl.xcrunAsync(['instruments', '-s']); async function listAsync(type, query) { const result = await simctlAsync(['list', type, '--json', query]); const info = parseSimControlJSONResults(result.stdout); for (const runtime of Object.keys(info.devices)) { // Given a string like 'com.apple.CoreSimulator.SimRuntime.tvOS-13-4' const runtimeSuffix = runtime.split('com.apple.CoreSimulator.SimRuntime.').pop(); // Create an array [tvOS, 13, 4] const [osType, ...osVersionComponents] = runtimeSuffix.split('-'); // Join the end components [13, 4] -> '13.4' const osVersion = osVersionComponents.join('.'); const sims = info.devices[runtime]; for (const device of sims) { device.runtime = runtime; device.osVersion = osVersion; device.windowName = `${device.name} (${osVersion})`; device.osType = osType; } } return info; } /** * Get a list of all connected devices. */ async function listDevicesAsync() { const { output } = await xcrunAsync(['xctrace', 'list', 'devices']); const text = output.join(''); const devices = []; if (!text.includes('== Simulators ==')) { return []; } const lines = text.split('\n'); for (const line of lines) { if (line === '== Simulators ==') { break; } const device = line.match(/(.*?) (\(([0-9.]+)\) )?\(([0-9A-F-]+)\)/i); if (device) { const [, name,, osVersion, udid] = device; const metadata = { name, udid, osVersion: osVersion !== null && osVersion !== void 0 ? osVersion : '??', deviceType: osVersion ? 'device' : 'catalyst' }; devices.push(metadata); } } return devices; } async function shutdownAsync(udid) { try { return simctlAsync(['shutdown', deviceUDIDOrBooted(udid)]); } catch (e) { var _e$message; if (!((_e$message = e.message) === null || _e$message === void 0 ? void 0 : _e$message.includes('No devices are booted.'))) { throw e; } } return null; } // Some permission changes will terminate the application if running async function updatePermissionsAsync(udid, action, permission, bundleIdentifier) { return simctlAsync(['privacy', deviceUDIDOrBooted(udid), action, permission, bundleIdentifier]); } async function setAppearanceAsync(udid, theme) { return simctlAsync(['ui', deviceUDIDOrBooted(udid), theme]); } // Cannot be invoked unless the simulator is `shutdown` async function eraseAsync(udid) { return simctlAsync(['erase', deviceUDIDOrBooted(udid)]); } async function eraseAllAsync() { return simctlAsync(['erase', 'all']); } // Add photos and videos to the simulator's gallery async function addMediaAsync(udid, mediaPath) { return simctlAsync(['addmedia', deviceUDIDOrBooted(udid), mediaPath]); } async function captureScreenAsync(udid, captureType, outputFilePath) { return simctlAsync(['io', deviceUDIDOrBooted(udid), captureType, `—type=${_path().default.extname(outputFilePath)}`, outputFilePath]); } // Clear all unused simulators async function deleteUnavailableAsync() { return simctlAsync(['delete', 'unavailable']); } async function simctlAsync([command, ...args], options) { return xcrunWithLogging( // @ts-ignore ['simctl', command, ...args.filter(Boolean)], options); } function deviceUDIDOrBooted(udid) { return udid ? udid : 'booted'; } /** * I think the app can be open while no simulators are booted. */ async function isSimulatorAppRunningAsync() { try { const zeroMeansNo = (await osascript().execAsync('tell app "System Events" to count processes whose name is "Simulator"')).trim(); if (zeroMeansNo === '0') { return false; } } catch (error) { if (error.message.includes('Application isn’t running')) { return false; } throw error; } return true; } async function openSimulatorAppAsync({ udid }) { const args = ['-a', 'Simulator']; if (udid) { // This has no effect if the app is already running. args.push('--args', '-CurrentDeviceUDID', udid); } return await (0, _spawnAsync().default)('open', args); } async function killAllAsync() { return await (0, _spawnAsync().default)('killAll', ['Simulator']); } function isLicenseOutOfDate(text) { if (!text) { return false; } const lower = text.toLowerCase(); return lower.includes('xcode') && lower.includes('license'); } async function isXcrunInstalledAsync() { try { await (0, _spawnAsync().default)('xcrun', ['--version']); return true; } catch (error) { return false; } } async function xcrunAsync(args, options) { try { return await (0, _spawnAsync().default)('xcrun', args, options); } catch (e) { var _e$stderr; if (isLicenseOutOfDate(e.stdout) || isLicenseOutOfDate(e.stderr)) { throw new (_XDLError().default)('XCODE_LICENSE_NOT_ACCEPTED', 'Xcode license is not accepted. Please run `sudo xcodebuild -license`.'); } else if ((_e$stderr = e.stderr) === null || _e$stderr === void 0 ? void 0 : _e$stderr.includes('not a developer tool or in PATH')) { throw new (_XDLError().default)('SIMCTL_NOT_AVAILABLE', `You may need to run ${_chalk().default.bold('sudo xcode-select -s /Applications/Xcode.app')} and try again.`); } throw e; } } async function xcrunWithLogging(args, options) { try { return await xcrunAsync(args, options); } catch (e) { _Logger().default.global.error(`Error running \`xcrun ${args.join(' ')}\`: ${e.stderr || e.message}`); throw e; } } //# sourceMappingURL=__sourcemaps__/SimControl.js.map