From 7b791d622f6654557547f03edd123b5cdb9f49e0 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Thu, 24 Aug 2017 19:56:12 -0400 Subject: [PATCH] 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 --- lib/shared/drive-constraints.js | 43 ++- tests/shared/drive-constraints.spec.js | 351 +++++++++++++++++++------ 2 files changed, 305 insertions(+), 89 deletions(-) diff --git a/lib/shared/drive-constraints.js b/lib/shared/drive-constraints.js index f667ff64..e32dacc2 100644 --- a/lib/shared/drive-constraints.js +++ b/lib/shared/drive-constraints.js @@ -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 diff --git a/tests/shared/drive-constraints.spec.js b/tests/shared/drive-constraints.spec.js index 0059f593..6deeb884 100644 --- a/tests/shared/drive-constraints.spec.js +++ b/tests/shared/drive-constraints.spec.js @@ -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')