Merge branch 'master' into StudioProjectCompat

This will have to be linted
This commit is contained in:
Joe Bowser 2017-06-26 10:29:31 -07:00
commit e456175a81
42 changed files with 3130 additions and 3262 deletions

10
.eslintrc.yml Normal file
View File

@ -0,0 +1,10 @@
root: true
extends: semistandard
rules:
indent:
- error
- 4
camelcase: off
padded-blocks: off
operator-linebreak: off
no-throw-literal: off

View File

@ -15,7 +15,7 @@ android:
- tools - tools
- tools - tools
script: script:
- npm run jshint - npm run eslint
- npm run cover - npm run cover
- npm run test-build - npm run test-build
after_script: after_script:

View File

@ -19,12 +19,12 @@
under the License. under the License.
*/ */
var shell = require('shelljs'), var shell = require('shelljs');
Q = require('q'), var Q = require('q');
path = require('path'), var path = require('path');
fs = require('fs'), var fs = require('fs');
check_reqs = require('./../templates/cordova/lib/check_reqs'), var check_reqs = require('./../templates/cordova/lib/check_reqs');
ROOT = path.join(__dirname, '..', '..'); var ROOT = path.join(__dirname, '..', '..');
var MIN_SDK_VERSION = 19; var MIN_SDK_VERSION = 19;
@ -62,9 +62,9 @@ function copyJsAndLibrary(projectPath, shared, projectName, isLegacy) {
// We need these files to build cordova.js if using browserify method. // We need these files to build cordova.js if using browserify method.
shell.cp('-rf', path.join(ROOT, 'cordova-js-src'), path.join(projectPath, 'platform_www')); shell.cp('-rf', path.join(ROOT, 'cordova-js-src'), path.join(projectPath, 'platform_www'));
// Don't fail if there are no old jars, because there hasn't been cordova JARs for years! // Don't fail if there are no old jars.
setShellFatal(false, function () { setShellFatal(false, function () {
shell.ls(path.join(app_path, 'libs', 'cordova-*.jar')).forEach(function(oldJar) { shell.ls(path.join(projectPath, 'libs', 'cordova-*.jar')).forEach(function (oldJar) {
console.log('Deleting ' + oldJar); console.log('Deleting ' + oldJar);
shell.rm('-f', oldJar); shell.rm('-f', oldJar);
}); });
@ -116,9 +116,8 @@ function writeProjectProperties(projectPath, target_api) {
var subProjects = extractSubProjectPaths(data); var subProjects = extractSubProjectPaths(data);
subProjects = subProjects.filter(function (p) { subProjects = subProjects.filter(function (p) {
return !(/^CordovaLib$/m.exec(p) || return !(/^CordovaLib$/m.exec(p) ||
/[\\\/]cordova-android[\\\/]framework$/m.exec(p) || /[\\/]cordova-android[\\/]framework$/m.exec(p) ||
/^(\.\.[\\\/])+framework$/m.exec(p) /^(\.\.[\\/])+framework$/m.exec(p));
);
}); });
subProjects.unshift('CordovaLib'); subProjects.unshift('CordovaLib');
data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, ''); data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, '');
@ -326,7 +325,7 @@ exports.create = function(project_path, config, options, events) {
function generateDoneMessage (type, link) { function generateDoneMessage (type, link) {
var pkg = require('../../package'); var pkg = require('../../package');
var msg = 'Android project ' + (type == 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version; var msg = 'Android project ' + (type === 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version;
if (link) { if (link) {
msg += ' and has a linked CordovaLib'; msg += ' and has a linked CordovaLib';
} }
@ -372,7 +371,6 @@ exports.update = function(projectPath, options, events) {
}).thenResolve(projectPath); }).thenResolve(projectPath);
}; };
// For testing // For testing
exports.validatePackageName = validatePackageName; exports.validatePackageName = validatePackageName;
exports.validateProjectName = validateProjectName; exports.validateProjectName = validateProjectName;

View File

@ -29,7 +29,6 @@ var selfEvents = require('cordova-common').events;
var PLATFORM = 'android'; var PLATFORM = 'android';
function setupEvents (externalEventEmitter) { function setupEvents (externalEventEmitter) {
if (externalEventEmitter) { if (externalEventEmitter) {
// This will make the platform internal events visible outside // This will make the platform internal events visible outside
@ -43,7 +42,6 @@ function setupEvents(externalEventEmitter) {
return selfEvents; return selfEvents;
} }
/** /**
* Class, that acts as abstraction over particular platform. Encapsulates the * Class, that acts as abstraction over particular platform. Encapsulates the
* platform's properties and methods. * platform's properties and methods.
@ -117,14 +115,11 @@ Api.createPlatform = function (destination, config, options, events) {
events = setupEvents(events); events = setupEvents(events);
var result; var result;
try { try {
result = require('../../lib/create') result = require('../../lib/create').create(destination, config, options, events).then(function (destination) {
.create(destination, config, options, events)
.then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api')); var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi(PLATFORM, destination, events); return new PlatformApi(PLATFORM, destination, events);
}); });
} } catch (e) {
catch (e) {
events.emit('error', 'createPlatform is not callable from the android project API.'); events.emit('error', 'createPlatform is not callable from the android project API.');
throw (e); throw (e);
} }
@ -151,14 +146,11 @@ Api.updatePlatform = function (destination, options, events) {
events = setupEvents(events); events = setupEvents(events);
var result; var result;
try { try {
result = require('../../lib/create') result = require('../../lib/create').update(destination, options, events).then(function (destination) {
.update(destination, options, events)
.then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api')); var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi('android', destination, events); return new PlatformApi('android', destination, events);
}); });
} } catch (e) {
catch (e) {
events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.'); events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.');
throw (e); throw (e);
} }
@ -235,12 +227,10 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
installOptions.android_studio = true; installOptions.android_studio = true;
} }
return Q() return Q().then(function () {
.then(function () {
// CB-11964: Do a clean when installing the plugin code to get around // CB-11964: Do a clean when installing the plugin code to get around
// the Gradle bug introduced by the Android Gradle Plugin Version 2.2 // the Gradle bug introduced by the Android Gradle Plugin Version 2.2
// TODO: Delete when the next version of Android Gradle plugin comes out // TODO: Delete when the next version of Android Gradle plugin comes out
// Since clean doesn't just clean the build, it also wipes out www, we need // Since clean doesn't just clean the build, it also wipes out www, we need
// to pass additional options. // to pass additional options.
@ -253,14 +243,10 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
if (!AndroidStudio.isAndroidStudioProject(self.root) && !project.isClean()) { if (!AndroidStudio.isAndroidStudioProject(self.root) && !project.isClean()) {
return self.clean(opts); return self.clean(opts);
} }
}) }).then(function () {
.then(function () { return PluginManager.get(self.platform, self.locations, project).addPlugin(plugin, installOptions);
return PluginManager.get(self.platform, self.locations, project) }).then(function () {
.addPlugin(plugin, installOptions);
})
.then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return; if (plugin.getFrameworks(this.platform).length === 0) return;
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>'); selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
//This should pick the correct builder, not just get gradle //This should pick the correct builder, not just get gradle
require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles(); require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles();
@ -351,11 +337,9 @@ Api.prototype.build = function (buildOptions) {
var self = this; var self = this;
if(this.android_studio) if(this.android_studio)
buildOptions.studio = true; buildOptions.studio = true;
return require('./lib/check_reqs').run() return require('./lib/check_reqs').run() .then(function () {
.then(function () {
return require('./lib/build').run.call(self, buildOptions); return require('./lib/build').run.call(self, buildOptions);
}) }).then(function (buildResults) {
.then(function (buildResults) {
// Cast build result to array of build artifacts // Cast build result to array of build artifacts
return buildResults.apkPaths.map(function (apkPath) { return buildResults.apkPaths.map(function (apkPath) {
return { return {
@ -382,8 +366,7 @@ Api.prototype.build = function (buildOptions) {
*/ */
Api.prototype.run = function (runOptions) { Api.prototype.run = function (runOptions) {
var self = this; var self = this;
return require('./lib/check_reqs').run() return require('./lib/check_reqs').run().then(function () {
.then(function () {
return require('./lib/run').run.call(self, runOptions); return require('./lib/run').run.call(self, runOptions);
}); });
}; };
@ -397,17 +380,13 @@ Api.prototype.run = function(runOptions) {
*/ */
Api.prototype.clean = function (cleanOptions) { Api.prototype.clean = function (cleanOptions) {
var self = this; var self = this;
return require('./lib/check_reqs').run() return require('./lib/check_reqs').run().then(function () {
.then(function () {
return require('./lib/build').runClean.call(self, cleanOptions); return require('./lib/build').runClean.call(self, cleanOptions);
}) }).then(function () {
.then(function () {
return require('./lib/prepare').clean.call(self, cleanOptions); return require('./lib/prepare').clean.call(self, cleanOptions);
}); });
}; };
/** /**
* Performs a requirements check for current platform. Each platform defines its * Performs a requirements check for current platform. Each platform defines its
* own set of requirements, which should be resolved before platform can be * own set of requirements, which should be resolved before platform can be

View File

@ -44,8 +44,7 @@ function isEmulator(line) {
* devices/emulators * devices/emulators
*/ */
Adb.devices = function (opts) { Adb.devices = function (opts) {
return spawn('adb', ['devices'], {cwd: os.tmpdir()}) return spawn('adb', ['devices'], {cwd: os.tmpdir()}).then(function (output) {
.then(function(output) {
return output.split('\n').filter(function (line) { return output.split('\n').filter(function (line) {
// Filter out either real devices or emulators, depending on options // Filter out either real devices or emulators, depending on options
return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line); return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line);
@ -59,8 +58,7 @@ Adb.install = function (target, packagePath, opts) {
events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...'); events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
var args = ['-s', target, 'install']; var args = ['-s', target, 'install'];
if (opts && opts.replace) args.push('-r'); if (opts && opts.replace) args.push('-r');
return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()}) return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()}).then(function (output) {
.then(function(output) {
// 'adb install' seems to always returns no error, even if installation fails // 'adb install' seems to always returns no error, even if installation fails
// so we catching output to detect installation failure // so we catching output to detect installation failure
if (output.match(/Failure/)) { if (output.match(/Failure/)) {
@ -86,8 +84,7 @@ Adb.shell = function (target, shellCommand) {
events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...'); events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
var args = ['-s', target, 'shell']; var args = ['-s', target, 'shell'];
shellCommand = shellCommand.split(/\s+/); shellCommand = shellCommand.split(/\s+/);
return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()}) return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()}).catch(function (output) {
.catch(function (output) {
return Q.reject(new CordovaError('Failed to execute shell command "' + return Q.reject(new CordovaError('Failed to execute shell command "' +
shellCommand + '"" on device: ' + output)); shellCommand + '"" on device: ' + output));
}); });
@ -95,8 +92,7 @@ Adb.shell = function (target, shellCommand) {
Adb.start = function (target, activityName) { Adb.start = function (target, activityName) {
events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...'); events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...');
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName) return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName).catch(function (output) {
.catch(function (output) {
return Q.reject(new CordovaError('Failed to start application "' + return Q.reject(new CordovaError('Failed to start application "' +
activityName + '"" on device: ' + output)); activityName + '"" on device: ' + output));
}); });

View File

@ -102,8 +102,7 @@ AndroidManifest.prototype.getActivity = function() {
}; };
}; };
['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'] ['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'].forEach(function (sdkPrefName) {
.forEach(function(sdkPrefName) {
// Copy variable reference to avoid closure issues // Copy variable reference to avoid closure issues
var prefName = sdkPrefName; var prefName = sdkPrefName;

View File

@ -28,8 +28,7 @@ var projectFileCache = {};
function addToPropertyList (projectProperties, key, value) { function addToPropertyList (projectProperties, key, value) {
var i = 1; var i = 1;
while (projectProperties.get(key + '.' + i)) while (projectProperties.get(key + '.' + i)) { i++; }
i++;
projectProperties.set(key + '.' + i, value); projectProperties.set(key + '.' + i, value);
projectProperties.dirty = true; projectProperties.dirty = true;
@ -54,7 +53,7 @@ function removeFromPropertyList(projectProperties, key, value) {
function getRelativeLibraryPath (parentDir, subDir) { function getRelativeLibraryPath (parentDir, subDir) {
var libraryPath = path.relative(parentDir, subDir); var libraryPath = path.relative(parentDir, subDir);
return (path.sep == '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath; return (path.sep === '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
} }
function AndroidProject (projectDir) { function AndroidProject (projectDir) {

View File

@ -17,8 +17,8 @@
under the License. under the License.
*/ */
var Q = require('q'), var Q = require('q');
superspawn = require('cordova-common').superspawn; var superspawn = require('cordova-common').superspawn;
var suffix_number_regex = /(\d+)$/; var suffix_number_regex = /(\d+)$/;
// Used for sorting Android targets, example strings to sort: // Used for sorting Android targets, example strings to sort:
@ -44,8 +44,7 @@ function sort_by_largest_numerical_suffix(a, b) {
} }
module.exports.print_newest_available_sdk_target = function () { module.exports.print_newest_available_sdk_target = function () {
return module.exports.list_targets() return module.exports.list_targets().then(function (targets) {
.then(function(targets) {
targets.sort(sort_by_largest_numerical_suffix); targets.sort(sort_by_largest_numerical_suffix);
console.log(targets[0]); console.log(targets[0]);
}); });
@ -78,26 +77,22 @@ function parse_targets(output) {
} }
module.exports.list_targets_with_android = function () { module.exports.list_targets_with_android = function () {
return superspawn.spawn('android', ['list', 'target']) return superspawn.spawn('android', ['list', 'target']).then(parse_targets);
.then(parse_targets);
}; };
module.exports.list_targets_with_avdmanager = function () { module.exports.list_targets_with_avdmanager = function () {
return superspawn.spawn('avdmanager', ['list', 'target']) return superspawn.spawn('avdmanager', ['list', 'target']).then(parse_targets);
.then(parse_targets);
}; };
module.exports.list_targets = function () { module.exports.list_targets = function () {
return module.exports.list_targets_with_avdmanager() return module.exports.list_targets_with_avdmanager().catch(function (err) {
.catch(function(err) {
// If there's an error, like avdmanager could not be found, we can try // If there's an error, like avdmanager could not be found, we can try
// as a last resort, to run `android`, in case this is a super old // as a last resort, to run `android`, in case this is a super old
// SDK installation. // SDK installation.
if (err && (err.code == 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) { if (err && (err.code === 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) {
return module.exports.list_targets_with_android(); return module.exports.list_targets_with_android();
} else throw err; } else throw err;
}) }).then(function (targets) {
.then(function(targets) {
if (targets.length === 0) { if (targets.length === 0) {
return Q.reject(new Error('No android targets (SDKs) installed!')); return Q.reject(new Error('No android targets (SDKs) installed!'));
} }

View File

@ -19,10 +19,10 @@
under the License. under the License.
*/ */
var Q = require('q'), var Q = require('q');
path = require('path'), var path = require('path');
fs = require('fs'), var fs = require('fs');
nopt = require('nopt'); var nopt = require('nopt');
var Adb = require('./Adb'); var Adb = require('./Adb');
@ -64,11 +64,9 @@ function parseOpts(options, resolvedTarget, projectRoot) {
if (options.nobuild) ret.buildMethod = 'none'; if (options.nobuild) ret.buildMethod = 'none';
if (options.argv.versionCode) if (options.argv.versionCode) { ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode); }
ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode);
if (options.argv.minSdkVersion) if (options.argv.minSdkVersion) { ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion); }
ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion);
if (options.argv.gradleArg) { if (options.argv.gradleArg) {
ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg); ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg);
@ -76,12 +74,10 @@ function parseOpts(options, resolvedTarget, projectRoot) {
var packageArgs = {}; var packageArgs = {};
if (options.argv.keystore) if (options.argv.keystore) { packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore)); }
packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore));
['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (flagName) { ['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (flagName) {
if (options.argv[flagName]) if (options.argv[flagName]) { packageArgs[flagName] = options.argv[flagName]; }
packageArgs[flagName] = options.argv[flagName];
}); });
var buildConfig = options.buildConfig; var buildConfig = options.buildConfig;
@ -132,8 +128,7 @@ function parseOpts(options, resolvedTarget, projectRoot) {
module.exports.runClean = function (options) { module.exports.runClean = function (options) {
var opts = parseOpts(options, null, this.root); var opts = parseOpts(options, null, this.root);
var builder = builders.getBuilder(opts.buildMethod); var builder = builders.getBuilder(opts.buildMethod);
return builder.prepEnv(opts) return builder.prepEnv(opts).then(function () {
.then(function() {
return builder.clean(opts); return builder.clean(opts);
}); });
}; };
@ -154,14 +149,12 @@ module.exports.run = function(options, optResolvedTarget) {
var opts = parseOpts(options, optResolvedTarget, this.root); var opts = parseOpts(options, optResolvedTarget, this.root);
console.log(opts.buildMethod); console.log(opts.buildMethod);
var builder = builders.getBuilder(opts.buildMethod); var builder = builders.getBuilder(opts.buildMethod);
return builder.prepEnv(opts) return builder.prepEnv(opts).then(function () {
.then(function() {
if (opts.prepEnv) { if (opts.prepEnv) {
events.emit('verbose', 'Build file successfully prepared.'); events.emit('verbose', 'Build file successfully prepared.');
return; return;
} }
return builder.build(opts) return builder.build(opts).then(function () {
.then(function() {
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch); var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t')); events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t'));
return { return {
@ -179,31 +172,24 @@ module.exports.run = function(options, optResolvedTarget) {
*/ */
module.exports.detectArchitecture = function (target) { module.exports.detectArchitecture = function (target) {
function helper () { function helper () {
return Adb.shell(target, 'cat /proc/cpuinfo') return Adb.shell(target, 'cat /proc/cpuinfo').then(function (output) {
.then(function(output) {
return /intel/i.exec(output) ? 'x86' : 'arm'; return /intel/i.exec(output) ? 'x86' : 'arm';
}); });
} }
// It sometimes happens (at least on OS X), that this command will hang forever. // It sometimes happens (at least on OS X), that this command will hang forever.
// To fix it, either unplug & replug device, or restart adb server. // To fix it, either unplug & replug device, or restart adb server.
return helper() return helper().timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.')).then(null, function (err) {
.timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.'))
.then(null, function(err) {
if (/timed out/.exec('' + err)) { if (/timed out/.exec('' + err)) {
// adb kill-server doesn't seem to do the trick. // adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually // Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines. // sure that this scenario even happens on non-OSX machines.
events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.'); events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']) return spawn('killall', ['adb']).then(function () {
.then(function() { return helper().then(null, function () {
return helper()
.then(null, function() {
// The double kill is sadly often necessary, at least on mac. // The double kill is sadly often necessary, at least on mac.
events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.'); events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']) return spawn('killall', ['adb']).then(function () {
.then(function() { return helper().then(null, function () {
return helper()
.then(null, function() {
return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.')); return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
}); });
}); });
@ -220,7 +206,7 @@ module.exports.detectArchitecture = function(target) {
module.exports.findBestApkForArchitecture = function (buildResults, arch) { module.exports.findBestApkForArchitecture = function (buildResults, arch) {
var paths = buildResults.apkPaths.filter(function (p) { var paths = buildResults.apkPaths.filter(function (p) {
var apkName = path.basename(p); var apkName = path.basename(p);
if (buildResults.buildType == 'debug') { if (buildResults.buildType === 'debug') {
return /-debug/.exec(apkName); return /-debug/.exec(apkName);
} }
return !/-debug/.exec(apkName); return !/-debug/.exec(apkName);

View File

@ -16,6 +16,8 @@
specific language governing permissions and limitations specific language governing permissions and limitations
under the License. under the License.
*/ */
/* eslint no-self-assign: 0 */
/* eslint no-unused-vars: 0 */
var Q = require('q'); var Q = require('q');
var fs = require('fs'); var fs = require('fs');
@ -46,13 +48,11 @@ GenericBuilder.prototype.clean = function() {
GenericBuilder.prototype.findOutputApks = function (build_type, arch) { GenericBuilder.prototype.findOutputApks = function (build_type, arch) {
var self = this; var self = this;
return Object.keys(this.binDirs) return Object.keys(this.binDirs) .reduce(function (result, builderName) {
.reduce(function (result, builderName) {
console.log('builderName:'+ builderName); console.log('builderName:'+ builderName);
var binDir = self.binDirs[builderName]; var binDir = self.binDirs[builderName];
return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch)); return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch));
}, []) }, []).sort(apkSorter);
.sort(apkSorter);
}; };
module.exports = GenericBuilder; module.exports = GenericBuilder;
@ -74,8 +74,7 @@ function findOutputApksHelper(dir, build_type, arch) {
var shellSilent = shell.config.silent; var shellSilent = shell.config.silent;
shell.config.silent = true; shell.config.silent = true;
var ret = shell.ls(path.join(dir, '*.apk')) var ret = shell.ls(path.join(dir, '*.apk')).filter(function (candidate) {
.filter(function(candidate) {
var apkName = path.basename(candidate); var apkName = path.basename(candidate);
// Need to choose between release and debug .apk. // Need to choose between release and debug .apk.
if (build_type === 'debug') { if (build_type === 'debug') {
@ -85,8 +84,7 @@ function findOutputApksHelper(dir, build_type, arch) {
return /-release/.exec(apkName) && !/-unaligned/.exec(apkName); return /-release/.exec(apkName) && !/-unaligned/.exec(apkName);
} }
return true; return true;
}) }).sort(apkSorter);
.sort(apkSorter);
shellSilent = shellSilent; shellSilent = shellSilent;
@ -98,13 +96,13 @@ function findOutputApksHelper(dir, build_type, arch) {
// And show only arch-specific ones (or non-arch-specific) // And show only arch-specific ones (or non-arch-specific)
ret = ret.filter(function (p) { ret = ret.filter(function (p) {
/* jshint -W018 */ /* jshint -W018 */
return !!/-x86|-arm/.exec(path.basename(p)) == archSpecific; return !!/-x86|-arm/.exec(path.basename(p)) === archSpecific;
/* jshint +W018 */ /* jshint +W018 */
}); });
if (archSpecific && ret.length > 1 && arch) { if (archSpecific && ret.length > 1 && arch) {
ret = ret.filter(function (p) { ret = ret.filter(function (p) {
return path.basename(p).indexOf('-' + arch) != -1; return path.basename(p).indexOf('-' + arch) !== -1;
}); });
} }

View File

@ -43,9 +43,9 @@ function GradleBuilder (projectRoot) {
util.inherits(GradleBuilder, GenericBuilder); util.inherits(GradleBuilder, GenericBuilder);
GradleBuilder.prototype.getArgs = function (cmd, opts) { GradleBuilder.prototype.getArgs = function (cmd, opts) {
if (cmd == 'release') { if (cmd === 'release') {
cmd = 'cdvBuildRelease'; cmd = 'cdvBuildRelease';
} else if (cmd == 'debug') { } else if (cmd === 'debug') {
cmd = 'cdvBuildDebug'; cmd = 'cdvBuildDebug';
} }
var args = [cmd, '-b', path.join(this.root, 'build.gradle')]; var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
@ -150,8 +150,7 @@ GradleBuilder.prototype.prepBuildFiles = function() {
var realDir = p.replace(/[/\\]/g, ':'); var realDir = p.replace(/[/\\]/g, ':');
var libName = realDir.replace(name + '-', ''); var libName = realDir.replace(name + '-', '');
var str = 'include ":' + libName + '"\n'; var str = 'include ":' + libName + '"\n';
if(realDir.indexOf(name+'-')!==-1) if (realDir.indexOf(name + '-') !== -1) { str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n'; }
str+='project(":'+libName+'").projectDir = new File("'+p+'")\n';
return str; return str;
}); });
@ -173,8 +172,7 @@ GradleBuilder.prototype.prepBuildFiles = function() {
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8'); var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
if(projectGradleFile.indexOf('CordovaLib') != -1) { if(projectGradleFile.indexOf('CordovaLib') != -1) {
depsList += '{\n exclude module:("CordovaLib")\n }\n'; depsList += '{\n exclude module:("CordovaLib")\n }\n';
} } else {
else {
depsList += '\n'; depsList += '\n';
} }
}; };
@ -194,7 +192,6 @@ GradleBuilder.prototype.prepBuildFiles = function() {
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'], [/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+'] [/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
]; ];
propertiesObj.systemLibs.forEach(function (p) { propertiesObj.systemLibs.forEach(function (p) {
var mavenRef; var mavenRef;
// It's already in gradle form if it has two ':'s // It's already in gradle form if it has two ':'s
@ -228,8 +225,7 @@ GradleBuilder.prototype.prepBuildFiles = function() {
GradleBuilder.prototype.prepEnv = function (opts) { GradleBuilder.prototype.prepEnv = function (opts) {
var self = this; var self = this;
return check_reqs.check_gradle() return check_reqs.check_gradle().then(function (gradlePath) {
.then(function(gradlePath) {
return self.runGradleWrapper(gradlePath); return self.runGradleWrapper(gradlePath);
}).then(function () { }).then(function () {
return self.prepBuildFiles(); return self.prepBuildFiles();
@ -276,10 +272,9 @@ GradleBuilder.prototype.prepEnv = function(opts) {
*/ */
GradleBuilder.prototype.build = function (opts) { GradleBuilder.prototype.build = function (opts) {
var wrapper = path.join(this.root, 'gradlew'); var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts); var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
return superspawn.spawn(wrapper, args, {stdio: 'pipe'}) return superspawn.spawn(wrapper, args, {stdio: 'pipe'}).progress(function (stdio) {
.progress(function (stdio){
if (stdio.stderr) { if (stdio.stderr) {
/* /*
* Workaround for the issue with Java printing some unwanted information to * Workaround for the issue with Java printing some unwanted information to
@ -314,8 +309,7 @@ GradleBuilder.prototype.clean = function(opts) {
var args = builder.getArgs('clean', opts); var args = builder.getArgs('clean', opts);
return Q().then(function () { return Q().then(function () {
return superspawn.spawn(wrapper, args, {stdio: 'inherit'}); return superspawn.spawn(wrapper, args, {stdio: 'inherit'});
}) }).then(function () {
.then(function () {
shell.rm('-rf', path.join(builder.root, 'out')); shell.rm('-rf', path.join(builder.root, 'out'));
['debug', 'release'].forEach(function (config) { ['debug', 'release'].forEach(function (config) {

View File

@ -35,8 +35,7 @@ var knownBuilders = {
* @return {Builder} A builder instance for specified build type. * @return {Builder} A builder instance for specified build type.
*/ */
module.exports.getBuilder = function (builderType, projectRoot) { module.exports.getBuilder = function (builderType, projectRoot) {
if (!knownBuilders[builderType]) if (!knownBuilders[builderType]) { throw new CordovaError('Builder ' + builderType + ' is not supported.'); }
throw new CordovaError('Builder ' + builderType + ' is not supported.');
try { try {
var Builder = require('./' + knownBuilders[builderType]); var Builder = require('./' + knownBuilders[builderType]);

View File

@ -21,14 +21,14 @@
/* jshint sub:true */ /* jshint sub:true */
var shelljs = require('shelljs'), var shelljs = require('shelljs');
child_process = require('child_process'), var child_process = require('child_process');
Q = require('q'), var Q = require('q');
path = require('path'), var path = require('path');
fs = require('fs'), var fs = require('fs');
os = require('os'), var os = require('os');
REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'), var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..');
PROJECT_ROOT = path.join(__dirname, '..', '..'); var PROJECT_ROOT = path.join(__dirname, '..', '..');
var CordovaError = require('cordova-common').CordovaError; var CordovaError = require('cordova-common').CordovaError;
var superspawn = require('cordova-common').superspawn; var superspawn = require('cordova-common').superspawn;
var android_sdk = require('./android_sdk'); var android_sdk = require('./android_sdk');
@ -53,11 +53,11 @@ function tryCommand(cmd, errMsg, catchStderr) {
} }
module.exports.isWindows = function () { module.exports.isWindows = function () {
return (os.platform() == 'win32'); return (os.platform() === 'win32');
}; };
module.exports.isDarwin = function () { module.exports.isDarwin = function () {
return (os.platform() == 'darwin'); return (os.platform() === 'darwin');
}; };
// Get valid target from framework/project.properties if run from this repo // Get valid target from framework/project.properties if run from this repo
@ -84,12 +84,13 @@ module.exports.get_target = function() {
// Returns a promise. Called only by build and clean commands. // Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function () { module.exports.check_ant = function () {
return superspawn.spawn('ant', ['-version']) return superspawn.spawn('ant', ['-version']).then(function (output) {
.then(function(output) {
// Parse Ant version from command output // Parse Ant version from command output
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1]; return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
}).catch(function (err) { }).catch(function (err) {
if (err) {
throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.'); throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.');
}
}); });
}; };
@ -108,6 +109,12 @@ module.exports.get_gradle_wrapper = function() {
} else { ++i; } } else { ++i; }
} }
} else if (module.exports.isWindows()) { } else if (module.exports.isWindows()) {
var result = child_process.spawnSync(path.join(__dirname, 'getASPath.bat'));
// console.log('result.stdout =' + result.stdout.toString());
// console.log('result.stderr =' + result.stderr.toString());
if (result.stderr.toString().length > 0) {
var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/'; var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/';
if (fs.existsSync(androidPath)) { if (fs.existsSync(androidPath)) {
program_dir = fs.readdirSync(androidPath); program_dir = fs.readdirSync(androidPath);
@ -118,11 +125,17 @@ module.exports.get_gradle_wrapper = function() {
} else { ++i; } } else { ++i; }
} }
} }
} else {
// console.log('got android studio path from registry');
// remove the (os independent) new line char at the end of stdout
// add gradle to match the above.
androidStudioPath = path.join(result.stdout.toString().split('\r\n')[0], 'gradle');
}
} }
if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) { if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
var dirs = fs.readdirSync(androidStudioPath); var dirs = fs.readdirSync(androidStudioPath);
if(dirs[0].split('-')[0] == 'gradle') { if (dirs[0].split('-')[0] === 'gradle') {
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle'); return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
} }
} else { } else {
@ -135,17 +148,17 @@ module.exports.get_gradle_wrapper = function() {
module.exports.check_gradle = function () { module.exports.check_gradle = function () {
var sdkDir = process.env['ANDROID_HOME']; var sdkDir = process.env['ANDROID_HOME'];
var d = Q.defer(); var d = Q.defer();
if (!sdkDir) if (!sdkDir) {
return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' + return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.')); 'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
}
var gradlePath = module.exports.get_gradle_wrapper(); var gradlePath = module.exports.get_gradle_wrapper();
if (gradlePath.length !== 0) if (gradlePath.length !== 0) { d.resolve(gradlePath); } else {
d.resolve(gradlePath);
else
d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' + d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
'or on your system to install the gradle wrapper. Please include gradle \n' + 'or on your system to install the gradle wrapper. Please include gradle \n' +
'in your path, or install Android Studio')); 'in your path, or install Android Studio'));
}
return d.promise; return d.promise;
}; };
@ -165,11 +178,12 @@ module.exports.check_java = function() {
var find_java = '/usr/libexec/java_home'; var find_java = '/usr/libexec/java_home';
var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.'; var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.';
if (fs.existsSync(find_java)) { if (fs.existsSync(find_java)) {
return superspawn.spawn(find_java) return superspawn.spawn(find_java).then(function (stdout) {
.then(function(stdout) {
process.env['JAVA_HOME'] = stdout.trim(); process.env['JAVA_HOME'] = stdout.trim();
}).catch(function (err) { }).catch(function (err) {
if (err) {
throw new CordovaError(default_java_error_msg); throw new CordovaError(default_java_error_msg);
}
}); });
} else { } else {
// See if we can derive it from javac's location. // See if we can derive it from javac's location.
@ -209,8 +223,7 @@ module.exports.check_java = function() {
} }
// We use tryCommand with catchStderr = true, because // We use tryCommand with catchStderr = true, because
// javac writes version info to stderr instead of stdout // javac writes version info to stderr instead of stdout
return tryCommand('javac -version', msg, true) return tryCommand('javac -version', msg, true).then(function (output) {
.then(function (output) {
// Let's check for at least Java 8, and keep it future proof so we can support Java 10 // Let's check for at least Java 8, and keep it future proof so we can support Java 10
var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output); var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output);
return match && match[1]; return match && match[1];
@ -265,7 +278,7 @@ module.exports.check_android = function() {
if (androidCmdPath) { if (androidCmdPath) {
parentDir = path.dirname(androidCmdPath); parentDir = path.dirname(androidCmdPath);
grandParentDir = path.dirname(parentDir); grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) == 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) { if (path.basename(parentDir) === 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
maybeSetAndroidHome(grandParentDir); maybeSetAndroidHome(grandParentDir);
} else { } else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' + throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
@ -276,7 +289,7 @@ module.exports.check_android = function() {
if (adbInPath) { if (adbInPath) {
parentDir = path.dirname(adbInPath); parentDir = path.dirname(adbInPath);
grandParentDir = path.dirname(parentDir); grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) == 'platform-tools') { if (path.basename(parentDir) === 'platform-tools') {
maybeSetAndroidHome(grandParentDir); maybeSetAndroidHome(grandParentDir);
} else { } else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' + throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
@ -287,7 +300,7 @@ module.exports.check_android = function() {
if (avdmanagerInPath) { if (avdmanagerInPath) {
parentDir = path.dirname(avdmanagerInPath); parentDir = path.dirname(avdmanagerInPath);
grandParentDir = path.dirname(parentDir); grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) == 'bin' && path.basename(grandParentDir) == 'tools') { if (path.basename(parentDir) === 'bin' && path.basename(grandParentDir) === 'tools') {
maybeSetAndroidHome(path.dirname(grandParentDir)); maybeSetAndroidHome(path.dirname(grandParentDir));
} else { } else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' + throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
@ -337,8 +350,7 @@ module.exports.check_android_target = function(originalError) {
// Google Inc.:Google APIs:20 // Google Inc.:Google APIs:20
// Google Inc.:Glass Development Kit Preview:20 // Google Inc.:Glass Development Kit Preview:20
var desired_api_level = module.exports.get_target(); var desired_api_level = module.exports.get_target();
return android_sdk.list_targets() return android_sdk.list_targets().then(function (targets) {
.then(function(targets) {
if (targets.indexOf(desired_api_level) >= 0) { if (targets.indexOf(desired_api_level) >= 0) {
return targets; return targets;
} }
@ -358,8 +370,7 @@ module.exports.check_android_target = function(originalError) {
// Returns a promise. // Returns a promise.
module.exports.run = function () { module.exports.run = function () {
return Q.all([this.check_java(), this.check_android()]) return Q.all([this.check_java(), this.check_android()]).then(function (values) {
.then(function(values) {
console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']); console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
console.log('JAVA_HOME=' + process.env['JAVA_HOME']); console.log('JAVA_HOME=' + process.env['JAVA_HOME']);
@ -373,7 +384,6 @@ module.exports.run = function() {
}); });
}; };
/** /**
* Object thar represents one of requirements for current platform. * Object thar represents one of requirements for current platform.
* @param {String} id The unique identifier for this requirements. * @param {String} id The unique identifier for this requirements.
@ -387,7 +397,7 @@ var Requirement = function (id, name, version, installed) {
this.name = name; this.name = name;
this.installed = installed || false; this.installed = installed || false;
this.metadata = { this.metadata = {
version: version, version: version
}; };
}; };
@ -417,15 +427,13 @@ module.exports.check_all = function() {
return checkFns.reduce(function (promise, checkFn, idx) { return checkFns.reduce(function (promise, checkFn, idx) {
// Update each requirement with results // Update each requirement with results
var requirement = requirements[idx]; var requirement = requirements[idx];
return promise.then(checkFn) return promise.then(checkFn).then(function (version) {
.then(function (version) {
requirement.installed = true; requirement.installed = true;
requirement.metadata.version = version; requirement.metadata.version = version;
}, function (err) { }, function (err) {
requirement.metadata.reason = err instanceof Error ? err.message : err; requirement.metadata.reason = err instanceof Error ? err.message : err;
}); });
}, Q()) }, Q()).then(function () {
.then(function () {
// When chain is completed, return requirements array to upstream API // When chain is completed, return requirements array to upstream API
return requirements; return requirements;
}); });

View File

@ -19,8 +19,8 @@
under the License. under the License.
*/ */
var Q = require('q'), var Q = require('q');
build = require('./build'); var build = require('./build');
var path = require('path'); var path = require('path');
var Adb = require('./Adb'); var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest'); var AndroidManifest = require('./AndroidManifest');
@ -33,14 +33,12 @@ var events = require('cordova-common').events;
* @param lookHarder When true, try restarting adb if no devices are found. * @param lookHarder When true, try restarting adb if no devices are found.
*/ */
module.exports.list = function (lookHarder) { module.exports.list = function (lookHarder) {
return Adb.devices() return Adb.devices().then(function (list) {
.then(function(list) {
if (list.length === 0 && lookHarder) { if (list.length === 0 && lookHarder) {
// adb kill-server doesn't seem to do the trick. // adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually // Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines. // sure that this scenario even happens on non-OSX machines.
return spawn('killall', ['adb']) return spawn('killall', ['adb']).then(function () {
.then(function() {
events.emit('verbose', 'Restarting adb to see if more devices are detected.'); events.emit('verbose', 'Restarting adb to see if more devices are detected.');
return Adb.devices(); return Adb.devices();
}, function () { }, function () {
@ -53,8 +51,7 @@ module.exports.list = function(lookHarder) {
}; };
module.exports.resolveTarget = function (target) { module.exports.resolveTarget = function (target) {
return this.list(true) return this.list(true).then(function (device_list) {
.then(function(device_list) {
if (!device_list || !device_list.length) { if (!device_list || !device_list.length) {
return Q.reject(new CordovaError('Failed to deploy to device, no devices found.')); return Q.reject(new CordovaError('Failed to deploy to device, no devices found.'));
} }
@ -65,8 +62,7 @@ module.exports.resolveTarget = function(target) {
return Q.reject('ERROR: Unable to find target \'' + target + '\'.'); return Q.reject('ERROR: Unable to find target \'' + target + '\'.');
} }
return build.detectArchitecture(target) return build.detectArchitecture(target).then(function (arch) {
.then(function(arch) {
return { target: target, arch: arch, isEmulator: false }; return { target: target, arch: arch, isEmulator: false };
}); });
}); });
@ -79,7 +75,7 @@ module.exports.resolveTarget = function(target) {
*/ */
module.exports.install = function (target, buildResults) { module.exports.install = function (target, buildResults) {
return Q().then(function () { return Q().then(function () {
if (target && typeof target == 'object') { if (target && typeof target === 'object') {
return target; return target;
} }
return module.exports.resolveTarget(target); return module.exports.resolveTarget(target);
@ -91,24 +87,20 @@ module.exports.install = function(target, buildResults) {
events.emit('log', 'Using apk: ' + apk_path); events.emit('log', 'Using apk: ' + apk_path);
events.emit('log', 'Package name: ' + pkgName); events.emit('log', 'Package name: ' + pkgName);
return Adb.install(resolvedTarget.target, apk_path, {replace: true}) return Adb.install(resolvedTarget.target, apk_path, {replace: true}).catch(function (error) {
.catch(function (error) {
// CB-9557 CB-10157 only uninstall and reinstall app if the one that // CB-9557 CB-10157 only uninstall and reinstall app if the one that
// is already installed on device was signed w/different certificate // is already installed on device was signed w/different certificate
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
throw error;
events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' + events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' +
'installed app already signed with different key'); 'installed app already signed with different key');
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app // This promise is always resolved, even if 'adb uninstall' fails to uninstall app
// or the app doesn't installed at all, so no error catching needed. // or the app doesn't installed at all, so no error catching needed.
return Adb.uninstall(resolvedTarget.target, pkgName) return Adb.uninstall(resolvedTarget.target, pkgName).then(function () {
.then(function() {
return Adb.install(resolvedTarget.target, apk_path, {replace: true}); return Adb.install(resolvedTarget.target, apk_path, {replace: true});
}); });
}) }).then(function () {
.then(function() {
// unlock screen // unlock screen
return Adb.shell(resolvedTarget.target, 'input keyevent 82'); return Adb.shell(resolvedTarget.target, 'input keyevent 82');
}).then(function () { }).then(function () {

View File

@ -55,8 +55,7 @@ function forgivingWhichSync(cmd) {
} }
module.exports.list_images_using_avdmanager = function () { module.exports.list_images_using_avdmanager = function () {
return superspawn.spawn('avdmanager', ['list', 'avd']) return superspawn.spawn('avdmanager', ['list', 'avd']).then(function (output) {
.then(function(output) {
var response = output.split('\n'); var response = output.split('\n');
var emulator_list = []; var emulator_list = [];
for (var i = 1; i < response.length; i++) { for (var i = 1; i < response.length; i++) {
@ -116,8 +115,7 @@ module.exports.list_images_using_avdmanager = function () {
}; };
module.exports.list_images_using_android = function () { module.exports.list_images_using_android = function () {
return superspawn.spawn('android', ['list', 'avd']) return superspawn.spawn('android', ['list', 'avd']).then(function (output) {
.then(function(output) {
var response = output.split('\n'); var response = output.split('\n');
var emulator_list = []; var emulator_list = [];
for (var i = 1; i < response.length; i++) { for (var i = 1; i < response.length; i++) {
@ -188,8 +186,7 @@ module.exports.list_images = function() {
* Returns a promise. * Returns a promise.
*/ */
module.exports.best_image = function () { module.exports.best_image = function () {
return this.list_images() return this.list_images().then(function (images) {
.then(function(images) {
// Just return undefined if there is no images // Just return undefined if there is no images
if (images.length === 0) return; if (images.length === 0) return;
@ -200,7 +197,7 @@ module.exports.best_image = function() {
var target = images[i].target; var target = images[i].target;
if (target) { if (target) {
var num = target.split('(API level ')[1].replace(')', ''); var num = target.split('(API level ')[1].replace(')', '');
if (num == project_target) { if (num === project_target) {
return images[i]; return images[i];
} else if (project_target - num < closest && project_target > num) { } else if (project_target - num < closest && project_target > num) {
closest = project_target - num; closest = project_target - num;
@ -220,8 +217,7 @@ module.exports.list_started = function() {
// Returns a promise. // Returns a promise.
// TODO: we should remove this, there's a more robust method under android_sdk.js // TODO: we should remove this, there's a more robust method under android_sdk.js
module.exports.list_targets = function () { module.exports.list_targets = function () {
return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()}) return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()}).then(function (output) {
.then(function(output) {
var target_out = output.split('\n'); var target_out = output.split('\n');
var targets = []; var targets = [];
for (var i = target_out.length; i >= 0; i--) { for (var i = target_out.length; i >= 0; i--) {
@ -240,8 +236,7 @@ module.exports.list_targets = function() {
module.exports.get_available_port = function () { module.exports.get_available_port = function () {
var self = this; var self = this;
return self.list_started() return self.list_started().then(function (emulators) {
.then(function (emulators) {
for (var p = 5584; p >= 5554; p -= 2) { for (var p = 5584; p >= 5554; p -= 2) {
if (emulators.indexOf('emulator-' + p) === -1) { if (emulators.indexOf('emulator-' + p) === -1) {
events.emit('verbose', 'Found available port: ' + p); events.emit('verbose', 'Found available port: ' + p);
@ -268,8 +263,7 @@ module.exports.start = function(emulator_ID, boot_timeout) {
return Q().then(function () { return Q().then(function () {
if (emulator_ID) return Q(emulator_ID); if (emulator_ID) return Q(emulator_ID);
return self.best_image() return self.best_image().then(function (best) {
.then(function(best) {
if (best && best.name) { if (best && best.name) {
events.emit('warn', 'No emulator specified, defaulting to ' + best.name); events.emit('warn', 'No emulator specified, defaulting to ' + best.name);
return best.name; return best.name;
@ -282,8 +276,7 @@ module.exports.start = function(emulator_ID, boot_timeout) {
'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n')); 'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n'));
}); });
}).then(function (emulatorId) { }).then(function (emulatorId) {
return self.get_available_port() return self.get_available_port().then(function (port) {
.then(function (port) {
// Figure out the directory the emulator binary runs in, and set the cwd to that directory. // Figure out the directory the emulator binary runs in, and set the cwd to that directory.
// Workaround for https://code.google.com/p/android/issues/detail?id=235461 // Workaround for https://code.google.com/p/android/issues/detail?id=235461
var emulator_dir = path.dirname(shelljs.which('emulator')); var emulator_dir = path.dirname(shelljs.which('emulator'));
@ -298,18 +291,15 @@ module.exports.start = function(emulator_ID, boot_timeout) {
return self.wait_for_emulator(port); return self.wait_for_emulator(port);
}); });
}).then(function (emulatorId) { }).then(function (emulatorId) {
if (!emulatorId) if (!emulatorId) { return Q.reject(new CordovaError('Failed to start emulator')); }
return Q.reject(new CordovaError('Failed to start emulator'));
// wait for emulator to boot up // wait for emulator to boot up
process.stdout.write('Waiting for emulator to boot (this may take a while)...'); process.stdout.write('Waiting for emulator to boot (this may take a while)...');
return self.wait_for_boot(emulatorId, boot_timeout) return self.wait_for_boot(emulatorId, boot_timeout).then(function (success) {
.then(function(success) {
if (success) { if (success) {
events.emit('log', 'BOOT COMPLETE'); events.emit('log', 'BOOT COMPLETE');
// unlock screen // unlock screen
return Adb.shell(emulatorId, 'input keyevent 82') return Adb.shell(emulatorId, 'input keyevent 82').then(function () {
.then(function() {
// return the new emulator id for the started emulators // return the new emulator id for the started emulators
return emulatorId; return emulatorId;
}); });
@ -329,16 +319,15 @@ module.exports.wait_for_emulator = function(port) {
var self = this; var self = this;
return Q().then(function () { return Q().then(function () {
var emulator_id = 'emulator-' + port; var emulator_id = 'emulator-' + port;
return Adb.shell(emulator_id, 'getprop dev.bootcomplete') return Adb.shell(emulator_id, 'getprop dev.bootcomplete').then(function (output) {
.then(function (output) {
if (output.indexOf('1') >= 0) { if (output.indexOf('1') >= 0) {
return emulator_id; return emulator_id;
} }
return self.wait_for_emulator(port); return self.wait_for_emulator(port);
}, function (error) { }, function (error) {
if (error && error.message && if ((error && error.message &&
(error.message.indexOf('not found') > -1) || (error.message.indexOf('not found') > -1)) ||
error.message.indexOf('device offline') > -1) { (error.message.indexOf('device offline') > -1)) {
// emulator not yet started, continue waiting // emulator not yet started, continue waiting
return self.wait_for_emulator(port); return self.wait_for_emulator(port);
} else { } else {
@ -356,8 +345,7 @@ module.exports.wait_for_emulator = function(port) {
*/ */
module.exports.wait_for_boot = function (emulator_id, time_remaining) { module.exports.wait_for_boot = function (emulator_id, time_remaining) {
var self = this; var self = this;
return Adb.shell(emulator_id, 'ps') return Adb.shell(emulator_id, 'ps').then(function (output) {
.then(function(output) {
if (output.match(/android\.process\.acore/)) { if (output.match(/android\.process\.acore/)) {
return true; return true;
} else if (time_remaining === 0) { } else if (time_remaining === 0) {
@ -382,8 +370,7 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) {
module.exports.create_image = function (name, target) { module.exports.create_image = function (name, target) {
console.log('Creating new avd named ' + name); console.log('Creating new avd named ' + name);
if (target) { if (target) {
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target]) return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) {
.then(null, function(error) {
console.error('ERROR : Failed to create emulator image : '); console.error('ERROR : Failed to create emulator image : ');
console.error(' Do you have the latest android targets including ' + target + '?'); console.error(' Do you have the latest android targets including ' + target + '?');
console.error(error); console.error(error);
@ -391,8 +378,7 @@ module.exports.create_image = function(name, target) {
} else { } else {
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.'); console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
// TODO: there's a more robust method for finding targets in android_sdk.js // TODO: there's a more robust method for finding targets in android_sdk.js
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]) return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () {
.then(function() {
// TODO: This seems like another error case, even though it always happens. // TODO: This seems like another error case, even though it always happens.
console.error('ERROR : Unable to create an avd emulator, no targets found.'); console.error('ERROR : Unable to create an avd emulator, no targets found.');
console.error('Ensure you have targets available by running the "android" command'); console.error('Ensure you have targets available by running the "android" command');
@ -405,8 +391,7 @@ module.exports.create_image = function(name, target) {
}; };
module.exports.resolveTarget = function (target) { module.exports.resolveTarget = function (target) {
return this.list_started() return this.list_started().then(function (emulator_list) {
.then(function(emulator_list) {
if (emulator_list.length < 1) { if (emulator_list.length < 1) {
return Q.reject('No running Android emulators found, please start an emulator before deploying your project.'); return Q.reject('No running Android emulators found, please start an emulator before deploying your project.');
} }
@ -417,8 +402,7 @@ module.exports.resolveTarget = function(target) {
return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.'); return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.');
} }
return build.detectArchitecture(target) return build.detectArchitecture(target).then(function (arch) {
.then(function(arch) {
return {target: target, arch: arch, isEmulator: true}; return {target: target, arch: arch, isEmulator: true};
}); });
}); });
@ -438,7 +422,7 @@ module.exports.install = function(givenTarget, buildResults) {
// resolve the target emulator // resolve the target emulator
return Q().then(function () { return Q().then(function () {
if (givenTarget && typeof givenTarget == 'object') { if (givenTarget && typeof givenTarget === 'object') {
return givenTarget; return givenTarget;
} else { } else {
return module.exports.resolveTarget(givenTarget); return module.exports.resolveTarget(givenTarget);
@ -452,8 +436,7 @@ module.exports.install = function(givenTarget, buildResults) {
}).then(function () { }).then(function () {
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app // This promise is always resolved, even if 'adb uninstall' fails to uninstall app
// or the app doesn't installed at all, so no error catching needed. // or the app doesn't installed at all, so no error catching needed.
return Q.when() return Q.when().then(function () {
.then(function() {
var apk_path = build.findBestApkForArchitecture(buildResults, target.arch); var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
var execOptions = { var execOptions = {
@ -494,27 +477,23 @@ module.exports.install = function(givenTarget, buildResults) {
} }
function installPromise () { function installPromise () {
return adbInstallWithOptions(target.target, apk_path, execOptions) return adbInstallWithOptions(target.target, apk_path, execOptions).catch(function (error) {
.catch(function (error) {
// CB-9557 CB-10157 only uninstall and reinstall app if the one that // CB-9557 CB-10157 only uninstall and reinstall app if the one that
// is already installed on device was signed w/different certificate // is already installed on device was signed w/different certificate
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
throw error;
events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' + events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' +
'currently installed app was signed with different key'); 'currently installed app was signed with different key');
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app // This promise is always resolved, even if 'adb uninstall' fails to uninstall app
// or the app doesn't installed at all, so no error catching needed. // or the app doesn't installed at all, so no error catching needed.
return Adb.uninstall(target.target, pkgName) return Adb.uninstall(target.target, pkgName).then(function () {
.then(function() {
return adbInstallWithOptions(target.target, apk_path, execOptions); return adbInstallWithOptions(target.target, apk_path, execOptions);
}); });
}); });
} }
return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise) return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise).then(function (output) {
.then(function (output) {
events.emit('log', 'INSTALL SUCCESS'); events.emit('log', 'INSTALL SUCCESS');
}); });
}); });

View File

@ -0,0 +1,3 @@
@ECHO OFF
for /f "tokens=2*" %%a in ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Android Studio" /v Path') do set "ASPath=%%~b"
ECHO %ASPath%

View File

@ -19,11 +19,11 @@
under the License. under the License.
*/ */
var path = require('path'), var path = require('path');
os = require('os'), var os = require('os');
Q = require('q'), var Q = require('q');
child_process = require('child_process'), var child_process = require('child_process');
ROOT = path.join(__dirname, '..', '..'); var ROOT = path.join(__dirname, '..', '..');
/* /*
* Starts running logcat in the shell. * Starts running logcat in the shell.

View File

@ -95,9 +95,9 @@ var handlers = {
subDir = src; subDir = src;
} }
if (obj.type == 'gradleReference') { if (obj.type === 'gradleReference') {
project.addGradleReference(parentDir, subDir); project.addGradleReference(parentDir, subDir);
} else if (obj.type == 'sys') { } else if (obj.type === 'sys') {
project.addSystemLibrary(parentDir, subDir); project.addSystemLibrary(parentDir, subDir);
} else { } else {
project.addSubProject(parentDir, subDir); project.addSubProject(parentDir, subDir);
@ -125,9 +125,9 @@ var handlers = {
subDir = src; subDir = src;
} }
if (obj.type == 'gradleReference') { if (obj.type === 'gradleReference') {
project.removeGradleReference(parentDir, subDir); project.removeGradleReference(parentDir, subDir);
} else if (obj.type == 'sys') { } else if (obj.type === 'sys') {
project.removeSystemLibrary(parentDir, subDir); project.removeSystemLibrary(parentDir, subDir);
} else { } else {
project.removeSubProject(parentDir, subDir); project.removeSubProject(parentDir, subDir);
@ -221,14 +221,12 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
// check that src path is inside plugin directory // check that src path is inside plugin directory
var real_path = fs.realpathSync(src); var real_path = fs.realpathSync(src);
var real_plugin_path = fs.realpathSync(plugin_dir); var real_plugin_path = fs.realpathSync(plugin_dir);
if (real_path.indexOf(real_plugin_path) !== 0) if (real_path.indexOf(real_plugin_path) !== 0) { throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); }
throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"');
dest = path.resolve(project_dir, dest); dest = path.resolve(project_dir, dest);
// check that dest path is located in project directory // check that dest path is located in project directory
if (dest.indexOf(project_dir) !== 0) if (dest.indexOf(project_dir) !== 0) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); }
throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project');
shell.mkdir('-p', path.dirname(dest)); shell.mkdir('-p', path.dirname(dest));
if (link) { if (link) {
@ -244,8 +242,7 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
// Same as copy file but throws error if target exists // Same as copy file but throws error if target exists
function copyNewFile (plugin_dir, src, project_dir, dest, link) { function copyNewFile (plugin_dir, src, project_dir, dest, link) {
var target_path = path.resolve(project_dir, dest); var target_path = path.resolve(project_dir, dest);
if (fs.existsSync(target_path)) if (fs.existsSync(target_path)) { throw new CordovaError('"' + target_path + '" already exists!'); }
throw new CordovaError('"' + target_path + '" already exists!');
copyFile(plugin_dir, src, project_dir, dest, !!link); copyFile(plugin_dir, src, project_dir, dest, !!link);
} }
@ -260,8 +257,7 @@ function symlinkFileOrDirTree(src, dest) {
fs.readdirSync(src).forEach(function (entry) { fs.readdirSync(src).forEach(function (entry) {
symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry)); symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
}); });
} } else {
else {
fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest); fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest);
} }
} }

View File

@ -16,6 +16,7 @@
specific language governing permissions and limitations specific language governing permissions and limitations
under the License. under the License.
*/ */
/* eslint no-useless-escape: 0 */
var Q = require('q'); var Q = require('q');
var fs = require('fs'); var fs = require('fs');
@ -40,17 +41,14 @@ module.exports.prepare = function (cordovaProject, options) {
this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations); this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations);
// Update own www dir with project's www assets and plugins' assets and js-files // Update own www dir with project's www assets and plugins' assets and js-files
return Q.when(updateWww(cordovaProject, this.locations)) return Q.when(updateWww(cordovaProject, this.locations)).then(function () {
.then(function () {
// update project according to config.xml changes. // update project according to config.xml changes.
return updateProjectAccordingTo(self._config, self.locations); return updateProjectAccordingTo(self._config, self.locations);
}) }).then(function () {
.then(function () {
updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res)); updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
updateSplashes(cordovaProject, path.relative(cordovaProject.root, self.locations.res)); updateSplashes(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root)); updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root));
}) }).then(function () {
.then(function () {
events.emit('verbose', 'Prepared android project successfully'); events.emit('verbose', 'Prepared android project successfully');
}); });
}; };
@ -272,7 +270,7 @@ function updateSplashes(cordovaProject, platformResourcesDir) {
if (!resource.density) { if (!resource.density) {
return; return;
} }
if (resource.density == 'mdpi') { if (resource.density === 'mdpi') {
hadMdpi = true; hadMdpi = true;
} }
var targetPath = getImageResourcePath( var targetPath = getImageResourcePath(
@ -397,15 +395,13 @@ function cleanIcons(projectRoot, projectConfig, platformResourcesDir) {
*/ */
function mapImageResources (rootDir, subDir, type, resourceName) { function mapImageResources (rootDir, subDir, type, resourceName) {
var pathMap = {}; var pathMap = {};
shell.ls(path.join(rootDir, subDir, type + '-*')) shell.ls(path.join(rootDir, subDir, type + '-*')).forEach(function (drawableFolder) {
.forEach(function (drawableFolder) {
var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName); var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
pathMap[imagePath] = null; pathMap[imagePath] = null;
}); });
return pathMap; return pathMap;
} }
function updateFileResources (cordovaProject, platformDir) { function updateFileResources (cordovaProject, platformDir) {
var files = cordovaProject.projectConfig.getFileResources('android'); var files = cordovaProject.projectConfig.getFileResources('android');
@ -426,7 +422,6 @@ function updateFileResources(cordovaProject, platformDir) {
resourceMap, { rootDir: cordovaProject.root }, logFileOp); resourceMap, { rootDir: cordovaProject.root }, logFileOp);
} }
function cleanFileResources (projectRoot, projectConfig, platformDir) { function cleanFileResources (projectRoot, projectConfig, platformDir) {
var files = projectConfig.getFileResources('android', true); var files = projectConfig.getFileResources('android', true);
if (files.length > 0) { if (files.length > 0) {
@ -439,7 +434,8 @@ function cleanFileResources(projectRoot, projectConfig, platformDir) {
}); });
FileUpdater.updatePaths( FileUpdater.updatePaths(
resourceMap, { rootDir: projectRoot, all: true}, logFileOp); resourceMap, {
rootDir: projectRoot, all: true}, logFileOp);
} }
} }

View File

@ -21,12 +21,12 @@
/* jshint loopfunc:true */ /* jshint loopfunc:true */
var path = require('path'), var path = require('path');
build = require('./build'), var build = require('./build');
emulator = require('./emulator'), var emulator = require('./emulator');
device = require('./device'), var device = require('./device');
Q = require('q'), var Q = require('q');
events = require('cordova-common').events; var events = require('cordova-common').events;
function getInstallTarget (runOptions) { function getInstallTarget (runOptions) {
var install_target; var install_target;
@ -56,12 +56,10 @@ function getInstallTarget(runOptions) {
var self = this; var self = this;
var install_target = getInstallTarget(runOptions); var install_target = getInstallTarget(runOptions);
return Q() return Q().then(function () {
.then(function() {
if (!install_target) { if (!install_target) {
// no target given, deploy to device if available, otherwise use the emulator. // no target given, deploy to device if available, otherwise use the emulator.
return device.list() return device.list().then(function (device_list) {
.then(function(device_list) {
if (device_list.length > 0) { if (device_list.length > 0) {
events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.'); events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.');
install_target = device_list[0]; install_target = device_list[0];
@ -72,35 +70,30 @@ function getInstallTarget(runOptions) {
}); });
} }
}).then(function () { }).then(function () {
if (install_target == '--device') { if (install_target === '--device') {
return device.resolveTarget(null); return device.resolveTarget(null);
} else if (install_target == '--emulator') { } else if (install_target === '--emulator') {
// Give preference to any already started emulators. Else, start one. // Give preference to any already started emulators. Else, start one.
return emulator.list_started() return emulator.list_started().then(function (started) {
.then(function(started) {
return started && started.length > 0 ? started[0] : emulator.start(); return started && started.length > 0 ? started[0] : emulator.start();
}).then(function (emulatorId) { }).then(function (emulatorId) {
return emulator.resolveTarget(emulatorId); return emulator.resolveTarget(emulatorId);
}); });
} }
// They specified a specific device/emulator ID. // They specified a specific device/emulator ID.
return device.list() return device.list().then(function (devices) {
.then(function(devices) {
if (devices.indexOf(install_target) > -1) { if (devices.indexOf(install_target) > -1) {
return device.resolveTarget(install_target); return device.resolveTarget(install_target);
} }
return emulator.list_started() return emulator.list_started().then(function (started_emulators) {
.then(function(started_emulators) {
if (started_emulators.indexOf(install_target) > -1) { if (started_emulators.indexOf(install_target) > -1) {
return emulator.resolveTarget(install_target); return emulator.resolveTarget(install_target);
} }
return emulator.list_images() return emulator.list_images().then(function (avds) {
.then(function(avds) {
// if target emulator isn't started, then start it. // if target emulator isn't started, then start it.
for (var avd in avds) { for (var avd in avds) {
if (avds[avd].name == install_target) { if (avds[avd].name === install_target) {
return emulator.start(install_target) return emulator.start(install_target).then(function (emulatorId) {
.then(function(emulatorId) {
return emulator.resolveTarget(emulatorId); return emulator.resolveTarget(emulatorId);
}); });
} }
@ -114,11 +107,9 @@ function getInstallTarget(runOptions) {
// build results (according to platformApi spec) so they are in different // build results (according to platformApi spec) so they are in different
// format than emulator.install expects. // format than emulator.install expects.
// TODO: Update emulator/device.install to handle this change // TODO: Update emulator/device.install to handle this change
return build.run.call(self, runOptions, resolvedTarget) return build.run.call(self, runOptions, resolvedTarget).then(function (buildResults) {
.then(function(buildResults) {
if (resolvedTarget.isEmulator) { if (resolvedTarget.isEmulator) {
return emulator.wait_for_boot(resolvedTarget.target) return emulator.wait_for_boot(resolvedTarget.target).then(function () {
.then(function () {
return emulator.install(resolvedTarget, buildResults); return emulator.install(resolvedTarget, buildResults);
}); });
} }

File diff suppressed because it is too large Load Diff

View File

@ -19,10 +19,10 @@
"apache" "apache"
], ],
"scripts": { "scripts": {
"test": "npm run jshint && jasmine", "test": "npm run eslint && jasmine",
"cover": "istanbul cover --root bin/templates/cordova --print detail jasmine", "cover": "istanbul cover --root bin/templates/cordova --print detail jasmine",
"test-build": "jasmine --captureExceptions --color spec/e2e/*.spec.js", "test-build": "jasmine --captureExceptions --color spec/e2e/*.spec.js",
"jshint": "jshint bin && jshint spec" "eslint": "eslint bin && eslint spec"
}, },
"author": "Apache Software Foundation", "author": "Apache Software Foundation",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -43,9 +43,15 @@
"shelljs" "shelljs"
], ],
"devDependencies": { "devDependencies": {
"eslint": "^3.19.0",
"eslint-config-semistandard": "^11.0.0",
"eslint-config-standard": "^10.2.1",
"eslint-plugin-import": "^2.3.0",
"eslint-plugin-node": "^5.0.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-standard": "^3.0.1",
"istanbul": "^0.4.2", "istanbul": "^0.4.2",
"jasmine": "^2.5.2", "jasmine": "^2.5.2",
"jshint": "^2.6.0",
"promise-matchers": "~0", "promise-matchers": "~0",
"rewire": "^2.1.3" "rewire": "^2.1.3"
}, },

2
spec/.eslintrc.yml Normal file
View File

@ -0,0 +1,2 @@
env:
jasmine: true

View File

@ -32,7 +32,6 @@ function createAndBuild(projectname, projectid, done) {
}); });
} }
describe('create', function () { describe('create', function () {
it('Test#001 : create project with ascii name, no spaces', function (done) { it('Test#001 : create project with ascii name, no spaces', function (done) {

View File

@ -17,11 +17,11 @@
under the License. under the License.
*/ */
var PluginInfoProvider = require('cordova-common').PluginInfoProvider, var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
shell = require('shelljs'), var shell = require('shelljs');
cp = require('child_process'), var cp = require('child_process');
path = require('path'), var path = require('path');
util = require('util'); var util = require('util');
var cordova_bin = path.join(__dirname, '../../../bin'); var cordova_bin = path.join(__dirname, '../../../bin');

View File

@ -17,8 +17,8 @@
under the License. under the License.
*/ */
var path = require('path'), var path = require('path');
actions = require('./helpers/projectActions.js'); var actions = require('./helpers/projectActions.js');
var PLUGIN_ADD_TIMEOUT = 90000; var PLUGIN_ADD_TIMEOUT = 90000;

View File

@ -17,16 +17,16 @@
under the License. under the License.
*/ */
var actions = require('./helpers/projectActions.js'), var actions = require('./helpers/projectActions.js');
shell = require('shelljs'), var shell = require('shelljs');
fs = require('fs'), var fs = require('fs');
util = require('util'), var util = require('util');
platformOld = { version: '4.0.0', path: 'cordova-android-old' }, var platformOld = { version: '4.0.0', path: 'cordova-android-old' };
platformEdge = { version: getCurrentVersion(), path: '.' }; var platformEdge = { version: getCurrentVersion(), path: '.' };
var DOWNLOAD_TIMEOUT = 2 * 60 * 1000, var DOWNLOAD_TIMEOUT = 2 * 60 * 1000;
UPDATE_TIMEOUT = 90 * 1000, var UPDATE_TIMEOUT = 90 * 1000;
PLATFORM_GIT_URL = 'https://github.com/apache/cordova-android'; var PLATFORM_GIT_URL = 'https://github.com/apache/cordova-android';
function getCurrentVersion () { function getCurrentVersion () {
return fs.readFileSync('VERSION').toString().trim(); return fs.readFileSync('VERSION').toString().trim();

View File

@ -1 +0,0 @@
./org.test.plugins.dummyplugin/www/dummyplugin.js

View File

@ -2,7 +2,6 @@
var path = require('path'); var path = require('path');
var AndroidStudio = require('../../bin/templates/cordova/lib/AndroidStudio'); var AndroidStudio = require('../../bin/templates/cordova/lib/AndroidStudio');
describe('AndroidStudio module', function () { describe('AndroidStudio module', function () {
it('should return true for Android Studio project', function () { it('should return true for Android Studio project', function () {
var root = path.join(__dirname, '../fixtures/android_studio_project/'); var root = path.join(__dirname, '../fixtures/android_studio_project/');

View File

@ -17,8 +17,6 @@
under the License. under the License.
*/ */
/* jshint node:true */
var Q = require('q'); var Q = require('q');
var os = require('os'); var os = require('os');
var path = require('path'); var path = require('path');
@ -59,9 +57,7 @@ describe('addPlugin method', function () {
}); });
it('Test#001 : should call gradleBuilder.prepBuildFiles for every plugin with frameworks', function (done) { it('Test#001 : should call gradleBuilder.prepBuildFiles for every plugin with frameworks', function (done) {
api.addPlugin(new PluginInfo(path.join(FIXTURES, 'cordova-plugin-fake'))) api.addPlugin(new PluginInfo(path.join(FIXTURES, 'cordova-plugin-fake'))).catch(fail).fin(function () {
.catch(fail)
.fin(function () {
expect(fail).not.toHaveBeenCalled(); expect(fail).not.toHaveBeenCalled();
expect(gradleBuilder.prepBuildFiles).toHaveBeenCalled(); expect(gradleBuilder.prepBuildFiles).toHaveBeenCalled();
done(); done();
@ -69,9 +65,7 @@ describe('addPlugin method', function () {
}); });
it('Test#002 : shouldn\'t trigger gradleBuilder.prepBuildFiles for plugins without android frameworks', function (done) { it('Test#002 : shouldn\'t trigger gradleBuilder.prepBuildFiles for plugins without android frameworks', function (done) {
api.addPlugin(new PluginInfo(path.join(FIXTURES, 'cordova-plugin-fake-ios-frameworks'))) api.addPlugin(new PluginInfo(path.join(FIXTURES, 'cordova-plugin-fake-ios-frameworks'))).catch(fail).fin(function () {
.catch(fail)
.fin(function () {
expect(fail).not.toHaveBeenCalled(); expect(fail).not.toHaveBeenCalled();
expect(gradleBuilder.prepBuildFiles).not.toHaveBeenCalled(); expect(gradleBuilder.prepBuildFiles).not.toHaveBeenCalled();
done(); done();

View File

@ -16,41 +16,37 @@
specific language governing permissions and limitations specific language governing permissions and limitations
under the License. under the License.
*/ */
/* jshint laxcomma:true */
var android_sdk = require("../../bin/templates/cordova/lib/android_sdk"); var android_sdk = require('../../bin/templates/cordova/lib/android_sdk');
var superspawn = require("cordova-common").superspawn; var superspawn = require('cordova-common').superspawn;
var fs = require("fs"); var fs = require('fs');
var path = require("path"); var path = require('path');
var Q = require("q"); var Q = require('q');
describe("android_sdk", function () { describe('android_sdk', function () {
describe("list_targets_with_android", function() { describe('list_targets_with_android', function () {
it("should invoke `android` with the `list target` command and _not_ the `list targets` command, as the plural form is not supported in some Android SDK Tools versions", function() { it('should invoke `android` with the `list target` command and _not_ the `list targets` command, as the plural form is not supported in some Android SDK Tools versions', function () {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(superspawn, "spawn").and.returnValue(deferred.promise); spyOn(superspawn, 'spawn').and.returnValue(deferred.promise);
android_sdk.list_targets_with_android(); android_sdk.list_targets_with_android();
expect(superspawn.spawn).toHaveBeenCalledWith("android", ["list", "target"]); expect(superspawn.spawn).toHaveBeenCalledWith('android', ['list', 'target']);
}); });
it("should parse and return results from `android list targets` command", function(done) { it('should parse and return results from `android list targets` command', function (done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(superspawn, "spawn").and.returnValue(deferred.promise); spyOn(superspawn, 'spawn').and.returnValue(deferred.promise);
deferred.resolve(fs.readFileSync(path.join("spec", "fixtures", "sdk25.2-android_list_targets.txt"), "utf-8")); deferred.resolve(fs.readFileSync(path.join('spec', 'fixtures', 'sdk25.2-android_list_targets.txt'), 'utf-8'));
return android_sdk.list_targets_with_android() return android_sdk.list_targets_with_android().then(function (list) {
.then(function(list) { [ 'Google Inc.:Google APIs:23',
[ "Google Inc.:Google APIs:23", 'Google Inc.:Google APIs:22',
"Google Inc.:Google APIs:22", 'Google Inc.:Google APIs:21',
"Google Inc.:Google APIs:21", 'android-25',
"android-25", 'android-24',
"android-24", 'android-N',
"android-N", 'android-23',
"android-23", 'android-MNC',
"android-MNC", 'android-22',
"android-22", 'android-21',
"android-21", 'android-20' ].forEach(function (target) { expect(list).toContain(target); });
"android-20" ].forEach(function(target) {
expect(list).toContain(target);
});
}).fail(function (err) { }).fail(function (err) {
console.log(err); console.log(err);
expect(err).toBeUndefined(); expect(err).toBeUndefined();
@ -59,14 +55,13 @@ describe("android_sdk", function () {
}); });
}); });
}); });
describe("list_targets_with_avdmanager", function() { describe('list_targets_with_avdmanager', function () {
it("should parse and return results from `avdmanager list target` command", function(done) { it('should parse and return results from `avdmanager list target` command', function (done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(superspawn, "spawn").and.returnValue(deferred.promise); spyOn(superspawn, 'spawn').and.returnValue(deferred.promise);
deferred.resolve(fs.readFileSync(path.join("spec", "fixtures", "sdk25.3-avdmanager_list_target.txt"), "utf-8")); deferred.resolve(fs.readFileSync(path.join('spec', 'fixtures', 'sdk25.3-avdmanager_list_target.txt'), 'utf-8'));
return android_sdk.list_targets_with_avdmanager() return android_sdk.list_targets_with_avdmanager().then(function (list) {
.then(function(list) { expect(list).toContain('android-25');
expect(list).toContain("android-25");
}).fail(function (err) { }).fail(function (err) {
console.log(err); console.log(err);
expect(err).toBeUndefined(); expect(err).toBeUndefined();
@ -75,56 +70,53 @@ describe("android_sdk", function () {
}); });
}); });
}); });
describe("list_targets", function() { describe('list_targets', function () {
it("should parse Android SDK installed target information with `avdmanager` command first", function() { it('should parse Android SDK installed target information with `avdmanager` command first', function () {
var deferred = Q.defer(); var deferred = Q.defer();
var avdmanager_spy = spyOn(android_sdk, "list_targets_with_avdmanager").and.returnValue(deferred.promise); var avdmanager_spy = spyOn(android_sdk, 'list_targets_with_avdmanager').and.returnValue(deferred.promise);
android_sdk.list_targets(); android_sdk.list_targets();
expect(avdmanager_spy).toHaveBeenCalled(); expect(avdmanager_spy).toHaveBeenCalled();
}); });
it("should parse Android SDK installed target information with `android` command if list_targets_with_avdmanager fails with ENOENT", function(done) { it('should parse Android SDK installed target information with `android` command if list_targets_with_avdmanager fails with ENOENT', function (done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(android_sdk, "list_targets_with_avdmanager").and.returnValue(deferred.promise); spyOn(android_sdk, 'list_targets_with_avdmanager').and.returnValue(deferred.promise);
deferred.reject({ deferred.reject({
code: "ENOENT" code: 'ENOENT'
}); });
var twoferred = Q.defer(); var twoferred = Q.defer();
twoferred.resolve(["target1"]); twoferred.resolve(['target1']);
var avdmanager_spy = spyOn(android_sdk, "list_targets_with_android").and.returnValue(twoferred.promise); var avdmanager_spy = spyOn(android_sdk, 'list_targets_with_android').and.returnValue(twoferred.promise);
return android_sdk.list_targets() return android_sdk.list_targets().then(function (targets) {
.then(function(targets) {
expect(avdmanager_spy).toHaveBeenCalled(); expect(avdmanager_spy).toHaveBeenCalled();
expect(targets[0]).toEqual("target1"); expect(targets[0]).toEqual('target1');
done(); done();
}); });
}); });
it("should parse Android SDK installed target information with `android` command if list_targets_with_avdmanager fails with not-recognized error (Windows)", function(done) { it('should parse Android SDK installed target information with `android` command if list_targets_with_avdmanager fails with not-recognized error (Windows)', function (done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(android_sdk, "list_targets_with_avdmanager").and.returnValue(deferred.promise); spyOn(android_sdk, 'list_targets_with_avdmanager').and.returnValue(deferred.promise);
deferred.reject({ deferred.reject({
code: 1, code: 1,
stderr: "'avdmanager' is not recognized as an internal or external commmand,\r\noperable program or batch file.\r\n" stderr: "'avdmanager' is not recognized as an internal or external commmand,\r\noperable program or batch file.\r\n"
}); });
var twoferred = Q.defer(); var twoferred = Q.defer();
twoferred.resolve(["target1"]); twoferred.resolve(['target1']);
var avdmanager_spy = spyOn(android_sdk, "list_targets_with_android").and.returnValue(twoferred.promise); var avdmanager_spy = spyOn(android_sdk, 'list_targets_with_android').and.returnValue(twoferred.promise);
return android_sdk.list_targets() return android_sdk.list_targets().then(function (targets) {
.then(function(targets) {
expect(avdmanager_spy).toHaveBeenCalled(); expect(avdmanager_spy).toHaveBeenCalled();
expect(targets[0]).toEqual("target1"); expect(targets[0]).toEqual('target1');
done(); done();
}); });
}); });
it("should throw an error if no Android targets were found.", function(done) { it('should throw an error if no Android targets were found.', function (done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(android_sdk, "list_targets_with_avdmanager").and.returnValue(deferred.promise); spyOn(android_sdk, 'list_targets_with_avdmanager').and.returnValue(deferred.promise);
deferred.resolve([]); deferred.resolve([]);
return android_sdk.list_targets() return android_sdk.list_targets().then(function (targets) {
.then(function(targets) {
done.fail(); done.fail();
}).catch(function (err) { }).catch(function (err) {
expect(err).toBeDefined(); expect(err).toBeDefined();
expect(err.message).toContain("No android targets (SDKs) installed!"); expect(err.message).toContain('No android targets (SDKs) installed!');
done(); done();
}); });
}); });

View File

@ -1,4 +1,5 @@
var gradle_builder = require('../../../bin/templates/cordova/lib/builders/GradleBuilder.js');
var Gradle_builder = require('../../../bin/templates/cordova/lib/builders/GradleBuilder.js');
var fs = require('fs'); var fs = require('fs');
var superspawn = require('cordova-common').superspawn; var superspawn = require('cordova-common').superspawn;
var builder; var builder;
@ -6,7 +7,7 @@ var builder;
describe('Gradle Builder', function () { describe('Gradle Builder', function () {
beforeEach(function () { beforeEach(function () {
spyOn(fs, 'existsSync').and.returnValue(true); spyOn(fs, 'existsSync').and.returnValue(true);
builder = new gradle_builder('/root'); builder = new Gradle_builder('/root');
spyOn(superspawn, 'spawn'); spyOn(superspawn, 'spawn');
}); });

View File

@ -16,16 +16,15 @@
specific language governing permissions and limitations specific language governing permissions and limitations
under the License. under the License.
*/ */
/* jshint laxcomma:true */
var check_reqs = require("../../bin/templates/cordova/lib/check_reqs"); var check_reqs = require('../../bin/templates/cordova/lib/check_reqs');
var android_sdk = require("../../bin/templates/cordova/lib/android_sdk"); var android_sdk = require('../../bin/templates/cordova/lib/android_sdk');
var shelljs = require("shelljs"); var shelljs = require('shelljs');
var fs = require("fs"); var fs = require('fs');
var path = require("path"); var path = require('path');
var Q = require("q"); var Q = require('q');
describe("check_reqs", function () { describe('check_reqs', function () {
var original_env; var original_env;
beforeAll(function () { beforeAll(function () {
original_env = Object.create(process.env); original_env = Object.create(process.env);
@ -35,23 +34,22 @@ describe("check_reqs", function () {
process.env[k] = original_env[k]; process.env[k] = original_env[k];
}); });
}); });
describe("check_android", function() { describe('check_android', function () {
describe("set ANDROID_HOME if not set", function() { describe('set ANDROID_HOME if not set', function () {
beforeEach(function () { beforeEach(function () {
delete process.env.ANDROID_HOME; delete process.env.ANDROID_HOME;
}); });
describe("even if no Android binaries are on the PATH", function() { describe('even if no Android binaries are on the PATH', function () {
beforeEach(function () { beforeEach(function () {
spyOn(shelljs, "which").and.returnValue(null); spyOn(shelljs, 'which').and.returnValue(null);
spyOn(fs, "existsSync").and.returnValue(true); spyOn(fs, 'existsSync').and.returnValue(true);
}); });
it("it should set ANDROID_HOME on Windows", function(done) { it('it should set ANDROID_HOME on Windows', function (done) {
spyOn(check_reqs, "isWindows").and.returnValue(true); spyOn(check_reqs, 'isWindows').and.returnValue(true);
process.env.LOCALAPPDATA = "windows-local-app-data"; process.env.LOCALAPPDATA = 'windows-local-app-data';
process.env.ProgramFiles = "windows-program-files"; process.env.ProgramFiles = 'windows-program-files';
return check_reqs.check_android() return check_reqs.check_android().then(function () {
.then(function() { expect(process.env.ANDROID_HOME).toContain('windows-local-app-data');
expect(process.env.ANDROID_HOME).toContain("windows-local-app-data");
}).fail(function (err) { }).fail(function (err) {
expect(err).toBeUndefined(); expect(err).toBeUndefined();
console.log(err); console.log(err);
@ -61,13 +59,12 @@ describe("check_reqs", function () {
done(); done();
}); });
}); });
it("it should set ANDROID_HOME on Darwin", function(done) { it('it should set ANDROID_HOME on Darwin', function (done) {
spyOn(check_reqs, "isWindows").and.returnValue(false); spyOn(check_reqs, 'isWindows').and.returnValue(false);
spyOn(check_reqs, "isDarwin").and.returnValue(true); spyOn(check_reqs, 'isDarwin').and.returnValue(true);
process.env.HOME = "home is where the heart is"; process.env.HOME = 'home is where the heart is';
return check_reqs.check_android() return check_reqs.check_android().then(function () {
.then(function() { expect(process.env.ANDROID_HOME).toContain('home is where the heart is');
expect(process.env.ANDROID_HOME).toContain("home is where the heart is");
}).fail(function (err) { }).fail(function (err) {
expect(err).toBeUndefined(); expect(err).toBeUndefined();
console.log(err); console.log(err);
@ -77,134 +74,127 @@ describe("check_reqs", function () {
}); });
}); });
}); });
describe("if some Android tooling exists on the PATH", function() { describe('if some Android tooling exists on the PATH', function () {
beforeEach(function () { beforeEach(function () {
spyOn(fs, "realpathSync").and.callFake(function(path) { spyOn(fs, 'realpathSync').and.callFake(function (path) {
return path; return path;
}); });
}); });
it("should set ANDROID_HOME based on `android` command if command exists in a SDK-like directory structure", function(done) { it('should set ANDROID_HOME based on `android` command if command exists in a SDK-like directory structure', function (done) {
spyOn(fs, "existsSync").and.returnValue(true); spyOn(fs, 'existsSync').and.returnValue(true);
spyOn(shelljs, "which").and.callFake(function(cmd) { spyOn(shelljs, 'which').and.callFake(function (cmd) {
if (cmd == "android") { if (cmd === 'android') {
return "/android/sdk/tools/android"; return '/android/sdk/tools/android';
} else { } else {
return null; return null;
} }
}); });
return check_reqs.check_android() return check_reqs.check_android().then(function () {
.then(function() { expect(process.env.ANDROID_HOME).toEqual('/android/sdk');
expect(process.env.ANDROID_HOME).toEqual("/android/sdk");
done(); done();
}).fail(function (err) { }).fail(function (err) {
expect(err).toBeUndefined(); expect(err).toBeUndefined();
console.log(err); console.log(err);
}); });
}); });
it("should error out if `android` command exists in a non-SDK-like directory structure", function(done) { it('should error out if `android` command exists in a non-SDK-like directory structure', function (done) {
spyOn(shelljs, "which").and.callFake(function(cmd) { spyOn(shelljs, 'which').and.callFake(function (cmd) {
if (cmd == "android") { if (cmd === 'android') {
return "/just/some/random/path/android"; return '/just/some/random/path/android';
} else { } else {
return null; return null;
} }
}); });
return check_reqs.check_android() return check_reqs.check_android().then(function () {
.then(function() {
done.fail(); done.fail();
}).fail(function (err) { }).fail(function (err) {
expect(err).toBeDefined(); expect(err).toBeDefined();
expect(err.message).toContain("update your PATH to include valid path"); expect(err.message).toContain('update your PATH to include valid path');
done(); done();
}); });
}); });
it("should set ANDROID_HOME based on `adb` command if command exists in a SDK-like directory structure", function(done) { it('should set ANDROID_HOME based on `adb` command if command exists in a SDK-like directory structure', function (done) {
spyOn(fs, "existsSync").and.returnValue(true); spyOn(fs, 'existsSync').and.returnValue(true);
spyOn(shelljs, "which").and.callFake(function(cmd) { spyOn(shelljs, 'which').and.callFake(function (cmd) {
if (cmd == "adb") { if (cmd === 'adb') {
return "/android/sdk/platform-tools/adb"; return '/android/sdk/platform-tools/adb';
} else { } else {
return null; return null;
} }
}); });
return check_reqs.check_android() return check_reqs.check_android().then(function () {
.then(function() { expect(process.env.ANDROID_HOME).toEqual('/android/sdk');
expect(process.env.ANDROID_HOME).toEqual("/android/sdk");
done(); done();
}).fail(function (err) { }).fail(function (err) {
expect(err).toBeUndefined(); expect(err).toBeUndefined();
console.log(err); console.log(err);
}); });
}); });
it("should error out if `adb` command exists in a non-SDK-like directory structure", function(done) { it('should error out if `adb` command exists in a non-SDK-like directory structure', function (done) {
spyOn(shelljs, "which").and.callFake(function(cmd) { spyOn(shelljs, 'which').and.callFake(function (cmd) {
if (cmd == "adb") { if (cmd === 'adb') {
return "/just/some/random/path/adb"; return '/just/some/random/path/adb';
} else { } else {
return null; return null;
} }
}); });
return check_reqs.check_android() return check_reqs.check_android().then(function () {
.then(function() {
done.fail(); done.fail();
}).fail(function (err) { }).fail(function (err) {
expect(err).toBeDefined(); expect(err).toBeDefined();
expect(err.message).toContain("update your PATH to include valid path"); expect(err.message).toContain('update your PATH to include valid path');
done(); done();
}); });
}); });
it("should set ANDROID_HOME based on `avdmanager` command if command exists in a SDK-like directory structure", function(done) { it('should set ANDROID_HOME based on `avdmanager` command if command exists in a SDK-like directory structure', function (done) {
spyOn(fs, "existsSync").and.returnValue(true); spyOn(fs, 'existsSync').and.returnValue(true);
spyOn(shelljs, "which").and.callFake(function(cmd) { spyOn(shelljs, 'which').and.callFake(function (cmd) {
if (cmd == "avdmanager") { if (cmd === 'avdmanager') {
return "/android/sdk/tools/bin/avdmanager"; return '/android/sdk/tools/bin/avdmanager';
} else { } else {
return null; return null;
} }
}); });
return check_reqs.check_android() return check_reqs.check_android().then(function () {
.then(function() { expect(process.env.ANDROID_HOME).toEqual('/android/sdk');
expect(process.env.ANDROID_HOME).toEqual("/android/sdk");
done(); done();
}).fail(function (err) { }).fail(function (err) {
expect(err).toBeUndefined(); expect(err).toBeUndefined();
console.log(err); console.log(err);
}); });
}); });
it("should error out if `avdmanager` command exists in a non-SDK-like directory structure", function(done) { it('should error out if `avdmanager` command exists in a non-SDK-like directory structure', function (done) {
spyOn(shelljs, "which").and.callFake(function(cmd) { spyOn(shelljs, 'which').and.callFake(function (cmd) {
if (cmd == "avdmanager") { if (cmd === 'avdmanager') {
return "/just/some/random/path/avdmanager"; return '/just/some/random/path/avdmanager';
} else { } else {
return null; return null;
} }
}); });
return check_reqs.check_android() return check_reqs.check_android().then(function () {
.then(function() {
done.fail(); done.fail();
}).fail(function (err) { }).fail(function (err) {
expect(err).toBeDefined(); expect(err).toBeDefined();
expect(err.message).toContain("update your PATH to include valid path"); expect(err.message).toContain('update your PATH to include valid path');
done(); done();
}); });
}); });
}); });
}); });
describe("set PATH for various Android binaries if not available", function() { describe('set PATH for various Android binaries if not available', function () {
beforeEach(function () { beforeEach(function () {
spyOn(shelljs, "which").and.returnValue(null); spyOn(shelljs, 'which').and.returnValue(null);
process.env.ANDROID_HOME = "let the children play"; process.env.ANDROID_HOME = 'let the children play';
spyOn(fs, "existsSync").and.returnValue(true); spyOn(fs, 'existsSync').and.returnValue(true);
}); });
afterEach(function () { afterEach(function () {
delete process.env.ANDROID_HOME; delete process.env.ANDROID_HOME;
}); });
it("should add tools/bin,tools,platform-tools to PATH if `avdmanager`,`android`,`adb` is not found", function(done) { it('should add tools/bin,tools,platform-tools to PATH if `avdmanager`,`android`,`adb` is not found', function (done) {
return check_reqs.check_android() return check_reqs.check_android().then(function () {
.then(function() { expect(process.env.PATH).toContain('let the children play' + path.sep + 'tools');
expect(process.env.PATH).toContain("let the children play" + path.sep + "tools"); expect(process.env.PATH).toContain('let the children play' + path.sep + 'platform-tools');
expect(process.env.PATH).toContain("let the children play" + path.sep + "platform-tools"); expect(process.env.PATH).toContain('let the children play' + path.sep + 'tools' + path.sep + 'bin');
expect(process.env.PATH).toContain("let the children play" + path.sep + "tools" + path.sep + "bin");
done(); done();
}).fail(function (err) { }).fail(function (err) {
expect(err).toBeUndefined(); expect(err).toBeUndefined();
@ -213,37 +203,35 @@ describe("check_reqs", function () {
}); });
}); });
}); });
describe("get_target", function() { describe('get_target', function () {
it("should retrieve target from framework project.properties file", function() { it('should retrieve target from framework project.properties file', function () {
var target = check_reqs.get_target(); var target = check_reqs.get_target();
expect(target).toBeDefined(); expect(target).toBeDefined();
expect(target).toContain("android-"); expect(target).toContain('android-');
}); });
}); });
describe("check_android_target", function() { describe('check_android_target', function () {
it("should should return full list of supported targets if there is a match to ideal api level", function(done) { it('should should return full list of supported targets if there is a match to ideal api level', function (done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(android_sdk, "list_targets").and.returnValue(deferred.promise); spyOn(android_sdk, 'list_targets').and.returnValue(deferred.promise);
var fake_targets = ["you are my fire", "my one desire"]; var fake_targets = ['you are my fire', 'my one desire'];
deferred.resolve(fake_targets); deferred.resolve(fake_targets);
spyOn(check_reqs, "get_target").and.returnValue("you are my fire"); spyOn(check_reqs, 'get_target').and.returnValue('you are my fire');
return check_reqs.check_android_target() return check_reqs.check_android_target().then(function (targets) {
.then(function(targets) {
expect(targets).toBeDefined(); expect(targets).toBeDefined();
expect(targets).toEqual(fake_targets); expect(targets).toEqual(fake_targets);
done(); done();
}); });
}); });
it("should error out if there is no match between ideal api level and installed targets", function(done) { it('should error out if there is no match between ideal api level and installed targets', function (done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(android_sdk, "list_targets").and.returnValue(deferred.promise); spyOn(android_sdk, 'list_targets').and.returnValue(deferred.promise);
var fake_targets = ["you are my fire", "my one desire"]; var fake_targets = ['you are my fire', 'my one desire'];
deferred.resolve(fake_targets); deferred.resolve(fake_targets);
spyOn(check_reqs, "get_target").and.returnValue("and i knowwwwwwwwwwww"); spyOn(check_reqs, 'get_target').and.returnValue('and i knowwwwwwwwwwww');
return check_reqs.check_android_target() return check_reqs.check_android_target().catch(function (err) {
.catch(function(err) {
expect(err).toBeDefined(); expect(err).toBeDefined();
expect(err.message).toContain("Please install Android target"); expect(err.message).toContain('Please install Android target');
done(); done();
}); });
}); });

View File

@ -16,39 +16,37 @@
specific language governing permissions and limitations specific language governing permissions and limitations
under the License. under the License.
*/ */
/* jshint laxcomma:true */
var create = require("../../bin/lib/create"); var create = require('../../bin/lib/create');
describe("create", function () { describe('create', function () {
describe("validatePackageName", function() { describe('validatePackageName', function () {
var valid = [ var valid = [
"org.apache.mobilespec" 'org.apache.mobilespec',
, "com.example" 'com.example',
, "com.floors42.package" 'com.floors42.package',
, "ball8.ball8.ball8ball" 'ball8.ball8.ball8ball'
]; ];
var invalid = [ var invalid = [
"" '',
, "com.class.is.bad" 'com.class.is.bad',
, "0com.example.mobilespec" '0com.example.mobilespec',
, "c-m.e@a!p%e.mobilespec" 'c-m.e@a!p%e.mobilespec',
, "notenoughdots" 'notenoughdots',
, ".starts.with.a.dot" '.starts.with.a.dot',
, "ends.with.a.dot." 'ends.with.a.dot.',
, "_underscore.anything" '_underscore.anything',
, "underscore._something" 'underscore._something',
, "_underscore._all._the._things" '_underscore._all._the._things',
, "8.ball" '8.ball',
, "8ball.ball" '8ball.ball',
, "ball8.8ball" 'ball8.8ball',
, "ball8.com.8ball" 'ball8.com.8ball'
]; ];
valid.forEach(function (package_name) { valid.forEach(function (package_name) {
it("Test#001 : should accept " + package_name, function(done) { it('Test#001 : should accept ' + package_name, function (done) {
return create.validatePackageName(package_name) return create.validatePackageName(package_name).then(function () {
.then(function() {
// resolved // resolved
done(); done();
}).fail(function (err) { }).fail(function (err) {
@ -58,9 +56,8 @@ describe("create", function () {
}); });
invalid.forEach(function (package_name) { invalid.forEach(function (package_name) {
it("Test#002 : should reject " + package_name, function(done) { it('Test#002 : should reject ' + package_name, function (done) {
return create.validatePackageName(package_name) return create.validatePackageName(package_name).then(function () {
.then(function() {
// shouldn't be here // shouldn't be here
expect(true).toBe(false); expect(true).toBe(false);
}).fail(function (err) { }).fail(function (err) {
@ -70,23 +67,22 @@ describe("create", function () {
}); });
}); });
}); });
describe("validateProjectName", function() { describe('validateProjectName', function () {
var valid = [ var valid = [
"mobilespec" 'mobilespec',
, "package_name" 'package_name',
, "PackageName" 'PackageName',
, "CordovaLib" 'CordovaLib'
]; ];
var invalid = [ var invalid = [
"" '',
, "0startswithdigit" '0startswithdigit',
, "CordovaActivity" 'CordovaActivity'
]; ];
valid.forEach(function (project_name) { valid.forEach(function (project_name) {
it("Test#003 : should accept " + project_name, function(done) { it('Test#003 : should accept ' + project_name, function (done) {
return create.validateProjectName(project_name) return create.validateProjectName(project_name).then(function () {
.then(function() {
// resolved // resolved
done(); done();
}).fail(function (err) { }).fail(function (err) {
@ -96,9 +92,8 @@ describe("create", function () {
}); });
invalid.forEach(function (project_name) { invalid.forEach(function (project_name) {
it("Test#004 : should reject " + project_name, function(done) { it('Test#004 : should reject ' + project_name, function (done) {
return create.validateProjectName(project_name) return create.validateProjectName(project_name).then(function () {
.then(function() {
// shouldn't be here // shouldn't be here
expect(true).toBe(false); expect(true).toBe(false);
}).fail(function (err) { }).fail(function (err) {
@ -109,4 +104,3 @@ describe("create", function () {
}); });
}); });
}); });

View File

@ -16,26 +16,26 @@
specific language governing permissions and limitations specific language governing permissions and limitations
under the License. under the License.
*/ */
var emu = require("../../bin/templates/cordova/lib/emulator");
var superspawn = require("cordova-common").superspawn;
var Q = require("q");
var fs = require("fs");
var path = require("path");
var shelljs = require("shelljs");
describe("emulator", function () { var emu = require('../../bin/templates/cordova/lib/emulator');
describe("list_images_using_avdmanager", function() { var superspawn = require('cordova-common').superspawn;
it("should properly parse details of SDK Tools 25.3.1 `avdmanager` output", function(done) { var Q = require('q');
var fs = require('fs');
var path = require('path');
var shelljs = require('shelljs');
describe('emulator', function () {
describe('list_images_using_avdmanager', function () {
it('should properly parse details of SDK Tools 25.3.1 `avdmanager` output', function (done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(superspawn, "spawn").and.returnValue(deferred.promise); spyOn(superspawn, 'spawn').and.returnValue(deferred.promise);
deferred.resolve(fs.readFileSync(path.join("spec", "fixtures", "sdk25.3-avdmanager_list_avd.txt"), "utf-8")); deferred.resolve(fs.readFileSync(path.join('spec', 'fixtures', 'sdk25.3-avdmanager_list_avd.txt'), 'utf-8'));
return emu.list_images_using_avdmanager() return emu.list_images_using_avdmanager().then(function (list) {
.then(function(list) {
expect(list).toBeDefined(); expect(list).toBeDefined();
expect(list[0].name).toEqual("nexus5-5.1"); expect(list[0].name).toEqual('nexus5-5.1');
expect(list[0].target).toEqual("Android 5.1 (API level 22)"); expect(list[0].target).toEqual('Android 5.1 (API level 22)');
expect(list[1].device).toEqual("pixel (Google)"); expect(list[1].device).toEqual('pixel (Google)');
expect(list[2].abi).toEqual("default/x86_64"); expect(list[2].abi).toEqual('default/x86_64');
}).fail(function (err) { }).fail(function (err) {
expect(err).toBeUndefined(); expect(err).toBeUndefined();
}).fin(function () { }).fin(function () {
@ -43,26 +43,25 @@ describe("emulator", function () {
}); });
}); });
}); });
describe("list_images_using_android", function() { describe('list_images_using_android', function () {
it("should invoke `android` with the `list avd` command and _not_ the `list avds` command, as the plural form is not supported in some Android SDK Tools versions", function() { it('should invoke `android` with the `list avd` command and _not_ the `list avds` command, as the plural form is not supported in some Android SDK Tools versions', function () {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(superspawn, "spawn").and.returnValue(deferred.promise); spyOn(superspawn, 'spawn').and.returnValue(deferred.promise);
emu.list_images_using_android(); emu.list_images_using_android();
expect(superspawn.spawn).toHaveBeenCalledWith("android", ["list", "avd"]); expect(superspawn.spawn).toHaveBeenCalledWith('android', ['list', 'avd']);
}); });
it("should properly parse details of SDK Tools pre-25.3.1 `android list avd` output", function(done) { it('should properly parse details of SDK Tools pre-25.3.1 `android list avd` output', function (done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(superspawn, "spawn").and.returnValue(deferred.promise); spyOn(superspawn, 'spawn').and.returnValue(deferred.promise);
deferred.resolve(fs.readFileSync(path.join("spec", "fixtures", "sdk25.2-android_list_avd.txt"), "utf-8")); deferred.resolve(fs.readFileSync(path.join('spec', 'fixtures', 'sdk25.2-android_list_avd.txt'), 'utf-8'));
return emu.list_images_using_android() return emu.list_images_using_android().then(function (list) {
.then(function(list) {
expect(list).toBeDefined(); expect(list).toBeDefined();
expect(list[0].name).toEqual("QWR"); expect(list[0].name).toEqual('QWR');
expect(list[0].device).toEqual("Nexus 5 (Google)"); expect(list[0].device).toEqual('Nexus 5 (Google)');
expect(list[0].path).toEqual("/Users/shazron/.android/avd/QWR.avd"); expect(list[0].path).toEqual('/Users/shazron/.android/avd/QWR.avd');
expect(list[0].target).toEqual("Android 7.1.1 (API level 25)"); expect(list[0].target).toEqual('Android 7.1.1 (API level 25)');
expect(list[0].abi).toEqual("google_apis/x86_64"); expect(list[0].abi).toEqual('google_apis/x86_64');
expect(list[0].skin).toEqual("1080x1920"); expect(list[0].skin).toEqual('1080x1920');
}).fail(function (err) { }).fail(function (err) {
expect(err).toBeUndefined(); expect(err).toBeUndefined();
}).fin(function () { }).fin(function () {
@ -70,42 +69,41 @@ describe("emulator", function () {
}); });
}); });
}); });
describe("list_images", function() { describe('list_images', function () {
beforeEach(function () { beforeEach(function () {
spyOn(fs, "realpathSync").and.callFake(function(cmd) { spyOn(fs, 'realpathSync').and.callFake(function (cmd) {
return cmd; return cmd;
}); });
}); });
it("should try to parse AVD information using `avdmanager` first", function() { it('should try to parse AVD information using `avdmanager` first', function () {
spyOn(shelljs, "which").and.callFake(function(cmd) { spyOn(shelljs, 'which').and.callFake(function (cmd) {
if (cmd == "avdmanager") { if (cmd === 'avdmanager') {
return true; return true;
} else { } else {
return false; return false;
} }
}); });
var avdmanager_spy = spyOn(emu, "list_images_using_avdmanager").and.returnValue({catch:function(){}}); var avdmanager_spy = spyOn(emu, 'list_images_using_avdmanager').and.returnValue({catch: function () {}});
emu.list_images(); emu.list_images();
expect(avdmanager_spy).toHaveBeenCalled(); expect(avdmanager_spy).toHaveBeenCalled();
}); });
it("should delegate to `android` if `avdmanager` cant be found and `android` can", function() { it('should delegate to `android` if `avdmanager` cant be found and `android` can', function () {
spyOn(shelljs, "which").and.callFake(function(cmd) { spyOn(shelljs, 'which').and.callFake(function (cmd) {
if (cmd == "avdmanager") { if (cmd === 'avdmanager') {
return false; return false;
} else { } else {
return true; return true;
} }
}); });
var android_spy = spyOn(emu, "list_images_using_android"); var android_spy = spyOn(emu, 'list_images_using_android');
emu.list_images(); emu.list_images();
expect(android_spy).toHaveBeenCalled(); expect(android_spy).toHaveBeenCalled();
}); });
it("should throw an error if neither `avdmanager` nor `android` are able to be found", function(done) { it('should throw an error if neither `avdmanager` nor `android` are able to be found', function (done) {
spyOn(shelljs, "which").and.returnValue(false); spyOn(shelljs, 'which').and.returnValue(false);
return emu.list_images() return emu.list_images().catch(function (err) {
.catch(function(err) {
expect(err).toBeDefined(); expect(err).toBeDefined();
expect(err.message).toContain("Could not find either `android` or `avdmanager`"); expect(err.message).toContain('Could not find either `android` or `avdmanager`');
done(); done();
}); });
}); });

View File

@ -48,8 +48,8 @@ describe('common platform handler', function() {
shell.mkdir('-p', project_dir); shell.mkdir('-p', project_dir);
fs.writeFileSync(non_plugin_file, 'contents', 'utf-8'); fs.writeFileSync(non_plugin_file, 'contents', 'utf-8');
var outside_file = '../non_plugin_file'; var outside_file = '../non_plugin_file';
expect(function(){copyFile(test_dir, outside_file, project_dir, dest);}). expect(function () { copyFile(test_dir, outside_file, project_dir, dest); })
toThrow(new Error('File "' + path.resolve(test_dir, outside_file) + '" is located outside the plugin directory "' + test_dir + '"')); .toThrow(new Error('File "' + path.resolve(test_dir, outside_file) + '" is located outside the plugin directory "' + test_dir + '"'));
shell.rm('-rf', test_dir); shell.rm('-rf', test_dir);
}); });
@ -75,16 +75,16 @@ describe('common platform handler', function() {
return; return;
} }
expect(function(){copyFile(test_dir, symlink_file, project_dir, dest);}). expect(function () { copyFile(test_dir, symlink_file, project_dir, dest); })
toThrow(new Error('File "' + path.resolve(test_dir, symlink_file) + '" is located outside the plugin directory "' + test_dir + '"')); .toThrow(new Error('File "' + path.resolve(test_dir, symlink_file) + '" is located outside the plugin directory "' + test_dir + '"'));
shell.rm('-rf', project_dir); shell.rm('-rf', project_dir);
}); });
it('Test#005 : should throw if dest is outside the project directory', function () { it('Test#005 : should throw if dest is outside the project directory', function () {
shell.mkdir('-p', java_dir); shell.mkdir('-p', java_dir);
fs.writeFileSync(java_file, 'contents', 'utf-8'); fs.writeFileSync(java_file, 'contents', 'utf-8');
expect(function(){copyFile(test_dir, java_file, project_dir, non_plugin_file);}). expect(function () { copyFile(test_dir, java_file, project_dir, non_plugin_file); })
toThrow(new Error('Destination "' + path.resolve(project_dir, non_plugin_file) + '" for source file "' + path.resolve(test_dir, java_file) + '" is located outside the project')); .toThrow(new Error('Destination "' + path.resolve(project_dir, non_plugin_file) + '" for source file "' + path.resolve(test_dir, java_file) + '" is located outside the project'));
shell.rm('-rf', project_dir); shell.rm('-rf', project_dir);
}); });
@ -121,8 +121,8 @@ describe('common platform handler', function() {
describe('copyNewFile', function () { describe('copyNewFile', function () {
it('Test#008 : should throw if target path exists', function () { it('Test#008 : should throw if target path exists', function () {
shell.mkdir('-p', dest); shell.mkdir('-p', dest);
expect(function(){copyNewFile(test_dir, src, project_dir, dest);}). expect(function () { copyNewFile(test_dir, src, project_dir, dest); })
toThrow(new Error('"' + dest + '" already exists!')); .toThrow(new Error('"' + dest + '" already exists!'));
shell.rm('-rf', dest); shell.rm('-rf', dest);
}); });
}); });

View File

@ -34,9 +34,9 @@ var PluginInfo = require('cordova-common').PluginInfo;
var AndroidProject = require('../../../bin/templates/cordova/lib/AndroidProject'); var AndroidProject = require('../../../bin/templates/cordova/lib/AndroidProject');
var dummyPluginInfo = new PluginInfo(dummyplugin); var dummyPluginInfo = new PluginInfo(dummyplugin);
var valid_source = dummyPluginInfo.getSourceFiles('android'), var valid_source = dummyPluginInfo.getSourceFiles('android');
valid_resources = dummyPluginInfo.getResourceFiles('android'), var valid_resources = dummyPluginInfo.getResourceFiles('android');
valid_libs = dummyPluginInfo.getLibFiles('android'); var valid_libs = dummyPluginInfo.getLibFiles('android');
var faultyPluginInfo = new PluginInfo(faultyplugin); var faultyPluginInfo = new PluginInfo(faultyplugin);
var invalid_source = faultyPluginInfo.getSourceFiles('android'); var invalid_source = faultyPluginInfo.getSourceFiles('android');
@ -198,7 +198,8 @@ describe('android project handler', function() {
describe('of <asset> elements', function () { describe('of <asset> elements', function () {
var asset = {src: 'www/dummyPlugin.js', target: 'foo/dummy.js'}; var asset = {src: 'www/dummyPlugin.js', target: 'foo/dummy.js'};
var wwwDest, platformWwwDest; var wwwDest; /* eslint no-unused-vars: "off" */
var platformWwwDest; /* eslint no-unused-vars: "off" */
beforeEach(function () { beforeEach(function () {
wwwDest = path.resolve(dummyProject.www, asset.target); wwwDest = path.resolve(dummyProject.www, asset.target);
@ -321,10 +322,10 @@ describe('android project handler', function() {
}); });
}); });
describe('of <js-module> elements', function () { describe('of <js-module> elements', function () {
var jsModule = {src: 'www/dummyPlugin.js'}; var jsModule = {src: 'www/dummyPlugin.js'};
var wwwDest, platformWwwDest; var wwwDest;
var platformWwwDest;
beforeEach(function () { beforeEach(function () {
wwwDest = path.resolve(dummyProject.www, 'plugins', dummyPluginInfo.id, jsModule.src); wwwDest = path.resolve(dummyProject.www, 'plugins', dummyPluginInfo.id, jsModule.src);

View File

@ -17,21 +17,21 @@
under the License. under the License.
*/ */
var rewire = require("rewire"); var rewire = require('rewire');
var run = rewire("../../bin/templates/cordova/lib/run"); var run = rewire('../../bin/templates/cordova/lib/run');
var getInstallTarget = run.__get__("getInstallTarget"); var getInstallTarget = run.__get__('getInstallTarget');
describe("run", function () { describe('run', function () {
describe("getInstallTarget", function() { describe('getInstallTarget', function () {
var targetOpts = { target: "emu" }; var targetOpts = { target: 'emu' };
var deviceOpts = { device: true }; var deviceOpts = { device: true };
var emulatorOpts = { emulator: true }; var emulatorOpts = { emulator: true };
var emptyOpts = {}; var emptyOpts = {};
it("Test#001 : should select correct target based on the run opts", function() { it('Test#001 : should select correct target based on the run opts', function () {
expect(getInstallTarget(targetOpts)).toBe("emu"); expect(getInstallTarget(targetOpts)).toBe('emu');
expect(getInstallTarget(deviceOpts)).toBe("--device"); expect(getInstallTarget(deviceOpts)).toBe('--device');
expect(getInstallTarget(emulatorOpts)).toBe("--emulator"); expect(getInstallTarget(emulatorOpts)).toBe('--emulator');
expect(getInstallTarget(emptyOpts)).toBeUndefined(); expect(getInstallTarget(emptyOpts)).toBeUndefined();
}); });
}); });