mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-22 18:56:31 +00:00
feat: add drive multi-selection in store (#1736)
We lay the foundation for multi-selecting drives by implementing it into the `store` and relevant modules interacting with the `store`. Change-Type: patch Changelog-Entry: Add drive multi-selection to the store.
This commit is contained in:
parent
ee93013220
commit
207c2ef5b6
@ -118,6 +118,7 @@ module.exports = function (
|
|||||||
previouslySelected: selectionState.isCurrentDrive(drive.device)
|
previouslySelected: selectionState.isCurrentDrive(drive.device)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
selectionState.deselectOtherDrives(drive.device)
|
||||||
selectionState.toggleDrive(drive.device)
|
selectionState.toggleDrive(drive.device)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -132,7 +133,7 @@ module.exports = function (
|
|||||||
* DriveSelectorController.closeModal();
|
* DriveSelectorController.closeModal();
|
||||||
*/
|
*/
|
||||||
this.closeModal = () => {
|
this.closeModal = () => {
|
||||||
const selectedDrive = selectionState.getDrive()
|
const selectedDrive = selectionState.getCurrentDrive()
|
||||||
|
|
||||||
// Sanity check to cover the case where a drive is selected,
|
// Sanity check to cover the case where a drive is selected,
|
||||||
// the drive is then unplugged from the computer and the modal
|
// the drive is then unplugged from the computer and the modal
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<span class="list-group-item-section tick tick--success"
|
<span class="list-group-item-section tick tick--success"
|
||||||
ng-show="modal.constraints.isDriveValid(drive, modal.state.getImage())"
|
ng-show="modal.constraints.isDriveValid(drive, modal.state.getImage())"
|
||||||
ng-disabled="!modal.state.isCurrentDrive(drive.device)"></span>
|
ng-disabled="!modal.state.isDriveSelected(drive.device)"></span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item"
|
<li class="list-group-item"
|
||||||
ng-show="!modal.drives.hasAvailableDrives()">
|
ng-show="!modal.drives.hasAvailableDrives()">
|
||||||
|
@ -51,7 +51,7 @@ module.exports = function ($state) {
|
|||||||
if (!options.preserveImage) {
|
if (!options.preserveImage) {
|
||||||
selectionState.deselectImage()
|
selectionState.deselectImage()
|
||||||
}
|
}
|
||||||
selectionState.deselectDrive()
|
selectionState.deselectAllDrives()
|
||||||
analytics.logEvent('Restart', options)
|
analytics.logEvent('Restart', options)
|
||||||
$state.go('main')
|
$state.go('main')
|
||||||
}
|
}
|
||||||
|
@ -66,11 +66,11 @@
|
|||||||
'text-disabled': main.shouldDriveStepBeDisabled()
|
'text-disabled': main.shouldDriveStepBeDisabled()
|
||||||
}">
|
}">
|
||||||
<span class="drive-step step-name"
|
<span class="drive-step step-name"
|
||||||
uib-tooltip="{{ main.selection.getDrive().description }} ({{ main.selection.getDrive().displayName }})">
|
uib-tooltip="{{ main.selection.getCurrentDrive().description }} ({{ main.selection.getCurrentDrive().displayName }})">
|
||||||
<!-- middleEllipses errors on undefined, therefore fallback to empty string -->
|
<!-- middleEllipses errors on undefined, therefore fallback to empty string -->
|
||||||
{{ (main.selection.getDrive().description || "") | middleEllipses:11 }}
|
{{ (main.selection.getCurrentDrive().description || "") | middleEllipses:11 }}
|
||||||
</span>
|
</span>
|
||||||
<span class="step-drive step-size">{{ main.selection.getDrive().size | closestUnit }}</span>
|
<span class="step-drive step-size">{{ main.selection.getCurrentDrive().size | closestUnit }}</span>
|
||||||
<span class="step-drive step-warning glyphicon glyphicon-exclamation-sign"
|
<span class="step-drive step-warning glyphicon glyphicon-exclamation-sign"
|
||||||
uib-tooltip="{{ main.constraints.getDriveImageCompatibilityStatuses(main.selection.getDrive(), main.selection.getImage())[0].message }}"
|
uib-tooltip="{{ main.constraints.getDriveImageCompatibilityStatuses(main.selection.getDrive(), main.selection.getImage())[0].message }}"
|
||||||
ng-show="main.constraints.hasDriveImageCompatibilityStatus(main.selection.getDrive(), main.selection.getImage())"></span>
|
ng-show="main.constraints.hasDriveImageCompatibilityStatus(main.selection.getDrive(), main.selection.getImage())"></span>
|
||||||
@ -97,7 +97,7 @@
|
|||||||
percentage="main.state.getFlashState().percentage"
|
percentage="main.state.getFlashState().percentage"
|
||||||
striped="{{ main.state.getFlashState().type == 'check' }}"
|
striped="{{ main.state.getFlashState().type == 'check' }}"
|
||||||
ng-attr-active="{{ main.state.isFlashing() }}"
|
ng-attr-active="{{ main.state.isFlashing() }}"
|
||||||
ng-click="flash.flashImageToDrive(main.selection.getImage(), main.selection.getDrive())"
|
ng-click="flash.flashImageToDrive(main.selection.getImage(), main.selection.getCurrentDrive())"
|
||||||
ng-disabled="main.shouldFlashStepBeDisabled() || main.state.getLastFlashErrorCode()">
|
ng-disabled="main.shouldFlashStepBeDisabled() || main.state.getLastFlashErrorCode()">
|
||||||
<span ng-bind="flash.getProgressButtonLabel()"></span>
|
<span ng-bind="flash.getProgressButtonLabel()"></span>
|
||||||
</progress-button>
|
</progress-button>
|
||||||
|
@ -48,13 +48,41 @@ exports.selectDrive = (driveDevice) => {
|
|||||||
* selectionState.toggleDrive('/dev/disk2');
|
* selectionState.toggleDrive('/dev/disk2');
|
||||||
*/
|
*/
|
||||||
exports.toggleDrive = (driveDevice) => {
|
exports.toggleDrive = (driveDevice) => {
|
||||||
if (exports.isCurrentDrive(driveDevice)) {
|
if (exports.isDriveSelected(driveDevice)) {
|
||||||
exports.deselectDrive()
|
exports.deselectDrive(driveDevice)
|
||||||
} else {
|
} else {
|
||||||
exports.selectDrive(driveDevice)
|
exports.selectDrive(driveDevice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Deselect all other drives and keep the current drive's status
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* This is a temporary function during the transition to multi-writes,
|
||||||
|
* remove this and its uses when multi-selection should become user-facing.
|
||||||
|
*
|
||||||
|
* @param {String} driveDevice - drive device identifier
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* console.log(selectionState.getSelectedDevices())
|
||||||
|
* > [ '/dev/disk1', '/dev/disk2', '/dev/disk3' ]
|
||||||
|
* selectionState.deselectOtherDrives('/dev/disk2')
|
||||||
|
* console.log(selectionState.getSelectedDevices())
|
||||||
|
* > [ '/dev/disk2' ]
|
||||||
|
*/
|
||||||
|
exports.deselectOtherDrives = (driveDevice) => {
|
||||||
|
if (exports.isDriveSelected(driveDevice)) {
|
||||||
|
const otherDevices = _.reject(exports.getSelectedDevices(), _.partial(_.isEqual, driveDevice))
|
||||||
|
_.each(otherDevices, exports.deselectDrive)
|
||||||
|
} else {
|
||||||
|
exports.deselectAllDrives()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Select an image
|
* @summary Select an image
|
||||||
* @function
|
* @function
|
||||||
@ -82,19 +110,38 @@ exports.selectImage = (image) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get drive
|
* @summary Get all selected drives' devices
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @returns {String[]} selected drives' devices
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* for (driveDevice of selectionState.getSelectedDevices()) {
|
||||||
|
* console.log(driveDevice)
|
||||||
|
* }
|
||||||
|
* > '/dev/disk1'
|
||||||
|
* > '/dev/disk2'
|
||||||
|
*/
|
||||||
|
exports.getSelectedDevices = () => {
|
||||||
|
return store.getState().getIn([ 'selection', 'devices' ]).toJS()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Get the head of the list of selected drives
|
||||||
* @function
|
* @function
|
||||||
* @public
|
* @public
|
||||||
*
|
*
|
||||||
* @returns {Object} drive
|
* @returns {Object} drive
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const drive = selectionState.getDrive();
|
* const drive = selectionState.getCurrentDrive();
|
||||||
|
* console.log(drive)
|
||||||
|
* > { device: '/dev/disk1', name: 'Flash drive', ... }
|
||||||
*/
|
*/
|
||||||
exports.getDrive = () => {
|
exports.getCurrentDrive = () => {
|
||||||
return _.find(availableDrives.getDrives(), {
|
const device = _.head(exports.getSelectedDevices())
|
||||||
device: store.getState().getIn([ 'selection', 'drive' ])
|
return _.find(availableDrives.getDrives(), { device })
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -252,7 +299,7 @@ exports.getImageRecommendedDriveSize = () => {
|
|||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
exports.hasDrive = () => {
|
exports.hasDrive = () => {
|
||||||
return Boolean(exports.getDrive())
|
return Boolean(exports.getSelectedDevices().length)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -272,16 +319,22 @@ exports.hasImage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Remove drive
|
* @summary Remove drive from selection
|
||||||
* @function
|
* @function
|
||||||
* @public
|
* @public
|
||||||
*
|
*
|
||||||
|
* @param {String} driveDevice - drive device identifier
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* selectionState.deselectDrive();
|
* selectionState.deselectDrive('/dev/sdc');
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* selectionState.deselectDrive('\\\\.\\PHYSICALDRIVE3');
|
||||||
*/
|
*/
|
||||||
exports.deselectDrive = () => {
|
exports.deselectDrive = (driveDevice) => {
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: store.Actions.DESELECT_DRIVE
|
type: store.Actions.DESELECT_DRIVE,
|
||||||
|
data: driveDevice
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,6 +352,18 @@ exports.deselectImage = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Unselect all drives
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* selectionState.deselectAllDrives()
|
||||||
|
*/
|
||||||
|
exports.deselectAllDrives = () => {
|
||||||
|
_.each(exports.getSelectedDevices(), exports.deselectDrive)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Clear selections
|
* @summary Clear selections
|
||||||
* @function
|
* @function
|
||||||
@ -306,13 +371,10 @@ exports.deselectImage = () => {
|
|||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* selectionState.clear();
|
* selectionState.clear();
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* selectionState.clear({ preserveImage: true });
|
|
||||||
*/
|
*/
|
||||||
exports.clear = () => {
|
exports.clear = () => {
|
||||||
exports.deselectImage()
|
exports.deselectImage()
|
||||||
exports.deselectDrive()
|
exports.deselectAllDrives()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -333,5 +395,29 @@ exports.isCurrentDrive = (driveDevice) => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return driveDevice === _.get(exports.getDrive(), [ 'device' ])
|
return driveDevice === _.get(exports.getCurrentDrive(), [ 'device' ])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Check whether a given device is selected.
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @param {String} driveDevice - drive device identifier
|
||||||
|
* @returns {Boolean}
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const isSelected = selectionState.isDriveSelected('/dev/sdb')
|
||||||
|
*
|
||||||
|
* if (isSelected) {
|
||||||
|
* selectionState.deselectDrive(driveDevice)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
exports.isDriveSelected = (driveDevice) => {
|
||||||
|
if (!driveDevice) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedDriveDevices = exports.getSelectedDevices()
|
||||||
|
return _.includes(selectedDriveDevices, driveDevice)
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,9 @@ const selectImageNoNilFields = [
|
|||||||
*/
|
*/
|
||||||
const DEFAULT_STATE = Immutable.fromJS({
|
const DEFAULT_STATE = Immutable.fromJS({
|
||||||
availableDrives: [],
|
availableDrives: [],
|
||||||
selection: {},
|
selection: {
|
||||||
|
devices: new Immutable.OrderedSet()
|
||||||
|
},
|
||||||
isFlashing: false,
|
isFlashing: false,
|
||||||
flashResults: {},
|
flashResults: {},
|
||||||
flashState: {
|
flashState: {
|
||||||
@ -122,25 +124,20 @@ const ACTIONS = _.fromPairs(_.map([
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Find a drive from the list of available drives
|
* @summary Get available drives from the state
|
||||||
* @function
|
* @function
|
||||||
* @private
|
* @public
|
||||||
*
|
*
|
||||||
* @param {Object} state - application state
|
* @param {Object} state - state object
|
||||||
* @param {String} device - drive device
|
* @returns {Object} new state
|
||||||
* @returns {(Object|Undefined)} drive
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const drive = findDrive(state, '/dev/disk2');
|
* const drives = getAvailableDrives(state)
|
||||||
|
* _.find(drives, { device: '/dev/sda' })
|
||||||
*/
|
*/
|
||||||
const findDrive = (state, device) => {
|
const getAvailableDrives = (state) => {
|
||||||
/* eslint-disable lodash/prefer-lodash-method */
|
// eslint-disable-next-line lodash/prefer-lodash-method
|
||||||
|
return state.get('availableDrives').toJS()
|
||||||
return state.get('availableDrives').find((drive) => {
|
|
||||||
return drive.get('device') === device
|
|
||||||
})
|
|
||||||
|
|
||||||
/* eslint-enable lodash/prefer-lodash-method */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -160,6 +157,8 @@ const findDrive = (state, device) => {
|
|||||||
const storeReducer = (state = DEFAULT_STATE, action) => {
|
const storeReducer = (state = DEFAULT_STATE, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ACTIONS.SET_AVAILABLE_DRIVES: {
|
case ACTIONS.SET_AVAILABLE_DRIVES: {
|
||||||
|
// Type: action.data : Array<DriveObject>
|
||||||
|
|
||||||
if (!action.data) {
|
if (!action.data) {
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
title: 'Missing drives'
|
title: 'Missing drives'
|
||||||
@ -167,20 +166,20 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert object instances to plain objects
|
// Convert object instances to plain objects
|
||||||
action.data = JSON.parse(JSON.stringify(action.data))
|
const drives = JSON.parse(JSON.stringify(action.data))
|
||||||
|
|
||||||
if (!_.isArray(action.data) || !_.every(action.data, _.isPlainObject)) {
|
if (!_.isArray(drives) || !_.every(drives, _.isPlainObject)) {
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
title: `Invalid drives: ${action.data}`
|
title: `Invalid drives: ${drives}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const newState = state.set('availableDrives', Immutable.fromJS(action.data))
|
const newState = state.set('availableDrives', Immutable.fromJS(drives))
|
||||||
|
|
||||||
const AUTOSELECT_DRIVE_COUNT = 1
|
const AUTOSELECT_DRIVE_COUNT = 1
|
||||||
const numberOfDrives = action.data.length
|
const numberOfDrives = drives.length
|
||||||
if (numberOfDrives === AUTOSELECT_DRIVE_COUNT) {
|
if (numberOfDrives === AUTOSELECT_DRIVE_COUNT) {
|
||||||
const drive = _.first(action.data)
|
const [ drive ] = drives
|
||||||
|
|
||||||
// Even if there's no image selected, we need to call several
|
// Even if there's no image selected, we need to call several
|
||||||
// drive/image related checks, and `{}` works fine with them
|
// drive/image related checks, and `{}` works fine with them
|
||||||
@ -198,27 +197,42 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
|||||||
!constraints.isSystemDrive(drive)
|
!constraints.isSystemDrive(drive)
|
||||||
|
|
||||||
])) {
|
])) {
|
||||||
|
// Auto-select this drive
|
||||||
return storeReducer(newState, {
|
return storeReducer(newState, {
|
||||||
type: ACTIONS.SELECT_DRIVE,
|
type: ACTIONS.SELECT_DRIVE,
|
||||||
data: drive.device
|
data: drive.device
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const selectedDevice = newState.getIn([ 'selection', 'drive' ])
|
// Deselect this drive in case it still is selected
|
||||||
|
|
||||||
if (selectedDevice && !_.find(action.data, {
|
|
||||||
device: selectedDevice
|
|
||||||
})) {
|
|
||||||
return storeReducer(newState, {
|
return storeReducer(newState, {
|
||||||
type: ACTIONS.DESELECT_DRIVE
|
type: ACTIONS.DESELECT_DRIVE,
|
||||||
|
data: drive.device
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return newState
|
const selectedDevices = newState.getIn([ 'selection', 'devices' ]).toJS()
|
||||||
|
|
||||||
|
// Remove selected drives that are stale, i.e. missing from availableDrives
|
||||||
|
return _.reduce(selectedDevices, (accState, device) => {
|
||||||
|
// Check whether the drive still exists in availableDrives
|
||||||
|
if (device && !_.find(drives, {
|
||||||
|
device
|
||||||
|
})) {
|
||||||
|
// Deselect this drive gone from availableDrives
|
||||||
|
return storeReducer(accState, {
|
||||||
|
type: ACTIONS.DESELECT_DRIVE,
|
||||||
|
data: device
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return accState
|
||||||
|
}, newState)
|
||||||
}
|
}
|
||||||
|
|
||||||
case ACTIONS.SET_FLASH_STATE: {
|
case ACTIONS.SET_FLASH_STATE: {
|
||||||
|
// Type: action.data : FlashStateObject
|
||||||
|
|
||||||
if (!state.get('isFlashing')) {
|
if (!state.get('isFlashing')) {
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
title: 'Can\'t set the flashing state when not flashing'
|
title: 'Can\'t set the flashing state when not flashing'
|
||||||
@ -264,6 +278,8 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ACTIONS.UNSET_FLASHING_FLAG: {
|
case ACTIONS.UNSET_FLASHING_FLAG: {
|
||||||
|
// Type: action.data : FlashResultsObject
|
||||||
|
|
||||||
if (!action.data) {
|
if (!action.data) {
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
title: 'Missing results'
|
title: 'Missing results'
|
||||||
@ -305,40 +321,46 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ACTIONS.SELECT_DRIVE: {
|
case ACTIONS.SELECT_DRIVE: {
|
||||||
if (!action.data) {
|
// Type: action.data : String
|
||||||
|
|
||||||
|
const device = action.data
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
title: 'Missing drive'
|
title: 'Missing drive'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_.isString(action.data)) {
|
if (!_.isString(device)) {
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
title: `Invalid drive: ${action.data}`
|
title: `Invalid drive: ${device}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedDrive = findDrive(state, action.data)
|
const selectedDrive = _.find(getAvailableDrives(state), { device })
|
||||||
|
|
||||||
if (!selectedDrive) {
|
if (!selectedDrive) {
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
title: `The drive is not available: ${action.data}`
|
title: `The drive is not available: ${device}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedDrive.get('isReadOnly')) {
|
if (selectedDrive.isReadOnly) {
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
title: 'The drive is write-protected'
|
title: 'The drive is write-protected'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const image = state.getIn([ 'selection', 'image' ])
|
const image = state.getIn([ 'selection', 'image' ])
|
||||||
if (image && !constraints.isDriveLargeEnough(selectedDrive.toJS(), image.toJS())) {
|
if (image && !constraints.isDriveLargeEnough(selectedDrive, image.toJS())) {
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
title: 'The drive is not large enough'
|
title: 'The drive is not large enough'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.setIn([ 'selection', 'drive' ], Immutable.fromJS(action.data))
|
const selectedDevices = state.getIn([ 'selection', 'devices' ])
|
||||||
|
|
||||||
|
return state.setIn([ 'selection', 'devices' ], selectedDevices.add(device))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(jhermsmeier): Consolidate these assertions
|
// TODO(jhermsmeier): Consolidate these assertions
|
||||||
@ -346,6 +368,8 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
|||||||
// place where all the image extension / format handling
|
// place where all the image extension / format handling
|
||||||
// takes place, to avoid having to check 2+ locations with different logic
|
// takes place, to avoid having to check 2+ locations with different logic
|
||||||
case ACTIONS.SELECT_IMAGE: {
|
case ACTIONS.SELECT_IMAGE: {
|
||||||
|
// Type: action.data : ImageObject
|
||||||
|
|
||||||
verifyNoNilFields(action.data, selectImageNoNilFields, 'image')
|
verifyNoNilFields(action.data, selectImageNoNilFields, 'image')
|
||||||
|
|
||||||
if (!_.isString(action.data.path)) {
|
if (!_.isString(action.data.path)) {
|
||||||
@ -432,24 +456,41 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedDrive = findDrive(state, state.getIn([ 'selection', 'drive' ]))
|
const selectedDevices = state.getIn([ 'selection', 'devices' ])
|
||||||
|
|
||||||
return _.attempt(() => {
|
// Remove image-incompatible drives from selection with `constraints.isDriveValid`
|
||||||
if (selectedDrive && !_.every([
|
return _.reduce(selectedDevices.toJS(), (accState, device) => {
|
||||||
constraints.isDriveValid(selectedDrive.toJS(), action.data),
|
const drive = _.find(getAvailableDrives(state), { device })
|
||||||
constraints.isDriveSizeRecommended(selectedDrive.toJS(), action.data)
|
if (!constraints.isDriveValid(drive, action.data) || !constraints.isDriveSizeRecommended(drive, action.data)) {
|
||||||
])) {
|
return storeReducer(accState, {
|
||||||
return storeReducer(state, {
|
type: ACTIONS.DESELECT_DRIVE,
|
||||||
type: ACTIONS.DESELECT_DRIVE
|
data: device
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return state
|
return accState
|
||||||
}).setIn([ 'selection', 'image' ], Immutable.fromJS(action.data))
|
}, state).setIn([ 'selection', 'image' ], Immutable.fromJS(action.data))
|
||||||
}
|
}
|
||||||
|
|
||||||
case ACTIONS.DESELECT_DRIVE: {
|
case ACTIONS.DESELECT_DRIVE: {
|
||||||
return state.deleteIn([ 'selection', 'drive' ])
|
// Type: action.data : String
|
||||||
|
|
||||||
|
if (!action.data) {
|
||||||
|
throw errors.createError({
|
||||||
|
title: 'Missing drive'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_.isString(action.data)) {
|
||||||
|
throw errors.createError({
|
||||||
|
title: `Invalid drive: ${action.data}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedDevices = state.getIn([ 'selection', 'devices' ])
|
||||||
|
|
||||||
|
// Remove drive from set in state
|
||||||
|
return state.setIn([ 'selection', 'devices' ], selectedDevices.delete(action.data))
|
||||||
}
|
}
|
||||||
|
|
||||||
case ACTIONS.DESELECT_IMAGE: {
|
case ACTIONS.DESELECT_IMAGE: {
|
||||||
@ -457,6 +498,8 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ACTIONS.SET_SETTINGS: {
|
case ACTIONS.SET_SETTINGS: {
|
||||||
|
// Type: action.data : SettingsObject
|
||||||
|
|
||||||
if (!action.data) {
|
if (!action.data) {
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
title: 'Missing settings'
|
title: 'Missing settings'
|
||||||
|
@ -143,8 +143,7 @@ describe('Model: availableDrives', function () {
|
|||||||
|
|
||||||
describe('given no selected image and no selected drive', function () {
|
describe('given no selected image and no selected drive', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
selectionState.deselectDrive()
|
selectionState.clear()
|
||||||
selectionState.deselectImage()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should auto-select a single valid available drive', function () {
|
it('should auto-select a single valid available drive', function () {
|
||||||
@ -164,7 +163,7 @@ describe('Model: availableDrives', function () {
|
|||||||
])
|
])
|
||||||
|
|
||||||
m.chai.expect(selectionState.hasDrive()).to.be.true
|
m.chai.expect(selectionState.hasDrive()).to.be.true
|
||||||
m.chai.expect(selectionState.getDrive().device).to.equal('/dev/sdb')
|
m.chai.expect(selectionState.getCurrentDrive().device).to.equal('/dev/sdb')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -176,7 +175,7 @@ describe('Model: availableDrives', function () {
|
|||||||
this.imagePath = '/mnt/bar/foo.img'
|
this.imagePath = '/mnt/bar/foo.img'
|
||||||
}
|
}
|
||||||
|
|
||||||
selectionState.deselectDrive()
|
selectionState.clear()
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: this.imagePath,
|
path: this.imagePath,
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -240,7 +239,7 @@ describe('Model: availableDrives', function () {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
m.chai.expect(selectionState.getDrive()).to.deep.equal({
|
m.chai.expect(selectionState.getCurrentDrive()).to.deep.equal({
|
||||||
device: '/dev/sdb',
|
device: '/dev/sdb',
|
||||||
name: 'Foo',
|
name: 'Foo',
|
||||||
size: 2000000000,
|
size: 2000000000,
|
||||||
@ -420,7 +419,7 @@ describe('Model: availableDrives', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
selectionState.deselectDrive()
|
selectionState.clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should be deleted if its not contained in the available drives anymore', function () {
|
it('should be deleted if its not contained in the available drives anymore', function () {
|
||||||
|
@ -28,8 +28,8 @@ describe('Model: selectionState', function () {
|
|||||||
selectionState.clear()
|
selectionState.clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('getDrive() should return undefined', function () {
|
it('getCurrentDrive() should return undefined', function () {
|
||||||
const drive = selectionState.getDrive()
|
const drive = selectionState.getCurrentDrive()
|
||||||
m.chai.expect(drive).to.be.undefined
|
m.chai.expect(drive).to.be.undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -96,9 +96,9 @@ describe('Model: selectionState', function () {
|
|||||||
selectionState.selectDrive('/dev/disk2')
|
selectionState.selectDrive('/dev/disk2')
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.getDrive()', function () {
|
describe('.getCurrentDrive()', function () {
|
||||||
it('should return the drive', function () {
|
it('should return the drive', function () {
|
||||||
const drive = selectionState.getDrive()
|
const drive = selectionState.getCurrentDrive()
|
||||||
m.chai.expect(drive).to.deep.equal({
|
m.chai.expect(drive).to.deep.equal({
|
||||||
device: '/dev/disk2',
|
device: '/dev/disk2',
|
||||||
name: 'USB Drive',
|
name: 'USB Drive',
|
||||||
@ -115,11 +115,13 @@ describe('Model: selectionState', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.setDrive()', function () {
|
describe('.selectDrive()', function () {
|
||||||
it('should override the drive', function () {
|
it('should queue the drive', function () {
|
||||||
selectionState.selectDrive('/dev/disk5')
|
selectionState.selectDrive('/dev/disk5')
|
||||||
const drive = selectionState.getDrive()
|
const drives = selectionState.getSelectedDevices()
|
||||||
m.chai.expect(drive).to.deep.equal({
|
const lastDriveDevice = _.last(drives)
|
||||||
|
const lastDrive = _.find(availableDrives.getDrives(), { device: lastDriveDevice })
|
||||||
|
m.chai.expect(lastDrive).to.deep.equal({
|
||||||
device: '/dev/disk5',
|
device: '/dev/disk5',
|
||||||
name: 'USB Drive',
|
name: 'USB Drive',
|
||||||
size: 999999999,
|
size: 999999999,
|
||||||
@ -128,17 +130,117 @@ describe('Model: selectionState', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.removeDrive()', function () {
|
describe('.deselectDrive()', function () {
|
||||||
it('should clear the drive', function () {
|
it('should clear drives', function () {
|
||||||
selectionState.deselectDrive()
|
const firstDrive = selectionState.getCurrentDrive()
|
||||||
const drive = selectionState.getDrive()
|
selectionState.deselectDrive(firstDrive.device)
|
||||||
|
const secondDrive = selectionState.getCurrentDrive()
|
||||||
|
selectionState.deselectDrive(secondDrive.device)
|
||||||
|
const drive = selectionState.getCurrentDrive()
|
||||||
m.chai.expect(drive).to.be.undefined
|
m.chai.expect(drive).to.be.undefined
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('given several drives', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
this.drives = [
|
||||||
|
{
|
||||||
|
device: '/dev/sdb',
|
||||||
|
description: 'DataTraveler 2.0',
|
||||||
|
size: 999999999,
|
||||||
|
mountpoint: '/media/UNTITLED',
|
||||||
|
name: '/dev/sdb',
|
||||||
|
system: false,
|
||||||
|
isReadOnly: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
device: '/dev/disk2',
|
||||||
|
name: 'USB Drive 2',
|
||||||
|
size: 999999999,
|
||||||
|
isReadOnly: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
device: '/dev/disk3',
|
||||||
|
name: 'USB Drive 3',
|
||||||
|
size: 999999999,
|
||||||
|
isReadOnly: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
availableDrives.setDrives(this.drives)
|
||||||
|
|
||||||
|
selectionState.selectDrive(this.drives[0].device)
|
||||||
|
selectionState.selectDrive(this.drives[1].device)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
selectionState.clear()
|
||||||
|
availableDrives.setDrives([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be able to add more drives', function () {
|
||||||
|
selectionState.selectDrive(this.drives[2].device)
|
||||||
|
m.chai.expect(selectionState.getSelectedDevices()).to.deep.equal(_.map(this.drives, 'device'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be able to remove drives', function () {
|
||||||
|
selectionState.deselectDrive(this.drives[1].device)
|
||||||
|
m.chai.expect(selectionState.getSelectedDevices()).to.deep.equal([ this.drives[0].device ])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('current drive should be affected by add order', function () {
|
||||||
|
m.chai.expect(selectionState.getCurrentDrive()).to.deep.equal(this.drives[0])
|
||||||
|
selectionState.toggleDrive(this.drives[0].device)
|
||||||
|
selectionState.toggleDrive(this.drives[0].device)
|
||||||
|
m.chai.expect(selectionState.getCurrentDrive()).to.deep.equal(this.drives[1])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should keep system drives selected', function () {
|
||||||
|
const systemDrive = {
|
||||||
|
device: '/dev/disk0',
|
||||||
|
name: 'USB Drive 0',
|
||||||
|
size: 999999999,
|
||||||
|
isReadOnly: false,
|
||||||
|
system: true
|
||||||
|
}
|
||||||
|
|
||||||
|
const newDrives = [ ..._.initial(this.drives), systemDrive ]
|
||||||
|
availableDrives.setDrives(newDrives)
|
||||||
|
|
||||||
|
selectionState.selectDrive(systemDrive.device)
|
||||||
|
availableDrives.setDrives(newDrives)
|
||||||
|
m.chai.expect(selectionState.getSelectedDevices()).to.deep.equal(_.map(newDrives, 'device'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be able to remove a drive', function () {
|
||||||
|
m.chai.expect(selectionState.getSelectedDevices().length).to.equal(2)
|
||||||
|
selectionState.toggleDrive(this.drives[0].device)
|
||||||
|
m.chai.expect(selectionState.getSelectedDevices()).to.deep.equal([ this.drives[1].device ])
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('.deselectAllDrives()', function () {
|
||||||
|
it('should remove all drives', function () {
|
||||||
|
selectionState.deselectAllDrives()
|
||||||
|
m.chai.expect(selectionState.getSelectedDevices()).to.deep.equal([])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('.deselectOtherDrives()', function () {
|
||||||
|
it('should deselect other drives', function () {
|
||||||
|
selectionState.deselectOtherDrives(this.drives[0].device)
|
||||||
|
m.chai.expect(selectionState.getSelectedDevices()).to.not.include.members([ this.drives[1].device ])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not remove the specified drive', function () {
|
||||||
|
selectionState.deselectOtherDrives(this.drives[0].device)
|
||||||
|
m.chai.expect(selectionState.getSelectedDevices()).to.deep.equal([ this.drives[0].device ])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('given no drive', function () {
|
describe('given no drive', function () {
|
||||||
describe('.setDrive()', function () {
|
describe('.selectDrive()', function () {
|
||||||
it('should be able to set a drive', function () {
|
it('should be able to set a drive', function () {
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
{
|
{
|
||||||
@ -150,7 +252,7 @@ describe('Model: selectionState', function () {
|
|||||||
])
|
])
|
||||||
|
|
||||||
selectionState.selectDrive('/dev/disk5')
|
selectionState.selectDrive('/dev/disk5')
|
||||||
const drive = selectionState.getDrive()
|
const drive = selectionState.getCurrentDrive()
|
||||||
m.chai.expect(drive).to.deep.equal({
|
m.chai.expect(drive).to.deep.equal({
|
||||||
device: '/dev/disk5',
|
device: '/dev/disk5',
|
||||||
name: 'USB Drive',
|
name: 'USB Drive',
|
||||||
@ -159,7 +261,7 @@ describe('Model: selectionState', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should throw if drive is write protected', function () {
|
it('should throw if drive is read-only', function () {
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
{
|
{
|
||||||
device: '/dev/disk1',
|
device: '/dev/disk1',
|
||||||
@ -219,7 +321,7 @@ describe('Model: selectionState', function () {
|
|||||||
selectionState.selectImage(this.image)
|
selectionState.selectImage(this.image)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.setDrive()', function () {
|
describe('.selectDrive()', function () {
|
||||||
it('should throw if drive is not large enough', function () {
|
it('should throw if drive is not large enough', function () {
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
{
|
{
|
||||||
@ -298,7 +400,7 @@ describe('Model: selectionState', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.setImage()', function () {
|
describe('.selectImage()', function () {
|
||||||
it('should override the image', function () {
|
it('should override the image', function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'bar.img',
|
path: 'bar.img',
|
||||||
@ -319,7 +421,7 @@ describe('Model: selectionState', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.removeImage()', function () {
|
describe('.deselectImage()', function () {
|
||||||
it('should clear the image', function () {
|
it('should clear the image', function () {
|
||||||
selectionState.deselectImage()
|
selectionState.deselectImage()
|
||||||
|
|
||||||
@ -332,7 +434,9 @@ describe('Model: selectionState', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('given no image', function () {
|
describe('given no image', function () {
|
||||||
describe('.setImage()', function () {
|
describe('.selectImage()', function () {
|
||||||
|
afterEach(selectionState.clear)
|
||||||
|
|
||||||
it('should be able to set an image', function () {
|
it('should be able to set an image', function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
@ -755,7 +859,7 @@ describe('Model: selectionState', function () {
|
|||||||
{
|
{
|
||||||
device: '/dev/disk1',
|
device: '/dev/disk1',
|
||||||
name: 'USB Drive',
|
name: 'USB Drive',
|
||||||
size: 999999999,
|
size: 123456789,
|
||||||
isReadOnly: false
|
isReadOnly: false
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
@ -767,10 +871,10 @@ describe('Model: selectionState', function () {
|
|||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
size: {
|
size: {
|
||||||
original: 9999999999,
|
original: 1234567890,
|
||||||
final: {
|
final: {
|
||||||
estimation: false,
|
estimation: false,
|
||||||
value: 9999999999
|
value: 1234567890
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -890,20 +994,126 @@ describe('Model: selectionState', function () {
|
|||||||
m.chai.expect(selectionState.hasImage()).to.be.false
|
m.chai.expect(selectionState.hasImage()).to.be.false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.deselectImage()', function () {
|
describe('.deselectImage()', function () {
|
||||||
it('should not clear any drives', function () {
|
beforeEach(function () {
|
||||||
selectionState.deselectImage()
|
selectionState.deselectImage()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getCurrentDrive() should return the selected drive object', function () {
|
||||||
|
const drive = selectionState.getCurrentDrive()
|
||||||
|
m.chai.expect(drive).to.deep.equal({
|
||||||
|
device: '/dev/disk1',
|
||||||
|
isReadOnly: false,
|
||||||
|
name: 'USB Drive',
|
||||||
|
size: 999999999
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getImagePath() should return undefined', function () {
|
||||||
|
const imagePath = selectionState.getImagePath()
|
||||||
|
m.chai.expect(imagePath).to.be.undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getImageSize() should return undefined', function () {
|
||||||
|
const imageSize = selectionState.getImageSize()
|
||||||
|
m.chai.expect(imageSize).to.be.undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not clear any drives', function () {
|
||||||
m.chai.expect(selectionState.hasDrive()).to.be.true
|
m.chai.expect(selectionState.hasDrive()).to.be.true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('hasImage() should return false', function () {
|
||||||
|
const hasImage = selectionState.hasImage()
|
||||||
|
m.chai.expect(hasImage).to.be.false
|
||||||
|
})
|
||||||
})
|
})
|
||||||
describe('.deselectDrive()', function () {
|
|
||||||
|
describe('.deselectAllDrives()', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
selectionState.deselectAllDrives()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getCurrentDrive() should return undefined', function () {
|
||||||
|
const drive = selectionState.getCurrentDrive()
|
||||||
|
m.chai.expect(drive).to.be.undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getImagePath() should return the image path', function () {
|
||||||
|
const imagePath = selectionState.getImagePath()
|
||||||
|
m.chai.expect(imagePath).to.equal('foo.img')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getImageSize() should return the image size', function () {
|
||||||
|
const imageSize = selectionState.getImageSize()
|
||||||
|
m.chai.expect(imageSize).to.equal(999999999)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('hasDrive() should return false', function () {
|
||||||
|
const hasDrive = selectionState.hasDrive()
|
||||||
|
m.chai.expect(hasDrive).to.be.false
|
||||||
|
})
|
||||||
|
|
||||||
it('should not clear the image', function () {
|
it('should not clear the image', function () {
|
||||||
selectionState.deselectDrive()
|
|
||||||
m.chai.expect(selectionState.hasImage()).to.be.true
|
m.chai.expect(selectionState.hasImage()).to.be.true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('given several drives', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
availableDrives.setDrives([
|
||||||
|
{
|
||||||
|
device: '/dev/disk1',
|
||||||
|
name: 'USB Drive 1',
|
||||||
|
size: 999999999,
|
||||||
|
isReadOnly: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
device: '/dev/disk2',
|
||||||
|
name: 'USB Drive 2',
|
||||||
|
size: 999999999,
|
||||||
|
isReadOnly: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
device: '/dev/disk3',
|
||||||
|
name: 'USB Drive 3',
|
||||||
|
size: 999999999,
|
||||||
|
isReadOnly: false
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
selectionState.selectDrive('/dev/disk1')
|
||||||
|
selectionState.selectDrive('/dev/disk2')
|
||||||
|
selectionState.selectDrive('/dev/disk3')
|
||||||
|
|
||||||
|
selectionState.selectImage({
|
||||||
|
path: 'foo.img',
|
||||||
|
extension: 'img',
|
||||||
|
size: {
|
||||||
|
original: 999999999,
|
||||||
|
final: {
|
||||||
|
estimation: false,
|
||||||
|
value: 999999999
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('.clear()', function () {
|
||||||
|
it('should clear all selections', function () {
|
||||||
|
m.chai.expect(selectionState.hasDrive()).to.be.true
|
||||||
|
m.chai.expect(selectionState.hasImage()).to.be.true
|
||||||
|
|
||||||
|
selectionState.clear()
|
||||||
|
|
||||||
|
m.chai.expect(selectionState.hasDrive()).to.be.false
|
||||||
|
m.chai.expect(selectionState.hasImage()).to.be.false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('.isCurrentDrive()', function () {
|
describe('.isCurrentDrive()', function () {
|
||||||
describe('given a selected drive', function () {
|
describe('given a selected drive', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
@ -924,6 +1134,11 @@ describe('Model: selectionState', function () {
|
|||||||
selectionState.selectDrive('/dev/sdb')
|
selectionState.selectDrive('/dev/sdb')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
selectionState.clear()
|
||||||
|
availableDrives.setDrives([])
|
||||||
|
})
|
||||||
|
|
||||||
it('should return false if an undefined value is passed', function () {
|
it('should return false if an undefined value is passed', function () {
|
||||||
m.chai.expect(selectionState.isCurrentDrive()).to.be.false
|
m.chai.expect(selectionState.isCurrentDrive()).to.be.false
|
||||||
})
|
})
|
||||||
@ -939,7 +1154,7 @@ describe('Model: selectionState', function () {
|
|||||||
|
|
||||||
describe('given no selected drive', function () {
|
describe('given no selected drive', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
selectionState.deselectDrive()
|
selectionState.clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return false if an undefined value is passed', function () {
|
it('should return false if an undefined value is passed', function () {
|
||||||
@ -952,7 +1167,7 @@ describe('Model: selectionState', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.toggleSetDrive()', function () {
|
describe('.toggleDrive()', function () {
|
||||||
describe('given a selected drive', function () {
|
describe('given a selected drive', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.drive = {
|
this.drive = {
|
||||||
@ -971,7 +1186,7 @@ describe('Model: selectionState', function () {
|
|||||||
this.drive,
|
this.drive,
|
||||||
{
|
{
|
||||||
device: '/dev/disk2',
|
device: '/dev/disk2',
|
||||||
name: 'USB Drive',
|
name: 'USB Drive 2',
|
||||||
size: 999999999,
|
size: 999999999,
|
||||||
isReadOnly: false
|
isReadOnly: false
|
||||||
}
|
}
|
||||||
@ -980,13 +1195,18 @@ describe('Model: selectionState', function () {
|
|||||||
selectionState.selectDrive(this.drive.device)
|
selectionState.selectDrive(this.drive.device)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
selectionState.clear()
|
||||||
|
availableDrives.setDrives([])
|
||||||
|
})
|
||||||
|
|
||||||
it('should be able to remove the drive', function () {
|
it('should be able to remove the drive', function () {
|
||||||
m.chai.expect(selectionState.hasDrive()).to.be.true
|
m.chai.expect(selectionState.hasDrive()).to.be.true
|
||||||
selectionState.toggleDrive(this.drive.device)
|
selectionState.toggleDrive(this.drive.device)
|
||||||
m.chai.expect(selectionState.hasDrive()).to.be.false
|
m.chai.expect(selectionState.hasDrive()).to.be.false
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should be able to replace the drive', function () {
|
it('should not replace a different drive', function () {
|
||||||
const drive = {
|
const drive = {
|
||||||
device: '/dev/disk2',
|
device: '/dev/disk2',
|
||||||
name: 'USB Drive',
|
name: 'USB Drive',
|
||||||
@ -994,29 +1214,48 @@ describe('Model: selectionState', function () {
|
|||||||
isReadOnly: false
|
isReadOnly: false
|
||||||
}
|
}
|
||||||
|
|
||||||
m.chai.expect(selectionState.getDrive()).to.deep.equal(this.drive)
|
m.chai.expect(selectionState.getCurrentDrive()).to.deep.equal(this.drive)
|
||||||
selectionState.toggleDrive(drive.device)
|
selectionState.toggleDrive(drive.device)
|
||||||
m.chai.expect(selectionState.getDrive()).to.deep.equal(drive)
|
m.chai.expect(selectionState.getCurrentDrive()).to.deep.equal(this.drive)
|
||||||
m.chai.expect(selectionState.getDrive()).to.not.deep.equal(this.drive)
|
m.chai.expect(selectionState.getCurrentDrive()).to.not.deep.equal(drive)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('given no selected drive', function () {
|
describe('given no selected drive', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
selectionState.deselectDrive()
|
selectionState.clear()
|
||||||
|
|
||||||
|
availableDrives.setDrives([
|
||||||
|
{
|
||||||
|
device: '/dev/disk2',
|
||||||
|
name: 'USB Drive 2',
|
||||||
|
size: 999999999,
|
||||||
|
isReadOnly: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
device: '/dev/disk3',
|
||||||
|
name: 'USB Drive 3',
|
||||||
|
size: 999999999,
|
||||||
|
isReadOnly: false
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
availableDrives.setDrives([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should set the drive', function () {
|
it('should set the drive', function () {
|
||||||
const drive = {
|
const drive = {
|
||||||
device: '/dev/disk2',
|
device: '/dev/disk2',
|
||||||
name: 'USB Drive',
|
name: 'USB Drive 2',
|
||||||
size: 999999999,
|
size: 999999999,
|
||||||
isReadOnly: false
|
isReadOnly: false
|
||||||
}
|
}
|
||||||
|
|
||||||
m.chai.expect(selectionState.hasDrive()).to.be.false
|
m.chai.expect(selectionState.hasDrive()).to.be.false
|
||||||
selectionState.toggleDrive(drive.device)
|
selectionState.toggleDrive(drive.device)
|
||||||
m.chai.expect(selectionState.getDrive()).to.deep.equal(drive)
|
m.chai.expect(selectionState.getCurrentDrive()).to.deep.equal(drive)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user