mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-23 11:16:39 +00:00
Show a "Locked" label if the drive is write-protected (#475)
* Implement SelectionStateModel.isDriveLocked() Notice we also increase the maximum number of lines JSCS check, since the `SelectionStateModel` test suite already met that limit. Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com> * Show a "Locked" label if the drive is write-protected Fixes: https://github.com/resin-io/etcher/issues/458 Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This commit is contained in:
parent
51b6de4634
commit
fbe6fe1142
2
.jscsrc
2
.jscsrc
@ -131,7 +131,7 @@
|
||||
"!=="
|
||||
],
|
||||
"maximumLineLength": 130,
|
||||
"maximumNumberOfLines": 500,
|
||||
"maximumNumberOfLines": 1000,
|
||||
"requireAlignedMultilineParams": true,
|
||||
"requireAlignedObjectValues": false,
|
||||
"requireAnonymousFunctions": true,
|
||||
|
@ -6,20 +6,32 @@
|
||||
<div class="component-drive-selector-body modal-body">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" ng-repeat="drive in modal.drives.getDrives()"
|
||||
ng-disabled="!modal.state.isDriveLargeEnough(drive)"
|
||||
ng-click="modal.state.isDriveLargeEnough(drive) && modal.state.toggleSetDrive(drive)">
|
||||
ng-disabled="!modal.state.isDriveValid(drive)"
|
||||
ng-click="modal.state.isDriveValid(drive) && modal.state.toggleSetDrive(drive)">
|
||||
<div>
|
||||
<header class="list-group-item-header">
|
||||
|
||||
<!-- There can be a case where the device it not large enough, and it's also locked. -->
|
||||
<!-- Since in this case both labels will be displayed, we chose to only show the -->
|
||||
<!-- "not large enough label", since from the point of view of the user, the locked -->
|
||||
<!-- state is irrelevent if the drive is not large enough anyway. -->
|
||||
|
||||
<span class="label label-danger"
|
||||
ng-hide="modal.state.isDriveLargeEnough(drive)">
|
||||
<i class="glyphicon glyphicon-resize-small"></i>
|
||||
TOO SMALL FOR IMAGE</span>
|
||||
|
||||
<span class="label label-danger"
|
||||
ng-show="modal.state.isDriveLocked(drive) && modal.state.isDriveLargeEnough(drive)">
|
||||
<i class="glyphicon glyphicon-lock"></i>
|
||||
LOCKED</span>
|
||||
|
||||
</header>
|
||||
<h4 class="list-group-item-heading">{{ drive.description }} - {{ drive.size | gigabyte | number:1 }} GB</h4>
|
||||
<p class="list-group-item-text">{{ drive.name }}</p>
|
||||
</div>
|
||||
<span class="tick tick--success"
|
||||
ng-show="modal.state.isDriveLargeEnough(drive)"
|
||||
ng-show="modal.state.isDriveValid(drive)"
|
||||
ng-disabled="!modal.state.isCurrentDrive(drive)"></span>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -83,6 +83,10 @@ SelectionStateModel.service('SelectionStateModel', function() {
|
||||
throw new Error(`Invalid drive size: ${drive.size}`);
|
||||
}
|
||||
|
||||
if (!_.isBoolean(drive.protected)) {
|
||||
throw new Error(`Invalid drive protected state: ${drive.protected}`);
|
||||
}
|
||||
|
||||
if (!self.isDriveLargeEnough(drive)) {
|
||||
throw new Error('The drive is not large enough');
|
||||
}
|
||||
@ -124,6 +128,62 @@ SelectionStateModel.service('SelectionStateModel', function() {
|
||||
return (self.getImageSize() || 0) <= drive.size;
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Check if a drive is locked
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @description
|
||||
* This usually points out a locked SD Card.
|
||||
*
|
||||
* @param {Object} drive - drive
|
||||
* @returns {Boolean} whether the drive is locked
|
||||
*
|
||||
* @example
|
||||
* if (SelectionStateModel.isDriveLocked({
|
||||
* device: '/dev/disk2',
|
||||
* name: 'My Drive',
|
||||
* size: 123456789,
|
||||
* protected: true
|
||||
* })) {
|
||||
* console.log('This drive is locked (e.g: write-protected)');
|
||||
* }
|
||||
*/
|
||||
this.isDriveLocked = function(drive) {
|
||||
return _.get(drive, 'protected', false);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Check if a drive is valid
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @description
|
||||
* This function is a facade to:
|
||||
*
|
||||
* - `SelectionStateModel.isDriveLargeEnough()`
|
||||
* - `SelectionStateModel.isDriveLocked()`
|
||||
*
|
||||
* @param {Object} drive - drive
|
||||
* @returns {Boolean} whether the drive is valid
|
||||
*
|
||||
* @example
|
||||
* if (SelectionStateModel.isDriveValid({
|
||||
* device: '/dev/disk2',
|
||||
* name: 'My Drive',
|
||||
* size: 123456789,
|
||||
* protected: true
|
||||
* })) {
|
||||
* console.log('This drive is valid!');
|
||||
* }
|
||||
*/
|
||||
this.isDriveValid = function(drive) {
|
||||
return _.every([
|
||||
self.isDriveLargeEnough(drive),
|
||||
!self.isDriveLocked(drive)
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Toggle set drive
|
||||
* @function
|
||||
|
@ -57,7 +57,7 @@
|
||||
"bluebird": "^3.0.5",
|
||||
"bootstrap-sass": "^3.3.5",
|
||||
"chalk": "^1.1.3",
|
||||
"drivelist": "^3.1.2",
|
||||
"drivelist": "^3.2.0",
|
||||
"electron-is-running-in-asar": "^1.0.0",
|
||||
"etcher-image-stream": "^2.0.0",
|
||||
"etcher-image-write": "^5.0.0",
|
||||
|
@ -49,13 +49,76 @@ describe('Browser: SelectionState', function() {
|
||||
|
||||
});
|
||||
|
||||
describe('.isDriveLocked()', function() {
|
||||
|
||||
it('should return true if the drive is protected', function() {
|
||||
const result = SelectionStateModel.isDriveLocked({
|
||||
device: '/dev/disk2',
|
||||
name: 'USB Drive',
|
||||
size: 999999999,
|
||||
protected: true
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.true;
|
||||
});
|
||||
|
||||
it('should return false if the drive is not protected', function() {
|
||||
const result = SelectionStateModel.isDriveLocked({
|
||||
device: '/dev/disk2',
|
||||
name: 'USB Drive',
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.false;
|
||||
});
|
||||
|
||||
it('should return false if we don\'t know if the drive is protected', function() {
|
||||
const result = SelectionStateModel.isDriveLocked({
|
||||
device: '/dev/disk2',
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('.isDriveValid()', function() {
|
||||
|
||||
it('should return true if the drive is not locked', function() {
|
||||
const result = SelectionStateModel.isDriveValid({
|
||||
device: '/dev/disk2',
|
||||
name: 'USB Drive',
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.true;
|
||||
});
|
||||
|
||||
it('should return false if the drive is locked', function() {
|
||||
const result = SelectionStateModel.isDriveValid({
|
||||
device: '/dev/disk2',
|
||||
name: 'USB Drive',
|
||||
size: 999999999,
|
||||
protected: true
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('given a drive', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
SelectionStateModel.setDrive({
|
||||
device: '/dev/disk2',
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
});
|
||||
|
||||
@ -66,7 +129,8 @@ describe('Browser: SelectionState', function() {
|
||||
m.chai.expect(drive).to.deep.equal({
|
||||
device: '/dev/disk2',
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
});
|
||||
|
||||
@ -87,14 +151,16 @@ describe('Browser: SelectionState', function() {
|
||||
SelectionStateModel.setDrive({
|
||||
device: '/dev/disk5',
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
|
||||
const drive = SelectionStateModel.getDrive();
|
||||
m.chai.expect(drive).to.deep.equal({
|
||||
device: '/dev/disk5',
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
});
|
||||
|
||||
@ -120,14 +186,16 @@ describe('Browser: SelectionState', function() {
|
||||
SelectionStateModel.setDrive({
|
||||
device: '/dev/disk5',
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
|
||||
const drive = SelectionStateModel.getDrive();
|
||||
m.chai.expect(drive).to.deep.equal({
|
||||
device: '/dev/disk5',
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
});
|
||||
|
||||
@ -135,7 +203,8 @@ describe('Browser: SelectionState', function() {
|
||||
m.chai.expect(function() {
|
||||
SelectionStateModel.setDrive({
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
}).to.throw('Missing drive device');
|
||||
});
|
||||
@ -145,7 +214,8 @@ describe('Browser: SelectionState', function() {
|
||||
SelectionStateModel.setDrive({
|
||||
device: 123,
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
}).to.throw('Invalid drive device: 123');
|
||||
});
|
||||
@ -154,7 +224,8 @@ describe('Browser: SelectionState', function() {
|
||||
m.chai.expect(function() {
|
||||
SelectionStateModel.setDrive({
|
||||
device: '/dev/disk2',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
}).to.throw('Missing drive name');
|
||||
});
|
||||
@ -164,7 +235,8 @@ describe('Browser: SelectionState', function() {
|
||||
SelectionStateModel.setDrive({
|
||||
device: '/dev/disk2',
|
||||
name: 123,
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
}).to.throw('Invalid drive name: 123');
|
||||
});
|
||||
@ -173,7 +245,8 @@ describe('Browser: SelectionState', function() {
|
||||
m.chai.expect(function() {
|
||||
SelectionStateModel.setDrive({
|
||||
device: '/dev/disk2',
|
||||
name: 'USB Drive'
|
||||
name: 'USB Drive',
|
||||
protected: false
|
||||
});
|
||||
}).to.throw('Missing drive size');
|
||||
});
|
||||
@ -183,11 +256,33 @@ describe('Browser: SelectionState', function() {
|
||||
SelectionStateModel.setDrive({
|
||||
device: '/dev/disk2',
|
||||
name: 'USB Drive',
|
||||
size: '999999999'
|
||||
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');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@ -207,7 +302,8 @@ describe('Browser: SelectionState', function() {
|
||||
const result = SelectionStateModel.isDriveLargeEnough({
|
||||
device: '/dev/disk1',
|
||||
name: 'USB Drive',
|
||||
size: 99999999999999
|
||||
size: 99999999999999,
|
||||
protected: false
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.true;
|
||||
@ -217,7 +313,8 @@ describe('Browser: SelectionState', function() {
|
||||
const result = SelectionStateModel.isDriveLargeEnough({
|
||||
device: '/dev/disk1',
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.true;
|
||||
@ -227,7 +324,56 @@ describe('Browser: SelectionState', function() {
|
||||
const result = SelectionStateModel.isDriveLargeEnough({
|
||||
device: '/dev/disk1',
|
||||
name: 'USB Drive',
|
||||
size: 999999998
|
||||
size: 999999998,
|
||||
protected: false
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('.isDriveValid()', function() {
|
||||
|
||||
it('should return true if the drive is large enough and it is not locked', function() {
|
||||
const result = SelectionStateModel.isDriveValid({
|
||||
device: '/dev/disk1',
|
||||
name: 'USB Drive',
|
||||
size: 99999999999999,
|
||||
protected: false
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.true;
|
||||
});
|
||||
|
||||
it('should return false if the drive is large enough but it is locked', function() {
|
||||
const result = SelectionStateModel.isDriveValid({
|
||||
device: '/dev/disk1',
|
||||
name: 'USB Drive',
|
||||
size: 99999999999999,
|
||||
protected: true
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.false;
|
||||
});
|
||||
|
||||
it('should return false if the drive is not large enough and it is not locked', function() {
|
||||
const result = SelectionStateModel.isDriveValid({
|
||||
device: '/dev/disk1',
|
||||
name: 'USB Drive',
|
||||
size: 1,
|
||||
protected: false
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.false;
|
||||
});
|
||||
|
||||
it('should return false if the drive is not large enough and it is locked', function() {
|
||||
const result = SelectionStateModel.isDriveValid({
|
||||
device: '/dev/disk1',
|
||||
name: 'USB Drive',
|
||||
size: 1,
|
||||
protected: true
|
||||
});
|
||||
|
||||
m.chai.expect(result).to.be.false;
|
||||
@ -242,7 +388,8 @@ describe('Browser: SelectionState', function() {
|
||||
SelectionStateModel.setDrive({
|
||||
device: '/dev/disk1',
|
||||
name: 'USB Drive',
|
||||
size: 999999998
|
||||
size: 999999998,
|
||||
protected: false
|
||||
});
|
||||
}).to.throw('The drive is not large enough');
|
||||
});
|
||||
@ -381,7 +528,8 @@ describe('Browser: SelectionState', function() {
|
||||
SelectionStateModel.setDrive({
|
||||
device: '/dev/disk1',
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
});
|
||||
|
||||
SelectionStateModel.setImage({
|
||||
@ -452,7 +600,8 @@ describe('Browser: SelectionState', function() {
|
||||
size: 999999999,
|
||||
mountpoint: '/media/UNTITLED',
|
||||
name: '/dev/sdb',
|
||||
system: false
|
||||
system: false,
|
||||
protected: false
|
||||
});
|
||||
});
|
||||
|
||||
@ -471,7 +620,8 @@ describe('Browser: SelectionState', function() {
|
||||
size: 999999999,
|
||||
mountpoint: '/media/UNTITLED',
|
||||
name: '/dev/sdb',
|
||||
system: false
|
||||
system: false,
|
||||
protected: false
|
||||
})).to.be.true;
|
||||
});
|
||||
|
||||
@ -483,7 +633,8 @@ describe('Browser: SelectionState', function() {
|
||||
mountpoint: '/media/UNTITLED',
|
||||
name: '/dev/sdb',
|
||||
system: false,
|
||||
$$hashKey: 1234
|
||||
$$hashKey: 1234,
|
||||
protected: false
|
||||
})).to.be.true;
|
||||
});
|
||||
|
||||
@ -494,7 +645,8 @@ describe('Browser: SelectionState', function() {
|
||||
size: 999999999,
|
||||
mountpoint: '/media/UNTITLED',
|
||||
name: '/dev/sdb',
|
||||
system: false
|
||||
system: false,
|
||||
protected: false
|
||||
})).to.be.false;
|
||||
});
|
||||
|
||||
@ -505,7 +657,8 @@ describe('Browser: SelectionState', function() {
|
||||
size: 999999999,
|
||||
mountpoint: '/media/UNTITLED',
|
||||
name: '/dev/sdb',
|
||||
system: false
|
||||
system: false,
|
||||
protected: false
|
||||
})).to.be.true;
|
||||
});
|
||||
|
||||
@ -533,7 +686,8 @@ describe('Browser: SelectionState', function() {
|
||||
size: 999999999,
|
||||
mountpoint: '/media/UNTITLED',
|
||||
name: '/dev/sdb',
|
||||
system: false
|
||||
system: false,
|
||||
protected: false
|
||||
})).to.be.false;
|
||||
|
||||
});
|
||||
@ -553,7 +707,8 @@ describe('Browser: SelectionState', function() {
|
||||
size: 999999999,
|
||||
mountpoint: '/media/UNTITLED',
|
||||
name: '/dev/sdb',
|
||||
system: false
|
||||
system: false,
|
||||
protected: false
|
||||
};
|
||||
|
||||
SelectionStateModel.setDrive(this.drive);
|
||||
@ -569,7 +724,8 @@ describe('Browser: SelectionState', function() {
|
||||
const drive = {
|
||||
device: '/dev/disk2',
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
};
|
||||
|
||||
m.chai.expect(SelectionStateModel.getDrive()).to.deep.equal(this.drive);
|
||||
@ -590,7 +746,8 @@ describe('Browser: SelectionState', function() {
|
||||
const drive = {
|
||||
device: '/dev/disk2',
|
||||
name: 'USB Drive',
|
||||
size: 999999999
|
||||
size: 999999999,
|
||||
protected: false
|
||||
};
|
||||
|
||||
m.chai.expect(SelectionStateModel.hasDrive()).to.be.false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user