You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

409 lines
13 KiB

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.addMediaAsync = addMediaAsync;
exports.bootAsync = bootAsync;
exports.captureScreenAsync = captureScreenAsync;
exports.deleteUnavailableAsync = deleteUnavailableAsync;
exports.eraseAllAsync = eraseAllAsync;
exports.eraseAsync = eraseAsync;
exports.getContainerPathAsync = getContainerPathAsync;
exports.getDefaultSimulatorDeviceUDIDAsync = getDefaultSimulatorDeviceUDIDAsync;
exports.installAsync = installAsync;
exports.isLicenseOutOfDate = isLicenseOutOfDate;
exports.isXcrunInstalledAsync = isXcrunInstalledAsync;
exports.listAsync = listAsync;
exports.listDevicesAsync = listDevicesAsync;
exports.listSimulatorDevicesAsync = listSimulatorDevicesAsync;
exports.openBundleIdAsync = openBundleIdAsync;
exports.openURLAsync = openURLAsync;
exports.parseXcrunError = parseXcrunError;
exports.runBootAsync = runBootAsync;
exports.setAppearanceAsync = setAppearanceAsync;
exports.shutdownAsync = shutdownAsync;
exports.simctlAsync = simctlAsync;
exports.uninstallAsync = uninstallAsync;
exports.updatePermissionsAsync = updatePermissionsAsync;
exports.waitForDeviceToBootAsync = waitForDeviceToBootAsync;
exports.xcrunAsync = xcrunAsync;
exports.xcrunWithLogging = xcrunWithLogging;
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 _child_process() {
const data = require("child_process");
_child_process = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _waitForActionAsync() {
const data = require("./apple/utils/waitForActionAsync");
_waitForActionAsync = function () {
return data;
};
return data;
}
function _internal() {
const data = require("./internal");
_internal = function () {
return data;
};
return data;
}
function _profileMethod() {
const data = require("./utils/profileMethod");
_profileMethod = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
async function getDefaultSimulatorDeviceUDIDAsync() {
try {
const {
stdout: defaultDeviceUDID
} = await (0, _spawnAsync().default)('defaults', ['read', 'com.apple.iphonesimulator', 'CurrentDeviceUDID']);
return defaultDeviceUDID.trim();
} catch {
return null;
}
}
/**
* Returns the local path for the installed tar.app. Returns null when the app isn't installed.
*
* @param props.udid device udid.
* @param props.bundleIdentifier bundle identifier for app
* @returns local file path to installed app binary, e.g. '/Users/evanbacon/Library/Developer/CoreSimulator/Devices/EFEEA6EF-E3F5-4EDE-9B72-29EAFA7514AE/data/Containers/Bundle/Application/FA43A0C6-C2AD-442D-B8B1-EAF3E88CF3BF/Exponent-2.21.3.tar.app'
*/
async function getContainerPathAsync({
udid,
bundleIdentifier
}) {
if (_internal().CoreSimulator.isEnabled()) {
return _internal().CoreSimulator.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 && _error$stderr.match(/No such file or directory/)) {
return null;
}
throw error;
}
}
async function waitForDeviceToBootAsync({
udid
}) {
return (0, _waitForActionAsync().waitForActionAsync)({
action: () => bootAsync({
udid
})
});
}
async function openURLAsync(options) {
try {
// Skip logging since this is likely to fail.
await xcrunAsync(['simctl', 'openurl', deviceUDIDOrBooted(options.udid), options.url]);
} catch (error) {
var _error$stderr2;
if (!((_error$stderr2 = error.stderr) !== null && _error$stderr2 !== void 0 && _error$stderr2.match(/Unable to lookup in current state: Shut/))) {
throw error;
}
// If the device was in a weird in-between state ("Shutting Down" or "Shutdown"), then attempt to reboot it and try again.
// This can happen when quitting the Simulator app, and immediately pressing `i` to reopen the project.
// First boot the simulator
await runBootAsync({
udid: deviceUDIDOrBooted(options.udid)
});
// Finally, try again...
return await openURLAsync(options);
}
}
async function openBundleIdAsync(options) {
return xcrunAsync(['simctl', 'launch', deviceUDIDOrBooted(options.udid), options.bundleIdentifier]);
}
// This will only boot in headless mode if the Simulator app is not running.
async function bootAsync({
udid
}) {
if (_internal().CoreSimulator.isEnabled()) {
const device = await _internal().CoreSimulator.getDeviceInfoAsync({
udid
}).catch(() => null);
if ((device === null || device === void 0 ? void 0 : device.state) === 'Booted') {
return device;
}
await runBootAsync({
udid
});
return await (0, _profileMethod().profileMethod)(_internal().CoreSimulator.getDeviceInfoAsync)({
udid
});
}
// TODO: Deprecate
await runBootAsync({
udid
});
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 runBootAsync({
udid
}) {
try {
// Skip logging since this is likely to fail.
await xcrunAsync(['simctl', 'boot', udid]);
} catch (error) {
var _error$stderr3;
if (!((_error$stderr3 = error.stderr) !== null && _error$stderr3 !== void 0 && _error$stderr3.match(/Unable to boot device in current state: Booted/))) {
throw error;
}
}
}
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')) {
_internal().Logger.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;
}
async function listSimulatorDevicesAsync() {
if (_internal().CoreSimulator.isEnabled()) {
return _internal().CoreSimulator.listDevicesAsync();
}
const simulatorDeviceInfo = await listAsync('devices');
return Object.values(simulatorDeviceInfo.devices).reduce((prev, runtime) => {
return prev.concat(runtime);
}, []);
}
/**
* Get a list of all connected devices.
*/
async function listDevicesAsync() {
if (_internal().AppleDevice.isEnabled()) {
const results = await _internal().AppleDevice.getConnectedDevices();
// TODO: Add support for osType (ipad, watchos, etc)
return results.map(device => {
var _ref, _device$DeviceName;
return {
// TODO: Better name
name: (_ref = (_device$DeviceName = device.DeviceName) !== null && _device$DeviceName !== void 0 ? _device$DeviceName : device.ProductType) !== null && _ref !== void 0 ? _ref : 'unknown ios device',
model: device.ProductType,
osVersion: device.ProductVersion,
deviceType: 'device',
udid: device.UniqueDeviceID
};
});
}
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 && _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';
}
function isLicenseOutOfDate(text) {
if (!text) {
return false;
}
const lower = text.toLowerCase();
return lower.includes('xcode') && lower.includes('license');
}
async function isXcrunInstalledAsync() {
try {
(0, _child_process().execSync)('xcrun --version', {
stdio: 'ignore'
});
return true;
} catch {
return false;
}
}
async function xcrunAsync(args, options) {
_internal().Logger.global.debug('Running: xcrun ' + args.join(' '));
try {
return await (0, _spawnAsync().default)('xcrun', args, options);
} catch (e) {
throw parseXcrunError(e);
}
}
function parseXcrunError(e) {
var _e$stderr;
if (isLicenseOutOfDate(e.stdout) || isLicenseOutOfDate(e.stderr)) {
return new (_internal().XDLError)('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 && _e$stderr.includes('not a developer tool or in PATH')) {
return new (_internal().XDLError)('SIMCTL_NOT_AVAILABLE', `You may need to run ${_chalk().default.bold('sudo xcode-select -s /Applications/Xcode.app')} and try again.`);
}
// Attempt to craft a better error message...
if (Array.isArray(e.output)) {
e.message += '\n' + e.output.join('\n').trim();
} else if (e.stderr) {
e.message += '\n' + e.stderr;
}
return e;
}
async function xcrunWithLogging(args, options) {
try {
return await xcrunAsync(args, options);
} catch (e) {
_internal().Logger.global.error(`Error running \`xcrun ${args.join(' ')}\`: ${e.stderr || e.message}`);
throw e;
}
}
//# sourceMappingURL=SimControl.js.map