diff --git a/.travis.yml b/.travis.yml index 324c263f..873b89ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,3 @@ before_install: install: - npm install - bower install - -script: - - node_modules/.bin/ember try:each --skip-cleanup diff --git a/lib/commands/build.js b/lib/commands/build.js index 2f050954..f015693b 100644 --- a/lib/commands/build.js +++ b/lib/commands/build.js @@ -60,7 +60,7 @@ module.exports = Command.extend({ //lets for live reload addon service this.project.targetIsCordova = true; - this.project.CORDOVA_PLATFORM = options.platform; + this.project.CORBER_PLATFORM = options.platform; let hook = new Hook({ project: this.project diff --git a/lib/commands/init.js b/lib/commands/init.js index 056a5ef4..c19c2654 100644 --- a/lib/commands/init.js +++ b/lib/commands/init.js @@ -3,6 +3,7 @@ const CreateProject = require('../tasks/create-project'); const PlatformTask = require('../targets/cordova/tasks/platform'); const logger = require('../utils/logger'); const RSVP = require('rsvp'); +const _pick = require('lodash').pick; const Promise = RSVP.Promise; module.exports = Command.extend({ @@ -13,7 +14,9 @@ module.exports = Command.extend({ { name: 'name', type: String }, { name: 'cordovaid', type: String }, { name: 'template-path', type: String }, - { name: 'platform', type: String } + { name: 'platform', type: String }, + { name: 'crosswalk', type: Boolean, default: false }, + { name: 'uiwebview', type: Boolean, default: false } ], getPlatforms(platformStr) { @@ -35,39 +38,43 @@ module.exports = Command.extend({ return this.ui.prompt(promptOpts); }, - installPlatforms(platforms) { + installPlatforms(platforms, options) { let installs = []; + let installOpts = _pick(options, 'crosswalk', 'uiwebview'); + installOpts.save = true; + let addPlatform = new PlatformTask({ project: this.project }); platforms.forEach((platform) => { logger.info(`Installing ${platform}`); - installs.push(addPlatform.run('add', platform, { save: true })); + installs.push(addPlatform.run('add', platform, installOpts)); }); return RSVP.allSettled(installs); }, - run(options) { + run(opts) { let create = new CreateProject({ project: this.project, ui: this.ui, - cordovaId: options.cordovaId, - name: options.name, - templatePath: options.templatePath + cordovaId: opts.cordovaId, + name: opts.name, + templatePath: opts.templatePath }); return new Promise((resolve, reject) => { create.run().then(() => { - if (options.platform) { - let platforms = this.getPlatforms(options.platform); - return this.installPlatforms(platforms); + if (opts.platform) { + let platforms = this.getPlatforms(opts.platform); + this.installPlatforms(platforms); } else { return this.promptPlatform().then((selected) => { - if (selected.platforms[0] === 'none') { + let platforms = selected.platforms; + if (platforms[0] === 'none') { logger.success('Project created with no platforms'); } else { - return this.installPlatforms(selected.platforms).then(() => { + return this.installPlatforms(platforms, opts).then(() => { logger.success('Project created'); }); } diff --git a/lib/commands/platform.js b/lib/commands/platform.js index 3a2e6f6a..a61fae9f 100644 --- a/lib/commands/platform.js +++ b/lib/commands/platform.js @@ -13,7 +13,6 @@ module.exports = Command.extend({ /* eslint-disable max-len */ availableOptions: [ { name: 'save', type: Boolean, default: true }, - { name: 'default-webview', type: Boolean, default: false }, { name: 'crosswalk', type: Boolean, default: false }, { name: 'uiwebview', type: Boolean, default: false }, { name: 'link', type: String, default: undefined } diff --git a/lib/commands/serve.js b/lib/commands/serve.js index fb857c8e..715c35c2 100644 --- a/lib/commands/serve.js +++ b/lib/commands/serve.js @@ -68,7 +68,7 @@ module.exports = Command.extend({ //lets for live reload addon service this.project.targetIsCordova = true; this.project.targetIsCordovaLivereload = true; - this.project.CORDOVA_PLATFORM = opts.platform; + this.project.CORBER_PLATFORM = opts.platform; let hook = new Hook({ diff --git a/lib/commands/start.js b/lib/commands/start.js index faadc653..1d03f6d8 100644 --- a/lib/commands/start.js +++ b/lib/commands/start.js @@ -6,10 +6,12 @@ const CordovaRaw = require('../targets/cordova/tasks/raw'); const editXml = require('../targets/cordova/utils/edit-xml'); const getNetworkIp = require('../utils/get-network-ip'); const requireFramework = require('../utils/require-framework'); -const Promise = require('rsvp').Promise; +const RSVP = require('rsvp'); +const Promise = RSVP.Promise; const flatten = require('lodash').flatten; const listAndroidEms = require('../targets/android/tasks/list-emulators'); +const listAndroidDevices = require('../targets/android/tasks/list-devices'); const AndroidTarget = require('../targets/android/target'); const listIOSEms = require('../targets/ios/tasks/list-emulators'); const IOSTarget = require('../targets/ios/target'); @@ -22,14 +24,18 @@ module.exports = Command.extend({ description: 'Run app on device/emulator /w livereload', works: 'insideProject', + /* eslint-disable max-len */ availableOptions: [ { name: 'platform', type: String }, { name: 'emulator', type: String, default: '' }, { name: 'emulatorid', type: String }, { name: 'build', type: Boolean }, { name: 'reload-url', type: String, aliases: ['r'] }, - { name: 'port', type: Number, aliases: ['p'] } + { name: 'port', type: Number, aliases: ['p'] }, + { name: 'skip-framework-build', type: Boolean, default: false, aliases: ['sfb'] }, + { name: 'skip-cordova-build', type: Boolean, default: false, aliases: ['scb'] } ], + /* eslint-enable max-len */ getReloadUrl(port, reloadUrl, framework) { if (reloadUrl) { return reloadUrl; } @@ -42,28 +48,29 @@ module.exports = Command.extend({ return 'http://' + networkAddress + ':' + port; }, - selectEmulator(opts) { - let getEmulators = []; - if (opts.platform !== 'ios') { - getEmulators.push(listAndroidEms()); + selectDevice(opts, installedPlatforms) { + let foundDevices = []; + if (opts.platform !== 'ios' && installedPlatforms.includes('android')) { + foundDevices.push(listAndroidEms()); + foundDevices.push(listAndroidDevices()); } - if (opts.platform !== 'android') { - getEmulators.push(listIOSEms()); + if (opts.platform !== 'android' && installedPlatforms.includes('ios')) { + foundDevices.push(listIOSEms()); } - return Promise.all(getEmulators).then((emulators) => { - emulators = flatten(emulators); + return RSVP.all(foundDevices).then((devices) => { + devices = flatten(devices); if (opts.emulator === '') { let promptOpts = { type: 'list', - name: 'emulator', - message: 'Select an emulator', + name: 'device', + message: 'Select a device/emulator', pageSize: 30, choices: [] }; - emulators.forEach((em, i) => { + devices.forEach((em, i) => { promptOpts.choices.push({ key: i, name: em.label(), @@ -72,45 +79,59 @@ module.exports = Command.extend({ }); return this.ui.prompt(promptOpts).then((selected) => { - return selected.emulator; + return selected.device; }); } else { //Preference --emulator vs emulatorID if passed - let emulator; + let device; if (opts.emulator) { - emulator = emulators.find(function(em) { - if (em.name === opts.emulator) { return em; } - }); - } else { - emulator = emulators.find(function(em) { - if (em.id === opts.emulatorid) { return em; } + device = devices.find(function(d) { + if (d.name === opts.emulator) { return d; } }); } - return Promise.resolve(emulator); + return Promise.resolve(device); } }); }, + validatePlatform(installedPlatforms, targetPlatform) { + //If no target platfor mshow all installed platforms + if (targetPlatform && !installedPlatforms.includes(targetPlatform)) { + logger.error(` + You are passing platform=${targetPlatform} which is not installed. + Try running corber platform add ${targetPlatform}`); + } + }, + run(opts) { this._super.apply(this, arguments); logger.info('Corber Starting'); - return this.selectEmulator(opts).then((emulator) => { + let cdvTarget = new CordovaTarget({ + project: this.project + }); + + let installedPlatforms = cdvTarget.installedPlatforms(); + this.validatePlatform(installedPlatforms, opts.platform); + + return this.selectDevice(opts, installedPlatforms).then((device) => { //supports webpack addon this.project.targetIsCordova = true; - this.project.CORDOVA_PLATFORM = emulator.platform; + this.project.CORBER_PLATFORM = device.platform; this.project.targetIsCordovaLivereload = true; + cdvTarget.platform = device.platform; + let platformTarget; - if (emulator.platform === 'ios') { + if (device.platform === 'ios') { platformTarget = new IOSTarget({ - emulator: emulator, + device: device, project: this.project }); - } else if (emulator.platform === 'android') { + } else if (device.platform === 'android') { platformTarget = new AndroidTarget({ - emulator: emulator, + device: device, project: this.project }); } @@ -122,11 +143,6 @@ module.exports = Command.extend({ project: this.project }); - let cdvTarget = new CordovaTarget({ - platform: emulator.platform, - project: this.project - }); - let prepare = new CordovaRaw({ project: this.project, api: 'prepare' @@ -137,16 +153,24 @@ module.exports = Command.extend({ }); return new Promise((resolve, reject) => { - framework.validateServe(opts) + hook.run('beforeBuild') .then(() => editXml.addNavigation(this.project, reloadUrl)) - .then(() => createLivereloadShell.run(reloadUrl)) - .then(() => prepare.run({ platforms: [emulator.platform] })) - .then(() => hook.run('beforeBuild')) .then(() => cdvTarget.validateServe()) - .then(() => platformTarget.build()) + .then(() => framework.validateServe(opts)) + .then(() => createLivereloadShell.run(reloadUrl)) + .then(() => prepare.run({ platforms: [device.platform] })) + .then(function() { + if (opts.skipCordovaBuild !== true) { + return platformTarget.build(); + } + }) .then(() => hook.run('afterBuild')) .then(() => platformTarget.run()) - .then(() => framework.serve(opts, this.ui)) + .then(() => { + if (opts.skipFrameworkBuild !== true) { + return framework.serve(opts, this.ui); + } + }) .then(resolve).catch(reject); }); }); diff --git a/lib/frameworks/ember/tasks/serve.js b/lib/frameworks/ember/tasks/serve.js index b6adcfef..59a5d4cd 100644 --- a/lib/frameworks/ember/tasks/serve.js +++ b/lib/frameworks/ember/tasks/serve.js @@ -39,7 +39,7 @@ module.exports = Task.extend({ treeFor(treeName) { if (treeName !== 'addon') { return; } - let platform = cloneProject.CORDOVA_PLATFORM; + let platform = cloneProject.CORBER_PLATFORM; let projectPath = getPath(cloneProject); let assets = cordovaAssets.getPaths(platform, projectPath); diff --git a/lib/frameworks/react/validators/homepage.js b/lib/frameworks/react/validators/homepage.js index 6f8b6d74..cbcbbcf6 100644 --- a/lib/frameworks/react/validators/homepage.js +++ b/lib/frameworks/react/validators/homepage.js @@ -1,4 +1,5 @@ const Task = require('../../../tasks/-task'); +const path = require('path'); const getPackage = require('../../../utils/get-package'); const Promise = require('rsvp').Promise; const chalk = require('chalk'); @@ -17,7 +18,8 @@ module.exports = Task.extend({ }, run() { - let packageJSON = getPackage(this.root); + let packagePath = path.join(this.root, 'package.json'); + let packageJSON = getPackage(packagePath); let homepage = packageJSON.homepage; if (homepage === undefined || homepage && homepage[0] === '/') { diff --git a/lib/objects/device.js b/lib/objects/device.js new file mode 100644 index 00000000..54c3ae13 --- /dev/null +++ b/lib/objects/device.js @@ -0,0 +1,19 @@ +const CoreObject = require('core-object'); + +module.exports = CoreObject.extend({ + id: undefined, + uuid: undefined, + name: undefined, + platform: undefined, + deviceType: undefined, + state: undefined, + + label() { + let display = `${this.platform} ${this.deviceType} - ${this.name}`; + if (this.apiVersion) { + display += ` API: ${this.apiVersion}` + } + + return display; + } +}); diff --git a/lib/objects/emulator.js b/lib/objects/emulator.js deleted file mode 100644 index 0129e98d..00000000 --- a/lib/objects/emulator.js +++ /dev/null @@ -1,11 +0,0 @@ -const CoreObject = require('core-object'); - -module.exports = CoreObject.extend({ - id: undefined, - platform: undefined, - name: undefined, - - label() { - return `${this.platform} - ${this.name}`; - } -}); diff --git a/lib/objects/emulator/ios.js b/lib/objects/emulator/ios.js deleted file mode 100644 index 24806512..00000000 --- a/lib/objects/emulator/ios.js +++ /dev/null @@ -1,12 +0,0 @@ -const Emulator = require('../emulator'); - -module.exports = Emulator.extend({ - apiVersion: undefined, - uuid: undefined, - state: undefined, - - label() { - return `iOS ${this.apiVersion} ${this.name}`; - } -}); - diff --git a/lib/targets/android/target.js b/lib/targets/android/target.js index 03a98650..bbd14e85 100644 --- a/lib/targets/android/target.js +++ b/lib/targets/android/target.js @@ -1,19 +1,20 @@ const CoreObject = require('core-object'); -const path = require('path'); const bootEm = require('./tasks/boot-emulator'); -const installApp = require('./tasks/install-app'); +const installAppEm = require('./tasks/install-app-emulator'); +const installAppDevice = require('./tasks/install-app-device'); const launchApp = require('./tasks/launch-app'); +const getApkPath = require('./utils/apk-path'); const CordovaTarget = require('../cordova/target'); const cdvConfig = require('../cordova/utils/get-config'); const cdvPath = require('../cordova/utils/get-path'); module.exports = CoreObject.extend({ - emulator: undefined, + device: undefined, packageName: undefined, - project: undefined, + isDebug: true, init() { this._super(...arguments); @@ -22,21 +23,6 @@ module.exports = CoreObject.extend({ }); }, - apkPath() { - return path.join( - cdvPath(this.project, true), - 'cordova', - 'platforms', - 'android', - 'app', - 'build', - 'outputs', - 'apk', - 'debug', - 'app-debug.apk' - ); - }, - build() { let cdvTarget = new CordovaTarget({ platform: 'android', @@ -47,8 +33,16 @@ module.exports = CoreObject.extend({ }, run() { - return bootEm(this.emulator) - .then(() => installApp(this.apkPath(), this.emulator)) - .then(() => launchApp(this.packageName)); + let device = this.device; + return getApkPath(cdvPath(this.project), this.isDebug).then((apkPath) => { + if (device.deviceType === 'emulator') { + return bootEm(device) + .then(() => installAppEm(apkPath, device)) + .then(() => launchApp(this.packageName)); + } else { + return installAppDevice(device.uuid, apkPath) + .then(() => launchApp(this.packageName)); + } + }); } }); diff --git a/lib/targets/android/tasks/install-app-device.js b/lib/targets/android/tasks/install-app-device.js new file mode 100644 index 00000000..ea060a62 --- /dev/null +++ b/lib/targets/android/tasks/install-app-device.js @@ -0,0 +1,13 @@ +const spawn = require('../../../utils/spawn'); +const sdkPaths = require('../utils/sdk-paths'); + +module.exports = function(deviceUUID, apkPath) { + let adbPath = sdkPaths().adb; + + let install = [ + adbPath, + ['-s', deviceUUID, 'install', apkPath] + ]; + + return spawn.apply(null, install); +}; diff --git a/lib/targets/android/tasks/install-app.js b/lib/targets/android/tasks/install-app-emulator.js similarity index 100% rename from lib/targets/android/tasks/install-app.js rename to lib/targets/android/tasks/install-app-emulator.js diff --git a/lib/targets/android/tasks/list-devices.js b/lib/targets/android/tasks/list-devices.js new file mode 100644 index 00000000..748039f9 --- /dev/null +++ b/lib/targets/android/tasks/list-devices.js @@ -0,0 +1,40 @@ +const Device = require('../../../objects/device'); +const spawn = require('../../../utils/spawn'); +const sdkPaths = require('../utils/sdk-paths'); + +const deserializeDevices = function(list) { + let devices = []; + if (list === undefined) { return devices; } + + let items = list.split('\n'); + items = items.splice(1, items.length - 1); + + items.forEach((item) => { + if (item.trim() === '') { return; } + + let device = new Device({ + name: item.split('model:')[1].split(' ')[0], + uuid: item.split(' ')[0], + deviceType: 'device', + platform: 'android' + }); + + devices.push(device); + }); + + return devices; +}; + +module.exports = function() { + let adbPath = sdkPaths().adb; + + let list = [ + adbPath, + ['devices', '-l'] + ]; + + return spawn.apply(null, list).then((found) => { + let devices = deserializeDevices(found); + return devices.reverse(); + }); +}; diff --git a/lib/targets/android/tasks/list-emulators.js b/lib/targets/android/tasks/list-emulators.js index 68b01051..67e2af57 100644 --- a/lib/targets/android/tasks/list-emulators.js +++ b/lib/targets/android/tasks/list-emulators.js @@ -1,4 +1,4 @@ -const Emulator = require('../../../objects/emulator'); +const Device = require('../../../objects/device'); const spawn = require('../../../utils/spawn'); const sdkPaths = require('../utils/sdk-paths'); @@ -16,8 +16,9 @@ module.exports = function() { let listedEmulators = found.split('\n'); listedEmulators.forEach((emulator, i) => { if (emulator && emulator !== '') { - emulators.push(new Emulator({ + emulators.push(new Device({ name: emulator, + deviceType: 'emulator', platform: 'android' })); } diff --git a/lib/targets/android/utils/apk-path.js b/lib/targets/android/utils/apk-path.js new file mode 100644 index 00000000..65212feb --- /dev/null +++ b/lib/targets/android/utils/apk-path.js @@ -0,0 +1,41 @@ +const path = require('path'); +const spawn = require('../../../utils/spawn'); +const RSVP = require('rsvp'); + +const findApk = function(values) { + let files = values.split('\n'); + files = files.filter(function(file) { + if (file.match(/apk/)) { return file; } + }); + + //return the last modified apk + return files[files.length - 1]; +}; + +module.exports = function(root, isDebug) { + let buildType; + isDebug ? buildType = 'debug' : buildType = 'release'; + + //directory differs if build was with gradle vs studio + /* eslint-disable max-len */ + let basePath = path.join(root, 'platforms', 'android'); + let gradlePath = path.join(basePath, 'build', 'outputs', 'apk', buildType); + let studioPath = path.join(basePath, 'app', 'build', 'outputs', 'apk', buildType); + /* eslint-enable max-len */ + + let lookups = []; + lookups.push(spawn.apply(null, ['ls', ['-r', gradlePath]])); + lookups.push(spawn.apply(null, ['ls', ['-r', studioPath]])); + + return RSVP.allSettled(lookups).then(function(promises) { + if (promises[0].state === 'fulfilled') { + let apkName = findApk(promises[0].value); + return path.join(gradlePath, apkName); + } else if (promises[1].state === 'fulfilled') { + let apkName = findApk(promises[1].value); + return path.join(studioPath, apkName); + } else { + return RSVP.Promise.reject('No apk found'); + } + }); +}; diff --git a/lib/targets/cordova/target.js b/lib/targets/cordova/target.js index 64433b3f..8d0c3fd6 100644 --- a/lib/targets/cordova/target.js +++ b/lib/targets/cordova/target.js @@ -1,8 +1,12 @@ const CoreObject = require('core-object'); +const path = require('path'); const Build = require('./tasks/build'); const runValidators = require('../../utils/run-validators'); const ValidatePlugin = require('./validators/plugin'); const AllowNavigation = require('./validators/allow-navigation'); +const fsUtils = require('../../utils/fs-utils'); +const getPackage = require('../../utils/get-package'); +const cdvPath = require('./utils/get-path'); module.exports = CoreObject.extend({ platform: undefined, @@ -42,6 +46,16 @@ module.exports = CoreObject.extend({ return runValidators(validators); }, + installedPlatforms() { + let packagePath = path.join(cdvPath(this.project), 'package.json'); + if (fsUtils.existsSync(packagePath)) { + let packageJSON = getPackage(packagePath); + return packageJSON.cordova.platforms; + } else { + return []; + } + }, + build() { let cordovaBuild = new Build({ project: this.project, diff --git a/lib/targets/cordova/tasks/raw.js b/lib/targets/cordova/tasks/raw.js index ee17ef7a..3f00447f 100644 --- a/lib/targets/cordova/tasks/raw.js +++ b/lib/targets/cordova/tasks/raw.js @@ -29,7 +29,7 @@ module.exports = Task.extend({ // map default log level 'info' to cordova's default log level 'normal' cordovaLogger.setLevel(logLevel); } - if (logLevel === 'error' && this.project.CORDOVA_PLATFORM === 'android') { + if (logLevel === 'error' && this.project.CORBER_PLATFORM === 'android') { // set gradle log level args['0'].options.argv.push('--gradleArg=--quiet'); diff --git a/lib/targets/cordova/utils/cordova-validator.js b/lib/targets/cordova/utils/cordova-validator.js index 77f79af3..935aa63b 100644 --- a/lib/targets/cordova/utils/cordova-validator.js +++ b/lib/targets/cordova/utils/cordova-validator.js @@ -49,7 +49,7 @@ function CordovaValidator(opts) { /* eslint-disable max-len */ CordovaValidator.prototype.makeError = function(error) { let message = chalk.red('* cordova ' + this.type + ' ' + this.desiredKeyName + ' is missing or not installed: \n'); - message += chalk.grey('You probably need to run ember cdv:' + this.type + ' add ' + this.desiredKeyName + '. '); + message += chalk.grey('You probably need to run corber' + this.type + ' add ' + this.desiredKeyName + '. '); message += chalk.grey('cordova error: ' + error + '\n'); return message; }; diff --git a/lib/targets/ios/target.js b/lib/targets/ios/target.js index dfd0a60f..6cc13479 100644 --- a/lib/targets/ios/target.js +++ b/lib/targets/ios/target.js @@ -11,7 +11,7 @@ const cdvConfig = require('../cordova/utils/get-config'); const cdvPath = require('../cordova/utils/get-path'); module.exports = CoreObject.extend({ - emulator: undefined, + device: undefined, project: undefined, scheme: undefined, @@ -34,7 +34,7 @@ module.exports = CoreObject.extend({ build() { return buildEm( - this.emulator.uuid, + this.device.uuid, this.buildPath, this.scheme, this.iosPath @@ -47,9 +47,9 @@ module.exports = CoreObject.extend({ }, run() { - let emulatorId = this.emulator.uuid; + let emulatorId = this.device.uuid; - return bootEm(this.emulator) + return bootEm(this.device) .then(() => openEm()) .then(() => installApp(emulatorId, this.builtPath)) .then(() => launchApp(emulatorId, this.packageName)) diff --git a/lib/targets/ios/tasks/list-emulators.js b/lib/targets/ios/tasks/list-emulators.js index 97366c30..2cf5f6e8 100644 --- a/lib/targets/ios/tasks/list-emulators.js +++ b/lib/targets/ios/tasks/list-emulators.js @@ -1,5 +1,5 @@ const spawn = require('../../../utils/spawn'); -const IOSEmulator = require('../../../objects/emulator/ios'); +const Device = require('../../../objects/device'); const deserializeEmulators = function(iosVersion, list) { let emulators = []; @@ -9,8 +9,9 @@ const deserializeEmulators = function(iosVersion, list) { items.forEach((item) => { if (item.trim() === '') { return; } let split = item.replace(/\)/g, '').split('('); - let emulator = new IOSEmulator({ + let emulator = new Device({ platform: 'ios', + deviceType: 'emulator', apiVersion: iosVersion, name: split[0].trim(), uuid: split[split.length - 2].trim(), diff --git a/lib/utils/framework-type.js b/lib/utils/framework-type.js index 00088adb..a8ace042 100644 --- a/lib/utils/framework-type.js +++ b/lib/utils/framework-type.js @@ -1,9 +1,11 @@ const get = require('lodash').get; +const path = require('path'); const getPackage = require('./get-package'); module.exports = { get(root) { - let packageJSON = getPackage(root); + let packagePath = path.join(root, 'package.json'); + let packageJSON = getPackage(packagePath); let devDeps = packageJSON.devDependencies; let deps = packageJSON.dependencies; diff --git a/lib/utils/get-package.js b/lib/utils/get-package.js index 3a7ca249..176d4a94 100644 --- a/lib/utils/get-package.js +++ b/lib/utils/get-package.js @@ -1,5 +1,3 @@ -const path = require('path'); - -module.exports = function(root) { - return require(path.join(root, 'package.json')); +module.exports = function(packagePath) { + return require(packagePath); }; diff --git a/lib/utils/require-framework.js b/lib/utils/require-framework.js index 6fbb79af..a33131f8 100644 --- a/lib/utils/require-framework.js +++ b/lib/utils/require-framework.js @@ -8,7 +8,10 @@ module.exports = function(project) { project.root, 'corber/config/framework.js' )); } catch (err) { - logger.error('No corber/config/framework.js found'); + logger.error(` + No corber/config/framework.js found. + Have you run corber init? + `); throw err; } diff --git a/node-tests/unit/commands/init-test.js b/node-tests/unit/commands/init-test.js index ff1a0359..a5de40eb 100644 --- a/node-tests/unit/commands/init-test.js +++ b/node-tests/unit/commands/init-test.js @@ -87,7 +87,7 @@ describe('Init Command', function() { }); }); - it('does not call installPlatforms if the selected platfor is none', function() { + it('does not call installPlatforms if the selected platform is none', function() { let init = setupCmd(true); let installDouble = td.replace(init, 'installPlatforms'); opts.platform = undefined; @@ -131,5 +131,21 @@ describe('Init Command', function() { expect(calls).to.deep.equal(['ios', 'android']); }); }); + + it('passes webview and save options to the platform task', function() { + let calls = []; + + let PlatformTask = require('../../../lib/targets/cordova/tasks/platform'); + td.replace(PlatformTask.prototype, 'run', function(action, platform, opts) { + calls.push(opts); + return Promise.resolve(); + }); + + let init = setupCmd(); + + return init.installPlatforms(['ios', 'android'], {uiwebview: false, crosswalk: true}).then(function() { + expect(calls[0]).to.deep.equal({uiwebview: false, crosswalk: true, save: true}); + }); + }); }); }); diff --git a/node-tests/unit/commands/serve-test.js b/node-tests/unit/commands/serve-test.js index dbefc1e2..aec3c935 100644 --- a/node-tests/unit/commands/serve-test.js +++ b/node-tests/unit/commands/serve-test.js @@ -105,6 +105,15 @@ describe('Serve Command', function() { }).not.to.throw(Error); }); + it('sets vars for webpack livereload', function() { + return serveCmd.run({platform: 'ios'}).then(function() { + let project = mockProject.project; + expect(project.targetIsCordova).to.equal(true); + expect(project.targetIsCordovaLivereload).to.equal(true); + expect(project.CORBER_PLATFORM).to.equal('ios'); + }); + }); + it('runs tasks in the correct order', function() { return serveCmd.run({}).then(function() { expect(tasks).to.deep.equal([ diff --git a/node-tests/unit/commands/start-test.js b/node-tests/unit/commands/start-test.js index e8dad324..bb0cf3be 100644 --- a/node-tests/unit/commands/start-test.js +++ b/node-tests/unit/commands/start-test.js @@ -16,7 +16,8 @@ const iosEmulators = [{ return 'iOS Em 1 Label'; }, state: 'Booted', - platform: 'ios' + platform: 'ios', + deviceType: 'device' }, { id: 2, name: 'iOS Em 2', @@ -24,7 +25,8 @@ const iosEmulators = [{ return 'iOS Em 2 Label'; }, state: 'Shutdown', - platform: 'ios' + platform: 'ios', + deviceType: 'device' }]; const androidEmulators = [{ @@ -33,7 +35,18 @@ const androidEmulators = [{ label() { return 'Android Em 1 Label'; }, - platform: 'android' + platform: 'android', + deviceType: 'emulator' +}]; + +const androidDevices = [{ + id: 1, + name: 'Android Device 1', + label() { + return 'Android Device 1 Label'; + }, + platform: 'android', + deviceType: 'device' }]; const setupStart = function() { @@ -104,6 +117,10 @@ describe('Start Command', function() { return Promise.resolve(); }); + td.replace(CdvTarget.prototype, 'installedPlatforms', function() { + return ['ios']; + }); + td.replace(LRloadShell.prototype, 'run', function() { tasks.push('create-livereload-shell'); return Promise.resolve(); @@ -116,7 +133,7 @@ describe('Start Command', function() { start = setupStart(); - td.replace(start, 'selectEmulator', function() { + td.replace(start, 'selectDevice', function() { tasks.push('select-emulator'); return Promise.resolve({name: 'emulator', platform: 'ios'}); }); @@ -126,13 +143,30 @@ describe('Start Command', function() { return start.run({}).then(function() { expect(tasks).to.deep.equal([ 'select-emulator', - 'framework-validate-serve', + 'hook-beforeBuild', 'add-navigation', + 'cordova-validate-serve', + 'framework-validate-serve', 'create-livereload-shell', 'cordova-prepare', + 'platform-target-build', + 'hook-afterBuild', + 'platform-target-run', + 'framework-serve' + ]); + }); + }); + + it('skips platformTarget build with --scb', function() { + return start.run({skipCordovaBuild: true}).then(function() { + expect(tasks).to.deep.equal([ + 'select-emulator', 'hook-beforeBuild', + 'add-navigation', 'cordova-validate-serve', - 'platform-target-build', + 'framework-validate-serve', + 'create-livereload-shell', + 'cordova-prepare', 'hook-afterBuild', 'platform-target-run', 'framework-serve' @@ -140,10 +174,27 @@ describe('Start Command', function() { }); }); - it('makes the required changes to project', function() { + it('skips framework serve with --sfb', function() { + return start.run({skipFrameworkBuild: true}).then(function() { + expect(tasks).to.deep.equal([ + 'select-emulator', + 'hook-beforeBuild', + 'add-navigation', + 'cordova-validate-serve', + 'framework-validate-serve', + 'create-livereload-shell', + 'cordova-prepare', + 'platform-target-build', + 'hook-afterBuild', + 'platform-target-run' + ]); + }); + }); + + it('sets vars for webpack livereload', function() { return start.run({build: false, platform: 'ios'}).then(function() { expect(mockProject.project.targetIsCordova).to.equal(true); - expect(mockProject.project.CORDOVA_PLATFORM).to.equal('ios') + expect(mockProject.project.CORBER_PLATFORM).to.equal('ios') expect(mockProject.project.targetIsCordovaLivereload).to.equal(true); }); }); @@ -179,7 +230,7 @@ describe('Start Command', function() { }); }); - describe('selectEmulator', function() { + describe('selectDevice', function() { beforeEach(function() { td.replace('../../../lib/targets/ios/tasks/list-emulators', function() { return Promise.resolve(iosEmulators); @@ -188,6 +239,10 @@ describe('Start Command', function() { td.replace('../../../lib/targets/android/tasks/list-emulators', function() { return Promise.resolve(androidEmulators); }); + + td.replace('../../../lib/targets/android/tasks/list-devices', function() { + return Promise.resolve(androidDevices); + }); }); it('prompts for an emulator if one is not passed', function() { @@ -201,8 +256,8 @@ describe('Start Command', function() { } } - return start.selectEmulator({emulator: '', platform: 'ios'}).then(function() { - expect(promptArgs.message).to.equal('Select an emulator'); + return start.selectDevice({emulator: '', platform: 'ios'}, ['ios']).then(function() { + expect(promptArgs.message).to.equal('Select a device/emulator'); expect(promptArgs.type).to.equal('list'); expect(promptArgs.choices[0].value).to.deep.equal(iosEmulators[0]); expect(promptArgs.choices[1].value).to.deep.equal(iosEmulators[1]); @@ -212,19 +267,11 @@ describe('Start Command', function() { it('finds emulator by name', function() { let start = setupStart(); - return start.selectEmulator({emulator: 'iOS Em 1'}).then(function(selected) { + return start.selectDevice({emulator: 'iOS Em 1'}, ['ios']).then(function(selected) { expect(selected).to.deep.equal(iosEmulators[0]); }); }); - it('finds emulator by id', function() { - let start = setupStart(); - - return start.selectEmulator({emulatorid: 2}).then(function(selected) { - expect(selected).to.deep.equal(iosEmulators[1]); - }); - }); - it('only shows emulators for the selected platform', function() { let start = setupStart(); @@ -236,8 +283,8 @@ describe('Start Command', function() { } } - return start.selectEmulator({emulator: '', platform: 'android'}).then(function() { - expect(promptArgs.choices.length).to.equal(1); + return start.selectDevice({emulator: '', platform: 'android'}, ['android']).then(function() { + expect(promptArgs.choices.length).to.equal(2); }); }); @@ -252,9 +299,34 @@ describe('Start Command', function() { } } - return start.selectEmulator({emulator: ''}).then(function() { - expect(promptArgs.choices.length).to.equal(3); + return start.selectDevice({emulator: ''}, ['ios', 'android']).then(function() { + expect(promptArgs.choices.length).to.equal(4); }); }); }); + + describe('validatePlatform', function() { + afterEach(function() { + td.reset(); + }); + + it('throws an error when builds are for a platform that is not installed', function() { + let logger = td.replace('../../../lib/utils/logger'); + + + let start = setupStart(); + start.validatePlatform(['ios'], 'android'); + + td.verify(logger.error(td.matchers.anything())); + }); + + it('passes when builds are for an installed platform', function() { + let logger = td.replace('../../../lib/utils/logger'); + + let start = setupStart(); + start.validatePlatform(['ios'], 'ios'); + + td.verify(logger.error(), { times: 0 }); + }); + }); }); diff --git a/node-tests/unit/targets/android/target-test.js b/node-tests/unit/targets/android/target-test.js index 49ddf533..8fa04a84 100644 --- a/node-tests/unit/targets/android/target-test.js +++ b/node-tests/unit/targets/android/target-test.js @@ -4,15 +4,16 @@ const expect = require('../../../helpers/expect'); const mockProject = require('../../../fixtures/corber-mock/project'); const libPath = '../../../../lib'; -const Emulator = require(`${libPath}/objects/emulator`); +const Device = require(`${libPath}/objects/device`); const setupTarget = function() { let AndroidTarget = require(`${libPath}/targets/android/target`); return new AndroidTarget({ - emulator: new Emulator({ + device: new Device({ name: 'Emultor', uuid: 'uuid', - platform: 'android' + platform: 'android', + deviceType: 'emulator' }), project: mockProject.project }); @@ -63,16 +64,23 @@ describe('Android Target', function() { context('run', function() { - it('runs tasks in the correct order', function() { - let tasks = []; + let tasks; + + beforeEach(function() { + tasks = []; td.replace(`${libPath}/targets/android/tasks/boot-emulator`, function() { tasks.push('boot-emulator'); return Promise.resolve(); }); - td.replace(`${libPath}/targets/android/tasks/install-app`, function() { - tasks.push('install-app'); + td.replace(`${libPath}/targets/android/tasks/install-app-emulator`, function() { + tasks.push('install-app-emulator'); + return Promise.resolve(); + }); + + td.replace(`${libPath}/targets/android/tasks/install-app-device`, function() { + tasks.push('install-app-device'); return Promise.resolve(); }); @@ -81,11 +89,33 @@ describe('Android Target', function() { return Promise.resolve(); }); + td.replace(`${libPath}/targets/android/utils/apk-path`, function() { + tasks.push('apk-path'); + return Promise.resolve('apk-path'); + }); + }); + + it('deviceType: device runs tasks in the correct order', function() { let target = setupTarget(); + target.device.deviceType = 'device'; + + return target.run().then(function() { + expect(tasks).to.deep.equal([ + 'apk-path', + 'install-app-device', + 'launch-app' + ]); + }); + }); + + it('deviceType: emulator runs tasks in the correct order', function() { + let target = setupTarget(); + return target.run().then(function() { expect(tasks).to.deep.equal([ + 'apk-path', 'boot-emulator', - 'install-app', + 'install-app-emulator', 'launch-app' ]); }); diff --git a/node-tests/unit/targets/android/tasks/install-app-device-test.js b/node-tests/unit/targets/android/tasks/install-app-device-test.js new file mode 100644 index 00000000..7bd61bcf --- /dev/null +++ b/node-tests/unit/targets/android/tasks/install-app-device-test.js @@ -0,0 +1,32 @@ +const td = require('testdouble'); + +describe('Android Install App - Device', function() { + beforeEach(function() { + td.replace('../../../../../lib/targets/android/utils/sdk-paths', function() { + return { + adb: 'adbPath' + } + }); + }); + + afterEach(function() { + td.reset(); + }); + + it('spawns adb install', function() { + let spawnDouble = td.replace('../../../../../lib/utils/spawn'); + let installApp = require('../../../../../lib/targets/android/tasks/install-app-device'); + + installApp('uuid', 'apk-path'); + + td.verify(spawnDouble( + 'adbPath', + [ + '-s', + 'uuid', + 'install', + 'apk-path' + ] + )); + }); +}); diff --git a/node-tests/unit/targets/android/tasks/install-app-test.js b/node-tests/unit/targets/android/tasks/install-app-emulator-test.js similarity index 89% rename from node-tests/unit/targets/android/tasks/install-app-test.js rename to node-tests/unit/targets/android/tasks/install-app-emulator-test.js index 3a704739..a2133398 100644 --- a/node-tests/unit/targets/android/tasks/install-app-test.js +++ b/node-tests/unit/targets/android/tasks/install-app-emulator-test.js @@ -13,9 +13,9 @@ describe('Android Install App', function() { td.reset(); }); - it('spawns adb kill', function() { + it('spawns adb install', function() { let spawnDouble = td.replace('../../../../../lib/utils/spawn'); - let installApp = require('../../../../../lib/targets/android/tasks/install-app'); + let installApp = require('../../../../../lib/targets/android/tasks/install-app-emulator'); installApp('apk-path'); diff --git a/node-tests/unit/targets/android/tasks/list-devices-test.js b/node-tests/unit/targets/android/tasks/list-devices-test.js new file mode 100644 index 00000000..c2d1050c --- /dev/null +++ b/node-tests/unit/targets/android/tasks/list-devices-test.js @@ -0,0 +1,34 @@ +const td = require('testdouble'); +const Promise = require('rsvp').Promise; +const expect = require('../../../../helpers/expect'); +const Device = require('../../../../../lib/objects/device'); + +describe('Android List Device Task', function() { + afterEach(function() { + td.reset(); + }); + + it('lints out emulators, ignoring non iOS devices', function() { + td.replace('../../../../../lib/utils/spawn', function(cmd, args) { + let deviceList = `List of Devices Attached \nuuid device usb:337641472X product:jfltevl model:SGH_I337M device:jfltecan transport_id:1 \n`; + return Promise.resolve(deviceList); + }); + + td.replace('../../../../../lib/targets/android/utils/sdk-paths', function() { + return { + adb: 'fakeAdb' + } + }); + + let list = require('../../../../../lib/targets/android/tasks/list-devices'); + + return list().then(function(found) { + expect(found).to.deep.equal([new Device({ + name: 'SGH_I337M', + uuid: 'uuid', + platform: 'android', + deviceType: 'device' + })]); + }); + }); +}); diff --git a/node-tests/unit/targets/android/tasks/list-emulators-test.js b/node-tests/unit/targets/android/tasks/list-emulators-test.js index 8ae27f70..bc61ebda 100644 --- a/node-tests/unit/targets/android/tasks/list-emulators-test.js +++ b/node-tests/unit/targets/android/tasks/list-emulators-test.js @@ -1,7 +1,7 @@ const td = require('testdouble'); const expect = require('../../../../helpers/expect'); const Promise = require('rsvp').Promise; -const AndroidEm = require('../../../../../lib/objects/emulator'); +const Device = require('../../../../../lib/objects/device'); const emList = 'Nexus_5X_API_27\nPixel_2_API_27'; @@ -44,13 +44,15 @@ describe('Android List Emulators', function() { return listEms().then(function(found) { expect(found).to.deep.equal([ - new AndroidEm({ + new Device({ name: 'Pixel_2_API_27', - platform: 'android' + platform: 'android', + deviceType: 'emulator' }), - new AndroidEm({ + new Device({ name: 'Nexus_5X_API_27', - platform: 'android' + platform: 'android', + deviceType: 'emulator' })]); }); }); diff --git a/node-tests/unit/targets/android/utils/apk-path-test.js b/node-tests/unit/targets/android/utils/apk-path-test.js new file mode 100644 index 00000000..267b3d53 --- /dev/null +++ b/node-tests/unit/targets/android/utils/apk-path-test.js @@ -0,0 +1,43 @@ +const td = require('testdouble'); +const Promise = require('rsvp').Promise; +const expect = require('../../../../helpers/expect'); + +describe('Android apk paths util', function() { + afterEach(function() { + td.reset(); + }); + + it('checks gradle & studio dirs', function() { + let calls = []; + td.replace('../../../../../lib/utils/spawn', function() { + calls.push([...arguments]); + return Promise.resolve('fake-debug.apk'); + }); + let apkPath = require('../../../../../lib/targets/android/utils/apk-path'); + + return apkPath('fakePath', true).then(function() { + expect(calls[0]).to.deep.equal(['ls', ['-r', 'fakePath/platforms/android/build/outputs/apk/debug']]); + expect(calls[1]).to.deep.equal(['ls', ['-r', 'fakePath/platforms/android/app/build/outputs/apk/debug']]); + }); + }); + + it('returns a matched apk file', function() { + td.replace('../../../../../lib/utils/spawn', function() { + return Promise.resolve('fake-debug.apk'); + }); + let apkPath = require('../../../../../lib/targets/android/utils/apk-path'); + + return apkPath('fakePath', true).then(function(found) { + expect(found).to.equal('fakePath/platforms/android/build/outputs/apk/debug/fake-debug.apk'); + }); + }); + + it('rejects if nothing was returned', function() { + td.replace('../../../../../lib/utils/spawn', function() { + return Promise.reject(); + }); + let apkPath = require('../../../../../lib/targets/android/utils/apk-path'); + + return expect(apkPath('fakePath', true)).to.eventually.be.rejected; + }); +}); diff --git a/node-tests/unit/targets/cordova/target-test.js b/node-tests/unit/targets/cordova/target-test.js index 50ad9e3b..a551c1eb 100644 --- a/node-tests/unit/targets/cordova/target-test.js +++ b/node-tests/unit/targets/cordova/target-test.js @@ -55,6 +55,30 @@ describe('Cordova Target', function() { }); }); + context('installedPlatforms', function() { + it('returns platforms in cordova package.json', function() { + let fsUtils = require('../../../../lib/utils/fs-utils'); + td.replace(fsUtils, 'existsSync', function() { + return true; + }); + + td.replace('../../../../lib/utils/get-package', function() { + return { + cordova: { + platforms: ['ios', 'android'] + } + }; + }); + + let CordovaTarget = require('../../../../lib/targets/cordova/target'); + let target = new CordovaTarget({ + project: mockProject.project + }); + + let installed = target.installedPlatforms(); + expect(installed).to.deep.equal(['ios', 'android']); + }); + }); context('build', function() { it('runs cordova build task', function() { diff --git a/node-tests/unit/targets/ios/target-test.js b/node-tests/unit/targets/ios/target-test.js index b98c329c..b1ba1125 100644 --- a/node-tests/unit/targets/ios/target-test.js +++ b/node-tests/unit/targets/ios/target-test.js @@ -5,16 +5,17 @@ const expect = require('../../../helpers/expect'); const mockProject = require('../../../fixtures/corber-mock/project'); const libPath = '../../../../lib'; -const IOSEmulator = require(`${libPath}/objects/emulator/ios`); +const Device = require(`${libPath}/objects/device`); const setupTarget = function() { let IOSTarget = require(`${libPath}/targets/ios/target`); return new IOSTarget({ - emulator: new IOSEmulator({ + device: new Device({ apiVersion: '11.1', name: 'iPad Pro', uuid: 'uuid', platform: 'ios', + deviceType: 'emulator', state: 'Booted' }), project: mockProject.project diff --git a/node-tests/unit/targets/ios/tasks/list-emulators-test.js b/node-tests/unit/targets/ios/tasks/list-emulators-test.js index d458fdca..cc152680 100644 --- a/node-tests/unit/targets/ios/tasks/list-emulators-test.js +++ b/node-tests/unit/targets/ios/tasks/list-emulators-test.js @@ -1,7 +1,7 @@ const td = require('testdouble'); const Promise = require('rsvp').Promise; const expect = require('../../../../helpers/expect'); -const IOSEmulator = require('../../../../../lib/objects/emulator/ios'); +const Device = require('../../../../../lib/objects/device'); describe('iOS List Emulator Task', function() { afterEach(function() { @@ -33,41 +33,47 @@ describe('iOS List Emulator Task', function() { let list = require('../../../../../lib/targets/ios/tasks/list-emulators'); return list().then(function(found) { - expect(found).to.deep.equal([new IOSEmulator({ + expect(found).to.deep.equal([new Device({ apiVersion: '11.1', name: 'iPad Pro', uuid: 'uuid', platform: 'ios', + deviceType: 'emulator', state: 'Shutdown' - }), new IOSEmulator({ + }), new Device({ apiVersion: '11.1', name: 'iPhone X', uuid: '3B388D0A-01F2-4E68-B86B-55FDB6F96B37', platform: 'ios', + deviceType: 'emulator', state: 'Shutdown' - }), new IOSEmulator({ + }), new Device({ apiVersion: '9.1', name: 'iPhone 5', uuid: 'uuid', platform: 'ios', + deviceType: 'emulator', state: 'Shutdown' - }), new IOSEmulator({ + }), new Device({ apiVersion: '9.1', name: 'iPhone 4s', uuid: 'uuid', platform: 'ios', + deviceType: 'emulator', state: 'Shutdown' - }), new IOSEmulator({ + }), new Device({ apiVersion: '8.4', name: 'iPhone 5', uuid: 'uuid', platform: 'ios', + deviceType: 'emulator', state: 'Shutdown' - }), new IOSEmulator({ + }), new Device({ apiVersion: '8.4', name: 'iPhone 4s', uuid: 'uuid', platform: 'ios', + deviceType: 'emulator', state: 'Shutdown' })]); }); diff --git a/node-tests/unit/utils/get-package-test.js b/node-tests/unit/utils/get-package-test.js index 8f74410a..b4b9913e 100644 --- a/node-tests/unit/utils/get-package-test.js +++ b/node-tests/unit/utils/get-package-test.js @@ -1,4 +1,5 @@ const expect = require('../../helpers/expect'); +const path = require('path'); const mockProject = require('../../fixtures/corber-mock/project'); const getPackage = require('../../../lib/utils/get-package'); @@ -6,8 +7,9 @@ const getPackage = require('../../../lib/utils/get-package'); const root = mockProject.project.root; describe('getPackage', function() { - it('attempts to read package.json at root', function() { - let packageJSON = getPackage(root); + it('attempts to requre package path', function() { + const packagePath = path.join(root, 'package.json'); + let packageJSON = getPackage(packagePath); expect(packageJSON.name).to.equal('mock-project'); }); });