mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-24 03:36:36 +00:00
feat(GUI): add colorised multi-writes progress status dots (#2115)
We add colorised progress status dots with quantities of `flashing`, `validating`, `succeeded`, and `failed` devices. Change-Type: patch Changelog-Entry: Add colorised multi-writes progress status dots.
This commit is contained in:
parent
3fe5d7711f
commit
835f2cf769
@ -28,7 +28,10 @@ const utils = require('../../../shared/utils')
|
||||
*
|
||||
* @example
|
||||
* const status = progressStatus.fromFlashState({
|
||||
* type: 'write',
|
||||
* flashing: 1,
|
||||
* validating: 0,
|
||||
* succeeded: 0,
|
||||
* failed: 0,
|
||||
* percentage: 55,
|
||||
* speed: 2049
|
||||
* })
|
||||
@ -37,25 +40,26 @@ const utils = require('../../../shared/utils')
|
||||
* // '55% Flashing'
|
||||
*/
|
||||
exports.fromFlashState = (state) => {
|
||||
const isChecking = state.type === 'check'
|
||||
const isFlashing = Boolean(state.flashing)
|
||||
const isValidating = !isFlashing && Boolean(state.validating)
|
||||
const shouldValidate = settings.get('validateWriteOnSuccess')
|
||||
const shouldUnmount = settings.get('unmountOnSuccess')
|
||||
|
||||
if (state.percentage === utils.PERCENTAGE_MINIMUM && !state.speed) {
|
||||
if (isChecking) {
|
||||
if (isValidating) {
|
||||
return 'Validating...'
|
||||
}
|
||||
|
||||
return 'Starting...'
|
||||
} else if (state.percentage === utils.PERCENTAGE_MAXIMUM) {
|
||||
if ((isChecking || !shouldValidate) && shouldUnmount) {
|
||||
if ((isValidating || !shouldValidate) && shouldUnmount) {
|
||||
return 'Unmounting...'
|
||||
}
|
||||
|
||||
return 'Finishing...'
|
||||
} else if (state.type === 'write') {
|
||||
} else if (isFlashing) {
|
||||
return `${state.percentage}% Flashing`
|
||||
} else if (state.type === 'check') {
|
||||
} else if (isValidating) {
|
||||
return `${state.percentage}% Validating`
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,10 @@ const INITIAL_TITLE = document.title
|
||||
*
|
||||
* @example
|
||||
* const title = getWindowTitle({
|
||||
* type: 'write',
|
||||
* flashing: 1,
|
||||
* validating: 0,
|
||||
* succeeded: 0,
|
||||
* failed: 0,
|
||||
* percentage: 55,
|
||||
* speed: 2049
|
||||
* });
|
||||
@ -78,7 +81,10 @@ exports.currentWindow = electron.remote.getCurrentWindow()
|
||||
*
|
||||
* @example
|
||||
* windowProgress.set({
|
||||
* type: 'write',
|
||||
* flashing: 1,
|
||||
* validating: 0,
|
||||
* succeeded: 0,
|
||||
* failed: 0,
|
||||
* percentage: 55,
|
||||
* speed: 2049
|
||||
* })
|
||||
|
@ -23,6 +23,7 @@ const exceptionReporter = require('../../../modules/exception-reporter')
|
||||
const availableDrives = require('../../../../../shared/models/available-drives')
|
||||
const selectionState = require('../../../../../shared/models/selection-state')
|
||||
const driveConstraints = require('../../../../../shared/drive-constraints')
|
||||
const messages = require('../../../../../shared/messages')
|
||||
|
||||
module.exports = function (
|
||||
TooltipModalService,
|
||||
@ -35,6 +36,7 @@ module.exports = function (
|
||||
this.settings = settings
|
||||
this.external = OSOpenExternalService
|
||||
this.constraints = driveConstraints
|
||||
this.progressMessage = messages.progress
|
||||
|
||||
/**
|
||||
* @summary Determine if the drive step should be disabled
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
.page-main {
|
||||
margin-top: 75px;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
svg-icon > img[disabled] {
|
||||
@ -69,7 +69,6 @@ svg-icon > img[disabled] {
|
||||
|
||||
.page-main .step-footer {
|
||||
margin-top: 10px;
|
||||
margin-bottom: -40px;
|
||||
color: $palette-theme-dark-disabled-foreground;
|
||||
font-size: 10px;
|
||||
}
|
||||
@ -106,3 +105,47 @@ svg-icon > img[disabled] {
|
||||
.page-main .step-size {
|
||||
color: $palette-theme-dark-disabled-foreground;
|
||||
}
|
||||
|
||||
.target-status-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 8px 28px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.target-status-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
font-family: inherit;
|
||||
|
||||
> .target-status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&.target-status-flashing > .target-status-dot {
|
||||
background-color: $palette-theme-warning-background;
|
||||
}
|
||||
&.target-status-validating > .target-status-dot {
|
||||
background-color: $palette-theme-primary-background;
|
||||
}
|
||||
&.target-status-succeeded > .target-status-dot {
|
||||
background-color: $palette-theme-success-background;
|
||||
}
|
||||
&.target-status-failed > .target-status-dot {
|
||||
background-color: $palette-theme-danger-background;
|
||||
}
|
||||
|
||||
> .target-status-quantity {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
> .target-status-message {
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +106,15 @@
|
||||
<span ng-bind="main.state.getFlashState().speed.toFixed(2) + ' MB/s'"></span>
|
||||
<span>ETA: {{ main.state.getFlashState().eta | secondsToDate | amDateFormat:'m[m]ss[s]' }}</span>
|
||||
</p>
|
||||
|
||||
<div class="target-status-wrap" ng-if="main.state.isFlashing()">
|
||||
<div class="target-status-line target-status-{{ type }}"
|
||||
ng-repeat="(type, quantity) in main.state.getFlashQuantities()">
|
||||
<span class="target-status-dot"></span>
|
||||
<span class="target-status-quantity">{{ quantity }}</span>
|
||||
<span class="target-status-message">{{ main.progressMessage[type]() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6499,7 +6499,7 @@ svg-icon {
|
||||
* limitations under the License.
|
||||
*/
|
||||
.page-main {
|
||||
margin-top: 75px; }
|
||||
margin-top: 50px; }
|
||||
|
||||
svg-icon > img[disabled] {
|
||||
opacity: 0.2; }
|
||||
@ -6539,7 +6539,6 @@ svg-icon > img[disabled] {
|
||||
|
||||
.page-main .step-footer {
|
||||
margin-top: 10px;
|
||||
margin-bottom: -40px;
|
||||
color: #787c7f;
|
||||
font-size: 10px; }
|
||||
|
||||
@ -6570,6 +6569,37 @@ svg-icon > img[disabled] {
|
||||
.page-main .step-size {
|
||||
color: #787c7f; }
|
||||
|
||||
.target-status-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 8px 28px;
|
||||
align-items: flex-start; }
|
||||
|
||||
.target-status-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
font-family: inherit; }
|
||||
.target-status-line > .target-status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px; }
|
||||
.target-status-line.target-status-flashing > .target-status-dot {
|
||||
background-color: #ff912f; }
|
||||
.target-status-line.target-status-validating > .target-status-dot {
|
||||
background-color: #5793db; }
|
||||
.target-status-line.target-status-succeeded > .target-status-dot {
|
||||
background-color: #5fb835; }
|
||||
.target-status-line.target-status-failed > .target-status-dot {
|
||||
background-color: #d9534f; }
|
||||
.target-status-line > .target-status-quantity {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
min-width: 32px; }
|
||||
.target-status-line > .target-status-message {
|
||||
color: gray; }
|
||||
|
||||
/*
|
||||
* Copyright 2016 resin.io
|
||||
*
|
||||
|
@ -25,6 +25,30 @@
|
||||
*/
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* @summary Progress messages
|
||||
* @namespace progress
|
||||
* @memberof messages
|
||||
*/
|
||||
progress: {
|
||||
|
||||
flashing: () => {
|
||||
return 'Flashing device(s)'
|
||||
},
|
||||
|
||||
validating: () => {
|
||||
return 'Validating device(s)'
|
||||
},
|
||||
|
||||
succeeded: () => {
|
||||
return 'Done device(s)'
|
||||
},
|
||||
|
||||
failed: () => {
|
||||
return 'Failed device(s)'
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @summary Informational messages
|
||||
* @namespace info
|
||||
|
@ -114,25 +114,38 @@ exports.unsetFlashingFlag = (results) => {
|
||||
* });
|
||||
*/
|
||||
exports.setProgressState = (state) => {
|
||||
if (!_.isString(state.type)) {
|
||||
throw new Error(`Invalid state type: ${state.type}`)
|
||||
}
|
||||
|
||||
// NOTE(Shou): we can most likely remove `state.type` when multi-writes
|
||||
// is in proper, thanks to the status quantities.
|
||||
const isValidating = state.type === 'check'
|
||||
const type = isValidating ? 'validating' : 'flashing'
|
||||
const data = _.assign({
|
||||
flashing: 0,
|
||||
validating: 0,
|
||||
succeeded: 0,
|
||||
failed: 0,
|
||||
percentage: _.isNumber(state.percentage) && !_.isNaN(state.percentage)
|
||||
? Math.floor(state.percentage)
|
||||
: state.percentage,
|
||||
eta: state.eta,
|
||||
|
||||
speed: _.attempt(() => {
|
||||
if (_.isNumber(state.speed) && !_.isNaN(state.speed)) {
|
||||
// Preserve only two decimal places
|
||||
const PRECISION = 2
|
||||
return _.round(units.bytesToMegabytes(state.speed), PRECISION)
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
}, { [type]: 1 })
|
||||
|
||||
store.dispatch({
|
||||
type: store.Actions.SET_FLASH_STATE,
|
||||
data: {
|
||||
type: state.type,
|
||||
percentage: _.isNumber(state.percentage) && !_.isNaN(state.percentage)
|
||||
? Math.floor(state.percentage)
|
||||
: state.percentage,
|
||||
eta: state.eta,
|
||||
|
||||
speed: _.attempt(() => {
|
||||
if (_.isNumber(state.speed) && !_.isNaN(state.speed)) {
|
||||
// Preserve only two decimal places
|
||||
const PRECISION = 2
|
||||
return _.round(units.bytesToMegabytes(state.speed), PRECISION)
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
}
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
@ -164,6 +177,15 @@ exports.getFlashState = () => {
|
||||
return store.getState().get('flashState').toJS()
|
||||
}
|
||||
|
||||
exports.getFlashQuantities = () => {
|
||||
return _.pick(exports.getFlashState(), [
|
||||
'flashing',
|
||||
'validating',
|
||||
'succeeded',
|
||||
'failed'
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Determine if the last flash was cancelled
|
||||
* @function
|
||||
|
@ -57,7 +57,6 @@ const verifyNoNilFields = (object, fields, name) => {
|
||||
* @private
|
||||
*/
|
||||
const flashStateNoNilFields = [
|
||||
'type',
|
||||
'percentage',
|
||||
'eta',
|
||||
'speed'
|
||||
@ -88,6 +87,10 @@ const DEFAULT_STATE = Immutable.fromJS({
|
||||
isFlashing: false,
|
||||
flashResults: {},
|
||||
flashState: {
|
||||
flashing: 0,
|
||||
validating: 0,
|
||||
succeeded: 0,
|
||||
failed: 0,
|
||||
percentage: 0,
|
||||
speed: 0
|
||||
},
|
||||
@ -241,9 +244,14 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
||||
|
||||
verifyNoNilFields(action.data, flashStateNoNilFields, 'flash')
|
||||
|
||||
if (!_.isString(action.data.type)) {
|
||||
if (_.every(_.pick(action.data, [
|
||||
'flashing',
|
||||
'validating',
|
||||
'succeeded',
|
||||
'failed'
|
||||
]), _.identity)) {
|
||||
throw errors.createError({
|
||||
title: `Invalid state type: ${action.data.type}`
|
||||
title: 'Missing state quantity field(s)'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,10 @@ describe('Browser: progressStatus', function () {
|
||||
describe('.fromFlashState()', function () {
|
||||
beforeEach(function () {
|
||||
this.state = {
|
||||
type: 'write',
|
||||
flashing: 1,
|
||||
validating: 0,
|
||||
succeeded: 0,
|
||||
failed: 0,
|
||||
percentage: 0,
|
||||
eta: 15,
|
||||
speed: 100000000000000
|
||||
@ -22,80 +25,86 @@ describe('Browser: progressStatus', function () {
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('0% Flashing')
|
||||
})
|
||||
|
||||
it('should handle percentage == 0, type == write, unmountOnSuccess', function () {
|
||||
it('should handle percentage == 0, flashing, unmountOnSuccess', function () {
|
||||
this.state.speed = 0
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('Starting...')
|
||||
})
|
||||
|
||||
it('should handle percentage == 0, type == write, !unmountOnSuccess', function () {
|
||||
it('should handle percentage == 0, flashing, !unmountOnSuccess', function () {
|
||||
this.state.speed = 0
|
||||
settings.set('unmountOnSuccess', false)
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('Starting...')
|
||||
})
|
||||
|
||||
it('should handle percentage == 0, type == check, unmountOnSuccess', function () {
|
||||
it('should handle percentage == 0, validating, unmountOnSuccess', function () {
|
||||
this.state.speed = 0
|
||||
this.state.type = 'check'
|
||||
this.state.flashing = 0
|
||||
this.state.validating = 1
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('Validating...')
|
||||
})
|
||||
|
||||
it('should handle percentage == 0, type == check, !unmountOnSuccess', function () {
|
||||
it('should handle percentage == 0, validating, !unmountOnSuccess', function () {
|
||||
this.state.speed = 0
|
||||
this.state.type = 'check'
|
||||
this.state.flashing = 0
|
||||
this.state.validating = 1
|
||||
settings.set('unmountOnSuccess', false)
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('Validating...')
|
||||
})
|
||||
|
||||
it('should handle percentage == 50, type == write, unmountOnSuccess', function () {
|
||||
it('should handle percentage == 50, flashing, unmountOnSuccess', function () {
|
||||
this.state.percentage = 50
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('50% Flashing')
|
||||
})
|
||||
|
||||
it('should handle percentage == 50, type == write, !unmountOnSuccess', function () {
|
||||
it('should handle percentage == 50, flashing, !unmountOnSuccess', function () {
|
||||
this.state.percentage = 50
|
||||
settings.set('unmountOnSuccess', false)
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('50% Flashing')
|
||||
})
|
||||
|
||||
it('should handle percentage == 50, type == check, unmountOnSuccess', function () {
|
||||
this.state.type = 'check'
|
||||
it('should handle percentage == 50, validating, unmountOnSuccess', function () {
|
||||
this.state.flashing = 0
|
||||
this.state.validating = 1
|
||||
this.state.percentage = 50
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('50% Validating')
|
||||
})
|
||||
|
||||
it('should handle percentage == 50, type == check, !unmountOnSuccess', function () {
|
||||
this.state.type = 'check'
|
||||
it('should handle percentage == 50, validating, !unmountOnSuccess', function () {
|
||||
this.state.flashing = 0
|
||||
this.state.validating = 1
|
||||
this.state.percentage = 50
|
||||
settings.set('unmountOnSuccess', false)
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('50% Validating')
|
||||
})
|
||||
|
||||
it('should handle percentage == 100, type == write, unmountOnSuccess, validateWriteOnSuccess', function () {
|
||||
it('should handle percentage == 100, flashing, unmountOnSuccess, validateWriteOnSuccess', function () {
|
||||
this.state.percentage = 100
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('Finishing...')
|
||||
})
|
||||
|
||||
it('should handle percentage == 100, type == write, unmountOnSuccess, !validateWriteOnSuccess', function () {
|
||||
it('should handle percentage == 100, flashing, unmountOnSuccess, !validateWriteOnSuccess', function () {
|
||||
this.state.percentage = 100
|
||||
settings.set('validateWriteOnSuccess', false)
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('Unmounting...')
|
||||
})
|
||||
|
||||
it('should handle percentage == 100, type == write, !unmountOnSuccess, !validateWriteOnSuccess', function () {
|
||||
it('should handle percentage == 100, flashing, !unmountOnSuccess, !validateWriteOnSuccess', function () {
|
||||
this.state.percentage = 100
|
||||
settings.set('unmountOnSuccess', false)
|
||||
settings.set('validateWriteOnSuccess', false)
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('Finishing...')
|
||||
})
|
||||
|
||||
it('should handle percentage == 100, type == check, unmountOnSuccess', function () {
|
||||
this.state.type = 'check'
|
||||
it('should handle percentage == 100, validating, unmountOnSuccess', function () {
|
||||
this.state.flashing = 0
|
||||
this.state.validating = 1
|
||||
this.state.percentage = 100
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('Unmounting...')
|
||||
})
|
||||
|
||||
it('should handle percentage == 100, type == check, !unmountOnSuccess', function () {
|
||||
this.state.type = 'check'
|
||||
it('should handle percentage == 100, validatinf, !unmountOnSuccess', function () {
|
||||
this.state.flashing = 0
|
||||
this.state.validating = 1
|
||||
this.state.percentage = 100
|
||||
settings.set('unmountOnSuccess', false)
|
||||
m.chai.expect(progressStatus.fromFlashState(this.state)).to.equal('Finishing...')
|
||||
|
@ -32,9 +32,12 @@ describe('Browser: WindowProgress', function () {
|
||||
}
|
||||
|
||||
this.state = {
|
||||
flashing: 1,
|
||||
validating: 0,
|
||||
succeeded: 0,
|
||||
failed: 0,
|
||||
percentage: 85,
|
||||
speed: 100,
|
||||
type: 'write'
|
||||
speed: 100
|
||||
}
|
||||
})
|
||||
|
||||
@ -73,19 +76,18 @@ describe('Browser: WindowProgress', function () {
|
||||
})
|
||||
|
||||
it('should set the flashing title', function () {
|
||||
this.state.type = 'write'
|
||||
windowProgress.set(this.state)
|
||||
m.chai.expect(this.setTitleSpy).to.have.been.calledWith(' \u2013 85% Flashing')
|
||||
})
|
||||
|
||||
it('should set the validating title', function () {
|
||||
this.state.type = 'check'
|
||||
this.state.flashing = 0
|
||||
this.state.validating = 1
|
||||
windowProgress.set(this.state)
|
||||
m.chai.expect(this.setTitleSpy).to.have.been.calledWith(' \u2013 85% Validating')
|
||||
})
|
||||
|
||||
it('should set the starting title', function () {
|
||||
this.state.type = 'write'
|
||||
this.state.percentage = 0
|
||||
this.state.speed = 0
|
||||
windowProgress.set(this.state)
|
||||
@ -93,7 +95,6 @@ describe('Browser: WindowProgress', function () {
|
||||
})
|
||||
|
||||
it('should set the finishing title', function () {
|
||||
this.state.type = 'write'
|
||||
this.state.percentage = 100
|
||||
windowProgress.set(this.state)
|
||||
m.chai.expect(this.setTitleSpy).to.have.been.calledWith(' \u2013 Finishing...')
|
||||
|
@ -38,6 +38,10 @@ describe('Model: flashState', function () {
|
||||
flashState.resetState()
|
||||
|
||||
m.chai.expect(flashState.getFlashState()).to.deep.equal({
|
||||
flashing: 0,
|
||||
validating: 0,
|
||||
succeeded: 0,
|
||||
failed: 0,
|
||||
percentage: 0,
|
||||
speed: 0
|
||||
})
|
||||
@ -94,17 +98,6 @@ describe('Model: flashState', function () {
|
||||
}).to.throw('Can\'t set the flashing state when not flashing')
|
||||
})
|
||||
|
||||
it('should throw if type is missing', function () {
|
||||
flashState.setFlashingFlag()
|
||||
m.chai.expect(function () {
|
||||
flashState.setProgressState({
|
||||
percentage: 50,
|
||||
eta: 15,
|
||||
speed: 100000000000
|
||||
})
|
||||
}).to.throw('Missing flash fields: type')
|
||||
})
|
||||
|
||||
it('should throw if type is not a string', function () {
|
||||
flashState.setFlashingFlag()
|
||||
m.chai.expect(function () {
|
||||
@ -267,6 +260,10 @@ describe('Model: flashState', function () {
|
||||
flashState.resetState()
|
||||
const currentFlashState = flashState.getFlashState()
|
||||
m.chai.expect(currentFlashState).to.deep.equal({
|
||||
flashing: 0,
|
||||
validating: 0,
|
||||
succeeded: 0,
|
||||
failed: 0,
|
||||
percentage: 0,
|
||||
speed: 0
|
||||
})
|
||||
@ -283,7 +280,15 @@ describe('Model: flashState', function () {
|
||||
flashState.setFlashingFlag()
|
||||
flashState.setProgressState(state)
|
||||
const currentFlashState = flashState.getFlashState()
|
||||
m.chai.expect(currentFlashState).to.deep.equal(state)
|
||||
m.chai.expect(currentFlashState).to.deep.equal({
|
||||
flashing: 1,
|
||||
validating: 0,
|
||||
succeeded: 0,
|
||||
failed: 0,
|
||||
percentage: 50,
|
||||
eta: 15,
|
||||
speed: 0
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -377,6 +382,10 @@ describe('Model: flashState', function () {
|
||||
})
|
||||
|
||||
m.chai.expect(flashState.getFlashState()).to.not.deep.equal({
|
||||
flashing: 0,
|
||||
validating: 0,
|
||||
succeeded: 0,
|
||||
failed: 0,
|
||||
percentage: 0,
|
||||
speed: 0
|
||||
})
|
||||
@ -387,6 +396,10 @@ describe('Model: flashState', function () {
|
||||
})
|
||||
|
||||
m.chai.expect(flashState.getFlashState()).to.deep.equal({
|
||||
flashing: 0,
|
||||
validating: 0,
|
||||
succeeded: 0,
|
||||
failed: 0,
|
||||
percentage: 0,
|
||||
speed: 0
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user