Prevent selection of drives that are not large enough (#408)

* Make .selectImage() dialog return an object with a `path` property

This allows to return more than one value for the selected image,
like image size and other metadata for example.

Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>

* Return image size from .selectImage() dialog

This property will be useful to perform some sanity checks, like
ensuring the selected drive is large enough to contain the image.

Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>

* Store both the image path and the image size in the selection model

In order to simplify accessing such properties in an encapsulated
manner, `SelectionStateModel.getImage()` was replaced with the following
functions:

- `SelectionStateModel.getImagePath()`.
- `SelectionStateModel.getImageSize()`.

Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>

* Increase SelectionStateModel setter validation

The model not providing any kind of validation was the source of some
bugs I encountered while implementing the previous commits that would
not have happened otherwise.

Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>

* Prevent selection of drives that are not large enough

- The drive selector modal was modified such that drives that are not
large enough are crossed out, and the user is not able to click them.

- In the case of drive auto-selection, not large enough drives won't
attempt to get autoselected.

This commit introduces:

- The `SelectionStateModel.isDriveLargeEnough()` function.

Fixes: https://github.com/resin-io/etcher/issues/344
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This commit is contained in:
Juan Cruz Viotti 2016-05-12 15:38:19 -04:00
parent c31ccdbdbe
commit bf37ee72df
9 changed files with 456 additions and 79 deletions

View File

@ -6252,6 +6252,25 @@ button.btn:focus, button.progress-button:focus {
.alert-ribbon--open { .alert-ribbon--open {
top: 0; } top: 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.
*/
.list-group-item[disabled] {
text-decoration: line-through;
cursor: not-allowed; }
/* /*
* Copyright 2016 Resin.io * Copyright 2016 Resin.io
* *

View File

@ -160,10 +160,14 @@ app.controller('AppController', function(
// `angular.equals` is used instead of `_.isEqual` to // `angular.equals` is used instead of `_.isEqual` to
// cope with `$$hashKey`. // cope with `$$hashKey`.
if (!angular.equals(self.selection.getDrive(), drive)) { if (!angular.equals(self.selection.getDrive(), drive)) {
AnalyticsService.logEvent('Auto-select drive', {
device: drive.device if (self.selection.isDriveLargeEnough(drive)) {
}); self.selectDrive(drive);
self.selectDrive(drive);
AnalyticsService.logEvent('Auto-select drive', {
device: drive.device
});
}
} }
} }
@ -181,9 +185,7 @@ app.controller('AppController', function(
this.selectImage = function(image) { this.selectImage = function(image) {
self.selection.setImage(image); self.selection.setImage(image);
AnalyticsService.logEvent('Select image', { AnalyticsService.logEvent('Select image', image);
image: image
});
}; };
this.openImageSelector = function() { this.openImageSelector = function() {

View File

@ -6,12 +6,15 @@
<div class="modal-body"> <div class="modal-body">
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item" ng-repeat="drive in modal.scanner.drives" <li class="list-group-item" ng-repeat="drive in modal.scanner.drives"
ng-click="modal.state.toggleSetDrive(drive)"> ng-disabled="!modal.state.isDriveLargeEnough(drive)"
ng-click="modal.state.isDriveLargeEnough(drive) && modal.state.toggleSetDrive(drive)">
<div> <div>
<h4 class="list-group-item-heading">{{ drive.description }} - {{ drive.size | gigabyte | number:1 }} GB</h4> <h4 class="list-group-item-heading">{{ drive.description }} - {{ drive.size | gigabyte | number:1 }} GB</h4>
<p class="list-group-item-text">{{ drive.name }}</p> <p class="list-group-item-text">{{ drive.name }}</p>
</div> </div>
<span class="tick tick--success" ng-disabled="!modal.state.isCurrentDrive(drive)"></span> <span class="tick tick--success"
ng-show="modal.state.isDriveLargeEnough(drive)"
ng-disabled="!modal.state.isCurrentDrive(drive)"></span>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -42,15 +42,88 @@ SelectionStateModel.service('SelectionStateModel', function() {
* *
* @param {Object} drive - drive * @param {Object} drive - drive
* *
* @throws Will throw if drive lacks `.device`.
* @throws Will throw if `drive.device` is not a string.
* @throws Will throw if drive lacks `.name`.
* @throws Will throw if `drive.name` is not a string.
* @throws Will throw if drive lacks `.size`.
* @throws Will throw if `drive.size` is not a number.
* @throws Will throw if there is an image and the drive is not large enough.
*
* @example * @example
* SelectionStateModel.setDrive({ * SelectionStateModel.setDrive({
* device: '/dev/disk2' * device: '/dev/disk2',
* name: 'USB drive',
* size: 999999999
* }); * });
*/ */
this.setDrive = function(drive) { this.setDrive = function(drive) {
if (!drive.device) {
throw new Error('Missing drive device');
}
if (!_.isString(drive.device)) {
throw new Error(`Invalid drive device: ${drive.device}`);
}
if (!drive.name) {
throw new Error('Missing drive name');
}
if (!_.isString(drive.name)) {
throw new Error(`Invalid drive name: ${drive.name}`);
}
if (!drive.size) {
throw new Error('Missing drive size');
}
if (!_.isNumber(drive.size)) {
throw new Error(`Invalid drive size: ${drive.size}`);
}
if (!self.isDriveLargeEnough(drive)) {
throw new Error('The drive is not large enough');
}
selection.drive = drive; selection.drive = drive;
}; };
/**
* @summary Check if a drive is large enough for the selected image
* @function
* @public
*
* @description
* For convenience, if there is no image selected, this function
* returns true.
*
* Notice that if you select the drive before the image, the check
* will not take place and it'll be the client's responsibility
* to do so.
*
* @param {Object} drive - drive
* @returns {Boolean} whether the drive is large enough
*
* @example
* SelectionStateModel.setImage({
* path: 'rpi.img',
* size: 100000000
* });
*
* if (SelectionStateModel.isDriveLargeEnough({
* device: '/dev/disk2',
* name: 'My Drive',
* size: 123456789
* })) {
* console.log('We can flash the image to this drive!');
* }
*/
this.isDriveLargeEnough = function(drive) {
return (self.getImageSize() || 0) <= drive.size;
};
/** /**
* @summary Toggle set drive * @summary Toggle set drive
* @function * @function
@ -76,12 +149,36 @@ SelectionStateModel.service('SelectionStateModel', function() {
* @function * @function
* @public * @public
* *
* @param {String} image - image * @param {Object} image - image
*
* @throws Will throw if image lacks `.path`.
* @throws Will throw if `image.path` is not a string.
* @throws Will throw if image lacks `.size`.
* @throws Will throw if `image.size` is not a number.
* *
* @example * @example
* SelectionStateModel.setImage('foo.img'); * SelectionStateModel.setImage({
* path: 'foo.img'
* });
*/ */
this.setImage = function(image) { this.setImage = function(image) {
if (!image.path) {
throw new Error('Missing image path');
}
if (!_.isString(image.path)) {
throw new Error(`Invalid image path: ${image.path}`);
}
if (!image.size) {
throw new Error('Missing image size');
}
if (!_.isNumber(image.size)) {
throw new Error(`Invalid image size: ${image.size}`);
}
selection.image = image; selection.image = image;
}; };
@ -104,17 +201,31 @@ SelectionStateModel.service('SelectionStateModel', function() {
}; };
/** /**
* @summary Get image * @summary Get image path
* @function * @function
* @public * @public
* *
* @returns {String} image * @returns {String} image path
* *
* @example * @example
* const image = SelectionStateModel.getImage(); * const imagePath = SelectionStateModel.getImagePath();
*/ */
this.getImage = function() { this.getImagePath = function() {
return selection.image; return _.get(selection.image, 'path');
};
/**
* @summary Get image size
* @function
* @public
*
* @returns {Number} image size
*
* @example
* const imageSize = SelectionStateModel.getImageSize();
*/
this.getImageSize = function() {
return _.get(selection.image, 'size');
}; };
/** /**
@ -146,7 +257,7 @@ SelectionStateModel.service('SelectionStateModel', function() {
* } * }
*/ */
this.hasImage = function() { this.hasImage = function() {
return Boolean(self.getImage()); return Boolean(self.getImagePath() && self.getImageSize());
}; };
/** /**
@ -157,7 +268,7 @@ SelectionStateModel.service('SelectionStateModel', function() {
* @example * @example
* SelectionStateModel.removeDrive(); * SelectionStateModel.removeDrive();
*/ */
this.removeDrive = _.partial(self.setDrive, undefined); this.removeDrive = _.partial(_.unset, selection, 'drive');
/** /**
* @summary Remove image * @summary Remove image
@ -167,7 +278,7 @@ SelectionStateModel.service('SelectionStateModel', function() {
* @example * @example
* SelectionStateModel.removeImage(); * SelectionStateModel.removeImage();
*/ */
this.removeImage = _.partial(self.setImage, undefined); this.removeImage = _.partial(_.unset, selection, 'image');
/** /**
* @summary Clear selections * @summary Clear selections

View File

@ -17,6 +17,7 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const fs = require('fs');
const electron = require('electron'); const electron = require('electron');
const imageStream = require('etcher-image-stream'); const imageStream = require('etcher-image-stream');
@ -30,16 +31,16 @@ module.exports = function($q) {
* @description * @description
* Notice that by image, we mean *.img/*.iso/*.zip/etc files. * Notice that by image, we mean *.img/*.iso/*.zip/etc files.
* *
* @fulfil {String} - selected image * @fulfil {Object} - selected image
* @returns {Promise}; * @returns {Promise};
* *
* @example * @example
* OSDialogService.selectImage().then(function(image) { * OSDialogService.selectImage().then(function(image) {
* console.log('The selected image is', image); * console.log('The selected image is', image.path);
* }); * });
*/ */
this.selectImage = function() { this.selectImage = function() {
return $q(function(resolve) { return $q(function(resolve, reject) {
electron.remote.dialog.showOpenDialog({ electron.remote.dialog.showOpenDialog({
properties: [ properties: [
'openFile' 'openFile'
@ -55,8 +56,23 @@ module.exports = function($q) {
// `_.first` is smart enough to not throw and return `undefined` // `_.first` is smart enough to not throw and return `undefined`
// if we pass it an `undefined` value (e.g: when the selection // if we pass it an `undefined` value (e.g: when the selection
// dialog was cancelled). // dialog was cancelled).
return resolve(_.first(files)); const imagePath = _.first(files);
if (!imagePath) {
return resolve();
}
fs.stat(imagePath, function(error, stats) {
if (error) {
return reject(error);
}
return resolve({
path: imagePath,
size: stats.size
});
});
}); });
}); });
}; };

View File

@ -11,7 +11,7 @@
<p class="step-footer">.img, .iso, or <span class="step-footer-underline" uib-tooltip=".zip, .xz">compressed images</span></p> <p class="step-footer">.img, .iso, or <span class="step-footer-underline" uib-tooltip=".zip, .xz">compressed images</span></p>
</div> </div>
<div ng-show="app.selection.hasImage()"> <div ng-show="app.selection.hasImage()">
<div ng-bind="app.selection.getImage() | basename"></div> <div ng-bind="app.selection.getImagePath() | basename"></div>
<button class="btn btn-link step-footer" <button class="btn btn-link step-footer"
ng-click="app.reselectImage()" ng-click="app.reselectImage()"
@ -74,7 +74,7 @@
striped="{{ app.writer.state.type == 'check' }}" striped="{{ app.writer.state.type == 'check' }}"
ng-attr-active="{{ app.writer.isFlashing() }}" ng-attr-active="{{ app.writer.isFlashing() }}"
ng-show="app.success" ng-show="app.success"
ng-click="app.flash(app.selection.getImage(), app.selection.getDrive())" ng-click="app.flash(app.selection.getImagePath(), app.selection.getDrive())"
ng-disabled="!app.selection.hasImage() || !app.selection.hasDrive()"> ng-disabled="!app.selection.hasImage() || !app.selection.hasDrive()">
<span ng-show="app.writer.state.progress == 100 && app.writer.isFlashing()">Finishing...</span> <span ng-show="app.writer.state.progress == 100 && app.writer.isFlashing()">Finishing...</span>
<span ng-show="app.writer.state.progress == 0 && !app.writer.isFlashing()">Flash!</span> <span ng-show="app.writer.state.progress == 0 && !app.writer.isFlashing()">Flash!</span>

View File

@ -0,0 +1,20 @@
/*
* 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.
*/
.list-group-item[disabled] {
text-decoration: line-through;
cursor: not-allowed;
}

View File

@ -43,6 +43,7 @@ $alert-padding: 13px;
@import "./components/tick"; @import "./components/tick";
@import "./components/modal"; @import "./components/modal";
@import "./components/alert-ribbon"; @import "./components/alert-ribbon";
@import "./components/list-group";
@import "../components/update-notifier/styles/update-notifier"; @import "../components/update-notifier/styles/update-notifier";
@import "../components/progress-button/styles/progress-button"; @import "../components/progress-button/styles/progress-button";
@import "../components/svg-icon/styles/svg-icon"; @import "../components/svg-icon/styles/svg-icon";

View File

@ -29,9 +29,12 @@ describe('Browser: SelectionState', function() {
m.chai.expect(drive).to.be.undefined; m.chai.expect(drive).to.be.undefined;
}); });
it('getImage() should return undefined', function() { it('getImagePath() should return undefined', function() {
const image = SelectionStateModel.getImage(); m.chai.expect(SelectionStateModel.getImagePath()).to.be.undefined;
m.chai.expect(image).to.be.undefined; });
it('getImageSize() should return undefined', function() {
m.chai.expect(SelectionStateModel.getImageSize()).to.be.undefined;
}); });
it('hasDrive() should return false', function() { it('hasDrive() should return false', function() {
@ -46,34 +49,25 @@ describe('Browser: SelectionState', function() {
}); });
describe('given an empty object drive', function() {
beforeEach(function() {
SelectionStateModel.setDrive({});
});
describe('.getDrive()', function() {
it('should return undefined', function() {
const drive = SelectionStateModel.getDrive();
m.chai.expect(drive).to.be.undefined;
});
});
});
describe('given a drive', function() { describe('given a drive', function() {
beforeEach(function() { beforeEach(function() {
SelectionStateModel.setDrive('/dev/disk2'); SelectionStateModel.setDrive({
device: '/dev/disk2',
name: 'USB Drive',
size: 999999999
});
}); });
describe('.getDrive()', function() { describe('.getDrive()', function() {
it('should return the drive', function() { it('should return the drive', function() {
const drive = SelectionStateModel.getDrive(); const drive = SelectionStateModel.getDrive();
m.chai.expect(drive).to.equal('/dev/disk2'); m.chai.expect(drive).to.deep.equal({
device: '/dev/disk2',
name: 'USB Drive',
size: 999999999
});
}); });
}); });
@ -90,9 +84,18 @@ describe('Browser: SelectionState', function() {
describe('.setDrive()', function() { describe('.setDrive()', function() {
it('should override the drive', function() { it('should override the drive', function() {
SelectionStateModel.setDrive('/dev/disk5'); SelectionStateModel.setDrive({
device: '/dev/disk5',
name: 'USB Drive',
size: 999999999
});
const drive = SelectionStateModel.getDrive(); const drive = SelectionStateModel.getDrive();
m.chai.expect(drive).to.equal('/dev/disk5'); m.chai.expect(drive).to.deep.equal({
device: '/dev/disk5',
name: 'USB Drive',
size: 999999999
});
}); });
}); });
@ -114,9 +117,75 @@ describe('Browser: SelectionState', function() {
describe('.setDrive()', function() { describe('.setDrive()', function() {
it('should be able to set a drive', function() { it('should be able to set a drive', function() {
SelectionStateModel.setDrive('/dev/disk5'); SelectionStateModel.setDrive({
device: '/dev/disk5',
name: 'USB Drive',
size: 999999999
});
const drive = SelectionStateModel.getDrive(); const drive = SelectionStateModel.getDrive();
m.chai.expect(drive).to.equal('/dev/disk5'); m.chai.expect(drive).to.deep.equal({
device: '/dev/disk5',
name: 'USB Drive',
size: 999999999
});
});
it('should throw if no device', function() {
m.chai.expect(function() {
SelectionStateModel.setDrive({
name: 'USB Drive',
size: 999999999
});
}).to.throw('Missing drive device');
});
it('should throw if device is not a string', function() {
m.chai.expect(function() {
SelectionStateModel.setDrive({
device: 123,
name: 'USB Drive',
size: 999999999
});
}).to.throw('Invalid drive device: 123');
});
it('should throw if no name', function() {
m.chai.expect(function() {
SelectionStateModel.setDrive({
device: '/dev/disk2',
size: 999999999
});
}).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
});
}).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'
});
}).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'
});
}).to.throw('Invalid drive size: 999999999');
}); });
}); });
@ -126,14 +195,74 @@ describe('Browser: SelectionState', function() {
describe('given an image', function() { describe('given an image', function() {
beforeEach(function() { beforeEach(function() {
SelectionStateModel.setImage('foo.img'); SelectionStateModel.setImage({
path: 'foo.img',
size: 999999999
});
}); });
describe('.getImage()', function() { describe('.isDriveLargeEnough()', function() {
it('should return the image', function() { it('should return true if the drive size is greater than the image size', function() {
const image = SelectionStateModel.getImage(); const result = SelectionStateModel.isDriveLargeEnough({
m.chai.expect(image).to.equal('foo.img'); device: '/dev/disk1',
name: 'USB Drive',
size: 99999999999999
});
m.chai.expect(result).to.be.true;
});
it('should return true if the drive size is equal to the image size', function() {
const result = SelectionStateModel.isDriveLargeEnough({
device: '/dev/disk1',
name: 'USB Drive',
size: 999999999
});
m.chai.expect(result).to.be.true;
});
it('should return false if the drive size is less than the image size', function() {
const result = SelectionStateModel.isDriveLargeEnough({
device: '/dev/disk1',
name: 'USB Drive',
size: 999999998
});
m.chai.expect(result).to.be.false;
});
});
describe('.setDrive()', function() {
it('should throw if drive is no large enough', function() {
m.chai.expect(function() {
SelectionStateModel.setDrive({
device: '/dev/disk1',
name: 'USB Drive',
size: 999999998
});
}).to.throw('The drive is not large enough');
});
});
describe('.getImagePath()', function() {
it('should return the image path', function() {
const imagePath = SelectionStateModel.getImagePath();
m.chai.expect(imagePath).to.equal('foo.img');
});
});
describe('.getImageSize()', function() {
it('should return the image size', function() {
const imageSize = SelectionStateModel.getImageSize();
m.chai.expect(imageSize).to.equal(999999999);
}); });
}); });
@ -150,9 +279,15 @@ describe('Browser: SelectionState', function() {
describe('.setImage()', function() { describe('.setImage()', function() {
it('should override the image', function() { it('should override the image', function() {
SelectionStateModel.setImage('bar.img'); SelectionStateModel.setImage({
const image = SelectionStateModel.getImage(); path: 'bar.img',
m.chai.expect(image).to.equal('bar.img'); size: 999999999
});
const imagePath = SelectionStateModel.getImagePath();
m.chai.expect(imagePath).to.equal('bar.img');
const imageSize = SelectionStateModel.getImageSize();
m.chai.expect(imageSize).to.equal(999999999);
}); });
}); });
@ -161,8 +296,11 @@ describe('Browser: SelectionState', function() {
it('should clear the image', function() { it('should clear the image', function() {
SelectionStateModel.removeImage(); SelectionStateModel.removeImage();
const image = SelectionStateModel.getImage();
m.chai.expect(image).to.be.undefined; const imagePath = SelectionStateModel.getImagePath();
m.chai.expect(imagePath).to.be.undefined;
const imageSize = SelectionStateModel.getImageSize();
m.chai.expect(imageSize).to.be.undefined;
}); });
}); });
@ -171,12 +309,66 @@ describe('Browser: SelectionState', function() {
describe('given no image', function() { describe('given no image', function() {
describe('.isDriveLargeEnough()', function() {
it('should return true', function() {
const result = SelectionStateModel.isDriveLargeEnough({
device: '/dev/disk1',
name: 'USB Drive',
size: 1
});
m.chai.expect(result).to.be.true;
});
});
describe('.setImage()', function() { describe('.setImage()', function() {
it('should be able to set an image', function() { it('should be able to set an image', function() {
SelectionStateModel.setImage('foo.img'); SelectionStateModel.setImage({
const image = SelectionStateModel.getImage(); path: 'foo.img',
m.chai.expect(image).to.equal('foo.img'); size: 999999999
});
const imagePath = SelectionStateModel.getImagePath();
m.chai.expect(imagePath).to.equal('foo.img');
const imageSize = SelectionStateModel.getImageSize();
m.chai.expect(imageSize).to.equal(999999999);
});
it('should throw if no path', function() {
m.chai.expect(function() {
SelectionStateModel.setImage({
size: 999999999
});
}).to.throw('Missing image path');
});
it('should throw if path is not a string', function() {
m.chai.expect(function() {
SelectionStateModel.setImage({
path: 123,
size: 999999999
});
}).to.throw('Invalid image path: 123');
});
it('should throw if no size', function() {
m.chai.expect(function() {
SelectionStateModel.setImage({
path: 'foo.img'
});
}).to.throw('Missing image size');
});
it('should throw if size is not a number', function() {
m.chai.expect(function() {
SelectionStateModel.setImage({
path: 'foo.img',
size: '999999999'
});
}).to.throw('Invalid image size: 999999999');
}); });
}); });
@ -186,8 +378,16 @@ describe('Browser: SelectionState', function() {
describe('given a drive', function() { describe('given a drive', function() {
beforeEach(function() { beforeEach(function() {
SelectionStateModel.setDrive('/dev/disk2'); SelectionStateModel.setDrive({
SelectionStateModel.setImage('foo.img'); device: '/dev/disk1',
name: 'USB Drive',
size: 999999999
});
SelectionStateModel.setImage({
path: 'foo.img',
size: 999999999
});
}); });
describe('.clear()', function() { describe('.clear()', function() {
@ -217,9 +417,14 @@ describe('Browser: SelectionState', function() {
m.chai.expect(drive).to.be.undefined; m.chai.expect(drive).to.be.undefined;
}); });
it('getImage() should return the image', function() { it('getImagePath() should return the image path', function() {
const image = SelectionStateModel.getImage(); const imagePath = SelectionStateModel.getImagePath();
m.chai.expect(image).to.equal('foo.img'); m.chai.expect(imagePath).to.equal('foo.img');
});
it('getImageSize() should return the image size', function() {
const imageSize = SelectionStateModel.getImageSize();
m.chai.expect(imageSize).to.equal(999999999);
}); });
it('hasDrive() should return false', function() { it('hasDrive() should return false', function() {
@ -244,7 +449,7 @@ describe('Browser: SelectionState', function() {
SelectionStateModel.setDrive({ SelectionStateModel.setDrive({
device: '/dev/sdb', device: '/dev/sdb',
description: 'DataTraveler 2.0', description: 'DataTraveler 2.0',
size: '7.3G', size: 999999999,
mountpoint: '/media/UNTITLED', mountpoint: '/media/UNTITLED',
name: '/dev/sdb', name: '/dev/sdb',
system: false system: false
@ -263,7 +468,7 @@ describe('Browser: SelectionState', function() {
m.chai.expect(SelectionStateModel.isCurrentDrive({ m.chai.expect(SelectionStateModel.isCurrentDrive({
device: '/dev/sdb', device: '/dev/sdb',
description: 'DataTraveler 2.0', description: 'DataTraveler 2.0',
size: '7.3G', size: 999999999,
mountpoint: '/media/UNTITLED', mountpoint: '/media/UNTITLED',
name: '/dev/sdb', name: '/dev/sdb',
system: false system: false
@ -274,7 +479,7 @@ describe('Browser: SelectionState', function() {
m.chai.expect(SelectionStateModel.isCurrentDrive({ m.chai.expect(SelectionStateModel.isCurrentDrive({
device: '/dev/sdb', device: '/dev/sdb',
description: 'DataTraveler 2.0', description: 'DataTraveler 2.0',
size: '7.3G', size: 999999999,
mountpoint: '/media/UNTITLED', mountpoint: '/media/UNTITLED',
name: '/dev/sdb', name: '/dev/sdb',
system: false, system: false,
@ -286,7 +491,7 @@ describe('Browser: SelectionState', function() {
m.chai.expect(SelectionStateModel.isCurrentDrive({ m.chai.expect(SelectionStateModel.isCurrentDrive({
device: '/dev/sdc', device: '/dev/sdc',
description: 'DataTraveler 2.0', description: 'DataTraveler 2.0',
size: '7.3G', size: 999999999,
mountpoint: '/media/UNTITLED', mountpoint: '/media/UNTITLED',
name: '/dev/sdb', name: '/dev/sdb',
system: false system: false
@ -297,7 +502,7 @@ describe('Browser: SelectionState', function() {
m.chai.expect(SelectionStateModel.isCurrentDrive({ m.chai.expect(SelectionStateModel.isCurrentDrive({
device: '/dev/sdb', device: '/dev/sdb',
description: 'DataTraveler 3.0', description: 'DataTraveler 3.0',
size: '7.3G', size: 999999999,
mountpoint: '/media/UNTITLED', mountpoint: '/media/UNTITLED',
name: '/dev/sdb', name: '/dev/sdb',
system: false system: false
@ -325,7 +530,7 @@ describe('Browser: SelectionState', function() {
m.chai.expect(SelectionStateModel.isCurrentDrive({ m.chai.expect(SelectionStateModel.isCurrentDrive({
device: '/dev/sdb', device: '/dev/sdb',
description: 'DataTraveler 2.0', description: 'DataTraveler 2.0',
size: '7.3G', size: 999999999,
mountpoint: '/media/UNTITLED', mountpoint: '/media/UNTITLED',
name: '/dev/sdb', name: '/dev/sdb',
system: false system: false
@ -345,7 +550,7 @@ describe('Browser: SelectionState', function() {
this.drive = { this.drive = {
device: '/dev/sdb', device: '/dev/sdb',
description: 'DataTraveler 2.0', description: 'DataTraveler 2.0',
size: '7.3G', size: 999999999,
mountpoint: '/media/UNTITLED', mountpoint: '/media/UNTITLED',
name: '/dev/sdb', name: '/dev/sdb',
system: false system: false
@ -364,7 +569,7 @@ describe('Browser: SelectionState', function() {
const drive = { const drive = {
device: '/dev/disk2', device: '/dev/disk2',
name: 'USB Drive', name: 'USB Drive',
size: '16GB' size: 999999999
}; };
m.chai.expect(SelectionStateModel.getDrive()).to.deep.equal(this.drive); m.chai.expect(SelectionStateModel.getDrive()).to.deep.equal(this.drive);
@ -385,7 +590,7 @@ describe('Browser: SelectionState', function() {
const drive = { const drive = {
device: '/dev/disk2', device: '/dev/disk2',
name: 'USB Drive', name: 'USB Drive',
size: '16GB' size: 999999999
}; };
m.chai.expect(SelectionStateModel.hasDrive()).to.be.false; m.chai.expect(SelectionStateModel.hasDrive()).to.be.false;