feat(GUI): support new "pending" drive flag (#1709)

We recently added a "pending" flag to all drives that represents whether
the drive is ready for selection or not. This flag will be used by the
"usbboot" flashing adaptor, which will emit various "pending" USB
devices while it converts them to block devices that can actually be
flashed.

In terms of the GUI, the following visible changes were made:

- Drives with a `pending: true` property will be disabled in the drive
  selector window
- Drives with a `pending: true` property have a "PENDING" red badge

See: https://github.com/resin-io/etcher/pull/1707
See: https://github.com/resin-io/etcher/pull/1686
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
This commit is contained in:
Juan Cruz Viotti 2017-08-24 19:56:12 -04:00 committed by GitHub
parent f3c8ec496a
commit 7b791d622f
2 changed files with 305 additions and 89 deletions

View File

@ -168,7 +168,29 @@ exports.isDriveLargeEnough = (drive, image) => {
}
/**
* @summary Check if a drive is is valid, i.e. not locked and large enough for an image
* @summary Check if a drive is pending (i.e. not ready for selection)
* @function
* @public
*
* @param {Object} drive - drive
* @returns {Boolean} whether the drive is pending
*
* @example
* if (constraints.isDrivePending({
* device: '/dev/disk2',
* name: 'My Drive',
* size: 1000000000,
* pending: true
* })) {
* console.log('The drive is pending');
* }
*/
exports.isDrivePending = (drive) => {
return _.get(drive, [ 'pending' ], false)
}
/**
* @summary Check if a drive is valid, i.e. not locked and large enough for an image
* @function
* @public
*
@ -194,7 +216,8 @@ exports.isDriveValid = (drive, image) => {
return _.every([
!this.isDriveLocked(drive),
this.isDriveLargeEnough(drive, image),
!this.isSourceDrive(drive, image)
!this.isSourceDrive(drive, image),
!this.isDrivePending(drive)
])
}
@ -278,6 +301,15 @@ exports.COMPATIBILITY_STATUS_MESSAGES = {
*/
SYSTEM: 'System Drive',
/**
* @property {String} PENDING
* @memberof COMPATIBILITY_STATUS_MESSAGES
*
* @description
* The drive is not fully loaded.
*/
PENDING: 'Pending',
/**
* @property {String} CONTAINS_IMAGE
* @memberof COMPATIBILITY_STATUS_MESSAGES
@ -342,7 +374,12 @@ exports.getDriveImageCompatibilityStatuses = (drive, image) => {
const statusList = []
// Mind the order of the if-statements if you modify.
if (exports.isSourceDrive(drive, image)) {
if (exports.isDrivePending(drive)) {
statusList.push({
type: exports.COMPATIBILITY_STATUS_TYPES.ERROR,
message: exports.COMPATIBILITY_STATUS_MESSAGES.PENDING
})
} else if (exports.isSourceDrive(drive, image)) {
statusList.push({
type: exports.COMPATIBILITY_STATUS_TYPES.ERROR,
message: exports.COMPATIBILITY_STATUS_MESSAGES.CONTAINS_IMAGE

View File

@ -527,6 +527,43 @@ describe('Shared: DriveConstraints', function () {
})
})
describe('.isDrivePending()', function () {
it('should return true if the drive is pending', function () {
const result = constraints.isDrivePending({
device: '/dev/disk1',
name: 'USB Drive',
size: 1000000000,
protected: false,
pending: true
})
m.chai.expect(result).to.be.true
})
it('should return false if the drive is not pending', function () {
const result = constraints.isDrivePending({
device: '/dev/disk1',
name: 'USB Drive',
size: 1000000000,
protected: false,
pending: false
})
m.chai.expect(result).to.be.false
})
it('should return false if "pending" is undefined', function () {
const result = constraints.isDrivePending({
device: '/dev/disk1',
name: 'USB Drive',
size: 1000000000,
protected: false
})
m.chai.expect(result).to.be.false
})
})
describe('.isDriveSizeRecommended()', function () {
it('should return true if the drive size is greater than the recommended size ', function () {
const result = constraints.isDriveSizeRecommended({
@ -639,56 +676,120 @@ describe('Shared: DriveConstraints', function () {
this.drive.protected = true
})
it('should return false if the drive is not large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
describe('given the drive is pending', function () {
beforeEach(function () {
this.drive.pending = true
})
it('should return false if the drive is not large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
}
}
}
})).to.be.false
})).to.be.false
})
it('should return false if the drive is not large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
}
}
})).to.be.false
})
it('should return false if the drive is large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
}
}
})).to.be.false
})
it('should return false if the drive is large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
}
}
})).to.be.false
})
})
it('should return false if the drive is not large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
}
}
})).to.be.false
})
describe('given the drive is not pending', function () {
beforeEach(function () {
this.drive.pending = false
})
it('should return false if the drive is large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
it('should return false if the drive is not large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
}
}
}
})).to.be.false
})
})).to.be.false
})
it('should return false if the drive is large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
it('should return false if the drive is not large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
}
}
}
})).to.be.false
})).to.be.false
})
it('should return false if the drive is large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
}
}
})).to.be.false
})
it('should return false if the drive is large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
}
}
})).to.be.false
})
})
})
@ -697,56 +798,120 @@ describe('Shared: DriveConstraints', function () {
this.drive.protected = false
})
it('should return false if the drive is not large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
describe('given the drive is pending', function () {
beforeEach(function () {
this.drive.pending = true
})
it('should return false if the drive is not large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
}
}
}
})).to.be.false
})).to.be.false
})
it('should return false if the drive is not large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
}
}
})).to.be.false
})
it('should return false if the drive is large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
}
}
})).to.be.false
})
it('should return false if the drive is large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
}
}
})).to.be.false
})
})
it('should return false if the drive is not large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
}
}
})).to.be.false
})
describe('given the drive is not pending', function () {
beforeEach(function () {
this.drive.pending = false
})
it('should return false if the drive is large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
it('should return false if the drive is not large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
}
}
}
})).to.be.false
})
})).to.be.false
})
it('should return true if the drive is large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
it('should return false if the drive is not large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 5000000000,
final: {
estimation: false,
value: 5000000000
}
}
}
})).to.be.true
})).to.be.false
})
it('should return false if the drive is large enough and is a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.join(this.mountpoint, 'rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
}
}
})).to.be.false
})
it('should return true if the drive is large enough and is not a source drive', function () {
m.chai.expect(constraints.isDriveValid(this.drive, {
path: path.resolve(this.mountpoint, '../bar/rpi.img'),
size: {
original: 2000000000,
final: {
estimation: false,
value: 2000000000
}
}
})).to.be.true
})
})
})
})
@ -766,6 +931,7 @@ describe('Shared: DriveConstraints', function () {
name: 'My Drive',
protected: false,
system: false,
pending: false,
mountpoints: [
{
path: this.mountpoint
@ -809,6 +975,19 @@ describe('Shared: DriveConstraints', function () {
})
})
describe('given the drive is pending', () => {
it('should return an empty list', function () {
this.drive.pending = true
const result = constraints.getDriveImageCompatibilityStatuses(this.drive, {
path: '/mnt/disk2/rpi.img',
size: 1000000000
})
const expectedTuples = [ [ 'ERROR', 'PENDING' ] ]
expectStatusTypesAndMessagesToBe(result, expectedTuples)
})
})
describe('given the drive contains the image', () => {
it('should return the contains-image error', function () {
this.image.path = path.join(this.mountpoint, 'rpi.img')