UNPKG

nativescript

Version:

Command-line interface for building NativeScript projects

873 lines • 40.8 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DevicesService = void 0; const assert = require("assert"); const events_1 = require("events"); const _ = require("lodash"); const os_1 = require("os"); const util = require("util"); const constants_1 = require("../../../constants"); const constants = require("../../constants"); const constants_2 = require("../../constants"); const decorators_1 = require("../../decorators"); const helpers = require("../../helpers"); const helpers_1 = require("../../helpers"); const yok_1 = require("../../yok"); class DevicesService extends events_1.EventEmitter { constructor($logger, $errors, $iOSSimulatorDiscovery, $iOSDeviceDiscovery, $androidDeviceDiscovery, $staticConfig, $messages, $mobileHelper, $deviceLogProvider, $hostInfo, $injector, $options, $androidProcessService, $iOSEmulatorServices, $androidEmulatorServices, $androidEmulatorDiscovery, $emulatorHelper, $prompter, $devicePlatformsConstants) { super(); this.$logger = $logger; this.$errors = $errors; this.$iOSSimulatorDiscovery = $iOSSimulatorDiscovery; this.$iOSDeviceDiscovery = $iOSDeviceDiscovery; this.$androidDeviceDiscovery = $androidDeviceDiscovery; this.$staticConfig = $staticConfig; this.$messages = $messages; this.$mobileHelper = $mobileHelper; this.$deviceLogProvider = $deviceLogProvider; this.$hostInfo = $hostInfo; this.$injector = $injector; this.$options = $options; this.$androidProcessService = $androidProcessService; this.$iOSEmulatorServices = $iOSEmulatorServices; this.$androidEmulatorServices = $androidEmulatorServices; this.$androidEmulatorDiscovery = $androidEmulatorDiscovery; this.$emulatorHelper = $emulatorHelper; this.$prompter = $prompter; this.$devicePlatformsConstants = $devicePlatformsConstants; this._devices = {}; this._availableEmulators = {}; this._isInitialized = false; this._otherDeviceDiscoveries = []; this._allDeviceDiscoveries = []; this.attachToKnownDeviceDiscoveryEvents(); this.attachToKnownEmulatorDiscoveryEvents(); this._allDeviceDiscoveries = [ this.$iOSDeviceDiscovery, this.$androidDeviceDiscovery, this.$iOSSimulatorDiscovery, ]; } async pickSingleDevice(options) { if (options.onlyDevices && options.onlyEmulators) { this.$errors.fail(constants_1.DebugCommandErrors.UNABLE_TO_USE_FOR_DEVICE_AND_EMULATOR); } if (options.deviceId) { const device = await this.getDevice(options.deviceId); return device; } // Now let's take data for each device: const availableDevicesAndEmulators = this.getDeviceInstances().filter((d) => d.deviceInfo.status === constants_2.CONNECTED_STATUS && (!this.platform || d.deviceInfo.platform.toLowerCase() === this.platform.toLowerCase())); const selectedDevices = availableDevicesAndEmulators.filter((d) => options.onlyEmulators ? d.isEmulator : options.onlyDevices ? !d.isEmulator : true); if (selectedDevices.length > 1) { if ((0, helpers_1.isInteractive)()) { const choices = selectedDevices.map((e) => `${e.deviceInfo.identifier} - ${e.deviceInfo.displayName}`); const selectedDeviceString = await this.$prompter.promptForChoice("Select device for debugging", choices); const selectedDevice = _.find(selectedDevices, (d) => `${d.deviceInfo.identifier} - ${d.deviceInfo.displayName}` === selectedDeviceString); return selectedDevice; } else { const sortedInstances = _.sortBy(selectedDevices, (e) => e.deviceInfo.version); const emulators = sortedInstances.filter((e) => e.isEmulator); const devices = sortedInstances.filter((d) => !d.isEmulator); let selectedInstance; if (options.onlyEmulators || options.onlyDevices) { // When --emulator or --forDevice is passed, the instances are already filtered // So we are sure we have exactly the type we need and we can safely return the last one (highest OS version). selectedInstance = _.last(sortedInstances); } else { if (emulators.length) { selectedInstance = _.last(emulators); } else { selectedInstance = _.last(devices); } } this.$logger.warn(`Multiple devices/emulators found. Starting debugger on ${selectedInstance.deviceInfo.identifier}. ` + "If you want to debug on specific device/emulator, you can specify it with --device option."); return selectedInstance; } } else if (selectedDevices.length === 1) { return _.head(selectedDevices); } this.$errors.fail(constants_1.DebugCommandErrors.NO_DEVICES_EMULATORS_FOUND_FOR_OPTIONS); } async getEmulatorImages(options) { const result = Object.create(null); if (this.$hostInfo.isDarwin && (!options || !options.platform || this.$mobileHelper.isiOSPlatform(options.platform))) { result.ios = await this.$iOSEmulatorServices.getEmulatorImages(); } if (!options || !options.platform || this.$mobileHelper.isAndroidPlatform(options.platform)) { result.android = await this.$androidEmulatorServices.getEmulatorImages(); } return result; } async startEmulator(options) { if (!options || (!options.imageIdentifier && !options.emulatorIdOrName)) { return ["Missing mandatory image identifier or name option."]; } const availableEmulatorsOutput = await this.getEmulatorImages({ platform: options.platform, }); const emulators = this.$emulatorHelper.getEmulatorsFromAvailableEmulatorsOutput(availableEmulatorsOutput); const errors = this.$emulatorHelper.getErrorsFromAvailableEmulatorsOutput(availableEmulatorsOutput); let emulator = null; if (options.imageIdentifier) { emulator = this.$emulatorHelper.getEmulatorByImageIdentifier(options.imageIdentifier, emulators); } else if (options.emulatorIdOrName) { emulator = this.$emulatorHelper.getEmulatorByIdOrName(options.emulatorIdOrName, emulators); } if (!emulator) { const additionalErrors = errors && errors.length ? errors : []; return [ `Unable to find emulator with provided options: ${options}`, ...additionalErrors, ]; } // emulator is already running if (emulator.status === constants.RUNNING_EMULATOR_STATUS) { return null; } options.emulator = emulator; const emulatorService = this.resolveEmulatorServices(emulator.platform); const result = await emulatorService.startEmulator(options); return result.errors && result.errors.length ? result.errors : null; } get platform() { return this._platform; } get deviceCount() { return this._device ? 1 : this.getDeviceInstances().length; } getDevices() { return this.getDeviceInstances().map((deviceInstance) => deviceInstance.deviceInfo); } getDevicesForPlatform(platform) { return _.filter(this.getDeviceInstances(), (d) => d.deviceInfo.platform.toLowerCase() === platform.toLowerCase()); } isAndroidDevice(device) { return this.$mobileHelper.isAndroidPlatform(device.deviceInfo.platform); } isiOSDevice(device) { return (this.$mobileHelper.isiOSPlatform(device.deviceInfo.platform) && !device.isEmulator); } isiOSSimulator(device) { return !!(this.$mobileHelper.isiOSPlatform(device.deviceInfo.platform) && device.isEmulator); } /* tslint:disable:no-unused-variable */ setLogLevel(logLevel, deviceIdentifier) { this.$deviceLogProvider.setLogLevel(logLevel, deviceIdentifier); } /* tslint:enable:no-unused-variable */ isAppInstalledOnDevices(deviceIdentifiers, appId, projectName, projectDir) { this.$logger.trace(`Called isInstalledOnDevices for identifiers ${deviceIdentifiers}. AppIdentifier is ${appId}.`); return _.map(deviceIdentifiers, (deviceIdentifier) => this.isApplicationInstalledOnDevice(deviceIdentifier, { appId, projectName, projectDir, })); } getDeviceInstances() { return _.values(this._devices).map((device) => { var _a; const simPlatform = (_a = device.simulator) === null || _a === void 0 ? void 0 : _a.platform; device.deviceInfo.platform = simPlatform !== null && simPlatform !== void 0 ? simPlatform : device.deviceInfo.platform; return device; }); } async getInstalledApplications(deviceIdentifier) { const device = await this.getDevice(deviceIdentifier); return device.applicationManager.getInstalledApplications(); } addDeviceDiscovery(deviceDiscovery) { this._otherDeviceDiscoveries.push(deviceDiscovery); this._allDeviceDiscoveries.push(deviceDiscovery); this.attachToDeviceDiscoveryEvents(deviceDiscovery); } attachToKnownDeviceDiscoveryEvents() { [ this.$iOSSimulatorDiscovery, this.$iOSDeviceDiscovery, this.$androidDeviceDiscovery, ].forEach(this.attachToDeviceDiscoveryEvents.bind(this)); } attachToKnownEmulatorDiscoveryEvents() { this.$androidEmulatorDiscovery.on(constants.EmulatorDiscoveryNames.EMULATOR_IMAGE_FOUND, (emulator) => this.onEmulatorImageFound(emulator)); this.$androidEmulatorDiscovery.on(constants.EmulatorDiscoveryNames.EMULATOR_IMAGE_LOST, (emulator) => this.onEmulatorImageLost(emulator)); this.$iOSSimulatorDiscovery.on(constants.EmulatorDiscoveryNames.EMULATOR_IMAGE_FOUND, (emulator) => this.onEmulatorImageFound(emulator)); this.$iOSSimulatorDiscovery.on(constants.EmulatorDiscoveryNames.EMULATOR_IMAGE_LOST, (emulator) => this.onEmulatorImageLost(emulator)); } attachToDeviceDiscoveryEvents(deviceDiscovery) { deviceDiscovery.on(constants.DeviceDiscoveryEventNames.DEVICE_FOUND, (device) => this.onDeviceFound(device)); deviceDiscovery.on(constants.DeviceDiscoveryEventNames.DEVICE_UPDATED, (device) => this.onDeviceUpdated(device)); deviceDiscovery.on(constants.DeviceDiscoveryEventNames.DEVICE_LOST, (device) => this.onDeviceLost(device)); } onDeviceFound(device) { this.$logger.trace(`Found device with identifier '${device.deviceInfo.identifier}'`); this._devices[device.deviceInfo.identifier] = device; this.emit(constants.DeviceDiscoveryEventNames.DEVICE_FOUND, device); } onDeviceUpdated(device) { this.$logger.trace(`Updated device with identifier '${device.deviceInfo.identifier}'`); this._devices[device.deviceInfo.identifier] = device; this.emit(constants.DeviceDiscoveryEventNames.DEVICE_UPDATED, device); } onDeviceLost(device) { this.$logger.trace(`Lost device with identifier '${device.deviceInfo.identifier}'`); if (device.detach) { device.detach(); } delete this._devices[device.deviceInfo.identifier]; this.emit(constants.DeviceDiscoveryEventNames.DEVICE_LOST, device); } onEmulatorImageFound(emulator) { this.$logger.trace(`Found emulator with image identifier: ${emulator.imageIdentifier}`); this._availableEmulators[emulator.imageIdentifier] = emulator; this.emit(constants.EmulatorDiscoveryNames.EMULATOR_IMAGE_FOUND, emulator); } onEmulatorImageLost(emulator) { this.$logger.trace(`Lost emulator with image identifier ${emulator.imageIdentifier}`); delete this._availableEmulators[emulator.imageIdentifier]; this.emit(constants.EmulatorDiscoveryNames.EMULATOR_IMAGE_LOST, emulator); } /** * Starts looking for devices. Any found devices are pushed to "_devices" variable. */ async detectCurrentlyAttachedDevices(deviceInitOpts) { const options = this.getDeviceLookingOptions(deviceInitOpts); for (const deviceDiscovery of this._allDeviceDiscoveries) { try { await deviceDiscovery.startLookingForDevices(options); } catch (err) { this.$logger.trace("Error while checking for devices.", err); } } } async detectCurrentlyAvailableEmulators() { try { await this.$androidEmulatorDiscovery.startLookingForDevices(); } catch (err) { this.$logger.trace(`Error while checking for Android emulators. ${err}`); } try { await this.$iOSSimulatorDiscovery.checkForAvailableSimulators(); } catch (err) { this.$logger.trace(`Error while checking for iOS simulators. ${err}`); } } startDeviceDetectionInterval(deviceInitOpts = {}) { if (!this.deviceDetectionInterval) { let isDeviceDetectionIntervalInProgress = false; this.deviceDetectionInterval = setInterval(async () => { if (isDeviceDetectionIntervalInProgress) { return; } isDeviceDetectionIntervalInProgress = true; await this.detectCurrentlyAttachedDevices(deviceInitOpts); try { const trustedDevices = _.filter(this._devices, (device) => device.deviceInfo.status === constants.CONNECTED_STATUS); await (0, helpers_1.settlePromises)(_.map(trustedDevices, (device) => device.applicationManager.checkForApplicationUpdates())); } catch (err) { this.$logger.trace("Error checking for application updates on devices.", err); } isDeviceDetectionIntervalInProgress = false; }, deviceInitOpts.detectionInterval || DevicesService.DEVICE_LOOKING_INTERVAL); this.deviceDetectionInterval.unref(); } } startEmulatorDetectionInterval(opts = {}) { let isEmulatorDetectionIntervalRunning = false; this.emulatorDetectionInterval = setInterval(async () => { if (isEmulatorDetectionIntervalRunning) { return; } isEmulatorDetectionIntervalRunning = true; await this.detectCurrentlyAvailableEmulators(); isEmulatorDetectionIntervalRunning = false; }, opts.detectionInterval || DevicesService.EMULATOR_IMAGES_DETECTION_INTERVAL); this.emulatorDetectionInterval.unref(); } stopDeviceDetectionInterval() { if (this.deviceDetectionInterval) { clearInterval(this.deviceDetectionInterval); } } stopEmulatorDetectionInterval() { if (this.emulatorDetectionInterval) { clearInterval(this.emulatorDetectionInterval); } } /** * Returns device that matches an identifier. * The identifier is expected to be the same as the running device declares it (emulator-5554 for android or GUID for ios). * @param identifier running emulator or device identifier */ getDeviceByIdentifier(identifier) { const searchedDevice = _.find(this.getDeviceInstances(), (device) => { if (this.$mobileHelper.isApplePlatform(device.deviceInfo.platform) && device.isEmulator) { return (device.deviceInfo.identifier === identifier || device.deviceInfo.displayName === identifier); } return device.deviceInfo.identifier === identifier; }); if (!searchedDevice) { this.$errors.fail(this.$messages.Devices .NotFoundDeviceByIdentifierErrorMessageWithIdentifier, identifier, this.$staticConfig.CLIENT_NAME.toLowerCase()); } return searchedDevice; } /** * Starts looking for running devices. All found devices are pushed to _devices variable. */ async startLookingForDevices(deviceInitOpts) { this.$logger.trace("startLookingForDevices; platform is %s", this._platform); if (this._platform) { return this.detectCurrentlyAttachedDevices(deviceInitOpts); } await this.detectCurrentlyAttachedDevices(deviceInitOpts); await this.detectCurrentlyAvailableEmulators(); await this.startDeviceDetectionInterval(deviceInitOpts); } /** * Returns device depending on the passed index. * The index refers to assigned number to listed devices by tns device command. * @param index assigned device number */ getDeviceByIndex(index) { this.validateIndex(index - 1); return this.getDeviceInstances()[index - 1]; } /** * Returns running device for specified --device <DeviceId>. * Method expects running devices. * @param deviceOption parameter passed by the user to --device flag. Can be name, identifier or imageIdentifier. */ async getDevice(deviceOption) { let device = null; if (!device) { device = _.find(this.getDeviceInstances(), (d) => (d.deviceInfo.identifier && d.deviceInfo.identifier === deviceOption) || (d.deviceInfo.displayName && d.deviceInfo.displayName === deviceOption) || (d.deviceInfo.imageIdentifier && d.deviceInfo.imageIdentifier === deviceOption)); } if (!device && helpers.isNumberWithoutExponent(deviceOption)) { device = this.getDeviceByIndex(parseInt(deviceOption, 10)); } if (!device) { this.$errors.fail(this.$messages.Devices .NotFoundDeviceByIdentifierErrorMessageWithIdentifier, deviceOption, this.$staticConfig.CLIENT_NAME.toLowerCase()); } return device; } /** * Method runs action for a --device (value), specified by the user. * @param action action to be executed if canExecute returns true * @param canExecute predicate to decide whether the command can be ran */ async executeOnDevice(action, canExecute) { if (!canExecute || canExecute(this._device)) { return { deviceIdentifier: this._device.deviceInfo.identifier, result: await action(this._device), }; } } /** * Executes passed action for each found device. * @param action action to be executed if canExecute returns true * @param canExecute predicate to decide whether the command can be ran */ async executeOnAllConnectedDevices(action, canExecute) { const devices = this.filterDevicesByPlatform(); const sortedDevices = _.sortBy(devices, (device) => device.deviceInfo.platform); const result = []; const errors = []; for (const device of sortedDevices) { try { if (!canExecute || canExecute(device)) { result.push({ deviceIdentifier: device.deviceInfo.identifier, result: await action(device), }); } } catch (err) { err.deviceIdentifier = device.deviceInfo.identifier; this.$logger.trace(`Error while executing action on device ${device.deviceInfo.identifier}. The error is`, err); errors.push(err); } } if (errors.length) { let preErrorMsg = ""; if (errors.length > 1) { preErrorMsg = "Multiple errors were thrown:" + os_1.EOL; } const errorMessage = `${preErrorMsg}${errors .map((e) => e.message || e) .join(os_1.EOL)}`; this.$errors.fail(errorMessage); } return result; } deployOnDevices(deviceIdentifiers, packagePath, appId, projectName, projectDir) { this.$logger.trace(`Called deployOnDevices for identifiers ${deviceIdentifiers} for packageFile: ${packagePath}. Application identifier is ${appId}. Project Name is: ${projectName}`); return _.map(deviceIdentifiers, async (deviceIdentifier) => { const device = this.getDeviceByIdentifier(deviceIdentifier); let identifier; if (typeof appId === "string") { identifier = appId; } else { identifier = appId[device.deviceInfo.platform.toLowerCase()]; } return this.deployOnDevice(device, { packagePath, appId: identifier, projectName, projectDir, }); }); } /** * Runs the passed action if the predicate "canExecute" returns true * @param action action to be executed if canExecute returns true. * @param canExecute predicate to decide whether the command can be ran * @param options all possible options that can be passed to the command. */ async execute(action, canExecute, options) { assert.ok(this._isInitialized, "Devices services not initialized!"); if (this.hasDevices) { if (this.$hostInfo.isDarwin && this._platform && this.$mobileHelper.isiOSPlatform(this._platform) && this.$options.emulator && !this.isOnlyiOSSimultorRunning()) { // Executes the command only on iOS simulator const originalCanExecute = canExecute; canExecute = (dev) => this.isiOSSimulator(dev) && (!originalCanExecute || !!originalCanExecute(dev)); } return this.executeCore(action, canExecute); } else { const message = constants.ERROR_NO_DEVICES; if (options && options.allowNoDevices) { this.$logger.info(message); } else { if (!this.$hostInfo.isDarwin && this._platform && this.$mobileHelper.isiOSPlatform(this._platform)) { this.$errors.fail(message); } else { return this.executeCore(action, canExecute); } } } } /** * Starts emulator or simulator if necessary depending on --device or --emulator flags. * If no options are passed runs default emulator/simulator if no devices are connected. * @param deviceInitOpts mainly contains information about --emulator and --deviceId flags. */ async startEmulatorIfNecessary(deviceInitOpts) { if (deviceInitOpts && deviceInitOpts.deviceId && deviceInitOpts.emulator) { this.$errors.fail(`--device and --emulator are incompatible options. If you are trying to run on specific emulator, use "${this.$staticConfig.CLIENT_NAME} run --device <DeviceID>`); } if (deviceInitOpts && deviceInitOpts.platform && !deviceInitOpts.skipEmulatorStart) { // are there any running devices this._platform = deviceInitOpts.platform; try { await this.startLookingForDevices(deviceInitOpts); } catch (err) { this.$logger.trace("Error while checking for devices.", err); } const deviceInstances = this.getDeviceInstances(); if (!deviceInitOpts.deviceId && _.isEmpty(deviceInstances)) { if (!this.$hostInfo.isDarwin && this.$mobileHelper.isApplePlatform(deviceInitOpts.platform)) { this.$errors.fail(constants.ERROR_NO_DEVICES_CANT_USE_IOS_SIMULATOR); } } try { await this._startEmulatorIfNecessary(deviceInitOpts); } catch (err) { const errorMessage = this.getEmulatorError(err, deviceInitOpts.platform); this.$errors.fail(errorMessage); } } } async _startEmulatorIfNecessary(data) { const deviceInstances = this.getDeviceInstances(); //if no --device is passed and no devices are found, the default emulator is started if (!data.deviceId && _.isEmpty(deviceInstances)) { return this.startEmulatorCore(data); } //check if --device(value) is running, if it's not or it's not the same as is specified, start with name from --device(value) if (data.deviceId) { if (!helpers.isNumberWithoutExponent(data.deviceId)) { const activeDeviceInstance = _.find(deviceInstances, (device) => device.deviceInfo.identifier === data.deviceId); if (!activeDeviceInstance) { return this.startEmulatorCore(data); } } } // make sure if the target platform is visionOS we don't try to run it on an already running iOS simulator... if (data.platform === this.$devicePlatformsConstants.visionOS && deviceInstances.length) { const runningDeviceInstance = deviceInstances.find((device) => device.deviceInfo.platform === this.$devicePlatformsConstants.visionOS); if (!runningDeviceInstance) { return this.startEmulatorCore(data); } } // if only emulator flag is passed and no other emulators are running, start default emulator if (data.emulator && deviceInstances.length) { const runningDeviceInstance = _.some(deviceInstances, (value) => value.isEmulator); if (!runningDeviceInstance) { return this.startEmulatorCore(data); } } } /** * Takes care of gathering information about all running devices. * Sets "_isInitialized" to true after infomation is present. * Method expects running devices. * @param data mainly contains information about --emulator and --deviceId flags. */ async initialize(data) { if (!this._deviceInitializePromise) { this._deviceInitializePromise = this.initializeCore(data); } try { await this._deviceInitializePromise; } catch (err) { // In case the initalization fails, we want to allow calling `initlialize` again with other arguments for example, so remove the cached promise value. this.$logger.trace(`Error while initializing devicesService: ${err}`); this._deviceInitializePromise = null; throw err; } } getPlatformsFromDeviceDescriptors(deviceDescriptors) { const platforms = _(deviceDescriptors) .map((device) => this.getDeviceByIdentifier(device.identifier)) .map((device) => device.deviceInfo.platform.toLowerCase()) .uniq() .value(); return platforms; } async initializeCore(deviceInitOpts) { if (this._isInitialized) { return; } if (!this.$options.json) { this.$logger.info("Searching for devices..."); } deviceInitOpts = deviceInitOpts || {}; this._data = deviceInitOpts; if (!deviceInitOpts.skipEmulatorStart) { // TODO: Remove from here as it calls startLookingForDevices, so we double the calls to specific device detection services await this.startEmulatorIfNecessary(deviceInitOpts); } const platform = deviceInitOpts.platform; const deviceOption = deviceInitOpts.deviceId; const deviceLookingOptions = { emulator: deviceInitOpts.emulator, platform: deviceInitOpts.platform, shouldReturnImmediateResult: deviceInitOpts.shouldReturnImmediateResult, detectionInterval: deviceInitOpts.detectionInterval, fullDiscovery: deviceInitOpts.fullDiscovery, }; if (platform && deviceOption) { this._platform = this.$mobileHelper.validatePlatformName(deviceInitOpts.platform); await this.startLookingForDevices(deviceLookingOptions); this._device = await this.getDevice(deviceOption); if (this._device.deviceInfo.platform !== this._platform) { this.$errors.fail(constants.ERROR_CANNOT_RESOLVE_DEVICE); } this.$logger.warn("Your application will be deployed only on the device specified by the provided index or identifier."); } else if (!platform && deviceOption) { await this.startLookingForDevices(deviceLookingOptions); this._device = await this.getDevice(deviceOption); this._platform = this._device.deviceInfo.platform; } else if (platform && !deviceOption) { this._platform = this.$mobileHelper.validatePlatformName(platform); await this.startLookingForDevices(deviceLookingOptions); } else { // platform and deviceId are not specified if (deviceInitOpts.skipInferPlatform) { if (deviceInitOpts.skipDeviceDetectionInterval) { await this.detectCurrentlyAttachedDevices(deviceLookingOptions); } else { deviceInitOpts.shouldReturnImmediateResult = true; await this.startLookingForDevices(deviceLookingOptions); } } else { await this.startLookingForDevices(deviceLookingOptions); const devices = this.getDeviceInstances(); const platforms = _(devices) .map((device) => device.deviceInfo.platform) .filter((pl) => { try { return !!this.$mobileHelper.validatePlatformName(pl); } catch (err) { this.$logger.warn(err.message); return null; } }) .uniq() .value(); if (platforms.length === 1) { this._platform = platforms[0]; } else if (platforms.length === 0) { this.$errors.fail(constants.ERROR_NO_DEVICES); } else { this.$errors.fail("Multiple device platforms detected (%s). Specify platform or device on command line.", helpers.formatListOfNames(platforms, "and")); } } } if (!this.$hostInfo.isDarwin && this._platform && this.$mobileHelper.isiOSPlatform(this._platform) && this.$options.emulator) { this.$errors.fail(constants.ERROR_CANT_USE_SIMULATOR); } this._isInitialized = true; } get hasDevices() { if (!this._platform) { return this.getDeviceInstances().length !== 0; } else { return this.filterDevicesByPlatform().length !== 0; } } isOnlyiOSSimultorRunning() { const devices = this.getDeviceInstances(); return (this._platform && this.$mobileHelper.isiOSPlatform(this._platform) && _.find(devices, (d) => d.isEmulator) && !_.find(devices, (d) => !d.isEmulator)); } getDeviceByDeviceOption() { return this._device; } async mapAbstractToTcpPort(deviceIdentifier, appIdentifier, framework) { return this.$androidProcessService.mapAbstractToTcpPort(deviceIdentifier, appIdentifier, framework); } getDebuggableApps(deviceIdentifiers) { return _.map(deviceIdentifiers, (deviceIdentifier) => this.getDebuggableAppsCore(deviceIdentifier)); } async getDebuggableViews(deviceIdentifier, appIdentifier) { const device = this.getDeviceByIdentifier(deviceIdentifier), debuggableViewsPerApp = await device.applicationManager.getDebuggableAppViews([appIdentifier]); return debuggableViewsPerApp && debuggableViewsPerApp[appIdentifier]; } getDebuggableAppsCore(deviceIdentifier) { const device = this.getDeviceByIdentifier(deviceIdentifier); return device.applicationManager.getDebuggableApps(); } async deployOnDevice(device, appData) { await device.applicationManager.reinstallApplication(appData.appId, appData.packagePath); this.$logger.info(`Successfully deployed on device with identifier '${device.deviceInfo.identifier}'.`); await device.applicationManager.tryStartApplication(appData); } filterDevicesByPlatform() { return _.filter(this.getDeviceInstances(), (device) => { if (this.$options.emulator && !device.isEmulator) { return false; } if (this._platform) { return device.deviceInfo.platform === this._platform; } return true; }); } validateIndex(index) { if (index < 0 || index > this.getDeviceInstances().length) { throw new Error(util.format(this.$messages.Devices.NotFoundDeviceByIndexErrorMessage, index, this.$staticConfig.CLIENT_NAME.toLowerCase())); } } resolveEmulatorServices(platform) { platform = platform || this._platform; if (this.$mobileHelper.isApplePlatform(platform)) { return this.$injector.resolve("iOSEmulatorServices"); } else if (this.$mobileHelper.isAndroidPlatform(platform)) { return this.$injector.resolve("androidEmulatorServices"); } return null; } /** * Starts emulator for platform and makes sure started devices/emulators/simulators are in _devices array before finishing. * @param platform (optional) platform to start emulator/simulator for * @param emulatorIdOrName (optional) emulator/simulator image identifier or name */ async startEmulatorCore(deviceInitOpts = {}) { const { deviceId } = deviceInitOpts; const platform = deviceInitOpts.platform || this._platform; const emulatorServices = this.resolveEmulatorServices(platform); if (!emulatorServices) { this.$errors.fail("Unable to detect platform for which to start emulator."); } const result = await emulatorServices.startEmulator({ emulatorIdOrName: deviceId, imageIdentifier: deviceId, platform: platform, sdk: this._data && this._data.sdk, }); if (result && result.errors && result.errors.length) { this.$errors.fail(result.errors.join("\n")); } const deviceLookingOptions = this.getDeviceLookingOptions(deviceInitOpts); if (this.$mobileHelper.isAndroidPlatform(platform)) { await this.$androidDeviceDiscovery.startLookingForDevices(deviceLookingOptions); } else if (this.$mobileHelper.isiOSPlatform(platform) && this.$hostInfo.isDarwin) { await this.$iOSSimulatorDiscovery.startLookingForDevices(deviceLookingOptions); } } async executeCore(action, canExecute) { if (this._device) { return [await this.executeOnDevice(action, canExecute)]; } return this.executeOnAllConnectedDevices(action, canExecute); } async isApplicationInstalledOnDevice(deviceIdentifier, appData) { let isInstalled = false; const device = this.getDeviceByIdentifier(deviceIdentifier); try { isInstalled = await device.applicationManager.isApplicationInstalled(appData.appId); await device.applicationManager.tryStartApplication(appData); } catch (err) { this.$logger.trace("Error while checking is application installed. Error is: ", err); } return { appIdentifier: appData.appId, deviceIdentifier, isInstalled, }; } getDeviceLookingOptions(deviceInitOpts = {}) { const { shouldReturnImmediateResult, emulator } = deviceInitOpts; const platform = deviceInitOpts.platform || this._platform; return { platform, shouldReturnImmediateResult: !!shouldReturnImmediateResult, emulator: !!emulator, fullDiscovery: deviceInitOpts.fullDiscovery, }; } getEmulatorError(error, platform) { let emulatorName = constants.DeviceTypes.Emulator; if (this.$mobileHelper.isiOSPlatform(platform)) { emulatorName = constants.DeviceTypes.Simulator; } return (`Cannot find connected devices.${os_1.EOL}` + `${emulatorName} start failed with: ${error.message}${os_1.EOL}` + `To list currently connected devices and verify that the specified identifier exists, run '${this.$staticConfig.CLIENT_NAME.toLowerCase()} device'.${os_1.EOL}` + `To list available ${emulatorName.toLowerCase()} images, run '${this.$staticConfig.CLIENT_NAME.toLowerCase()} device <Platform> --available-devices'.`); } } exports.DevicesService = DevicesService; DevicesService.DEVICE_LOOKING_INTERVAL = 200; DevicesService.EMULATOR_IMAGES_DETECTION_INTERVAL = 60 * 1000; __decorate([ (0, decorators_1.performanceLog)() ], DevicesService.prototype, "pickSingleDevice", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "getEmulatorImages", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "startEmulator", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "getDevices", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "setLogLevel", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "isAppInstalledOnDevices", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "getInstalledApplications", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "addDeviceDiscovery", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "startDeviceDetectionInterval", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "startEmulatorDetectionInterval", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "stopDeviceDetectionInterval", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "stopEmulatorDetectionInterval", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "deployOnDevices", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "initialize", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "mapAbstractToTcpPort", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "getDebuggableApps", null); __decorate([ (0, decorators_1.exported)("devicesService") ], DevicesService.prototype, "getDebuggableViews", null); yok_1.injector.register("devicesService", DevicesService); //# sourceMappingURL=devices-service.js.map