blob: 700240b043ed417656f643c3994771e7cc6d3b33 [file] [log] [blame]
/**
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 path = require('path');
const rewire = require('rewire');
const { events, cordova } = require('cordova-lib');
const telemetry = require('../src/telemetry');
describe('cordova cli', () => {
let cli, logger;
beforeEach(() => {
cli = rewire('../src/cli');
// Event registration is currently process-global. Since all jasmine
// tests in a directory run in a single process (and in parallel),
// logging events registered as a result of the "--verbose" flag in
// CLI testing below would cause lots of logging messages printed out by other specs.
// Prevent listeners from piling up
spyOn(process, 'on');
events.removeAllListeners();
// Spy and mute output
logger = jasmine.createSpyObj('logger', [
'subscribe', 'setLevel', 'results', 'warn'
]);
cli.__set__({ logger });
spyOn(console, 'log');
// Prevent accidentally turning telemetry on/off during testing
spyOn(telemetry, 'track');
spyOn(telemetry, 'turnOn');
spyOn(telemetry, 'turnOff');
spyOn(telemetry, 'showPrompt').and.returnValue(Promise.resolve());
});
describe('options', () => {
describe('version', () => {
const version = require('../package').version;
it('Test#001 : will spit out the version with -v', () => {
return cli(['node', 'cordova', '-v']).then(() => {
expect(logger.results.calls.mostRecent().args[0]).toMatch(version);
});
}, 60000);
it('Test#002 : will spit out the version with --version', () => {
return cli(['node', 'cordova', '--version']).then(() => {
expect(logger.results.calls.mostRecent().args[0]).toMatch(version);
});
}, 60000);
it('Test#003 : will spit out the version with -v anywhere', () => {
return cli(['node', 'cordova', 'one', '-v', 'three']).then(() => {
expect(logger.results.calls.mostRecent().args[0]).toMatch(version);
});
}, 60000);
});
});
describe('build', () => {
beforeEach(() => {
spyOn(cordova, 'build').and.returnValue(Promise.resolve());
});
it('Test#005 : will call command with all arguments passed through', () => {
return cli(['node', 'cordova', 'build', 'blackberry10', '--', '-k', 'abcd1234']).then(() => {
expect(cordova.build).toHaveBeenCalledWith({ platforms: ['blackberry10'], options: { argv: ['-k', 'abcd1234'] }, verbose: false, silent: false, nohooks: [ ], searchpath: undefined });
});
}, 60000);
it('Test#006 : will consume the first instance of -d', () => {
return cli(['node', 'cordova', '-d', 'build', 'blackberry10', '--', '-k', 'abcd1234', '-d']).then(() => {
expect(cordova.build).toHaveBeenCalledWith({ platforms: ['blackberry10'], options: { verbose: true, argv: ['-k', 'abcd1234', '-d'] }, verbose: true, silent: false, nohooks: [ ], searchpath: undefined });
});
});
it('Test#007 : will consume the first instance of --verbose', () => {
return cli(['node', 'cordova', '--verbose', 'build', 'blackberry10', '--', '-k', 'abcd1234', '--verbose']).then(() => {
expect(cordova.build).toHaveBeenCalledWith({ platforms: ['blackberry10'], options: { verbose: true, argv: ['-k', 'abcd1234', '--verbose'] }, verbose: true, silent: false, nohooks: [ ], searchpath: undefined });
});
});
it('Test#008 : will consume the first instance of either --verbose or -d', () => {
return cli(['node', 'cordova', '--verbose', 'build', 'blackberry10', '--', '-k', 'abcd1234', '-d']).then(() => {
expect(cordova.build).toHaveBeenCalledWith({ platforms: ['blackberry10'], options: { verbose: true, argv: ['-k', 'abcd1234', '-d'] }, verbose: true, silent: false, nohooks: [ ], searchpath: undefined });
});
});
it('Test#009 : will consume the first instance of either --verbose or -d', () => {
return cli(['node', 'cordova', '-d', 'build', 'blackberry10', '--', '-k', 'abcd1234', '--verbose']).then(() => {
expect(cordova.build).toHaveBeenCalledWith({ platforms: ['blackberry10'], options: { verbose: true, argv: ['-k', 'abcd1234', '--verbose'] }, verbose: true, silent: false, nohooks: [ ], searchpath: undefined });
});
});
it('Test#010 : will consume the first instance of --silent', () => {
return cli(['node', 'cordova', '--silent', 'build', 'blackberry10', '--', '-k', 'abcd1234', '--silent']).then(() => {
expect(cordova.build).toHaveBeenCalledWith({ platforms: ['blackberry10'], options: { silent: true, argv: ['-k', 'abcd1234', '--silent'] }, verbose: false, silent: true, nohooks: [ ], searchpath: undefined });
});
});
});
describe('create', () => {
beforeEach(() => {
cli.__set__({
cordovaCreate: jasmine.createSpy('cordovaCreate').and.resolveTo()
});
});
it('Test#011 : calls cordova create', () => {
return cli(['node', 'cordova', 'create', 'a', 'b', 'c', '--link-to', 'c:\\personalWWW']).then(() => {
expect(cli.__get__('cordovaCreate')).toHaveBeenCalledWith(
'a', 'b', 'c', jasmine.any(Object), jasmine.any(Object)
);
});
});
});
describe('plugin', () => {
beforeEach(() => {
spyOn(cordova, 'plugin').and.returnValue(Promise.resolve());
});
it('Test#012 : will pass variables', () => {
return cli(['node', 'cordova', 'plugin', 'add', 'facebook', '--variable', 'FOO=foo']).then(() => {
expect(cordova.plugin).toHaveBeenCalledWith(
'add',
['facebook'],
jasmine.any(Object)
);
const opts = cordova.plugin.calls.argsFor(0)[2];
expect(opts.cli_variables.FOO).toBe('foo');
});
});
it('Test#013 : will support variables with =', () => {
return cli(['node', 'cordova', 'plugin', 'add', 'facebook', '--variable', 'MOTO=DELTA=WAS=HERE']).then(() => {
expect(cordova.plugin).toHaveBeenCalledWith(
'add',
['facebook'],
jasmine.any(Object)
);
const opts = cordova.plugin.calls.argsFor(0)[2];
expect(opts.cli_variables.MOTO).toBe('DELTA=WAS=HERE');
});
});
it('Test#014 : will pass hook patterns to suppress', () => {
return cli(['node', 'cordova', 'plugin', 'add', 'facebook', '--nohooks', 'before_plugin_add']).then(() => {
expect(cordova.plugin).toHaveBeenCalledWith(
'add',
['facebook'],
jasmine.any(Object)
);
const opts = cordova.plugin.calls.argsFor(0)[2];
expect(opts.nohooks[0]).toBe('before_plugin_add');
});
});
it('Test #015 : (add) will pass save:true', () => {
return cli(['node', 'cordova', 'plugin', 'add', 'device']).then(() => {
expect(cordova.plugin).toHaveBeenCalledWith(
'add',
['device'],
jasmine.any(Object)
);
const opts = cordova.plugin.calls.argsFor(0)[2];
expect(opts.save).toBe(true);
});
});
it('Test #016 : (add) will pass save:false', () => {
return cli(['node', 'cordova', 'plugin', 'add', 'device', '--nosave']).then(() => {
expect(cordova.plugin).toHaveBeenCalledWith(
'add',
['device'],
jasmine.any(Object)
);
const opts = cordova.plugin.calls.argsFor(0)[2];
expect(opts.save).toBe(false);
});
});
it('Test #017: (remove) will pass save:false', () => {
return cli(['node', 'cordova', 'plugin', 'remove', 'device', '--nosave']).then(() => {
expect(cordova.plugin).toHaveBeenCalledWith(
'remove',
['device'],
jasmine.any(Object)
);
const opts = cordova.plugin.calls.argsFor(0)[2];
expect(opts.save).toBe(false);
});
});
it('Test #018 : (remove) autosave is default and will pass save:true', () => {
return cli(['node', 'cordova', 'plugin', 'remove', 'device']).then(() => {
expect(cordova.plugin).toHaveBeenCalledWith(
'remove',
['device'],
jasmine.any(Object)
);
const opts = cordova.plugin.calls.argsFor(0)[2];
expect(opts.save).toBe(true);
});
});
it('(add) will pass save-exact:true', () => {
return cli(['node', 'cordova', 'plugin', 'add', 'device', '--save-exact']).then(() => {
expect(cordova.plugin).toHaveBeenCalledWith(
'add',
['device'],
jasmine.any(Object)
);
const opts = cordova.plugin.calls.argsFor(0)[2];
expect(opts.save_exact).toBe(true);
});
});
it('(add) will pass noprod:true and production:false', () => {
return cli(['node', 'cordova', 'plugin', 'add', 'device', '--noprod']).then(() => {
expect(cordova.plugin).toHaveBeenCalledWith(
'add',
['device'],
jasmine.any(Object)
);
const opts = cordova.plugin.calls.argsFor(0)[2];
expect(opts.production).toBe(false);
});
});
});
describe('telemetry', () => {
const Insight = require('insight');
let isOptedOut;
beforeEach(() => {
// Allow testing if we _really_ would send tracking requests
telemetry.track.and.callThrough();
telemetry.turnOn.and.callThrough();
telemetry.turnOff.and.callThrough();
spyOn(Insight.prototype, 'track').and.callThrough();
spyOn(Insight.prototype, '_save');
spyOnProperty(Insight.prototype, 'optOut', 'get')
.and.callFake(() => isOptedOut);
spyOnProperty(Insight.prototype, 'optOut', 'set')
.and.callFake(x => { isOptedOut = x; });
// Set a normal opted-in user as default
spyOn(telemetry, 'isCI').and.returnValue(false);
isOptedOut = false;
});
it("Test#023 : skips prompt when user runs 'cordova telemetry X'", () => {
isOptedOut = undefined;
return Promise.resolve()
.then(_ => cli(['node', 'cordova', 'telemetry', 'on']))
.then(_ => cli(['node', 'cordova', 'telemetry', 'off']))
.then(() => {
expect(telemetry.showPrompt).not.toHaveBeenCalled();
});
});
it("Test#024 : is NOT collected when user runs 'cordova telemetry on' while NOT opted-in", () => {
isOptedOut = true;
return cli(['node', 'cordova', 'telemetry', 'on']).then(() => {
expect(Insight.prototype.track).not.toHaveBeenCalled();
});
});
it("Test#025 : is collected when user runs 'cordova telemetry off' while opted-in", () => {
return cli(['node', 'cordova', 'telemetry', 'off']).then(() => {
expect(telemetry.track).toHaveBeenCalledWith('telemetry', 'off', 'via-cordova-telemetry-cmd', 'successful');
expect(Insight.prototype.track).toHaveBeenCalled();
expect(Insight.prototype._save).toHaveBeenCalled();
});
});
it('Test#026 : tracks platforms/plugins subcommands', () => {
spyOn(cordova, 'platform').and.returnValue(Promise.resolve());
return cli(['node', 'cordova', 'platform', 'add', 'ios']).then(() => {
expect(telemetry.track).toHaveBeenCalledWith('platform', 'add', 'successful');
expect(Insight.prototype.track).toHaveBeenCalled();
expect(Insight.prototype._save).toHaveBeenCalled();
});
});
it('Test#027 : shows prompt if user neither opted in or out yet', () => {
isOptedOut = undefined;
spyOn(cordova, 'prepare').and.returnValue(Promise.resolve());
return cli(['node', 'cordova', 'prepare']).then(() => {
expect(telemetry.showPrompt).toHaveBeenCalled();
});
});
it('Test#029 : is NOT collected in CI environments', () => {
telemetry.isCI.and.returnValue(true);
return cli(['node', 'cordova', '--version']).then(() => {
expect(telemetry.track).not.toHaveBeenCalled();
});
});
it("Test#030 : is NOT collected when --no-telemetry flag found and doesn't prompt", () => {
isOptedOut = undefined;
return cli(['node', 'cordova', '--version', '--no-telemetry']).then(() => {
expect(telemetry.showPrompt).not.toHaveBeenCalled();
expect(Insight.prototype.track).not.toHaveBeenCalled();
});
});
it("Test#033 : track opt-out that happened via 'cordova telemetry off' even if user is NOT opted-in ", () => {
isOptedOut = true;
return cli(['node', 'cordova', 'telemetry', 'off']).then(() => {
expect(telemetry.isOptedIn()).toBeFalsy();
expect(telemetry.track).toHaveBeenCalledWith('telemetry', 'off', 'via-cordova-telemetry-cmd', 'successful');
expect(Insight.prototype.track).toHaveBeenCalled();
expect(Insight.prototype._save).toHaveBeenCalled();
});
});
});
describe('platform', () => {
beforeEach(() => {
spyOn(cordova, 'platform').and.returnValue(Promise.resolve());
});
it('Test #034 : (add) autosave is the default setting for platform add', () => {
return cli(['node', 'cordova', 'platform', 'add', 'ios']).then(() => {
expect(cordova.platform).toHaveBeenCalledWith(
'add',
['ios'],
jasmine.any(Object)
);
const opts = cordova.platform.calls.argsFor(0)[2];
expect(opts.save).toBe(true);
});
});
it('Test #035 : (add) platform is not saved when --nosave is passed in', () => {
return cli(['node', 'cordova', 'platform', 'add', 'ios', '--nosave']).then(() => {
expect(cordova.platform).toHaveBeenCalledWith(
'add',
['ios'],
jasmine.any(Object)
);
const opts = cordova.platform.calls.argsFor(0)[2];
expect(opts.save).toBe(false);
});
});
it('Test #036 : (remove) autosave is the default setting for platform remove', () => {
return cli(['node', 'cordova', 'platform', 'remove', 'ios']).then(() => {
expect(cordova.platform).toHaveBeenCalledWith(
'remove',
['ios'],
jasmine.any(Object)
);
const opts = cordova.platform.calls.argsFor(0)[2];
expect(opts.save).toBe(true);
});
});
it('Test #037 : (remove) platform is not removed when --nosave is passed in', () => {
return cli(['node', 'cordova', 'platform', 'remove', 'ios', '--nosave']).then(() => {
expect(cordova.platform).toHaveBeenCalledWith(
'remove',
['ios'],
jasmine.any(Object)
);
const opts = cordova.platform.calls.argsFor(0)[2];
expect(opts.save).toBe(false);
});
});
});
describe('config', () => {
let clirevert, confrevert, editorArgs, confHolder;
const cordovaConfig = {};
const confMock = {
all: cordovaConfig,
set (key, value) {
cordovaConfig[key] = value;
},
del (key) {
delete cordovaConfig[key];
},
path () {
confHolder = 'Pathcalled';
return 'some/path/cordova-config.json';
},
get (key) {
confHolder = cordovaConfig[key];
return cordovaConfig[key];
}
};
beforeEach(() => {
clirevert = cli.__set__('editor', (path1, cb) => {
editorArgs = path1();
cb();
});
confrevert = cli.__set__('conf', confMock);
});
afterEach(() => {
clirevert();
confrevert();
confHolder = undefined;
});
it('Test#042 : config set is called with true', () => {
return cli(['node', 'cordova', 'config', 'set', 'foo', 'true', '--silent']).then(() => {
expect(cordovaConfig.foo).toBe('true');
});
});
it('Test#043 : config delete is called', () => {
return cli(['node', 'cordova', 'config', 'delete', 'foo']).then(() => {
expect(cordovaConfig.foo).toBeUndefined();
});
});
it('Test#044 : config set is called even without value, defaults to true', () => {
return cli(['node', 'cordova', 'config', 'set', 'foo']).then(() => {
expect(cordovaConfig.foo).toBe(true);
});
});
it('Test #045 : config get is called', () => {
return cli(['node', 'cordova', 'config', 'get', 'foo']).then(() => {
expect(confHolder).toBe(true);
});
});
it('Test #046 : config edit is called', () => {
return cli(['node', 'cordova', 'config', 'edit']).then(() => {
expect(path.basename(editorArgs)).toEqual('cordova-config.json');
expect(confHolder).toEqual('Pathcalled');
});
});
it('Test #047 : config ls is called', () => {
const expectedOutput = JSON.stringify(cordovaConfig, null, 4);
return cli(['node', 'cordova', 'config', 'ls']).then(() => {
expect(logger.results).toHaveBeenCalledWith(expectedOutput);
});
});
});
describe('requirements', () => {
beforeEach(() => {
spyOn(cordova, 'requirements').and.returnValue(Promise.resolve({ browser: [] }));
});
it('should succeed on browser as the platform argument to the requirement check method.', () => {
return cli(['node', 'cordova', 'requirements', 'browser']).then(() => {
expect(cordova.requirements).toHaveBeenCalledWith(['browser']);
});
});
});
describe('node requirement', () => {
it('should warn users about unsupported node version', () => {
cli.__set__('NODE_VERSION', 'v6.1.0');
cli.__set__('NODE_VERSION_DEPRECATING_RANGE', null);
cli.__set__('NODE_VERSION_REQUIREMENT', '>=8');
return cli(['node', 'cordova']).then(() => {
const errorMsg = logger.warn.calls.argsFor(1).toString();
expect(errorMsg).toMatch(/v6.1.0 is no longer supported./);
});
});
it('should not warn users about unsupported node version', () => {
cli.__set__('NODE_VERSION', 'v8.0.0');
cli.__set__('NODE_VERSION_DEPRECATING_RANGE', null);
cli.__set__('NODE_VERSION_REQUIREMENT', '>=8');
return cli(['node', 'cordova']).then(() => {
const errorMsg = logger.warn.calls.argsFor(1).toString();
expect(errorMsg).toBeFalsy();
});
});
it('should warn users about deprecated node version', () => {
cli.__set__('NODE_VERSION', 'v8.0.0');
cli.__set__('NODE_VERSION_DEPRECATING_RANGE', '<10');
cli.__set__('NODE_VERSION_REQUIREMENT', '>=8');
return cli(['node', 'cordova']).then(() => {
const errorMsg = logger.warn.calls.argsFor(1).toString();
expect(errorMsg).toMatch(/v8.0.0 has been deprecated./);
});
});
it('should warn users about deprecated node version', () => {
cli.__set__('NODE_VERSION', 'v10.0.0');
cli.__set__('NODE_VERSION_DEPRECATING_RANGE', '<10');
cli.__set__('NODE_VERSION_REQUIREMENT', '>=8');
return cli(['node', 'cordova']).then(() => {
const errorMsg = logger.warn.calls.argsFor(1).toString();
expect(errorMsg).toBeFalsy();
});
});
});
});