diff --git a/lib/commands/device-types.ts b/lib/commands/device-types.ts index 0194cf0..5bfebc8 100644 --- a/lib/commands/device-types.ts +++ b/lib/commands/device-types.ts @@ -3,7 +3,7 @@ import iphoneSimulatorLibPath = require("./../iphone-simulator"); export class Command implements ICommand { - public execute(args: string[]): void { + public execute(args: string[]): Promise { var iphoneSimulator = new iphoneSimulatorLibPath.iPhoneSimulator(); return iphoneSimulator.printDeviceTypes(); } diff --git a/lib/commands/launch.ts b/lib/commands/launch.ts index 334d116..b61ca8e 100644 --- a/lib/commands/launch.ts +++ b/lib/commands/launch.ts @@ -2,7 +2,7 @@ import iphoneSimulatorLibPath = require("./../iphone-simulator"); import options = require("../options"); export class Command implements ICommand { - public execute(args: string[]): string { + public execute(args: string[]): Promise { var iphoneSimulator = new iphoneSimulatorLibPath.iPhoneSimulator(); return iphoneSimulator.run(args[0], args[1], options); } diff --git a/lib/commands/notify-post.ts b/lib/commands/notify-post.ts index 5f88e2a..fb39ce5 100644 --- a/lib/commands/notify-post.ts +++ b/lib/commands/notify-post.ts @@ -1,7 +1,7 @@ import iphoneSimulatorLibPath = require("./../iphone-simulator"); export class Command implements ICommand { - public execute(args: string[]): void { + public execute(args: string[]): Promise { var iphoneSimulator = new iphoneSimulatorLibPath.iPhoneSimulator(); return iphoneSimulator.sendNotification(args[0], args[1]); } diff --git a/lib/commands/sdks.ts b/lib/commands/sdks.ts index a59623b..e70ccdc 100644 --- a/lib/commands/sdks.ts +++ b/lib/commands/sdks.ts @@ -3,7 +3,7 @@ import iphoneSimulatorLibPath = require("./../iphone-simulator"); export class Command implements ICommand { - public execute(args: string[]): void { + public execute(args: string[]): Promise { var iphoneSimulator = new iphoneSimulatorLibPath.iPhoneSimulator(); return iphoneSimulator.printSDKS(); } diff --git a/lib/declarations.ts b/lib/declarations.ts index 1cce5f1..c401aac 100644 --- a/lib/declarations.ts +++ b/lib/declarations.ts @@ -2,10 +2,10 @@ "use strict"; interface IiPhoneSimulator { - run(applicationPath: string, applicationIdentifier: string, options: IOptions): string; - printDeviceTypes(): void; - printSDKS(): void; - sendNotification(notification: string, deviceId: string): void; + run(applicationPath: string, applicationIdentifier: string, options: IOptions): Promise; + printDeviceTypes(): Promise; + printSDKS(): Promise; + sendNotification(notification: string, deviceId: string): Promise; createSimulator(): ISimulator; } @@ -27,15 +27,15 @@ interface IDevice { } interface ISimctl { - launch(deviceId: string, applicationIdentifier: string, options: IOptions): string; - boot(deviceId: string): void; - terminate(deviceId: string, appIdentifier: string): string; - install(deviceId: string, applicationPath: string): void; - uninstall(deviceId: string, applicationIdentifier: string, opts?: any): void; - notifyPost(deviceId: string, notification: string): void; - getDevices(): IDevice[]; + launch(deviceId: string, applicationIdentifier: string, options: IOptions): Promise; + boot(deviceId: string): Promise; + terminate(deviceId: string, appIdentifier: string): Promise; + install(deviceId: string, applicationPath: string): Promise; + uninstall(deviceId: string, applicationIdentifier: string, opts?: any): Promise; + notifyPost(deviceId: string, notification: string): Promise; + getDevices(): Promise; getLog(deviceId: string, predicate?: string): any; - getAppContainer(deviceId: string, applicationIdentifier: string): string; + getAppContainer(deviceId: string, applicationIdentifier: string): Promise; } interface IDictionary { @@ -43,18 +43,18 @@ interface IDictionary { } interface ISimulator extends INameGetter { - getDevices(): IDevice[]; - getSdks(): ISdk[]; - run(applicationPath: string, applicationIdentifier: string, options: IOptions): string; - sendNotification(notification: string, deviceId: string): void; - getApplicationPath(deviceId: string, applicationIdentifier: string): string; + getDevices(): Promise; + getSdks(): Promise; + run(applicationPath: string, applicationIdentifier: string, options: IOptions): Promise; + sendNotification(notification: string, deviceId: string): Promise; + getApplicationPath(deviceId: string, applicationIdentifier: string): Promise; getInstalledApplications(deviceId: string): IApplication[]; - installApplication(deviceId: string, applicationPath: string): void; - uninstallApplication(deviceId: string, appIdentifier: string): void; - startApplication(deviceId: string, appIdentifier: string, options: IOptions): string; - stopApplication(deviceId: string, appIdentifier: string, bundleExecutable: string): string; - getDeviceLogProcess(deviceId: string): any; - startSimulator(options: IOptions, device?: IDevice): void; + installApplication(deviceId: string, applicationPath: string): Promise; + uninstallApplication(deviceId: string, appIdentifier: string): Promise; + startApplication(deviceId: string, appIdentifier: string, options: IOptions): Promise; + stopApplication(deviceId: string, appIdentifier: string, bundleExecutable: string): Promise; + getDeviceLogProcess(deviceId: string): Promise; + startSimulator(options: IOptions, device?: IDevice): Promise; } interface INameGetter { diff --git a/lib/ios-sim.ts b/lib/ios-sim.ts index 667f94c..996f9d6 100644 --- a/lib/ios-sim.ts +++ b/lib/ios-sim.ts @@ -52,13 +52,13 @@ Object.defineProperty(publicApi, "getRunningSimulators", { return (...args: any[]) => { let isResolved = false; - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { const libraryPath = require("./iphone-simulator-xcode-simctl"); const simulator = new libraryPath.XCodeSimctlSimulator(); - - const tryGetBootedDevices = () => { + + const tryGetBootedDevices = async () => { try { - return simulator.getBootedDevices.apply(simulator, args); + return await simulator.getBootedDevices.apply(simulator, args); } catch (err) { if (!isResolved) { isResolved = true; @@ -67,7 +67,7 @@ Object.defineProperty(publicApi, "getRunningSimulators", { } } - let result = tryGetBootedDevices(); + let result = await tryGetBootedDevices(); if (result && result.length) { isResolved = true; resolve(result); @@ -75,9 +75,8 @@ Object.defineProperty(publicApi, "getRunningSimulators", { } if (!isResolved && (!result || !result.length)) { - const timer = setTimeout(() => { - result = tryGetBootedDevices(); - + const timer = setTimeout(async () => { + result = await tryGetBootedDevices(); if (!isResolved) { isResolved = true; resolve(result); diff --git a/lib/iphone-simulator-xcode-simctl.ts b/lib/iphone-simulator-xcode-simctl.ts index 45bdac8..17a22a9 100755 --- a/lib/iphone-simulator-xcode-simctl.ts +++ b/lib/iphone-simulator-xcode-simctl.ts @@ -30,12 +30,12 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I this.simctl = new Simctl(); } - public getDevices(): IDevice[] { + public getDevices(): Promise { return this.simctl.getDevices(); } - public getSdks(): ISdk[] { - let devices = this.simctl.getDevices(); + public async getSdks(): Promise { + let devices = await this.simctl.getDevices(); return _.map(devices, device => { return { displayName: `iOS ${device.runtimeVersion}`, @@ -44,8 +44,8 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I }); } - public run(applicationPath: string, applicationIdentifier: string, options: IOptions): string { - const device = this.getDeviceToRun(options); + public async run(applicationPath: string, applicationIdentifier: string, options: IOptions): Promise { + const device = await this.getDeviceToRun(options); this.startSimulator(options, device); if (!options.skipInstall) { @@ -55,16 +55,17 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I return this.simctl.launch(device.id, applicationIdentifier, options); } - public sendNotification(notification: string, deviceId: string): void { + public sendNotification(notification: string, deviceId: string): Promise { let device = this.getDeviceFromIdentifier(deviceId); + if (!device) { errors.fail("Could not find device."); } - this.simctl.notifyPost(deviceId, notification); + return this.simctl.notifyPost(deviceId, notification); } - public getApplicationPath(deviceId: string, applicationIdentifier: string): string { + public getApplicationPath(deviceId: string, applicationIdentifier: string): Promise { return this.simctl.getAppContainer(deviceId, applicationIdentifier); } @@ -72,23 +73,23 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I return common.getInstalledApplications(deviceId); } - public installApplication(deviceId: string, applicationPath: string): void { + public installApplication(deviceId: string, applicationPath: string): Promise { return this.simctl.install(deviceId, applicationPath); } - public uninstallApplication(deviceId: string, appIdentifier: string): void { + public uninstallApplication(deviceId: string, appIdentifier: string): Promise { return this.simctl.uninstall(deviceId, appIdentifier, { skipError: true }); } - public startApplication(deviceId: string, appIdentifier: string, options: IOptions): string { + public async startApplication(deviceId: string, appIdentifier: string, options: IOptions): Promise { // simctl launch command does not launch the process immediately and we have to wait a little bit, // just to ensure all related processes and services are alive. - const launchResult = this.simctl.launch(deviceId, appIdentifier, options); + const launchResult = await this.simctl.launch(deviceId, appIdentifier, options); utils.sleep(0.5); return launchResult; } - public stopApplication(deviceId: string, appIdentifier: string, bundleExecutable: string): string { + public async stopApplication(deviceId: string, appIdentifier: string, bundleExecutable: string): Promise { try { let xcodeMajorVersion: number = null; try { @@ -103,7 +104,7 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I // Xcode 7.x does not have support for `xcrun simctl terminate` command resultOfTermination = childProcess.execSync(`killall ${bundleExecutable}`, { skipError: true }); } else { - resultOfTermination = this.simctl.terminate(deviceId, appIdentifier); + resultOfTermination = await this.simctl.terminate(deviceId, appIdentifier); } // killall command does not terminate the processes immediately and we have to wait a little bit, @@ -116,9 +117,9 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I } } - public getDeviceLogProcess(deviceId: string, predicate?: string): child_process.ChildProcess { + public async getDeviceLogProcess(deviceId: string, predicate?: string): Promise { if (!this.isDeviceLogOperationStarted) { - const device = this.getDeviceFromIdentifier(deviceId); + const device = await this.getDeviceFromIdentifier(deviceId); const deviceVersion = device ? device.runtimeVersion : ""; const majorVersion = deviceVersion.split(".")[0]; @@ -135,8 +136,8 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I return this.deviceLogChildProcess; } - private getDeviceToRun(options: IOptions, device?: any): IDevice { - let devices = _.sortBy(this.simctl.getDevices(), (device) => device.runtimeVersion), + private async getDeviceToRun(options: IOptions, device?: any): Promise { + let devices = _.sortBy(await this.simctl.getDevices(), (device) => device.runtimeVersion), sdkVersion = options.sdkVersion || options.sdk, deviceIdOrName = options.device; @@ -181,22 +182,22 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I return device.state === 'Booted'; } - private getBootedDevice(): IDevice { - let devices = this.simctl.getDevices(); + private async getBootedDevice(): Promise { + let devices = await this.simctl.getDevices(); return _.find(devices, device => this.isDeviceBooted(device)); } - private getBootedDevices(): IDevice[] { - const devices = this.simctl.getDevices(); + private async getBootedDevices(): Promise { + const devices = await this.simctl.getDevices(); return _.filter(devices, device => this.isDeviceBooted(device)); } - public startSimulator(options: IOptions, device?: IDevice): void { + public async startSimulator(options: IOptions, device?: IDevice): Promise { if (!device && options.device) { this.verifyDevice(options.device); } - device = device || this.getDeviceToRun(options); + device = device || await this.getDeviceToRun(options); // In case the id is undefined, skip verification - we'll start default simulator. if (device && device.id) { @@ -204,7 +205,7 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I } if (!device || !device.runtimeVersion || !device.fullId) { - device = this.getDeviceToRun(options, device); + device = await this.getDeviceToRun(options, device); } if (!this.isDeviceBooted(device)) { @@ -214,7 +215,7 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I if (isSimulatorAppRunning) { // In case user closes simulator window but simulator app is still alive if (!haveBootedDevices) { - device = this.getDeviceToRun(options); + device = await this.getDeviceToRun(options); } this.simctl.boot(device.id); } else { @@ -229,8 +230,8 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I } } - private haveBootedDevices(): boolean { - const bootedDevices = this.getBootedDevices(); + private async haveBootedDevices(): Promise { + const bootedDevices = await this.getBootedDevices(); return bootedDevices && bootedDevices.length > 0; } @@ -245,16 +246,16 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I } } - private verifyDevice(device: IDevice | string): void { - const availableDevices = this.getDevices(); + private async verifyDevice(device: IDevice | string): Promise { + const availableDevices = await this.getDevices(); const deviceId = (device).id || device; if (!_.find(availableDevices, { id: deviceId }) && !_.find(availableDevices, { name: deviceId })) { errors.fail(`No simulator image available for device identifier '${deviceId}'.`); } } - private getDeviceFromIdentifier(deviceId: string) { - const availableDevices = this.getDevices(); + private async getDeviceFromIdentifier(deviceId: string) { + const availableDevices = await this.getDevices(); return _.find(availableDevices, { id: deviceId }); } diff --git a/lib/iphone-simulator.ts b/lib/iphone-simulator.ts index 8e08c62..fe087c2 100644 --- a/lib/iphone-simulator.ts +++ b/lib/iphone-simulator.ts @@ -18,13 +18,13 @@ export class iPhoneSimulator implements IiPhoneSimulator { this.simulator = this.createSimulator(); } - public run(applicationPath: string, applicationIdentifier: string, options: IOptions): string { + public async run(applicationPath: string, applicationIdentifier: string, options: IOptions): Promise { if (!fs.existsSync(applicationPath)) { errors.fail("Path does not exist ", applicationPath); } if (options.device) { - const hasSuchDevice = _.find(this.simulator.getDevices(), device => device.name === options.device || device.id === options.device); + const hasSuchDevice = _.find(await this.simulator.getDevices(), device => device.name === options.device || device.id === options.device); if (!hasSuchDevice) { errors.fail(`Unable to find device ${options.device}.`); } @@ -32,7 +32,7 @@ export class iPhoneSimulator implements IiPhoneSimulator { let sdkVersion = options.sdkVersion || options.sdk; if (sdkVersion) { - let runtimeVersions = _.unique(_.map(this.simulator.getDevices(), (device: IDevice) => device.runtimeVersion)); + let runtimeVersions = _.unique(_.map(await this.simulator.getDevices(), (device: IDevice) => device.runtimeVersion)); if (!_.contains(runtimeVersions, sdkVersion)) { errors.fail(`Unable to find sdk ${sdkVersion}. The valid runtime versions are ${runtimeVersions.join(", ")}`); } @@ -41,13 +41,13 @@ export class iPhoneSimulator implements IiPhoneSimulator { return this.simulator.run(applicationPath, applicationIdentifier, options); } - public printDeviceTypes(): void { - let devices = this.simulator.getDevices(); + public async printDeviceTypes(): Promise { + let devices = await this.simulator.getDevices(); _.each(devices, device => console.log(`Device Identifier: ${device.fullId}. ${os.EOL}Runtime version: ${device.runtimeVersion} ${os.EOL}`)); } - public printSDKS(): void { - let sdks = this.simulator.getSdks(); + public async printSDKS(): Promise { + let sdks = await this.simulator.getSdks(); _.each(sdks, (sdk) => { let output = ` Display Name: ${sdk.displayName} ${os.EOL} Version: ${sdk.version} ${os.EOL}`; if (sdk.rootPath) { @@ -57,7 +57,7 @@ export class iPhoneSimulator implements IiPhoneSimulator { }); } - public sendNotification(notification: string, deviceId: string): void { + public sendNotification(notification: string, deviceId: string): Promise { if (!notification) { errors.fail("Notification required."); } diff --git a/lib/simctl.ts b/lib/simctl.ts index 40ec4a1..fae28c1 100644 --- a/lib/simctl.ts +++ b/lib/simctl.ts @@ -5,7 +5,7 @@ import * as _ from "lodash"; export class Simctl implements ISimctl { - public launch(deviceId: string, appIdentifier: string, options: IOptions): string { + public async launch(deviceId: string, appIdentifier: string, options: IOptions): Promise { options = options || {}; let args: string[] = []; if (options.waitForDebugger) { @@ -19,7 +19,7 @@ export class Simctl implements ISimctl { _.each(applicationArgs, (arg: string) => args.push(arg)); } - let result = this.simctlExec("launch", args); + let result = await this.spawnAsync("launch", args); if (options.waitForDebugger) { console.log(`${appIdentifier}: ${result}`); @@ -28,29 +28,29 @@ export class Simctl implements ISimctl { return result; } - public boot(deviceId: string) { - return this.simctlExec("boot", [deviceId]); + public boot(deviceId: string): Promise { + return this.spawnAsync("boot", [deviceId]); } - public terminate(deviceId: string, appIdentifier: string): string { - return this.simctlExec("terminate", [deviceId, appIdentifier]); + public terminate(deviceId: string, appIdentifier: string): Promise { + return this.spawnAsync("terminate", [deviceId, appIdentifier]); } - public install(deviceId: string, applicationPath: string): void { - this.simctlExec("install", [deviceId, applicationPath]); + public install(deviceId: string, applicationPath: string): Promise { + return this.spawnAsync("install", [deviceId, applicationPath]); } - public uninstall(deviceId: string, appIdentifier: string, opts?: any): void { - this.simctlExec("uninstall", [deviceId, appIdentifier], opts); + public uninstall(deviceId: string, appIdentifier: string, opts?: any): Promise { + return this.spawnAsync("uninstall", [deviceId, appIdentifier], opts); } - public notifyPost(deviceId: string, notification: string): void { - this.simctlExec("notify_post", [deviceId, notification]); + public notifyPost(deviceId: string, notification: string): Promise { + return this.spawnAsync("notify_post", [deviceId, notification]); } - public getAppContainer(deviceId: string, appIdentifier: string): string { + public async getAppContainer(deviceId: string, appIdentifier: string): Promise { try { - return this.simctlExec("get_app_container", [deviceId, appIdentifier]); + return await this.spawnAsync("get_app_container", [deviceId, appIdentifier]); } catch (e) { if (e.message.indexOf("No such file or directory") > -1) { return null; @@ -59,8 +59,8 @@ export class Simctl implements ISimctl { } } - public getDevices(): IDevice[] { - let rawDevices = this.simctlExec("list", ["devices"]); + public async getDevices(): Promise { + let rawDevices = await this.spawnAsync("list", ["devices"]); // expect to get a listing like // -- iOS 8.1 -- @@ -158,4 +158,8 @@ export class Simctl implements ISimctl { private simctlSpawn(command: string, args: string[], opts?: child_process.SpawnOptions): child_process.ChildProcess { return child_process.spawn("xcrun", ["simctl", command].concat(args), opts); } + + private spawnAsync(command: string, args: string[], opts?: child_process.SpawnOptions): Promise { + return childProcess.spawn("xcrun", ["simctl", command].concat(args), opts); + } }