From c26d8b18997a97294350fbb05b405b56d9c982da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=A8=E3=83=AA=E3=82=B9?= Date: Wed, 27 May 2020 13:38:33 +0900 Subject: [PATCH] refactor: use superspawn (#859) --- bin/templates/scripts/cordova/lib/Podfile.js | 10 +- bin/templates/scripts/cordova/lib/build.js | 11 +- bin/templates/scripts/cordova/lib/clean.js | 4 +- .../scripts/cordova/lib/listDevices.js | 38 ++--- .../cordova/lib/listEmulatorBuildTargets.js | 9 +- .../cordova/lib/listStartedEmulators.js | 29 +++- bin/templates/scripts/cordova/lib/run.js | 39 ++--- bin/templates/scripts/cordova/lib/spawn.js | 51 ------ bin/templates/scripts/cordova/lib/versions.js | 112 ++++--------- .../unit/fixtures/sample-ioreg-output.txt | 154 ++++++++++++++++++ tests/spec/unit/lib/list-devices.spec.js | 33 ++-- 11 files changed, 270 insertions(+), 220 deletions(-) delete mode 100644 bin/templates/scripts/cordova/lib/spawn.js create mode 100644 tests/spec/unit/fixtures/sample-ioreg-output.txt diff --git a/bin/templates/scripts/cordova/lib/Podfile.js b/bin/templates/scripts/cordova/lib/Podfile.js index b8932bdc1..465ed6ff3 100644 --- a/bin/templates/scripts/cordova/lib/Podfile.js +++ b/bin/templates/scripts/cordova/lib/Podfile.js @@ -21,10 +21,12 @@ const fs = require('fs-extra'); const path = require('path'); const util = require('util'); -const events = require('cordova-common').events; const Q = require('q'); -const superspawn = require('cordova-common').superspawn; -const CordovaError = require('cordova-common').CordovaError; +const { + CordovaError, + events, + superspawn: { spawn } +} = require('cordova-common'); Podfile.FILENAME = 'Podfile'; Podfile.declarationRegexpMap = { @@ -396,7 +398,7 @@ Podfile.prototype.install = function (requirementsCheckerFunction) { events.emit('verbose', toolOptions.ignoreMessage); return Q.resolve(); } else { - return superspawn.spawn('pod', ['install', '--verbose'], opts) + return spawn('pod', ['install', '--verbose'], opts) .progress(stdio => { if (stdio.stderr) { console.error(stdio.stderr); } if (stdio.stdout) { diff --git a/bin/templates/scripts/cordova/lib/build.js b/bin/templates/scripts/cordova/lib/build.js index 740bc7318..0126f5901 100644 --- a/bin/templates/scripts/cordova/lib/build.js +++ b/bin/templates/scripts/cordova/lib/build.js @@ -20,7 +20,10 @@ const Q = require('q'); const path = require('path'); const which = require('which'); -const superspawn = require('cordova-common').superspawn; +const { + events, + superspawn: { spawn } +} = require('cordova-common'); const fs = require('fs-extra'); const plist = require('plist'); const util = require('util'); @@ -28,8 +31,6 @@ const util = require('util'); const check_reqs = require('./check_reqs'); const projectFile = require('./projectFile'); -const events = require('cordova-common').events; - // These are regular expressions to detect if the user is changing any of the built-in xcodebuildArgs /* eslint-disable no-useless-escape */ const buildFlagMatchers = { @@ -225,7 +226,7 @@ module.exports.run = buildOpts => { fs.removeSync(buildOutputDir); const xcodebuildArgs = getXcodeBuildArgs(projectName, projectPath, configuration, buildOpts.device, buildOpts.buildFlag, emulatorTarget, buildOpts.automaticProvisioning); - return superspawn.spawn('xcodebuild', xcodebuildArgs, { cwd: projectPath, printCommand: true, stdio: 'inherit' }); + return spawn('xcodebuild', xcodebuildArgs, { cwd: projectPath, printCommand: true, stdio: 'inherit' }); }).then(() => { if (!buildOpts.device || buildOpts.noSign) { return; @@ -273,7 +274,7 @@ module.exports.run = buildOpts => { function packageArchive () { const xcodearchiveArgs = getXcodeArchiveArgs(projectName, projectPath, buildOutputDir, exportOptionsPath, buildOpts.automaticProvisioning); - return superspawn.spawn('xcodebuild', xcodearchiveArgs, { cwd: projectPath, printCommand: true, stdio: 'inherit' }); + return spawn('xcodebuild', xcodearchiveArgs, { cwd: projectPath, printCommand: true, stdio: 'inherit' }); } return Q.nfcall(fs.writeFile, exportOptionsPath, exportOptionsPlist, 'utf-8') diff --git a/bin/templates/scripts/cordova/lib/clean.js b/bin/templates/scripts/cordova/lib/clean.js index 0c07b33ff..ddfc52da9 100644 --- a/bin/templates/scripts/cordova/lib/clean.js +++ b/bin/templates/scripts/cordova/lib/clean.js @@ -20,7 +20,7 @@ const Q = require('q'); const path = require('path'); const fs = require('fs-extra'); -const superspawn = require('cordova-common').superspawn; +const { superspawn: { spawn } } = require('cordova-common'); const projectPath = path.join(__dirname, '..', '..'); @@ -32,7 +32,7 @@ module.exports.run = () => { } const xcodebuildClean = configName => { - return superspawn.spawn( + return spawn( 'xcodebuild', ['-project', projectName, '-configuration', configName, '-alltargets', 'clean'], { cwd: projectPath, printCommand: true, stdio: 'inherit' } diff --git a/bin/templates/scripts/cordova/lib/listDevices.js b/bin/templates/scripts/cordova/lib/listDevices.js index f6bf2dd9b..86a67ffbb 100644 --- a/bin/templates/scripts/cordova/lib/listDevices.js +++ b/bin/templates/scripts/cordova/lib/listDevices.js @@ -17,38 +17,26 @@ under the License. */ -var Q = require('q'); -var exec = require('child_process').exec; +const { superspawn: { spawn } } = require('cordova-common'); + +const DEVICE_REGEX = /-o (iPhone|iPad|iPod)@.*?"USB Serial Number" = "([^"]*)"/gs; /** * Gets list of connected iOS devices * @return {Promise} Promise fulfilled with list of available iOS devices */ function listDevices () { - var commands = [ - Q.nfcall(exec, "ioreg -p IOUSB -l | sed -n -e '/iPad/,/USB Serial Number/p' | grep 'Serial Number' | awk -F\\\" '{print $4 \" iPad\"}'"), - Q.nfcall(exec, "ioreg -p IOUSB -l | sed -n -e '/iPhone/,/USB Serial Number/p' | grep 'Serial Number' | awk -F\\\" '{print $4 \" iPhone\"}'"), - Q.nfcall(exec, "ioreg -p IOUSB -l | sed -n -e '/iPod/,/USB Serial Number/p' | grep 'Serial Number' | awk -F\\\" '{print $4 \" iPod\"}'") - ]; - - // wrap al lexec calls into promises and wait until they're fullfilled - return Q.all(commands).then(function (results) { - var accumulator = []; - results.forEach(function (result) { - var devicefound; - // Each command promise resolves with array [stout, stderr], and we need stdout only - // Append stdout lines to accumulator - devicefound = result[0].trim().split('\n'); - if (devicefound && devicefound.length) { - devicefound.forEach(function (device) { - if (device) { - accumulator.push(device); - } - }); - } + return spawn('ioreg', ['-p', 'IOUSB', '-l']) + .then(output => { + return [...matchAll(output, DEVICE_REGEX)] + .map(m => m.slice(1).reverse().join(' ')); }); - return accumulator; - }); +} + +// TODO: Should be replaced with String#matchAll once available +function * matchAll (s, r) { + let match; + while ((match = r.exec(s))) yield match; } exports.run = listDevices; diff --git a/bin/templates/scripts/cordova/lib/listEmulatorBuildTargets.js b/bin/templates/scripts/cordova/lib/listEmulatorBuildTargets.js index 73ba52621..73e35c05a 100644 --- a/bin/templates/scripts/cordova/lib/listEmulatorBuildTargets.js +++ b/bin/templates/scripts/cordova/lib/listEmulatorBuildTargets.js @@ -17,8 +17,7 @@ under the License. */ -var Q = require('q'); -var exec = require('child_process').exec; +const { superspawn: { spawn } } = require('cordova-common'); /** * Returns a list of available simulator build targets of the form @@ -32,10 +31,8 @@ var exec = require('child_process').exec; * */ function listEmulatorBuildTargets () { - return Q.nfcall(exec, 'xcrun simctl list --json') - .then(function (stdio) { - return JSON.parse(stdio[0]); - }) + return spawn('xcrun', ['simctl', 'list', '--json']) + .then(output => JSON.parse(output)) .then(function (simInfo) { var devices = simInfo.devices; var deviceTypes = simInfo.devicetypes; diff --git a/bin/templates/scripts/cordova/lib/listStartedEmulators.js b/bin/templates/scripts/cordova/lib/listStartedEmulators.js index c898ddc2f..ec1cb7f2f 100644 --- a/bin/templates/scripts/cordova/lib/listStartedEmulators.js +++ b/bin/templates/scripts/cordova/lib/listStartedEmulators.js @@ -17,21 +17,34 @@ under the License. */ -var Q = require('q'); -var exec = require('child_process').exec; +const { superspawn: { spawn } } = require('cordova-common'); /** * Gets list of running iOS simulators * @return {Promise} Promise fulfilled with list of running iOS simulators + * + * @todo In the next PR, I will refactor this entire method. + * + * The process no longer contains the pattern "[i]OS Simulator". + * The process is now called "Simulator.app" + * + * Additionaly, `defaults read com.apple.iphonesimulator "SimulateDevice"` is also not valid aymore. + * + * I will replace this entire method to locate the active simulators though `simctl` + * + * Alternativly, remove this file. It is not documented in Cordova and not used anywhere in our code base. */ function listStartedEmulators () { // wrap exec call into promise - return Q.nfcall(exec, 'ps aux | grep -i "[i]OS Simulator"') - .then(function () { - return Q.nfcall(exec, 'defaults read com.apple.iphonesimulator "SimulateDevice"'); - }).then(function (stdio) { - return stdio[0].trim().split('\n'); - }); + return spawn('ps', ['aux']) + .then(output => { + if (output.match(/[i]OS Simulator/)) { + return spawn('defaults', ['read', 'com.apple.iphonesimulator', '"SimulateDevice"']); + } + + return ''; + }) + .then(output => output.split('\n')); } exports.run = listStartedEmulators; diff --git a/bin/templates/scripts/cordova/lib/run.js b/bin/templates/scripts/cordova/lib/run.js index 4a806581d..19d3dfa60 100644 --- a/bin/templates/scripts/cordova/lib/run.js +++ b/bin/templates/scripts/cordova/lib/run.js @@ -20,12 +20,13 @@ const Q = require('q'); const path = require('path'); const build = require('./build'); -const superspawn = require('cordova-common').superspawn; +const { + events, + superspawn: { spawn } +} = require('cordova-common'); const check_reqs = require('./check_reqs'); const fs = require('fs-extra'); -const events = require('cordova-common').events; - const cordovaPath = path.join(__dirname, '..'); const projectPath = path.join(__dirname, '..', '..'); @@ -74,7 +75,7 @@ module.exports.run = runOptions => { const ipafile = path.join(buildOutputDir, `${projectName}.ipa`); // unpack the existing platform/ios/build/device/appname.ipa (zipfile), will create a Payload folder - return superspawn.spawn('unzip', ['-o', '-qq', ipafile], { cwd: buildOutputDir, printCommand: true, stdio: 'inherit' }); + return spawn('unzip', ['-o', '-qq', ipafile], { cwd: buildOutputDir, printCommand: true, stdio: 'inherit' }); }) .then(() => { // Uncompress IPA (zip file) @@ -144,7 +145,7 @@ function filterSupportedArgs (args) { * @return {Promise} Fullfilled when any device is connected, rejected otherwise */ function checkDeviceConnected () { - return superspawn.spawn('ios-deploy', ['-c', '-t', '1'], { printCommand: true, stdio: 'inherit' }); + return spawn('ios-deploy', ['-c', '-t', '1'], { printCommand: true, stdio: 'inherit' }); } /** @@ -157,9 +158,9 @@ function deployToDevice (appPath, target, extraArgs) { events.emit('log', 'Deploying to device'); // Deploying to device... if (target) { - return superspawn.spawn('ios-deploy', ['--justlaunch', '-d', '-b', appPath, '-i', target].concat(extraArgs), { printCommand: true, stdio: 'inherit' }); + return spawn('ios-deploy', ['--justlaunch', '-d', '-b', appPath, '-i', target].concat(extraArgs), { printCommand: true, stdio: 'inherit' }); } else { - return superspawn.spawn('ios-deploy', ['--justlaunch', '--no-wifi', '-d', '-b', appPath].concat(extraArgs), { printCommand: true, stdio: 'inherit' }); + return spawn('ios-deploy', ['--justlaunch', '--no-wifi', '-d', '-b', appPath].concat(extraArgs), { printCommand: true, stdio: 'inherit' }); } } @@ -198,18 +199,18 @@ function startSim (appPath, target) { } function iossimLaunch (appPath, devicetypeid, log, exit) { - const f = path.resolve(path.dirname(require.resolve('ios-sim')), 'bin', 'ios-sim'); - const params = ['launch', appPath, '--devicetypeid', devicetypeid, '--log', log, exit]; - - return superspawn.spawn(f, params, { cwd: projectPath, printCommand: true }) - .progress(stdio => { - if (stdio.stderr) { - events.emit('error', `[ios-sim] ${stdio.stderr}`); - } - if (stdio.stdout) { - events.emit('log', `[ios-sim] ${stdio.stdout.trim()}`); - } - }) + return spawn( + require.resolve('ios-sim/bin/ios-sim'), + ['launch', appPath, '--devicetypeid', devicetypeid, '--log', log, exit], + { cwd: projectPath, printCommand: true } + ).progress(stdio => { + if (stdio.stderr) { + events.emit('error', `[ios-sim] ${stdio.stderr}`); + } + if (stdio.stdout) { + events.emit('log', `[ios-sim] ${stdio.stdout.trim()}`); + } + }) .then(result => { events.emit('log', 'Simulator successfully started via `ios-sim`.'); }); diff --git a/bin/templates/scripts/cordova/lib/spawn.js b/bin/templates/scripts/cordova/lib/spawn.js deleted file mode 100644 index 351452ab0..000000000 --- a/bin/templates/scripts/cordova/lib/spawn.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. -*/ - -const Q = require('q'); -const proc = require('child_process'); - -/** - * Run specified command with arguments - * @param {String} cmd Command - * @param {Array} args Array of arguments that should be passed to command - * @param {String} opt_cwd Working directory for command - * @param {String} opt_verbosity Verbosity level for command stdout output, "verbose" by default - * @return {Promise} Promise either fullfilled or rejected with error code - * @deprecated Use `require('cordova-common').superspawn` instead. - */ -module.exports = (cmd, args, opt_cwd) => { - console.warn( - 'This function is deprecated, may be removed from a future release. ' + - "Use `require('cordova-common').superspawn` instead."); - const d = Q.defer(); - try { - const child = proc.spawn(cmd, args, { cwd: opt_cwd, stdio: 'inherit' }); - - child.on('exit', code => { - if (code) { - d.reject(`Error code ${code} for command: ${cmd} with args: ${args}`); - } else { - d.resolve(); - } - }); - } catch (e) { - d.reject(e); - } - return d.promise; -}; diff --git a/bin/templates/scripts/cordova/lib/versions.js b/bin/templates/scripts/cordova/lib/versions.js index 0e782292a..318e8dfff 100644 --- a/bin/templates/scripts/cordova/lib/versions.js +++ b/bin/templates/scripts/cordova/lib/versions.js @@ -17,73 +17,41 @@ under the License. */ -const child_process = require('child_process'); +const { superspawn: { spawn } } = require('cordova-common'); const Q = require('q'); const semver = require('semver'); +function fetchSdkVersionByType (sdkType) { + return spawn('xcodebuild', ['-showsdks']) + .then(output => { + const regexSdk = new RegExp(`^${sdkType} \\d`); + + const versions = output.split('\n') + .filter(line => line.trim().match(regexSdk)) + .map(line => line.match(/\d+\.\d+/)[0]) + .sort(exports.compareVersions); + + console.log(versions[0]); + }); +} + exports.get_apple_ios_version = () => { - const d = Q.defer(); - child_process.exec('xcodebuild -showsdks', (error, stdout, stderr) => { - if (error) { - d.reject(stderr); - } else { - d.resolve(stdout); - } - }); - - return d.promise.then(output => { - const regex = /[0-9]*\.[0-9]*/; - const versions = []; - const regexIOS = /^iOS \d+/; - output = output.split('\n'); - for (let i = 0; i < output.length; i++) { - if (output[i].trim().match(regexIOS)) { - versions[versions.length] = parseFloat(output[i].match(regex)[0]); - } - } - versions.sort(); - console.log(versions[0]); - return Q(); - }, stderr => Q.reject(stderr)); + return fetchSdkVersionByType('iOS'); }; exports.get_apple_osx_version = () => { - const d = Q.defer(); - child_process.exec('xcodebuild -showsdks', (error, stdout, stderr) => { - if (error) { - d.reject(stderr); - } else { - d.resolve(stdout); - } - }); - - return d.promise.then(output => { - const regex = /[0-9]*\.[0-9]*/; - const versions = []; - const regexOSX = /^macOS \d+/; - output = output.split('\n'); - for (let i = 0; i < output.length; i++) { - if (output[i].trim().match(regexOSX)) { - versions[versions.length] = parseFloat(output[i].match(regex)[0]); - } - } - versions.sort(); - console.log(versions[0]); - return Q(); - }, stderr => Q.reject(stderr)); + return fetchSdkVersionByType('macOS'); }; exports.get_apple_xcode_version = () => { - const d = Q.defer(); - child_process.exec('xcodebuild -version', (error, stdout, stderr) => { - const versionMatch = /Xcode (.*)/.exec(stdout); - if (error || !versionMatch) { - d.reject(stderr); - } else { - d.resolve(versionMatch[1]); - } - }); - return d.promise; + return spawn('xcodebuild', ['-version']) + .then(output => { + const versionMatch = /Xcode (.*)/.exec(output); + + if (!versionMatch) return Promise.reject(output); + + return versionMatch[1]; + }); }; /** @@ -92,15 +60,7 @@ exports.get_apple_xcode_version = () => { * or rejected in case of error */ exports.get_ios_deploy_version = () => { - const d = Q.defer(); - child_process.exec('ios-deploy --version', (error, stdout, stderr) => { - if (error) { - d.reject(stderr); - } else { - d.resolve(stdout); - } - }); - return d.promise; + return spawn('ios-deploy', ['--version']); }; /** @@ -109,15 +69,7 @@ exports.get_ios_deploy_version = () => { * or rejected in case of error */ exports.get_cocoapods_version = () => { - const d = Q.defer(); - child_process.exec('pod --version', (error, stdout, stderr) => { - if (error) { - d.reject(stderr); - } else { - d.resolve(stdout); - } - }); - return d.promise; + return spawn('pod', ['--version']); }; /** @@ -126,15 +78,7 @@ exports.get_cocoapods_version = () => { * or rejected in case of error */ exports.get_ios_sim_version = () => { - const d = Q.defer(); - child_process.exec('ios-sim --version', (error, stdout, stderr) => { - if (error) { - d.reject(stderr); - } else { - d.resolve(stdout); - } - }); - return d.promise; + return spawn('ios-sim', ['--version']); }; /** diff --git a/tests/spec/unit/fixtures/sample-ioreg-output.txt b/tests/spec/unit/fixtures/sample-ioreg-output.txt new file mode 100644 index 000000000..e997c2a3a --- /dev/null +++ b/tests/spec/unit/fixtures/sample-ioreg-output.txt @@ -0,0 +1,154 @@ ++-o Root + | { + | "IOKitBuildVersion" = "Darwin Kernel Version 18.7.0: Thu Jun 20 00:00:00 PDT 1977; root:xnu-1337.133.37~0/RELEASE_X86_64" + | "OS Build Version" = "9999G" + | "IOBluetoothActive" = Yes + | "OSKernelCPUSubtype" = 3 + | "OSKernelCPUType" = 13333337 + | "OSPrelinkKextCount" = 300 + | "IOConsoleLocked" = No + | "IORegistryPlanes" = {"Foobar"="Foobar"} + | "IOConsoleUsers" = ({"Foobar"="Foobar"}) + | "IOKitDiagnostics" = {"Foobar"="Foobar"} ++-o Root + | { + | "IOKitBuildVersion" = "Darwin Kernel Version 18.7.0: Thu Jun 20 00:00:00 PDT 1977; root:xnu-1337.133.37~0/RELEASE_X86_64" + | "OS Build Version" = "9999G" + | "IOBluetoothActive" = Yes + | "OSKernelCPUSubtype" = 3 + | "OSKernelCPUType" = 13333337 + | "OSPrelinkKextCount" = 300 + | "IOConsoleLocked" = No + | "IORegistryPlanes" = {"Foobar"="Foobar"} + | "IOConsoleUsers" = ({"Foobar"="Foobar"}) + | "IOKitDiagnostics" = {"Foobar"="Foobar"} + | } + | + +-o AppleUSBXHCI Root Hub Simulation@14000000 + | { + | "iManufacturer" = 0 + | "bNumConfigurations" = 1 + | "idProduct" = 12121 + | "bMaxPacketSize0" = 8 + | "Built-In" = Yes + | "iProduct" = 0 + | "USB Product Name" = "AppleUSBXHCI Root Hub Simulation" + | "iSerialNumber" = 0 + | "bDeviceClass" = 10 + | "IOPowerManagement" = {"DevicePowerState"=0,"CurrentPowerState"=4,"CapabilityFlags"=32768,"MaxPowerState"=4,"DriverPowerState"=4} + | "IOUserClientClass" = "IOUSBDeviceUserClientV2" + | "locationID" = 333333333 + | "bDeviceSubClass" = 256 + | "bcdUSB" = 255 + | "non-removable" = "yes" + | "IOCFPlugInTypes" = {"d490eb2f-28f5-433e-b447-8c6e06680b19"="IOUSBFamily.kext/Contents/PlugIns/IOUSBLib.bundle"} + | "bDeviceProtocol" = 5 + | "USB Vendor Name" = "Apple Inc." + | "Device Speed" = 5 + | "idVendor" = 1452 + | "IOClassNameOverride" = "IOUSBRootHubDevice" + | } + | + +-o USB3.0 Hub@14300000 + | { + | "sessionID" = 756776508399194 + | "iManufacturer" = 1 + | "bNumConfigurations" = 1 + | "idProduct" = 1344 + | "bcdDevice" = 33322 + | "Bus Power Available" = 250 + | "USB Address" = 2 + | "bMaxPacketSize0" = 64 + | "iProduct" = 2 + | "iSerialNumber" = 0 + | "bDeviceClass" = 9 + | "Built-In" = No + | "locationID" = 444444444 + | "bDeviceSubClass" = 0 + | "bcdUSB" = 512 + | "USB Product Name" = "USB3.0 Hub" + | "PortNum" = 3 + | "non-removable" = "no" + | "IOCFPlugInTypes" = {"d490eb2f-28f5-433e-b447-8c6e06680b19"="IOUSBFamily.kext/Contents/PlugIns/IOUSBLib.bundle"} + | "bDeviceProtocol" = 2 + | "IOUserClientClass" = "IOUSBDeviceUserClientV2" + | "IOPowerManagement" = {"DevicePowerState"=0,"CurrentPowerState"=3,"CapabilityFlags"=65536,"MaxPowerState"=4,"DriverPowerState"=3} + | "kUSBCurrentConfiguration" = 1 + | "Device Speed" = 2 + | "USB Vendor Name" = "Unknown" + | "idVendor" = 0000 + | "IOGeneralInterest" = "IOCommand is not serializable" + | "IOClassNameOverride" = "IOUSBDevice" + | } + | + +-o iPhone@14400000 + | { + | "sessionID" = 117367051705201 + | "iManufacturer" = 1 + | "bNumConfigurations" = 5 + | "idProduct" = 7890 + | "bcdDevice" = 9867 + | "Bus Power Available" = 250 + | "USB Address" = 4 + | "bMaxPacketSize0" = 64 + | "iProduct" = 2 + | "iSerialNumber" = 3 + | "bDeviceClass" = 0 + | "Built-In" = No + | "locationID" = 63829761 + | "bDeviceSubClass" = 0 + | "bcdUSB" = 512 + | "USB Product Name" = "iPhone" + | "PortNum" = 2 + | "non-removable" = "no" + | "IOCFPlugInTypes" = {"d490eb2f-28f5-433e-b447-8c6e06680b19"="IOUSBFamily.kext/Contents/PlugIns/IOUSBLib.bundle"} + | "bDeviceProtocol" = 0 + | "IOUserClientClass" = "IOUSBDeviceUserClientV2" + | "IOPowerManagement" = {"DevicePowerState"=0,"CurrentPowerState"=4,"CapabilityFlags"=32768,"MaxPowerState"=4,"DriverPowerState"=4} + | "SupportsIPhoneOS" = Yes + | "Device Speed" = 2 + | "USB Vendor Name" = "Apple Inc." + | "idVendor" = 1452 + | "Preferred Configuration" = 3 + | "kUSBCurrentConfiguration" = 5 + | "IOGeneralInterest" = "IOCommand is not serializable" + | "USB Serial Number" = "THE_IPHONE_SERIAL" + | "kCallInterfaceOpenWithGate" = Yes + | "IOClassNameOverride" = "IOUSBDevice" + | } + | + +-o iPad@14500000 + { + "sessionID" = 391543963420811 + "iManufacturer" = 1 + "bNumConfigurations" = 5 + "idProduct" = 7891 + "bcdDevice" = 9866 + "Bus Power Available" = 250 + "USB Address" = 3 + "bMaxPacketSize0" = 64 + "iProduct" = 2 + "iSerialNumber" = 3 + "bDeviceClass" = 0 + "Built-In" = No + "locationID" = 63829762 + "bDeviceSubClass" = 0 + "bcdUSB" = 512 + "USB Product Name" = "iPad" + "PortNum" = 3 + "non-removable" = "no" + "IOCFPlugInTypes" = {"d490eb2f-28f5-433e-b447-8c6e06680b19"="IOUSBFamily.kext/Contents/PlugIns/IOUSBLib.bundle"} + "bDeviceProtocol" = 0 + "IOUserClientClass" = "IOUSBDeviceUserClientV2" + "IOPowerManagement" = {"DevicePowerState"=0,"CurrentPowerState"=4,"CapabilityFlags"=32768,"MaxPowerState"=4,"DriverPowerState"=4} + "SupportsIPadOS" = Yes + "Device Speed" = 2 + "USB Vendor Name" = "Apple Inc." + "idVendor" = 1452 + "Preferred Configuration" = 3 + "kUSBCurrentConfiguration" = 5 + "IOGeneralInterest" = "IOCommand is not serializable" + "USB Serial Number" = "THE_IPAD_SERIAL" + "kCallInterfaceOpenWithGate" = Yes + "IOClassNameOverride" = "IOUSBDevice" + } diff --git a/tests/spec/unit/lib/list-devices.spec.js b/tests/spec/unit/lib/list-devices.spec.js index b154764f5..4a75a9294 100644 --- a/tests/spec/unit/lib/list-devices.spec.js +++ b/tests/spec/unit/lib/list-devices.spec.js @@ -17,30 +17,31 @@ under the License. */ -const list_devices = require('../../../../bin/templates/scripts/cordova/lib/listDevices'); -const Q = require('q'); +const fs = require('fs-extra'); +const path = require('path'); +const rewire = require('rewire'); + +const list_devices = rewire('../../../../bin/templates/scripts/cordova/lib/listDevices'); + +const sampleData = fs.readFileSync(path.resolve(__dirname, '../fixtures/sample-ioreg-output.txt'), 'utf-8'); describe('cordova/lib/listDevices', () => { describe('run method', () => { + let spawnSpy; + beforeEach(() => { - spyOn(Q, 'all').and.returnValue(Q.resolve([])); - spyOn(Q, 'nfcall'); - }); - it('should invoke proper system calls to retrieve connected devices', () => { - return list_devices.run() - .then(() => { - expect(Q.nfcall).toHaveBeenCalledWith(jasmine.any(Function), jasmine.stringMatching(/ioreg.*iPad/g)); - expect(Q.nfcall).toHaveBeenCalledWith(jasmine.any(Function), jasmine.stringMatching(/ioreg.*iPod/g)); - expect(Q.nfcall).toHaveBeenCalledWith(jasmine.any(Function), jasmine.stringMatching(/ioreg.*iPhone/g)); - }); + spawnSpy = jasmine.createSpy('spawn').and.returnValue(Promise.resolve(sampleData)); + list_devices.__set__('spawn', spawnSpy); }); + it('should trim and split standard output and return as array', () => { - Q.all.and.returnValue(Q.resolve([[' this is\nmy sweet\nstdout\n ']])); return list_devices.run() .then(results => { - expect(results).toContain('this is'); - expect(results).toContain('my sweet'); - expect(results).toContain('stdout'); + expect(spawnSpy).toHaveBeenCalledWith('ioreg', ['-p', 'IOUSB', '-l']); + expect(results).toEqual([ + 'THE_IPHONE_SERIAL iPhone', + 'THE_IPAD_SERIAL iPad' + ]); }); }); });