diff --git a/lib/gui/app.js b/lib/gui/app.js index 695fbcb4..e8871e6a 100644 --- a/lib/gui/app.js +++ b/lib/gui/app.js @@ -235,7 +235,7 @@ app.controller('AppController', function( return; } - this.selection.setDrive(drive); + this.selection.setDrive(drive.device); AnalyticsService.logEvent('Select drive', { device: drive.device diff --git a/lib/gui/components/drive-selector/templates/drive-selector-modal.tpl.html b/lib/gui/components/drive-selector/templates/drive-selector-modal.tpl.html index 58074b2d..89e864a9 100644 --- a/lib/gui/components/drive-selector/templates/drive-selector-modal.tpl.html +++ b/lib/gui/components/drive-selector/templates/drive-selector-modal.tpl.html @@ -7,7 +7,7 @@ diff --git a/lib/gui/models/selection-state.js b/lib/gui/models/selection-state.js index a0c0805e..fadc6726 100644 --- a/lib/gui/models/selection-state.js +++ b/lib/gui/models/selection-state.js @@ -24,23 +24,21 @@ const _ = require('lodash'); const angular = require('angular'); const Store = require('./store'); const MODULE_NAME = 'Etcher.Models.SelectionState'; -const SelectionStateModel = angular.module(MODULE_NAME, []); +const SelectionStateModel = angular.module(MODULE_NAME, [ + require('./drives') +]); -SelectionStateModel.service('SelectionStateModel', function() { +SelectionStateModel.service('SelectionStateModel', function(DrivesModel) { /** * @summary Set a drive * @function * @public * - * @param {Object} drive - drive + * @param {String} drive - drive device * * @example - * SelectionStateModel.setDrive({ - * device: '/dev/disk2', - * name: 'USB drive', - * size: 999999999 - * }); + * SelectionStateModel.setDrive('/dev/disk2'); */ this.setDrive = (drive) => { Store.dispatch({ @@ -144,12 +142,10 @@ SelectionStateModel.service('SelectionStateModel', function() { * @function * @public * - * @param {Object} drive - drive + * @param {String} drive - drive device * * @example - * SelectionStateModel.toggleSetDrive({ - * device: '/dev/disk2' - * }); + * SelectionStateModel.toggleSetDrive('/dev/disk2'); */ this.toggleSetDrive = (drive) => { if (this.isCurrentDrive(drive)) { @@ -189,7 +185,9 @@ SelectionStateModel.service('SelectionStateModel', function() { * const drive = SelectionStateModel.getDrive(); */ this.getDrive = () => { - return _.get(Store.getState().toJS(), 'selection.drive'); + return _.find(DrivesModel.getDrives(), { + device: Store.getState().getIn([ 'selection', 'drive' ]) + }); }; /** @@ -355,27 +353,20 @@ SelectionStateModel.service('SelectionStateModel', function() { * @function * @public * - * @param {Object} drive - drive + * @param {String} drive - drive device * @returns {Boolean} whether the drive is the current drive * * @example - * if (SelectionStateModel.isCurrentDrive({ - * device: '/dev/sdb', - * description: 'DataTraveler 2.0', - * size: '7.3G', - * mountpoint: '/media/UNTITLED', - * name: '/dev/sdb', - * system: false - * })) { + * if (SelectionStateModel.isCurrentDrive('/dev/sdb')) { * console.log('This is the current drive!'); * } */ this.isCurrentDrive = (drive) => { - if (!drive || !drive.device) { + if (!drive) { return false; } - return drive.device === _.get(this.getDrive(), 'device'); + return drive === _.get(this.getDrive(), 'device'); }; }); diff --git a/lib/gui/models/store.js b/lib/gui/models/store.js index 46a467ce..0fb528a5 100644 --- a/lib/gui/models/store.js +++ b/lib/gui/models/store.js @@ -102,7 +102,7 @@ const storeReducer = (state, action) => { if (state.getIn([ 'selection', 'image', 'size' ], 0) <= drive.size && !drive.protected) { return storeReducer(newState, { type: ACTIONS.SELECT_DRIVE, - data: drive + data: drive.device }); } @@ -217,40 +217,28 @@ const storeReducer = (state, action) => { } case ACTIONS.SELECT_DRIVE: { - if (!action.data.device) { - throw new Error('Missing drive device'); + if (!action.data) { + throw new Error('Missing drive'); } - if (!_.isString(action.data.device)) { - throw new Error(`Invalid drive device: ${action.data.device}`); + if (!_.isString(action.data)) { + throw new Error(`Invalid drive: ${action.data}`); } - if (!action.data.name) { - throw new Error('Missing drive name'); + const selectedDrive = state.get('availableDrives').find((drive) => { + return drive.get('device') === action.data; + }); + + if (!selectedDrive) { + throw new Error(`The drive is not available: ${action.data}`); } - if (!_.isString(action.data.name)) { - throw new Error(`Invalid drive name: ${action.data.name}`); - } - - if (!action.data.size) { - throw new Error('Missing drive size'); - } - - if (!_.isNumber(action.data.size)) { - throw new Error(`Invalid drive size: ${action.data.size}`); - } - - if (!_.isBoolean(action.data.protected)) { - throw new Error(`Invalid drive protected state: ${action.data.protected}`); - } - - if (action.data.protected) { + if (selectedDrive.get('protected')) { throw new Error('The drive is write-protected'); } // TODO: Reuse from SelectionStateModel.isDriveLargeEnough() - if (state.getIn([ 'selection', 'image', 'size' ], 0) > action.data.size) { + if (state.getIn([ 'selection', 'image', 'size' ], 0) > selectedDrive.get('size')) { throw new Error('The drive is not large enough'); } diff --git a/tests/gui/models/drives.spec.js b/tests/gui/models/drives.spec.js index 748806cc..28782076 100644 --- a/tests/gui/models/drives.spec.js +++ b/tests/gui/models/drives.spec.js @@ -235,21 +235,25 @@ describe('Browser: DrivesModel', function() { describe('given one of the drives was selected', function() { beforeEach(function() { - SelectionStateModel.setDrive({ - device: '/dev/sdc', - name: 'USB Drive', - size: 9999999, - mountpoint: '/mnt/bar', - system: false, - protected: false - }); + DrivesModel.setDrives([ + { + device: '/dev/sdc', + name: 'USB Drive', + size: 9999999, + mountpoint: '/mnt/bar', + system: false, + protected: false + } + ]); + + SelectionStateModel.setDrive('/dev/sdc'); }); afterEach(function() { SelectionStateModel.removeDrive(); }); - it('should be delected if its not contain in the available drives anymore', function() { + it('should be deleted if its not contain in the available drives anymore', function() { m.chai.expect(SelectionStateModel.hasDrive()).to.be.true; DrivesModel.setDrives([ { diff --git a/tests/gui/models/selection-state.spec.js b/tests/gui/models/selection-state.spec.js index 43bf2ec2..955f3d51 100644 --- a/tests/gui/models/selection-state.spec.js +++ b/tests/gui/models/selection-state.spec.js @@ -10,12 +10,18 @@ describe('Browser: SelectionState', function() { require('../../../lib/gui/models/selection-state') )); + beforeEach(angular.mock.module( + require('../../../lib/gui/models/drives') + )); + describe('SelectionStateModel', function() { let SelectionStateModel; + let DrivesModel; - beforeEach(angular.mock.inject(function(_SelectionStateModel_) { + beforeEach(angular.mock.inject(function(_SelectionStateModel_, _DrivesModel_) { SelectionStateModel = _SelectionStateModel_; + DrivesModel = _DrivesModel_; })); describe('given a clean state', function() { @@ -126,12 +132,22 @@ describe('Browser: SelectionState', function() { describe('given a drive', function() { beforeEach(function() { - SelectionStateModel.setDrive({ - device: '/dev/disk2', - name: 'USB Drive', - size: 999999999, - protected: false - }); + DrivesModel.setDrives([ + { + device: '/dev/disk2', + name: 'USB Drive', + size: 999999999, + protected: false + }, + { + device: '/dev/disk5', + name: 'USB Drive', + size: 999999999, + protected: false + } + ]); + + SelectionStateModel.setDrive('/dev/disk2'); }); describe('.getDrive()', function() { @@ -160,13 +176,7 @@ describe('Browser: SelectionState', function() { describe('.setDrive()', function() { it('should override the drive', function() { - SelectionStateModel.setDrive({ - device: '/dev/disk5', - name: 'USB Drive', - size: 999999999, - protected: false - }); - + SelectionStateModel.setDrive('/dev/disk5'); const drive = SelectionStateModel.getDrive(); m.chai.expect(drive).to.deep.equal({ device: '/dev/disk5', @@ -195,13 +205,16 @@ describe('Browser: SelectionState', function() { describe('.setDrive()', function() { it('should be able to set a drive', function() { - SelectionStateModel.setDrive({ - device: '/dev/disk5', - name: 'USB Drive', - size: 999999999, - protected: false - }); + DrivesModel.setDrives([ + { + device: '/dev/disk5', + name: 'USB Drive', + size: 999999999, + protected: false + } + ]); + SelectionStateModel.setDrive('/dev/disk5'); const drive = SelectionStateModel.getDrive(); m.chai.expect(drive).to.deep.equal({ device: '/dev/disk5', @@ -212,98 +225,39 @@ describe('Browser: SelectionState', function() { }); it('should throw if drive is write protected', function() { - m.chai.expect(function() { - SelectionStateModel.setDrive({ + DrivesModel.setDrives([ + { device: '/dev/disk1', name: 'USB Drive', size: 999999999, protected: true - }); + } + ]); + + m.chai.expect(function() { + SelectionStateModel.setDrive('/dev/disk1'); }).to.throw('The drive is write-protected'); }); - it('should throw if no device', function() { - m.chai.expect(function() { - SelectionStateModel.setDrive({ + it('should throw if the drive is not available', function() { + DrivesModel.setDrives([ + { + device: '/dev/disk1', name: 'USB Drive', size: 999999999, - protected: false - }); - }).to.throw('Missing drive device'); + protected: true + } + ]); + + m.chai.expect(function() { + SelectionStateModel.setDrive('/dev/disk5'); + }).to.throw('The drive is not available: /dev/disk5'); }); it('should throw if device is not a string', function() { m.chai.expect(function() { - SelectionStateModel.setDrive({ - device: 123, - name: 'USB Drive', - size: 999999999, - protected: false - }); - }).to.throw('Invalid drive device: 123'); - }); - - it('should throw if no name', function() { - m.chai.expect(function() { - SelectionStateModel.setDrive({ - device: '/dev/disk2', - size: 999999999, - protected: false - }); - }).to.throw('Missing drive name'); - }); - - it('should throw if name is not a string', function() { - m.chai.expect(function() { - SelectionStateModel.setDrive({ - device: '/dev/disk2', - name: 123, - size: 999999999, - protected: false - }); - }).to.throw('Invalid drive name: 123'); - }); - - it('should throw if no size', function() { - m.chai.expect(function() { - SelectionStateModel.setDrive({ - device: '/dev/disk2', - name: 'USB Drive', - protected: false - }); - }).to.throw('Missing drive size'); - }); - - it('should throw if size is not a number', function() { - m.chai.expect(function() { - SelectionStateModel.setDrive({ - device: '/dev/disk2', - name: 'USB Drive', - size: '999999999', - protected: false - }); - }).to.throw('Invalid drive size: 999999999'); - }); - - it('should throw if no protected property', function() { - m.chai.expect(function() { - SelectionStateModel.setDrive({ - device: '/dev/disk2', - name: 'USB Drive', - size: 999999999 - }); - }).to.throw('Invalid drive protected state: undefined'); - }); - - it('should throw if the protected is not boolean', function() { - m.chai.expect(function() { - SelectionStateModel.setDrive({ - device: '/dev/disk2', - name: 'USB Drive', - size: 999999999, - protected: 'foo' - }); - }).to.throw('Invalid drive protected state: foo'); + SelectionStateModel.setDrive(123); + }).to.throw('Invalid drive: 123'); }); }); @@ -410,13 +364,17 @@ describe('Browser: SelectionState', function() { describe('.setDrive()', function() { it('should throw if drive is not large enough', function() { - m.chai.expect(function() { - SelectionStateModel.setDrive({ - device: '/dev/disk1', + DrivesModel.setDrives([ + { + device: '/dev/disk2', name: 'USB Drive', size: 999999998, protected: false - }); + } + ]); + + m.chai.expect(function() { + SelectionStateModel.setDrive('/dev/disk2'); }).to.throw('The drive is not large enough'); }); @@ -608,12 +566,16 @@ describe('Browser: SelectionState', function() { describe('given a drive', function() { beforeEach(function() { - SelectionStateModel.setDrive({ - device: '/dev/disk1', - name: 'USB Drive', - size: 999999999, - protected: false - }); + DrivesModel.setDrives([ + { + device: '/dev/disk1', + name: 'USB Drive', + size: 999999999, + protected: false + } + ]); + + SelectionStateModel.setDrive('/dev/disk1'); SelectionStateModel.setImage({ path: 'foo.img', @@ -677,72 +639,31 @@ describe('Browser: SelectionState', function() { describe('given a selected drive', function() { beforeEach(function() { - SelectionStateModel.setDrive({ - device: '/dev/sdb', - description: 'DataTraveler 2.0', - size: 999999999, - mountpoint: '/media/UNTITLED', - name: '/dev/sdb', - system: false, - protected: false - }); + DrivesModel.setDrives([ + { + device: '/dev/sdb', + description: 'DataTraveler 2.0', + size: 999999999, + mountpoint: '/media/UNTITLED', + name: '/dev/sdb', + system: false, + protected: false + } + ]); + + SelectionStateModel.setDrive('/dev/sdb'); }); it('should return false if an undefined value is passed', function() { m.chai.expect(SelectionStateModel.isCurrentDrive()).to.be.false; }); - it('should return false if an empty object is passed', function() { - m.chai.expect(SelectionStateModel.isCurrentDrive({})).to.be.false; - }); - it('should return true given the exact same drive', function() { - m.chai.expect(SelectionStateModel.isCurrentDrive({ - device: '/dev/sdb', - description: 'DataTraveler 2.0', - size: 999999999, - mountpoint: '/media/UNTITLED', - name: '/dev/sdb', - system: false, - protected: false - })).to.be.true; + m.chai.expect(SelectionStateModel.isCurrentDrive('/dev/sdb')).to.be.true; }); - it('should return true given the exact same drive with a $$hashKey', function() { - m.chai.expect(SelectionStateModel.isCurrentDrive({ - device: '/dev/sdb', - description: 'DataTraveler 2.0', - size: 999999999, - mountpoint: '/media/UNTITLED', - name: '/dev/sdb', - system: false, - $$hashKey: 1234, - protected: false - })).to.be.true; - }); - - it('should return false if the device changes', function() { - m.chai.expect(SelectionStateModel.isCurrentDrive({ - device: '/dev/sdc', - description: 'DataTraveler 2.0', - size: 999999999, - mountpoint: '/media/UNTITLED', - name: '/dev/sdb', - system: false, - protected: false - })).to.be.false; - }); - - it('should return true if the description changes', function() { - m.chai.expect(SelectionStateModel.isCurrentDrive({ - device: '/dev/sdb', - description: 'DataTraveler 3.0', - size: 999999999, - mountpoint: '/media/UNTITLED', - name: '/dev/sdb', - system: false, - protected: false - })).to.be.true; + it('should return false if it is not the current drive', function() { + m.chai.expect(SelectionStateModel.isCurrentDrive('/dev/sdc')).to.be.false; }); }); @@ -757,22 +678,8 @@ describe('Browser: SelectionState', function() { m.chai.expect(SelectionStateModel.isCurrentDrive()).to.be.false; }); - it('should return false if an empty object is passed', function() { - m.chai.expect(SelectionStateModel.isCurrentDrive({})).to.be.false; - }); - it('should return false for anything', function() { - - m.chai.expect(SelectionStateModel.isCurrentDrive({ - device: '/dev/sdb', - description: 'DataTraveler 2.0', - size: 999999999, - mountpoint: '/media/UNTITLED', - name: '/dev/sdb', - system: false, - protected: false - })).to.be.false; - + m.chai.expect(SelectionStateModel.isCurrentDrive('/dev/sdb')).to.be.false; }); }); @@ -794,12 +701,22 @@ describe('Browser: SelectionState', function() { protected: false }; - SelectionStateModel.setDrive(this.drive); + DrivesModel.setDrives([ + this.drive, + { + device: '/dev/disk2', + name: 'USB Drive', + size: 999999999, + protected: false + } + ]); + + SelectionStateModel.setDrive(this.drive.device); }); it('should be able to remove the drive', function() { m.chai.expect(SelectionStateModel.hasDrive()).to.be.true; - SelectionStateModel.toggleSetDrive(this.drive); + SelectionStateModel.toggleSetDrive(this.drive.device); m.chai.expect(SelectionStateModel.hasDrive()).to.be.false; }); @@ -812,7 +729,7 @@ describe('Browser: SelectionState', function() { }; m.chai.expect(SelectionStateModel.getDrive()).to.deep.equal(this.drive); - SelectionStateModel.toggleSetDrive(drive); + SelectionStateModel.toggleSetDrive(drive.device); m.chai.expect(SelectionStateModel.getDrive()).to.deep.equal(drive); m.chai.expect(SelectionStateModel.getDrive()).to.not.deep.equal(this.drive); }); @@ -834,7 +751,7 @@ describe('Browser: SelectionState', function() { }; m.chai.expect(SelectionStateModel.hasDrive()).to.be.false; - SelectionStateModel.toggleSetDrive(drive); + SelectionStateModel.toggleSetDrive(drive.device); m.chai.expect(SelectionStateModel.getDrive()).to.deep.equal(drive); });