From 832d6843dfabc82dd4d9f20ed6ceae154db94f5f Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Fri, 1 Apr 2016 10:08:14 -0400 Subject: [PATCH] Extract browser window progress into WindowProgressService --- lib/browser/app.js | 26 +++--- .../services/window-progress.js | 73 +++++++++++++++ .../utils/window-progress/window-progress.js | 29 ++++++ tests/browser/utils/window-progress.spec.js | 90 +++++++++++++++++++ 4 files changed, 203 insertions(+), 15 deletions(-) create mode 100644 lib/browser/utils/window-progress/services/window-progress.js create mode 100644 lib/browser/utils/window-progress/window-progress.js create mode 100644 tests/browser/utils/window-progress.spec.js diff --git a/lib/browser/app.js b/lib/browser/app.js index 4cf7feee..91a26601 100644 --- a/lib/browser/app.js +++ b/lib/browser/app.js @@ -24,8 +24,6 @@ var angular = require('angular'); const _ = require('lodash'); const electron = require('electron'); const dialog = electron.remote.require('./src/dialog'); -const BrowserWindow = electron.remote.BrowserWindow; -const currentWindow = BrowserWindow.fromId(1); require('angular-ui-bootstrap'); require('angular-ui-router'); @@ -40,6 +38,7 @@ require('./browser/components/progress-button/progress-button'); require('./browser/components/drive-selector'); require('./browser/pages/finish/finish'); require('./browser/pages/settings/settings'); +require('./browser/utils/window-progress/window-progress'); const app = angular.module('Etcher', [ 'ui.router', @@ -62,7 +61,10 @@ const app = angular.module('Etcher', [ // Pages 'Etcher.Pages.Finish', - 'Etcher.Pages.Settings' + 'Etcher.Pages.Settings', + + // Utils + 'Etcher.Utils.WindowProgress' ]); app.config(function($stateProvider, $urlRouterProvider) { @@ -85,7 +87,8 @@ app.controller('AppController', function( SelectionStateService, ImageWriterService, AnalyticsService, - DriveSelectorService + DriveSelectorService, + WindowProgressService ) { let self = this; this.selection = SelectionStateService; @@ -110,9 +113,7 @@ app.controller('AppController', function( NotifierService.subscribe($scope, 'image-writer:state', function(state) { AnalyticsService.log(`Progress: ${state.progress}% at ${state.speed} MB/s`); - - // Show progress inline in operating system task bar - currentWindow.setProgressBar(state.progress / 100); + WindowProgressService.set(state.progress); }); this.scanner.start(2000).on('scan', function(drives) { @@ -213,13 +214,8 @@ app.controller('AppController', function( return self.writer.burn(image, drive).then(function() { AnalyticsService.logEvent('Done'); $state.go('success'); - }).catch(dialog.showError).finally(function() { - - // Remove progress bar from task bar - // Passing 0 or null/undefined doesn't do - // the trick for Electron for some reason. - currentWindow.setProgressBar(-1); - - }); + }) + .catch(dialog.showError) + .finally(WindowProgressService.clear); }; }); diff --git a/lib/browser/utils/window-progress/services/window-progress.js b/lib/browser/utils/window-progress/services/window-progress.js new file mode 100644 index 00000000..be821c6a --- /dev/null +++ b/lib/browser/utils/window-progress/services/window-progress.js @@ -0,0 +1,73 @@ +/* + * 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'); + +module.exports = function() { + const self = this; + + /** + * @summary A reference to the current renderer Electron window + * @property + * @protected + * + * @description + * Since electron only has one renderer view, we can assume the + * current window is the one with id == 1. + * + * We expose this property to `this` for testability purposes. + */ + this.currentWindow = electron.remote.BrowserWindow.fromId(1); + + /** + * @summary Set operating system window progress + * @function + * @public + * + * @description + * Show progress inline in operating system task bar + * + * @param {Number} percentage - percentage + * + * @example + * WindowProgressService.set(85); + */ + this.set = function(percentage) { + if (percentage > 100 || percentage < 0) { + throw new Error(`Invalid window progress percentage: ${percentage}`); + } + + self.currentWindow.setProgressBar(percentage / 100); + }; + + /** + * @summary Clear the operating system window progress bar + * @function + * @public + * + * @example + * WindowProgressService.clear(); + */ + this.clear = function() { + + // Passing 0 or null/undefined doesn't work. + self.currentWindow.setProgressBar(-1); + + }; + +}; diff --git a/lib/browser/utils/window-progress/window-progress.js b/lib/browser/utils/window-progress/window-progress.js new file mode 100644 index 00000000..dfc6072c --- /dev/null +++ b/lib/browser/utils/window-progress/window-progress.js @@ -0,0 +1,29 @@ +/* + * 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'; + +/** + * @module Etcher.Utils.WindowProgress + * + * 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. + */ + +const angular = require('angular'); +const WindowProgress = angular.module('Etcher.Utils.WindowProgress', []); +WindowProgress.service('WindowProgressService', require('./services/window-progress')); diff --git a/tests/browser/utils/window-progress.spec.js b/tests/browser/utils/window-progress.spec.js new file mode 100644 index 00000000..7bde1c7e --- /dev/null +++ b/tests/browser/utils/window-progress.spec.js @@ -0,0 +1,90 @@ +/* + * 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 m = require('mochainon'); +const angular = require('angular'); +require('angular-mocks'); +require('../../../lib/browser/utils/window-progress/window-progress'); + +describe('Browser: WindowProgress', function() { + + beforeEach(angular.mock.module('Etcher.Utils.WindowProgress')); + + describe('WindowProgressService', function() { + + let WindowProgressService; + + beforeEach(angular.mock.inject(function(_WindowProgressService_) { + WindowProgressService = _WindowProgressService_; + })); + + describe('given a stubbed current window', function() { + + beforeEach(function() { + this.setProgressBarSpy = m.sinon.spy(); + + WindowProgressService.currentWindow = { + setProgressBar: this.setProgressBarSpy + }; + }); + + describe('.set()', function() { + + it('should translate 0-100 percentages to 0-1 ranges', function() { + WindowProgressService.set(85); + m.chai.expect(this.setProgressBarSpy).to.have.been.calledWith(0.85); + }); + + it('should set 0 given 0', function() { + WindowProgressService.set(0); + m.chai.expect(this.setProgressBarSpy).to.have.been.calledWith(0); + }); + + it('should set 1 given 100', function() { + WindowProgressService.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() { + WindowProgressService.set(101); + }).to.throw('Invalid window progress percentage: 101'); + }); + + it('should throw if given a percentage less than 0', function() { + m.chai.expect(function() { + WindowProgressService.set(-1); + }).to.throw('Invalid window progress percentage: -1'); + }); + + }); + + describe('.clear()', function() { + + it('should set -1', function() { + WindowProgressService.clear(); + m.chai.expect(this.setProgressBarSpy).to.have.been.calledWith(-1); + }); + + }); + + }); + + }); + +});