mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-23 09:22:53 +08:00
refactor: unify target resolution for devices & emulators (#1101)
* refactor: unify target resolution for devices & emulators * fix: use unified target methods in platform-centric bins
This commit is contained in:
parent
c774bf3311
commit
c04ea9b1c0
48
bin/templates/cordova/lib/device.js
vendored
48
bin/templates/cordova/lib/device.js
vendored
@ -1,48 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var build = require('./build');
|
||||
var Adb = require('./Adb');
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
/**
|
||||
* Returns a promise for the list of the device ID's found
|
||||
*/
|
||||
module.exports.list = async () => {
|
||||
return (await Adb.devices())
|
||||
.filter(id => !id.startsWith('emulator-'));
|
||||
};
|
||||
|
||||
module.exports.resolveTarget = function (target) {
|
||||
return this.list().then(function (device_list) {
|
||||
if (!device_list || !device_list.length) {
|
||||
return Promise.reject(new CordovaError('Failed to deploy to device, no devices found.'));
|
||||
}
|
||||
// default device
|
||||
target = target || device_list[0];
|
||||
|
||||
if (device_list.indexOf(target) < 0) {
|
||||
return Promise.reject(new CordovaError('ERROR: Unable to find target \'' + target + '\'.'));
|
||||
}
|
||||
|
||||
return build.detectArchitecture(target).then(function (arch) {
|
||||
return { target: target, arch: arch, isEmulator: false };
|
||||
});
|
||||
});
|
||||
};
|
19
bin/templates/cordova/lib/emulator.js
vendored
19
bin/templates/cordova/lib/emulator.js
vendored
@ -20,7 +20,6 @@
|
||||
const execa = require('execa');
|
||||
const fs = require('fs-extra');
|
||||
var android_versions = require('android-versions');
|
||||
var build = require('./build');
|
||||
var path = require('path');
|
||||
var Adb = require('./Adb');
|
||||
var events = require('cordova-common').events;
|
||||
@ -349,21 +348,3 @@ module.exports.wait_for_boot = function (emulator_id, time_remaining) {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.resolveTarget = function (target) {
|
||||
return this.list_started().then(function (emulator_list) {
|
||||
if (emulator_list.length < 1) {
|
||||
return Promise.reject(new CordovaError('No running Android emulators found, please start an emulator before deploying your project.'));
|
||||
}
|
||||
|
||||
// default emulator
|
||||
target = target || emulator_list[0];
|
||||
if (emulator_list.indexOf(target) < 0) {
|
||||
return Promise.reject(new CordovaError('Unable to find target \'' + target + '\'. Failed to deploy to emulator.'));
|
||||
}
|
||||
|
||||
return build.detectArchitecture(target).then(function (arch) {
|
||||
return { target: target, arch: arch, isEmulator: true };
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -19,21 +19,21 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const { install } = require('./target');
|
||||
var device = require('./device');
|
||||
const { resolve, install } = require('./target');
|
||||
|
||||
var args = process.argv;
|
||||
const targetSpec = { type: 'device' };
|
||||
|
||||
if (args.length > 2) {
|
||||
var install_target;
|
||||
if (args[2].substring(0, 9) === '--target=') {
|
||||
install_target = args[2].substring(9, args[2].length);
|
||||
targetSpec.id = args[2].substring(9, args[2].length);
|
||||
} else {
|
||||
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
device.resolveTarget(install_target).then(install).catch(err => {
|
||||
resolve(targetSpec).then(install).catch(err => {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
|
@ -19,21 +19,21 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const { install } = require('./target');
|
||||
var emulator = require('./emulator');
|
||||
var args = process.argv;
|
||||
const { resolve, install } = require('./target');
|
||||
|
||||
var args = process.argv;
|
||||
const targetSpec = { type: 'emulator' };
|
||||
|
||||
var install_target;
|
||||
if (args.length > 2) {
|
||||
if (args[2].substring(0, 9) === '--target=') {
|
||||
install_target = args[2].substring(9, args[2].length);
|
||||
targetSpec.id = args[2].substring(9, args[2].length);
|
||||
} else {
|
||||
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
emulator.resolveTarget(install_target).then(install).catch(err => {
|
||||
resolve(targetSpec).then(install).catch(err => {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
|
@ -19,14 +19,16 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var devices = require('./device');
|
||||
const { list } = require('./target');
|
||||
|
||||
// Usage support for when args are given
|
||||
require('./check_reqs').check_android().then(function () {
|
||||
devices.list().then(function (device_list) {
|
||||
device_list && device_list.forEach(function (dev) {
|
||||
console.log(dev);
|
||||
});
|
||||
list().then(targets => {
|
||||
const deviceIds = targets
|
||||
.filter(({ type }) => type === 'device')
|
||||
.map(({ id }) => id);
|
||||
|
||||
console.log(deviceIds.join('\n'));
|
||||
}, function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
|
78
bin/templates/cordova/lib/run.js
vendored
78
bin/templates/cordova/lib/run.js
vendored
@ -19,22 +19,30 @@
|
||||
|
||||
var path = require('path');
|
||||
var emulator = require('./emulator');
|
||||
var device = require('./device');
|
||||
const target = require('./target');
|
||||
var PackageType = require('./PackageType');
|
||||
const { CordovaError, events } = require('cordova-common');
|
||||
const { events } = require('cordova-common');
|
||||
|
||||
function getInstallTarget (runOptions) {
|
||||
var install_target;
|
||||
/**
|
||||
* Builds a target spec from a runOptions object
|
||||
*
|
||||
* @param {{target?: string, device?: boolean, emulator?: boolean}} runOptions
|
||||
* @return {target.TargetSpec}
|
||||
*/
|
||||
function buildTargetSpec (runOptions) {
|
||||
const spec = {};
|
||||
if (runOptions.target) {
|
||||
install_target = runOptions.target;
|
||||
spec.id = runOptions.target;
|
||||
} else if (runOptions.device) {
|
||||
install_target = '--device';
|
||||
spec.type = 'device';
|
||||
} else if (runOptions.emulator) {
|
||||
install_target = '--emulator';
|
||||
spec.type = 'emulator';
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
return install_target;
|
||||
function formatResolvedTarget ({ id, type }) {
|
||||
return `${type} ${id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -51,55 +59,11 @@ module.exports.run = function (runOptions) {
|
||||
runOptions = runOptions || {};
|
||||
|
||||
var self = this;
|
||||
var install_target = getInstallTarget(runOptions);
|
||||
const spec = buildTargetSpec(runOptions);
|
||||
|
||||
return target.resolve(spec).then(function (resolvedTarget) {
|
||||
events.emit('log', `Deploying to ${formatResolvedTarget(resolvedTarget)}`);
|
||||
|
||||
return Promise.resolve().then(function () {
|
||||
if (!install_target) {
|
||||
// no target given, deploy to device if available, otherwise use the emulator.
|
||||
return device.list().then(function (device_list) {
|
||||
if (device_list.length > 0) {
|
||||
events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.');
|
||||
install_target = device_list[0];
|
||||
} else {
|
||||
events.emit('warn', 'No target specified and no devices found, deploying to emulator');
|
||||
install_target = '--emulator';
|
||||
}
|
||||
});
|
||||
}
|
||||
}).then(function () {
|
||||
if (install_target === '--device') {
|
||||
return device.resolveTarget(null);
|
||||
} else if (install_target === '--emulator') {
|
||||
// Give preference to any already started emulators. Else, start one.
|
||||
return emulator.list_started().then(function (started) {
|
||||
return started && started.length > 0 ? started[0] : emulator.start();
|
||||
}).then(function (emulatorId) {
|
||||
return emulator.resolveTarget(emulatorId);
|
||||
});
|
||||
}
|
||||
// They specified a specific device/emulator ID.
|
||||
return device.list().then(function (devices) {
|
||||
if (devices.indexOf(install_target) > -1) {
|
||||
return device.resolveTarget(install_target);
|
||||
}
|
||||
return emulator.list_started().then(function (started_emulators) {
|
||||
if (started_emulators.indexOf(install_target) > -1) {
|
||||
return emulator.resolveTarget(install_target);
|
||||
}
|
||||
return emulator.list_images().then(function (avds) {
|
||||
// if target emulator isn't started, then start it.
|
||||
for (var avd in avds) {
|
||||
if (avds[avd].name === install_target) {
|
||||
return emulator.start(install_target).then(function (emulatorId) {
|
||||
return emulator.resolveTarget(emulatorId);
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.reject(new CordovaError(`Target '${install_target}' not found, unable to run project`));
|
||||
});
|
||||
});
|
||||
});
|
||||
}).then(function (resolvedTarget) {
|
||||
return new Promise((resolve) => {
|
||||
const buildOptions = require('./build').parseBuildOptions(runOptions, null, self.root);
|
||||
|
||||
@ -112,7 +76,7 @@ module.exports.run = function (runOptions) {
|
||||
|
||||
resolve(self._builder.fetchBuildResults(buildOptions.buildType, buildOptions.arch));
|
||||
}).then(async function (buildResults) {
|
||||
if (resolvedTarget && resolvedTarget.isEmulator) {
|
||||
if (resolvedTarget.type === 'emulator') {
|
||||
await emulator.wait_for_boot(resolvedTarget.id);
|
||||
}
|
||||
|
||||
|
86
bin/templates/cordova/lib/target.js
vendored
86
bin/templates/cordova/lib/target.js
vendored
@ -18,17 +18,97 @@
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const { inspect } = require('util');
|
||||
const Adb = require('./Adb');
|
||||
const build = require('./build');
|
||||
const emulator = require('./emulator');
|
||||
const AndroidManifest = require('./AndroidManifest');
|
||||
const { compareBy } = require('./utils');
|
||||
const { retryPromise } = require('./retry');
|
||||
const { events } = require('cordova-common');
|
||||
const { events, CordovaError } = require('cordova-common');
|
||||
|
||||
const INSTALL_COMMAND_TIMEOUT = 5 * 60 * 1000;
|
||||
const NUM_INSTALL_RETRIES = 3;
|
||||
const EXEC_KILL_SIGNAL = 'SIGKILL';
|
||||
|
||||
exports.install = async function ({ target, arch, isEmulator }, buildResults) {
|
||||
/**
|
||||
* @typedef { 'device' | 'emulator' } TargetType
|
||||
* @typedef { { id: string, type: TargetType } } Target
|
||||
* @typedef { { id?: string, type?: TargetType } } TargetSpec
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a list of available targets (connected devices & started emulators)
|
||||
*
|
||||
* @return {Promise<Target[]>}
|
||||
*/
|
||||
exports.list = async () => {
|
||||
return (await Adb.devices())
|
||||
.map(id => ({
|
||||
id,
|
||||
type: id.startsWith('emulator-') ? 'emulator' : 'device'
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {TargetSpec?} spec
|
||||
* @return {Promise<Target>}
|
||||
*/
|
||||
async function resolveToOnlineTarget (spec = {}) {
|
||||
const targetList = await exports.list();
|
||||
if (targetList.length === 0) return null;
|
||||
|
||||
// Sort by type: devices first, then emulators.
|
||||
targetList.sort(compareBy(t => t.type));
|
||||
|
||||
// Find first matching target for spec. {} matches any target.
|
||||
return targetList.find(target =>
|
||||
Object.keys(spec).every(k => spec[k] === target[k])
|
||||
) || null;
|
||||
}
|
||||
|
||||
async function isEmulatorName (name) {
|
||||
const emus = await emulator.list_images();
|
||||
return emus.some(avd => avd.name === name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TargetSpec?} spec
|
||||
* @return {Promise<Target>}
|
||||
*/
|
||||
async function resolveToOfflineEmulator (spec = {}) {
|
||||
if (spec.type === 'device') return null;
|
||||
if (spec.id && !(await isEmulatorName(spec.id))) return null;
|
||||
|
||||
// try to start an emulator with name spec.id
|
||||
// if spec.id is undefined, picks best match regarding target API
|
||||
const emulatorId = await emulator.start(spec.id);
|
||||
|
||||
return { id: emulatorId, type: 'emulator' };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TargetSpec?} spec
|
||||
* @return {Promise<Target & {arch: string}>}
|
||||
*/
|
||||
exports.resolve = async (spec = {}) => {
|
||||
events.emit('verbose', `Trying to find target matching ${inspect(spec)}`);
|
||||
|
||||
const resolvedTarget =
|
||||
(await resolveToOnlineTarget(spec)) ||
|
||||
(await resolveToOfflineEmulator(spec));
|
||||
|
||||
if (!resolvedTarget) {
|
||||
throw new CordovaError(`Could not find target matching ${inspect(spec)}`);
|
||||
}
|
||||
|
||||
return {
|
||||
...resolvedTarget,
|
||||
arch: await build.detectArchitecture(resolvedTarget.id)
|
||||
};
|
||||
};
|
||||
|
||||
exports.install = async function ({ id: target, arch, type }, buildResults) {
|
||||
const apk_path = build.findBestApkForArchitecture(buildResults, arch);
|
||||
const manifest = new AndroidManifest(path.join(__dirname, '../../app/src/main/AndroidManifest.xml'));
|
||||
const pkgName = manifest.getPackageId();
|
||||
@ -56,7 +136,7 @@ exports.install = async function ({ target, arch, isEmulator }, buildResults) {
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmulator) {
|
||||
if (type === 'emulator') {
|
||||
// Work around sporadic emulator hangs: http://issues.apache.org/jira/browse/CB-9119
|
||||
await retryPromise(NUM_INSTALL_RETRIES, () => doInstall({
|
||||
timeout: INSTALL_COMMAND_TIMEOUT,
|
||||
|
@ -1,106 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const rewire = require('rewire');
|
||||
|
||||
const CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
describe('device', () => {
|
||||
const DEVICE_LIST = ['device1', 'device2', 'device3'];
|
||||
let AdbSpy;
|
||||
let device;
|
||||
|
||||
beforeEach(() => {
|
||||
device = rewire('../../bin/templates/cordova/lib/device');
|
||||
AdbSpy = jasmine.createSpyObj('Adb', ['devices', 'install', 'shell', 'start', 'uninstall']);
|
||||
device.__set__('Adb', AdbSpy);
|
||||
});
|
||||
|
||||
describe('list', () => {
|
||||
it('should return a list of all connected devices', () => {
|
||||
AdbSpy.devices.and.resolveTo(['emulator-5556', '123a76565509e124']);
|
||||
|
||||
return device.list().then(list => {
|
||||
expect(list).toEqual(['123a76565509e124']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveTarget', () => {
|
||||
let buildSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
buildSpy = jasmine.createSpyObj('build', ['detectArchitecture']);
|
||||
buildSpy.detectArchitecture.and.returnValue(Promise.resolve());
|
||||
device.__set__('build', buildSpy);
|
||||
|
||||
spyOn(device, 'list').and.returnValue(Promise.resolve(DEVICE_LIST));
|
||||
});
|
||||
|
||||
it('should select the first device to be the target if none is specified', () => {
|
||||
return device.resolveTarget().then(deviceInfo => {
|
||||
expect(deviceInfo.target).toBe(DEVICE_LIST[0]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should use the given target instead of the default', () => {
|
||||
return device.resolveTarget(DEVICE_LIST[2]).then(deviceInfo => {
|
||||
expect(deviceInfo.target).toBe(DEVICE_LIST[2]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set emulator to false', () => {
|
||||
return device.resolveTarget().then(deviceInfo => {
|
||||
expect(deviceInfo.isEmulator).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if there are no devices', () => {
|
||||
device.list.and.returnValue(Promise.resolve([]));
|
||||
|
||||
return device.resolveTarget().then(
|
||||
() => fail('Unexpectedly resolved'),
|
||||
err => {
|
||||
expect(err).toEqual(jasmine.any(CordovaError));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if the specified target does not exist', () => {
|
||||
return device.resolveTarget('nonexistent-target').then(
|
||||
() => fail('Unexpectedly resolved'),
|
||||
err => {
|
||||
expect(err).toEqual(jasmine.any(CordovaError));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should detect the architecture and return it with the device info', () => {
|
||||
const target = DEVICE_LIST[1];
|
||||
const arch = 'unittestarch';
|
||||
|
||||
buildSpy.detectArchitecture.and.returnValue(Promise.resolve(arch));
|
||||
|
||||
return device.resolveTarget(target).then(deviceInfo => {
|
||||
expect(buildSpy.detectArchitecture).toHaveBeenCalledWith(target);
|
||||
expect(deviceInfo.arch).toBe(arch);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -26,7 +26,6 @@ const CordovaError = require('cordova-common').CordovaError;
|
||||
const check_reqs = require('../../bin/templates/cordova/lib/check_reqs');
|
||||
|
||||
describe('emulator', () => {
|
||||
const EMULATOR_LIST = ['emulator-5555', 'emulator-5556', 'emulator-5557'];
|
||||
let emu;
|
||||
|
||||
beforeEach(() => {
|
||||
@ -529,50 +528,4 @@ describe('emulator', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveTarget', () => {
|
||||
const arch = 'arm7-test';
|
||||
|
||||
beforeEach(() => {
|
||||
const buildSpy = jasmine.createSpyObj('build', ['detectArchitecture']);
|
||||
buildSpy.detectArchitecture.and.returnValue(Promise.resolve(arch));
|
||||
emu.__set__('build', buildSpy);
|
||||
|
||||
spyOn(emu, 'list_started').and.returnValue(Promise.resolve(EMULATOR_LIST));
|
||||
});
|
||||
|
||||
it('should throw an error if there are no running emulators', () => {
|
||||
emu.list_started.and.returnValue(Promise.resolve([]));
|
||||
|
||||
return emu.resolveTarget().then(
|
||||
() => fail('Unexpectedly resolved'),
|
||||
err => {
|
||||
expect(err).toEqual(jasmine.any(CordovaError));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if the requested emulator is not running', () => {
|
||||
const targetEmulator = 'unstarted-emu';
|
||||
|
||||
return emu.resolveTarget(targetEmulator).then(
|
||||
() => fail('Unexpectedly resolved'),
|
||||
err => {
|
||||
expect(err.message).toContain(targetEmulator);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should return info on the first running emulator if none is specified', () => {
|
||||
return emu.resolveTarget().then(emulatorInfo => {
|
||||
expect(emulatorInfo.target).toBe(EMULATOR_LIST[0]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the emulator info', () => {
|
||||
return emu.resolveTarget(EMULATOR_LIST[1]).then(emulatorInfo => {
|
||||
expect(emulatorInfo).toEqual({ target: EMULATOR_LIST[1], arch, isEmulator: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -25,45 +25,38 @@ describe('run', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
run = rewire('../../bin/templates/cordova/lib/run');
|
||||
run.__set__({
|
||||
events: jasmine.createSpyObj('eventsSpy', ['emit'])
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInstallTarget', () => {
|
||||
const targetOpts = { target: 'emu' };
|
||||
const deviceOpts = { device: true };
|
||||
const emulatorOpts = { emulator: true };
|
||||
const emptyOpts = {};
|
||||
|
||||
describe('buildTargetSpec', () => {
|
||||
it('Test#001 : should select correct target based on the run opts', () => {
|
||||
const getInstallTarget = run.__get__('getInstallTarget');
|
||||
expect(getInstallTarget(targetOpts)).toBe('emu');
|
||||
expect(getInstallTarget(deviceOpts)).toBe('--device');
|
||||
expect(getInstallTarget(emulatorOpts)).toBe('--emulator');
|
||||
expect(getInstallTarget(emptyOpts)).toBeUndefined();
|
||||
const buildTargetSpec = run.__get__('buildTargetSpec');
|
||||
|
||||
expect(buildTargetSpec({ target: 'emu' })).toEqual({ id: 'emu' });
|
||||
expect(buildTargetSpec({ device: true })).toEqual({ type: 'device' });
|
||||
expect(buildTargetSpec({ emulator: true })).toEqual({ type: 'emulator' });
|
||||
expect(buildTargetSpec({})).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('run method', () => {
|
||||
let deviceSpyObj;
|
||||
let emulatorSpyObj;
|
||||
let targetSpyObj;
|
||||
let eventsSpyObj;
|
||||
let getInstallTargetSpy;
|
||||
let targetSpyObj, emulatorSpyObj, resolvedTarget;
|
||||
|
||||
beforeEach(() => {
|
||||
deviceSpyObj = jasmine.createSpyObj('deviceSpy', ['list', 'resolveTarget']);
|
||||
emulatorSpyObj = jasmine.createSpyObj('emulatorSpy', ['list_images', 'list_started', 'resolveTarget', 'start', 'wait_for_boot']);
|
||||
eventsSpyObj = jasmine.createSpyObj('eventsSpy', ['emit']);
|
||||
getInstallTargetSpy = jasmine.createSpy('getInstallTargetSpy');
|
||||
resolvedTarget = { id: 'dev1', type: 'device', arch: 'atari' };
|
||||
|
||||
targetSpyObj = jasmine.createSpyObj('target', ['install']);
|
||||
targetSpyObj = jasmine.createSpyObj('deviceSpy', ['resolve', 'install']);
|
||||
targetSpyObj.resolve.and.resolveTo(resolvedTarget);
|
||||
targetSpyObj.install.and.resolveTo();
|
||||
|
||||
emulatorSpyObj = jasmine.createSpyObj('emulatorSpy', ['wait_for_boot']);
|
||||
emulatorSpyObj.wait_for_boot.and.resolveTo();
|
||||
|
||||
run.__set__({
|
||||
device: deviceSpyObj,
|
||||
emulator: emulatorSpyObj,
|
||||
target: targetSpyObj,
|
||||
events: eventsSpyObj,
|
||||
getInstallTarget: getInstallTargetSpy
|
||||
emulator: emulatorSpyObj
|
||||
});
|
||||
|
||||
// run needs `this` to behave like an Api instance
|
||||
@ -72,152 +65,19 @@ describe('run', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should run on default device when no target arguments are specified', () => {
|
||||
const deviceList = ['testDevice1', 'testDevice2'];
|
||||
|
||||
getInstallTargetSpy.and.returnValue(null);
|
||||
deviceSpyObj.list.and.returnValue(Promise.resolve(deviceList));
|
||||
|
||||
it('should install on target after build', () => {
|
||||
return run.run().then(() => {
|
||||
expect(deviceSpyObj.resolveTarget).toHaveBeenCalledWith(deviceList[0]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should run on emulator when no target arguments are specified, and no devices are found', () => {
|
||||
const deviceList = [];
|
||||
|
||||
getInstallTargetSpy.and.returnValue(null);
|
||||
deviceSpyObj.list.and.returnValue(Promise.resolve(deviceList));
|
||||
emulatorSpyObj.list_started.and.returnValue(Promise.resolve([]));
|
||||
|
||||
return run.run().then(() => {
|
||||
expect(emulatorSpyObj.list_started).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should run on default device when device is requested, but none specified', () => {
|
||||
getInstallTargetSpy.and.returnValue('--device');
|
||||
|
||||
return run.run().then(() => {
|
||||
// Default device is selected by calling device.resolveTarget(null)
|
||||
expect(deviceSpyObj.resolveTarget).toHaveBeenCalledWith(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should run on a running emulator if one exists', () => {
|
||||
const emulatorList = ['emulator1', 'emulator2'];
|
||||
|
||||
getInstallTargetSpy.and.returnValue('--emulator');
|
||||
emulatorSpyObj.list_started.and.returnValue(Promise.resolve(emulatorList));
|
||||
|
||||
return run.run().then(() => {
|
||||
expect(emulatorSpyObj.resolveTarget).toHaveBeenCalledWith(emulatorList[0]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should start an emulator and run on that if none is running', () => {
|
||||
const emulatorList = [];
|
||||
const defaultEmulator = 'default-emu';
|
||||
|
||||
getInstallTargetSpy.and.returnValue('--emulator');
|
||||
emulatorSpyObj.list_started.and.returnValue(Promise.resolve(emulatorList));
|
||||
emulatorSpyObj.start.and.returnValue(Promise.resolve(defaultEmulator));
|
||||
|
||||
return run.run().then(() => {
|
||||
expect(emulatorSpyObj.resolveTarget).toHaveBeenCalledWith(defaultEmulator);
|
||||
});
|
||||
});
|
||||
|
||||
it('should run on a named device if it is specified', () => {
|
||||
const deviceList = ['device1', 'device2', 'device3'];
|
||||
getInstallTargetSpy.and.returnValue(deviceList[1]);
|
||||
|
||||
deviceSpyObj.list.and.returnValue(Promise.resolve(deviceList));
|
||||
|
||||
return run.run().then(() => {
|
||||
expect(deviceSpyObj.resolveTarget).toHaveBeenCalledWith(deviceList[1]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should run on a named emulator if it is specified', () => {
|
||||
const startedEmulatorList = ['emu1', 'emu2', 'emu3'];
|
||||
getInstallTargetSpy.and.returnValue(startedEmulatorList[2]);
|
||||
|
||||
deviceSpyObj.list.and.returnValue(Promise.resolve([]));
|
||||
emulatorSpyObj.list_started.and.returnValue(Promise.resolve(startedEmulatorList));
|
||||
|
||||
return run.run().then(() => {
|
||||
expect(emulatorSpyObj.resolveTarget).toHaveBeenCalledWith(startedEmulatorList[2]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should start named emulator and then run on it if it is specified', () => {
|
||||
const emulatorList = [
|
||||
{ name: 'emu1', id: 1 },
|
||||
{ name: 'emu2', id: 2 },
|
||||
{ name: 'emu3', id: 3 }
|
||||
];
|
||||
getInstallTargetSpy.and.returnValue(emulatorList[2].name);
|
||||
|
||||
deviceSpyObj.list.and.returnValue(Promise.resolve([]));
|
||||
emulatorSpyObj.list_started.and.returnValue(Promise.resolve([]));
|
||||
emulatorSpyObj.list_images.and.returnValue(Promise.resolve(emulatorList));
|
||||
emulatorSpyObj.start.and.returnValue(Promise.resolve(emulatorList[2].id));
|
||||
|
||||
return run.run().then(() => {
|
||||
expect(emulatorSpyObj.start).toHaveBeenCalledWith(emulatorList[2].name);
|
||||
expect(emulatorSpyObj.resolveTarget).toHaveBeenCalledWith(emulatorList[2].id);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if target is specified but does not exist', () => {
|
||||
const emulatorList = [{ name: 'emu1', id: 1 }];
|
||||
const deviceList = ['device1'];
|
||||
const target = 'nonexistentdevice';
|
||||
getInstallTargetSpy.and.returnValue(target);
|
||||
|
||||
deviceSpyObj.list.and.returnValue(Promise.resolve(deviceList));
|
||||
emulatorSpyObj.list_started.and.returnValue(Promise.resolve([]));
|
||||
emulatorSpyObj.list_images.and.returnValue(Promise.resolve(emulatorList));
|
||||
|
||||
return run.run().then(
|
||||
() => fail('Expected error to be thrown'),
|
||||
err => expect(err.message).toContain(target)
|
||||
expect(targetSpyObj.install).toHaveBeenCalledWith(
|
||||
resolvedTarget,
|
||||
{ apkPaths: [], buildType: 'debug' }
|
||||
);
|
||||
});
|
||||
|
||||
it('should install on device after build', () => {
|
||||
const deviceTarget = { target: 'device1', isEmulator: false };
|
||||
getInstallTargetSpy.and.returnValue('--device');
|
||||
deviceSpyObj.resolveTarget.and.returnValue(deviceTarget);
|
||||
|
||||
return run.run().then(() => {
|
||||
expect(targetSpyObj.install).toHaveBeenCalledWith(deviceTarget, { apkPaths: [], buildType: 'debug' });
|
||||
});
|
||||
});
|
||||
|
||||
it('should install on emulator after build', () => {
|
||||
const emulatorTarget = { target: 'emu1', isEmulator: true };
|
||||
getInstallTargetSpy.and.returnValue('--emulator');
|
||||
emulatorSpyObj.list_started.and.returnValue(Promise.resolve([emulatorTarget.target]));
|
||||
emulatorSpyObj.resolveTarget.and.returnValue(emulatorTarget);
|
||||
emulatorSpyObj.wait_for_boot.and.returnValue(Promise.resolve());
|
||||
|
||||
return run.run().then(() => {
|
||||
expect(targetSpyObj.install).toHaveBeenCalledWith(emulatorTarget, { apkPaths: [], buildType: 'debug' });
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail with the error message if --packageType=bundle setting is used', () => {
|
||||
const deviceList = ['testDevice1', 'testDevice2'];
|
||||
getInstallTargetSpy.and.returnValue(null);
|
||||
|
||||
deviceSpyObj.list.and.returnValue(Promise.resolve(deviceList));
|
||||
|
||||
return run.run({ argv: ['--packageType=bundle'] }).then(
|
||||
() => fail('Expected error to be thrown'),
|
||||
err => expect(err).toContain('Package type "bundle" is not supported during cordova run.')
|
||||
);
|
||||
targetSpyObj.resolve.and.resolveTo(resolvedTarget);
|
||||
return expectAsync(run.run({ argv: ['--packageType=bundle'] }))
|
||||
.toBeRejectedWith(jasmine.stringMatching(/Package type "bundle" is not supported/));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -27,6 +27,193 @@ describe('target', () => {
|
||||
target = rewire('../../bin/templates/cordova/lib/target');
|
||||
});
|
||||
|
||||
describe('list', () => {
|
||||
it('should return available targets from Adb.devices', () => {
|
||||
const AdbSpy = jasmine.createSpyObj('Adb', ['devices']);
|
||||
AdbSpy.devices.and.resolveTo(['emulator-5556', '123a76565509e124']);
|
||||
target.__set__('Adb', AdbSpy);
|
||||
|
||||
return target.list().then(emus => {
|
||||
expect(emus).toEqual([
|
||||
{ id: 'emulator-5556', type: 'emulator' },
|
||||
{ id: '123a76565509e124', type: 'device' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveToOnlineTarget', () => {
|
||||
let resolveToOnlineTarget, emus, devs;
|
||||
|
||||
beforeEach(() => {
|
||||
resolveToOnlineTarget = target.__get__('resolveToOnlineTarget');
|
||||
|
||||
emus = [
|
||||
{ id: 'emu1', type: 'emulator' },
|
||||
{ id: 'emu2', type: 'emulator' }
|
||||
];
|
||||
devs = [
|
||||
{ id: 'dev1', type: 'device' },
|
||||
{ id: 'dev2', type: 'device' }
|
||||
];
|
||||
|
||||
spyOn(target, 'list').and.returnValue([...emus, ...devs]);
|
||||
});
|
||||
|
||||
it('should return first device when no target arguments are specified', async () => {
|
||||
return resolveToOnlineTarget().then(result => {
|
||||
expect(result.id).toBe('dev1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return first emulator when no target arguments are specified and no devices are found', async () => {
|
||||
target.list.and.resolveTo(emus);
|
||||
return resolveToOnlineTarget().then(result => {
|
||||
expect(result.id).toBe('emu1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return first device when type device is specified', async () => {
|
||||
return resolveToOnlineTarget({ type: 'device' }).then(result => {
|
||||
expect(result.id).toBe('dev1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return first running emulator when type emulator is specified', async () => {
|
||||
return resolveToOnlineTarget({ type: 'emulator' }).then(result => {
|
||||
expect(result.id).toBe('emu1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a device that matches given ID', async () => {
|
||||
return resolveToOnlineTarget({ id: 'dev2' }).then(result => {
|
||||
expect(result.id).toBe('dev2');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a running emulator that matches given ID', async () => {
|
||||
return resolveToOnlineTarget({ id: 'emu2' }).then(result => {
|
||||
expect(result.id).toBe('emu2');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if there are no online targets', async () => {
|
||||
target.list.and.resolveTo([]);
|
||||
return expectAsync(resolveToOnlineTarget())
|
||||
.toBeResolvedTo(null);
|
||||
});
|
||||
|
||||
it('should return null if no target matches given ID', async () => {
|
||||
return expectAsync(resolveToOnlineTarget({ id: 'foo' }))
|
||||
.toBeResolvedTo(null);
|
||||
});
|
||||
|
||||
it('should return null if no target matches given type', async () => {
|
||||
target.list.and.resolveTo(devs);
|
||||
return expectAsync(resolveToOnlineTarget({ type: 'emulator' }))
|
||||
.toBeResolvedTo(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveToOfflineEmulator', () => {
|
||||
const emuId = 'emulator-5554';
|
||||
let resolveToOfflineEmulator, emulatorSpyObj;
|
||||
|
||||
beforeEach(() => {
|
||||
resolveToOfflineEmulator = target.__get__('resolveToOfflineEmulator');
|
||||
|
||||
emulatorSpyObj = jasmine.createSpyObj('emulatorSpy', ['start']);
|
||||
emulatorSpyObj.start.and.resolveTo(emuId);
|
||||
|
||||
target.__set__({
|
||||
emulator: emulatorSpyObj,
|
||||
isEmulatorName: name => name.startsWith('emu')
|
||||
});
|
||||
});
|
||||
|
||||
it('should start an emulator and run on that if none is running', () => {
|
||||
return resolveToOfflineEmulator().then(result => {
|
||||
expect(result).toEqual({ id: emuId, type: 'emulator' });
|
||||
expect(emulatorSpyObj.start).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should start named emulator and then run on it if it is specified', () => {
|
||||
return resolveToOfflineEmulator({ id: 'emu3' }).then(result => {
|
||||
expect(result).toEqual({ id: emuId, type: 'emulator' });
|
||||
expect(emulatorSpyObj.start).toHaveBeenCalledWith('emu3');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if given ID is not an avd name', () => {
|
||||
return resolveToOfflineEmulator({ id: 'dev1' }).then(result => {
|
||||
expect(result).toBe(null);
|
||||
expect(emulatorSpyObj.start).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if given type is not emulator', () => {
|
||||
return resolveToOfflineEmulator({ type: 'device' }).then(result => {
|
||||
expect(result).toBe(null);
|
||||
expect(emulatorSpyObj.start).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolve', () => {
|
||||
let resolveToOnlineTarget, resolveToOfflineEmulator;
|
||||
|
||||
beforeEach(() => {
|
||||
resolveToOnlineTarget = jasmine.createSpy('resolveToOnlineTarget')
|
||||
.and.resolveTo(null);
|
||||
|
||||
resolveToOfflineEmulator = jasmine.createSpy('resolveToOfflineEmulator')
|
||||
.and.resolveTo(null);
|
||||
|
||||
target.__set__({
|
||||
resolveToOnlineTarget,
|
||||
resolveToOfflineEmulator,
|
||||
build: { detectArchitecture: id => id + '-arch' }
|
||||
});
|
||||
});
|
||||
|
||||
it('should delegate to resolveToOnlineTarget', () => {
|
||||
const spec = { type: 'device' };
|
||||
resolveToOnlineTarget.and.resolveTo({ id: 'dev1', type: 'device' });
|
||||
|
||||
return target.resolve(spec).then(result => {
|
||||
expect(result.id).toBe('dev1');
|
||||
expect(resolveToOnlineTarget).toHaveBeenCalledWith(spec);
|
||||
expect(resolveToOfflineEmulator).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delegate to resolveToOfflineEmulator if resolveToOnlineTarget fails', () => {
|
||||
const spec = { type: 'emulator' };
|
||||
resolveToOfflineEmulator.and.resolveTo({ id: 'emu1', type: 'emulator' });
|
||||
|
||||
return target.resolve(spec).then(result => {
|
||||
expect(result.id).toBe('emu1');
|
||||
expect(resolveToOnlineTarget).toHaveBeenCalledWith(spec);
|
||||
expect(resolveToOfflineEmulator).toHaveBeenCalledWith(spec);
|
||||
});
|
||||
});
|
||||
|
||||
it('should add the target arch', () => {
|
||||
const spec = { type: 'device' };
|
||||
resolveToOnlineTarget.and.resolveTo({ id: 'dev1', type: 'device' });
|
||||
|
||||
return target.resolve(spec).then(result => {
|
||||
expect(result.arch).toBe('dev1-arch');
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if target cannot be resolved', () => {
|
||||
return expectAsync(target.resolve())
|
||||
.toBeRejectedWithError(/Could not find target matching/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('install', () => {
|
||||
let AndroidManifestSpy;
|
||||
let AndroidManifestFns;
|
||||
@ -36,7 +223,7 @@ describe('target', () => {
|
||||
let installTarget;
|
||||
|
||||
beforeEach(() => {
|
||||
installTarget = { target: 'emulator-5556', isEmulator: true, arch: 'atari' };
|
||||
installTarget = { id: 'emulator-5556', type: 'emulator', arch: 'atari' };
|
||||
|
||||
buildSpy = jasmine.createSpyObj('build', ['findBestApkForArchitecture']);
|
||||
target.__set__('build', buildSpy);
|
||||
@ -60,7 +247,7 @@ describe('target', () => {
|
||||
|
||||
it('should install to the passed target', () => {
|
||||
return target.install(installTarget, {}).then(() => {
|
||||
expect(AdbSpy.install.calls.argsFor(0)[0]).toBe(installTarget.target);
|
||||
expect(AdbSpy.install.calls.argsFor(0)[0]).toBe(installTarget.id);
|
||||
});
|
||||
});
|
||||
|
||||
@ -108,7 +295,7 @@ describe('target', () => {
|
||||
|
||||
it('should unlock the screen on device', () => {
|
||||
return target.install(installTarget, {}).then(() => {
|
||||
expect(AdbSpy.shell).toHaveBeenCalledWith(installTarget.target, 'input keyevent 82');
|
||||
expect(AdbSpy.shell).toHaveBeenCalledWith(installTarget.id, 'input keyevent 82');
|
||||
});
|
||||
});
|
||||
|
||||
@ -119,7 +306,7 @@ describe('target', () => {
|
||||
AndroidManifestGetActivitySpy.getName.and.returnValue(activityName);
|
||||
|
||||
return target.install(installTarget, {}).then(() => {
|
||||
expect(AdbSpy.start).toHaveBeenCalledWith(installTarget.target, `${packageId}/.${activityName}`);
|
||||
expect(AdbSpy.start).toHaveBeenCalledWith(installTarget.id, `${packageId}/.${activityName}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user