diff --git a/lib/gui/app.js b/lib/gui/app.js index 7ae49c6d..e1861c77 100644 --- a/lib/gui/app.js +++ b/lib/gui/app.js @@ -32,6 +32,7 @@ const EXIT_CODES = require('../shared/exit-codes'); const messages = require('../shared/messages'); const packageJSON = require('../../package.json'); const flashState = require('./models/flash-state'); +const windowProgress = require('./os/window-progress'); const Store = require('./models/store'); @@ -60,7 +61,6 @@ const app = angular.module('Etcher', [ require('./pages/settings/settings'), // OS - require('./os/window-progress/window-progress'), require('./os/open-external/open-external'), require('./os/dropzone/dropzone'), require('./os/dialog/dialog'), @@ -129,7 +129,7 @@ app.run((AnalyticsService, ErrorService, UpdateNotifierService, SelectionStateMo }); -app.run((AnalyticsService, OSWindowProgressService) => { +app.run((AnalyticsService) => { Store.subscribe(() => { const currentFlashState = flashState.getFlashState(); @@ -150,7 +150,7 @@ app.run((AnalyticsService, OSWindowProgressService) => { `(eta ${currentFlashState.eta}s)` ].join(' ')); - OSWindowProgressService.set(currentFlashState.percentage); + windowProgress.set(currentFlashState.percentage); }); }); diff --git a/lib/gui/os/window-progress.js b/lib/gui/os/window-progress.js new file mode 100644 index 00000000..318548b2 --- /dev/null +++ b/lib/gui/os/window-progress.js @@ -0,0 +1,63 @@ +/* + * Copyright 2016 resin.io + * + * Licensed 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. + */ + +'use strict'; + +const electron = require('electron'); +const utils = require('../../shared/utils'); + +/** + * @summary A reference to the current renderer Electron window + * @type {Object} + * @protected + * + * @description + * We expose this property to `this` for testability purposes. + */ +exports.currentWindow = electron.remote.getCurrentWindow(); + +/** + * @summary Set operating system window progress + * @function + * @public + * + * @description + * Show progress inline in operating system task bar + * + * @param {Number} percentage - percentage + * + * @example + * windowProgress.set(85); + */ +exports.set = (percentage) => { + exports.currentWindow.setProgressBar(utils.percentageToFloat(percentage)); +}; + +/** + * @summary Clear the window progress bar + * @function + * @public + * + * @example + * windowProgress.clear(); + */ +exports.clear = () => { + + // Passing 0 or null/undefined doesn't work. + const ELECTRON_PROGRESS_BAR_RESET_VALUE = -1; + + exports.currentWindow.setProgressBar(ELECTRON_PROGRESS_BAR_RESET_VALUE); +}; diff --git a/lib/gui/os/window-progress/services/window-progress.js b/lib/gui/os/window-progress/services/window-progress.js deleted file mode 100644 index 646509fa..00000000 --- a/lib/gui/os/window-progress/services/window-progress.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed 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. - */ - -'use strict'; - -const electron = require('electron'); -const errors = require('../../../../shared/errors'); - -module.exports = function() { - - /** - * @summary A reference to the current renderer Electron window - * @type {Object} - * @protected - * - * @description - * We expose this property to `this` for testability purposes. - */ - this.currentWindow = electron.remote.getCurrentWindow(); - - /** - * @summary Set operating system window progress - * @function - * @public - * - * @description - * Show progress inline in operating system task bar - * - * @param {Number} percentage - percentage - * - * @example - * OSWindowProgressService.set(85); - */ - this.set = (percentage) => { - const PERCENTAGE_MINIMUM = 0; - const PERCENTAGE_MAXIMUM = 100; - - if (percentage > PERCENTAGE_MAXIMUM || percentage < PERCENTAGE_MINIMUM) { - throw errors.createError(`Invalid window progress percentage: ${percentage}`); - } - - this.currentWindow.setProgressBar(percentage / PERCENTAGE_MAXIMUM); - }; - - /** - * @summary Clear the operating system window progress bar - * @function - * @public - * - * @example - * OSWindowProgressService.clear(); - */ - this.clear = () => { - - // Passing 0 or null/undefined doesn't work. - const ELECTRON_PROGRESS_BAR_RESET_VALUE = -1; - - this.currentWindow.setProgressBar(ELECTRON_PROGRESS_BAR_RESET_VALUE); - }; - -}; diff --git a/lib/gui/os/window-progress/window-progress.js b/lib/gui/os/window-progress/window-progress.js deleted file mode 100644 index b740ce6c..00000000 --- a/lib/gui/os/window-progress/window-progress.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed 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. - */ - -'use strict'; - -/** - * The purpose of this module is to provide an easy way - * to interact with the operating system's window progress - * functionality, as described in Electron docs. - * - * @module Etcher.OS.WindowProgress - */ - -const angular = require('angular'); -const MODULE_NAME = 'Etcher.OS.WindowProgress'; -const OSWindowProgress = angular.module(MODULE_NAME, []); -OSWindowProgress.service('OSWindowProgressService', require('./services/window-progress')); - -module.exports = MODULE_NAME; diff --git a/lib/gui/pages/main/controllers/flash.js b/lib/gui/pages/main/controllers/flash.js index 866deaf9..57131a03 100644 --- a/lib/gui/pages/main/controllers/flash.js +++ b/lib/gui/pages/main/controllers/flash.js @@ -19,6 +19,7 @@ const messages = require('../../../../shared/messages'); const settings = require('../../../models/settings'); const flashState = require('../../../models/flash-state'); +const windowProgress = require('../../../os/window-progress'); module.exports = function( $state, @@ -27,8 +28,7 @@ module.exports = function( AnalyticsService, FlashErrorModalService, ErrorService, - OSNotificationService, - OSWindowProgressService + OSNotificationService ) { /** @@ -105,7 +105,7 @@ module.exports = function( }) .finally(() => { - OSWindowProgressService.clear(); + windowProgress.clear(); DriveScannerService.start(); }); }; diff --git a/lib/gui/pages/main/main.js b/lib/gui/pages/main/main.js index 27349951..8d78708f 100644 --- a/lib/gui/pages/main/main.js +++ b/lib/gui/pages/main/main.js @@ -39,7 +39,6 @@ const MainPage = angular.module(MODULE_NAME, [ require('../../components/flash-error-modal/flash-error-modal'), require('../../components/progress-button/progress-button'), - require('../../os/window-progress/window-progress'), require('../../os/notification/notification'), require('../../os/dialog/dialog'), require('../../os/open-external/open-external'), diff --git a/lib/shared/utils.js b/lib/shared/utils.js new file mode 100644 index 00000000..c9e98e3a --- /dev/null +++ b/lib/shared/utils.js @@ -0,0 +1,78 @@ +/* + * Copyright 2017 resin.io + * + * Licensed 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. + */ + +'use strict'; + +const _ = require('lodash'); +const errors = require('./errors'); + +/** + * @summary Minimum percentage value + * @constant + * @private + * @type {Number} + */ +const PERCENTAGE_MINIMUM = 0; + +/** + * @summary Maximum percentage value + * @constant + * @private + * @type {Number} + */ +const PERCENTAGE_MAXIMUM = 100; + +/** + * @summary Check if a percentage is valid + * @function + * @public + * + * @param {Number} percentage - percentage + * @returns {Boolean} whether the percentage is valid + * + * @example + * if (utils.isValidPercentage(85)) { + * console.log('The percentage is valid'); + * } + */ +exports.isValidPercentage = (percentage) => { + return _.every([ + _.isNumber(percentage), + percentage >= PERCENTAGE_MINIMUM, + percentage <= PERCENTAGE_MAXIMUM + ]); +}; + +/** + * @summary Convert a percentage to a float + * @function + * @public + * + * @param {Number} percentage - percentage + * @returns {Number} float percentage + * + * @example + * const value = utils.percentageToFloat(50); + * console.log(value); + * > 0.5 + */ +exports.percentageToFloat = (percentage) => { + if (!exports.isValidPercentage(percentage)) { + throw errors.createError(`Invalid percentage: ${percentage}`); + } + + return percentage / PERCENTAGE_MAXIMUM; +}; diff --git a/tests/gui/os/window-progress.spec.js b/tests/gui/os/window-progress.spec.js index d874d4b1..62c9b029 100644 --- a/tests/gui/os/window-progress.spec.js +++ b/tests/gui/os/window-progress.spec.js @@ -17,29 +17,18 @@ 'use strict'; const m = require('mochainon'); -const angular = require('angular'); -require('angular-mocks'); +const windowProgress = require('../../../lib/gui/os/window-progress'); -describe('Browser: OSWindowProgress', function() { +describe('Browser: WindowProgress', function() { - beforeEach(angular.mock.module( - require('../../../lib/gui/os/window-progress/window-progress') - )); - - describe('OSWindowProgressService', function() { - - let OSWindowProgressService; - - beforeEach(angular.mock.inject(function(_OSWindowProgressService_) { - OSWindowProgressService = _OSWindowProgressService_; - })); + describe('windowProgress', function() { describe('given a stubbed current window', function() { beforeEach(function() { this.setProgressBarSpy = m.sinon.spy(); - OSWindowProgressService.currentWindow = { + windowProgress.currentWindow = { setProgressBar: this.setProgressBarSpy }; }); @@ -47,30 +36,30 @@ describe('Browser: OSWindowProgress', function() { describe('.set()', function() { it('should translate 0-100 percentages to 0-1 ranges', function() { - OSWindowProgressService.set(85); + windowProgress.set(85); m.chai.expect(this.setProgressBarSpy).to.have.been.calledWith(0.85); }); it('should set 0 given 0', function() { - OSWindowProgressService.set(0); + windowProgress.set(0); m.chai.expect(this.setProgressBarSpy).to.have.been.calledWith(0); }); it('should set 1 given 100', function() { - OSWindowProgressService.set(100); + windowProgress.set(100); m.chai.expect(this.setProgressBarSpy).to.have.been.calledWith(1); }); it('should throw if given a percentage higher than 100', function() { m.chai.expect(function() { - OSWindowProgressService.set(101); - }).to.throw('Invalid window progress percentage: 101'); + windowProgress.set(101); + }).to.throw('Invalid percentage: 101'); }); it('should throw if given a percentage less than 0', function() { m.chai.expect(function() { - OSWindowProgressService.set(-1); - }).to.throw('Invalid window progress percentage: -1'); + windowProgress.set(-1); + }).to.throw('Invalid percentage: -1'); }); }); @@ -78,7 +67,7 @@ describe('Browser: OSWindowProgress', function() { describe('.clear()', function() { it('should set -1', function() { - OSWindowProgressService.clear(); + windowProgress.clear(); m.chai.expect(this.setProgressBarSpy).to.have.been.calledWith(-1); }); diff --git a/tests/shared/utils.spec.js b/tests/shared/utils.spec.js new file mode 100644 index 00000000..fd604ec3 --- /dev/null +++ b/tests/shared/utils.spec.js @@ -0,0 +1,134 @@ +/* + * Copyright 2017 resin.io + * + * Licensed 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. + */ + +'use strict'; + +const m = require('mochainon'); +const utils = require('../../lib/shared/utils'); + +describe('Shared: Utils', function() { + + describe('.isValidPercentage()', function() { + + it('should return false if percentage is not a number', function() { + m.chai.expect(utils.isValidPercentage('50')).to.be.false; + }); + + it('should return false if percentage is null', function() { + m.chai.expect(utils.isValidPercentage(null)).to.be.false; + }); + + it('should return false if percentage is undefined', function() { + m.chai.expect(utils.isValidPercentage(undefined)).to.be.false; + }); + + it('should return false if percentage is an integer less than 0', function() { + m.chai.expect(utils.isValidPercentage(-1)).to.be.false; + }); + + it('should return false if percentage is a float less than 0', function() { + m.chai.expect(utils.isValidPercentage(-0.1)).to.be.false; + }); + + it('should return true if percentage is 0', function() { + m.chai.expect(utils.isValidPercentage(0)).to.be.true; + }); + + it('should return true if percentage is an integer greater than 0, but less than 100', function() { + m.chai.expect(utils.isValidPercentage(50)).to.be.true; + }); + + it('should return true if percentage is a float greater than 0, but less than 100', function() { + m.chai.expect(utils.isValidPercentage(49.55)).to.be.true; + }); + + it('should return true if percentage is 100', function() { + m.chai.expect(utils.isValidPercentage(100)).to.be.true; + }); + + it('should return false if percentage is an integer greater than 100', function() { + m.chai.expect(utils.isValidPercentage(101)).to.be.false; + }); + + it('should return false if percentage is a float greater than 100', function() { + m.chai.expect(utils.isValidPercentage(100.001)).to.be.false; + }); + + }); + + describe('.percentageToFloat()', function() { + + it('should throw an error if given a string percentage', function() { + m.chai.expect(function() { + utils.percentageToFloat('50'); + }).to.throw('Invalid percentage: 50'); + }); + + it('should throw an error if given a null percentage', function() { + m.chai.expect(function() { + utils.percentageToFloat(null); + }).to.throw('Invalid percentage: null'); + }); + + it('should throw an error if given an undefined percentage', function() { + m.chai.expect(function() { + utils.percentageToFloat(undefined); + }).to.throw('Invalid percentage: undefined'); + }); + + it('should throw an error if given an integer percentage < 0', function() { + m.chai.expect(function() { + utils.percentageToFloat(-1); + }).to.throw('Invalid percentage: -1'); + }); + + it('should throw an error if given a float percentage < 0', function() { + m.chai.expect(function() { + utils.percentageToFloat(-0.1); + }).to.throw('Invalid percentage: -0.1'); + }); + + it('should covert a 0 percentage to 0', function() { + m.chai.expect(utils.percentageToFloat(0)).to.equal(0); + }); + + it('should covert an integer percentage to a float', function() { + m.chai.expect(utils.percentageToFloat(50)).to.equal(0.5); + }); + + it('should covert an float percentage to a float', function() { + m.chai.expect(utils.percentageToFloat(46.54)).to.equal(0.4654); + }); + + it('should covert a 100 percentage to 1', function() { + m.chai.expect(utils.percentageToFloat(100)).to.equal(1); + }); + + it('should throw an error if given an integer percentage > 100', function() { + m.chai.expect(function() { + utils.percentageToFloat(101); + }).to.throw('Invalid percentage: 101'); + }); + + it('should throw an error if given a float percentage > 100', function() { + m.chai.expect(function() { + utils.percentageToFloat(100.01); + }).to.throw('Invalid percentage: 100.01'); + }); + + }); + +});