Unit tests for url opener

This commit is contained in:
Jeffrey Morgan
2015-04-27 11:30:23 -04:00
parent b9cb2e5fe5
commit 3869b5ca18
8 changed files with 166 additions and 83 deletions

View File

@@ -66,6 +66,7 @@
"mixpanel": "0.2.0",
"node-uuid": "^1.4.3",
"object-assign": "^2.0.0",
"parseUri": "^1.2.3-2",
"react": "^0.13.1",
"react-bootstrap": "^0.20.3",
"react-retina-image": "^1.1.2",

View File

@@ -11,7 +11,9 @@ var metrics = require('./utils/MetricsUtil');
var router = require('./router');
var template = require('./menutemplate');
var webUtil = require('./utils/WebUtil');
var util = require ('./utils/Util');
var urlUtil = require ('./utils/URLUtil');
var app = remote.require('app');
var request = require('request');
webUtil.addWindowSizeSaving();
webUtil.addLiveReload();
@@ -28,12 +30,6 @@ setInterval(function () {
router.run(Handler => React.render(<Handler/>, document.body));
ipc.on('application:quitting', opts => {
if (!opts.updating && localStorage.getItem('settings.closeVMOnQuit') === 'true') {
machine.stop();
}
});
SetupStore.setup().then(() => {
if (ContainerStore.pending()) {
router.transitionTo('pull');
@@ -60,26 +56,15 @@ ipc.on('application:quitting', () => {
}
});
// Event fires when the app receives a docker:// URL such as
// docker://repository/run/redis
ipc.on('application:open-url', opts => {
var parser = document.createElement('a');
parser.href = opts.url;
if (parser.protocol !== 'docker:') {
return;
}
var pathname = parser.pathname.replace('//', '');
var tokens = pathname.split('/');
var type = tokens[0];
var method = tokens[1];
var repo = tokens.slice(2).join('/');
// Only accept official repos for now
if (!util.isOfficialRepo(repo)) {
return;
}
if (type === 'repository' && method === 'run') {
ContainerStore.setPending(repo, 'latest');
}
request.get('https://kitematic.com/flags.json', (flags, err) => {
if (err || !flags) {
return;
}
urlUtil.openUrl(opts.url, flags, app.getVersion());
});
});
urlUtil.openUrl('docker://repository/run/redis', {dockerURLEnabledVersion: '0.5.19'}, app.getVersion());

View File

@@ -21,7 +21,7 @@ describe('SetupStore', function () {
pit('downloads virtualbox if it is installed but has an outdated version', function () {
virtualBox.installed.mockReturnValue(true);
virtualBox.version.mockReturnValue(Promise.resolve('4.3.16'));
setupUtil.compareVersions.mockReturnValue(-1);
util.compareVersions.mockReturnValue(-1);
setupUtil.download.mockReturnValue(Promise.resolve());
util.packagejson.mockReturnValue({'virtualbox-filename': ''});
util.supportDir.mockReturnValue('');
@@ -50,7 +50,7 @@ describe('SetupStore', function () {
pit('only installs binaries if virtualbox is installed', function () {
virtualBox.installed.mockReturnValue(true);
setupUtil.compareVersions.mockReturnValue(0);
util.compareVersions.mockReturnValue(0);
setupUtil.needsBinaryFix.mockReturnValue(true);
return setupStore.steps().install.run().then(() => {
expect(util.exec).toBeCalledWith('macsudo copycmd && fixcmd');
@@ -68,7 +68,7 @@ describe('SetupStore', function () {
machine.stop.mockReturnValue(Promise.resolve());
machine.start.mockReturnValue(Promise.resolve());
machine.upgrade.mockReturnValue(Promise.resolve());
setupUtil.compareVersions.mockReturnValue(-1);
util.compareVersions.mockReturnValue(-1);
machine.create.mockClear();
machine.upgrade.mockClear();
machine.start.mockClear();

View File

@@ -76,7 +76,7 @@ var _steps = [{
var isoversion = machine.isoversion();
var packagejson = util.packagejson();
if (!isoversion || setupUtil.compareVersions(isoversion, packagejson['docker-version']) < 0) {
if (!isoversion || util.compareVersions(isoversion, packagejson['docker-version']) < 0) {
yield machine.start();
yield machine.upgrade();
}
@@ -152,10 +152,10 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), {
var vboxNeedsInstall = !virtualBox.installed();
required.download = vboxNeedsInstall && (!fs.existsSync(vboxfile) || setupUtil.checksum(vboxfile) !== packagejson['virtualbox-checksum']);
required.install = vboxNeedsInstall || setupUtil.needsBinaryFix();
required.init = required.install || !(yield machine.exists()) || (yield machine.state()) !== 'Running' || !isoversion || setupUtil.compareVersions(isoversion, packagejson['docker-version']) < 0;
required.init = required.install || !(yield machine.exists()) || (yield machine.state()) !== 'Running' || !isoversion || util.compareVersions(isoversion, packagejson['docker-version']) < 0;
var exists = yield machine.exists();
if (isoversion && setupUtil.compareVersions(isoversion, packagejson['docker-version']) < 0) {
if (isoversion && util.compareVersions(isoversion, packagejson['docker-version']) < 0) {
this.steps().init.seconds = 33;
} else if (exists && (yield machine.state()) === 'Saved') {
this.steps().init.seconds = 8;

View File

@@ -108,55 +108,6 @@ var SetupUtil = {
resolve();
});
});
},
compareVersions: function (v1, v2, options) {
var lexicographical = options && options.lexicographical,
zeroExtend = options && options.zeroExtend,
v1parts = v1.split('.'),
v2parts = v2.split('.');
function isValidPart(x) {
return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x);
}
if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {
return NaN;
}
if (zeroExtend) {
while (v1parts.length < v2parts.length) {
v1parts.push('0');
}
while (v2parts.length < v1parts.length) {
v2parts.push('0');
}
}
if (!lexicographical) {
v1parts = v1parts.map(Number);
v2parts = v2parts.map(Number);
}
for (var i = 0; i < v1parts.length; ++i) {
if (v2parts.length === i) {
return 1;
}
if (v1parts[i] === v2parts[i]) {
continue;
}
else if (v1parts[i] > v2parts[i]) {
return 1;
}
else {
return -1;
}
}
if (v1parts.length !== v2parts.length) {
return -1;
}
return 0;
}
};

53
src/utils/URLUtil-test.js Normal file
View File

@@ -0,0 +1,53 @@
jest.dontMock('./URLUtil');
jest.dontMock('parseUri');
var urlUtil = require('./URLUtil');
var util = require('./Util');
describe('URLUtil', function () {
beforeEach(() => {
util.compareVersions.mockClear();
util.isOfficialRepo.mockClear();
});
it('does nothing if the url is undefined', () => {
util.compareVersions.mockReturnValue(1);
util.isOfficialRepo.mockReturnValue(true);
expect(urlUtil.openUrl()).toBe(false);
});
it('does nothing if the flags are undefined', () => {
util.compareVersions.mockReturnValue(1);
util.isOfficialRepo.mockReturnValue(true);
expect(urlUtil.openUrl('docker://repository/run/redis')).toBe(false);
});
it('does nothing if the url enabled flag is falsy', () => {
util.compareVersions.mockReturnValue(1);
util.isOfficialRepo.mockReturnValue(true);
expect(urlUtil.openUrl('docker://repository/run/redis', {dockerURLEnabledVersion: undefined})).toBe(false);
});
it('does nothing if the url enabled flag is less than the flag version', () => {
util.compareVersions.mockReturnValue(-1);
util.isOfficialRepo.mockReturnValue(true);
expect(urlUtil.openUrl('docker://repository/run/redis', {dockerURLEnabledVersion: '0.5.19'}, '0.5.18')).toBe(false);
});
it('does nothing if protocol is not docker:', () => {
util.compareVersions.mockReturnValue(1);
util.isOfficialRepo.mockReturnValue(true);
expect(urlUtil.openUrl('facetime://')).toBe(false);
});
it('does nothing if repo is not official', () => {
util.compareVersions.mockReturnValue(1);
util.isOfficialRepo.mockReturnValue(false);
expect(urlUtil.openUrl('docker://repository/run/not/official', {dockerURLEnabledVersion: '0.5.19'}, '0.5.20')).toBe(false);
});
it('returns true if type and method are correct', () => {
util.compareVersions.mockReturnValue(1);
util.isOfficialRepo.mockReturnValue(true);
expect(urlUtil.openUrl('docker://repository/run/redis', {dockerURLEnabledVersion: '0.5.19'}, '0.5.20')).toBe(true);
});
});

44
src/utils/URLUtil.js Normal file
View File

@@ -0,0 +1,44 @@
var util = require('./Util');
var parseUri = require('parseUri');
var containerStore = require('../Stores/ContainerStore');
module.exports = {
openUrl: function (url, flags, appVersion) {
if (!url || !flags || !flags.dockerURLEnabledVersion) {
return false;
}
// Make sure this feature is enabled via the feature flag
if (util.compareVersions(appVersion, flags.dockerURLEnabledVersion) < 0) {
return false;
}
var parser = parseUri(url);
if (parser.protocol !== 'docker') {
return false;
}
// Get the type of object we're operating on, e.g. 'repository'
var type = parser.host;
// Separate the path into [run', 'redis']
var tokens = parser.path.replace('/', '').split('/');
// Get the method trying to be executed, e.g. 'run'
var method = tokens[0];
// Get the repository namespace and repo name, e.g. 'redis' or 'myusername/myrepo'
var repo = tokens.slice(1).join('/');
// Only accept official repos for now
if (!util.isOfficialRepo(repo)) {
return false;
}
if (type === 'repository' && method === 'run') {
containerStore.setPending(repo, 'latest');
return true;
}
}
};

View File

@@ -59,5 +59,54 @@ module.exports = {
var repoRegexp = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
return repoRegexp.test(name);
},
compareVersions: function (v1, v2, options) {
var lexicographical = options && options.lexicographical,
zeroExtend = options && options.zeroExtend,
v1parts = v1.split('.'),
v2parts = v2.split('.');
function isValidPart(x) {
return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x);
}
if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {
return NaN;
}
if (zeroExtend) {
while (v1parts.length < v2parts.length) {
v1parts.push('0');
}
while (v2parts.length < v1parts.length) {
v2parts.push('0');
}
}
if (!lexicographical) {
v1parts = v1parts.map(Number);
v2parts = v2parts.map(Number);
}
for (var i = 0; i < v1parts.length; ++i) {
if (v2parts.length === i) {
return 1;
}
if (v1parts[i] === v2parts[i]) {
continue;
}
else if (v1parts[i] > v2parts[i]) {
return 1;
}
else {
return -1;
}
}
if (v1parts.length !== v2parts.length) {
return -1;
}
return 0;
},
webPorts: ['80', '8000', '8080', '3000', '5000', '2368', '9200', '8983']
};