From 3869b5ca18567f73e6a3b36c8817c82385d4a769 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Mon, 27 Apr 2015 11:30:23 -0400 Subject: [PATCH] Unit tests for url opener --- package.json | 1 + src/app.js | 41 +++++++++------------------ src/stores/SetupStore-test.js | 6 ++-- src/stores/SetupStore.js | 6 ++-- src/utils/SetupUtil.js | 49 -------------------------------- src/utils/URLUtil-test.js | 53 +++++++++++++++++++++++++++++++++++ src/utils/URLUtil.js | 44 +++++++++++++++++++++++++++++ src/utils/Util.js | 49 ++++++++++++++++++++++++++++++++ 8 files changed, 166 insertions(+), 83 deletions(-) create mode 100644 src/utils/URLUtil-test.js create mode 100644 src/utils/URLUtil.js diff --git a/package.json b/package.json index dbf5d3f6c9..35ae89fd23 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/app.js b/src/app.js index ad4e661484..2c782c1ebd 100644 --- a/src/app.js +++ b/src/app.js @@ -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(, 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()); diff --git a/src/stores/SetupStore-test.js b/src/stores/SetupStore-test.js index 38bd33dec4..17bf010474 100644 --- a/src/stores/SetupStore-test.js +++ b/src/stores/SetupStore-test.js @@ -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(); diff --git a/src/stores/SetupStore.js b/src/stores/SetupStore.js index 6bdb76033a..10caa0d4a3 100644 --- a/src/stores/SetupStore.js +++ b/src/stores/SetupStore.js @@ -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; diff --git a/src/utils/SetupUtil.js b/src/utils/SetupUtil.js index 95fe6c0b5f..5fe0aa10f4 100644 --- a/src/utils/SetupUtil.js +++ b/src/utils/SetupUtil.js @@ -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; } }; diff --git a/src/utils/URLUtil-test.js b/src/utils/URLUtil-test.js new file mode 100644 index 0000000000..717729e5c4 --- /dev/null +++ b/src/utils/URLUtil-test.js @@ -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); + }); +}); diff --git a/src/utils/URLUtil.js b/src/utils/URLUtil.js new file mode 100644 index 0000000000..4e0bd8367a --- /dev/null +++ b/src/utils/URLUtil.js @@ -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; + } + } +}; diff --git a/src/utils/Util.js b/src/utils/Util.js index 47cc5ad6fa..cc4fff1712 100644 --- a/src/utils/Util.js +++ b/src/utils/Util.js @@ -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'] };