Merge pull request #3377 from balena-io/113

113
This commit is contained in:
bulldozer-balena[bot] 2020-12-17 14:20:57 +00:00 committed by GitHub
commit d814202424
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 143 additions and 188 deletions

2
FAQ.md
View File

@ -43,4 +43,4 @@ Etcher requires an available [polkit authentication agent](https://wiki.archlinu
## May I run Etcher in older macOS versions? ## May I run Etcher in older macOS versions?
Etcher GUI is based on the [Electron](http://electron.atom.io/) framework, [which only supports macOS 10.9 and newer versions](https://github.com/electron/electron/blob/master/docs/tutorial/support.md#supported-platforms). Etcher GUI is based on the [Electron](http://electron.atom.io/) framework, [which only supports macOS 10.10 and newer versions](https://github.com/electron/electron/blob/master/docs/tutorial/support.md#supported-platforms).

View File

@ -3,7 +3,7 @@
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
RESIN_SCRIPTS ?= ./scripts/resin RESIN_SCRIPTS ?= ./scripts/resin
export NPM_VERSION ?= 6.14.5 export NPM_VERSION ?= 6.14.8
S3_BUCKET = artifacts.ci.balena-cloud.com S3_BUCKET = artifacts.ci.balena-cloud.com
# This directory will be completely deleted by the `clean` rule # This directory will be completely deleted by the `clean` rule

View File

@ -4,6 +4,13 @@ Getting help with Etcher
There are various ways to get support for Etcher if you experience an issue or There are various ways to get support for Etcher if you experience an issue or
have an idea you'd like to share with us. have an idea you'd like to share with us.
Documentation
------
We have answers to a variety of frequently asked questions in the [user
documentation][documentation] and also in the [FAQs][faq] on the Etcher website.
Forums Forums
------ ------
@ -32,3 +39,5 @@ one][new-issue].
[discourse]: https://forums.balena.io/c/etcher [discourse]: https://forums.balena.io/c/etcher
[issues]: https://github.com/balena-io/etcher/issues [issues]: https://github.com/balena-io/etcher/issues
[new-issue]: https://github.com/balena-io/etcher/issues/new [new-issue]: https://github.com/balena-io/etcher/issues/new
[documentation]: https://github.com/balena-io/etcher/blob/master/docs/USER-DOCUMENTATION.md
[faq]: https://etcher.io

View File

@ -74,6 +74,8 @@ function isDrivelistDrive(drive: Drive): drive is DrivelistDrive {
const DrivesTable = styled((props: GenericTableProps<Drive>) => ( const DrivesTable = styled((props: GenericTableProps<Drive>) => (
<Table<Drive> {...props} /> <Table<Drive> {...props} />
))` ))`
border-bottom: none;
[data-display='table-head'], [data-display='table-head'],
[data-display='table-body'] { [data-display='table-body'] {
> [data-display='table-row'] > [data-display='table-cell'] { > [data-display='table-row'] > [data-display='table-cell'] {
@ -303,9 +305,9 @@ export class DriveSelector extends React.Component<
case compatibility.system(): case compatibility.system():
return warning.systemDrive(); return warning.systemDrive();
case compatibility.tooSmall(): case compatibility.tooSmall():
const recommendedDriveSize = const size =
this.state.image?.recommendedDriveSize || this.state.image?.size || 0; this.state.image?.recommendedDriveSize || this.state.image?.size || 0;
return warning.unrecommendedDriveSize({ recommendedDriveSize }, drive); return warning.tooSmall({ size }, drive);
} }
} }

View File

@ -17,7 +17,6 @@
import CircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle.svg'; import CircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle.svg';
import CheckCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/check-circle.svg'; import CheckCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/check-circle.svg';
import TimesCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/times-circle.svg'; import TimesCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/times-circle.svg';
import * as _ from 'lodash';
import outdent from 'outdent'; import outdent from 'outdent';
import * as React from 'react'; import * as React from 'react';
import { Flex, FlexProps, Link, TableColumn, Txt } from 'rendition'; import { Flex, FlexProps, Link, TableColumn, Txt } from 'rendition';
@ -104,6 +103,19 @@ const columns: Array<TableColumn<FlashError>> = [
}, },
]; ];
function getEffectiveSpeed(results: {
sourceMetadata: {
size: number;
blockmappedSize?: number;
};
averageFlashingSpeed: number;
}) {
const flashedSize =
results.sourceMetadata.blockmappedSize ?? results.sourceMetadata.size;
const timeSpent = flashedSize / results.averageFlashingSpeed;
return results.sourceMetadata.size / timeSpent;
}
export function FlashResults({ export function FlashResults({
goToMain, goToMain,
image = '', image = '',
@ -117,10 +129,9 @@ export function FlashResults({
errors: FlashError[]; errors: FlashError[];
skip: boolean; skip: boolean;
results: { results: {
bytesWritten: number;
sourceMetadata: { sourceMetadata: {
size: number; size: number;
blockmappedSize: number; blockmappedSize?: number;
}; };
averageFlashingSpeed: number; averageFlashingSpeed: number;
devices: { failed: number; successful: number }; devices: { failed: number; successful: number };
@ -129,11 +140,7 @@ export function FlashResults({
const [showErrorsInfo, setShowErrorsInfo] = React.useState(false); const [showErrorsInfo, setShowErrorsInfo] = React.useState(false);
const allFailed = !skip && results.devices.successful === 0; const allFailed = !skip && results.devices.successful === 0;
const someFailed = results.devices.failed !== 0 || errors.length !== 0; const someFailed = results.devices.failed !== 0 || errors.length !== 0;
const effectiveSpeed = _.round( const effectiveSpeed = bytesToMegabytes(getEffectiveSpeed(results)).toFixed(
bytesToMegabytes(
results.sourceMetadata.size /
(results.sourceMetadata.blockmappedSize / results.averageFlashingSpeed),
),
1, 1,
); );
return ( return (

View File

@ -16,7 +16,6 @@
import GithubSvg from '@fortawesome/fontawesome-free/svgs/brands/github.svg'; import GithubSvg from '@fortawesome/fontawesome-free/svgs/brands/github.svg';
import * as _ from 'lodash'; import * as _ from 'lodash';
import * as os from 'os';
import * as React from 'react'; import * as React from 'react';
import { Flex, Checkbox, Txt } from 'rendition'; import { Flex, Checkbox, Txt } from 'rendition';
@ -26,40 +25,25 @@ import * as analytics from '../../modules/analytics';
import { open as openExternal } from '../../os/open-external/services/open-external'; import { open as openExternal } from '../../os/open-external/services/open-external';
import { Modal } from '../../styled-components'; import { Modal } from '../../styled-components';
const platform = os.platform();
interface Setting { interface Setting {
name: string; name: string;
label: string | JSX.Element; label: string | JSX.Element;
options?: {
description: string;
confirmLabel: string;
};
hide?: boolean;
} }
async function getSettingsList(): Promise<Setting[]> { async function getSettingsList(): Promise<Setting[]> {
return [ const list: Setting[] = [
{ {
name: 'errorReporting', name: 'errorReporting',
label: 'Anonymously report errors and usage statistics to balena.io', label: 'Anonymously report errors and usage statistics to balena.io',
}, },
{ ];
name: 'unmountOnSuccess', if (['appimage', 'nsis', 'dmg'].includes(packageType)) {
/** list.push({
* On Windows, "Unmounting" basically means "ejecting".
* On top of that, Windows users are usually not even
* familiar with the meaning of "unmount", which comes
* from the UNIX world.
*/
label: `${platform === 'win32' ? 'Eject' : 'Auto-unmount'} on success`,
},
{
name: 'updatesEnabled', name: 'updatesEnabled',
label: 'Auto-updates enabled', label: 'Auto-updates enabled',
hide: ['rpm', 'deb'].includes(packageType), });
}, }
]; return list;
} }
interface SettingsModalProps { interface SettingsModalProps {
@ -86,25 +70,14 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) {
})(); })();
}); });
const toggleSetting = async ( const toggleSetting = async (setting: string) => {
setting: string,
options?: Setting['options'],
) => {
const value = currentSettings[setting]; const value = currentSettings[setting];
const dangerous = options !== undefined; analytics.logEvent('Toggle setting', { setting, value });
analytics.logEvent('Toggle setting', {
setting,
value,
dangerous,
});
await settings.set(setting, !value); await settings.set(setting, !value);
setCurrentSettings({ setCurrentSettings({
...currentSettings, ...currentSettings,
[setting]: !value, [setting]: !value,
}); });
return;
}; };
return ( return (
@ -118,14 +91,14 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) {
> >
<Flex flexDirection="column"> <Flex flexDirection="column">
{settingsList.map((setting: Setting, i: number) => { {settingsList.map((setting: Setting, i: number) => {
return setting.hide ? null : ( return (
<Flex key={setting.name} mb={14}> <Flex key={setting.name} mb={14}>
<Checkbox <Checkbox
toggle toggle
tabIndex={6 + i} tabIndex={6 + i}
label={setting.label} label={setting.label}
checked={currentSettings[setting.name]} checked={currentSettings[setting.name]}
onChange={() => toggleSetting(setting.name, setting.options)} onChange={() => toggleSetting(setting.name)}
/> />
</Flex> </Flex>
); );

View File

@ -64,3 +64,19 @@ input[type="checkbox"] + div {
#rendition-tooltip-root > div { #rendition-tooltip-root > div {
font-family: "SourceSansPro", sans-serif; font-family: "SourceSansPro", sans-serif;
} }
/* HIGH-CONTRAST CHANGES */
input[type="text"],
input[type="checkbox"] ~ div,
input[type="checkbox"] ~ span {
border-color: #b5b5b5 !important;
}
[data-display="table-head"]
> [data-display="table-row"]
> [data-display="table-cell"],
[data-display="table-body"]
> [data-display="table-row"]
> [data-display="table-cell"] {
border-bottom: 1px solid #b5b5b5 !important;
}

View File

@ -85,6 +85,10 @@ export function addFailedDeviceError({
const failedDeviceErrorsMap = new Map( const failedDeviceErrorsMap = new Map(
store.getState().toJS().failedDeviceErrors, store.getState().toJS().failedDeviceErrors,
); );
if (failedDeviceErrorsMap.has(device.device)) {
// Only store the first error
return;
}
failedDeviceErrorsMap.set(device.device, { failedDeviceErrorsMap.set(device.device, {
description: device.description, description: device.description,
device: device.device, device: device.device,

View File

@ -15,7 +15,7 @@
*/ */
import * as _ from 'lodash'; import * as _ from 'lodash';
import { AnimationFunction, Color, RGBLed } from 'sys-class-rgb-led'; import { Animator, AnimationFunction, Color, RGBLed } from 'sys-class-rgb-led';
import { import {
isSourceDrive, isSourceDrive,
@ -25,30 +25,14 @@ import * as settings from './settings';
import { DEFAULT_STATE, observe } from './store'; import { DEFAULT_STATE, observe } from './store';
const leds: Map<string, RGBLed> = new Map(); const leds: Map<string, RGBLed> = new Map();
const animator = new Animator([], 10);
function setLeds( const red: Color = [0.59, 0, 0];
drivesPaths: Set<string>, const green: Color = [0, 0.59, 0];
colorOrAnimation: Color | AnimationFunction, const blue: Color = [0, 0, 0.59];
frequency?: number, const white: Color = [0.04, 0.04, 0.04];
) {
for (const path of drivesPaths) {
const led = leds.get(path);
if (led) {
if (Array.isArray(colorOrAnimation)) {
led.setStaticColor(colorOrAnimation);
} else {
led.setAnimation(colorOrAnimation, frequency);
}
}
}
}
const red: Color = [1, 0, 0];
const green: Color = [0, 1, 0];
const blue: Color = [0, 0, 1];
const white: Color = [1, 1, 1];
const black: Color = [0, 0, 0]; const black: Color = [0, 0, 0];
const purple: Color = [0.5, 0, 0.5]; const purple: Color = [0.117, 0, 0.196];
function createAnimationFunction( function createAnimationFunction(
intensityFunction: (t: number) => number, intensityFunction: (t: number) => number,
@ -61,16 +45,20 @@ function createAnimationFunction(
} }
function blink(t: number) { function blink(t: number) {
return Math.floor(t / 1000) % 2; return Math.floor(t) % 2;
} }
function breathe(t: number) { function one(_t: number) {
return (1 + Math.sin(t / 1000)) / 2; return 1;
} }
const breatheBlue = createAnimationFunction(breathe, blue);
const blinkGreen = createAnimationFunction(blink, green); const blinkGreen = createAnimationFunction(blink, green);
const blinkPurple = createAnimationFunction(blink, purple); const blinkPurple = createAnimationFunction(blink, purple);
const staticRed = createAnimationFunction(one, red);
const staticGreen = createAnimationFunction(one, green);
const staticBlue = createAnimationFunction(one, blue);
const staticWhite = createAnimationFunction(one, white);
const staticBlack = createAnimationFunction(one, black);
interface LedsState { interface LedsState {
step: 'main' | 'flashing' | 'verifying' | 'finish'; step: 'main' | 'flashing' | 'verifying' | 'finish';
@ -80,6 +68,17 @@ interface LedsState {
failedDrives: string[]; failedDrives: string[];
} }
function setLeds(animation: AnimationFunction, drivesPaths: Set<string>) {
const rgbLeds: RGBLed[] = [];
for (const path of drivesPaths) {
const led = leds.get(path);
if (led) {
rgbLeds.push(led);
}
}
return { animation, rgbLeds };
}
// Source slot (1st slot): behaves as a target unless it is chosen as source // Source slot (1st slot): behaves as a target unless it is chosen as source
// No drive: black // No drive: black
// Drive plugged: blue - on // Drive plugged: blue - on
@ -110,6 +109,7 @@ export function updateLeds({
// Remove selected devices from plugged set // Remove selected devices from plugged set
for (const d of selectedOk) { for (const d of selectedOk) {
plugged.delete(d); plugged.delete(d);
unplugged.delete(d);
} }
// Remove plugged devices from unplugged set // Remove plugged devices from unplugged set
@ -122,38 +122,42 @@ export function updateLeds({
selectedOk.delete(d); selectedOk.delete(d);
} }
const mapping: Array<{
animation: AnimationFunction;
rgbLeds: RGBLed[];
}> = [];
// Handle source slot // Handle source slot
if (sourceDrive !== undefined) { if (sourceDrive !== undefined) {
if (unplugged.has(sourceDrive)) { if (plugged.has(sourceDrive)) {
unplugged.delete(sourceDrive);
// TODO
setLeds(new Set([sourceDrive]), breatheBlue, 2);
} else if (plugged.has(sourceDrive)) {
plugged.delete(sourceDrive); plugged.delete(sourceDrive);
setLeds(new Set([sourceDrive]), blue); mapping.push(setLeds(staticBlue, new Set([sourceDrive])));
} }
} }
if (step === 'main') { if (step === 'main') {
setLeds(unplugged, black); mapping.push(
setLeds(plugged, black); setLeds(staticBlack, new Set([...unplugged, ...plugged])),
setLeds(selectedOk, white); setLeds(staticWhite, new Set([...selectedOk, ...selectedFailed])),
setLeds(selectedFailed, white); );
} else if (step === 'flashing') { } else if (step === 'flashing') {
setLeds(unplugged, black); mapping.push(
setLeds(plugged, black); setLeds(staticBlack, new Set([...unplugged, ...plugged])),
setLeds(selectedOk, blinkPurple, 2); setLeds(blinkPurple, selectedOk),
setLeds(selectedFailed, red); setLeds(staticRed, selectedFailed),
);
} else if (step === 'verifying') { } else if (step === 'verifying') {
setLeds(unplugged, black); mapping.push(
setLeds(plugged, black); setLeds(staticBlack, new Set([...unplugged, ...plugged])),
setLeds(selectedOk, blinkGreen, 2); setLeds(blinkGreen, selectedOk),
setLeds(selectedFailed, red); setLeds(staticRed, selectedFailed),
);
} else if (step === 'finish') { } else if (step === 'finish') {
setLeds(unplugged, black); mapping.push(
setLeds(plugged, black); setLeds(staticBlack, new Set([...unplugged, ...plugged])),
setLeds(selectedOk, green); setLeds(staticGreen, selectedOk),
setLeds(selectedFailed, red); setLeds(staticRed, selectedFailed),
);
} }
animator.mapping = mapping;
} }
interface DeviceFromState { interface DeviceFromState {
@ -189,7 +193,7 @@ function stateObserver(state: typeof DEFAULT_STATE) {
selectedDrivesPaths = s.devicePaths; selectedDrivesPaths = s.devicePaths;
} }
const failedDevicePaths = s.failedDeviceErrors.map( const failedDevicePaths = s.failedDeviceErrors.map(
([devicePath]: [string]) => devicePath, ([, { devicePath }]: [string, { devicePath: string }]) => devicePath,
); );
const newLedsState = { const newLedsState = {
step, step,

View File

@ -77,8 +77,7 @@ export async function writeConfigFile(
const DEFAULT_SETTINGS: _.Dictionary<any> = { const DEFAULT_SETTINGS: _.Dictionary<any> = {
errorReporting: true, errorReporting: true,
unmountOnSuccess: true, updatesEnabled: ['appimage', 'nsis', 'dmg'].includes(packageJSON.packageType),
updatesEnabled: !_.includes(['rpm', 'deb'], packageJSON.packageType),
desktopNotifications: true, desktopNotifications: true,
autoBlockmapping: true, autoBlockmapping: true,
decompressFirst: true, decompressFirst: true,

View File

@ -151,11 +151,7 @@ async function performWrite(
let cancelled = false; let cancelled = false;
let skip = false; let skip = false;
ipc.serve(); ipc.serve();
const { const { autoBlockmapping, decompressFirst } = await settings.getAll();
unmountOnSuccess,
autoBlockmapping,
decompressFirst,
} = await settings.getAll();
return await new Promise((resolve, reject) => { return await new Promise((resolve, reject) => {
ipc.server.on('error', (error) => { ipc.server.on('error', (error) => {
terminateServer(); terminateServer();
@ -174,7 +170,6 @@ async function performWrite(
driveCount: drives.length, driveCount: drives.length,
uuid: flashState.getFlashUuid(), uuid: flashState.getFlashUuid(),
flashInstanceUuid: flashState.getFlashUuid(), flashInstanceUuid: flashState.getFlashUuid(),
unmountOnSuccess,
}; };
ipc.server.on('fail', ({ device, error }) => { ipc.server.on('fail', ({ device, error }) => {
@ -211,7 +206,6 @@ async function performWrite(
destinations: drives, destinations: drives,
SourceType: image.SourceType.name, SourceType: image.SourceType.name,
autoBlockmapping, autoBlockmapping,
unmountOnSuccess,
decompressFirst, decompressFirst,
}); });
}); });
@ -290,7 +284,6 @@ export async function flash(
uuid: flashState.getFlashUuid(), uuid: flashState.getFlashUuid(),
status: 'started', status: 'started',
flashInstanceUuid: flashState.getFlashUuid(), flashInstanceUuid: flashState.getFlashUuid(),
unmountOnSuccess: await settings.get('unmountOnSuccess'),
}; };
analytics.logEvent('Flash', analyticsData); analytics.logEvent('Flash', analyticsData);
@ -345,7 +338,6 @@ export async function cancel(type: string) {
driveCount: drives.length, driveCount: drives.length,
uuid: flashState.getFlashUuid(), uuid: flashState.getFlashUuid(),
flashInstanceUuid: flashState.getFlashUuid(), flashInstanceUuid: flashState.getFlashUuid(),
unmountOnSuccess: await settings.get('unmountOnSuccess'),
status, status,
}; };
analytics.logEvent('Cancel', analyticsData); analytics.logEvent('Cancel', analyticsData);

View File

@ -167,7 +167,6 @@ async function writeAndValidate({
interface WriteOptions { interface WriteOptions {
image: SourceMetadata; image: SourceMetadata;
destinations: DrivelistDrive[]; destinations: DrivelistDrive[];
unmountOnSuccess: boolean;
autoBlockmapping: boolean; autoBlockmapping: boolean;
decompressFirst: boolean; decompressFirst: boolean;
SourceType: string; SourceType: string;
@ -257,13 +256,12 @@ ipc.connectTo(IPC_SERVER_ID, () => {
const imagePath = options.image.path; const imagePath = options.image.path;
log(`Image: ${imagePath}`); log(`Image: ${imagePath}`);
log(`Devices: ${destinations.join(', ')}`); log(`Devices: ${destinations.join(', ')}`);
log(`Umount on success: ${options.unmountOnSuccess}`);
log(`Auto blockmapping: ${options.autoBlockmapping}`); log(`Auto blockmapping: ${options.autoBlockmapping}`);
log(`Decompress first: ${options.decompressFirst}`); log(`Decompress first: ${options.decompressFirst}`);
const dests = options.destinations.map((destination) => { const dests = options.destinations.map((destination) => {
return new BlockDevice({ return new BlockDevice({
drive: destination, drive: destination,
unmountOnSuccess: options.unmountOnSuccess, unmountOnSuccess: true,
write: true, write: true,
direct: true, direct: true,
}); });

View File

@ -81,13 +81,10 @@ export const compatibility = {
} as const; } as const;
export const warning = { export const warning = {
unrecommendedDriveSize: ( tooSmall: (source: { size: number }, target: { size: number }) => {
image: { recommendedDriveSize: number },
drive: { device: string; size: number },
) => {
return outdent({ newline: ' ' })` return outdent({ newline: ' ' })`
This image recommends a ${prettyBytes(image.recommendedDriveSize)} The selected source is ${prettyBytes(source.size - target.size)}
drive, however ${drive.device} is only ${prettyBytes(drive.size)}. larger than this drive.
`; `;
}, },

18
npm-shrinkwrap.json generated
View File

@ -5864,9 +5864,9 @@
} }
}, },
"electron": { "electron": {
"version": "9.3.3", "version": "9.4.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-9.3.3.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-9.4.0.tgz",
"integrity": "sha512-xghKeUY1qgnEcJ5w2rXo/toH+8NT2Dktx2aAxBNPV7CIJr3mejJJAPwLbycwtddzr37tgKxHeHlc8ivfKtMkJQ==", "integrity": "sha512-hOC4q0jkb+UDYZRy8vrZ1IANnq+jznZnbkD62OEo06nU+hIbp2IrwDRBNuSLmQ3cwZMVir0WSIA1qEVK0PkzGA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@electron/get": "^1.0.1", "@electron/get": "^1.0.1",
@ -7357,9 +7357,9 @@
"dev": true "dev": true
}, },
"etcher-sdk": { "etcher-sdk": {
"version": "5.1.10", "version": "5.1.11",
"resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-5.1.10.tgz", "resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-5.1.11.tgz",
"integrity": "sha512-tCHY6v4txJr6+3KCIYhaLem6U3ZTEiXRtchMZiuZO6X9t2w8U5ntn6RgHbs7YIixrr/17o1aRUnqPKsXrLbDHQ==", "integrity": "sha512-aS5gbclUoBF+8NOV2R2MJN52BOYKJHc+gjlZF3xZh1hVoglVb0AtCZpFFfTk8/cDn2SvrfAv03/amXaJxJjaPQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@balena/udif": "^1.1.1", "@balena/udif": "^1.1.1",
@ -15006,9 +15006,9 @@
"dev": true "dev": true
}, },
"sys-class-rgb-led": { "sys-class-rgb-led": {
"version": "2.1.1", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/sys-class-rgb-led/-/sys-class-rgb-led-2.1.1.tgz", "resolved": "https://registry.npmjs.org/sys-class-rgb-led/-/sys-class-rgb-led-3.0.0.tgz",
"integrity": "sha512-CPx01dR22xsqqgpGQ0BcKWf1hCJNTK/Y/gK/hvNEZX5PyuvUzrCYsBWgletzlaruc47RYGi/0be+ZbkIIiQjnA==", "integrity": "sha512-e5vMYgWgDFfXMN67lbTW6niSxzm3eiD8A8hEciUtOUexfYGM6lpd6dH6bERq2LL99mmBYFSxYFZTMWHga4xe7Q==",
"dev": true "dev": true
}, },
"tapable": { "tapable": {

View File

@ -27,8 +27,8 @@
"webpack": "webpack", "webpack": "webpack",
"watch": "webpack --watch", "watch": "webpack --watch",
"concourse-build-electron": "npm run webpack", "concourse-build-electron": "npm run webpack",
"concourse-test": "npx npm@6.14.5 test", "concourse-test": "npx npm@6.14.8 test",
"concourse-test-electron": "npx npm@6.14.5 test" "concourse-test-electron": "npx npm@6.14.8 test"
}, },
"husky": { "husky": {
"hooks": { "hooks": {
@ -71,13 +71,13 @@
"css-loader": "^4.2.1", "css-loader": "^4.2.1",
"d3": "^4.13.0", "d3": "^4.13.0",
"debug": "^4.2.0", "debug": "^4.2.0",
"electron": "9.3.3", "electron": "9.4.0",
"electron-builder": "^22.9.1", "electron-builder": "^22.9.1",
"electron-mocha": "^9.3.2", "electron-mocha": "^9.3.2",
"electron-notarize": "^1.0.0", "electron-notarize": "^1.0.0",
"electron-rebuild": "^2.3.2", "electron-rebuild": "^2.3.2",
"electron-updater": "^4.3.5", "electron-updater": "^4.3.5",
"etcher-sdk": "^5.1.10", "etcher-sdk": "^5.1.11",
"file-loader": "^6.0.0", "file-loader": "^6.0.0",
"husky": "^4.2.5", "husky": "^4.2.5",
"immutable": "^3.8.1", "immutable": "^3.8.1",
@ -103,7 +103,7 @@
"string-replace-loader": "^2.3.0", "string-replace-loader": "^2.3.0",
"styled-components": "^5.1.0", "styled-components": "^5.1.0",
"sudo-prompt": "github:zvin/sudo-prompt#7cdede2f0da28fbcc2db48402d7d935f3a825c91", "sudo-prompt": "github:zvin/sudo-prompt#7cdede2f0da28fbcc2db48402d7d935f3a825c91",
"sys-class-rgb-led": "^2.1.1", "sys-class-rgb-led": "^3.0.0",
"tmp": "^0.2.1", "tmp": "^0.2.1",
"ts-loader": "^8.0.0", "ts-loader": "^8.0.0",
"ts-node": "^9.0.0", "ts-node": "^9.0.0",

View File

@ -16,7 +16,6 @@
import { expect } from 'chai'; import { expect } from 'chai';
import * as settings from '../../../lib/gui/app/models/settings';
import * as progressStatus from '../../../lib/gui/app/modules/progress-status'; import * as progressStatus from '../../../lib/gui/app/modules/progress-status';
describe('Browser: progressStatus', function () { describe('Browser: progressStatus', function () {
@ -30,8 +29,6 @@ describe('Browser: progressStatus', function () {
eta: 15, eta: 15,
speed: 100000000000000, speed: 100000000000000,
}; };
settings.set('unmountOnSuccess', true);
}); });
it('should report 0% if percentage == 0 but speed != 0', function () { it('should report 0% if percentage == 0 but speed != 0', function () {
@ -40,22 +37,14 @@ describe('Browser: progressStatus', function () {
); );
}); });
it('should handle percentage == 0, flashing, unmountOnSuccess', function () { it('should handle percentage == 0, flashing', function () {
this.state.speed = 0; this.state.speed = 0;
expect(progressStatus.titleFromFlashState(this.state)).to.equal( expect(progressStatus.titleFromFlashState(this.state)).to.equal(
'0% Flashing...', '0% Flashing...',
); );
}); });
it('should handle percentage == 0, flashing, !unmountOnSuccess', function () { it('should handle percentage == 0, verifying', function () {
this.state.speed = 0;
settings.set('unmountOnSuccess', false);
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
'0% Flashing...',
);
});
it('should handle percentage == 0, verifying, unmountOnSuccess', function () {
this.state.speed = 0; this.state.speed = 0;
this.state.type = 'verifying'; this.state.type = 'verifying';
expect(progressStatus.titleFromFlashState(this.state)).to.equal( expect(progressStatus.titleFromFlashState(this.state)).to.equal(
@ -63,31 +52,14 @@ describe('Browser: progressStatus', function () {
); );
}); });
it('should handle percentage == 0, verifying, !unmountOnSuccess', function () { it('should handle percentage == 50, flashing', function () {
this.state.speed = 0;
this.state.type = 'verifying';
settings.set('unmountOnSuccess', false);
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
'0% Validating...',
);
});
it('should handle percentage == 50, flashing, unmountOnSuccess', function () {
this.state.percentage = 50; this.state.percentage = 50;
expect(progressStatus.titleFromFlashState(this.state)).to.equal( expect(progressStatus.titleFromFlashState(this.state)).to.equal(
'50% Flashing...', '50% Flashing...',
); );
}); });
it('should handle percentage == 50, flashing, !unmountOnSuccess', function () { it('should handle percentage == 50, verifying', function () {
this.state.percentage = 50;
settings.set('unmountOnSuccess', false);
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
'50% Flashing...',
);
});
it('should handle percentage == 50, verifying, unmountOnSuccess', function () {
this.state.percentage = 50; this.state.percentage = 50;
this.state.type = 'verifying'; this.state.type = 'verifying';
expect(progressStatus.titleFromFlashState(this.state)).to.equal( expect(progressStatus.titleFromFlashState(this.state)).to.equal(
@ -95,31 +67,14 @@ describe('Browser: progressStatus', function () {
); );
}); });
it('should handle percentage == 50, verifying, !unmountOnSuccess', function () { it('should handle percentage == 100, flashing', function () {
this.state.percentage = 50;
this.state.type = 'verifying';
settings.set('unmountOnSuccess', false);
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
'50% Validating...',
);
});
it('should handle percentage == 100, flashing, unmountOnSuccess', function () {
this.state.percentage = 100; this.state.percentage = 100;
expect(progressStatus.titleFromFlashState(this.state)).to.equal( expect(progressStatus.titleFromFlashState(this.state)).to.equal(
'Finishing...', 'Finishing...',
); );
}); });
it('should handle percentage == 100, flashing, !unmountOnSuccess', function () { it('should handle percentage == 100, verifying', function () {
this.state.percentage = 100;
settings.set('unmountOnSuccess', false);
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
'Finishing...',
);
});
it('should handle percentage == 100, verifying, unmountOnSuccess', function () {
this.state.percentage = 100; this.state.percentage = 100;
this.state.type = 'verifying'; this.state.type = 'verifying';
expect(progressStatus.titleFromFlashState(this.state)).to.equal( expect(progressStatus.titleFromFlashState(this.state)).to.equal(
@ -127,9 +82,8 @@ describe('Browser: progressStatus', function () {
); );
}); });
it('should handle percentage == 100, validatinf, !unmountOnSuccess', function () { it('should handle percentage == 100, validating', function () {
this.state.percentage = 100; this.state.percentage = 100;
settings.set('unmountOnSuccess', false);
expect(progressStatus.titleFromFlashState(this.state)).to.equal( expect(progressStatus.titleFromFlashState(this.state)).to.equal(
'Finishing...', 'Finishing...',
); );