diff --git a/lib/gui/app.js b/lib/gui/app.js index 3fb65219..7742e8bc 100644 --- a/lib/gui/app.js +++ b/lib/gui/app.js @@ -47,6 +47,7 @@ const app = angular.module('Etcher', [ require('./models/settings'), require('./models/supported-formats'), require('./models/drives'), + require('./models/flash-state'), // Components require('./components/progress-button/progress-button'), @@ -95,9 +96,9 @@ app.run((AnalyticsService, UpdateNotifierService, SelectionStateModel) => { }); -app.run((AnalyticsService, OSWindowProgressService, ImageWriterService) => { +app.run((AnalyticsService, OSWindowProgressService, FlashStateModel) => { Store.subscribe(() => { - const flashState = ImageWriterService.getFlashState(); + const flashState = FlashStateModel.getFlashState(); // There is usually a short time period between the `isFlashing()` // property being set, and the flashing actually starting, which @@ -106,7 +107,7 @@ app.run((AnalyticsService, OSWindowProgressService, ImageWriterService) => { // // We use the presence of `.eta` to determine that the actual // writing started. - if (!ImageWriterService.isFlashing() || !flashState.eta) { + if (!FlashStateModel.isFlashing() || !flashState.eta) { return; } @@ -135,6 +136,7 @@ app.controller('AppController', function( $state, DriveScannerService, SelectionStateModel, + FlashStateModel, SettingsModel, SupportedFormatsModel, DrivesModel, @@ -152,6 +154,7 @@ app.controller('AppController', function( this.selection = SelectionStateModel; this.drives = DrivesModel; this.writer = ImageWriterService; + this.state = FlashStateModel; this.settings = SettingsModel; this.tooltipModal = TooltipModalService; @@ -174,7 +177,7 @@ app.controller('AppController', function( // This catches the case where the user enters // the settings screen when a flash finished // and goes back to the main screen with the back button. - if (!this.writer.isFlashing()) { + if (!FlashStateModel.isFlashing()) { this.selection.clear({ @@ -248,7 +251,7 @@ app.controller('AppController', function( }; this.reselectImage = () => { - if (this.writer.isFlashing()) { + if (FlashStateModel.isFlashing()) { return; } @@ -263,7 +266,7 @@ app.controller('AppController', function( }; this.reselectDrive = () => { - if (this.writer.isFlashing()) { + if (FlashStateModel.isFlashing()) { return; } @@ -276,12 +279,12 @@ app.controller('AppController', function( preserveImage: true }); - this.writer.resetState(); + FlashStateModel.resetState(); AnalyticsService.logEvent('Restart after failure'); }; this.wasLastFlashSuccessful = () => { - const flashResults = this.writer.getFlashResults(); + const flashResults = FlashStateModel.getFlashResults(); if (_.get(flashResults, 'cancelled', false)) { return true; @@ -292,7 +295,7 @@ app.controller('AppController', function( this.flash = (image, drive) => { - if (this.writer.isFlashing()) { + if (FlashStateModel.isFlashing()) { return; } @@ -306,7 +309,7 @@ app.controller('AppController', function( }); return this.writer.flash(image, drive).then(() => { - const results = ImageWriterService.getFlashResults(); + const results = FlashStateModel.getFlashResults(); if (results.cancelled) { return; diff --git a/lib/gui/models/flash-state.js b/lib/gui/models/flash-state.js new file mode 100644 index 00000000..a883de22 --- /dev/null +++ b/lib/gui/models/flash-state.js @@ -0,0 +1,175 @@ +/* + * 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.Models.FlashState + */ + +const angular = require('angular'); +const _ = require('lodash'); +const Store = require('./store'); +const MODULE_NAME = 'Etcher.Models.FlashState'; +const FlashState = angular.module(MODULE_NAME, []); + +FlashState.service('FlashStateModel', function() { + + /** + * @summary Reset flash state + * @function + * @public + * + * @example + * FlashStateModel.resetState(); + */ + this.resetState = () => { + Store.dispatch({ + type: Store.Actions.RESET_FLASH_STATE + }); + }; + + /** + * @summary Check if currently flashing + * @function + * @private + * + * @returns {Boolean} whether is flashing or not + * + * @example + * if (FlashStateModel.isFlashing()) { + * console.log('We\'re currently flashing'); + * } + */ + this.isFlashing = () => { + return Store.getState().toJS().isFlashing; + }; + + /** + * @summary Set the flashing flag + * @function + * @private + * + * @description + * This function is extracted for testing purposes. + * + * The flag is used to signify that we're going to + * start a flash process. + * + * @example + * FlashStateModel.setFlashingFlag(); + */ + this.setFlashingFlag = () => { + Store.dispatch({ + type: Store.Actions.SET_FLASHING_FLAG + }); + }; + + /** + * @summary Unset the flashing flag + * @function + * @private + * + * @description + * This function is extracted for testing purposes. + * + * The flag is used to signify that the write process ended. + * + * @param {Object} results - flash results + * + * @example + * FlashStateModel.unsetFlashingFlag({ + * passedValidation: true, + * cancelled: false, + * sourceChecksum: 'a1b45d' + * }); + */ + this.unsetFlashingFlag = (results) => { + Store.dispatch({ + type: Store.Actions.UNSET_FLASHING_FLAG, + data: results + }); + }; + + /** + * @summary Set the flashing state + * @function + * @private + * + * @description + * This function is extracted for testing purposes. + * + * @param {Object} state - flashing state + * + * @example + * FlashStateModel.setProgressState({ + * type: 'write', + * percentage: 50, + * eta: 15, + * speed: 100000000000 + * }); + */ + this.setProgressState = (state) => { + Store.dispatch({ + type: Store.Actions.SET_FLASH_STATE, + data: { + type: state.type, + percentage: state.percentage, + eta: state.eta, + + speed: _.attempt(() => { + if (_.isNumber(state.speed) && !_.isNaN(state.speed)) { + + // Transform bytes to megabytes preserving only two decimal places + return Math.floor(state.speed / 1e+6 * 100) / 100; + + } + }) + } + }); + }; + + /** + * @summary Get the flash results + * @function + * @public + * + * @returns {Object} flash results + * + * @example + * const results = FlashStateModel.getFlashResults(); + */ + this.getFlashResults = () => { + return Store.getState().toJS().flashResults; + }; + + /** + * @summary Get the current flash state + * @function + * @public + * + * @returns {Object} flash state + * + * @example + * const flashState = FlashStateModel.getFlashState(); + */ + this.getFlashState = () => { + return Store.getState().get('flashState').toJS(); + }; + +}); + +module.exports = MODULE_NAME; diff --git a/lib/gui/modules/image-writer.js b/lib/gui/modules/image-writer.js index 0f029a44..4701d86a 100644 --- a/lib/gui/modules/image-writer.js +++ b/lib/gui/modules/image-writer.js @@ -22,157 +22,15 @@ const angular = require('angular'); const _ = require('lodash'); -const Store = require('../models/store'); const childWriter = require('../../src/child-writer'); const MODULE_NAME = 'Etcher.Modules.ImageWriter'; const imageWriter = angular.module(MODULE_NAME, [ - require('../models/settings') + require('../models/settings'), + require('../models/flash-state') ]); -imageWriter.service('ImageWriterService', function($q, $rootScope, SettingsModel) { - - /** - * @summary Reset flash state - * @function - * @public - * - * @example - * ImageWriterService.resetState(); - */ - this.resetState = () => { - Store.dispatch({ - type: Store.Actions.RESET_FLASH_STATE - }); - }; - - /** - * @summary Check if currently flashing - * @function - * @private - * - * @returns {Boolean} whether is flashing or not - * - * @example - * if (ImageWriterService.isFlashing()) { - * console.log('We\'re currently flashing'); - * } - */ - this.isFlashing = () => { - return Store.getState().toJS().isFlashing; - }; - - /** - * @summary Set the flashing flag - * @function - * @private - * - * @description - * This function is extracted for testing purposes. - * - * The flag is used to signify that we're going to - * start a flash process. - * - * @example - * ImageWriterService.setFlashingFlag(); - */ - this.setFlashingFlag = () => { - Store.dispatch({ - type: Store.Actions.SET_FLASHING_FLAG - }); - }; - - /** - * @summary Unset the flashing flag - * @function - * @private - * - * @description - * This function is extracted for testing purposes. - * - * The flag is used to signify that the write process ended. - * - * @param {Object} results - flash results - * - * @example - * ImageWriterService.unsetFlashingFlag({ - * passedValidation: true, - * cancelled: false, - * sourceChecksum: 'a1b45d' - * }); - */ - this.unsetFlashingFlag = (results) => { - Store.dispatch({ - type: Store.Actions.UNSET_FLASHING_FLAG, - data: results - }); - }; - - /** - * @summary Set the flashing state - * @function - * @private - * - * @description - * This function is extracted for testing purposes. - * - * @param {Object} state - flashing state - * - * @example - * ImageWriterService.setProgressState({ - * type: 'write', - * percentage: 50, - * eta: 15, - * speed: 100000000000 - * }); - */ - this.setProgressState = (state) => { - Store.dispatch({ - type: Store.Actions.SET_FLASH_STATE, - data: { - type: state.type, - percentage: state.percentage, - eta: state.eta, - - speed: _.attempt(() => { - if (_.isNumber(state.speed) && !_.isNaN(state.speed)) { - - // Transform bytes to megabytes preserving only two decimal places - return Math.floor(state.speed / 1e+6 * 100) / 100; - - } - }) - } - }); - }; - - /** - * @summary Get the flash results - * @function - * @public - * - * @returns {Object} flash results - * - * @example - * const results = ImageWriterService.getFlashResults(); - */ - this.getFlashResults = () => { - return Store.getState().toJS().flashResults; - }; - - /** - * @summary Get the current flash state - * @function - * @public - * - * @returns {Object} flash state - * - * @example - * const flashState = ImageWriterService.getFlashState(); - */ - this.getFlashState = () => { - return Store.getState().get('flashState').toJS(); - }; +imageWriter.service('ImageWriterService', function($q, $rootScope, SettingsModel, FlashStateModel) { /** * @summary Perform write operation @@ -228,11 +86,11 @@ imageWriter.service('ImageWriterService', function($q, $rootScope, SettingsModel * }); */ this.flash = (image, drive) => { - if (this.isFlashing()) { + if (FlashStateModel.isFlashing()) { return $q.reject(new Error('There is already a flash in progress')); } - this.setFlashingFlag(); + FlashStateModel.setFlashingFlag(); return this.performWrite(image, drive, (state) => { @@ -241,7 +99,7 @@ imageWriter.service('ImageWriterService', function($q, $rootScope, SettingsModel // `.getFlashState()` will not return // the latest updated progress state. $rootScope.$apply(() => { - this.setProgressState(state); + FlashStateModel.setProgressState(state); }); }).then((results) => { @@ -253,9 +111,9 @@ imageWriter.service('ImageWriterService', function($q, $rootScope, SettingsModel passedValidation: false }); - this.unsetFlashingFlag(results); + FlashStateModel.unsetFlashingFlag(results); }).catch((error) => { - this.unsetFlashingFlag({ + FlashStateModel.unsetFlashingFlag({ cancelled: false, passedValidation: false, errorCode: error.code diff --git a/lib/gui/pages/finish/controllers/finish.js b/lib/gui/pages/finish/controllers/finish.js index 24094ee9..c64a564f 100644 --- a/lib/gui/pages/finish/controllers/finish.js +++ b/lib/gui/pages/finish/controllers/finish.js @@ -16,7 +16,7 @@ 'use strict'; -module.exports = function($state, ImageWriterService, SelectionStateModel, AnalyticsService, SettingsModel) { +module.exports = function($state, FlashStateModel, SelectionStateModel, AnalyticsService, SettingsModel) { /** * @summary Settings model @@ -30,7 +30,7 @@ module.exports = function($state, ImageWriterService, SelectionStateModel, Analy * @type String * @public */ - this.checksum = ImageWriterService.getFlashResults().sourceChecksum; + this.checksum = FlashStateModel.getFlashResults().sourceChecksum; /** * @summary Restart the flashing process diff --git a/lib/gui/pages/finish/finish.js b/lib/gui/pages/finish/finish.js index 6c5e0484..0f725dd9 100644 --- a/lib/gui/pages/finish/finish.js +++ b/lib/gui/pages/finish/finish.js @@ -31,7 +31,7 @@ const MODULE_NAME = 'Etcher.Pages.Finish'; const FinishPage = angular.module(MODULE_NAME, [ require('angular-ui-router'), require('../../modules/analytics'), - require('../../modules/image-writer'), + require('../../models/flash-state'), require('../../models/selection-state'), require('../../models/settings') ]); diff --git a/lib/gui/partials/main.html b/lib/gui/partials/main.html index 0e143eb0..6bb68088 100644 --- a/lib/gui/partials/main.html +++ b/lib/gui/partials/main.html @@ -27,7 +27,7 @@ + ng-hide="app.state.isFlashing()">Change @@ -66,7 +66,7 @@ }">{{ app.selection.getDrive().name }} - {{ app.selection.getDrive().size | gigabyte | number:1 }} GB + ng-hide="app.state.isFlashing()">Change @@ -84,30 +84,30 @@
- Finishing... - Flash! - Starting... - - + Finishing... + Flash! + Starting... + +
- + Not enough space on the drive.
Please insert larger one and
- + Your removable drive may be corrupted.
Try inserting a different one and
- + Oops, seems something went wrong. Click to retry
@@ -116,9 +116,9 @@ Retry -
diff --git a/tests/gui/models/flash-state.spec.js b/tests/gui/models/flash-state.spec.js new file mode 100644 index 00000000..500322bd --- /dev/null +++ b/tests/gui/models/flash-state.spec.js @@ -0,0 +1,407 @@ +'use strict'; + +const m = require('mochainon'); +const angular = require('angular'); +require('angular-mocks'); + +describe('Browser: FlashStateModel', function() { + + beforeEach(angular.mock.module( + require('../../../lib/gui/models/flash-state') + )); + + describe('FlashStateModel', function() { + + let FlashStateModel; + + beforeEach(angular.mock.inject(function(_FlashStateModel_) { + FlashStateModel = _FlashStateModel_; + })); + + describe('.resetState()', function() { + + it('should be able to reset the progress state', function() { + FlashStateModel.setFlashingFlag(); + FlashStateModel.setProgressState({ + type: 'write', + percentage: 50, + eta: 15, + speed: 100000000000 + }); + + FlashStateModel.resetState(); + + m.chai.expect(FlashStateModel.getFlashState()).to.deep.equal({ + percentage: 0, + speed: 0 + }); + }); + + it('should be able to reset the progress state', function() { + FlashStateModel.unsetFlashingFlag({ + passedValidation: true, + cancelled: false, + sourceChecksum: '1234' + }); + + FlashStateModel.resetState(); + m.chai.expect(FlashStateModel.getFlashResults()).to.deep.equal({}); + }); + + }); + + describe('.isFlashing()', function() { + + it('should return false by default', function() { + m.chai.expect(FlashStateModel.isFlashing()).to.be.false; + }); + + it('should return true if flashing', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(FlashStateModel.isFlashing()).to.be.true; + }); + + }); + + describe('.setProgressState()', function() { + + it('should not allow setting the state if flashing is false', function() { + FlashStateModel.unsetFlashingFlag({ + passedValidation: true, + cancelled: false, + sourceChecksum: '1234' + }); + + m.chai.expect(function() { + FlashStateModel.setProgressState({ + type: 'write', + percentage: 50, + eta: 15, + speed: 100000000000 + }); + }).to.throw('Can\'t set the flashing state when not flashing'); + }); + + it('should throw if type is missing', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(function() { + FlashStateModel.setProgressState({ + percentage: 50, + eta: 15, + speed: 100000000000 + }); + }).to.throw('Missing state type'); + }); + + it('should throw if type is not a string', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(function() { + FlashStateModel.setProgressState({ + type: 1234, + percentage: 50, + eta: 15, + speed: 100000000000 + }); + }).to.throw('Invalid state type: 1234'); + }); + + it('should not throw if percentage is 0', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(function() { + FlashStateModel.setProgressState({ + type: 'write', + percentage: 0, + eta: 15, + speed: 100000000000 + }); + }).to.not.throw('Missing state percentage'); + }); + + it('should throw if percentage is missing', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(function() { + FlashStateModel.setProgressState({ + type: 'write', + eta: 15, + speed: 100000000000 + }); + }).to.throw('Missing state percentage'); + }); + + it('should throw if percentage is not a number', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(function() { + FlashStateModel.setProgressState({ + type: 'write', + percentage: '50', + eta: 15, + speed: 100000000000 + }); + }).to.throw('Invalid state percentage: 50'); + }); + + it('should throw if eta is missing', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(function() { + FlashStateModel.setProgressState({ + type: 'write', + percentage: 50, + speed: 100000000000 + }); + }).to.throw('Missing state eta'); + }); + + it('should not throw if eta is equal to zero', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(function() { + FlashStateModel.setProgressState({ + type: 'write', + percentage: 50, + eta: 0, + speed: 100000000000 + }); + }).to.not.throw('Missing state eta'); + }); + + it('should throw if eta is not a number', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(function() { + FlashStateModel.setProgressState({ + type: 'write', + percentage: 50, + eta: '15', + speed: 100000000000 + }); + }).to.throw('Invalid state eta: 15'); + }); + + it('should throw if speed is missing', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(function() { + FlashStateModel.setProgressState({ + type: 'write', + percentage: 50, + eta: 15 + }); + }).to.throw('Missing state speed'); + }); + + it('should not throw if speed is 0', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(function() { + FlashStateModel.setProgressState({ + type: 'write', + percentage: 50, + eta: 15, + speed: 0 + }); + }).to.not.throw('Missing state speed'); + }); + + }); + + describe('.getFlashResults()', function() { + + it('should get the flash results', function() { + FlashStateModel.setFlashingFlag(); + + const expectedResults = { + passedValidation: true, + cancelled: false, + sourceChecksum: '1234' + }; + + FlashStateModel.unsetFlashingFlag(expectedResults); + const results = FlashStateModel.getFlashResults(); + m.chai.expect(results).to.deep.equal(expectedResults); + }); + + }); + + describe('.getFlashState()', function() { + + it('should initially return an empty state', function() { + FlashStateModel.resetState(); + const flashState = FlashStateModel.getFlashState(); + m.chai.expect(flashState).to.deep.equal({ + percentage: 0, + speed: 0 + }); + }); + + it('should return the current flash state', function() { + const state = { + type: 'write', + percentage: 50, + eta: 15, + speed: 0 + }; + + FlashStateModel.setFlashingFlag(); + FlashStateModel.setProgressState(state); + const flashState = FlashStateModel.getFlashState(); + m.chai.expect(flashState).to.deep.equal(state); + }); + + }); + + describe('.unsetFlashingFlag()', function() { + + it('should throw if no flashing results', function() { + m.chai.expect(function() { + FlashStateModel.unsetFlashingFlag(); + }).to.throw('Missing results'); + }); + + it('should throw if errorCode is defined but it is not a number', function() { + m.chai.expect(function() { + FlashStateModel.unsetFlashingFlag({ + passedValidation: true, + cancelled: false, + sourceChecksum: '1234', + errorCode: 123 + }); + }).to.throw('Invalid results errorCode: 123'); + }); + + it('should throw if no passedValidation', function() { + m.chai.expect(function() { + FlashStateModel.unsetFlashingFlag({ + cancelled: false, + sourceChecksum: '1234' + }); + }).to.throw('Missing results passedValidation'); + }); + + it('should throw if passedValidation is not boolean', function() { + m.chai.expect(function() { + FlashStateModel.unsetFlashingFlag({ + passedValidation: 'true', + cancelled: false, + sourceChecksum: '1234' + }); + }).to.throw('Invalid results passedValidation: true'); + }); + + it('should throw if no cancelled', function() { + m.chai.expect(function() { + FlashStateModel.unsetFlashingFlag({ + passedValidation: true, + sourceChecksum: '1234' + }); + }).to.throw('Missing results cancelled'); + }); + + it('should throw if cancelled is not boolean', function() { + m.chai.expect(function() { + FlashStateModel.unsetFlashingFlag({ + passedValidation: true, + cancelled: 'false', + sourceChecksum: '1234' + }); + }).to.throw('Invalid results cancelled: false'); + }); + + it('should throw if passedValidation is true and sourceChecksum does not exist', function() { + m.chai.expect(function() { + FlashStateModel.unsetFlashingFlag({ + passedValidation: true, + cancelled: false + }); + }).to.throw('Missing results sourceChecksum'); + }); + + it('should throw if passedValidation is true and sourceChecksum is not a string', function() { + m.chai.expect(function() { + FlashStateModel.unsetFlashingFlag({ + passedValidation: true, + cancelled: false, + sourceChecksum: 12345 + }); + }).to.throw('Invalid results sourceChecksum: 12345'); + }); + + it('should throw if cancelled is true and sourceChecksum exists', function() { + m.chai.expect(function() { + FlashStateModel.unsetFlashingFlag({ + passedValidation: false, + cancelled: true, + sourceChecksum: '1234' + }); + }).to.throw('The sourceChecksum value can\'t exist if the flashing was cancelled'); + }); + + it('should throw if cancelled is true and passedValidation is true', function() { + m.chai.expect(function() { + FlashStateModel.unsetFlashingFlag({ + passedValidation: true, + cancelled: true + }); + }).to.throw('The passedValidation value can\'t be true if the flashing was cancelled'); + }); + + it('should be able to set flashing to false', function() { + FlashStateModel.unsetFlashingFlag({ + passedValidation: true, + cancelled: false, + sourceChecksum: '1234' + }); + + m.chai.expect(FlashStateModel.isFlashing()).to.be.false; + }); + + it('should reset the flashing state', function() { + FlashStateModel.setFlashingFlag(); + + FlashStateModel.setProgressState({ + type: 'write', + percentage: 50, + eta: 15, + speed: 100000000000 + }); + + m.chai.expect(FlashStateModel.getFlashState()).to.not.deep.equal({ + percentage: 0, + speed: 0 + }); + + FlashStateModel.unsetFlashingFlag({ + passedValidation: true, + cancelled: false, + sourceChecksum: '1234' + }); + + m.chai.expect(FlashStateModel.getFlashState()).to.deep.equal({ + percentage: 0, + speed: 0 + }); + }); + + }); + + describe('.setFlashingFlag()', function() { + + it('should be able to set flashing to true', function() { + FlashStateModel.setFlashingFlag(); + m.chai.expect(FlashStateModel.isFlashing()).to.be.true; + }); + + it('should reset the flash results', function() { + const expectedResults = { + passedValidation: true, + cancelled: false, + sourceChecksum: '1234' + }; + + FlashStateModel.unsetFlashingFlag(expectedResults); + const results = FlashStateModel.getFlashResults(); + m.chai.expect(results).to.deep.equal(expectedResults); + FlashStateModel.setFlashingFlag(); + m.chai.expect(FlashStateModel.getFlashResults()).to.deep.equal({}); + }); + + }); + + }); + +}); diff --git a/tests/gui/modules/image-writer.spec.js b/tests/gui/modules/image-writer.spec.js index 16540dd9..504555f5 100644 --- a/tests/gui/modules/image-writer.spec.js +++ b/tests/gui/modules/image-writer.spec.js @@ -10,402 +10,24 @@ describe('Browser: ImageWriter', function() { require('../../../lib/gui/modules/image-writer') )); + beforeEach(angular.mock.module( + require('../../../lib/gui/models/flash-state') + )); + describe('ImageWriterService', function() { let $q; let $rootScope; let ImageWriterService; + let FlashStateModel; - beforeEach(angular.mock.inject(function(_$q_, _$rootScope_, _ImageWriterService_) { + beforeEach(angular.mock.inject(function(_$q_, _$rootScope_, _ImageWriterService_, _FlashStateModel_) { $q = _$q_; $rootScope = _$rootScope_; ImageWriterService = _ImageWriterService_; + FlashStateModel = _FlashStateModel_; })); - describe('.resetState()', function() { - - it('should be able to reset the progress state', function() { - ImageWriterService.setFlashingFlag(); - ImageWriterService.setProgressState({ - type: 'write', - percentage: 50, - eta: 15, - speed: 100000000000 - }); - - ImageWriterService.resetState(); - - m.chai.expect(ImageWriterService.getFlashState()).to.deep.equal({ - percentage: 0, - speed: 0 - }); - }); - - it('should be able to reset the progress state', function() { - ImageWriterService.unsetFlashingFlag({ - passedValidation: true, - cancelled: false, - sourceChecksum: '1234' - }); - - ImageWriterService.resetState(); - m.chai.expect(ImageWriterService.getFlashResults()).to.deep.equal({}); - }); - - }); - - describe('.isFlashing()', function() { - - it('should return false by default', function() { - m.chai.expect(ImageWriterService.isFlashing()).to.be.false; - }); - - it('should return true if flashing', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(ImageWriterService.isFlashing()).to.be.true; - }); - - }); - - describe('.setProgressState()', function() { - - it('should not allow setting the state if flashing is false', function() { - ImageWriterService.unsetFlashingFlag({ - passedValidation: true, - cancelled: false, - sourceChecksum: '1234' - }); - - m.chai.expect(function() { - ImageWriterService.setProgressState({ - type: 'write', - percentage: 50, - eta: 15, - speed: 100000000000 - }); - }).to.throw('Can\'t set the flashing state when not flashing'); - }); - - it('should throw if type is missing', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(function() { - ImageWriterService.setProgressState({ - percentage: 50, - eta: 15, - speed: 100000000000 - }); - }).to.throw('Missing state type'); - }); - - it('should throw if type is not a string', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(function() { - ImageWriterService.setProgressState({ - type: 1234, - percentage: 50, - eta: 15, - speed: 100000000000 - }); - }).to.throw('Invalid state type: 1234'); - }); - - it('should not throw if percentage is 0', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(function() { - ImageWriterService.setProgressState({ - type: 'write', - percentage: 0, - eta: 15, - speed: 100000000000 - }); - }).to.not.throw('Missing state percentage'); - }); - - it('should throw if percentage is missing', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(function() { - ImageWriterService.setProgressState({ - type: 'write', - eta: 15, - speed: 100000000000 - }); - }).to.throw('Missing state percentage'); - }); - - it('should throw if percentage is not a number', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(function() { - ImageWriterService.setProgressState({ - type: 'write', - percentage: '50', - eta: 15, - speed: 100000000000 - }); - }).to.throw('Invalid state percentage: 50'); - }); - - it('should throw if eta is missing', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(function() { - ImageWriterService.setProgressState({ - type: 'write', - percentage: 50, - speed: 100000000000 - }); - }).to.throw('Missing state eta'); - }); - - it('should not throw if eta is equal to zero', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(function() { - ImageWriterService.setProgressState({ - type: 'write', - percentage: 50, - eta: 0, - speed: 100000000000 - }); - }).to.not.throw('Missing state eta'); - }); - - it('should throw if eta is not a number', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(function() { - ImageWriterService.setProgressState({ - type: 'write', - percentage: 50, - eta: '15', - speed: 100000000000 - }); - }).to.throw('Invalid state eta: 15'); - }); - - it('should throw if speed is missing', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(function() { - ImageWriterService.setProgressState({ - type: 'write', - percentage: 50, - eta: 15 - }); - }).to.throw('Missing state speed'); - }); - - it('should not throw if speed is 0', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(function() { - ImageWriterService.setProgressState({ - type: 'write', - percentage: 50, - eta: 15, - speed: 0 - }); - }).to.not.throw('Missing state speed'); - }); - - }); - - describe('.getFlashResults()', function() { - - it('should get the flash results', function() { - ImageWriterService.setFlashingFlag(); - - const expectedResults = { - passedValidation: true, - cancelled: false, - sourceChecksum: '1234' - }; - - ImageWriterService.unsetFlashingFlag(expectedResults); - const results = ImageWriterService.getFlashResults(); - m.chai.expect(results).to.deep.equal(expectedResults); - }); - - }); - - describe('.getFlashState()', function() { - - it('should initially return an empty state', function() { - ImageWriterService.resetState(); - const flashState = ImageWriterService.getFlashState(); - m.chai.expect(flashState).to.deep.equal({ - percentage: 0, - speed: 0 - }); - }); - - it('should return the current flash state', function() { - const state = { - type: 'write', - percentage: 50, - eta: 15, - speed: 0 - }; - - ImageWriterService.setFlashingFlag(); - ImageWriterService.setProgressState(state); - const flashState = ImageWriterService.getFlashState(); - m.chai.expect(flashState).to.deep.equal(state); - }); - - }); - - describe('.unsetFlashingFlag()', function() { - - it('should throw if no flashing results', function() { - m.chai.expect(function() { - ImageWriterService.unsetFlashingFlag(); - }).to.throw('Missing results'); - }); - - it('should throw if errorCode is defined but it is not a number', function() { - m.chai.expect(function() { - ImageWriterService.unsetFlashingFlag({ - passedValidation: true, - cancelled: false, - sourceChecksum: '1234', - errorCode: 123 - }); - }).to.throw('Invalid results errorCode: 123'); - }); - - it('should throw if no passedValidation', function() { - m.chai.expect(function() { - ImageWriterService.unsetFlashingFlag({ - cancelled: false, - sourceChecksum: '1234' - }); - }).to.throw('Missing results passedValidation'); - }); - - it('should throw if passedValidation is not boolean', function() { - m.chai.expect(function() { - ImageWriterService.unsetFlashingFlag({ - passedValidation: 'true', - cancelled: false, - sourceChecksum: '1234' - }); - }).to.throw('Invalid results passedValidation: true'); - }); - - it('should throw if no cancelled', function() { - m.chai.expect(function() { - ImageWriterService.unsetFlashingFlag({ - passedValidation: true, - sourceChecksum: '1234' - }); - }).to.throw('Missing results cancelled'); - }); - - it('should throw if cancelled is not boolean', function() { - m.chai.expect(function() { - ImageWriterService.unsetFlashingFlag({ - passedValidation: true, - cancelled: 'false', - sourceChecksum: '1234' - }); - }).to.throw('Invalid results cancelled: false'); - }); - - it('should throw if passedValidation is true and sourceChecksum does not exist', function() { - m.chai.expect(function() { - ImageWriterService.unsetFlashingFlag({ - passedValidation: true, - cancelled: false - }); - }).to.throw('Missing results sourceChecksum'); - }); - - it('should throw if passedValidation is true and sourceChecksum is not a string', function() { - m.chai.expect(function() { - ImageWriterService.unsetFlashingFlag({ - passedValidation: true, - cancelled: false, - sourceChecksum: 12345 - }); - }).to.throw('Invalid results sourceChecksum: 12345'); - }); - - it('should throw if cancelled is true and sourceChecksum exists', function() { - m.chai.expect(function() { - ImageWriterService.unsetFlashingFlag({ - passedValidation: false, - cancelled: true, - sourceChecksum: '1234' - }); - }).to.throw('The sourceChecksum value can\'t exist if the flashing was cancelled'); - }); - - it('should throw if cancelled is true and passedValidation is true', function() { - m.chai.expect(function() { - ImageWriterService.unsetFlashingFlag({ - passedValidation: true, - cancelled: true - }); - }).to.throw('The passedValidation value can\'t be true if the flashing was cancelled'); - }); - - it('should be able to set flashing to false', function() { - ImageWriterService.unsetFlashingFlag({ - passedValidation: true, - cancelled: false, - sourceChecksum: '1234' - }); - - m.chai.expect(ImageWriterService.isFlashing()).to.be.false; - }); - - it('should reset the flashing state', function() { - ImageWriterService.setFlashingFlag(); - - ImageWriterService.setProgressState({ - type: 'write', - percentage: 50, - eta: 15, - speed: 100000000000 - }); - - m.chai.expect(ImageWriterService.getFlashState()).to.not.deep.equal({ - percentage: 0, - speed: 0 - }); - - ImageWriterService.unsetFlashingFlag({ - passedValidation: true, - cancelled: false, - sourceChecksum: '1234' - }); - - m.chai.expect(ImageWriterService.getFlashState()).to.deep.equal({ - percentage: 0, - speed: 0 - }); - }); - - }); - - describe('.setFlashingFlag()', function() { - - it('should be able to set flashing to true', function() { - ImageWriterService.setFlashingFlag(); - m.chai.expect(ImageWriterService.isFlashing()).to.be.true; - }); - - it('should reset the flash results', function() { - const expectedResults = { - passedValidation: true, - cancelled: false, - sourceChecksum: '1234' - }; - - ImageWriterService.unsetFlashingFlag(expectedResults); - const results = ImageWriterService.getFlashResults(); - m.chai.expect(results).to.deep.equal(expectedResults); - ImageWriterService.setFlashingFlag(); - m.chai.expect(ImageWriterService.getFlashResults()).to.deep.equal({}); - }); - - }); - describe('.flash()', function() { describe('given a succesful write', function() { @@ -424,7 +46,7 @@ describe('Browser: ImageWriter', function() { }); it('should set flashing to false when done', function() { - ImageWriterService.unsetFlashingFlag({ + FlashStateModel.unsetFlashingFlag({ passedValidation: true, cancelled: false, sourceChecksum: '1234' @@ -432,11 +54,11 @@ describe('Browser: ImageWriter', function() { ImageWriterService.flash('foo.img', '/dev/disk2'); $rootScope.$apply(); - m.chai.expect(ImageWriterService.isFlashing()).to.be.false; + m.chai.expect(FlashStateModel.isFlashing()).to.be.false; }); it('should prevent writing more than once', function() { - ImageWriterService.unsetFlashingFlag({ + FlashStateModel.unsetFlashingFlag({ passedValidation: true, cancelled: false, sourceChecksum: '1234' @@ -480,18 +102,18 @@ describe('Browser: ImageWriter', function() { it('should set flashing to false when done', function() { ImageWriterService.flash('foo.img', '/dev/disk2'); $rootScope.$apply(); - m.chai.expect(ImageWriterService.isFlashing()).to.be.false; + m.chai.expect(FlashStateModel.isFlashing()).to.be.false; }); it('should set the error code in the flash results', function() { ImageWriterService.flash('foo.img', '/dev/disk2'); $rootScope.$apply(); - const flashResults = ImageWriterService.getFlashResults(); + const flashResults = FlashStateModel.getFlashResults(); m.chai.expect(flashResults.errorCode).to.equal('FOO'); }); it('should be rejected with the error', function() { - ImageWriterService.unsetFlashingFlag({ + FlashStateModel.unsetFlashingFlag({ passedValidation: true, cancelled: false, sourceChecksum: '1234'