mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-29 14:16:36 +00:00
Merge pull request #3131 from balena-io/decompress-first
Decompress first
This commit is contained in:
commit
5d8a211961
2
Makefile
2
Makefile
@ -149,7 +149,7 @@ sass:
|
|||||||
node-sass lib/gui/app/scss/main.scss > lib/gui/css/main.css
|
node-sass lib/gui/app/scss/main.scss > lib/gui/css/main.css
|
||||||
|
|
||||||
lint-ts:
|
lint-ts:
|
||||||
resin-lint --fix --typescript typings lib tests scripts/clean-shrinkwrap.ts webpack.config.ts
|
balena-lint --fix --typescript typings lib tests scripts/clean-shrinkwrap.ts webpack.config.ts
|
||||||
|
|
||||||
lint-sass:
|
lint-sass:
|
||||||
sass-lint -v lib/gui/app/scss/**/*.scss lib/gui/app/scss/*.scss
|
sass-lint -v lib/gui/app/scss/**/*.scss lib/gui/app/scss/*.scss
|
||||||
|
@ -33,7 +33,6 @@ import { Actions, observe, store } from './models/store';
|
|||||||
import * as analytics from './modules/analytics';
|
import * as analytics from './modules/analytics';
|
||||||
import { scanner as driveScanner } from './modules/drive-scanner';
|
import { scanner as driveScanner } from './modules/drive-scanner';
|
||||||
import * as exceptionReporter from './modules/exception-reporter';
|
import * as exceptionReporter from './modules/exception-reporter';
|
||||||
import { updateLock } from './modules/update-lock';
|
|
||||||
import * as osDialog from './os/dialog';
|
import * as osDialog from './os/dialog';
|
||||||
import * as windowProgress from './os/window-progress';
|
import * as windowProgress from './os/window-progress';
|
||||||
import MainPage from './pages/main/MainPage';
|
import MainPage from './pages/main/MainPage';
|
||||||
@ -86,33 +85,45 @@ const currentVersion = packageJSON.version;
|
|||||||
analytics.logEvent('Application start', {
|
analytics.logEvent('Application start', {
|
||||||
packageType: packageJSON.packageType,
|
packageType: packageJSON.packageType,
|
||||||
version: currentVersion,
|
version: currentVersion,
|
||||||
applicationSessionUuid,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const debouncedLog = _.debounce(console.log, 1000, { maxWait: 1000 });
|
||||||
|
|
||||||
|
function pluralize(word: string, quantity: number) {
|
||||||
|
return `${quantity} ${word}${quantity === 1 ? '' : 's'}`;
|
||||||
|
}
|
||||||
|
|
||||||
observe(() => {
|
observe(() => {
|
||||||
if (!flashState.isFlashing()) {
|
if (!flashState.isFlashing()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentFlashState = flashState.getFlashState();
|
const currentFlashState = flashState.getFlashState();
|
||||||
const stateType =
|
windowProgress.set(currentFlashState);
|
||||||
!currentFlashState.flashing && currentFlashState.verifying
|
|
||||||
? `Verifying ${currentFlashState.verifying}`
|
|
||||||
: `Flashing ${currentFlashState.flashing}`;
|
|
||||||
|
|
||||||
|
let eta = '';
|
||||||
|
if (currentFlashState.eta !== undefined) {
|
||||||
|
eta = `eta in ${currentFlashState.eta.toFixed(0)}s`;
|
||||||
|
}
|
||||||
|
let active = '';
|
||||||
|
if (currentFlashState.type !== 'decompressing') {
|
||||||
|
active = pluralize('device', currentFlashState.active);
|
||||||
|
}
|
||||||
// NOTE: There is usually a short time period between the `isFlashing()`
|
// NOTE: There is usually a short time period between the `isFlashing()`
|
||||||
// property being set, and the flashing actually starting, which
|
// property being set, and the flashing actually starting, which
|
||||||
// might cause some non-sense flashing state logs including
|
// might cause some non-sense flashing state logs including
|
||||||
// `undefined` values.
|
// `undefined` values.
|
||||||
analytics.logDebug(
|
debouncedLog(outdent({ newline: ' ' })`
|
||||||
`${stateType} devices, ` +
|
${_.capitalize(currentFlashState.type)}
|
||||||
`${currentFlashState.percentage}% at ${currentFlashState.speed} MB/s ` +
|
${active},
|
||||||
`(total ${currentFlashState.totalSpeed} MB/s) ` +
|
${currentFlashState.percentage}%
|
||||||
`eta in ${currentFlashState.eta}s ` +
|
at
|
||||||
`with ${currentFlashState.failed} failed devices`,
|
${(currentFlashState.speed || 0).toFixed(2)}
|
||||||
);
|
MB/s
|
||||||
|
(total ${(currentFlashState.speed * currentFlashState.active).toFixed(2)} MB/s)
|
||||||
windowProgress.set(currentFlashState);
|
${eta}
|
||||||
|
with
|
||||||
|
${pluralize('failed device', currentFlashState.failed)}
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -154,17 +165,16 @@ const COMPUTE_MODULE_DESCRIPTIONS: _.Dictionary<string> = {
|
|||||||
[USB_PRODUCT_ID_BCM2710_BOOT]: 'Compute Module 3',
|
[USB_PRODUCT_ID_BCM2710_BOOT]: 'Compute Module 3',
|
||||||
};
|
};
|
||||||
|
|
||||||
let BLACKLISTED_DRIVES: string[] = [];
|
async function driveIsAllowed(drive: {
|
||||||
|
|
||||||
function driveIsAllowed(drive: {
|
|
||||||
devicePath: string;
|
devicePath: string;
|
||||||
device: string;
|
device: string;
|
||||||
raw: string;
|
raw: string;
|
||||||
}) {
|
}) {
|
||||||
|
const driveBlacklist = (await settings.get('driveBlacklist')) || [];
|
||||||
return !(
|
return !(
|
||||||
BLACKLISTED_DRIVES.includes(drive.devicePath) ||
|
driveBlacklist.includes(drive.devicePath) ||
|
||||||
BLACKLISTED_DRIVES.includes(drive.device) ||
|
driveBlacklist.includes(drive.device) ||
|
||||||
BLACKLISTED_DRIVES.includes(drive.raw)
|
driveBlacklist.includes(drive.raw)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +195,7 @@ function prepareDrive(drive: Drive) {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
drive.progress = 0;
|
drive.progress = 0;
|
||||||
drive.disabled = true;
|
drive.disabled = true;
|
||||||
drive.on('progress', progress => {
|
drive.on('progress', (progress) => {
|
||||||
updateDriveProgress(drive, progress);
|
updateDriveProgress(drive, progress);
|
||||||
});
|
});
|
||||||
return drive;
|
return drive;
|
||||||
@ -229,9 +239,9 @@ function getDrives() {
|
|||||||
return _.keyBy(availableDrives.getDrives() || [], 'device');
|
return _.keyBy(availableDrives.getDrives() || [], 'device');
|
||||||
}
|
}
|
||||||
|
|
||||||
function addDrive(drive: Drive) {
|
async function addDrive(drive: Drive) {
|
||||||
const preparedDrive = prepareDrive(drive);
|
const preparedDrive = prepareDrive(drive);
|
||||||
if (!driveIsAllowed(preparedDrive)) {
|
if (!(await driveIsAllowed(preparedDrive))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const drives = getDrives();
|
const drives = getDrives();
|
||||||
@ -262,7 +272,7 @@ function updateDriveProgress(
|
|||||||
driveScanner.on('attach', addDrive);
|
driveScanner.on('attach', addDrive);
|
||||||
driveScanner.on('detach', removeDrive);
|
driveScanner.on('detach', removeDrive);
|
||||||
|
|
||||||
driveScanner.on('error', error => {
|
driveScanner.on('error', (error) => {
|
||||||
// Stop the drive scanning loop in case of errors,
|
// Stop the drive scanning loop in case of errors,
|
||||||
// otherwise we risk presenting the same error over
|
// otherwise we risk presenting the same error over
|
||||||
// and over again to the user, while also heavily
|
// and over again to the user, while also heavily
|
||||||
@ -276,11 +286,10 @@ driveScanner.start();
|
|||||||
|
|
||||||
let popupExists = false;
|
let popupExists = false;
|
||||||
|
|
||||||
window.addEventListener('beforeunload', async event => {
|
window.addEventListener('beforeunload', async (event) => {
|
||||||
if (!flashState.isFlashing() || popupExists) {
|
if (!flashState.isFlashing() || popupExists) {
|
||||||
analytics.logEvent('Close application', {
|
analytics.logEvent('Close application', {
|
||||||
isFlashing: flashState.isFlashing(),
|
isFlashing: flashState.isFlashing(),
|
||||||
applicationSessionUuid,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -291,10 +300,7 @@ window.addEventListener('beforeunload', async event => {
|
|||||||
// Don't open any more popups
|
// Don't open any more popups
|
||||||
popupExists = true;
|
popupExists = true;
|
||||||
|
|
||||||
analytics.logEvent('Close attempt while flashing', {
|
analytics.logEvent('Close attempt while flashing');
|
||||||
applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const confirmed = await osDialog.showWarning({
|
const confirmed = await osDialog.showWarning({
|
||||||
@ -306,8 +312,6 @@ window.addEventListener('beforeunload', async event => {
|
|||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
analytics.logEvent('Close confirmed while flashing', {
|
analytics.logEvent('Close confirmed while flashing', {
|
||||||
flashInstanceUuid: flashState.getFlashUuid(),
|
flashInstanceUuid: flashState.getFlashUuid(),
|
||||||
applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// This circumvents the 'beforeunload' event unlike
|
// This circumvents the 'beforeunload' event unlike
|
||||||
@ -325,24 +329,8 @@ window.addEventListener('beforeunload', async event => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function extendLock() {
|
async function main() {
|
||||||
updateLock.extend();
|
await ledsInit();
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('click', extendLock);
|
|
||||||
window.addEventListener('touchstart', extendLock);
|
|
||||||
|
|
||||||
// Initial update lock acquisition
|
|
||||||
extendLock();
|
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
|
||||||
try {
|
|
||||||
await settings.load();
|
|
||||||
} catch (error) {
|
|
||||||
exceptionReporter.report(error);
|
|
||||||
}
|
|
||||||
BLACKLISTED_DRIVES = settings.get('driveBlacklist') || [];
|
|
||||||
ledsInit();
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
React.createElement(MainPage),
|
React.createElement(MainPage),
|
||||||
document.getElementById('main'),
|
document.getElementById('main'),
|
||||||
|
@ -49,8 +49,6 @@ function toggleDrive(drive: DrivelistDrive) {
|
|||||||
analytics.logEvent('Toggle drive', {
|
analytics.logEvent('Toggle drive', {
|
||||||
drive,
|
drive,
|
||||||
previouslySelected: selectionState.isDriveSelected(drive.device),
|
previouslySelected: selectionState.isDriveSelected(drive.device),
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
selectionState.toggleDrive(drive.device);
|
selectionState.toggleDrive(drive.device);
|
||||||
@ -113,8 +111,6 @@ export function DriveSelectorModal({ close }: { close: () => void }) {
|
|||||||
if (drive.link) {
|
if (drive.link) {
|
||||||
analytics.logEvent('Open driver link modal', {
|
analytics.logEvent('Open driver link modal', {
|
||||||
url: drive.link,
|
url: drive.link,
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
});
|
||||||
setMissingDriversModal({ drive });
|
setMissingDriversModal({ drive });
|
||||||
}
|
}
|
||||||
@ -131,10 +127,7 @@ export function DriveSelectorModal({ close }: { close: () => void }) {
|
|||||||
if (canChangeDriveSelectionState) {
|
if (canChangeDriveSelectionState) {
|
||||||
selectionState.selectDrive(drive.device);
|
selectionState.selectDrive(drive.device);
|
||||||
|
|
||||||
analytics.logEvent('Drive selected (double click)', {
|
analytics.logEvent('Drive selected (double click)');
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
@ -190,7 +183,7 @@ export function DriveSelectorModal({ close }: { close: () => void }) {
|
|||||||
<div
|
<div
|
||||||
className="list-group-item-section list-group-item-section-expanded"
|
className="list-group-item-section list-group-item-section-expanded"
|
||||||
tabIndex={15 + index}
|
tabIndex={15 + index}
|
||||||
onKeyPress={evt => keyboardToggleDrive(drive, evt)}
|
onKeyPress={(evt) => keyboardToggleDrive(drive, evt)}
|
||||||
>
|
>
|
||||||
<h6 className="list-group-item-heading">
|
<h6 className="list-group-item-heading">
|
||||||
{drive.description}
|
{drive.description}
|
||||||
|
@ -34,15 +34,15 @@ import {
|
|||||||
} from '../../styled-components';
|
} from '../../styled-components';
|
||||||
import { middleEllipsis } from '../../utils/middle-ellipsis';
|
import { middleEllipsis } from '../../utils/middle-ellipsis';
|
||||||
|
|
||||||
const TargetDetail = styled(props => <Txt.span {...props}></Txt.span>)`
|
const TargetDetail = styled((props) => <Txt.span {...props}></Txt.span>)`
|
||||||
float: ${({ float }) => float};
|
float: ${({ float }) => float};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface TargetSelectorProps {
|
interface TargetSelectorProps {
|
||||||
targets: any[];
|
targets: any[];
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
openDriveSelector: () => any;
|
openDriveSelector: () => void;
|
||||||
reselectDrive: () => any;
|
reselectDrive: () => void;
|
||||||
flashing: boolean;
|
flashing: boolean;
|
||||||
show: boolean;
|
show: boolean;
|
||||||
tooltip: string;
|
tooltip: string;
|
||||||
|
@ -37,10 +37,10 @@ export class FeaturedProject extends React.Component<
|
|||||||
this.state = { endpoint: null };
|
this.state = { endpoint: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public async componentDidMount() {
|
||||||
try {
|
try {
|
||||||
const endpoint =
|
const endpoint =
|
||||||
settings.get('featuredProjectEndpoint') ||
|
(await settings.get('featuredProjectEndpoint')) ||
|
||||||
'https://assets.balena.io/etcher-featured/index.html';
|
'https://assets.balena.io/etcher-featured/index.html';
|
||||||
this.setState({ endpoint });
|
this.setState({ endpoint });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -22,29 +22,14 @@ import * as flashState from '../../models/flash-state';
|
|||||||
import * as selectionState from '../../models/selection-state';
|
import * as selectionState from '../../models/selection-state';
|
||||||
import { store } from '../../models/store';
|
import { store } from '../../models/store';
|
||||||
import * as analytics from '../../modules/analytics';
|
import * as analytics from '../../modules/analytics';
|
||||||
import { updateLock } from '../../modules/update-lock';
|
|
||||||
import { open as openExternal } from '../../os/open-external/services/open-external';
|
import { open as openExternal } from '../../os/open-external/services/open-external';
|
||||||
import { FlashAnother } from '../flash-another/flash-another';
|
import { FlashAnother } from '../flash-another/flash-another';
|
||||||
import { FlashResults } from '../flash-results/flash-results';
|
import { FlashResults } from '../flash-results/flash-results';
|
||||||
import { SVGIcon } from '../svg-icon/svg-icon';
|
import { SVGIcon } from '../svg-icon/svg-icon';
|
||||||
|
|
||||||
const restart = (options: any, goToMain: () => void) => {
|
function restart(goToMain: () => void) {
|
||||||
const {
|
|
||||||
applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid,
|
|
||||||
} = store.getState().toJS();
|
|
||||||
if (!options.preserveImage) {
|
|
||||||
selectionState.deselectImage();
|
|
||||||
}
|
|
||||||
selectionState.deselectAllDrives();
|
selectionState.deselectAllDrives();
|
||||||
analytics.logEvent('Restart', {
|
analytics.logEvent('Restart');
|
||||||
...options,
|
|
||||||
applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Re-enable lock release on inactivity
|
|
||||||
updateLock.resume();
|
|
||||||
|
|
||||||
// Reset the flashing workflow uuid
|
// Reset the flashing workflow uuid
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
@ -53,17 +38,17 @@ const restart = (options: any, goToMain: () => void) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
goToMain();
|
goToMain();
|
||||||
};
|
}
|
||||||
|
|
||||||
const formattedErrors = () => {
|
function formattedErrors() {
|
||||||
const errors = _.map(
|
const errors = _.map(
|
||||||
_.get(flashState.getFlashResults(), ['results', 'errors']),
|
_.get(flashState.getFlashResults(), ['results', 'errors']),
|
||||||
error => {
|
(error) => {
|
||||||
return `${error.device}: ${error.message || error.code}`;
|
return `${error.device}: ${error.message || error.code}`;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return errors.join('\n');
|
return errors.join('\n');
|
||||||
};
|
}
|
||||||
|
|
||||||
function FinishPage({ goToMain }: { goToMain: () => void }) {
|
function FinishPage({ goToMain }: { goToMain: () => void }) {
|
||||||
const results = flashState.getFlashResults().results || {};
|
const results = flashState.getFlashResults().results || {};
|
||||||
@ -74,8 +59,10 @@ function FinishPage({ goToMain }: { goToMain: () => void }) {
|
|||||||
<FlashResults results={results} errors={formattedErrors()} />
|
<FlashResults results={results} errors={formattedErrors()} />
|
||||||
|
|
||||||
<FlashAnother
|
<FlashAnother
|
||||||
onClick={(options: any) => restart(options, goToMain)}
|
onClick={() => {
|
||||||
></FlashAnother>
|
restart(goToMain);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="box center">
|
<div className="box center">
|
||||||
|
@ -26,17 +26,14 @@ const Div = styled.div<any>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export interface FlashAnotherProps {
|
export interface FlashAnotherProps {
|
||||||
onClick: (options: { preserveImage: boolean }) => void;
|
onClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FlashAnother = (props: FlashAnotherProps) => {
|
export const FlashAnother = (props: FlashAnotherProps) => {
|
||||||
return (
|
return (
|
||||||
<ThemedProvider>
|
<ThemedProvider>
|
||||||
<Div position="absolute" right="152px">
|
<Div position="absolute" right="152px">
|
||||||
<BaseButton
|
<BaseButton primary onClick={props.onClick}>
|
||||||
primary
|
|
||||||
onClick={props.onClick.bind(null, { preserveImage: true })}
|
|
||||||
>
|
|
||||||
Flash Another
|
Flash Another
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</Div>
|
</Div>
|
||||||
|
@ -14,39 +14,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as Color from 'color';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ProgressBar } from 'rendition';
|
import { ProgressBar } from 'rendition';
|
||||||
import { css, default as styled, keyframes } from 'styled-components';
|
import { default as styled } from 'styled-components';
|
||||||
|
|
||||||
import { StepButton, StepSelection } from '../../styled-components';
|
import { StepButton } from '../../styled-components';
|
||||||
import { colors } from '../../theme';
|
|
||||||
|
|
||||||
const darkenForegroundStripes = 0.18;
|
|
||||||
const desaturateForegroundStripes = 0.2;
|
|
||||||
const progressButtonStripesForegroundColor = Color(colors.primary.background)
|
|
||||||
.darken(darkenForegroundStripes)
|
|
||||||
.desaturate(desaturateForegroundStripes)
|
|
||||||
.string();
|
|
||||||
|
|
||||||
const desaturateBackgroundStripes = 0.05;
|
|
||||||
const progressButtonStripesBackgroundColor = Color(colors.primary.background)
|
|
||||||
.desaturate(desaturateBackgroundStripes)
|
|
||||||
.string();
|
|
||||||
|
|
||||||
const ProgressButtonStripes = keyframes`
|
|
||||||
0% {
|
|
||||||
background-position: 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
background-position: 20px 20px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ProgressButtonStripesRule = css`
|
|
||||||
${ProgressButtonStripes} 1s linear infinite;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const FlashProgressBar = styled(ProgressBar)`
|
const FlashProgressBar = styled(ProgressBar)`
|
||||||
> div {
|
> div {
|
||||||
@ -54,6 +26,10 @@ const FlashProgressBar = styled(ProgressBar)`
|
|||||||
height: 48px;
|
height: 48px;
|
||||||
color: white !important;
|
color: white !important;
|
||||||
text-shadow: none !important;
|
text-shadow: none !important;
|
||||||
|
transition-duration: 0s;
|
||||||
|
> div {
|
||||||
|
transition-duration: 0s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
width: 200px;
|
width: 200px;
|
||||||
@ -61,86 +37,47 @@ const FlashProgressBar = styled(ProgressBar)`
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 48px;
|
line-height: 48px;
|
||||||
|
|
||||||
background: ${Color(colors.warning.background)
|
background: #2f3033;
|
||||||
.darken(darkenForegroundStripes)
|
|
||||||
.string()};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const FlashProgressBarValidating = styled(FlashProgressBar)`
|
|
||||||
// Notice that we add 0.01 to certain gradient stop positions.
|
|
||||||
// That workarounds a Chrome rendering issue where diagonal
|
|
||||||
// lines look spiky.
|
|
||||||
// See https://github.com/balena-io/etcher/issues/472
|
|
||||||
|
|
||||||
background-image: -webkit-gradient(
|
|
||||||
linear,
|
|
||||||
0 0,
|
|
||||||
100% 100%,
|
|
||||||
color-stop(0.25, ${progressButtonStripesForegroundColor}),
|
|
||||||
color-stop(0.26, ${progressButtonStripesBackgroundColor}),
|
|
||||||
color-stop(0.5, ${progressButtonStripesBackgroundColor}),
|
|
||||||
color-stop(0.51, ${progressButtonStripesForegroundColor}),
|
|
||||||
color-stop(0.75, ${progressButtonStripesForegroundColor}),
|
|
||||||
color-stop(0.76, ${progressButtonStripesBackgroundColor}),
|
|
||||||
to(${progressButtonStripesBackgroundColor})
|
|
||||||
);
|
|
||||||
|
|
||||||
background-color: white;
|
|
||||||
|
|
||||||
animation: ${ProgressButtonStripesRule};
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
background-size: 20px 20px;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface ProgressButtonProps {
|
interface ProgressButtonProps {
|
||||||
striped: boolean;
|
type: 'decompressing' | 'flashing' | 'verifying';
|
||||||
active: boolean;
|
active: boolean;
|
||||||
percentage: number;
|
percentage: number;
|
||||||
label: string;
|
label: string;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
callback: () => any;
|
callback: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const colors = {
|
||||||
|
decompressing: '#00aeef',
|
||||||
|
flashing: '#da60ff',
|
||||||
|
verifying: '#1ac135',
|
||||||
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Progress Button component
|
* Progress Button component
|
||||||
*/
|
*/
|
||||||
export class ProgressButton extends React.Component<ProgressButtonProps> {
|
export class ProgressButton extends React.Component<ProgressButtonProps> {
|
||||||
public render() {
|
public render() {
|
||||||
if (this.props.active) {
|
if (this.props.active) {
|
||||||
if (this.props.striped) {
|
|
||||||
return (
|
|
||||||
<StepSelection>
|
|
||||||
<FlashProgressBarValidating
|
|
||||||
primary
|
|
||||||
emphasized
|
|
||||||
value={this.props.percentage}
|
|
||||||
>
|
|
||||||
{this.props.label}
|
|
||||||
</FlashProgressBarValidating>
|
|
||||||
</StepSelection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StepSelection>
|
<FlashProgressBar
|
||||||
<FlashProgressBar warning emphasized value={this.props.percentage}>
|
background={colors[this.props.type]}
|
||||||
{this.props.label}
|
value={this.props.percentage}
|
||||||
</FlashProgressBar>
|
|
||||||
</StepSelection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StepSelection>
|
|
||||||
<StepButton
|
|
||||||
primary
|
|
||||||
onClick={this.props.callback}
|
|
||||||
disabled={this.props.disabled}
|
|
||||||
>
|
>
|
||||||
{this.props.label}
|
{this.props.label}
|
||||||
</StepButton>
|
</FlashProgressBar>
|
||||||
</StepSelection>
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<StepButton
|
||||||
|
primary
|
||||||
|
onClick={this.props.callback}
|
||||||
|
disabled={this.props.disabled}
|
||||||
|
>
|
||||||
|
{this.props.label}
|
||||||
|
</StepButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ import * as React from 'react';
|
|||||||
|
|
||||||
import * as packageJSON from '../../../../../package.json';
|
import * as packageJSON from '../../../../../package.json';
|
||||||
import * as settings from '../../models/settings';
|
import * as settings from '../../models/settings';
|
||||||
import { store } from '../../models/store';
|
|
||||||
import * as analytics from '../../modules/analytics';
|
import * as analytics from '../../modules/analytics';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,7 +91,7 @@ export class SafeWebview extends React.PureComponent<
|
|||||||
url.searchParams.set(API_VERSION_PARAM, API_VERSION);
|
url.searchParams.set(API_VERSION_PARAM, API_VERSION);
|
||||||
url.searchParams.set(
|
url.searchParams.set(
|
||||||
OPT_OUT_ANALYTICS_PARAM,
|
OPT_OUT_ANALYTICS_PARAM,
|
||||||
(!settings.get('errorReporting')).toString(),
|
(!settings.getSync('errorReporting')).toString(),
|
||||||
);
|
);
|
||||||
this.entryHref = url.href;
|
this.entryHref = url.href;
|
||||||
// Events steal 'this'
|
// Events steal 'this'
|
||||||
@ -182,11 +181,7 @@ export class SafeWebview extends React.PureComponent<
|
|||||||
// only care about this event if it's a request for the main frame
|
// only care about this event if it's a request for the main frame
|
||||||
if (event.resourceType === 'mainFrame') {
|
if (event.resourceType === 'mainFrame') {
|
||||||
const HTTP_OK = 200;
|
const HTTP_OK = 200;
|
||||||
analytics.logEvent('SafeWebview loaded', {
|
analytics.logEvent('SafeWebview loaded', { event });
|
||||||
event,
|
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
this.setState({
|
this.setState({
|
||||||
shouldShow: event.statusCode === HTTP_OK,
|
shouldShow: event.statusCode === HTTP_OK,
|
||||||
});
|
});
|
||||||
@ -197,15 +192,13 @@ export class SafeWebview extends React.PureComponent<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Open link in browser if it's opened as a 'foreground-tab'
|
// Open link in browser if it's opened as a 'foreground-tab'
|
||||||
public static newWindow(event: electron.NewWindowEvent) {
|
public static async newWindow(event: electron.NewWindowEvent) {
|
||||||
const url = new window.URL(event.url);
|
const url = new window.URL(event.url);
|
||||||
if (
|
if (
|
||||||
_.every([
|
(url.protocol === 'http:' || url.protocol === 'https:') &&
|
||||||
url.protocol === 'http:' || url.protocol === 'https:',
|
event.disposition === 'foreground-tab' &&
|
||||||
event.disposition === 'foreground-tab',
|
// Don't open links if they're disabled by the env var
|
||||||
// Don't open links if they're disabled by the env var
|
!(await settings.get('disableExternalLinks'))
|
||||||
!settings.get('disableExternalLinks'),
|
|
||||||
])
|
|
||||||
) {
|
) {
|
||||||
electron.shell.openExternal(url.href);
|
electron.shell.openExternal(url.href);
|
||||||
}
|
}
|
||||||
|
@ -20,15 +20,12 @@ import * as _ from 'lodash';
|
|||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Badge, Checkbox, Modal } from 'rendition';
|
import { Badge, Checkbox, Modal } from 'rendition';
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import { version } from '../../../../../package.json';
|
import { version } from '../../../../../package.json';
|
||||||
import * as settings from '../../models/settings';
|
import * as settings from '../../models/settings';
|
||||||
import { store } from '../../models/store';
|
|
||||||
import * as analytics from '../../modules/analytics';
|
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';
|
||||||
|
|
||||||
const { useState } = React;
|
|
||||||
const platform = os.platform();
|
const platform = os.platform();
|
||||||
|
|
||||||
interface WarningModalProps {
|
interface WarningModalProps {
|
||||||
@ -64,159 +61,174 @@ const WarningModal = ({
|
|||||||
interface Setting {
|
interface Setting {
|
||||||
name: string;
|
name: string;
|
||||||
label: string | JSX.Element;
|
label: string | JSX.Element;
|
||||||
options?: any;
|
options?: {
|
||||||
|
description: string;
|
||||||
|
confirmLabel: string;
|
||||||
|
};
|
||||||
hide?: boolean;
|
hide?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsList: Setting[] = [
|
async function getSettingsList(): Promise<Setting[]> {
|
||||||
{
|
return [
|
||||||
name: 'errorReporting',
|
{
|
||||||
label: 'Anonymously report errors and usage statistics to balena.io',
|
name: 'errorReporting',
|
||||||
},
|
label: 'Anonymously report errors and usage statistics to balena.io',
|
||||||
{
|
|
||||||
name: 'unmountOnSuccess',
|
|
||||||
/**
|
|
||||||
* 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: 'validateWriteOnSuccess',
|
|
||||||
label: 'Validate write on success',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'trim',
|
|
||||||
label: 'Trim ext{2,3,4} partitions before writing (raw images only)',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'updatesEnabled',
|
|
||||||
label: 'Auto-updates enabled',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'unsafeMode',
|
|
||||||
label: (
|
|
||||||
<span>
|
|
||||||
Unsafe mode{' '}
|
|
||||||
<Badge danger fontSize={12}>
|
|
||||||
Dangerous
|
|
||||||
</Badge>
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
options: {
|
|
||||||
description: `Are you sure you want to turn this on?
|
|
||||||
You will be able to overwrite your system drives if you're not careful.`,
|
|
||||||
confirmLabel: 'Enable unsafe mode',
|
|
||||||
},
|
},
|
||||||
hide: settings.get('disableUnsafeMode'),
|
{
|
||||||
},
|
name: 'unmountOnSuccess',
|
||||||
];
|
/**
|
||||||
|
* 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: 'validateWriteOnSuccess',
|
||||||
|
label: 'Validate write on success',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'updatesEnabled',
|
||||||
|
label: 'Auto-updates enabled',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'unsafeMode',
|
||||||
|
label: (
|
||||||
|
<span>
|
||||||
|
Unsafe mode{' '}
|
||||||
|
<Badge danger fontSize={12}>
|
||||||
|
Dangerous
|
||||||
|
</Badge>
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
options: {
|
||||||
|
description: `Are you sure you want to turn this on?
|
||||||
|
You will be able to overwrite your system drives if you're not careful.`,
|
||||||
|
confirmLabel: 'Enable unsafe mode',
|
||||||
|
},
|
||||||
|
hide: await settings.get('disableUnsafeMode'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Warning {
|
||||||
|
setting: string;
|
||||||
|
settingValue: boolean;
|
||||||
|
description: string;
|
||||||
|
confirmLabel: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface SettingsModalProps {
|
interface SettingsModalProps {
|
||||||
toggleModal: (value: boolean) => void;
|
toggleModal: (value: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SettingsModal: any = styled(
|
export function SettingsModal({ toggleModal }: SettingsModalProps) {
|
||||||
({ toggleModal }: SettingsModalProps) => {
|
const [settingsList, setCurrentSettingsList] = React.useState<Setting[]>([]);
|
||||||
const [currentSettings, setCurrentSettings]: [
|
React.useEffect(() => {
|
||||||
_.Dictionary<any>,
|
(async () => {
|
||||||
React.Dispatch<React.SetStateAction<_.Dictionary<any>>>,
|
if (settingsList.length === 0) {
|
||||||
] = useState(settings.getAll());
|
setCurrentSettingsList(await getSettingsList());
|
||||||
const [warning, setWarning]: [
|
|
||||||
any,
|
|
||||||
React.Dispatch<React.SetStateAction<any>>,
|
|
||||||
] = useState({});
|
|
||||||
|
|
||||||
const toggleSetting = async (setting: string, options?: any) => {
|
|
||||||
const value = currentSettings[setting];
|
|
||||||
const dangerous = !_.isUndefined(options);
|
|
||||||
|
|
||||||
analytics.logEvent('Toggle setting', {
|
|
||||||
setting,
|
|
||||||
value,
|
|
||||||
dangerous,
|
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (value || !dangerous) {
|
|
||||||
await settings.set(setting, !value);
|
|
||||||
setCurrentSettings({
|
|
||||||
...currentSettings,
|
|
||||||
[setting]: !value,
|
|
||||||
});
|
|
||||||
setWarning({});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
const [currentSettings, setCurrentSettings] = React.useState<
|
||||||
|
_.Dictionary<boolean>
|
||||||
|
>({});
|
||||||
|
React.useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
if (_.isEmpty(currentSettings)) {
|
||||||
|
setCurrentSettings(await settings.getAll());
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
const [warning, setWarning] = React.useState<Warning | undefined>(undefined);
|
||||||
|
|
||||||
|
const toggleSetting = async (
|
||||||
|
setting: string,
|
||||||
|
options?: Setting['options'],
|
||||||
|
) => {
|
||||||
|
const value = currentSettings[setting];
|
||||||
|
const dangerous = options !== undefined;
|
||||||
|
|
||||||
|
analytics.logEvent('Toggle setting', {
|
||||||
|
setting,
|
||||||
|
value,
|
||||||
|
dangerous,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (value || options === undefined) {
|
||||||
|
await settings.set(setting, !value);
|
||||||
|
setCurrentSettings({
|
||||||
|
...currentSettings,
|
||||||
|
[setting]: !value,
|
||||||
|
});
|
||||||
|
setWarning(undefined);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
// Show warning since it's a dangerous setting
|
// Show warning since it's a dangerous setting
|
||||||
setWarning({
|
setWarning({
|
||||||
setting,
|
setting,
|
||||||
settingValue: value,
|
settingValue: value,
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
id="settings-modal"
|
id="settings-modal"
|
||||||
title="Settings"
|
title="Settings"
|
||||||
done={() => toggleModal(false)}
|
done={() => toggleModal(false)}
|
||||||
style={{
|
style={{
|
||||||
width: 780,
|
width: 780,
|
||||||
height: 420,
|
height: 420,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<div>
|
||||||
|
{_.map(settingsList, (setting: Setting, i: number) => {
|
||||||
|
return setting.hide ? null : (
|
||||||
|
<div key={setting.name}>
|
||||||
|
<Checkbox
|
||||||
|
toggle
|
||||||
|
tabIndex={6 + i}
|
||||||
|
label={setting.label}
|
||||||
|
checked={currentSettings[setting.name]}
|
||||||
|
onChange={() => toggleSetting(setting.name, setting.options)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
<div>
|
<div>
|
||||||
{_.map(settingsList, (setting: Setting, i: number) => {
|
<span
|
||||||
return setting.hide ? null : (
|
onClick={() =>
|
||||||
<div key={setting.name}>
|
openExternal(
|
||||||
<Checkbox
|
'https://github.com/balena-io/etcher/blob/master/CHANGELOG.md',
|
||||||
toggle
|
)
|
||||||
tabIndex={6 + i}
|
}
|
||||||
label={setting.label}
|
>
|
||||||
checked={currentSettings[setting.name]}
|
<FontAwesomeIcon icon={faGithub} /> {version}
|
||||||
onChange={() => toggleSetting(setting.name, setting.options)}
|
</span>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<div>
|
|
||||||
<span
|
|
||||||
onClick={() =>
|
|
||||||
openExternal(
|
|
||||||
'https://github.com/balena-io/etcher/blob/master/CHANGELOG.md',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faGithub} /> {version}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{_.isEmpty(warning) ? null : (
|
{warning === undefined ? null : (
|
||||||
<WarningModal
|
<WarningModal
|
||||||
message={warning.description}
|
message={warning.description}
|
||||||
confirmLabel={warning.confirmLabel}
|
confirmLabel={warning.confirmLabel}
|
||||||
done={() => {
|
done={async () => {
|
||||||
settings.set(warning.setting, !warning.settingValue);
|
await settings.set(warning.setting, !warning.settingValue);
|
||||||
setCurrentSettings({
|
setCurrentSettings({
|
||||||
...currentSettings,
|
...currentSettings,
|
||||||
[warning.setting]: true,
|
[warning.setting]: true,
|
||||||
});
|
});
|
||||||
setWarning({});
|
setWarning(undefined);
|
||||||
}}
|
}}
|
||||||
cancel={() => {
|
cancel={() => {
|
||||||
setWarning({});
|
setWarning(undefined);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
)`
|
|
||||||
> div:nth-child(3) {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
@ -29,7 +29,7 @@ import * as messages from '../../../../shared/messages';
|
|||||||
import * as supportedFormats from '../../../../shared/supported-formats';
|
import * as supportedFormats from '../../../../shared/supported-formats';
|
||||||
import * as shared from '../../../../shared/units';
|
import * as shared from '../../../../shared/units';
|
||||||
import * as selectionState from '../../models/selection-state';
|
import * as selectionState from '../../models/selection-state';
|
||||||
import { observe, store } from '../../models/store';
|
import { observe } from '../../models/store';
|
||||||
import * as analytics from '../../modules/analytics';
|
import * as analytics from '../../modules/analytics';
|
||||||
import * as exceptionReporter from '../../modules/exception-reporter';
|
import * as exceptionReporter from '../../modules/exception-reporter';
|
||||||
import * as osDialog from '../../os/dialog';
|
import * as osDialog from '../../os/dialog';
|
||||||
@ -148,7 +148,7 @@ const URLSelector = ({ done }: { done: (imageURL: string) => void }) => {
|
|||||||
Recent
|
Recent
|
||||||
<Card
|
<Card
|
||||||
style={{ padding: '10px 15px' }}
|
style={{ padding: '10px 15px' }}
|
||||||
rows={_.map(recentImages, recent => (
|
rows={_.map(recentImages, (recent) => (
|
||||||
<Txt
|
<Txt
|
||||||
key={recent}
|
key={recent}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -254,8 +254,6 @@ export class SourceSelector extends React.Component<
|
|||||||
private reselectImage() {
|
private reselectImage() {
|
||||||
analytics.logEvent('Reselect image', {
|
analytics.logEvent('Reselect image', {
|
||||||
previousImage: selectionState.getImage(),
|
previousImage: selectionState.getImage(),
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
selectionState.deselectImage();
|
selectionState.deselectImage();
|
||||||
@ -275,17 +273,7 @@ export class SourceSelector extends React.Component<
|
|||||||
});
|
});
|
||||||
|
|
||||||
osDialog.showError(invalidImageError);
|
osDialog.showError(invalidImageError);
|
||||||
analytics.logEvent(
|
analytics.logEvent('Invalid image', image);
|
||||||
'Invalid image',
|
|
||||||
_.merge(
|
|
||||||
{
|
|
||||||
applicationSessionUuid: store.getState().toJS()
|
|
||||||
.applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
},
|
|
||||||
image,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,21 +282,11 @@ export class SourceSelector extends React.Component<
|
|||||||
let title = null;
|
let title = null;
|
||||||
|
|
||||||
if (supportedFormats.looksLikeWindowsImage(image.path)) {
|
if (supportedFormats.looksLikeWindowsImage(image.path)) {
|
||||||
analytics.logEvent('Possibly Windows image', {
|
analytics.logEvent('Possibly Windows image', { image });
|
||||||
image,
|
|
||||||
applicationSessionUuid: store.getState().toJS()
|
|
||||||
.applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
message = messages.warning.looksLikeWindowsImage();
|
message = messages.warning.looksLikeWindowsImage();
|
||||||
title = 'Possible Windows image detected';
|
title = 'Possible Windows image detected';
|
||||||
} else if (!image.hasMBR) {
|
} else if (!image.hasMBR) {
|
||||||
analytics.logEvent('Missing partition table', {
|
analytics.logEvent('Missing partition table', { image });
|
||||||
image,
|
|
||||||
applicationSessionUuid: store.getState().toJS()
|
|
||||||
.applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
title = 'Missing partition table';
|
title = 'Missing partition table';
|
||||||
message = messages.warning.missingPartitionTable();
|
message = messages.warning.missingPartitionTable();
|
||||||
}
|
}
|
||||||
@ -331,8 +309,6 @@ export class SourceSelector extends React.Component<
|
|||||||
logo: Boolean(image.logo),
|
logo: Boolean(image.logo),
|
||||||
blockMap: Boolean(image.blockMap),
|
blockMap: Boolean(image.blockMap),
|
||||||
},
|
},
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
exceptionReporter.report(error);
|
exceptionReporter.report(error);
|
||||||
@ -375,7 +351,7 @@ export class SourceSelector extends React.Component<
|
|||||||
analytics.logEvent('Unsupported protocol', { path: imagePath });
|
analytics.logEvent('Unsupported protocol', { path: imagePath });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
source = new sourceDestination.Http(imagePath);
|
source = new sourceDestination.Http({ url: imagePath });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -420,21 +396,14 @@ export class SourceSelector extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async openImageSelector() {
|
private async openImageSelector() {
|
||||||
analytics.logEvent('Open image selector', {
|
analytics.logEvent('Open image selector');
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const imagePath = await osDialog.selectImage();
|
const imagePath = await osDialog.selectImage();
|
||||||
// Avoid analytics and selection state changes
|
// Avoid analytics and selection state changes
|
||||||
// if no file was resolved from the dialog.
|
// if no file was resolved from the dialog.
|
||||||
if (!imagePath) {
|
if (!imagePath) {
|
||||||
analytics.logEvent('Image selector closed', {
|
analytics.logEvent('Image selector closed');
|
||||||
applicationSessionUuid: store.getState().toJS()
|
|
||||||
.applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.selectImageByPath({
|
this.selectImageByPath({
|
||||||
@ -457,11 +426,7 @@ export class SourceSelector extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private openURLSelector() {
|
private openURLSelector() {
|
||||||
analytics.logEvent('Open image URL selector', {
|
analytics.logEvent('Open image URL selector');
|
||||||
applicationSessionUuid:
|
|
||||||
store.getState().toJS().applicationSessionUuid || '',
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
showURLSelector: true,
|
showURLSelector: true,
|
||||||
@ -481,8 +446,6 @@ export class SourceSelector extends React.Component<
|
|||||||
private showSelectedImageDetails() {
|
private showSelectedImageDetails() {
|
||||||
analytics.logEvent('Show selected image tooltip', {
|
analytics.logEvent('Show selected image tooltip', {
|
||||||
imagePath: selectionState.getImagePath(),
|
imagePath: selectionState.getImagePath(),
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -606,12 +569,7 @@ export class SourceSelector extends React.Component<
|
|||||||
// Avoid analytics and selection state changes
|
// Avoid analytics and selection state changes
|
||||||
// if no file was resolved from the dialog.
|
// if no file was resolved from the dialog.
|
||||||
if (!imagePath) {
|
if (!imagePath) {
|
||||||
analytics.logEvent('URL selector closed', {
|
analytics.logEvent('URL selector closed');
|
||||||
applicationSessionUuid: store.getState().toJS()
|
|
||||||
.applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS()
|
|
||||||
.flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
this.setState({
|
this.setState({
|
||||||
showURLSelector: false,
|
showURLSelector: false,
|
||||||
});
|
});
|
||||||
|
@ -82,7 +82,7 @@ export class SVGIcon extends React.Component<SVGIconProps> {
|
|||||||
|
|
||||||
let svgData = '';
|
let svgData = '';
|
||||||
|
|
||||||
_.find(this.props.contents, content => {
|
_.find(this.props.contents, (content) => {
|
||||||
const attempt = tryParseSVGContents(content);
|
const attempt = tryParseSVGContents(content);
|
||||||
|
|
||||||
if (attempt) {
|
if (attempt) {
|
||||||
@ -94,7 +94,7 @@ export class SVGIcon extends React.Component<SVGIconProps> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!svgData) {
|
if (!svgData) {
|
||||||
_.find(this.props.paths, relativePath => {
|
_.find(this.props.paths, (relativePath) => {
|
||||||
// This means the path to the icon should be
|
// This means the path to the icon should be
|
||||||
// relative to *this directory*.
|
// relative to *this directory*.
|
||||||
// TODO: There might be a way to compute the path
|
// TODO: There might be a way to compute the path
|
||||||
|
@ -85,13 +85,6 @@ export function setProgressState(
|
|||||||
return _.round(bytesToMegabytes(state.speed), PRECISION);
|
return _.round(bytesToMegabytes(state.speed), PRECISION);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}),
|
|
||||||
totalSpeed: _.attempt(() => {
|
|
||||||
if (_.isFinite(state.totalSpeed)) {
|
|
||||||
return _.round(bytesToMegabytes(state.totalSpeed), PRECISION);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
@ -107,10 +100,7 @@ export function getFlashResults() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getFlashState() {
|
export function getFlashState() {
|
||||||
return store
|
return store.getState().get('flashState').toJS();
|
||||||
.getState()
|
|
||||||
.get('flashState')
|
|
||||||
.toJS();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wasLastFlashCancelled() {
|
export function wasLastFlashCancelled() {
|
||||||
|
@ -66,7 +66,7 @@ interface DeviceFromState {
|
|||||||
device: string;
|
device: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init() {
|
export async function init(): Promise<void> {
|
||||||
// ledsMapping is something like:
|
// ledsMapping is something like:
|
||||||
// {
|
// {
|
||||||
// 'platform-xhci-hcd.0.auto-usb-0:1.1.1:1.0-scsi-0:0:0:0': [
|
// 'platform-xhci-hcd.0.auto-usb-0:1.1.1:1.0-scsi-0:0:0:0': [
|
||||||
@ -77,11 +77,11 @@ export function init() {
|
|||||||
// ...
|
// ...
|
||||||
// }
|
// }
|
||||||
const ledsMapping: _.Dictionary<[string, string, string]> =
|
const ledsMapping: _.Dictionary<[string, string, string]> =
|
||||||
settings.get('ledsMapping') || {};
|
(await settings.get('ledsMapping')) || {};
|
||||||
for (const [drivePath, ledsNames] of Object.entries(ledsMapping)) {
|
for (const [drivePath, ledsNames] of Object.entries(ledsMapping)) {
|
||||||
leds.set('/dev/disk/by-path/' + drivePath, new RGBLed(ledsNames));
|
leds.set('/dev/disk/by-path/' + drivePath, new RGBLed(ledsNames));
|
||||||
}
|
}
|
||||||
observe(state => {
|
observe((state) => {
|
||||||
const availableDrives = state
|
const availableDrives = state
|
||||||
.get('availableDrives')
|
.get('availableDrives')
|
||||||
.toJS()
|
.toJS()
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 balena.io
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as electron from 'electron';
|
|
||||||
import { promises as fs } from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
const JSON_INDENT = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Userdata directory path
|
|
||||||
* @description
|
|
||||||
* Defaults to the following:
|
|
||||||
* - `%APPDATA%/etcher` on Windows
|
|
||||||
* - `$XDG_CONFIG_HOME/etcher` or `~/.config/etcher` on Linux
|
|
||||||
* - `~/Library/Application Support/etcher` on macOS
|
|
||||||
* See https://electronjs.org/docs/api/app#appgetpathname
|
|
||||||
*
|
|
||||||
* NOTE: The ternary is due to this module being loaded both,
|
|
||||||
* Electron's main process and renderer process
|
|
||||||
*/
|
|
||||||
const USER_DATA_DIR = electron.app
|
|
||||||
? electron.app.getPath('userData')
|
|
||||||
: electron.remote.app.getPath('userData');
|
|
||||||
|
|
||||||
const CONFIG_PATH = path.join(USER_DATA_DIR, 'config.json');
|
|
||||||
|
|
||||||
async function readConfigFile(filename: string): Promise<any> {
|
|
||||||
let contents = '{}';
|
|
||||||
try {
|
|
||||||
contents = await fs.readFile(filename, { encoding: 'utf8' });
|
|
||||||
} catch (error) {
|
|
||||||
if (error.code !== 'ENOENT') {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return JSON.parse(contents);
|
|
||||||
} catch (parseError) {
|
|
||||||
console.error(parseError);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function writeConfigFile(filename: string, data: any): Promise<any> {
|
|
||||||
await fs.writeFile(filename, JSON.stringify(data, null, JSON_INDENT));
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function readAll(): Promise<any> {
|
|
||||||
return await readConfigFile(CONFIG_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function writeAll(settings: any): Promise<any> {
|
|
||||||
return await writeConfigFile(CONFIG_PATH, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function clear(): Promise<void> {
|
|
||||||
await fs.unlink(CONFIG_PATH);
|
|
||||||
}
|
|
@ -51,10 +51,7 @@ export function selectImage(image: any) {
|
|||||||
* @summary Get all selected drives' devices
|
* @summary Get all selected drives' devices
|
||||||
*/
|
*/
|
||||||
export function getSelectedDevices(): string[] {
|
export function getSelectedDevices(): string[] {
|
||||||
return store
|
return store.getState().getIn(['selection', 'devices']).toJS();
|
||||||
.getState()
|
|
||||||
.getIn(['selection', 'devices'])
|
|
||||||
.toJS();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,7 +59,7 @@ export function getSelectedDevices(): string[] {
|
|||||||
*/
|
*/
|
||||||
export function getSelectedDrives(): any[] {
|
export function getSelectedDrives(): any[] {
|
||||||
const drives = availableDrives.getDrives();
|
const drives = availableDrives.getDrives();
|
||||||
return _.map(getSelectedDevices(), device => {
|
return _.map(getSelectedDevices(), (device) => {
|
||||||
return _.find(drives, { device });
|
return _.find(drives, { device });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -15,71 +15,93 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as _debug from 'debug';
|
import * as _debug from 'debug';
|
||||||
|
import * as electron from 'electron';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
import * as packageJSON from '../../../../package.json';
|
import * as packageJSON from '../../../../package.json';
|
||||||
import * as errors from '../../../shared/errors';
|
|
||||||
import * as localSettings from './local-settings';
|
|
||||||
|
|
||||||
const debug = _debug('etcher:models:settings');
|
const debug = _debug('etcher:models:settings');
|
||||||
|
|
||||||
|
const JSON_INDENT = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Userdata directory path
|
||||||
|
* @description
|
||||||
|
* Defaults to the following:
|
||||||
|
* - `%APPDATA%/etcher` on Windows
|
||||||
|
* - `$XDG_CONFIG_HOME/etcher` or `~/.config/etcher` on Linux
|
||||||
|
* - `~/Library/Application Support/etcher` on macOS
|
||||||
|
* See https://electronjs.org/docs/api/app#appgetpathname
|
||||||
|
*
|
||||||
|
* NOTE: The ternary is due to this module being loaded both,
|
||||||
|
* Electron's main process and renderer process
|
||||||
|
*/
|
||||||
|
const USER_DATA_DIR = electron.app
|
||||||
|
? electron.app.getPath('userData')
|
||||||
|
: electron.remote.app.getPath('userData');
|
||||||
|
|
||||||
|
const CONFIG_PATH = join(USER_DATA_DIR, 'config.json');
|
||||||
|
|
||||||
|
async function readConfigFile(filename: string): Promise<_.Dictionary<any>> {
|
||||||
|
let contents = '{}';
|
||||||
|
try {
|
||||||
|
contents = await fs.readFile(filename, { encoding: 'utf8' });
|
||||||
|
} catch (error) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSON.parse(contents);
|
||||||
|
} catch (parseError) {
|
||||||
|
console.error(parseError);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// exported for tests
|
// exported for tests
|
||||||
export const DEFAULT_SETTINGS: _.Dictionary<any> = {
|
export async function readAll() {
|
||||||
|
return await readConfigFile(CONFIG_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// exported for tests
|
||||||
|
export async function writeConfigFile(
|
||||||
|
filename: string,
|
||||||
|
data: _.Dictionary<any>,
|
||||||
|
): Promise<void> {
|
||||||
|
await fs.writeFile(filename, JSON.stringify(data, null, JSON_INDENT));
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_SETTINGS: _.Dictionary<any> = {
|
||||||
unsafeMode: false,
|
unsafeMode: false,
|
||||||
errorReporting: true,
|
errorReporting: true,
|
||||||
unmountOnSuccess: true,
|
unmountOnSuccess: true,
|
||||||
validateWriteOnSuccess: true,
|
validateWriteOnSuccess: true,
|
||||||
trim: false,
|
updatesEnabled: !_.includes(['rpm', 'deb'], packageJSON.packageType),
|
||||||
updatesEnabled:
|
|
||||||
packageJSON.updates.enabled &&
|
|
||||||
!_.includes(['rpm', 'deb'], packageJSON.packageType),
|
|
||||||
lastSleptUpdateNotifier: null,
|
|
||||||
lastSleptUpdateNotifierVersion: null,
|
|
||||||
desktopNotifications: true,
|
desktopNotifications: true,
|
||||||
|
autoBlockmapping: true,
|
||||||
|
decompressFirst: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let settings = _.cloneDeep(DEFAULT_SETTINGS);
|
const settings = _.cloneDeep(DEFAULT_SETTINGS);
|
||||||
|
|
||||||
/**
|
async function load(): Promise<void> {
|
||||||
* @summary Reset settings to their default values
|
|
||||||
*/
|
|
||||||
export async function reset(): Promise<void> {
|
|
||||||
debug('reset');
|
|
||||||
// TODO: Remove default settings from config file (?)
|
|
||||||
settings = _.cloneDeep(DEFAULT_SETTINGS);
|
|
||||||
return await localSettings.writeAll(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Extend the application state with the local settings
|
|
||||||
*/
|
|
||||||
export async function load(): Promise<void> {
|
|
||||||
debug('load');
|
debug('load');
|
||||||
const loadedSettings = await localSettings.readAll();
|
// Use exports.readAll() so it can be mocked in tests
|
||||||
|
const loadedSettings = await exports.readAll();
|
||||||
_.assign(settings, loadedSettings);
|
_.assign(settings, loadedSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const loaded = load();
|
||||||
* @summary Set a setting value
|
|
||||||
*/
|
|
||||||
export async function set(key: string, value: any): Promise<void> {
|
export async function set(key: string, value: any): Promise<void> {
|
||||||
debug('set', key, value);
|
debug('set', key, value);
|
||||||
if (_.isNil(key)) {
|
await loaded;
|
||||||
throw errors.createError({
|
|
||||||
title: 'Missing setting key',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_.isString(key)) {
|
|
||||||
throw errors.createError({
|
|
||||||
title: `Invalid setting key: ${key}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const previousValue = settings[key];
|
const previousValue = settings[key];
|
||||||
settings[key] = value;
|
settings[key] = value;
|
||||||
try {
|
try {
|
||||||
await localSettings.writeAll(settings);
|
// Use exports.writeConfigFile() so it can be mocked in tests
|
||||||
|
await exports.writeConfigFile(CONFIG_PATH, settings);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Revert to previous value if persisting settings failed
|
// Revert to previous value if persisting settings failed
|
||||||
settings[key] = previousValue;
|
settings[key] = previousValue;
|
||||||
@ -87,24 +109,17 @@ export async function set(key: string, value: any): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function get(key: string): Promise<any> {
|
||||||
* @summary Get a setting value
|
await loaded;
|
||||||
*/
|
return getSync(key);
|
||||||
export function get(key: string): any {
|
|
||||||
return _.cloneDeep(_.get(settings, [key]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function getSync(key: string): any {
|
||||||
* @summary Check if setting value exists
|
return _.cloneDeep(settings[key]);
|
||||||
*/
|
|
||||||
export function has(key: string): boolean {
|
|
||||||
return settings[key] != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function getAll() {
|
||||||
* @summary Get all setting values
|
|
||||||
*/
|
|
||||||
export function getAll() {
|
|
||||||
debug('getAll');
|
debug('getAll');
|
||||||
|
await loaded;
|
||||||
return _.cloneDeep(settings);
|
return _.cloneDeep(settings);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ function verifyNoNilFields(
|
|||||||
fields: string[],
|
fields: string[],
|
||||||
name: string,
|
name: string,
|
||||||
) {
|
) {
|
||||||
const nilFields = _.filter(fields, field => {
|
const nilFields = _.filter(fields, (field) => {
|
||||||
return _.isNil(_.get(object, field));
|
return _.isNil(_.get(object, field));
|
||||||
});
|
});
|
||||||
if (nilFields.length) {
|
if (nilFields.length) {
|
||||||
@ -45,7 +45,7 @@ function verifyNoNilFields(
|
|||||||
/**
|
/**
|
||||||
* @summary FLASH_STATE fields that can't be nil
|
* @summary FLASH_STATE fields that can't be nil
|
||||||
*/
|
*/
|
||||||
const flashStateNoNilFields = ['speed', 'totalSpeed'];
|
const flashStateNoNilFields = ['speed'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary SELECT_IMAGE fields that can't be nil
|
* @summary SELECT_IMAGE fields that can't be nil
|
||||||
@ -65,14 +65,11 @@ const DEFAULT_STATE = Immutable.fromJS({
|
|||||||
isFlashing: false,
|
isFlashing: false,
|
||||||
flashResults: {},
|
flashResults: {},
|
||||||
flashState: {
|
flashState: {
|
||||||
flashing: 0,
|
active: 0,
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
percentage: 0,
|
percentage: 0,
|
||||||
speed: null,
|
speed: null,
|
||||||
averageSpeed: null,
|
averageSpeed: null,
|
||||||
totalSpeed: null,
|
|
||||||
},
|
},
|
||||||
lastAverageFlashingSpeed: null,
|
lastAverageFlashingSpeed: null,
|
||||||
});
|
});
|
||||||
@ -136,9 +133,9 @@ function storeReducer(
|
|||||||
|
|
||||||
drives = _.sortBy(drives, [
|
drives = _.sortBy(drives, [
|
||||||
// Devices with no devicePath first (usbboot)
|
// Devices with no devicePath first (usbboot)
|
||||||
d => !!d.devicePath,
|
(d) => !!d.devicePath,
|
||||||
// Then sort by devicePath (only available on Linux with udev) or device
|
// Then sort by devicePath (only available on Linux with udev) or device
|
||||||
d => d.devicePath || d.device,
|
(d) => d.devicePath || d.device,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const newState = state.set('availableDrives', Immutable.fromJS(drives));
|
const newState = state.set('availableDrives', Immutable.fromJS(drives));
|
||||||
@ -168,7 +165,7 @@ function storeReducer(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const shouldAutoselectAll = Boolean(
|
const shouldAutoselectAll = Boolean(
|
||||||
settings.get('disableExplicitDriveSelection'),
|
settings.getSync('disableExplicitDriveSelection'),
|
||||||
);
|
);
|
||||||
const AUTOSELECT_DRIVE_COUNT = 1;
|
const AUTOSELECT_DRIVE_COUNT = 1;
|
||||||
const nonStaleSelectedDevices = nonStaleNewState
|
const nonStaleSelectedDevices = nonStaleNewState
|
||||||
@ -234,17 +231,7 @@ function storeReducer(
|
|||||||
|
|
||||||
verifyNoNilFields(action.data, flashStateNoNilFields, 'flash');
|
verifyNoNilFields(action.data, flashStateNoNilFields, 'flash');
|
||||||
|
|
||||||
if (
|
if (!_.every(_.pick(action.data, ['active', 'failed']), _.isFinite)) {
|
||||||
!_.every(
|
|
||||||
_.pick(action.data, [
|
|
||||||
'flashing',
|
|
||||||
'verifying',
|
|
||||||
'successful',
|
|
||||||
'failed',
|
|
||||||
]),
|
|
||||||
_.isFinite,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
title: 'State quantity field(s) not finite number',
|
title: 'State quantity field(s) not finite number',
|
||||||
});
|
});
|
||||||
@ -266,7 +253,7 @@ function storeReducer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let ret = state.set('flashState', Immutable.fromJS(action.data));
|
let ret = state.set('flashState', Immutable.fromJS(action.data));
|
||||||
if (action.data.flashing) {
|
if (action.data.type === 'flashing') {
|
||||||
ret = ret.set('lastAverageFlashingSpeed', action.data.averageSpeed);
|
ret = ret.set('lastAverageFlashingSpeed', action.data.averageSpeed);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -20,34 +20,31 @@ import * as resinCorvus from 'resin-corvus/browser';
|
|||||||
import * as packageJSON from '../../../../package.json';
|
import * as packageJSON from '../../../../package.json';
|
||||||
import { getConfig, hasProps } from '../../../shared/utils';
|
import { getConfig, hasProps } from '../../../shared/utils';
|
||||||
import * as settings from '../models/settings';
|
import * as settings from '../models/settings';
|
||||||
|
import { store } from '../models/store';
|
||||||
const sentryToken =
|
|
||||||
settings.get('analyticsSentryToken') ||
|
|
||||||
_.get(packageJSON, ['analytics', 'sentry', 'token']);
|
|
||||||
const mixpanelToken =
|
|
||||||
settings.get('analyticsMixpanelToken') ||
|
|
||||||
_.get(packageJSON, ['analytics', 'mixpanel', 'token']);
|
|
||||||
|
|
||||||
const configUrl =
|
|
||||||
settings.get('configUrl') || 'https://balena.io/etcher/static/config.json';
|
|
||||||
|
|
||||||
const DEFAULT_PROBABILITY = 0.1;
|
const DEFAULT_PROBABILITY = 0.1;
|
||||||
|
|
||||||
const services = {
|
async function installCorvus(): Promise<void> {
|
||||||
sentry: sentryToken,
|
const sentryToken =
|
||||||
mixpanel: mixpanelToken,
|
(await settings.get('analyticsSentryToken')) ||
|
||||||
};
|
_.get(packageJSON, ['analytics', 'sentry', 'token']);
|
||||||
|
const mixpanelToken =
|
||||||
resinCorvus.install({
|
(await settings.get('analyticsMixpanelToken')) ||
|
||||||
services,
|
_.get(packageJSON, ['analytics', 'mixpanel', 'token']);
|
||||||
options: {
|
resinCorvus.install({
|
||||||
release: packageJSON.version,
|
services: {
|
||||||
shouldReport: () => {
|
sentry: sentryToken,
|
||||||
return settings.get('errorReporting');
|
mixpanel: mixpanelToken,
|
||||||
},
|
},
|
||||||
mixpanelDeferred: true,
|
options: {
|
||||||
},
|
release: packageJSON.version,
|
||||||
});
|
shouldReport: () => {
|
||||||
|
return settings.getSync('errorReporting');
|
||||||
|
},
|
||||||
|
mixpanelDeferred: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let mixpanelSample = DEFAULT_PROBABILITY;
|
let mixpanelSample = DEFAULT_PROBABILITY;
|
||||||
|
|
||||||
@ -55,9 +52,10 @@ let mixpanelSample = DEFAULT_PROBABILITY;
|
|||||||
* @summary Init analytics configurations
|
* @summary Init analytics configurations
|
||||||
*/
|
*/
|
||||||
async function initConfig() {
|
async function initConfig() {
|
||||||
|
await installCorvus();
|
||||||
let validatedConfig = null;
|
let validatedConfig = null;
|
||||||
try {
|
try {
|
||||||
const config = await getConfig(configUrl);
|
const config = await getConfig();
|
||||||
const mixpanel = _.get(config, ['analytics', 'mixpanel'], {});
|
const mixpanel = _.get(config, ['analytics', 'mixpanel'], {});
|
||||||
mixpanelSample = mixpanel.probability || DEFAULT_PROBABILITY;
|
mixpanelSample = mixpanel.probability || DEFAULT_PROBABILITY;
|
||||||
if (isClientEligible(mixpanelSample)) {
|
if (isClientEligible(mixpanelSample)) {
|
||||||
@ -96,22 +94,23 @@ function validateMixpanelConfig(config: {
|
|||||||
return mixpanelConfig;
|
return mixpanelConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Log a debug message
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* This function sends the debug message to error reporting services.
|
|
||||||
*/
|
|
||||||
export const logDebug = resinCorvus.logDebug;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Log an event
|
* @summary Log an event
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* This function sends the debug message to product analytics services.
|
* This function sends the debug message to product analytics services.
|
||||||
*/
|
*/
|
||||||
export function logEvent(message: string, data: any) {
|
export function logEvent(message: string, data: _.Dictionary<any> = {}) {
|
||||||
resinCorvus.logEvent(message, { ...data, sample: mixpanelSample });
|
const {
|
||||||
|
applicationSessionUuid,
|
||||||
|
flashingWorkflowUuid,
|
||||||
|
} = store.getState().toJS();
|
||||||
|
resinCorvus.logEvent(message, {
|
||||||
|
...data,
|
||||||
|
sample: mixpanelSample,
|
||||||
|
applicationSessionUuid,
|
||||||
|
flashingWorkflowUuid,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +23,9 @@ import * as settings from '../models/settings';
|
|||||||
* @summary returns true if system drives should be shown
|
* @summary returns true if system drives should be shown
|
||||||
*/
|
*/
|
||||||
function includeSystemDrives() {
|
function includeSystemDrives() {
|
||||||
return settings.get('unsafeMode') && !settings.get('disableUnsafeMode');
|
return (
|
||||||
|
settings.getSync('unsafeMode') && !settings.getSync('disableUnsafeMode')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const adapters: sdk.scanner.adapters.Adapter[] = [
|
const adapters: sdk.scanner.adapters.Adapter[] = [
|
||||||
|
@ -29,10 +29,8 @@ import { SourceOptions } from '../components/source-selector/source-selector';
|
|||||||
import * as flashState from '../models/flash-state';
|
import * as flashState from '../models/flash-state';
|
||||||
import * as selectionState from '../models/selection-state';
|
import * as selectionState from '../models/selection-state';
|
||||||
import * as settings from '../models/settings';
|
import * as settings from '../models/settings';
|
||||||
import { store } from '../models/store';
|
|
||||||
import * as analytics from '../modules/analytics';
|
import * as analytics from '../modules/analytics';
|
||||||
import * as windowProgress from '../os/window-progress';
|
import * as windowProgress from '../os/window-progress';
|
||||||
import { updateLock } from './update-lock';
|
|
||||||
|
|
||||||
const THREADS_PER_CPU = 16;
|
const THREADS_PER_CPU = 16;
|
||||||
|
|
||||||
@ -61,8 +59,6 @@ function handleErrorLogging(
|
|||||||
) {
|
) {
|
||||||
const eventData = {
|
const eventData = {
|
||||||
...analyticsData,
|
...analyticsData,
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
flashInstanceUuid: flashState.getFlashUuid(),
|
flashInstanceUuid: flashState.getFlashUuid(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -140,7 +136,7 @@ interface FlashResults {
|
|||||||
* @description
|
* @description
|
||||||
* This function is extracted for testing purposes.
|
* This function is extracted for testing purposes.
|
||||||
*/
|
*/
|
||||||
export function performWrite(
|
export async function performWrite(
|
||||||
image: string,
|
image: string,
|
||||||
drives: DrivelistDrive[],
|
drives: DrivelistDrive[],
|
||||||
onProgress: sdk.multiWrite.OnProgressFunction,
|
onProgress: sdk.multiWrite.OnProgressFunction,
|
||||||
@ -148,14 +144,20 @@ export function performWrite(
|
|||||||
): Promise<{ cancelled?: boolean }> {
|
): Promise<{ cancelled?: boolean }> {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
ipc.serve();
|
ipc.serve();
|
||||||
return new Promise((resolve, reject) => {
|
const {
|
||||||
ipc.server.on('error', error => {
|
unmountOnSuccess,
|
||||||
|
validateWriteOnSuccess,
|
||||||
|
autoBlockmapping,
|
||||||
|
decompressFirst,
|
||||||
|
} = await settings.getAll();
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
ipc.server.on('error', (error) => {
|
||||||
terminateServer();
|
terminateServer();
|
||||||
const errorObject = errors.fromJSON(error);
|
const errorObject = errors.fromJSON(error);
|
||||||
reject(errorObject);
|
reject(errorObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc.server.on('log', message => {
|
ipc.server.on('log', (message) => {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -166,17 +168,16 @@ export function performWrite(
|
|||||||
driveCount: drives.length,
|
driveCount: drives.length,
|
||||||
uuid: flashState.getFlashUuid(),
|
uuid: flashState.getFlashUuid(),
|
||||||
flashInstanceUuid: flashState.getFlashUuid(),
|
flashInstanceUuid: flashState.getFlashUuid(),
|
||||||
unmountOnSuccess: settings.get('unmountOnSuccess'),
|
unmountOnSuccess,
|
||||||
validateWriteOnSuccess: settings.get('validateWriteOnSuccess'),
|
validateWriteOnSuccess,
|
||||||
trim: settings.get('trim'),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ipc.server.on('fail', ({ error }: { error: Error & { code: string } }) => {
|
ipc.server.on('fail', ({ error }: { error: Error & { code: string } }) => {
|
||||||
handleErrorLogging(error, analyticsData);
|
handleErrorLogging(error, analyticsData);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc.server.on('done', event => {
|
ipc.server.on('done', (event) => {
|
||||||
event.results.errors = _.map(event.results.errors, data => {
|
event.results.errors = _.map(event.results.errors, (data) => {
|
||||||
return errors.fromJSON(data);
|
return errors.fromJSON(data);
|
||||||
});
|
});
|
||||||
_.merge(flashResults, event);
|
_.merge(flashResults, event);
|
||||||
@ -195,9 +196,10 @@ export function performWrite(
|
|||||||
destinations: drives,
|
destinations: drives,
|
||||||
source,
|
source,
|
||||||
SourceType: source.SourceType.name,
|
SourceType: source.SourceType.name,
|
||||||
validateWriteOnSuccess: settings.get('validateWriteOnSuccess'),
|
validateWriteOnSuccess,
|
||||||
trim: settings.get('trim'),
|
autoBlockmapping,
|
||||||
unmountOnSuccess: settings.get('unmountOnSuccess'),
|
unmountOnSuccess,
|
||||||
|
decompressFirst,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -245,7 +247,6 @@ export function performWrite(
|
|||||||
|
|
||||||
// Clear the update lock timer to prevent longer
|
// Clear the update lock timer to prevent longer
|
||||||
// flashing timing it out, and releasing the lock
|
// flashing timing it out, and releasing the lock
|
||||||
updateLock.pause();
|
|
||||||
ipc.server.start();
|
ipc.server.start();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -271,11 +272,8 @@ export async function flash(
|
|||||||
uuid: flashState.getFlashUuid(),
|
uuid: flashState.getFlashUuid(),
|
||||||
status: 'started',
|
status: 'started',
|
||||||
flashInstanceUuid: flashState.getFlashUuid(),
|
flashInstanceUuid: flashState.getFlashUuid(),
|
||||||
unmountOnSuccess: settings.get('unmountOnSuccess'),
|
unmountOnSuccess: await settings.get('unmountOnSuccess'),
|
||||||
validateWriteOnSuccess: settings.get('validateWriteOnSuccess'),
|
validateWriteOnSuccess: await settings.get('validateWriteOnSuccess'),
|
||||||
trim: settings.get('trim'),
|
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
analytics.logEvent('Flash', analyticsData);
|
analytics.logEvent('Flash', analyticsData);
|
||||||
@ -318,6 +316,8 @@ export async function flash(
|
|||||||
errors: results.errors,
|
errors: results.errors,
|
||||||
devices: results.devices,
|
devices: results.devices,
|
||||||
status: 'finished',
|
status: 'finished',
|
||||||
|
bytesWritten: results.bytesWritten,
|
||||||
|
sourceMetadata: results.sourceMetadata,
|
||||||
};
|
};
|
||||||
analytics.logEvent('Done', eventData);
|
analytics.logEvent('Done', eventData);
|
||||||
}
|
}
|
||||||
@ -326,7 +326,7 @@ export async function flash(
|
|||||||
/**
|
/**
|
||||||
* @summary Cancel write operation
|
* @summary Cancel write operation
|
||||||
*/
|
*/
|
||||||
export function cancel() {
|
export async function cancel() {
|
||||||
const drives = selectionState.getSelectedDevices();
|
const drives = selectionState.getSelectedDevices();
|
||||||
const analyticsData = {
|
const analyticsData = {
|
||||||
image: selectionState.getImagePath(),
|
image: selectionState.getImagePath(),
|
||||||
@ -334,17 +334,13 @@ export function cancel() {
|
|||||||
driveCount: drives.length,
|
driveCount: drives.length,
|
||||||
uuid: flashState.getFlashUuid(),
|
uuid: flashState.getFlashUuid(),
|
||||||
flashInstanceUuid: flashState.getFlashUuid(),
|
flashInstanceUuid: flashState.getFlashUuid(),
|
||||||
unmountOnSuccess: settings.get('unmountOnSuccess'),
|
unmountOnSuccess: await settings.get('unmountOnSuccess'),
|
||||||
validateWriteOnSuccess: settings.get('validateWriteOnSuccess'),
|
validateWriteOnSuccess: await settings.get('validateWriteOnSuccess'),
|
||||||
trim: settings.get('trim'),
|
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
status: 'cancel',
|
status: 'cancel',
|
||||||
};
|
};
|
||||||
analytics.logEvent('Cancel', analyticsData);
|
analytics.logEvent('Cancel', analyticsData);
|
||||||
|
|
||||||
// Re-enable lock release on inactivity
|
// Re-enable lock release on inactivity
|
||||||
updateLock.resume();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// @ts-ignore (no Server.sockets in @types/node-ipc)
|
// @ts-ignore (no Server.sockets in @types/node-ipc)
|
||||||
|
@ -15,16 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { bytesToClosestUnit } from '../../../shared/units';
|
import { bytesToClosestUnit } from '../../../shared/units';
|
||||||
import * as settings from '../models/settings';
|
// import * as settings from '../models/settings';
|
||||||
|
|
||||||
export interface FlashState {
|
export interface FlashState {
|
||||||
flashing: number;
|
active: number;
|
||||||
verifying: number;
|
|
||||||
successful: number;
|
|
||||||
failed: number;
|
failed: number;
|
||||||
percentage?: number;
|
percentage?: number;
|
||||||
speed: number;
|
speed: number;
|
||||||
position: number;
|
position: number;
|
||||||
|
type?: 'decompressing' | 'flashing' | 'verifying';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,45 +35,47 @@ export interface FlashState {
|
|||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const status = progressStatus.fromFlashState({
|
* const status = progressStatus.fromFlashState({
|
||||||
* flashing: 1,
|
* type: 'flashing'
|
||||||
* verifying: 0,
|
* active: 1,
|
||||||
* successful: 0,
|
|
||||||
* failed: 0,
|
* failed: 0,
|
||||||
* percentage: 55,
|
* percentage: 55,
|
||||||
* speed: 2049
|
* speed: 2049,
|
||||||
* })
|
* })
|
||||||
*
|
*
|
||||||
* console.log(status)
|
* console.log(status)
|
||||||
* // '55% Flashing'
|
* // '55% Flashing'
|
||||||
*/
|
*/
|
||||||
export function fromFlashState(state: FlashState): string {
|
export function fromFlashState({
|
||||||
const isFlashing = Boolean(state.flashing);
|
type,
|
||||||
const isValidating = !isFlashing && Boolean(state.verifying);
|
percentage,
|
||||||
const shouldValidate = settings.get('validateWriteOnSuccess');
|
position,
|
||||||
const shouldUnmount = settings.get('unmountOnSuccess');
|
}: FlashState): string {
|
||||||
|
if (type === undefined) {
|
||||||
if (state.percentage === 0 && !state.speed) {
|
|
||||||
if (isValidating) {
|
|
||||||
return 'Validating...';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Starting...';
|
return 'Starting...';
|
||||||
} else if (state.percentage === 100) {
|
} else if (type === 'decompressing') {
|
||||||
if ((isValidating || !shouldValidate) && shouldUnmount) {
|
if (percentage == null) {
|
||||||
return 'Unmounting...';
|
return 'Decompressing...';
|
||||||
|
} else {
|
||||||
|
return `${percentage}% Decompressing`;
|
||||||
}
|
}
|
||||||
|
} else if (type === 'flashing') {
|
||||||
return 'Finishing...';
|
if (percentage != null) {
|
||||||
} else if (isFlashing) {
|
if (percentage < 100) {
|
||||||
if (state.percentage != null) {
|
return `${percentage}% Flashing`;
|
||||||
return `${state.percentage}% Flashing`;
|
} else {
|
||||||
|
return 'Finishing...';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return `${bytesToClosestUnit(position)} flashed`;
|
||||||
|
}
|
||||||
|
} else if (type === 'verifying') {
|
||||||
|
if (percentage == null) {
|
||||||
|
return 'Validating...';
|
||||||
|
} else if (percentage < 100) {
|
||||||
|
return `${percentage}% Validating`;
|
||||||
|
} else {
|
||||||
|
return 'Finishing...';
|
||||||
}
|
}
|
||||||
return `${bytesToClosestUnit(state.position)} flashed`;
|
|
||||||
} else if (isValidating) {
|
|
||||||
return `${state.percentage}% Validating`;
|
|
||||||
} else if (!isFlashing && !isValidating) {
|
|
||||||
return 'Failed';
|
|
||||||
}
|
}
|
||||||
|
return 'Failed';
|
||||||
throw new Error(`Invalid state: ${JSON.stringify(state)}`);
|
|
||||||
}
|
}
|
||||||
|
@ -1,188 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 balena.io
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as _debug from 'debug';
|
|
||||||
import * as electron from 'electron';
|
|
||||||
import { EventEmitter } from 'events';
|
|
||||||
import * as createInactivityTimer from 'inactivity-timer';
|
|
||||||
|
|
||||||
import * as settings from '../models/settings';
|
|
||||||
import { logException } from './analytics';
|
|
||||||
|
|
||||||
const debug = _debug('etcher:update-lock');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interaction timeout in milliseconds (defaults to 5 minutes)
|
|
||||||
* @type {Number}
|
|
||||||
* @constant
|
|
||||||
*/
|
|
||||||
const INTERACTION_TIMEOUT_MS = settings.has('interactionTimeout')
|
|
||||||
? parseInt(settings.get('interactionTimeout'), 10)
|
|
||||||
: 5 * 60 * 1000;
|
|
||||||
|
|
||||||
class UpdateLock extends EventEmitter {
|
|
||||||
private paused: boolean;
|
|
||||||
private lockTimer: any;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.paused = false;
|
|
||||||
this.on('inactive', UpdateLock.onInactive);
|
|
||||||
this.lockTimer = createInactivityTimer(INTERACTION_TIMEOUT_MS, () => {
|
|
||||||
debug('inactive');
|
|
||||||
this.emit('inactive');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Inactivity event handler, releases the balena update lock on inactivity
|
|
||||||
*/
|
|
||||||
private static onInactive() {
|
|
||||||
if (settings.get('resinUpdateLock')) {
|
|
||||||
UpdateLock.check((checkError: Error, isLocked: boolean) => {
|
|
||||||
debug('inactive-check', Boolean(checkError));
|
|
||||||
if (checkError) {
|
|
||||||
logException(checkError);
|
|
||||||
}
|
|
||||||
if (isLocked) {
|
|
||||||
UpdateLock.release((error?: Error) => {
|
|
||||||
debug('inactive-release', Boolean(error));
|
|
||||||
if (error) {
|
|
||||||
logException(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Acquire the update lock
|
|
||||||
*/
|
|
||||||
private static acquire(callback: (error?: Error) => void) {
|
|
||||||
debug('lock');
|
|
||||||
if (settings.get('resinUpdateLock')) {
|
|
||||||
electron.ipcRenderer.once('resin-update-lock', (_event, error) => {
|
|
||||||
callback(error);
|
|
||||||
});
|
|
||||||
electron.ipcRenderer.send('resin-update-lock', 'lock');
|
|
||||||
} else {
|
|
||||||
callback(new Error('Update lock disabled'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Release the update lock
|
|
||||||
*/
|
|
||||||
public static release(callback: (error?: Error) => void) {
|
|
||||||
debug('unlock');
|
|
||||||
if (settings.get('resinUpdateLock')) {
|
|
||||||
electron.ipcRenderer.once('resin-update-lock', (_event, error) => {
|
|
||||||
callback(error);
|
|
||||||
});
|
|
||||||
electron.ipcRenderer.send('resin-update-lock', 'unlock');
|
|
||||||
} else {
|
|
||||||
callback(new Error('Update lock disabled'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Check the state of the update lock
|
|
||||||
* @param {Function} callback - callback(error, isLocked)
|
|
||||||
* @example
|
|
||||||
* UpdateLock.check((error, isLocked) => {
|
|
||||||
* if (isLocked) {
|
|
||||||
* // ...
|
|
||||||
* }
|
|
||||||
* })
|
|
||||||
*/
|
|
||||||
private static check(
|
|
||||||
callback: (error: Error | null, isLocked?: boolean) => void,
|
|
||||||
) {
|
|
||||||
debug('check');
|
|
||||||
if (settings.get('resinUpdateLock')) {
|
|
||||||
electron.ipcRenderer.once(
|
|
||||||
'resin-update-lock',
|
|
||||||
(_event, error, isLocked) => {
|
|
||||||
callback(error, isLocked);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
electron.ipcRenderer.send('resin-update-lock', 'check');
|
|
||||||
} else {
|
|
||||||
callback(new Error('Update lock disabled'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Extend the lock timer
|
|
||||||
*/
|
|
||||||
public extend() {
|
|
||||||
debug('extend');
|
|
||||||
|
|
||||||
if (this.paused) {
|
|
||||||
debug('extend:paused');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lockTimer.signal();
|
|
||||||
|
|
||||||
// When extending, check that we have the lock,
|
|
||||||
// and acquire it, if not
|
|
||||||
if (settings.get('resinUpdateLock')) {
|
|
||||||
UpdateLock.check((checkError, isLocked) => {
|
|
||||||
if (checkError) {
|
|
||||||
logException(checkError);
|
|
||||||
}
|
|
||||||
if (!isLocked) {
|
|
||||||
UpdateLock.acquire(error => {
|
|
||||||
if (error) {
|
|
||||||
logException(error);
|
|
||||||
}
|
|
||||||
debug('extend-acquire', Boolean(error));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Clear the lock timer
|
|
||||||
*/
|
|
||||||
private clearTimer() {
|
|
||||||
debug('clear');
|
|
||||||
this.lockTimer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Clear the lock timer, and pause extension, avoiding triggering until resume()d
|
|
||||||
*/
|
|
||||||
public pause() {
|
|
||||||
debug('pause');
|
|
||||||
this.paused = true;
|
|
||||||
this.clearTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Un-pause lock extension, and restart the timer
|
|
||||||
*/
|
|
||||||
public resume() {
|
|
||||||
debug('resume');
|
|
||||||
this.paused = false;
|
|
||||||
this.extend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const updateLock = new UpdateLock();
|
|
@ -21,9 +21,9 @@ import * as settings from '../models/settings';
|
|||||||
/**
|
/**
|
||||||
* @summary Send a notification
|
* @summary Send a notification
|
||||||
*/
|
*/
|
||||||
export function send(title: string, body: string, icon: string) {
|
export async function send(title: string, body: string, icon: string) {
|
||||||
// Bail out if desktop notifications are disabled
|
// Bail out if desktop notifications are disabled
|
||||||
if (!settings.get('desktopNotifications')) {
|
if (!(await settings.get('desktopNotifications'))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,22 +16,18 @@
|
|||||||
|
|
||||||
import * as electron from 'electron';
|
import * as electron from 'electron';
|
||||||
import * as settings from '../../../models/settings';
|
import * as settings from '../../../models/settings';
|
||||||
import { store } from '../../../models/store';
|
|
||||||
import { logEvent } from '../../../modules/analytics';
|
import { logEvent } from '../../../modules/analytics';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Open an external resource
|
* @summary Open an external resource
|
||||||
*/
|
*/
|
||||||
export function open(url: string) {
|
export async function open(url: string) {
|
||||||
// Don't open links if they're disabled by the env var
|
// Don't open links if they're disabled by the env var
|
||||||
if (settings.get('disableExternalLinks')) {
|
if (await settings.get('disableExternalLinks')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logEvent('Open external link', {
|
logEvent('Open external link', { url });
|
||||||
url,
|
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
electron.shell.openExternal(url);
|
electron.shell.openExternal(url);
|
||||||
|
@ -88,7 +88,7 @@ async function getWindowsNetworkDrives(): Promise<Map<string, string>> {
|
|||||||
trim(str.slice(colonPosition + 1)),
|
trim(str.slice(colonPosition + 1)),
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
.filter(couple => couple[1].length > 0)
|
.filter((couple) => couple[1].length > 0)
|
||||||
.value();
|
.value();
|
||||||
return new Map(couples);
|
return new Map(couples);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ import { TargetSelector } from '../../components/drive-selector/target-selector'
|
|||||||
import { SVGIcon } from '../../components/svg-icon/svg-icon';
|
import { SVGIcon } from '../../components/svg-icon/svg-icon';
|
||||||
import { getImage, getSelectedDrives } from '../../models/selection-state';
|
import { getImage, getSelectedDrives } from '../../models/selection-state';
|
||||||
import * as settings from '../../models/settings';
|
import * as settings from '../../models/settings';
|
||||||
import { observe, store } from '../../models/store';
|
import { observe } from '../../models/store';
|
||||||
import * as analytics from '../../modules/analytics';
|
import * as analytics from '../../modules/analytics';
|
||||||
|
|
||||||
const StepBorder = styled.div<{
|
const StepBorder = styled.div<{
|
||||||
@ -31,7 +31,7 @@ const StepBorder = styled.div<{
|
|||||||
right?: boolean;
|
right?: boolean;
|
||||||
}>`
|
}>`
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background-color: ${props =>
|
background-color: ${(props) =>
|
||||||
props.disabled
|
props.disabled
|
||||||
? props.theme.customColors.dark.disabled.foreground
|
? props.theme.customColors.dark.disabled.foreground
|
||||||
: props.theme.customColors.dark.foreground};
|
: props.theme.customColors.dark.foreground};
|
||||||
@ -39,8 +39,8 @@ const StepBorder = styled.div<{
|
|||||||
width: 124px;
|
width: 124px;
|
||||||
top: 19px;
|
top: 19px;
|
||||||
|
|
||||||
left: ${props => (props.left ? '-67px' : undefined)};
|
left: ${(props) => (props.left ? '-67px' : undefined)};
|
||||||
right: ${props => (props.right ? '-67px' : undefined)};
|
right: ${(props) => (props.right ? '-67px' : undefined)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const getDriveListLabel = () => {
|
const getDriveListLabel = () => {
|
||||||
@ -53,7 +53,7 @@ const getDriveListLabel = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const shouldShowDrivesButton = () => {
|
const shouldShowDrivesButton = () => {
|
||||||
return !settings.get('disableExplicitDriveSelection');
|
return !settings.getSync('disableExplicitDriveSelection');
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDriveSelectionStateSlice = () => ({
|
const getDriveSelectionStateSlice = () => ({
|
||||||
@ -117,12 +117,7 @@ export const DriveSelector = ({
|
|||||||
setShowDriveSelectorModal(true);
|
setShowDriveSelectorModal(true);
|
||||||
}}
|
}}
|
||||||
reselectDrive={() => {
|
reselectDrive={() => {
|
||||||
analytics.logEvent('Reselect drive', {
|
analytics.logEvent('Reselect drive');
|
||||||
applicationSessionUuid: store.getState().toJS()
|
|
||||||
.applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS()
|
|
||||||
.flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
setShowDriveSelectorModal(true);
|
setShowDriveSelectorModal(true);
|
||||||
}}
|
}}
|
||||||
flashing={flashing}
|
flashing={flashing}
|
||||||
|
@ -27,12 +27,12 @@ import { SVGIcon } from '../../components/svg-icon/svg-icon';
|
|||||||
import * as availableDrives from '../../models/available-drives';
|
import * as availableDrives from '../../models/available-drives';
|
||||||
import * as flashState from '../../models/flash-state';
|
import * as flashState from '../../models/flash-state';
|
||||||
import * as selection from '../../models/selection-state';
|
import * as selection from '../../models/selection-state';
|
||||||
import { store } from '../../models/store';
|
|
||||||
import * as analytics from '../../modules/analytics';
|
import * as analytics from '../../modules/analytics';
|
||||||
import { scanner as driveScanner } from '../../modules/drive-scanner';
|
import { scanner as driveScanner } from '../../modules/drive-scanner';
|
||||||
import * as imageWriter from '../../modules/image-writer';
|
import * as imageWriter from '../../modules/image-writer';
|
||||||
import * as progressStatus from '../../modules/progress-status';
|
import * as progressStatus from '../../modules/progress-status';
|
||||||
import * as notification from '../../os/notification';
|
import * as notification from '../../os/notification';
|
||||||
|
import { StepSelection } from '../../styled-components';
|
||||||
|
|
||||||
const COMPLETED_PERCENTAGE = 100;
|
const COMPLETED_PERCENTAGE = 100;
|
||||||
const SPEED_PRECISION = 2;
|
const SPEED_PRECISION = 2;
|
||||||
@ -200,10 +200,7 @@ export const Flash = ({
|
|||||||
setErrorMessage('');
|
setErrorMessage('');
|
||||||
flashState.resetState();
|
flashState.resetState();
|
||||||
if (shouldRetry) {
|
if (shouldRetry) {
|
||||||
analytics.logEvent('Restart after failure', {
|
analytics.logEvent('Restart after failure');
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
selection.clear();
|
selection.clear();
|
||||||
}
|
}
|
||||||
@ -243,14 +240,16 @@ export const Flash = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-vertical-large">
|
<div className="space-vertical-large">
|
||||||
<ProgressButton
|
<StepSelection>
|
||||||
striped={state.type === 'verifying'}
|
<ProgressButton
|
||||||
active={isFlashing}
|
type={state.type}
|
||||||
percentage={state.percentage}
|
active={isFlashing}
|
||||||
label={getProgressButtonLabel()}
|
percentage={state.percentage}
|
||||||
disabled={Boolean(flashErrorCode) || shouldFlashStepBeDisabled}
|
label={getProgressButtonLabel()}
|
||||||
callback={tryFlash}
|
disabled={Boolean(flashErrorCode) || shouldFlashStepBeDisabled}
|
||||||
></ProgressButton>
|
callback={tryFlash}
|
||||||
|
/>
|
||||||
|
</StepSelection>
|
||||||
|
|
||||||
{isFlashing && (
|
{isFlashing && (
|
||||||
<button
|
<button
|
||||||
|
@ -175,7 +175,7 @@ export class MainPage extends React.Component<
|
|||||||
tabIndex={5}
|
tabIndex={5}
|
||||||
onClick={() => this.setState({ hideSettings: false })}
|
onClick={() => this.setState({ hideSettings: false })}
|
||||||
/>
|
/>
|
||||||
{!settings.get('disableExternalLinks') && (
|
{!settings.getSync('disableExternalLinks') && (
|
||||||
<Icon
|
<Icon
|
||||||
icon={<FontAwesomeIcon icon={faQuestionCircle} />}
|
icon={<FontAwesomeIcon icon={faQuestionCircle} />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
@ -61,7 +61,7 @@ export const BaseButton = styled(Button)`
|
|||||||
height: 48px;
|
height: 48px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const IconButton = styled(props => <Button plain {...props} />)`
|
export const IconButton = styled((props) => <Button plain {...props} />)`
|
||||||
&&& {
|
&&& {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
@ -28,8 +28,6 @@ import * as settings from './app/models/settings';
|
|||||||
import * as analytics from './app/modules/analytics';
|
import * as analytics from './app/modules/analytics';
|
||||||
import { buildWindowMenu } from './menu';
|
import { buildWindowMenu } from './menu';
|
||||||
|
|
||||||
const configUrl =
|
|
||||||
settings.get('configUrl') || 'https://balena.io/etcher/static/config.json';
|
|
||||||
const updatablePackageTypes = ['appimage', 'nsis', 'dmg'];
|
const updatablePackageTypes = ['appimage', 'nsis', 'dmg'];
|
||||||
const packageUpdatable = _.includes(updatablePackageTypes, packageType);
|
const packageUpdatable = _.includes(updatablePackageTypes, packageType);
|
||||||
let packageUpdated = false;
|
let packageUpdated = false;
|
||||||
@ -38,7 +36,7 @@ async function checkForUpdates(interval: number) {
|
|||||||
// We use a while loop instead of a setInterval to preserve
|
// We use a while loop instead of a setInterval to preserve
|
||||||
// async execution time between each function call
|
// async execution time between each function call
|
||||||
while (!packageUpdated) {
|
while (!packageUpdated) {
|
||||||
if (settings.get('updatesEnabled')) {
|
if (await settings.get('updatesEnabled')) {
|
||||||
try {
|
try {
|
||||||
const release = await autoUpdater.checkForUpdates();
|
const release = await autoUpdater.checkForUpdates();
|
||||||
const isOutdated =
|
const isOutdated =
|
||||||
@ -56,8 +54,8 @@ async function checkForUpdates(interval: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMainWindow() {
|
async function createMainWindow() {
|
||||||
const fullscreen = Boolean(settings.get('fullscreen'));
|
const fullscreen = Boolean(await settings.get('fullscreen'));
|
||||||
const defaultWidth = 800;
|
const defaultWidth = 800;
|
||||||
const defaultHeight = 480;
|
const defaultHeight = 480;
|
||||||
let width = defaultWidth;
|
let width = defaultWidth;
|
||||||
@ -100,7 +98,7 @@ function createMainWindow() {
|
|||||||
// Prevent external resources from being loaded (like images)
|
// Prevent external resources from being loaded (like images)
|
||||||
// when dropping them on the WebView.
|
// when dropping them on the WebView.
|
||||||
// See https://github.com/electron/electron/issues/5919
|
// See https://github.com/electron/electron/issues/5919
|
||||||
mainWindow.webContents.on('will-navigate', event => {
|
mainWindow.webContents.on('will-navigate', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -111,12 +109,12 @@ function createMainWindow() {
|
|||||||
const page = mainWindow.webContents;
|
const page = mainWindow.webContents;
|
||||||
|
|
||||||
page.once('did-frame-finish-load', async () => {
|
page.once('did-frame-finish-load', async () => {
|
||||||
autoUpdater.on('error', err => {
|
autoUpdater.on('error', (err) => {
|
||||||
analytics.logException(err);
|
analytics.logException(err);
|
||||||
});
|
});
|
||||||
if (packageUpdatable) {
|
if (packageUpdatable) {
|
||||||
try {
|
try {
|
||||||
const onlineConfig = await getConfig(configUrl);
|
const onlineConfig = await getConfig();
|
||||||
const autoUpdaterConfig = _.get(
|
const autoUpdaterConfig = _.get(
|
||||||
onlineConfig,
|
onlineConfig,
|
||||||
['autoUpdates', 'autoUpdaterConfig'],
|
['autoUpdates', 'autoUpdaterConfig'],
|
||||||
@ -151,18 +149,10 @@ electron.app.on('before-quit', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
async function main(): Promise<void> {
|
||||||
try {
|
if (electron.app.isReady()) {
|
||||||
await settings.load();
|
await createMainWindow();
|
||||||
} catch (error) {
|
} else {
|
||||||
// TODO: What do if loading the config fails?
|
electron.app.on('ready', createMainWindow);
|
||||||
console.error('Error loading settings:');
|
|
||||||
console.error(error);
|
|
||||||
} finally {
|
|
||||||
if (electron.app.isReady()) {
|
|
||||||
createMainWindow();
|
|
||||||
} else {
|
|
||||||
electron.app.on('ready', createMainWindow);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
import { delay } from 'bluebird';
|
import { delay } from 'bluebird';
|
||||||
import { Drive as DrivelistDrive } from 'drivelist';
|
import { Drive as DrivelistDrive } from 'drivelist';
|
||||||
import * as sdk from 'etcher-sdk';
|
import * as sdk from 'etcher-sdk';
|
||||||
|
import { cleanupTmpFiles } from 'etcher-sdk/build/tmp';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as ipc from 'node-ipc';
|
import * as ipc from 'node-ipc';
|
||||||
|
|
||||||
@ -77,6 +78,7 @@ interface WriteResult {
|
|||||||
successful: number;
|
successful: number;
|
||||||
};
|
};
|
||||||
errors: Array<Error & { device: string }>;
|
errors: Array<Error & { device: string }>;
|
||||||
|
sourceMetadata: sdk.sourceDestination.Metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,38 +86,42 @@ interface WriteResult {
|
|||||||
* @param {SourceDestination} source - source
|
* @param {SourceDestination} source - source
|
||||||
* @param {SourceDestination[]} destinations - destinations
|
* @param {SourceDestination[]} destinations - destinations
|
||||||
* @param {Boolean} verify - whether to validate the writes or not
|
* @param {Boolean} verify - whether to validate the writes or not
|
||||||
* @param {Boolean} trim - whether to trim ext partitions before writing
|
* @param {Boolean} autoBlockmapping - whether to trim ext partitions before writing
|
||||||
* @param {Function} onProgress - function to call on progress
|
* @param {Function} onProgress - function to call on progress
|
||||||
* @param {Function} onFail - function to call on fail
|
* @param {Function} onFail - function to call on fail
|
||||||
* @returns {Promise<{ bytesWritten, devices, errors} >}
|
* @returns {Promise<{ bytesWritten, devices, errors} >}
|
||||||
*/
|
*/
|
||||||
async function writeAndValidate(
|
async function writeAndValidate({
|
||||||
source: sdk.sourceDestination.SourceDestination,
|
source,
|
||||||
destinations: sdk.sourceDestination.BlockDevice[],
|
destinations,
|
||||||
verify: boolean,
|
verify,
|
||||||
trim: boolean,
|
autoBlockmapping,
|
||||||
onProgress: sdk.multiWrite.OnProgressFunction,
|
decompressFirst,
|
||||||
onFail: sdk.multiWrite.OnFailFunction,
|
onProgress,
|
||||||
): Promise<WriteResult> {
|
onFail,
|
||||||
let innerSource: sdk.sourceDestination.SourceDestination = await source.getInnerSource();
|
}: {
|
||||||
if (trim && (await innerSource.canRead())) {
|
source: sdk.sourceDestination.SourceDestination;
|
||||||
innerSource = new sdk.sourceDestination.ConfiguredSource({
|
destinations: sdk.sourceDestination.BlockDevice[];
|
||||||
source: innerSource,
|
verify: boolean;
|
||||||
shouldTrimPartitions: trim,
|
autoBlockmapping: boolean;
|
||||||
createStreamFromDisk: true,
|
decompressFirst: boolean;
|
||||||
});
|
onProgress: sdk.multiWrite.OnProgressFunction;
|
||||||
}
|
onFail: sdk.multiWrite.OnFailFunction;
|
||||||
|
}): Promise<WriteResult> {
|
||||||
const {
|
const {
|
||||||
|
sourceMetadata,
|
||||||
failures,
|
failures,
|
||||||
bytesWritten,
|
bytesWritten,
|
||||||
} = await sdk.multiWrite.pipeSourceToDestinations(
|
} = await sdk.multiWrite.decompressThenFlash({
|
||||||
innerSource,
|
source,
|
||||||
destinations,
|
destinations,
|
||||||
onFail,
|
onFail,
|
||||||
onProgress,
|
onProgress,
|
||||||
verify,
|
verify,
|
||||||
32,
|
trim: autoBlockmapping,
|
||||||
);
|
numBuffers: 32,
|
||||||
|
decompressFirst,
|
||||||
|
});
|
||||||
const result: WriteResult = {
|
const result: WriteResult = {
|
||||||
bytesWritten,
|
bytesWritten,
|
||||||
devices: {
|
devices: {
|
||||||
@ -123,6 +129,7 @@ async function writeAndValidate(
|
|||||||
successful: destinations.length - failures.size,
|
successful: destinations.length - failures.size,
|
||||||
},
|
},
|
||||||
errors: [],
|
errors: [],
|
||||||
|
sourceMetadata,
|
||||||
};
|
};
|
||||||
for (const [destination, error] of failures) {
|
for (const [destination, error] of failures) {
|
||||||
const err = error as Error & { device: string };
|
const err = error as Error & { device: string };
|
||||||
@ -137,12 +144,15 @@ interface WriteOptions {
|
|||||||
destinations: DrivelistDrive[];
|
destinations: DrivelistDrive[];
|
||||||
unmountOnSuccess: boolean;
|
unmountOnSuccess: boolean;
|
||||||
validateWriteOnSuccess: boolean;
|
validateWriteOnSuccess: boolean;
|
||||||
trim: boolean;
|
autoBlockmapping: boolean;
|
||||||
|
decompressFirst: boolean;
|
||||||
source: SourceOptions;
|
source: SourceOptions;
|
||||||
SourceType: string;
|
SourceType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
ipc.connectTo(IPC_SERVER_ID, () => {
|
ipc.connectTo(IPC_SERVER_ID, () => {
|
||||||
|
// Remove leftover tmp files older than 1 hour
|
||||||
|
cleanupTmpFiles(Date.now() - 60 * 60 * 1000);
|
||||||
process.once('uncaughtException', handleError);
|
process.once('uncaughtException', handleError);
|
||||||
|
|
||||||
// Gracefully exit on the following cases. If the parent
|
// Gracefully exit on the following cases. If the parent
|
||||||
@ -219,8 +229,9 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
|||||||
log(`Devices: ${destinations.join(', ')}`);
|
log(`Devices: ${destinations.join(', ')}`);
|
||||||
log(`Umount on success: ${options.unmountOnSuccess}`);
|
log(`Umount on success: ${options.unmountOnSuccess}`);
|
||||||
log(`Validate on success: ${options.validateWriteOnSuccess}`);
|
log(`Validate on success: ${options.validateWriteOnSuccess}`);
|
||||||
log(`Trim: ${options.trim}`);
|
log(`Auto blockmapping: ${options.autoBlockmapping}`);
|
||||||
const dests = _.map(options.destinations, destination => {
|
log(`Decompress first: ${options.decompressFirst}`);
|
||||||
|
const dests = _.map(options.destinations, (destination) => {
|
||||||
return new sdk.sourceDestination.BlockDevice({
|
return new sdk.sourceDestination.BlockDevice({
|
||||||
drive: destination,
|
drive: destination,
|
||||||
unmountOnSuccess: options.unmountOnSuccess,
|
unmountOnSuccess: options.unmountOnSuccess,
|
||||||
@ -235,19 +246,20 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
|||||||
path: options.imagePath,
|
path: options.imagePath,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
source = new Http(options.imagePath);
|
source = new Http({ url: options.imagePath });
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const results = await writeAndValidate(
|
const results = await writeAndValidate({
|
||||||
source,
|
source,
|
||||||
dests,
|
destinations: dests,
|
||||||
options.validateWriteOnSuccess,
|
verify: options.validateWriteOnSuccess,
|
||||||
options.trim,
|
autoBlockmapping: options.autoBlockmapping,
|
||||||
|
decompressFirst: options.decompressFirst,
|
||||||
onProgress,
|
onProgress,
|
||||||
onFail,
|
onFail,
|
||||||
);
|
});
|
||||||
log(`Finish: ${results.bytesWritten}`);
|
log(`Finish: ${results.bytesWritten}`);
|
||||||
results.errors = _.map(results.errors, error => {
|
results.errors = _.map(results.errors, (error) => {
|
||||||
return toJSON(error);
|
return toJSON(error);
|
||||||
});
|
});
|
||||||
ipc.of[IPC_SERVER_ID].emit('done', { results });
|
ipc.of[IPC_SERVER_ID].emit('done', { results });
|
||||||
|
@ -67,7 +67,7 @@ export function isSourceDrive(drive: DrivelistDrive, image: Image): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return _.some(
|
return _.some(
|
||||||
_.map(mountpoints, mountpoint => {
|
_.map(mountpoints, (mountpoint) => {
|
||||||
return pathIsInside(imagePath, mountpoint.path);
|
return pathIsInside(imagePath, mountpoint.path);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -235,7 +235,7 @@ export function getListDriveImageCompatibilityStatuses(
|
|||||||
drives: DrivelistDrive[],
|
drives: DrivelistDrive[],
|
||||||
image: Image,
|
image: Image,
|
||||||
) {
|
) {
|
||||||
return _.flatMap(drives, drive => {
|
return _.flatMap(drives, (drive) => {
|
||||||
return getDriveImageCompatibilityStatuses(drive, image);
|
return getDriveImageCompatibilityStatuses(drive, image);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ function createErrorDetails(options: {
|
|||||||
description: (error: Error) => string;
|
description: (error: Error) => string;
|
||||||
} {
|
} {
|
||||||
return _.pick(
|
return _.pick(
|
||||||
_.mapValues(options, value => {
|
_.mapValues(options, (value) => {
|
||||||
return _.isFunction(value) ? value : _.constant(value);
|
return _.isFunction(value) ? value : _.constant(value);
|
||||||
}),
|
}),
|
||||||
['title', 'description'],
|
['title', 'description'],
|
||||||
|
@ -26,11 +26,7 @@ import { lookup } from 'mime-types';
|
|||||||
* > [ 'img', 'gz' ]
|
* > [ 'img', 'gz' ]
|
||||||
*/
|
*/
|
||||||
export function getFileExtensions(filePath: string): string[] {
|
export function getFileExtensions(filePath: string): string[] {
|
||||||
return _.chain(filePath)
|
return _.chain(filePath).split('.').tail().map(_.toLower).value();
|
||||||
.split('.')
|
|
||||||
.tail()
|
|
||||||
.map(_.toLower)
|
|
||||||
.value();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,6 +21,7 @@ import * as tmp from 'tmp';
|
|||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
|
|
||||||
import * as errors from './errors';
|
import * as errors from './errors';
|
||||||
|
import * as settings from '../gui/app/models/settings';
|
||||||
|
|
||||||
const getAsync = promisify(request.get);
|
const getAsync = promisify(request.get);
|
||||||
|
|
||||||
@ -40,8 +41,8 @@ export function percentageToFloat(percentage: any) {
|
|||||||
/**
|
/**
|
||||||
* @summary Check if obj has one or many specific props
|
* @summary Check if obj has one or many specific props
|
||||||
*/
|
*/
|
||||||
export function hasProps(obj: any, props: string[]): boolean {
|
export function hasProps(obj: _.Dictionary<any>, props: string[]): boolean {
|
||||||
return _.every(props, prop => {
|
return _.every(props, (prop) => {
|
||||||
return _.has(obj, prop);
|
return _.has(obj, prop);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -50,7 +51,10 @@ export function hasProps(obj: any, props: string[]): boolean {
|
|||||||
* @summary Get etcher configs stored online
|
* @summary Get etcher configs stored online
|
||||||
* @param {String} - url where config.json is stored
|
* @param {String} - url where config.json is stored
|
||||||
*/
|
*/
|
||||||
export async function getConfig(configUrl: string): Promise<any> {
|
export async function getConfig(): Promise<_.Dictionary<any>> {
|
||||||
|
const configUrl =
|
||||||
|
(await settings.get('configUrl')) ||
|
||||||
|
'https://balena.io/etcher/static/config.json';
|
||||||
return (await getAsync({ url: configUrl, json: true })).body;
|
return (await getAsync({ url: configUrl, json: true })).body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
806
npm-shrinkwrap.json
generated
806
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -4,11 +4,6 @@
|
|||||||
"displayName": "balenaEtcher",
|
"displayName": "balenaEtcher",
|
||||||
"version": "1.5.82",
|
"version": "1.5.82",
|
||||||
"packageType": "local",
|
"packageType": "local",
|
||||||
"updates": {
|
|
||||||
"enabled": true,
|
|
||||||
"sleepDays": 7,
|
|
||||||
"semverRange": "<2.0.0"
|
|
||||||
},
|
|
||||||
"main": "generated/etcher.js",
|
"main": "generated/etcher.js",
|
||||||
"description": "Flash OS images to SD cards and USB drives, safely and easily.",
|
"description": "Flash OS images to SD cards and USB drives, safely and easily.",
|
||||||
"productDescription": "Etcher is a powerful OS image flasher built with web technologies to ensure flashing an SDCard or USB drive is a pleasant and safe experience. It protects you from accidentally writing to your hard-drives, ensures every byte of data was written correctly and much more.",
|
"productDescription": "Etcher is a powerful OS image flasher built with web technologies to ensure flashing an SDCard or USB drive is a pleasant and safe experience. It protects you from accidentally writing to your hard-drives, ensures every byte of data was written correctly and much more.",
|
||||||
@ -57,11 +52,10 @@
|
|||||||
"bindings": "^1.3.0",
|
"bindings": "^1.3.0",
|
||||||
"bluebird": "^3.7.2",
|
"bluebird": "^3.7.2",
|
||||||
"bootstrap-sass": "^3.3.6",
|
"bootstrap-sass": "^3.3.6",
|
||||||
"color": "^2.0.1",
|
|
||||||
"d3": "^4.13.0",
|
"d3": "^4.13.0",
|
||||||
"debug": "^3.1.0",
|
"debug": "^3.1.0",
|
||||||
"electron-updater": "4.0.6",
|
"electron-updater": "4.0.6",
|
||||||
"etcher-sdk": "^3.0.1",
|
"etcher-sdk": "^4.0.0",
|
||||||
"flexboxgrid": "^6.3.0",
|
"flexboxgrid": "^6.3.0",
|
||||||
"immutable": "^3.8.1",
|
"immutable": "^3.8.1",
|
||||||
"inactivity-timer": "^1.0.0",
|
"inactivity-timer": "^1.0.0",
|
||||||
@ -87,6 +81,7 @@
|
|||||||
"uuid": "^3.0.1"
|
"uuid": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@balena/lint": "^5.0.4",
|
||||||
"@types/bindings": "^1.3.0",
|
"@types/bindings": "^1.3.0",
|
||||||
"@types/bluebird": "^3.5.30",
|
"@types/bluebird": "^3.5.30",
|
||||||
"@types/chai": "^4.2.7",
|
"@types/chai": "^4.2.7",
|
||||||
@ -113,7 +108,6 @@
|
|||||||
"node-gyp": "^3.8.0",
|
"node-gyp": "^3.8.0",
|
||||||
"node-sass": "^4.12.0",
|
"node-sass": "^4.12.0",
|
||||||
"omit-deep-lodash": "1.1.4",
|
"omit-deep-lodash": "1.1.4",
|
||||||
"resin-lint": "^3.2.0",
|
|
||||||
"sass-lint": "^1.12.1",
|
"sass-lint": "^1.12.1",
|
||||||
"simple-progress-webpack-plugin": "^1.1.2",
|
"simple-progress-webpack-plugin": "^1.1.2",
|
||||||
"sinon": "^8.0.4",
|
"sinon": "^8.0.4",
|
||||||
|
@ -21,44 +21,44 @@ import * as availableDrives from '../../../lib/gui/app/models/available-drives';
|
|||||||
import * as selectionState from '../../../lib/gui/app/models/selection-state';
|
import * as selectionState from '../../../lib/gui/app/models/selection-state';
|
||||||
import * as constraints from '../../../lib/shared/drive-constraints';
|
import * as constraints from '../../../lib/shared/drive-constraints';
|
||||||
|
|
||||||
describe('Model: availableDrives', function() {
|
describe('Model: availableDrives', function () {
|
||||||
describe('availableDrives', function() {
|
describe('availableDrives', function () {
|
||||||
it('should have no drives by default', function() {
|
it('should have no drives by default', function () {
|
||||||
expect(availableDrives.getDrives()).to.deep.equal([]);
|
expect(availableDrives.getDrives()).to.deep.equal([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.setDrives()', function() {
|
describe('.setDrives()', function () {
|
||||||
it('should throw if no drives', function() {
|
it('should throw if no drives', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
availableDrives.setDrives();
|
availableDrives.setDrives();
|
||||||
}).to.throw('Missing drives');
|
}).to.throw('Missing drives');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if drives is not an array', function() {
|
it('should throw if drives is not an array', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
availableDrives.setDrives(123);
|
availableDrives.setDrives(123);
|
||||||
}).to.throw('Invalid drives: 123');
|
}).to.throw('Invalid drives: 123');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if drives is not an array of objects', function() {
|
it('should throw if drives is not an array of objects', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
availableDrives.setDrives([123, 123, 123]);
|
availableDrives.setDrives([123, 123, 123]);
|
||||||
}).to.throw('Invalid drives: 123,123,123');
|
}).to.throw('Invalid drives: 123,123,123');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given no drives', function() {
|
describe('given no drives', function () {
|
||||||
describe('.hasAvailableDrives()', function() {
|
describe('.hasAvailableDrives()', function () {
|
||||||
it('should return false', function() {
|
it('should return false', function () {
|
||||||
expect(availableDrives.hasAvailableDrives()).to.be.false;
|
expect(availableDrives.hasAvailableDrives()).to.be.false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.setDrives()', function() {
|
describe('.setDrives()', function () {
|
||||||
it('should be able to set drives', function() {
|
it('should be able to set drives', function () {
|
||||||
const drives = [
|
const drives = [
|
||||||
{
|
{
|
||||||
device: '/dev/sdb',
|
device: '/dev/sdb',
|
||||||
@ -77,7 +77,7 @@ describe('Model: availableDrives', function() {
|
|||||||
expect(availableDrives.getDrives()).to.deep.equal(drives);
|
expect(availableDrives.getDrives()).to.deep.equal(drives);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to set drives with extra properties', function() {
|
it('should be able to set drives with extra properties', function () {
|
||||||
const drives = [
|
const drives = [
|
||||||
{
|
{
|
||||||
device: '/dev/sdb',
|
device: '/dev/sdb',
|
||||||
@ -101,7 +101,7 @@ describe('Model: availableDrives', function() {
|
|||||||
expect(availableDrives.getDrives()).to.deep.equal(drives);
|
expect(availableDrives.getDrives()).to.deep.equal(drives);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to set drives with null sizes', function() {
|
it('should be able to set drives with null sizes', function () {
|
||||||
const drives = [
|
const drives = [
|
||||||
{
|
{
|
||||||
device: '/dev/sdb',
|
device: '/dev/sdb',
|
||||||
@ -120,12 +120,12 @@ describe('Model: availableDrives', function() {
|
|||||||
expect(availableDrives.getDrives()).to.deep.equal(drives);
|
expect(availableDrives.getDrives()).to.deep.equal(drives);
|
||||||
});
|
});
|
||||||
|
|
||||||
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.clear();
|
selectionState.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should auto-select a single valid available drive', function() {
|
it('should auto-select a single valid available drive', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
|
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
@ -148,8 +148,8 @@ describe('Model: availableDrives', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given a selected image and no selected drive', function() {
|
describe('given a selected image and no selected drive', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
this.imagePath = 'E:\\bar\\foo.img';
|
this.imagePath = 'E:\\bar\\foo.img';
|
||||||
} else {
|
} else {
|
||||||
@ -166,11 +166,11 @@ describe('Model: availableDrives', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
selectionState.deselectImage();
|
selectionState.deselectImage();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not auto-select when there are multiple valid available drives', function() {
|
it('should not auto-select when there are multiple valid available drives', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
|
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
@ -203,7 +203,7 @@ describe('Model: availableDrives', function() {
|
|||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should auto-select a single valid available drive', function() {
|
it('should auto-select a single valid available drive', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
|
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
@ -224,7 +224,7 @@ describe('Model: availableDrives', function() {
|
|||||||
expect(selectionState.getSelectedDevices()[0]).to.equal('/dev/sdb');
|
expect(selectionState.getSelectedDevices()[0]).to.equal('/dev/sdb');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not auto-select a single too small drive', function() {
|
it('should not auto-select a single too small drive', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
|
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
@ -245,7 +245,7 @@ describe('Model: availableDrives', function() {
|
|||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not auto-select a single drive that doesn't meet the recommended size", function() {
|
it("should not auto-select a single drive that doesn't meet the recommended size", function () {
|
||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
|
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
@ -266,7 +266,7 @@ describe('Model: availableDrives', function() {
|
|||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not auto-select a single protected drive', function() {
|
it('should not auto-select a single protected drive', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
|
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
@ -287,7 +287,7 @@ describe('Model: availableDrives', function() {
|
|||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not auto-select a source drive', function() {
|
it('should not auto-select a source drive', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
|
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
@ -308,7 +308,7 @@ describe('Model: availableDrives', function() {
|
|||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not auto-select a single system drive', function() {
|
it('should not auto-select a single system drive', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
|
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
@ -329,7 +329,7 @@ describe('Model: availableDrives', function() {
|
|||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not auto-select a single large size drive', function() {
|
it('should not auto-select a single large size drive', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
|
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
@ -353,8 +353,8 @@ describe('Model: availableDrives', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given drives', function() {
|
describe('given drives', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
this.drives = [
|
this.drives = [
|
||||||
{
|
{
|
||||||
device: '/dev/sdb',
|
device: '/dev/sdb',
|
||||||
@ -385,8 +385,8 @@ describe('Model: availableDrives', function() {
|
|||||||
availableDrives.setDrives(this.drives);
|
availableDrives.setDrives(this.drives);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given one of the drives was selected', function() {
|
describe('given one of the drives was selected', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
{
|
{
|
||||||
device: '/dev/sdc',
|
device: '/dev/sdc',
|
||||||
@ -405,11 +405,11 @@ describe('Model: availableDrives', function() {
|
|||||||
selectionState.selectDrive('/dev/sdc');
|
selectionState.selectDrive('/dev/sdc');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
selectionState.clear();
|
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 () {
|
||||||
expect(selectionState.hasDrive()).to.be.true;
|
expect(selectionState.hasDrive()).to.be.true;
|
||||||
|
|
||||||
// We have to provide at least two drives, otherwise,
|
// We have to provide at least two drives, otherwise,
|
||||||
@ -446,25 +446,25 @@ describe('Model: availableDrives', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.hasAvailableDrives()', function() {
|
describe('.hasAvailableDrives()', function () {
|
||||||
it('should return true', function() {
|
it('should return true', function () {
|
||||||
const hasDrives = availableDrives.hasAvailableDrives();
|
const hasDrives = availableDrives.hasAvailableDrives();
|
||||||
expect(hasDrives).to.be.true;
|
expect(hasDrives).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.setDrives()', function() {
|
describe('.setDrives()', function () {
|
||||||
it('should keep the same drives if equal', function() {
|
it('should keep the same drives if equal', function () {
|
||||||
availableDrives.setDrives(this.drives);
|
availableDrives.setDrives(this.drives);
|
||||||
expect(availableDrives.getDrives()).to.deep.equal(this.drives);
|
expect(availableDrives.getDrives()).to.deep.equal(this.drives);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return empty array given an empty array', function() {
|
it('should return empty array given an empty array', function () {
|
||||||
availableDrives.setDrives([]);
|
availableDrives.setDrives([]);
|
||||||
expect(availableDrives.getDrives()).to.deep.equal([]);
|
expect(availableDrives.getDrives()).to.deep.equal([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should consider drives with different $$hashKey the same', function() {
|
it('should consider drives with different $$hashKey the same', function () {
|
||||||
this.drives[0].$$haskey = 1234;
|
this.drives[0].$$haskey = 1234;
|
||||||
availableDrives.setDrives(this.drives);
|
availableDrives.setDrives(this.drives);
|
||||||
expect(availableDrives.getDrives()).to.deep.equal(this.drives);
|
expect(availableDrives.getDrives()).to.deep.equal(this.drives);
|
||||||
|
@ -18,26 +18,22 @@ import { expect } from 'chai';
|
|||||||
|
|
||||||
import * as flashState from '../../../lib/gui/app/models/flash-state';
|
import * as flashState from '../../../lib/gui/app/models/flash-state';
|
||||||
|
|
||||||
describe('Model: flashState', function() {
|
describe('Model: flashState', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
flashState.resetState();
|
flashState.resetState();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('flashState', function() {
|
describe('flashState', function () {
|
||||||
describe('.resetState()', function() {
|
describe('.resetState()', function () {
|
||||||
it('should be able to reset the progress state', function() {
|
it('should be able to reset the progress state', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
type: 'flashing',
|
type: 'flashing',
|
||||||
percentage: 50,
|
percentage: 50,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
speed: 100000000000,
|
speed: 100000000000,
|
||||||
averageSpeed: 100000000000,
|
averageSpeed: 100000000000,
|
||||||
totalSpeed: 200000000000,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -46,18 +42,15 @@ describe('Model: flashState', function() {
|
|||||||
flashState.resetState();
|
flashState.resetState();
|
||||||
|
|
||||||
expect(flashState.getFlashState()).to.deep.equal({
|
expect(flashState.getFlashState()).to.deep.equal({
|
||||||
flashing: 0,
|
active: 0,
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
percentage: 0,
|
percentage: 0,
|
||||||
speed: null,
|
speed: null,
|
||||||
averageSpeed: null,
|
averageSpeed: null,
|
||||||
totalSpeed: null,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to reset the progress state', function() {
|
it('should be able to reset the progress state', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
@ -67,49 +60,45 @@ describe('Model: flashState', function() {
|
|||||||
expect(flashState.getFlashResults()).to.deep.equal({});
|
expect(flashState.getFlashResults()).to.deep.equal({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should unset the flashing flag', function() {
|
it('should unset the flashing flag', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
flashState.resetState();
|
flashState.resetState();
|
||||||
expect(flashState.isFlashing()).to.be.false;
|
expect(flashState.isFlashing()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should unset the flash uuid', function() {
|
it('should unset the flash uuid', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
flashState.resetState();
|
flashState.resetState();
|
||||||
expect(flashState.getFlashUuid()).to.be.undefined;
|
expect(flashState.getFlashUuid()).to.be.undefined;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.isFlashing()', function() {
|
describe('.isFlashing()', function () {
|
||||||
it('should return false by default', function() {
|
it('should return false by default', function () {
|
||||||
expect(flashState.isFlashing()).to.be.false;
|
expect(flashState.isFlashing()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if flashing', function() {
|
it('should return true if flashing', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
expect(flashState.isFlashing()).to.be.true;
|
expect(flashState.isFlashing()).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.setProgressState()', function() {
|
describe('.setProgressState()', function () {
|
||||||
it('should not allow setting the state if flashing is false', function() {
|
it('should not allow setting the state if flashing is false', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(function() {
|
expect(function () {
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
type: 'flashing',
|
type: 'flashing',
|
||||||
percentage: 50,
|
percentage: 50,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
speed: 100000000000,
|
speed: 100000000000,
|
||||||
averageSpeed: 100000000000,
|
averageSpeed: 100000000000,
|
||||||
totalSpeed: 200000000000,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -117,20 +106,16 @@ describe('Model: flashState', function() {
|
|||||||
}).to.throw("Can't set the flashing state when not flashing");
|
}).to.throw("Can't set the flashing state when not flashing");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw if percentage is 0', function() {
|
it('should not throw if percentage is 0', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
expect(function() {
|
expect(function () {
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
type: 'flashing',
|
type: 'flashing',
|
||||||
percentage: 0,
|
percentage: 0,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
speed: 100000000000,
|
speed: 100000000000,
|
||||||
averageSpeed: 100000000000,
|
averageSpeed: 100000000000,
|
||||||
totalSpeed: 200000000000,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -138,20 +123,16 @@ describe('Model: flashState', function() {
|
|||||||
}).to.not.throw('Missing flash fields: percentage');
|
}).to.not.throw('Missing flash fields: percentage');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if percentage is outside maximum bound', function() {
|
it('should throw if percentage is outside maximum bound', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
expect(function() {
|
expect(function () {
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
type: 'flashing',
|
type: 'flashing',
|
||||||
percentage: 101,
|
percentage: 101,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
speed: 0,
|
speed: 0,
|
||||||
averageSpeed: 0,
|
averageSpeed: 0,
|
||||||
totalSpeed: 1,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -159,20 +140,16 @@ describe('Model: flashState', function() {
|
|||||||
}).to.throw('Invalid state percentage: 101');
|
}).to.throw('Invalid state percentage: 101');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if percentage is outside minimum bound', function() {
|
it('should throw if percentage is outside minimum bound', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
expect(function() {
|
expect(function () {
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
type: 'flashing',
|
type: 'flashing',
|
||||||
percentage: -1,
|
percentage: -1,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
speed: 0,
|
speed: 0,
|
||||||
averageSpeed: 0,
|
averageSpeed: 0,
|
||||||
totalSpeed: 1,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -180,20 +157,16 @@ describe('Model: flashState', function() {
|
|||||||
}).to.throw('Invalid state percentage: -1');
|
}).to.throw('Invalid state percentage: -1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw if eta is equal to zero', function() {
|
it('should not throw if eta is equal to zero', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
expect(function() {
|
expect(function () {
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
type: 'flashing',
|
type: 'flashing',
|
||||||
percentage: 50,
|
percentage: 50,
|
||||||
eta: 0,
|
eta: 0,
|
||||||
speed: 100000000000,
|
speed: 100000000000,
|
||||||
averageSpeed: 100000000000,
|
averageSpeed: 100000000000,
|
||||||
totalSpeed: 200000000000,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -201,13 +174,10 @@ describe('Model: flashState', function() {
|
|||||||
}).to.not.throw('Missing flash field eta');
|
}).to.not.throw('Missing flash field eta');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if eta is not a number', function() {
|
it('should throw if eta is not a number', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
expect(function() {
|
expect(function () {
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
type: 'flashing',
|
type: 'flashing',
|
||||||
percentage: 50,
|
percentage: 50,
|
||||||
@ -215,7 +185,6 @@ describe('Model: flashState', function() {
|
|||||||
eta: '15',
|
eta: '15',
|
||||||
speed: 100000000000,
|
speed: 100000000000,
|
||||||
averageSpeed: 100000000000,
|
averageSpeed: 100000000000,
|
||||||
totalSpeed: 200000000000,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -223,20 +192,16 @@ describe('Model: flashState', function() {
|
|||||||
}).to.throw('Invalid state eta: 15');
|
}).to.throw('Invalid state eta: 15');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if speed is missing', function() {
|
it('should throw if speed is missing', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
expect(function() {
|
expect(function () {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
type: 'flashing',
|
type: 'flashing',
|
||||||
percentage: 50,
|
percentage: 50,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
averageSpeed: 0,
|
averageSpeed: 0,
|
||||||
totalSpeed: 1,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -244,20 +209,16 @@ describe('Model: flashState', function() {
|
|||||||
}).to.throw('Missing flash fields: speed');
|
}).to.throw('Missing flash fields: speed');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw if speed is 0', function() {
|
it('should not throw if speed is 0', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
expect(function() {
|
expect(function () {
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
type: 'flashing',
|
type: 'flashing',
|
||||||
percentage: 50,
|
percentage: 50,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
speed: 0,
|
speed: 0,
|
||||||
averageSpeed: 0,
|
averageSpeed: 0,
|
||||||
totalSpeed: 1,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -265,61 +226,15 @@ describe('Model: flashState', function() {
|
|||||||
}).to.not.throw('Missing flash fields: speed');
|
}).to.not.throw('Missing flash fields: speed');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if totalSpeed is missing', function() {
|
it('should floor the percentage number', function () {
|
||||||
flashState.setFlashingFlag();
|
|
||||||
expect(function() {
|
|
||||||
// @ts-ignore
|
|
||||||
flashState.setProgressState({
|
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
|
||||||
type: 'flashing',
|
|
||||||
percentage: 50,
|
|
||||||
eta: 15,
|
|
||||||
speed: 1,
|
|
||||||
averageSpeed: 1,
|
|
||||||
bytes: 0,
|
|
||||||
position: 0,
|
|
||||||
active: 0,
|
|
||||||
});
|
|
||||||
}).to.throw('Missing flash fields: totalSpeed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not throw if totalSpeed is 0', function() {
|
|
||||||
flashState.setFlashingFlag();
|
|
||||||
expect(function() {
|
|
||||||
flashState.setProgressState({
|
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
|
||||||
type: 'flashing',
|
|
||||||
percentage: 50,
|
|
||||||
eta: 15,
|
|
||||||
speed: 0,
|
|
||||||
averageSpeed: 0,
|
|
||||||
totalSpeed: 0,
|
|
||||||
bytes: 0,
|
|
||||||
position: 0,
|
|
||||||
active: 0,
|
|
||||||
});
|
|
||||||
}).to.not.throw('Missing flash fields: totalSpeed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should floor the percentage number', function() {
|
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
type: 'flashing',
|
type: 'flashing',
|
||||||
percentage: 50.253559459485,
|
percentage: 50.253559459485,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
speed: 0,
|
speed: 0,
|
||||||
averageSpeed: 0,
|
averageSpeed: 0,
|
||||||
totalSpeed: 1,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -328,7 +243,7 @@ describe('Model: flashState', function() {
|
|||||||
expect(flashState.getFlashState().percentage).to.equal(50);
|
expect(flashState.getFlashState().percentage).to.equal(50);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error when any field is non-nil but not a finite number', function() {
|
it('should error when any field is non-nil but not a finite number', function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
@ -344,7 +259,6 @@ describe('Model: flashState', function() {
|
|||||||
eta: 0,
|
eta: 0,
|
||||||
speed: 0,
|
speed: 0,
|
||||||
averageSpeed: 0,
|
averageSpeed: 0,
|
||||||
totalSpeed: 0,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -353,19 +267,15 @@ describe('Model: flashState', function() {
|
|||||||
}).to.throw('State quantity field(s) not finite number');
|
}).to.throw('State quantity field(s) not finite number');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not error when all quantity fields are zero', function() {
|
it('should not error when all quantity fields are zero', function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 0,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
percentage: 0,
|
percentage: 0,
|
||||||
eta: 0,
|
eta: 0,
|
||||||
speed: 0,
|
speed: 0,
|
||||||
averageSpeed: 0,
|
averageSpeed: 0,
|
||||||
totalSpeed: 0,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -375,8 +285,8 @@ describe('Model: flashState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getFlashResults()', function() {
|
describe('.getFlashResults()', function () {
|
||||||
it('should get the flash results', function() {
|
it('should get the flash results', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
|
|
||||||
const expectedResults = {
|
const expectedResults = {
|
||||||
@ -390,33 +300,26 @@ describe('Model: flashState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getFlashState()', function() {
|
describe('.getFlashState()', function () {
|
||||||
it('should initially return an empty state', function() {
|
it('should initially return an empty state', function () {
|
||||||
flashState.resetState();
|
flashState.resetState();
|
||||||
const currentFlashState = flashState.getFlashState();
|
const currentFlashState = flashState.getFlashState();
|
||||||
expect(currentFlashState).to.deep.equal({
|
expect(currentFlashState).to.deep.equal({
|
||||||
flashing: 0,
|
active: 0,
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
percentage: 0,
|
percentage: 0,
|
||||||
speed: null,
|
speed: null,
|
||||||
averageSpeed: null,
|
averageSpeed: null,
|
||||||
totalSpeed: null,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the current flash state', function() {
|
it('should return the current flash state', function () {
|
||||||
const state = {
|
const state = {
|
||||||
flashing: 1,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
percentage: 50,
|
percentage: 50,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
speed: 0,
|
speed: 0,
|
||||||
averageSpeed: 0,
|
averageSpeed: 0,
|
||||||
totalSpeed: 0,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -427,15 +330,11 @@ describe('Model: flashState', function() {
|
|||||||
flashState.setProgressState(state);
|
flashState.setProgressState(state);
|
||||||
const currentFlashState = flashState.getFlashState();
|
const currentFlashState = flashState.getFlashState();
|
||||||
expect(currentFlashState).to.deep.equal({
|
expect(currentFlashState).to.deep.equal({
|
||||||
flashing: 1,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
percentage: 50,
|
percentage: 50,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
speed: 0,
|
speed: 0,
|
||||||
averageSpeed: 0,
|
averageSpeed: 0,
|
||||||
totalSpeed: 0,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -444,15 +343,15 @@ describe('Model: flashState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.unsetFlashingFlag()', function() {
|
describe('.unsetFlashingFlag()', function () {
|
||||||
it('should throw if no flashing results', function() {
|
it('should throw if no flashing results', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
flashState.unsetFlashingFlag();
|
flashState.unsetFlashingFlag();
|
||||||
}).to.throw('Missing results');
|
}).to.throw('Missing results');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to set a string error code', function() {
|
it('should be able to set a string error code', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
@ -462,7 +361,7 @@ describe('Model: flashState', function() {
|
|||||||
expect(flashState.getLastFlashErrorCode()).to.equal('EBUSY');
|
expect(flashState.getLastFlashErrorCode()).to.equal('EBUSY');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to set a number error code', function() {
|
it('should be able to set a number error code', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
@ -472,8 +371,8 @@ describe('Model: flashState', function() {
|
|||||||
expect(flashState.getLastFlashErrorCode()).to.equal(123);
|
expect(flashState.getLastFlashErrorCode()).to.equal(123);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if errorCode is not a number not a string', function() {
|
it('should throw if errorCode is not a number not a string', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
@ -485,7 +384,7 @@ describe('Model: flashState', function() {
|
|||||||
}).to.throw('Invalid results errorCode: [object Object]');
|
}).to.throw('Invalid results errorCode: [object Object]');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should default cancelled to false', function() {
|
it('should default cancelled to false', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
});
|
});
|
||||||
@ -498,8 +397,8 @@ describe('Model: flashState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if cancelled is not boolean', function() {
|
it('should throw if cancelled is not boolean', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
cancelled: 'false',
|
cancelled: 'false',
|
||||||
@ -508,8 +407,8 @@ describe('Model: flashState', function() {
|
|||||||
}).to.throw('Invalid results cancelled: false');
|
}).to.throw('Invalid results cancelled: false');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if cancelled is true and sourceChecksum exists', function() {
|
it('should throw if cancelled is true and sourceChecksum exists', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
cancelled: true,
|
cancelled: true,
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
@ -519,7 +418,7 @@ describe('Model: flashState', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to set flashing to false', function() {
|
it('should be able to set flashing to false', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
@ -528,34 +427,26 @@ describe('Model: flashState', function() {
|
|||||||
expect(flashState.isFlashing()).to.be.false;
|
expect(flashState.isFlashing()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reset the flashing state', function() {
|
it('should reset the flashing state', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
|
|
||||||
flashState.setProgressState({
|
flashState.setProgressState({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
type: 'flashing',
|
type: 'flashing',
|
||||||
percentage: 50,
|
percentage: 50,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
speed: 100000000000,
|
speed: 100000000000,
|
||||||
averageSpeed: 100000000000,
|
averageSpeed: 100000000000,
|
||||||
totalSpeed: 200000000000,
|
|
||||||
bytes: 0,
|
bytes: 0,
|
||||||
position: 0,
|
position: 0,
|
||||||
active: 0,
|
active: 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(flashState.getFlashState()).to.not.deep.equal({
|
expect(flashState.getFlashState()).to.not.deep.equal({
|
||||||
flashing: 2,
|
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
percentage: 0,
|
percentage: 0,
|
||||||
speed: 0,
|
speed: 0,
|
||||||
averageSpeed: 0,
|
averageSpeed: 0,
|
||||||
totalSpeed: 0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
@ -564,18 +455,15 @@ describe('Model: flashState', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(flashState.getFlashState()).to.deep.equal({
|
expect(flashState.getFlashState()).to.deep.equal({
|
||||||
flashing: 0,
|
active: 0,
|
||||||
verifying: 0,
|
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
percentage: 0,
|
percentage: 0,
|
||||||
speed: null,
|
speed: null,
|
||||||
averageSpeed: null,
|
averageSpeed: null,
|
||||||
totalSpeed: null,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not reset the flash uuid', function() {
|
it('should not reset the flash uuid', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
const uuidBeforeUnset = flashState.getFlashUuid();
|
const uuidBeforeUnset = flashState.getFlashUuid();
|
||||||
|
|
||||||
@ -589,13 +477,13 @@ describe('Model: flashState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.setFlashingFlag()', function() {
|
describe('.setFlashingFlag()', function () {
|
||||||
it('should be able to set flashing to true', function() {
|
it('should be able to set flashing to true', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
expect(flashState.isFlashing()).to.be.true;
|
expect(flashState.isFlashing()).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reset the flash results', function() {
|
it('should reset the flash results', function () {
|
||||||
const expectedResults = {
|
const expectedResults = {
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
@ -609,13 +497,13 @@ describe('Model: flashState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.wasLastFlashCancelled()', function() {
|
describe('.wasLastFlashCancelled()', function () {
|
||||||
it('should return false given a pristine state', function() {
|
it('should return false given a pristine state', function () {
|
||||||
flashState.resetState();
|
flashState.resetState();
|
||||||
expect(flashState.wasLastFlashCancelled()).to.be.false;
|
expect(flashState.wasLastFlashCancelled()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if !cancelled', function() {
|
it('should return false if !cancelled', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
@ -624,7 +512,7 @@ describe('Model: flashState', function() {
|
|||||||
expect(flashState.wasLastFlashCancelled()).to.be.false;
|
expect(flashState.wasLastFlashCancelled()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if cancelled', function() {
|
it('should return true if cancelled', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
cancelled: true,
|
cancelled: true,
|
||||||
});
|
});
|
||||||
@ -633,13 +521,13 @@ describe('Model: flashState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getLastFlashSourceChecksum()', function() {
|
describe('.getLastFlashSourceChecksum()', function () {
|
||||||
it('should return undefined given a pristine state', function() {
|
it('should return undefined given a pristine state', function () {
|
||||||
flashState.resetState();
|
flashState.resetState();
|
||||||
expect(flashState.getLastFlashSourceChecksum()).to.be.undefined;
|
expect(flashState.getLastFlashSourceChecksum()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the last flash source checksum', function() {
|
it('should return the last flash source checksum', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
@ -648,7 +536,7 @@ describe('Model: flashState', function() {
|
|||||||
expect(flashState.getLastFlashSourceChecksum()).to.equal('1234');
|
expect(flashState.getLastFlashSourceChecksum()).to.equal('1234');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined if the last flash was cancelled', function() {
|
it('should return undefined if the last flash was cancelled', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
cancelled: true,
|
cancelled: true,
|
||||||
});
|
});
|
||||||
@ -657,13 +545,13 @@ describe('Model: flashState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getLastFlashErrorCode()', function() {
|
describe('.getLastFlashErrorCode()', function () {
|
||||||
it('should return undefined given a pristine state', function() {
|
it('should return undefined given a pristine state', function () {
|
||||||
flashState.resetState();
|
flashState.resetState();
|
||||||
expect(flashState.getLastFlashErrorCode()).to.be.undefined;
|
expect(flashState.getLastFlashErrorCode()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the last flash error code', function() {
|
it('should return the last flash error code', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
@ -673,7 +561,7 @@ describe('Model: flashState', function() {
|
|||||||
expect(flashState.getLastFlashErrorCode()).to.equal('ENOSPC');
|
expect(flashState.getLastFlashErrorCode()).to.equal('ENOSPC');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined if the last flash did not report an error code', function() {
|
it('should return undefined if the last flash did not report an error code', function () {
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
@ -683,20 +571,20 @@ describe('Model: flashState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getFlashUuid()', function() {
|
describe('.getFlashUuid()', function () {
|
||||||
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
||||||
|
|
||||||
it('should be initially undefined', function() {
|
it('should be initially undefined', function () {
|
||||||
expect(flashState.getFlashUuid()).to.be.undefined;
|
expect(flashState.getFlashUuid()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be a valid uuid if the flashing flag is set', function() {
|
it('should be a valid uuid if the flashing flag is set', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
const uuid = flashState.getFlashUuid();
|
const uuid = flashState.getFlashUuid();
|
||||||
expect(UUID_REGEX.test(uuid)).to.be.true;
|
expect(UUID_REGEX.test(uuid)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return different uuids every time the flashing flag is set', function() {
|
it('should return different uuids every time the flashing flag is set', function () {
|
||||||
flashState.setFlashingFlag();
|
flashState.setFlashingFlag();
|
||||||
const uuid1 = flashState.getFlashUuid();
|
const uuid1 = flashState.getFlashUuid();
|
||||||
flashState.unsetFlashingFlag({
|
flashState.unsetFlashingFlag({
|
||||||
|
@ -21,61 +21,61 @@ import * as path from 'path';
|
|||||||
import * as availableDrives from '../../../lib/gui/app/models/available-drives';
|
import * as availableDrives from '../../../lib/gui/app/models/available-drives';
|
||||||
import * as selectionState from '../../../lib/gui/app/models/selection-state';
|
import * as selectionState from '../../../lib/gui/app/models/selection-state';
|
||||||
|
|
||||||
describe('Model: selectionState', function() {
|
describe('Model: selectionState', function () {
|
||||||
describe('given a clean state', function() {
|
describe('given a clean state', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
selectionState.clear();
|
selectionState.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImage() should return undefined', function() {
|
it('getImage() should return undefined', function () {
|
||||||
expect(selectionState.getImage()).to.be.undefined;
|
expect(selectionState.getImage()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImagePath() should return undefined', function() {
|
it('getImagePath() should return undefined', function () {
|
||||||
expect(selectionState.getImagePath()).to.be.undefined;
|
expect(selectionState.getImagePath()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImageSize() should return undefined', function() {
|
it('getImageSize() should return undefined', function () {
|
||||||
expect(selectionState.getImageSize()).to.be.undefined;
|
expect(selectionState.getImageSize()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImageUrl() should return undefined', function() {
|
it('getImageUrl() should return undefined', function () {
|
||||||
expect(selectionState.getImageUrl()).to.be.undefined;
|
expect(selectionState.getImageUrl()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImageName() should return undefined', function() {
|
it('getImageName() should return undefined', function () {
|
||||||
expect(selectionState.getImageName()).to.be.undefined;
|
expect(selectionState.getImageName()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImageLogo() should return undefined', function() {
|
it('getImageLogo() should return undefined', function () {
|
||||||
expect(selectionState.getImageLogo()).to.be.undefined;
|
expect(selectionState.getImageLogo()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImageSupportUrl() should return undefined', function() {
|
it('getImageSupportUrl() should return undefined', function () {
|
||||||
expect(selectionState.getImageSupportUrl()).to.be.undefined;
|
expect(selectionState.getImageSupportUrl()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImageRecommendedDriveSize() should return undefined', function() {
|
it('getImageRecommendedDriveSize() should return undefined', function () {
|
||||||
expect(selectionState.getImageRecommendedDriveSize()).to.be.undefined;
|
expect(selectionState.getImageRecommendedDriveSize()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hasDrive() should return false', function() {
|
it('hasDrive() should return false', function () {
|
||||||
const hasDrive = selectionState.hasDrive();
|
const hasDrive = selectionState.hasDrive();
|
||||||
expect(hasDrive).to.be.false;
|
expect(hasDrive).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hasImage() should return false', function() {
|
it('hasImage() should return false', function () {
|
||||||
const hasImage = selectionState.hasImage();
|
const hasImage = selectionState.hasImage();
|
||||||
expect(hasImage).to.be.false;
|
expect(hasImage).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('.getSelectedDrives() should return []', function() {
|
it('.getSelectedDrives() should return []', function () {
|
||||||
expect(selectionState.getSelectedDrives()).to.deep.equal([]);
|
expect(selectionState.getSelectedDrives()).to.deep.equal([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given one available drive', function() {
|
describe('given one available drive', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
this.drives = [
|
this.drives = [
|
||||||
{
|
{
|
||||||
device: '/dev/disk2',
|
device: '/dev/disk2',
|
||||||
@ -86,13 +86,13 @@ describe('Model: selectionState', function() {
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
selectionState.clear();
|
selectionState.clear();
|
||||||
availableDrives.setDrives([]);
|
availableDrives.setDrives([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.selectDrive()', function() {
|
describe('.selectDrive()', function () {
|
||||||
it('should not deselect when warning is attached to image-drive pair', function() {
|
it('should not deselect when warning is attached to image-drive pair', function () {
|
||||||
this.drives[0].size = 64e10;
|
this.drives[0].size = 64e10;
|
||||||
|
|
||||||
availableDrives.setDrives(this.drives);
|
availableDrives.setDrives(this.drives);
|
||||||
@ -103,8 +103,8 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given a drive', function() {
|
describe('given a drive', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
{
|
{
|
||||||
device: '/dev/disk2',
|
device: '/dev/disk2',
|
||||||
@ -123,19 +123,19 @@ describe('Model: selectionState', function() {
|
|||||||
selectionState.selectDrive('/dev/disk2');
|
selectionState.selectDrive('/dev/disk2');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
selectionState.clear();
|
selectionState.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.hasDrive()', function() {
|
describe('.hasDrive()', function () {
|
||||||
it('should return true', function() {
|
it('should return true', function () {
|
||||||
const hasDrive = selectionState.hasDrive();
|
const hasDrive = selectionState.hasDrive();
|
||||||
expect(hasDrive).to.be.true;
|
expect(hasDrive).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.selectDrive()', function() {
|
describe('.selectDrive()', function () {
|
||||||
it('should queue the drive', function() {
|
it('should queue the drive', function () {
|
||||||
selectionState.selectDrive('/dev/disk5');
|
selectionState.selectDrive('/dev/disk5');
|
||||||
const drives = selectionState.getSelectedDevices();
|
const drives = selectionState.getSelectedDevices();
|
||||||
const lastDriveDevice = _.last(drives);
|
const lastDriveDevice = _.last(drives);
|
||||||
@ -151,8 +151,8 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.deselectDrive()', function() {
|
describe('.deselectDrive()', function () {
|
||||||
it('should clear drive', function() {
|
it('should clear drive', function () {
|
||||||
const firstDevice = selectionState.getSelectedDevices()[0];
|
const firstDevice = selectionState.getSelectedDevices()[0];
|
||||||
selectionState.deselectDrive(firstDevice);
|
selectionState.deselectDrive(firstDevice);
|
||||||
const devices = selectionState.getSelectedDevices();
|
const devices = selectionState.getSelectedDevices();
|
||||||
@ -160,8 +160,8 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getSelectedDrives()', function() {
|
describe('.getSelectedDrives()', function () {
|
||||||
it('should return that single selected drive', function() {
|
it('should return that single selected drive', function () {
|
||||||
expect(selectionState.getSelectedDrives()).to.deep.equal([
|
expect(selectionState.getSelectedDrives()).to.deep.equal([
|
||||||
{
|
{
|
||||||
device: '/dev/disk2',
|
device: '/dev/disk2',
|
||||||
@ -174,8 +174,8 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given several drives', function() {
|
describe('given several drives', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
this.drives = [
|
this.drives = [
|
||||||
{
|
{
|
||||||
device: '/dev/sdb',
|
device: '/dev/sdb',
|
||||||
@ -206,26 +206,26 @@ describe('Model: selectionState', function() {
|
|||||||
selectionState.selectDrive(this.drives[1].device);
|
selectionState.selectDrive(this.drives[1].device);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
selectionState.clear();
|
selectionState.clear();
|
||||||
availableDrives.setDrives([]);
|
availableDrives.setDrives([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to add more drives', function() {
|
it('should be able to add more drives', function () {
|
||||||
selectionState.selectDrive(this.drives[2].device);
|
selectionState.selectDrive(this.drives[2].device);
|
||||||
expect(selectionState.getSelectedDevices()).to.deep.equal(
|
expect(selectionState.getSelectedDevices()).to.deep.equal(
|
||||||
_.map(this.drives, 'device'),
|
_.map(this.drives, 'device'),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to remove drives', function() {
|
it('should be able to remove drives', function () {
|
||||||
selectionState.deselectDrive(this.drives[1].device);
|
selectionState.deselectDrive(this.drives[1].device);
|
||||||
expect(selectionState.getSelectedDevices()).to.deep.equal([
|
expect(selectionState.getSelectedDevices()).to.deep.equal([
|
||||||
this.drives[0].device,
|
this.drives[0].device,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should keep system drives selected', function() {
|
it('should keep system drives selected', function () {
|
||||||
const systemDrive = {
|
const systemDrive = {
|
||||||
device: '/dev/disk0',
|
device: '/dev/disk0',
|
||||||
name: 'USB Drive 0',
|
name: 'USB Drive 0',
|
||||||
@ -244,7 +244,7 @@ describe('Model: selectionState', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to remove a drive', function() {
|
it('should be able to remove a drive', function () {
|
||||||
expect(selectionState.getSelectedDevices().length).to.equal(2);
|
expect(selectionState.getSelectedDevices().length).to.equal(2);
|
||||||
selectionState.toggleDrive(this.drives[0].device);
|
selectionState.toggleDrive(this.drives[0].device);
|
||||||
expect(selectionState.getSelectedDevices()).to.deep.equal([
|
expect(selectionState.getSelectedDevices()).to.deep.equal([
|
||||||
@ -252,15 +252,15 @@ describe('Model: selectionState', function() {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.deselectAllDrives()', function() {
|
describe('.deselectAllDrives()', function () {
|
||||||
it('should remove all drives', function() {
|
it('should remove all drives', function () {
|
||||||
selectionState.deselectAllDrives();
|
selectionState.deselectAllDrives();
|
||||||
expect(selectionState.getSelectedDevices()).to.deep.equal([]);
|
expect(selectionState.getSelectedDevices()).to.deep.equal([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.deselectDrive()', function() {
|
describe('.deselectDrive()', function () {
|
||||||
it('should clear drives', function() {
|
it('should clear drives', function () {
|
||||||
const devices = selectionState.getSelectedDevices();
|
const devices = selectionState.getSelectedDevices();
|
||||||
selectionState.deselectDrive(devices[0]);
|
selectionState.deselectDrive(devices[0]);
|
||||||
selectionState.deselectDrive(devices[1]);
|
selectionState.deselectDrive(devices[1]);
|
||||||
@ -268,8 +268,8 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getSelectedDrives()', function() {
|
describe('.getSelectedDrives()', function () {
|
||||||
it('should return the selected drives', function() {
|
it('should return the selected drives', function () {
|
||||||
expect(selectionState.getSelectedDrives()).to.deep.equal([
|
expect(selectionState.getSelectedDrives()).to.deep.equal([
|
||||||
{
|
{
|
||||||
device: '/dev/sdb',
|
device: '/dev/sdb',
|
||||||
@ -291,9 +291,9 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given no drive', function() {
|
describe('given no drive', function () {
|
||||||
describe('.selectDrive()', 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([
|
||||||
{
|
{
|
||||||
device: '/dev/disk5',
|
device: '/dev/disk5',
|
||||||
@ -307,7 +307,7 @@ describe('Model: selectionState', function() {
|
|||||||
expect(selectionState.getSelectedDevices()[0]).to.equal('/dev/disk5');
|
expect(selectionState.getSelectedDevices()[0]).to.equal('/dev/disk5');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if drive is read-only', function() {
|
it('should throw if drive is read-only', function () {
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
{
|
{
|
||||||
device: '/dev/disk1',
|
device: '/dev/disk1',
|
||||||
@ -317,12 +317,12 @@ describe('Model: selectionState', function() {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectDrive('/dev/disk1');
|
selectionState.selectDrive('/dev/disk1');
|
||||||
}).to.throw('The drive is write-protected');
|
}).to.throw('The drive is write-protected');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if the drive is not available', function() {
|
it('should throw if the drive is not available', function () {
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
{
|
{
|
||||||
device: '/dev/disk1',
|
device: '/dev/disk1',
|
||||||
@ -332,13 +332,13 @@ describe('Model: selectionState', function() {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectDrive('/dev/disk5');
|
selectionState.selectDrive('/dev/disk5');
|
||||||
}).to.throw('The drive is not available: /dev/disk5');
|
}).to.throw('The drive is not available: /dev/disk5');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if device is not a string', function() {
|
it('should throw if device is not a string', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
selectionState.selectDrive(123);
|
selectionState.selectDrive(123);
|
||||||
}).to.throw('Invalid drive: 123');
|
}).to.throw('Invalid drive: 123');
|
||||||
@ -346,8 +346,8 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given an image', function() {
|
describe('given an image', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
this.image = {
|
this.image = {
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -362,8 +362,8 @@ describe('Model: selectionState', function() {
|
|||||||
selectionState.selectImage(this.image);
|
selectionState.selectImage(this.image);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.selectDrive()', 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([
|
||||||
{
|
{
|
||||||
device: '/dev/disk2',
|
device: '/dev/disk2',
|
||||||
@ -373,48 +373,48 @@ describe('Model: selectionState', function() {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectDrive('/dev/disk2');
|
selectionState.selectDrive('/dev/disk2');
|
||||||
}).to.throw('The drive is not large enough');
|
}).to.throw('The drive is not large enough');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getImage()', function() {
|
describe('.getImage()', function () {
|
||||||
it('should return the image', function() {
|
it('should return the image', function () {
|
||||||
expect(selectionState.getImage()).to.deep.equal(this.image);
|
expect(selectionState.getImage()).to.deep.equal(this.image);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getImagePath()', function() {
|
describe('.getImagePath()', function () {
|
||||||
it('should return the image path', function() {
|
it('should return the image path', function () {
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImagePath();
|
||||||
expect(imagePath).to.equal('foo.img');
|
expect(imagePath).to.equal('foo.img');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getImageSize()', function() {
|
describe('.getImageSize()', function () {
|
||||||
it('should return the image size', function() {
|
it('should return the image size', function () {
|
||||||
const imageSize = selectionState.getImageSize();
|
const imageSize = selectionState.getImageSize();
|
||||||
expect(imageSize).to.equal(999999999);
|
expect(imageSize).to.equal(999999999);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getImageUrl()', function() {
|
describe('.getImageUrl()', function () {
|
||||||
it('should return the image url', function() {
|
it('should return the image url', function () {
|
||||||
const imageUrl = selectionState.getImageUrl();
|
const imageUrl = selectionState.getImageUrl();
|
||||||
expect(imageUrl).to.equal('https://www.raspbian.org');
|
expect(imageUrl).to.equal('https://www.raspbian.org');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getImageName()', function() {
|
describe('.getImageName()', function () {
|
||||||
it('should return the image name', function() {
|
it('should return the image name', function () {
|
||||||
const imageName = selectionState.getImageName();
|
const imageName = selectionState.getImageName();
|
||||||
expect(imageName).to.equal('Raspbian');
|
expect(imageName).to.equal('Raspbian');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getImageLogo()', function() {
|
describe('.getImageLogo()', function () {
|
||||||
it('should return the image logo', function() {
|
it('should return the image logo', function () {
|
||||||
const imageLogo = selectionState.getImageLogo();
|
const imageLogo = selectionState.getImageLogo();
|
||||||
expect(imageLogo).to.equal(
|
expect(imageLogo).to.equal(
|
||||||
'<svg><text fill="red">Raspbian</text></svg>',
|
'<svg><text fill="red">Raspbian</text></svg>',
|
||||||
@ -422,29 +422,29 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getImageSupportUrl()', function() {
|
describe('.getImageSupportUrl()', function () {
|
||||||
it('should return the image support url', function() {
|
it('should return the image support url', function () {
|
||||||
const imageSupportUrl = selectionState.getImageSupportUrl();
|
const imageSupportUrl = selectionState.getImageSupportUrl();
|
||||||
expect(imageSupportUrl).to.equal('https://www.raspbian.org/forums/');
|
expect(imageSupportUrl).to.equal('https://www.raspbian.org/forums/');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getImageRecommendedDriveSize()', function() {
|
describe('.getImageRecommendedDriveSize()', function () {
|
||||||
it('should return the image recommended drive size', function() {
|
it('should return the image recommended drive size', function () {
|
||||||
const imageRecommendedDriveSize = selectionState.getImageRecommendedDriveSize();
|
const imageRecommendedDriveSize = selectionState.getImageRecommendedDriveSize();
|
||||||
expect(imageRecommendedDriveSize).to.equal(1000000000);
|
expect(imageRecommendedDriveSize).to.equal(1000000000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.hasImage()', function() {
|
describe('.hasImage()', function () {
|
||||||
it('should return true', function() {
|
it('should return true', function () {
|
||||||
const hasImage = selectionState.hasImage();
|
const hasImage = selectionState.hasImage();
|
||||||
expect(hasImage).to.be.true;
|
expect(hasImage).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.selectImage()', 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',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -459,8 +459,8 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.deselectImage()', function() {
|
describe('.deselectImage()', function () {
|
||||||
it('should clear the image', function() {
|
it('should clear the image', function () {
|
||||||
selectionState.deselectImage();
|
selectionState.deselectImage();
|
||||||
|
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImagePath();
|
||||||
@ -471,11 +471,11 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given no image', function() {
|
describe('given no image', function () {
|
||||||
describe('.selectImage()', function() {
|
describe('.selectImage()', function () {
|
||||||
afterEach(selectionState.clear);
|
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',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -489,7 +489,7 @@ describe('Model: selectionState', function() {
|
|||||||
expect(imageSize).to.equal(999999999);
|
expect(imageSize).to.equal(999999999);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to set an image with an archive extension', function() {
|
it('should be able to set an image with an archive extension', function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.zip',
|
path: 'foo.zip',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -502,7 +502,7 @@ describe('Model: selectionState', function() {
|
|||||||
expect(imagePath).to.equal('foo.zip');
|
expect(imagePath).to.equal('foo.zip');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should infer a compressed raw image if the penultimate extension is missing', function() {
|
it('should infer a compressed raw image if the penultimate extension is missing', function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.xz',
|
path: 'foo.xz',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -515,7 +515,7 @@ describe('Model: selectionState', function() {
|
|||||||
expect(imagePath).to.equal('foo.xz');
|
expect(imagePath).to.equal('foo.xz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should infer a compressed raw image if the penultimate extension is not a file extension', function() {
|
it('should infer a compressed raw image if the penultimate extension is not a file extension', function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'something.linux-x86-64.gz',
|
path: 'something.linux-x86-64.gz',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -528,8 +528,8 @@ describe('Model: selectionState', function() {
|
|||||||
expect(imagePath).to.equal('something.linux-x86-64.gz');
|
expect(imagePath).to.equal('something.linux-x86-64.gz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if no path', function() {
|
it('should throw if no path', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
size: 999999999,
|
size: 999999999,
|
||||||
@ -538,8 +538,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Missing image fields: path');
|
}).to.throw('Missing image fields: path');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if path is not a string', function() {
|
it('should throw if path is not a string', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 123,
|
path: 123,
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -549,8 +549,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image path: 123');
|
}).to.throw('Invalid image path: 123');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if no extension', function() {
|
it('should throw if no extension', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
size: 999999999,
|
size: 999999999,
|
||||||
@ -559,8 +559,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Missing image fields: extension');
|
}).to.throw('Missing image fields: extension');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if extension is not a string', function() {
|
it('should throw if extension is not a string', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 1,
|
extension: 1,
|
||||||
@ -570,8 +570,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image extension: 1');
|
}).to.throw('Invalid image extension: 1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw if the extension doesn't match the path and there is no archive extension", function() {
|
it("should throw if the extension doesn't match the path and there is no archive extension", function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'iso',
|
extension: 'iso',
|
||||||
@ -581,8 +581,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Missing image archive extension');
|
}).to.throw('Missing image archive extension');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw if the extension doesn't match the path and the archive extension is not a string", function() {
|
it("should throw if the extension doesn't match the path and the archive extension is not a string", function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'iso',
|
extension: 'iso',
|
||||||
@ -593,8 +593,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Missing image archive extension');
|
}).to.throw('Missing image archive extension');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw if the archive extension doesn't match the last path extension in a compressed image", function() {
|
it("should throw if the archive extension doesn't match the last path extension in a compressed image", function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img.xz',
|
path: 'foo.img.xz',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -605,8 +605,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Image archive extension mismatch: gz and xz');
|
}).to.throw('Image archive extension mismatch: gz and xz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if the extension is not recognised in an uncompressed image', function() {
|
it('should throw if the extension is not recognised in an uncompressed image', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.ifg',
|
path: 'foo.ifg',
|
||||||
extension: 'ifg',
|
extension: 'ifg',
|
||||||
@ -616,8 +616,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image extension: ifg');
|
}).to.throw('Invalid image extension: ifg');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if the extension is not recognised in a compressed image', function() {
|
it('should throw if the extension is not recognised in a compressed image', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.ifg.gz',
|
path: 'foo.ifg.gz',
|
||||||
extension: 'ifg',
|
extension: 'ifg',
|
||||||
@ -628,8 +628,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image extension: ifg');
|
}).to.throw('Invalid image extension: ifg');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if the archive extension is not recognised', function() {
|
it('should throw if the archive extension is not recognised', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img.ifg',
|
path: 'foo.img.ifg',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -640,8 +640,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image archive extension: ifg');
|
}).to.throw('Invalid image archive extension: ifg');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if the original size is not a number', function() {
|
it('should throw if the original size is not a number', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -652,8 +652,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image compressed size: 999999999');
|
}).to.throw('Invalid image compressed size: 999999999');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if the original size is a float number', function() {
|
it('should throw if the original size is a float number', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -664,8 +664,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image compressed size: 999999999.999');
|
}).to.throw('Invalid image compressed size: 999999999.999');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if the original size is negative', function() {
|
it('should throw if the original size is negative', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -676,8 +676,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image compressed size: -1');
|
}).to.throw('Invalid image compressed size: -1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if the final size is not a number', function() {
|
it('should throw if the final size is not a number', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -687,8 +687,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image size: 999999999');
|
}).to.throw('Invalid image size: 999999999');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if the final size is a float number', function() {
|
it('should throw if the final size is a float number', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -698,8 +698,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image size: 999999999.999');
|
}).to.throw('Invalid image size: 999999999.999');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if the final size is negative', function() {
|
it('should throw if the final size is negative', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -709,8 +709,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image size: -1');
|
}).to.throw('Invalid image size: -1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw if url is defined but it's not a string", function() {
|
it("should throw if url is defined but it's not a string", function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -721,8 +721,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image url: 1234');
|
}).to.throw('Invalid image url: 1234');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw if name is defined but it's not a string", function() {
|
it("should throw if name is defined but it's not a string", function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -733,8 +733,8 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image name: 1234');
|
}).to.throw('Invalid image name: 1234');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw if logo is defined but it's not a string", function() {
|
it("should throw if logo is defined but it's not a string", function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
selectionState.selectImage({
|
selectionState.selectImage({
|
||||||
path: 'foo.img',
|
path: 'foo.img',
|
||||||
extension: 'img',
|
extension: 'img',
|
||||||
@ -745,7 +745,7 @@ describe('Model: selectionState', function() {
|
|||||||
}).to.throw('Invalid image logo: 1234');
|
}).to.throw('Invalid image logo: 1234');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should de-select a previously selected not-large-enough drive', function() {
|
it('should de-select a previously selected not-large-enough drive', function () {
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
{
|
{
|
||||||
device: '/dev/disk1',
|
device: '/dev/disk1',
|
||||||
@ -769,7 +769,7 @@ describe('Model: selectionState', function() {
|
|||||||
selectionState.deselectImage();
|
selectionState.deselectImage();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should de-select a previously selected not-recommended drive', function() {
|
it('should de-select a previously selected not-recommended drive', function () {
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
{
|
{
|
||||||
device: '/dev/disk1',
|
device: '/dev/disk1',
|
||||||
@ -794,7 +794,7 @@ describe('Model: selectionState', function() {
|
|||||||
selectionState.deselectImage();
|
selectionState.deselectImage();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should de-select a previously selected source drive', function() {
|
it('should de-select a previously selected source drive', function () {
|
||||||
const imagePath =
|
const imagePath =
|
||||||
process.platform === 'win32'
|
process.platform === 'win32'
|
||||||
? 'E:\\bar\\foo.img'
|
? 'E:\\bar\\foo.img'
|
||||||
@ -830,8 +830,8 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given a drive and an image', function() {
|
describe('given a drive and an image', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
{
|
{
|
||||||
device: '/dev/disk1',
|
device: '/dev/disk1',
|
||||||
@ -851,8 +851,8 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.clear()', function() {
|
describe('.clear()', function () {
|
||||||
it('should clear all selections', function() {
|
it('should clear all selections', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.true;
|
expect(selectionState.hasDrive()).to.be.true;
|
||||||
expect(selectionState.hasImage()).to.be.true;
|
expect(selectionState.hasImage()).to.be.true;
|
||||||
|
|
||||||
@ -863,59 +863,59 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.deselectImage()', function() {
|
describe('.deselectImage()', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
selectionState.deselectImage();
|
selectionState.deselectImage();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImagePath() should return undefined', function() {
|
it('getImagePath() should return undefined', function () {
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImagePath();
|
||||||
expect(imagePath).to.be.undefined;
|
expect(imagePath).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImageSize() should return undefined', function() {
|
it('getImageSize() should return undefined', function () {
|
||||||
const imageSize = selectionState.getImageSize();
|
const imageSize = selectionState.getImageSize();
|
||||||
expect(imageSize).to.be.undefined;
|
expect(imageSize).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not clear any drives', function() {
|
it('should not clear any drives', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.true;
|
expect(selectionState.hasDrive()).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hasImage() should return false', function() {
|
it('hasImage() should return false', function () {
|
||||||
const hasImage = selectionState.hasImage();
|
const hasImage = selectionState.hasImage();
|
||||||
expect(hasImage).to.be.false;
|
expect(hasImage).to.be.false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.deselectAllDrives()', function() {
|
describe('.deselectAllDrives()', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
selectionState.deselectAllDrives();
|
selectionState.deselectAllDrives();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImagePath() should return the image path', function() {
|
it('getImagePath() should return the image path', function () {
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImagePath();
|
||||||
expect(imagePath).to.equal('foo.img');
|
expect(imagePath).to.equal('foo.img');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImageSize() should return the image size', function() {
|
it('getImageSize() should return the image size', function () {
|
||||||
const imageSize = selectionState.getImageSize();
|
const imageSize = selectionState.getImageSize();
|
||||||
expect(imageSize).to.equal(999999999);
|
expect(imageSize).to.equal(999999999);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hasDrive() should return false', function() {
|
it('hasDrive() should return false', function () {
|
||||||
const hasDrive = selectionState.hasDrive();
|
const hasDrive = selectionState.hasDrive();
|
||||||
expect(hasDrive).to.be.false;
|
expect(hasDrive).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not clear the image', function() {
|
it('should not clear the image', function () {
|
||||||
expect(selectionState.hasImage()).to.be.true;
|
expect(selectionState.hasImage()).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given several drives', function() {
|
describe('given several drives', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
{
|
{
|
||||||
device: '/dev/disk1',
|
device: '/dev/disk1',
|
||||||
@ -949,8 +949,8 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.clear()', function() {
|
describe('.clear()', function () {
|
||||||
it('should clear all selections', function() {
|
it('should clear all selections', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.true;
|
expect(selectionState.hasDrive()).to.be.true;
|
||||||
expect(selectionState.hasImage()).to.be.true;
|
expect(selectionState.hasImage()).to.be.true;
|
||||||
|
|
||||||
@ -962,9 +962,9 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.toggleDrive()', function() {
|
describe('.toggleDrive()', function () {
|
||||||
describe('given a selected drive', function() {
|
describe('given a selected drive', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
this.drive = {
|
this.drive = {
|
||||||
device: '/dev/sdb',
|
device: '/dev/sdb',
|
||||||
description: 'DataTraveler 2.0',
|
description: 'DataTraveler 2.0',
|
||||||
@ -992,18 +992,18 @@ describe('Model: selectionState', function() {
|
|||||||
selectionState.selectDrive(this.drive.device);
|
selectionState.selectDrive(this.drive.device);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
selectionState.clear();
|
selectionState.clear();
|
||||||
availableDrives.setDrives([]);
|
availableDrives.setDrives([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to remove the drive', function() {
|
it('should be able to remove the drive', function () {
|
||||||
expect(selectionState.hasDrive()).to.be.true;
|
expect(selectionState.hasDrive()).to.be.true;
|
||||||
selectionState.toggleDrive(this.drive.device);
|
selectionState.toggleDrive(this.drive.device);
|
||||||
expect(selectionState.hasDrive()).to.be.false;
|
expect(selectionState.hasDrive()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not replace a different 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',
|
||||||
@ -1021,8 +1021,8 @@ describe('Model: selectionState', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given no selected drive', function() {
|
describe('given no selected drive', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
selectionState.clear();
|
selectionState.clear();
|
||||||
|
|
||||||
availableDrives.setDrives([
|
availableDrives.setDrives([
|
||||||
@ -1041,11 +1041,11 @@ describe('Model: selectionState', function() {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
availableDrives.setDrives([]);
|
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 2',
|
name: 'USB Drive 2',
|
||||||
|
@ -18,206 +18,80 @@ import { expect } from 'chai';
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { stub } from 'sinon';
|
import { stub } from 'sinon';
|
||||||
|
|
||||||
import * as localSettings from '../../../lib/gui/app/models/local-settings';
|
|
||||||
import * as settings from '../../../lib/gui/app/models/settings';
|
import * as settings from '../../../lib/gui/app/models/settings';
|
||||||
|
|
||||||
async function checkError(promise: Promise<any>, fn: (err: Error) => void) {
|
async function checkError(promise: Promise<any>, fn: (err: Error) => any) {
|
||||||
try {
|
try {
|
||||||
await promise;
|
await promise;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
fn(error);
|
await fn(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Error('Expected error was not thrown');
|
throw new Error('Expected error was not thrown');
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Browser: settings', function() {
|
describe('Browser: settings', () => {
|
||||||
beforeEach(function() {
|
it('should be able to set and read values', async () => {
|
||||||
return settings.reset();
|
expect(await settings.get('foo')).to.be.undefined;
|
||||||
|
await settings.set('foo', true);
|
||||||
|
expect(await settings.get('foo')).to.be.true;
|
||||||
|
await settings.set('foo', false);
|
||||||
|
expect(await settings.get('foo')).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const DEFAULT_SETTINGS = _.cloneDeep(settings.DEFAULT_SETTINGS);
|
describe('.set()', () => {
|
||||||
|
it('should not change the application state if storing to the local machine results in an error', async () => {
|
||||||
it('should be able to set and read values', function() {
|
|
||||||
expect(settings.get('foo')).to.be.undefined;
|
|
||||||
return settings
|
|
||||||
.set('foo', true)
|
|
||||||
.then(() => {
|
|
||||||
expect(settings.get('foo')).to.be.true;
|
|
||||||
return settings.set('foo', false);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
expect(settings.get('foo')).to.be.false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('.reset()', function() {
|
|
||||||
it('should reset the settings to their default values', function() {
|
|
||||||
expect(settings.getAll()).to.deep.equal(DEFAULT_SETTINGS);
|
|
||||||
return settings
|
|
||||||
.set('foo', 1234)
|
|
||||||
.then(() => {
|
|
||||||
expect(settings.getAll()).to.not.deep.equal(DEFAULT_SETTINGS);
|
|
||||||
return settings.reset();
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
expect(settings.getAll()).to.deep.equal(DEFAULT_SETTINGS);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reset the local settings to their default values', function() {
|
|
||||||
return settings
|
|
||||||
.set('foo', 1234)
|
|
||||||
.then(localSettings.readAll)
|
|
||||||
.then(data => {
|
|
||||||
expect(data).to.not.deep.equal(DEFAULT_SETTINGS);
|
|
||||||
return settings.reset();
|
|
||||||
})
|
|
||||||
.then(localSettings.readAll)
|
|
||||||
.then(data => {
|
|
||||||
expect(data).to.deep.equal(DEFAULT_SETTINGS);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('given the local settings are cleared', function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
return localSettings.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set the local settings to their default values', function() {
|
|
||||||
return settings
|
|
||||||
.reset()
|
|
||||||
.then(localSettings.readAll)
|
|
||||||
.then(data => {
|
|
||||||
expect(data).to.deep.equal(DEFAULT_SETTINGS);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('.set()', function() {
|
|
||||||
it('should store the settings to the local machine', function() {
|
|
||||||
return localSettings
|
|
||||||
.readAll()
|
|
||||||
.then(data => {
|
|
||||||
expect(data.foo).to.be.undefined;
|
|
||||||
expect(data.bar).to.be.undefined;
|
|
||||||
return settings.set('foo', 'bar');
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
return settings.set('bar', 'baz');
|
|
||||||
})
|
|
||||||
.then(localSettings.readAll)
|
|
||||||
.then(data => {
|
|
||||||
expect(data.foo).to.equal('bar');
|
|
||||||
expect(data.bar).to.equal('baz');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not change the application state if storing to the local machine results in an error', async function() {
|
|
||||||
await settings.set('foo', 'bar');
|
await settings.set('foo', 'bar');
|
||||||
expect(settings.get('foo')).to.equal('bar');
|
expect(await settings.get('foo')).to.equal('bar');
|
||||||
|
|
||||||
const localSettingsWriteAllStub = stub(localSettings, 'writeAll');
|
const writeConfigFileStub = stub(settings, 'writeConfigFile');
|
||||||
localSettingsWriteAllStub.returns(
|
writeConfigFileStub.returns(Promise.reject(new Error('settings error')));
|
||||||
Promise.reject(new Error('localSettings error')),
|
|
||||||
);
|
|
||||||
|
|
||||||
await checkError(settings.set('foo', 'baz'), error => {
|
const p = settings.set('foo', 'baz');
|
||||||
|
await checkError(p, async (error) => {
|
||||||
expect(error).to.be.an.instanceof(Error);
|
expect(error).to.be.an.instanceof(Error);
|
||||||
expect(error.message).to.equal('localSettings error');
|
expect(error.message).to.equal('settings error');
|
||||||
localSettingsWriteAllStub.restore();
|
expect(await settings.get('foo')).to.equal('bar');
|
||||||
expect(settings.get('foo')).to.equal('bar');
|
|
||||||
});
|
});
|
||||||
|
writeConfigFileStub.restore();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.load()', function() {
|
describe('.set()', () => {
|
||||||
it('should extend the application state with the local settings content', function() {
|
it('should set an unknown key', async () => {
|
||||||
const object = {
|
expect(await settings.get('foobar')).to.be.undefined;
|
||||||
foo: 'bar',
|
await settings.set('foobar', true);
|
||||||
};
|
expect(await settings.get('foobar')).to.be.true;
|
||||||
|
|
||||||
expect(settings.getAll()).to.deep.equal(DEFAULT_SETTINGS);
|
|
||||||
|
|
||||||
return localSettings
|
|
||||||
.writeAll(object)
|
|
||||||
.then(() => {
|
|
||||||
expect(settings.getAll()).to.deep.equal(DEFAULT_SETTINGS);
|
|
||||||
return settings.load();
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
expect(settings.getAll()).to.deep.equal(
|
|
||||||
_.assign({}, DEFAULT_SETTINGS, object),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should keep the application state intact if there are no local settings', function() {
|
it('should set the key to undefined if no value', async () => {
|
||||||
expect(settings.getAll()).to.deep.equal(DEFAULT_SETTINGS);
|
|
||||||
return localSettings
|
|
||||||
.clear()
|
|
||||||
.then(settings.load)
|
|
||||||
.then(() => {
|
|
||||||
expect(settings.getAll()).to.deep.equal(DEFAULT_SETTINGS);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('.set()', function() {
|
|
||||||
it('should set an unknown key', function() {
|
|
||||||
expect(settings.get('foobar')).to.be.undefined;
|
|
||||||
return settings.set('foobar', true).then(() => {
|
|
||||||
expect(settings.get('foobar')).to.be.true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set the key to undefined if no value', function() {
|
|
||||||
return settings
|
|
||||||
.set('foo', 'bar')
|
|
||||||
.then(() => {
|
|
||||||
expect(settings.get('foo')).to.equal('bar');
|
|
||||||
return settings.set('foo', undefined);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
expect(settings.get('foo')).to.be.undefined;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should store the setting to the local machine', function() {
|
|
||||||
return localSettings
|
|
||||||
.readAll()
|
|
||||||
.then(data => {
|
|
||||||
expect(data.foo).to.be.undefined;
|
|
||||||
return settings.set('foo', 'bar');
|
|
||||||
})
|
|
||||||
.then(localSettings.readAll)
|
|
||||||
.then(data => {
|
|
||||||
expect(data.foo).to.equal('bar');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not change the application state if storing to the local machine results in an error', async function() {
|
|
||||||
await settings.set('foo', 'bar');
|
await settings.set('foo', 'bar');
|
||||||
expect(settings.get('foo')).to.equal('bar');
|
expect(await settings.get('foo')).to.equal('bar');
|
||||||
|
await settings.set('foo', undefined);
|
||||||
const localSettingsWriteAllStub = stub(localSettings, 'writeAll');
|
expect(await settings.get('foo')).to.be.undefined;
|
||||||
localSettingsWriteAllStub.returns(
|
|
||||||
Promise.reject(new Error('localSettings error')),
|
|
||||||
);
|
|
||||||
|
|
||||||
await checkError(settings.set('foo', 'baz'), error => {
|
|
||||||
expect(error).to.be.an.instanceof(Error);
|
|
||||||
expect(error.message).to.equal('localSettings error');
|
|
||||||
localSettingsWriteAllStub.restore();
|
|
||||||
expect(settings.get('foo')).to.equal('bar');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('.getAll()', function() {
|
it('should store the setting to the local machine', async () => {
|
||||||
it('should initial return all default values', function() {
|
const data = await settings.readAll();
|
||||||
expect(settings.getAll()).to.deep.equal(DEFAULT_SETTINGS);
|
expect(data.foo).to.be.undefined;
|
||||||
|
await settings.set('foo', 'bar');
|
||||||
|
const data1 = await settings.readAll();
|
||||||
|
expect(data1.foo).to.equal('bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change the application state if storing to the local machine results in an error', async () => {
|
||||||
|
await settings.set('foo', 'bar');
|
||||||
|
expect(await settings.get('foo')).to.equal('bar');
|
||||||
|
|
||||||
|
const writeConfigFileStub = stub(settings, 'writeConfigFile');
|
||||||
|
writeConfigFileStub.returns(Promise.reject(new Error('settings error')));
|
||||||
|
|
||||||
|
await checkError(settings.set('foo', 'baz'), async (error) => {
|
||||||
|
expect(error).to.be.an.instanceof(Error);
|
||||||
|
expect(error.message).to.equal('settings error');
|
||||||
|
expect(await settings.get('foo')).to.equal('bar');
|
||||||
|
});
|
||||||
|
writeConfigFileStub.restore();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -19,8 +19,8 @@ import * as ipc from 'node-ipc';
|
|||||||
|
|
||||||
import('../../../lib/gui/modules/child-writer');
|
import('../../../lib/gui/modules/child-writer');
|
||||||
|
|
||||||
describe('Browser: childWriter', function() {
|
describe('Browser: childWriter', function () {
|
||||||
it('should have the ipc config set to silent', function() {
|
it('should have the ipc config set to silent', function () {
|
||||||
expect(ipc.config.silent).to.be.true;
|
expect(ipc.config.silent).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -86,7 +86,7 @@ describe('Browser: imageWriter', () => {
|
|||||||
let rejectError: Error;
|
let rejectError: Error;
|
||||||
imageWriter
|
imageWriter
|
||||||
.flash(imagePath, [fakeDrive], sourceOptions)
|
.flash(imagePath, [fakeDrive], sourceOptions)
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
rejectError = error;
|
rejectError = error;
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@ -140,7 +140,7 @@ describe('Browser: imageWriter', () => {
|
|||||||
let rejection: Error;
|
let rejection: Error;
|
||||||
imageWriter
|
imageWriter
|
||||||
.flash(imagePath, [fakeDrive], sourceOptions)
|
.flash(imagePath, [fakeDrive], sourceOptions)
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
rejection = error;
|
rejection = error;
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@ -151,8 +151,8 @@ describe('Browser: imageWriter', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.performWrite()', function() {
|
describe('.performWrite()', function () {
|
||||||
it('should set the ipc config to silent', function() {
|
it('should set the ipc config to silent', function () {
|
||||||
// Reset this value as it can persist from other tests
|
// Reset this value as it can persist from other tests
|
||||||
expect(ipc.config.silent).to.be.true;
|
expect(ipc.config.silent).to.be.true;
|
||||||
});
|
});
|
||||||
|
@ -19,13 +19,12 @@ import { expect } from 'chai';
|
|||||||
import * as settings from '../../../lib/gui/app/models/settings';
|
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 () {
|
||||||
describe('.fromFlashState()', function() {
|
describe('.fromFlashState()', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
this.state = {
|
this.state = {
|
||||||
flashing: 1,
|
active: 1,
|
||||||
verifying: 0,
|
type: 'flashing',
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
percentage: 0,
|
percentage: 0,
|
||||||
eta: 15,
|
eta: 15,
|
||||||
@ -36,48 +35,46 @@ describe('Browser: progressStatus', function() {
|
|||||||
settings.set('validateWriteOnSuccess', true);
|
settings.set('validateWriteOnSuccess', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should report 0% if percentage == 0 but speed != 0', function() {
|
it('should report 0% if percentage == 0 but speed != 0', function () {
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal('0% Flashing');
|
expect(progressStatus.fromFlashState(this.state)).to.equal('0% Flashing');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 0, flashing, unmountOnSuccess', function() {
|
it('should handle percentage == 0, flashing, unmountOnSuccess', function () {
|
||||||
this.state.speed = 0;
|
this.state.speed = 0;
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal('Starting...');
|
expect(progressStatus.fromFlashState(this.state)).to.equal('0% Flashing');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 0, flashing, !unmountOnSuccess', function() {
|
it('should handle percentage == 0, flashing, !unmountOnSuccess', function () {
|
||||||
this.state.speed = 0;
|
this.state.speed = 0;
|
||||||
settings.set('unmountOnSuccess', false);
|
settings.set('unmountOnSuccess', false);
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal('Starting...');
|
expect(progressStatus.fromFlashState(this.state)).to.equal('0% Flashing');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 0, verifying, unmountOnSuccess', function() {
|
it('should handle percentage == 0, verifying, unmountOnSuccess', function () {
|
||||||
this.state.speed = 0;
|
this.state.speed = 0;
|
||||||
this.state.flashing = 0;
|
this.state.type = 'verifying';
|
||||||
this.state.verifying = 1;
|
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
||||||
'Validating...',
|
'0% Validating',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 0, verifying, !unmountOnSuccess', function() {
|
it('should handle percentage == 0, verifying, !unmountOnSuccess', function () {
|
||||||
this.state.speed = 0;
|
this.state.speed = 0;
|
||||||
this.state.flashing = 0;
|
this.state.type = 'verifying';
|
||||||
this.state.verifying = 1;
|
|
||||||
settings.set('unmountOnSuccess', false);
|
settings.set('unmountOnSuccess', false);
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
||||||
'Validating...',
|
'0% Validating',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 50, flashing, unmountOnSuccess', function() {
|
it('should handle percentage == 50, flashing, unmountOnSuccess', function () {
|
||||||
this.state.percentage = 50;
|
this.state.percentage = 50;
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
||||||
'50% Flashing',
|
'50% Flashing',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 50, flashing, !unmountOnSuccess', function() {
|
it('should handle percentage == 50, flashing, !unmountOnSuccess', function () {
|
||||||
this.state.percentage = 50;
|
this.state.percentage = 50;
|
||||||
settings.set('unmountOnSuccess', false);
|
settings.set('unmountOnSuccess', false);
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
||||||
@ -85,41 +82,39 @@ describe('Browser: progressStatus', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 50, verifying, unmountOnSuccess', function() {
|
it('should handle percentage == 50, verifying, unmountOnSuccess', function () {
|
||||||
this.state.flashing = 0;
|
|
||||||
this.state.verifying = 1;
|
|
||||||
this.state.percentage = 50;
|
this.state.percentage = 50;
|
||||||
|
this.state.type = 'verifying';
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
||||||
'50% Validating',
|
'50% Validating',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 50, verifying, !unmountOnSuccess', function() {
|
it('should handle percentage == 50, verifying, !unmountOnSuccess', function () {
|
||||||
this.state.flashing = 0;
|
|
||||||
this.state.verifying = 1;
|
|
||||||
this.state.percentage = 50;
|
this.state.percentage = 50;
|
||||||
|
this.state.type = 'verifying';
|
||||||
settings.set('unmountOnSuccess', false);
|
settings.set('unmountOnSuccess', false);
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
||||||
'50% Validating',
|
'50% Validating',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 100, flashing, unmountOnSuccess, validateWriteOnSuccess', function() {
|
it('should handle percentage == 100, flashing, unmountOnSuccess, validateWriteOnSuccess', function () {
|
||||||
this.state.percentage = 100;
|
this.state.percentage = 100;
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
||||||
'Finishing...',
|
'Finishing...',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 100, flashing, unmountOnSuccess, !validateWriteOnSuccess', function() {
|
it('should handle percentage == 100, flashing, unmountOnSuccess, !validateWriteOnSuccess', function () {
|
||||||
this.state.percentage = 100;
|
this.state.percentage = 100;
|
||||||
settings.set('validateWriteOnSuccess', false);
|
settings.set('validateWriteOnSuccess', false);
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
||||||
'Unmounting...',
|
'Finishing...',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 100, flashing, !unmountOnSuccess, !validateWriteOnSuccess', function() {
|
it('should handle percentage == 100, flashing, !unmountOnSuccess, !validateWriteOnSuccess', function () {
|
||||||
this.state.percentage = 100;
|
this.state.percentage = 100;
|
||||||
settings.set('unmountOnSuccess', false);
|
settings.set('unmountOnSuccess', false);
|
||||||
settings.set('validateWriteOnSuccess', false);
|
settings.set('validateWriteOnSuccess', false);
|
||||||
@ -128,18 +123,15 @@ describe('Browser: progressStatus', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 100, verifying, unmountOnSuccess', function() {
|
it('should handle percentage == 100, verifying, unmountOnSuccess', function () {
|
||||||
this.state.flashing = 0;
|
|
||||||
this.state.verifying = 1;
|
|
||||||
this.state.percentage = 100;
|
this.state.percentage = 100;
|
||||||
|
this.state.type = 'verifying';
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
||||||
'Unmounting...',
|
'Finishing...',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle percentage == 100, validatinf, !unmountOnSuccess', function() {
|
it('should handle percentage == 100, validatinf, !unmountOnSuccess', function () {
|
||||||
this.state.flashing = 0;
|
|
||||||
this.state.verifying = 1;
|
|
||||||
this.state.percentage = 100;
|
this.state.percentage = 100;
|
||||||
settings.set('unmountOnSuccess', false);
|
settings.set('unmountOnSuccess', false);
|
||||||
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
expect(progressStatus.fromFlashState(this.state)).to.equal(
|
||||||
|
@ -19,10 +19,10 @@ import { assert, spy } from 'sinon';
|
|||||||
|
|
||||||
import * as windowProgress from '../../../lib/gui/app/os/window-progress';
|
import * as windowProgress from '../../../lib/gui/app/os/window-progress';
|
||||||
|
|
||||||
describe('Browser: WindowProgress', function() {
|
describe('Browser: WindowProgress', function () {
|
||||||
describe('windowProgress', function() {
|
describe('windowProgress', function () {
|
||||||
describe('given a stubbed current window', function() {
|
describe('given a stubbed current window', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
this.setProgressBarSpy = spy();
|
this.setProgressBarSpy = spy();
|
||||||
this.setTitleSpy = spy();
|
this.setTitleSpy = spy();
|
||||||
|
|
||||||
@ -30,82 +30,80 @@ describe('Browser: WindowProgress', function() {
|
|||||||
windowProgress.currentWindow.setTitle = this.setTitleSpy;
|
windowProgress.currentWindow.setTitle = this.setTitleSpy;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
flashing: 1,
|
active: 1,
|
||||||
verifying: 0,
|
type: 'flashing',
|
||||||
successful: 0,
|
|
||||||
failed: 0,
|
failed: 0,
|
||||||
percentage: 85,
|
percentage: 85,
|
||||||
speed: 100,
|
speed: 100,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.set()', function() {
|
describe('.set()', function () {
|
||||||
it('should translate 0-100 percentages to 0-1 ranges', function() {
|
it('should translate 0-100 percentages to 0-1 ranges', function () {
|
||||||
windowProgress.set(this.state);
|
windowProgress.set(this.state);
|
||||||
assert.calledWith(this.setProgressBarSpy, 0.85);
|
assert.calledWith(this.setProgressBarSpy, 0.85);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set 0 given 0', function() {
|
it('should set 0 given 0', function () {
|
||||||
this.state.percentage = 0;
|
this.state.percentage = 0;
|
||||||
windowProgress.set(this.state);
|
windowProgress.set(this.state);
|
||||||
assert.calledWith(this.setProgressBarSpy, 0);
|
assert.calledWith(this.setProgressBarSpy, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set 1 given 100', function() {
|
it('should set 1 given 100', function () {
|
||||||
this.state.percentage = 100;
|
this.state.percentage = 100;
|
||||||
windowProgress.set(this.state);
|
windowProgress.set(this.state);
|
||||||
assert.calledWith(this.setProgressBarSpy, 1);
|
assert.calledWith(this.setProgressBarSpy, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if given a percentage higher than 100', function() {
|
it('should throw if given a percentage higher than 100', function () {
|
||||||
this.state.percentage = 101;
|
this.state.percentage = 101;
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
expect(function() {
|
expect(function () {
|
||||||
windowProgress.set(state);
|
windowProgress.set(state);
|
||||||
}).to.throw('Invalid percentage: 101');
|
}).to.throw('Invalid percentage: 101');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if given a percentage less than 0', function() {
|
it('should throw if given a percentage less than 0', function () {
|
||||||
this.state.percentage = -1;
|
this.state.percentage = -1;
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
expect(function() {
|
expect(function () {
|
||||||
windowProgress.set(state);
|
windowProgress.set(state);
|
||||||
}).to.throw('Invalid percentage: -1');
|
}).to.throw('Invalid percentage: -1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the flashing title', function() {
|
it('should set the flashing title', function () {
|
||||||
windowProgress.set(this.state);
|
windowProgress.set(this.state);
|
||||||
assert.calledWith(this.setTitleSpy, ' – 85% Flashing');
|
assert.calledWith(this.setTitleSpy, ' – 85% Flashing');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the verifying title', function() {
|
it('should set the verifying title', function () {
|
||||||
this.state.flashing = 0;
|
this.state.type = 'verifying';
|
||||||
this.state.verifying = 1;
|
|
||||||
windowProgress.set(this.state);
|
windowProgress.set(this.state);
|
||||||
assert.calledWith(this.setTitleSpy, ' – 85% Validating');
|
assert.calledWith(this.setTitleSpy, ' – 85% Validating');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the starting title', function() {
|
it('should set the starting title', function () {
|
||||||
this.state.percentage = 0;
|
this.state.percentage = 0;
|
||||||
this.state.speed = 0;
|
this.state.speed = 0;
|
||||||
windowProgress.set(this.state);
|
windowProgress.set(this.state);
|
||||||
assert.calledWith(this.setTitleSpy, ' – Starting...');
|
assert.calledWith(this.setTitleSpy, ' – 0% Flashing');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the finishing title', function() {
|
it('should set the finishing title', function () {
|
||||||
this.state.percentage = 100;
|
this.state.percentage = 100;
|
||||||
windowProgress.set(this.state);
|
windowProgress.set(this.state);
|
||||||
assert.calledWith(this.setTitleSpy, ' – Finishing...');
|
assert.calledWith(this.setTitleSpy, ' – Finishing...');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.clear()', function() {
|
describe('.clear()', function () {
|
||||||
it('should set -1', function() {
|
it('should set -1', function () {
|
||||||
windowProgress.clear();
|
windowProgress.clear();
|
||||||
assert.calledWith(this.setProgressBarSpy, -1);
|
assert.calledWith(this.setProgressBarSpy, -1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear the window title', function() {
|
it('should clear the window title', function () {
|
||||||
windowProgress.clear();
|
windowProgress.clear();
|
||||||
assert.calledWith(this.setTitleSpy, '');
|
assert.calledWith(this.setTitleSpy, '');
|
||||||
});
|
});
|
||||||
|
@ -18,16 +18,16 @@ import { expect } from 'chai';
|
|||||||
|
|
||||||
import { middleEllipsis } from '../../../lib/gui/app/utils/middle-ellipsis';
|
import { middleEllipsis } from '../../../lib/gui/app/utils/middle-ellipsis';
|
||||||
|
|
||||||
describe('Browser: MiddleEllipsis', function() {
|
describe('Browser: MiddleEllipsis', function () {
|
||||||
describe('.middleEllipsis()', function() {
|
describe('.middleEllipsis()', function () {
|
||||||
it('should throw error if limit < 3', function() {
|
it('should throw error if limit < 3', function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
middleEllipsis('No', 2);
|
middleEllipsis('No', 2);
|
||||||
}).to.throw('middleEllipsis: Limit should be at least 3');
|
}).to.throw('middleEllipsis: Limit should be at least 3');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given the input length is greater than the limit', function() {
|
describe('given the input length is greater than the limit', function () {
|
||||||
it('should always truncate input to an odd length', function() {
|
it('should always truncate input to an odd length', function () {
|
||||||
const alphabet = 'abcdefghijklmnopqrstuvwxyz';
|
const alphabet = 'abcdefghijklmnopqrstuvwxyz';
|
||||||
expect(middleEllipsis(alphabet, 3)).to.have.lengthOf(3);
|
expect(middleEllipsis(alphabet, 3)).to.have.lengthOf(3);
|
||||||
expect(middleEllipsis(alphabet, 4)).to.have.lengthOf(3);
|
expect(middleEllipsis(alphabet, 4)).to.have.lengthOf(3);
|
||||||
@ -36,7 +36,7 @@ describe('Browser: MiddleEllipsis', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the input if it is within the bounds of limit', function() {
|
it('should return the input if it is within the bounds of limit', function () {
|
||||||
expect(middleEllipsis('Hello', 10)).to.equal('Hello');
|
expect(middleEllipsis('Hello', 10)).to.equal('Hello');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -19,16 +19,16 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import * as errors from '../../lib/shared/errors';
|
import * as errors from '../../lib/shared/errors';
|
||||||
|
|
||||||
describe('Shared: Errors', function() {
|
describe('Shared: Errors', function () {
|
||||||
describe('.HUMAN_FRIENDLY', function() {
|
describe('.HUMAN_FRIENDLY', function () {
|
||||||
it('should be a plain object', function() {
|
it('should be a plain object', function () {
|
||||||
expect(_.isPlainObject(errors.HUMAN_FRIENDLY)).to.be.true;
|
expect(_.isPlainObject(errors.HUMAN_FRIENDLY)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain title and description function properties', function() {
|
it('should contain title and description function properties', function () {
|
||||||
expect(
|
expect(
|
||||||
_.every(
|
_.every(
|
||||||
_.map(errors.HUMAN_FRIENDLY, error => {
|
_.map(errors.HUMAN_FRIENDLY, (error) => {
|
||||||
return _.isFunction(error.title) && _.isFunction(error.description);
|
return _.isFunction(error.title) && _.isFunction(error.description);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@ -36,89 +36,89 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getTitle()', function() {
|
describe('.getTitle()', function () {
|
||||||
it('should accept a string', function() {
|
it('should accept a string', function () {
|
||||||
const error = 'This is an error';
|
const error = 'This is an error';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getTitle(error)).to.equal('This is an error');
|
expect(errors.getTitle(error)).to.equal('This is an error');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should accept a number 0', function() {
|
it('should accept a number 0', function () {
|
||||||
const error = 0;
|
const error = 0;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getTitle(error)).to.equal('0');
|
expect(errors.getTitle(error)).to.equal('0');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should accept a number 1', function() {
|
it('should accept a number 1', function () {
|
||||||
const error = 1;
|
const error = 1;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getTitle(error)).to.equal('1');
|
expect(errors.getTitle(error)).to.equal('1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should accept a number -1', function() {
|
it('should accept a number -1', function () {
|
||||||
const error = -1;
|
const error = -1;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getTitle(error)).to.equal('-1');
|
expect(errors.getTitle(error)).to.equal('-1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should accept an array', function() {
|
it('should accept an array', function () {
|
||||||
const error = [0, 1, 2];
|
const error = [0, 1, 2];
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getTitle(error)).to.equal('0,1,2');
|
expect(errors.getTitle(error)).to.equal('0,1,2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a generic error message if the error is an empty object', function() {
|
it('should return a generic error message if the error is an empty object', function () {
|
||||||
const error = {};
|
const error = {};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a generic error message if the error is undefined', function() {
|
it('should return a generic error message if the error is undefined', function () {
|
||||||
const error = undefined;
|
const error = undefined;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a generic error message if the error is null', function() {
|
it('should return a generic error message if the error is null', function () {
|
||||||
const error = null;
|
const error = null;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the error message', function() {
|
it('should return the error message', function () {
|
||||||
const error = new Error('This is an error');
|
const error = new Error('This is an error');
|
||||||
expect(errors.getTitle(error)).to.equal('This is an error');
|
expect(errors.getTitle(error)).to.equal('This is an error');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the error code if there is no message', function() {
|
it('should return the error code if there is no message', function () {
|
||||||
const error = new Error();
|
const error = new Error();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'MYERROR';
|
error.code = 'MYERROR';
|
||||||
expect(errors.getTitle(error)).to.equal('Error code: MYERROR');
|
expect(errors.getTitle(error)).to.equal('Error code: MYERROR');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should prioritize the message over the code', function() {
|
it('should prioritize the message over the code', function () {
|
||||||
const error = new Error('Foo bar');
|
const error = new Error('Foo bar');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'MYERROR';
|
error.code = 'MYERROR';
|
||||||
expect(errors.getTitle(error)).to.equal('Foo bar');
|
expect(errors.getTitle(error)).to.equal('Foo bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should prioritize the code over the message if the message is an empty string', function() {
|
it('should prioritize the code over the message if the message is an empty string', function () {
|
||||||
const error = new Error('');
|
const error = new Error('');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'MYERROR';
|
error.code = 'MYERROR';
|
||||||
expect(errors.getTitle(error)).to.equal('Error code: MYERROR');
|
expect(errors.getTitle(error)).to.equal('Error code: MYERROR');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should prioritize the code over the message if the message is a blank string', function() {
|
it('should prioritize the code over the message if the message is a blank string', function () {
|
||||||
const error = new Error(' ');
|
const error = new Error(' ');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'MYERROR';
|
error.code = 'MYERROR';
|
||||||
expect(errors.getTitle(error)).to.equal('Error code: MYERROR');
|
expect(errors.getTitle(error)).to.equal('Error code: MYERROR');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should understand an error-like object with a code', function() {
|
it('should understand an error-like object with a code', function () {
|
||||||
const error = {
|
const error = {
|
||||||
code: 'MYERROR',
|
code: 'MYERROR',
|
||||||
};
|
};
|
||||||
@ -127,7 +127,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getTitle(error)).to.equal('Error code: MYERROR');
|
expect(errors.getTitle(error)).to.equal('Error code: MYERROR');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should understand an error-like object with a message', function() {
|
it('should understand an error-like object with a message', function () {
|
||||||
const error = {
|
const error = {
|
||||||
message: 'Hello world',
|
message: 'Hello world',
|
||||||
};
|
};
|
||||||
@ -136,7 +136,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getTitle(error)).to.equal('Hello world');
|
expect(errors.getTitle(error)).to.equal('Hello world');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should understand an error-like object with a message and a code', function() {
|
it('should understand an error-like object with a message and a code', function () {
|
||||||
const error = {
|
const error = {
|
||||||
message: 'Hello world',
|
message: 'Hello world',
|
||||||
code: 'MYERROR',
|
code: 'MYERROR',
|
||||||
@ -146,57 +146,57 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getTitle(error)).to.equal('Hello world');
|
expect(errors.getTitle(error)).to.equal('Hello world');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display an error code 0', function() {
|
it('should display an error code 0', function () {
|
||||||
const error = new Error();
|
const error = new Error();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 0;
|
error.code = 0;
|
||||||
expect(errors.getTitle(error)).to.equal('Error code: 0');
|
expect(errors.getTitle(error)).to.equal('Error code: 0');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display an error code 1', function() {
|
it('should display an error code 1', function () {
|
||||||
const error = new Error();
|
const error = new Error();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 1;
|
error.code = 1;
|
||||||
expect(errors.getTitle(error)).to.equal('Error code: 1');
|
expect(errors.getTitle(error)).to.equal('Error code: 1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display an error code -1', function() {
|
it('should display an error code -1', function () {
|
||||||
const error = new Error();
|
const error = new Error();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = -1;
|
error.code = -1;
|
||||||
expect(errors.getTitle(error)).to.equal('Error code: -1');
|
expect(errors.getTitle(error)).to.equal('Error code: -1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not display an empty string error code', function() {
|
it('should not display an empty string error code', function () {
|
||||||
const error = new Error();
|
const error = new Error();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = '';
|
error.code = '';
|
||||||
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not display a blank string error code', function() {
|
it('should not display a blank string error code', function () {
|
||||||
const error = new Error();
|
const error = new Error();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = ' ';
|
error.code = ' ';
|
||||||
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a generic error message if no information was found', function() {
|
it('should return a generic error message if no information was found', function () {
|
||||||
const error = new Error();
|
const error = new Error();
|
||||||
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a generic error message if no code and the message is empty', function() {
|
it('should return a generic error message if no code and the message is empty', function () {
|
||||||
const error = new Error('');
|
const error = new Error('');
|
||||||
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a generic error message if no code and the message is blank', function() {
|
it('should return a generic error message if no code and the message is blank', function () {
|
||||||
const error = new Error(' ');
|
const error = new Error(' ');
|
||||||
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
expect(errors.getTitle(error)).to.equal('An error ocurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should rephrase an ENOENT error', function() {
|
it('should rephrase an ENOENT error', function () {
|
||||||
const error = new Error('ENOENT error');
|
const error = new Error('ENOENT error');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.path = '/foo/bar';
|
error.path = '/foo/bar';
|
||||||
@ -207,7 +207,7 @@ describe('Shared: Errors', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should rephrase an EPERM error', function() {
|
it('should rephrase an EPERM error', function () {
|
||||||
const error = new Error('EPERM error');
|
const error = new Error('EPERM error');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'EPERM';
|
error.code = 'EPERM';
|
||||||
@ -216,7 +216,7 @@ describe('Shared: Errors', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should rephrase an EACCES error', function() {
|
it('should rephrase an EACCES error', function () {
|
||||||
const error = new Error('EACCES error');
|
const error = new Error('EACCES error');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'EACCES';
|
error.code = 'EACCES';
|
||||||
@ -225,7 +225,7 @@ describe('Shared: Errors', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should rephrase an ENOMEM error', function() {
|
it('should rephrase an ENOMEM error', function () {
|
||||||
const error = new Error('ENOMEM error');
|
const error = new Error('ENOMEM error');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'ENOMEM';
|
error.code = 'ENOMEM';
|
||||||
@ -233,44 +233,44 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getDescription()', function() {
|
describe('.getDescription()', function () {
|
||||||
it('should return an empty string if the error is a string', function() {
|
it('should return an empty string if the error is a string', function () {
|
||||||
const error = 'My error';
|
const error = 'My error';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getDescription(error)).to.equal('');
|
expect(errors.getDescription(error)).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an empty string if the error is a number', function() {
|
it('should return an empty string if the error is a number', function () {
|
||||||
const error = 0;
|
const error = 0;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getDescription(error)).to.equal('');
|
expect(errors.getDescription(error)).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an empty string if the error is an array', function() {
|
it('should return an empty string if the error is an array', function () {
|
||||||
const error = [1, 2, 3];
|
const error = [1, 2, 3];
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getDescription(error)).to.equal('');
|
expect(errors.getDescription(error)).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an empty string if the error is undefined', function() {
|
it('should return an empty string if the error is undefined', function () {
|
||||||
const error = undefined;
|
const error = undefined;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getDescription(error)).to.equal('');
|
expect(errors.getDescription(error)).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an empty string if the error is null', function() {
|
it('should return an empty string if the error is null', function () {
|
||||||
const error = null;
|
const error = null;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getDescription(error)).to.equal('');
|
expect(errors.getDescription(error)).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an empty string if the error is an empty object', function() {
|
it('should return an empty string if the error is an empty object', function () {
|
||||||
const error = {};
|
const error = {};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(errors.getDescription(error)).to.equal('');
|
expect(errors.getDescription(error)).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should understand an error-like object with a description', function() {
|
it('should understand an error-like object with a description', function () {
|
||||||
const error = {
|
const error = {
|
||||||
description: 'My description',
|
description: 'My description',
|
||||||
};
|
};
|
||||||
@ -279,7 +279,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal('My description');
|
expect(errors.getDescription(error)).to.equal('My description');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should understand an error-like object with a stack', function() {
|
it('should understand an error-like object with a stack', function () {
|
||||||
const error = {
|
const error = {
|
||||||
stack: 'My stack',
|
stack: 'My stack',
|
||||||
};
|
};
|
||||||
@ -288,7 +288,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal('My stack');
|
expect(errors.getDescription(error)).to.equal('My stack');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should understand an error-like object with a description and a stack', function() {
|
it('should understand an error-like object with a description and a stack', function () {
|
||||||
const error = {
|
const error = {
|
||||||
description: 'My description',
|
description: 'My description',
|
||||||
stack: 'My stack',
|
stack: 'My stack',
|
||||||
@ -298,7 +298,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal('My description');
|
expect(errors.getDescription(error)).to.equal('My description');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should stringify and beautify an object without any known property', function() {
|
it('should stringify and beautify an object without any known property', function () {
|
||||||
const error = {
|
const error = {
|
||||||
name: 'John Doe',
|
name: 'John Doe',
|
||||||
job: 'Developer',
|
job: 'Developer',
|
||||||
@ -310,33 +310,33 @@ describe('Shared: Errors', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the stack for a basic error', function() {
|
it('should return the stack for a basic error', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should prefer a description property to a stack', function() {
|
it('should prefer a description property to a stack', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.description = 'My description';
|
error.description = 'My description';
|
||||||
expect(errors.getDescription(error)).to.equal('My description');
|
expect(errors.getDescription(error)).to.equal('My description');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the stack if the description is an empty string', function() {
|
it('should return the stack if the description is an empty string', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.description = '';
|
error.description = '';
|
||||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the stack if the description is a blank string', function() {
|
it('should return the stack if the description is a blank string', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.description = ' ';
|
error.description = ' ';
|
||||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get a generic description for ENOENT', function() {
|
it('should get a generic description for ENOENT', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'ENOENT';
|
error.code = 'ENOENT';
|
||||||
@ -345,7 +345,7 @@ describe('Shared: Errors', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get a generic description for EPERM', function() {
|
it('should get a generic description for EPERM', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'EPERM';
|
error.code = 'EPERM';
|
||||||
@ -354,7 +354,7 @@ describe('Shared: Errors', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get a generic description for EACCES', function() {
|
it('should get a generic description for EACCES', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'EACCES';
|
error.code = 'EACCES';
|
||||||
@ -363,7 +363,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal(message);
|
expect(errors.getDescription(error)).to.equal(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get a generic description for ENOMEM', function() {
|
it('should get a generic description for ENOMEM', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'ENOMEM';
|
error.code = 'ENOMEM';
|
||||||
@ -372,7 +372,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal(message);
|
expect(errors.getDescription(error)).to.equal(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should prefer a description property than a code description', function() {
|
it('should prefer a description property than a code description', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'ENOMEM';
|
error.code = 'ENOMEM';
|
||||||
@ -381,8 +381,8 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal('Memory error');
|
expect(errors.getDescription(error)).to.equal('Memory error');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given userFriendlyDescriptionsOnly is false', function() {
|
describe('given userFriendlyDescriptionsOnly is false', function () {
|
||||||
it('should return the stack for a basic error', function() {
|
it('should return the stack for a basic error', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
expect(
|
expect(
|
||||||
errors.getDescription(error, {
|
errors.getDescription(error, {
|
||||||
@ -391,7 +391,7 @@ describe('Shared: Errors', function() {
|
|||||||
).to.equal(error.stack);
|
).to.equal(error.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the stack if the description is an empty string', function() {
|
it('should return the stack if the description is an empty string', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.description = '';
|
error.description = '';
|
||||||
@ -402,7 +402,7 @@ describe('Shared: Errors', function() {
|
|||||||
).to.equal(error.stack);
|
).to.equal(error.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the stack if the description is a blank string', function() {
|
it('should return the stack if the description is a blank string', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.description = ' ';
|
error.description = ' ';
|
||||||
@ -414,8 +414,8 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('given userFriendlyDescriptionsOnly is true', function() {
|
describe('given userFriendlyDescriptionsOnly is true', function () {
|
||||||
it('should return an empty string for a basic error', function() {
|
it('should return an empty string for a basic error', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
expect(
|
expect(
|
||||||
errors.getDescription(error, {
|
errors.getDescription(error, {
|
||||||
@ -424,7 +424,7 @@ describe('Shared: Errors', function() {
|
|||||||
).to.equal('');
|
).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an empty string if the description is an empty string', function() {
|
it('should return an empty string if the description is an empty string', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.description = '';
|
error.description = '';
|
||||||
@ -435,7 +435,7 @@ describe('Shared: Errors', function() {
|
|||||||
).to.equal('');
|
).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an empty string if the description is a blank string', function() {
|
it('should return an empty string if the description is a blank string', function () {
|
||||||
const error = new Error('Foo');
|
const error = new Error('Foo');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.description = ' ';
|
error.description = ' ';
|
||||||
@ -448,8 +448,8 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.createError()', function() {
|
describe('.createError()', function () {
|
||||||
it('should not be a user error', function() {
|
it('should not be a user error', function () {
|
||||||
const error = errors.createError({
|
const error = errors.createError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: 'Something happened',
|
description: 'Something happened',
|
||||||
@ -458,7 +458,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.isUserError(error)).to.be.false;
|
expect(errors.isUserError(error)).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be a user error if `options.report` is false', function() {
|
it('should be a user error if `options.report` is false', function () {
|
||||||
const error = errors.createError({
|
const error = errors.createError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: 'Something happened',
|
description: 'Something happened',
|
||||||
@ -468,7 +468,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.isUserError(error)).to.be.true;
|
expect(errors.isUserError(error)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be a user error if `options.report` evaluates to false', function() {
|
it('should be a user error if `options.report` evaluates to false', function () {
|
||||||
const error = errors.createError({
|
const error = errors.createError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: 'Something happened',
|
description: 'Something happened',
|
||||||
@ -479,7 +479,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.isUserError(error)).to.be.true;
|
expect(errors.isUserError(error)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not be a user error if `options.report` is true', function() {
|
it('should not be a user error if `options.report` is true', function () {
|
||||||
const error = errors.createError({
|
const error = errors.createError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: 'Something happened',
|
description: 'Something happened',
|
||||||
@ -489,7 +489,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.isUserError(error)).to.be.false;
|
expect(errors.isUserError(error)).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not be a user error if `options.report` evaluates to true', function() {
|
it('should not be a user error if `options.report` evaluates to true', function () {
|
||||||
const error = errors.createError({
|
const error = errors.createError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: 'Something happened',
|
description: 'Something happened',
|
||||||
@ -500,7 +500,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.isUserError(error)).to.be.false;
|
expect(errors.isUserError(error)).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be an instance of Error', function() {
|
it('should be an instance of Error', function () {
|
||||||
const error = errors.createError({
|
const error = errors.createError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: 'Something happened',
|
description: 'Something happened',
|
||||||
@ -509,7 +509,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(error).to.be.an.instanceof(Error);
|
expect(error).to.be.an.instanceof(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly add both a title and a description', function() {
|
it('should correctly add both a title and a description', function () {
|
||||||
const error = errors.createError({
|
const error = errors.createError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: 'Something happened',
|
description: 'Something happened',
|
||||||
@ -519,7 +519,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal('Something happened');
|
expect(errors.getDescription(error)).to.equal('Something happened');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly add a code', function() {
|
it('should correctly add a code', function () {
|
||||||
const error = errors.createError({
|
const error = errors.createError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: 'Something happened',
|
description: 'Something happened',
|
||||||
@ -529,7 +529,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(error.code).to.equal('HELLO');
|
expect(error.code).to.equal('HELLO');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly add only a title', function() {
|
it('should correctly add only a title', function () {
|
||||||
const error = errors.createError({
|
const error = errors.createError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
});
|
});
|
||||||
@ -538,7 +538,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore an empty description', function() {
|
it('should ignore an empty description', function () {
|
||||||
const error = errors.createError({
|
const error = errors.createError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: '',
|
description: '',
|
||||||
@ -547,7 +547,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore a blank description', function() {
|
it('should ignore a blank description', function () {
|
||||||
const error = errors.createError({
|
const error = errors.createError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: ' ',
|
description: ' ',
|
||||||
@ -556,14 +556,14 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if no title', function() {
|
it('should throw if no title', function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
errors.createError({});
|
errors.createError({});
|
||||||
}).to.throw('Invalid error title: undefined');
|
}).to.throw('Invalid error title: undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if there is a description but no title', function() {
|
it('should throw if there is a description but no title', function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
errors.createError({
|
errors.createError({
|
||||||
@ -572,7 +572,7 @@ describe('Shared: Errors', function() {
|
|||||||
}).to.throw('Invalid error title: undefined');
|
}).to.throw('Invalid error title: undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if title is empty', function() {
|
it('should throw if title is empty', function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
errors.createError({
|
errors.createError({
|
||||||
title: '',
|
title: '',
|
||||||
@ -580,7 +580,7 @@ describe('Shared: Errors', function() {
|
|||||||
}).to.throw('Invalid error title: ');
|
}).to.throw('Invalid error title: ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if title is blank', function() {
|
it('should throw if title is blank', function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
errors.createError({
|
errors.createError({
|
||||||
title: ' ',
|
title: ' ',
|
||||||
@ -589,8 +589,8 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.createUserError()', function() {
|
describe('.createUserError()', function () {
|
||||||
it('should be a user error', function() {
|
it('should be a user error', function () {
|
||||||
const error = errors.createUserError({
|
const error = errors.createUserError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: 'Something happened',
|
description: 'Something happened',
|
||||||
@ -599,7 +599,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.isUserError(error)).to.be.true;
|
expect(errors.isUserError(error)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be an instance of Error', function() {
|
it('should be an instance of Error', function () {
|
||||||
const error = errors.createUserError({
|
const error = errors.createUserError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: 'Something happened',
|
description: 'Something happened',
|
||||||
@ -608,7 +608,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(error).to.be.an.instanceof(Error);
|
expect(error).to.be.an.instanceof(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly add both a title and a description', function() {
|
it('should correctly add both a title and a description', function () {
|
||||||
const error = errors.createUserError({
|
const error = errors.createUserError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: 'Something happened',
|
description: 'Something happened',
|
||||||
@ -618,7 +618,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal('Something happened');
|
expect(errors.getDescription(error)).to.equal('Something happened');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly add only a title', function() {
|
it('should correctly add only a title', function () {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const error = errors.createUserError({
|
const error = errors.createUserError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
@ -628,7 +628,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly add a code', function() {
|
it('should correctly add a code', function () {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const error = errors.createUserError({
|
const error = errors.createUserError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
@ -639,7 +639,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(error.code).to.equal('HELLO');
|
expect(error.code).to.equal('HELLO');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore an empty description', function() {
|
it('should ignore an empty description', function () {
|
||||||
const error = errors.createUserError({
|
const error = errors.createUserError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: '',
|
description: '',
|
||||||
@ -648,7 +648,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore a blank description', function() {
|
it('should ignore a blank description', function () {
|
||||||
const error = errors.createUserError({
|
const error = errors.createUserError({
|
||||||
title: 'Foo',
|
title: 'Foo',
|
||||||
description: ' ',
|
description: ' ',
|
||||||
@ -657,14 +657,14 @@ describe('Shared: Errors', function() {
|
|||||||
expect(errors.getDescription(error)).to.equal(error.stack);
|
expect(errors.getDescription(error)).to.equal(error.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if no title', function() {
|
it('should throw if no title', function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
errors.createUserError({});
|
errors.createUserError({});
|
||||||
}).to.throw('Invalid error title: undefined');
|
}).to.throw('Invalid error title: undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if title is empty', function() {
|
it('should throw if title is empty', function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
errors.createUserError({
|
errors.createUserError({
|
||||||
@ -673,7 +673,7 @@ describe('Shared: Errors', function() {
|
|||||||
}).to.throw('Invalid error title: ');
|
}).to.throw('Invalid error title: ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if there is a description but no title', function() {
|
it('should throw if there is a description but no title', function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
errors.createUserError({
|
errors.createUserError({
|
||||||
@ -682,7 +682,7 @@ describe('Shared: Errors', function() {
|
|||||||
}).to.throw('Invalid error title: undefined');
|
}).to.throw('Invalid error title: undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if title is blank', function() {
|
it('should throw if title is blank', function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
errors.createUserError({
|
errors.createUserError({
|
||||||
@ -692,9 +692,9 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.isUserError()', function() {
|
describe('.isUserError()', function () {
|
||||||
_.each([0, '', false], value => {
|
_.each([0, '', false], (value) => {
|
||||||
it(`should return true if report equals ${value}`, function() {
|
it(`should return true if report equals ${value}`, function () {
|
||||||
const error = new Error('foo bar');
|
const error = new Error('foo bar');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.report = value;
|
error.report = value;
|
||||||
@ -702,8 +702,8 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
_.each([undefined, null, true, 1, 3, 'foo'], value => {
|
_.each([undefined, null, true, 1, 3, 'foo'], (value) => {
|
||||||
it(`should return false if report equals ${value}`, function() {
|
it(`should return false if report equals ${value}`, function () {
|
||||||
const error = new Error('foo bar');
|
const error = new Error('foo bar');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.report = value;
|
error.report = value;
|
||||||
@ -712,8 +712,8 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.toJSON()', function() {
|
describe('.toJSON()', function () {
|
||||||
it('should convert a simple error', function() {
|
it('should convert a simple error', function () {
|
||||||
const error = new Error('My error');
|
const error = new Error('My error');
|
||||||
expect(errors.toJSON(error)).to.deep.equal({
|
expect(errors.toJSON(error)).to.deep.equal({
|
||||||
code: undefined,
|
code: undefined,
|
||||||
@ -730,7 +730,7 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert an error with a description', function() {
|
it('should convert an error with a description', function () {
|
||||||
const error = new Error('My error');
|
const error = new Error('My error');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.description = 'My description';
|
error.description = 'My description';
|
||||||
@ -750,7 +750,7 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert an error with a code', function() {
|
it('should convert an error with a code', function () {
|
||||||
const error = new Error('My error');
|
const error = new Error('My error');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'ENOENT';
|
error.code = 'ENOENT';
|
||||||
@ -770,7 +770,7 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert an error with a description and a code', function() {
|
it('should convert an error with a description and a code', function () {
|
||||||
const error = new Error('My error');
|
const error = new Error('My error');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.description = 'My description';
|
error.description = 'My description';
|
||||||
@ -792,7 +792,7 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert an error with a report value', function() {
|
it('should convert an error with a report value', function () {
|
||||||
const error = new Error('My error');
|
const error = new Error('My error');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.report = true;
|
error.report = true;
|
||||||
@ -812,7 +812,7 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert an error without a message', function() {
|
it('should convert an error without a message', function () {
|
||||||
const error = new Error();
|
const error = new Error();
|
||||||
|
|
||||||
expect(errors.toJSON(error)).to.deep.equal({
|
expect(errors.toJSON(error)).to.deep.equal({
|
||||||
@ -831,14 +831,14 @@ describe('Shared: Errors', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.fromJSON()', function() {
|
describe('.fromJSON()', function () {
|
||||||
it('should return an Error object', function() {
|
it('should return an Error object', function () {
|
||||||
const error = new Error('My error');
|
const error = new Error('My error');
|
||||||
const result = errors.fromJSON(errors.toJSON(error));
|
const result = errors.fromJSON(errors.toJSON(error));
|
||||||
expect(result).to.be.an.instanceof(Error);
|
expect(result).to.be.an.instanceof(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert a simple JSON error', function() {
|
it('should convert a simple JSON error', function () {
|
||||||
const error = new Error('My error');
|
const error = new Error('My error');
|
||||||
const result = errors.fromJSON(errors.toJSON(error));
|
const result = errors.fromJSON(errors.toJSON(error));
|
||||||
|
|
||||||
@ -852,7 +852,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(result.report).to.equal(error.report);
|
expect(result.report).to.equal(error.report);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert a JSON error with a description', function() {
|
it('should convert a JSON error with a description', function () {
|
||||||
const error = new Error('My error');
|
const error = new Error('My error');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.description = 'My description';
|
error.description = 'My description';
|
||||||
@ -868,7 +868,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(result.report).to.equal(error.report);
|
expect(result.report).to.equal(error.report);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert a JSON error with a code', function() {
|
it('should convert a JSON error with a code', function () {
|
||||||
const error = new Error('My error');
|
const error = new Error('My error');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.code = 'ENOENT';
|
error.code = 'ENOENT';
|
||||||
@ -884,7 +884,7 @@ describe('Shared: Errors', function() {
|
|||||||
expect(result.report).to.equal(error.report);
|
expect(result.report).to.equal(error.report);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert a JSON error with a report value', function() {
|
it('should convert a JSON error with a report value', function () {
|
||||||
const error = new Error('My error');
|
const error = new Error('My error');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error.report = false;
|
error.report = false;
|
||||||
|
@ -19,8 +19,8 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import * as fileExtensions from '../../lib/shared/file-extensions';
|
import * as fileExtensions from '../../lib/shared/file-extensions';
|
||||||
|
|
||||||
describe('Shared: fileExtensions', function() {
|
describe('Shared: fileExtensions', function () {
|
||||||
describe('.getFileExtensions()', function() {
|
describe('.getFileExtensions()', function () {
|
||||||
_.forEach(
|
_.forEach(
|
||||||
[
|
[
|
||||||
// No extension
|
// No extension
|
||||||
@ -83,8 +83,8 @@ describe('Shared: fileExtensions', function() {
|
|||||||
extensions: ['dmg'],
|
extensions: ['dmg'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
testCase => {
|
(testCase) => {
|
||||||
it(`should return ${testCase.extensions} for ${testCase.file}`, function() {
|
it(`should return ${testCase.extensions} for ${testCase.file}`, function () {
|
||||||
expect(fileExtensions.getFileExtensions(testCase.file)).to.deep.equal(
|
expect(fileExtensions.getFileExtensions(testCase.file)).to.deep.equal(
|
||||||
testCase.extensions,
|
testCase.extensions,
|
||||||
);
|
);
|
||||||
@ -92,7 +92,7 @@ describe('Shared: fileExtensions', function() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
it('should always return lowercase extensions', function() {
|
it('should always return lowercase extensions', function () {
|
||||||
const filePath = 'foo.IMG.gZ';
|
const filePath = 'foo.IMG.gZ';
|
||||||
expect(fileExtensions.getFileExtensions(filePath)).to.deep.equal([
|
expect(fileExtensions.getFileExtensions(filePath)).to.deep.equal([
|
||||||
'img',
|
'img',
|
||||||
@ -101,44 +101,44 @@ describe('Shared: fileExtensions', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getLastFileExtension()', function() {
|
describe('.getLastFileExtension()', function () {
|
||||||
it('should return undefined if the file path has no extension', function() {
|
it('should return undefined if the file path has no extension', function () {
|
||||||
expect(fileExtensions.getLastFileExtension('foo')).to.equal(null);
|
expect(fileExtensions.getLastFileExtension('foo')).to.equal(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the extension if there is only one extension', function() {
|
it('should return the extension if there is only one extension', function () {
|
||||||
expect(fileExtensions.getLastFileExtension('foo.img')).to.equal('img');
|
expect(fileExtensions.getLastFileExtension('foo.img')).to.equal('img');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the last extension if there are two extensions', function() {
|
it('should return the last extension if there are two extensions', function () {
|
||||||
expect(fileExtensions.getLastFileExtension('foo.img.gz')).to.equal('gz');
|
expect(fileExtensions.getLastFileExtension('foo.img.gz')).to.equal('gz');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the last extension if there are three extensions', function() {
|
it('should return the last extension if there are three extensions', function () {
|
||||||
expect(fileExtensions.getLastFileExtension('foo.bar.img.gz')).to.equal(
|
expect(fileExtensions.getLastFileExtension('foo.bar.img.gz')).to.equal(
|
||||||
'gz',
|
'gz',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getPenultimateFileExtension()', function() {
|
describe('.getPenultimateFileExtension()', function () {
|
||||||
it('should return undefined in the file path has no extension', function() {
|
it('should return undefined in the file path has no extension', function () {
|
||||||
expect(fileExtensions.getPenultimateFileExtension('foo')).to.equal(null);
|
expect(fileExtensions.getPenultimateFileExtension('foo')).to.equal(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined if there is only one extension', function() {
|
it('should return undefined if there is only one extension', function () {
|
||||||
expect(fileExtensions.getPenultimateFileExtension('foo.img')).to.equal(
|
expect(fileExtensions.getPenultimateFileExtension('foo.img')).to.equal(
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the penultimate extension if there are two extensions', function() {
|
it('should return the penultimate extension if there are two extensions', function () {
|
||||||
expect(fileExtensions.getPenultimateFileExtension('foo.img.gz')).to.equal(
|
expect(fileExtensions.getPenultimateFileExtension('foo.img.gz')).to.equal(
|
||||||
'img',
|
'img',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the penultimate extension if there are three extensions', function() {
|
it('should return the penultimate extension if there are three extensions', function () {
|
||||||
expect(
|
expect(
|
||||||
fileExtensions.getPenultimateFileExtension('foo.bar.img.gz'),
|
fileExtensions.getPenultimateFileExtension('foo.bar.img.gz'),
|
||||||
).to.equal('img');
|
).to.equal('img');
|
||||||
|
@ -19,8 +19,8 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import * as messages from '../../lib/shared/messages';
|
import * as messages from '../../lib/shared/messages';
|
||||||
|
|
||||||
describe('Shared: Messages', function() {
|
describe('Shared: Messages', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
this.drives = [
|
this.drives = [
|
||||||
{
|
{
|
||||||
description: 'My Drive',
|
description: 'My Drive',
|
||||||
@ -33,19 +33,19 @@ describe('Shared: Messages', function() {
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain object properties', function() {
|
it('should contain object properties', function () {
|
||||||
expect(_.every(_.map(messages, _.isPlainObject))).to.be.true;
|
expect(_.every(_.map(messages, _.isPlainObject))).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain function properties in each category', function() {
|
it('should contain function properties in each category', function () {
|
||||||
_.each(messages, category => {
|
_.each(messages, (category) => {
|
||||||
expect(_.every(_.map(category, _.isFunction))).to.be.true;
|
expect(_.every(_.map(category, _.isFunction))).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.info', function() {
|
describe('.info', function () {
|
||||||
describe('.flashComplete()', function() {
|
describe('.flashComplete()', function () {
|
||||||
it('should use singular when there are single results', function() {
|
it('should use singular when there are single results', function () {
|
||||||
const msg = messages.info.flashComplete('image.img', this.drives, {
|
const msg = messages.info.flashComplete('image.img', this.drives, {
|
||||||
failed: 1,
|
failed: 1,
|
||||||
successful: 1,
|
successful: 1,
|
||||||
@ -56,7 +56,7 @@ describe('Shared: Messages', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use plural when there are multiple results', function() {
|
it('should use plural when there are multiple results', function () {
|
||||||
const msg = messages.info.flashComplete('image.img', this.drives, {
|
const msg = messages.info.flashComplete('image.img', this.drives, {
|
||||||
failed: 2,
|
failed: 2,
|
||||||
successful: 2,
|
successful: 2,
|
||||||
@ -67,7 +67,7 @@ describe('Shared: Messages', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not contain failed target part when there are none', function() {
|
it('should not contain failed target part when there are none', function () {
|
||||||
const msg = messages.info.flashComplete('image.img', this.drives, {
|
const msg = messages.info.flashComplete('image.img', this.drives, {
|
||||||
failed: 0,
|
failed: 0,
|
||||||
successful: 2,
|
successful: 2,
|
||||||
@ -76,7 +76,7 @@ describe('Shared: Messages', function() {
|
|||||||
expect(msg).to.equal('image.img was successfully flashed to 2 targets');
|
expect(msg).to.equal('image.img was successfully flashed to 2 targets');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show drive name and description when only target', function() {
|
it('should show drive name and description when only target', function () {
|
||||||
const msg = messages.info.flashComplete('image.img', this.drives, {
|
const msg = messages.info.flashComplete('image.img', this.drives, {
|
||||||
failed: 0,
|
failed: 0,
|
||||||
successful: 1,
|
successful: 1,
|
||||||
@ -89,9 +89,9 @@ describe('Shared: Messages', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.error', function() {
|
describe('.error', function () {
|
||||||
describe('.flashFailure()', function() {
|
describe('.flashFailure()', function () {
|
||||||
it('should use plural when there are multiple drives', function() {
|
it('should use plural when there are multiple drives', function () {
|
||||||
const msg = messages.error.flashFailure('image.img', this.drives);
|
const msg = messages.error.flashFailure('image.img', this.drives);
|
||||||
|
|
||||||
expect(msg).to.equal(
|
expect(msg).to.equal(
|
||||||
@ -99,7 +99,7 @@ describe('Shared: Messages', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use singular when there is one drive', function() {
|
it('should use singular when there is one drive', function () {
|
||||||
const msg = messages.error.flashFailure('image.img', [this.drives[0]]);
|
const msg = messages.error.flashFailure('image.img', [this.drives[0]]);
|
||||||
|
|
||||||
expect(msg).to.equal(
|
expect(msg).to.equal(
|
||||||
|
@ -20,19 +20,19 @@ import { stub } from 'sinon';
|
|||||||
|
|
||||||
import * as permissions from '../../lib/shared/permissions';
|
import * as permissions from '../../lib/shared/permissions';
|
||||||
|
|
||||||
describe('Shared: permissions', function() {
|
describe('Shared: permissions', function () {
|
||||||
describe('.createLaunchScript()', function() {
|
describe('.createLaunchScript()', function () {
|
||||||
describe('given windows', function() {
|
describe('given windows', function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
this.osPlatformStub = stub(os, 'platform');
|
this.osPlatformStub = stub(os, 'platform');
|
||||||
this.osPlatformStub.returns('win32');
|
this.osPlatformStub.returns('win32');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
this.osPlatformStub.restore();
|
this.osPlatformStub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should escape environment variables and arguments', function() {
|
it('should escape environment variables and arguments', function () {
|
||||||
expect(
|
expect(
|
||||||
permissions.createLaunchScript(
|
permissions.createLaunchScript(
|
||||||
'C:\\Users\\Alice & Bob\'s Laptop\\"what"\\balenaEtcher',
|
'C:\\Users\\Alice & Bob\'s Laptop\\"what"\\balenaEtcher',
|
||||||
@ -54,17 +54,17 @@ describe('Shared: permissions', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (const platform of ['linux', 'darwin']) {
|
for (const platform of ['linux', 'darwin']) {
|
||||||
describe(`given ${platform}`, function() {
|
describe(`given ${platform}`, function () {
|
||||||
beforeEach(function() {
|
beforeEach(function () {
|
||||||
this.osPlatformStub = stub(os, 'platform');
|
this.osPlatformStub = stub(os, 'platform');
|
||||||
this.osPlatformStub.returns(platform);
|
this.osPlatformStub.returns(platform);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function () {
|
||||||
this.osPlatformStub.restore();
|
this.osPlatformStub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should escape environment variables and arguments', function() {
|
it('should escape environment variables and arguments', function () {
|
||||||
expect(
|
expect(
|
||||||
permissions.createLaunchScript(
|
permissions.createLaunchScript(
|
||||||
'/home/Alice & Bob\'s Laptop/"what"/balenaEtcher',
|
'/home/Alice & Bob\'s Laptop/"what"/balenaEtcher',
|
||||||
|
@ -19,16 +19,16 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import * as supportedFormats from '../../lib/shared/supported-formats';
|
import * as supportedFormats from '../../lib/shared/supported-formats';
|
||||||
|
|
||||||
describe('Shared: SupportedFormats', function() {
|
describe('Shared: SupportedFormats', function () {
|
||||||
describe('.getCompressedExtensions()', function() {
|
describe('.getCompressedExtensions()', function () {
|
||||||
it('should return the supported compressed extensions', function() {
|
it('should return the supported compressed extensions', function () {
|
||||||
const extensions = supportedFormats.getCompressedExtensions().sort();
|
const extensions = supportedFormats.getCompressedExtensions().sort();
|
||||||
expect(extensions).to.deep.equal(['bz2', 'gz', 'xz'].sort());
|
expect(extensions).to.deep.equal(['bz2', 'gz', 'xz'].sort());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getNonCompressedExtensions()', function() {
|
describe('.getNonCompressedExtensions()', function () {
|
||||||
it('should return the supported non compressed extensions', function() {
|
it('should return the supported non compressed extensions', function () {
|
||||||
const extensions = supportedFormats.getNonCompressedExtensions();
|
const extensions = supportedFormats.getNonCompressedExtensions();
|
||||||
expect(extensions).to.deep.equal([
|
expect(extensions).to.deep.equal([
|
||||||
'img',
|
'img',
|
||||||
@ -45,15 +45,15 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getArchiveExtensions()', function() {
|
describe('.getArchiveExtensions()', function () {
|
||||||
it('should return the supported archive extensions', function() {
|
it('should return the supported archive extensions', function () {
|
||||||
const extensions = supportedFormats.getArchiveExtensions();
|
const extensions = supportedFormats.getArchiveExtensions();
|
||||||
expect(extensions).to.deep.equal(['zip', 'etch']);
|
expect(extensions).to.deep.equal(['zip', 'etch']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getAllExtensions()', function() {
|
describe('.getAllExtensions()', function () {
|
||||||
it('should return the union of all compressed, uncompressed, and archive extensions', function() {
|
it('should return the union of all compressed, uncompressed, and archive extensions', function () {
|
||||||
const archiveExtensions = supportedFormats.getArchiveExtensions();
|
const archiveExtensions = supportedFormats.getArchiveExtensions();
|
||||||
const compressedExtensions = supportedFormats.getCompressedExtensions();
|
const compressedExtensions = supportedFormats.getCompressedExtensions();
|
||||||
const nonCompressedExtensions = supportedFormats.getNonCompressedExtensions();
|
const nonCompressedExtensions = supportedFormats.getNonCompressedExtensions();
|
||||||
@ -67,7 +67,7 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.isSupportedImage()', function() {
|
describe('.isSupportedImage()', function () {
|
||||||
_.forEach(
|
_.forEach(
|
||||||
[
|
[
|
||||||
// Type: 'archive'
|
// Type: 'archive'
|
||||||
@ -89,25 +89,25 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
'path/to/filename.sdcard',
|
'path/to/filename.sdcard',
|
||||||
'path/to/filename.wic',
|
'path/to/filename.wic',
|
||||||
],
|
],
|
||||||
filename => {
|
(filename) => {
|
||||||
it(`should return true for ${filename}`, function() {
|
it(`should return true for ${filename}`, function () {
|
||||||
const isSupported = supportedFormats.isSupportedImage(filename);
|
const isSupported = supportedFormats.isSupportedImage(filename);
|
||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
it('should return false if the file has no extension', function() {
|
it('should return false if the file has no extension', function () {
|
||||||
const isSupported = supportedFormats.isSupportedImage('/path/to/foo');
|
const isSupported = supportedFormats.isSupportedImage('/path/to/foo');
|
||||||
expect(isSupported).to.be.false;
|
expect(isSupported).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if the extension is not included in .getAllExtensions()', function() {
|
it('should return false if the extension is not included in .getAllExtensions()', function () {
|
||||||
const isSupported = supportedFormats.isSupportedImage('/path/to/foo.jpg');
|
const isSupported = supportedFormats.isSupportedImage('/path/to/foo.jpg');
|
||||||
expect(isSupported).to.be.false;
|
expect(isSupported).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if the extension is included in .getAllExtensions()', function() {
|
it('should return true if the extension is included in .getAllExtensions()', function () {
|
||||||
const nonCompressedExtension = _.first(
|
const nonCompressedExtension = _.first(
|
||||||
supportedFormats.getNonCompressedExtensions(),
|
supportedFormats.getNonCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -116,7 +116,7 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore casing when determining extension validity', function() {
|
it('should ignore casing when determining extension validity', function () {
|
||||||
const nonCompressedExtension = _.first(
|
const nonCompressedExtension = _.first(
|
||||||
supportedFormats.getNonCompressedExtensions(),
|
supportedFormats.getNonCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -125,7 +125,7 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not consider an extension before a non compressed extension', function() {
|
it('should not consider an extension before a non compressed extension', function () {
|
||||||
const nonCompressedExtension = _.first(
|
const nonCompressedExtension = _.first(
|
||||||
supportedFormats.getNonCompressedExtensions(),
|
supportedFormats.getNonCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -134,7 +134,7 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if the extension is supported and the file name includes dots', function() {
|
it('should return true if the extension is supported and the file name includes dots', function () {
|
||||||
const nonCompressedExtension = _.first(
|
const nonCompressedExtension = _.first(
|
||||||
supportedFormats.getNonCompressedExtensions(),
|
supportedFormats.getNonCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -143,14 +143,14 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if the extension is only a supported archive extension', function() {
|
it('should return true if the extension is only a supported archive extension', function () {
|
||||||
const archiveExtension = _.first(supportedFormats.getArchiveExtensions());
|
const archiveExtension = _.first(supportedFormats.getArchiveExtensions());
|
||||||
const imagePath = `/path/to/foo.${archiveExtension}`;
|
const imagePath = `/path/to/foo.${archiveExtension}`;
|
||||||
const isSupported = supportedFormats.isSupportedImage(imagePath);
|
const isSupported = supportedFormats.isSupportedImage(imagePath);
|
||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if the extension is a supported one plus a supported compressed extensions', function() {
|
it('should return true if the extension is a supported one plus a supported compressed extensions', function () {
|
||||||
const nonCompressedExtension = _.first(
|
const nonCompressedExtension = _.first(
|
||||||
supportedFormats.getNonCompressedExtensions(),
|
supportedFormats.getNonCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -162,7 +162,7 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if the extension is an unsupported one plus a supported compressed extensions', function() {
|
it('should return false if the extension is an unsupported one plus a supported compressed extensions', function () {
|
||||||
const compressedExtension = _.first(
|
const compressedExtension = _.first(
|
||||||
supportedFormats.getCompressedExtensions(),
|
supportedFormats.getCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -171,17 +171,17 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
expect(isSupported).to.be.false;
|
expect(isSupported).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if the file has no extension', function() {
|
it('should return false if the file has no extension', function () {
|
||||||
const isSupported = supportedFormats.isSupportedImage('/path/to/foo');
|
const isSupported = supportedFormats.isSupportedImage('/path/to/foo');
|
||||||
expect(isSupported).to.be.false;
|
expect(isSupported).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if the extension is not included in .getAllExtensions()', function() {
|
it('should return false if the extension is not included in .getAllExtensions()', function () {
|
||||||
const isSupported = supportedFormats.isSupportedImage('/path/to/foo.jpg');
|
const isSupported = supportedFormats.isSupportedImage('/path/to/foo.jpg');
|
||||||
expect(isSupported).to.be.false;
|
expect(isSupported).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if the extension is included in .getAllExtensions()', function() {
|
it('should return true if the extension is included in .getAllExtensions()', function () {
|
||||||
const nonCompressedExtension = _.first(
|
const nonCompressedExtension = _.first(
|
||||||
supportedFormats.getNonCompressedExtensions(),
|
supportedFormats.getNonCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -190,7 +190,7 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore casing when determining extension validity', function() {
|
it('should ignore casing when determining extension validity', function () {
|
||||||
const nonCompressedExtension = _.first(
|
const nonCompressedExtension = _.first(
|
||||||
supportedFormats.getNonCompressedExtensions(),
|
supportedFormats.getNonCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -199,7 +199,7 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not consider an extension before a non compressed extension', function() {
|
it('should not consider an extension before a non compressed extension', function () {
|
||||||
const nonCompressedExtension = _.first(
|
const nonCompressedExtension = _.first(
|
||||||
supportedFormats.getNonCompressedExtensions(),
|
supportedFormats.getNonCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -208,7 +208,7 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if the extension is supported and the file name includes dots', function() {
|
it('should return true if the extension is supported and the file name includes dots', function () {
|
||||||
const nonCompressedExtension = _.first(
|
const nonCompressedExtension = _.first(
|
||||||
supportedFormats.getNonCompressedExtensions(),
|
supportedFormats.getNonCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -217,14 +217,14 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if the extension is only a supported archive extension', function() {
|
it('should return true if the extension is only a supported archive extension', function () {
|
||||||
const archiveExtension = _.first(supportedFormats.getArchiveExtensions());
|
const archiveExtension = _.first(supportedFormats.getArchiveExtensions());
|
||||||
const imagePath = `/path/to/foo.${archiveExtension}`;
|
const imagePath = `/path/to/foo.${archiveExtension}`;
|
||||||
const isSupported = supportedFormats.isSupportedImage(imagePath);
|
const isSupported = supportedFormats.isSupportedImage(imagePath);
|
||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if the extension is a supported one plus a supported compressed extensions', function() {
|
it('should return true if the extension is a supported one plus a supported compressed extensions', function () {
|
||||||
const nonCompressedExtension = _.first(
|
const nonCompressedExtension = _.first(
|
||||||
supportedFormats.getNonCompressedExtensions(),
|
supportedFormats.getNonCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -236,7 +236,7 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
expect(isSupported).to.be.true;
|
expect(isSupported).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if the extension is an unsupported one plus a supported compressed extensions', function() {
|
it('should return false if the extension is an unsupported one plus a supported compressed extensions', function () {
|
||||||
const compressedExtension = _.first(
|
const compressedExtension = _.first(
|
||||||
supportedFormats.getCompressedExtensions(),
|
supportedFormats.getCompressedExtensions(),
|
||||||
);
|
);
|
||||||
@ -246,7 +246,7 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.looksLikeWindowsImage()', function() {
|
describe('.looksLikeWindowsImage()', function () {
|
||||||
_.each(
|
_.each(
|
||||||
[
|
[
|
||||||
'C:\\path\\to\\en_windows_10_multiple_editions_version_1607_updated_jan_2017_x64_dvd_9714399.iso',
|
'C:\\path\\to\\en_windows_10_multiple_editions_version_1607_updated_jan_2017_x64_dvd_9714399.iso',
|
||||||
@ -254,8 +254,8 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
'/path/to/Win10_1607_SingleLang_English_x32.iso',
|
'/path/to/Win10_1607_SingleLang_English_x32.iso',
|
||||||
'/path/to/en_winxp_pro_x86_build2600_iso.img',
|
'/path/to/en_winxp_pro_x86_build2600_iso.img',
|
||||||
],
|
],
|
||||||
imagePath => {
|
(imagePath) => {
|
||||||
it(`should return true if filename is ${imagePath}`, function() {
|
it(`should return true if filename is ${imagePath}`, function () {
|
||||||
const looksLikeWindowsImage = supportedFormats.looksLikeWindowsImage(
|
const looksLikeWindowsImage = supportedFormats.looksLikeWindowsImage(
|
||||||
imagePath,
|
imagePath,
|
||||||
);
|
);
|
||||||
@ -269,8 +269,8 @@ describe('Shared: SupportedFormats', function() {
|
|||||||
'C:\\path\\to\\2017-01-11-raspbian-jessie.img',
|
'C:\\path\\to\\2017-01-11-raspbian-jessie.img',
|
||||||
'/path/to/2017-01-11-raspbian-jessie.img',
|
'/path/to/2017-01-11-raspbian-jessie.img',
|
||||||
],
|
],
|
||||||
imagePath => {
|
(imagePath) => {
|
||||||
it(`should return false if filename is ${imagePath}`, function() {
|
it(`should return false if filename is ${imagePath}`, function () {
|
||||||
const looksLikeWindowsImage = supportedFormats.looksLikeWindowsImage(
|
const looksLikeWindowsImage = supportedFormats.looksLikeWindowsImage(
|
||||||
imagePath,
|
imagePath,
|
||||||
);
|
);
|
||||||
|
@ -17,41 +17,41 @@
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import * as units from '../../lib/shared/units';
|
import * as units from '../../lib/shared/units';
|
||||||
|
|
||||||
describe('Shared: Units', function() {
|
describe('Shared: Units', function () {
|
||||||
describe('.bytesToClosestUnit()', function() {
|
describe('.bytesToClosestUnit()', function () {
|
||||||
it('should convert bytes to terabytes', function() {
|
it('should convert bytes to terabytes', function () {
|
||||||
expect(units.bytesToClosestUnit(1000000000000)).to.equal('1 TB');
|
expect(units.bytesToClosestUnit(1000000000000)).to.equal('1 TB');
|
||||||
expect(units.bytesToClosestUnit(2987801405440)).to.equal('2.99 TB');
|
expect(units.bytesToClosestUnit(2987801405440)).to.equal('2.99 TB');
|
||||||
expect(units.bytesToClosestUnit(999900000000000)).to.equal('1000 TB');
|
expect(units.bytesToClosestUnit(999900000000000)).to.equal('1000 TB');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert bytes to gigabytes', function() {
|
it('should convert bytes to gigabytes', function () {
|
||||||
expect(units.bytesToClosestUnit(1000000000)).to.equal('1 GB');
|
expect(units.bytesToClosestUnit(1000000000)).to.equal('1 GB');
|
||||||
expect(units.bytesToClosestUnit(7801405440)).to.equal('7.8 GB');
|
expect(units.bytesToClosestUnit(7801405440)).to.equal('7.8 GB');
|
||||||
expect(units.bytesToClosestUnit(999900000000)).to.equal('1000 GB');
|
expect(units.bytesToClosestUnit(999900000000)).to.equal('1000 GB');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert bytes to megabytes', function() {
|
it('should convert bytes to megabytes', function () {
|
||||||
expect(units.bytesToClosestUnit(1000000)).to.equal('1 MB');
|
expect(units.bytesToClosestUnit(1000000)).to.equal('1 MB');
|
||||||
expect(units.bytesToClosestUnit(801405440)).to.equal('801 MB');
|
expect(units.bytesToClosestUnit(801405440)).to.equal('801 MB');
|
||||||
expect(units.bytesToClosestUnit(999900000)).to.equal('1000 MB');
|
expect(units.bytesToClosestUnit(999900000)).to.equal('1000 MB');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert bytes to kilobytes', function() {
|
it('should convert bytes to kilobytes', function () {
|
||||||
expect(units.bytesToClosestUnit(1000)).to.equal('1 kB');
|
expect(units.bytesToClosestUnit(1000)).to.equal('1 kB');
|
||||||
expect(units.bytesToClosestUnit(5440)).to.equal('5.44 kB');
|
expect(units.bytesToClosestUnit(5440)).to.equal('5.44 kB');
|
||||||
expect(units.bytesToClosestUnit(999900)).to.equal('1000 kB');
|
expect(units.bytesToClosestUnit(999900)).to.equal('1000 kB');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should keep bytes as bytes', function() {
|
it('should keep bytes as bytes', function () {
|
||||||
expect(units.bytesToClosestUnit(1)).to.equal('1 B');
|
expect(units.bytesToClosestUnit(1)).to.equal('1 B');
|
||||||
expect(units.bytesToClosestUnit(8)).to.equal('8 B');
|
expect(units.bytesToClosestUnit(8)).to.equal('8 B');
|
||||||
expect(units.bytesToClosestUnit(999)).to.equal('999 B');
|
expect(units.bytesToClosestUnit(999)).to.equal('999 B');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.bytesToMegabytes()', function() {
|
describe('.bytesToMegabytes()', function () {
|
||||||
it('should convert bytes to megabytes', function() {
|
it('should convert bytes to megabytes', function () {
|
||||||
expect(units.bytesToMegabytes(1.2e7)).to.equal(12);
|
expect(units.bytesToMegabytes(1.2e7)).to.equal(12);
|
||||||
expect(units.bytesToMegabytes(332000)).to.equal(0.332);
|
expect(units.bytesToMegabytes(332000)).to.equal(0.332);
|
||||||
});
|
});
|
||||||
|
@ -18,108 +18,108 @@ import { expect } from 'chai';
|
|||||||
|
|
||||||
import * as utils from '../../lib/shared/utils';
|
import * as utils from '../../lib/shared/utils';
|
||||||
|
|
||||||
describe('Shared: Utils', function() {
|
describe('Shared: Utils', function () {
|
||||||
describe('.isValidPercentage()', function() {
|
describe('.isValidPercentage()', function () {
|
||||||
it('should return false if percentage is not a number', function() {
|
it('should return false if percentage is not a number', function () {
|
||||||
expect(utils.isValidPercentage('50')).to.be.false;
|
expect(utils.isValidPercentage('50')).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if percentage is null', function() {
|
it('should return false if percentage is null', function () {
|
||||||
expect(utils.isValidPercentage(null)).to.be.false;
|
expect(utils.isValidPercentage(null)).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if percentage is undefined', function() {
|
it('should return false if percentage is undefined', function () {
|
||||||
expect(utils.isValidPercentage(undefined)).to.be.false;
|
expect(utils.isValidPercentage(undefined)).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if percentage is an integer less than 0', function() {
|
it('should return false if percentage is an integer less than 0', function () {
|
||||||
expect(utils.isValidPercentage(-1)).to.be.false;
|
expect(utils.isValidPercentage(-1)).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if percentage is a float less than 0', function() {
|
it('should return false if percentage is a float less than 0', function () {
|
||||||
expect(utils.isValidPercentage(-0.1)).to.be.false;
|
expect(utils.isValidPercentage(-0.1)).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if percentage is 0', function() {
|
it('should return true if percentage is 0', function () {
|
||||||
expect(utils.isValidPercentage(0)).to.be.true;
|
expect(utils.isValidPercentage(0)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if percentage is an integer greater than 0, but less than 100', function() {
|
it('should return true if percentage is an integer greater than 0, but less than 100', function () {
|
||||||
expect(utils.isValidPercentage(50)).to.be.true;
|
expect(utils.isValidPercentage(50)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if percentage is a float greater than 0, but less than 100', function() {
|
it('should return true if percentage is a float greater than 0, but less than 100', function () {
|
||||||
expect(utils.isValidPercentage(49.55)).to.be.true;
|
expect(utils.isValidPercentage(49.55)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if percentage is 100', function() {
|
it('should return true if percentage is 100', function () {
|
||||||
expect(utils.isValidPercentage(100)).to.be.true;
|
expect(utils.isValidPercentage(100)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if percentage is an integer greater than 100', function() {
|
it('should return false if percentage is an integer greater than 100', function () {
|
||||||
expect(utils.isValidPercentage(101)).to.be.false;
|
expect(utils.isValidPercentage(101)).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if percentage is a float greater than 100', function() {
|
it('should return false if percentage is a float greater than 100', function () {
|
||||||
expect(utils.isValidPercentage(100.001)).to.be.false;
|
expect(utils.isValidPercentage(100.001)).to.be.false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.percentageToFloat()', function() {
|
describe('.percentageToFloat()', function () {
|
||||||
it('should throw an error if given a string percentage', function() {
|
it('should throw an error if given a string percentage', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
utils.percentageToFloat('50');
|
utils.percentageToFloat('50');
|
||||||
}).to.throw('Invalid percentage: 50');
|
}).to.throw('Invalid percentage: 50');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if given a null percentage', function() {
|
it('should throw an error if given a null percentage', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
utils.percentageToFloat(null);
|
utils.percentageToFloat(null);
|
||||||
}).to.throw('Invalid percentage: null');
|
}).to.throw('Invalid percentage: null');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if given an undefined percentage', function() {
|
it('should throw an error if given an undefined percentage', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
utils.percentageToFloat(undefined);
|
utils.percentageToFloat(undefined);
|
||||||
}).to.throw('Invalid percentage: undefined');
|
}).to.throw('Invalid percentage: undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if given an integer percentage < 0', function() {
|
it('should throw an error if given an integer percentage < 0', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
utils.percentageToFloat(-1);
|
utils.percentageToFloat(-1);
|
||||||
}).to.throw('Invalid percentage: -1');
|
}).to.throw('Invalid percentage: -1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if given a float percentage < 0', function() {
|
it('should throw an error if given a float percentage < 0', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
utils.percentageToFloat(-0.1);
|
utils.percentageToFloat(-0.1);
|
||||||
}).to.throw('Invalid percentage: -0.1');
|
}).to.throw('Invalid percentage: -0.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should covert a 0 percentage to 0', function() {
|
it('should covert a 0 percentage to 0', function () {
|
||||||
expect(utils.percentageToFloat(0)).to.equal(0);
|
expect(utils.percentageToFloat(0)).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should covert an integer percentage to a float', function() {
|
it('should covert an integer percentage to a float', function () {
|
||||||
expect(utils.percentageToFloat(50)).to.equal(0.5);
|
expect(utils.percentageToFloat(50)).to.equal(0.5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should covert an float percentage to a float', function() {
|
it('should covert an float percentage to a float', function () {
|
||||||
expect(utils.percentageToFloat(46.54)).to.equal(0.4654);
|
expect(utils.percentageToFloat(46.54)).to.equal(0.4654);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should covert a 100 percentage to 1', function() {
|
it('should covert a 100 percentage to 1', function () {
|
||||||
expect(utils.percentageToFloat(100)).to.equal(1);
|
expect(utils.percentageToFloat(100)).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if given an integer percentage > 100', function() {
|
it('should throw an error if given an integer percentage > 100', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
utils.percentageToFloat(101);
|
utils.percentageToFloat(101);
|
||||||
}).to.throw('Invalid percentage: 101');
|
}).to.throw('Invalid percentage: 101');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if given a float percentage > 100', function() {
|
it('should throw an error if given a float percentage > 100', function () {
|
||||||
expect(function() {
|
expect(function () {
|
||||||
utils.percentageToFloat(100.01);
|
utils.percentageToFloat(100.01);
|
||||||
}).to.throw('Invalid percentage: 100.01');
|
}).to.throw('Invalid percentage: 100.01');
|
||||||
});
|
});
|
||||||
|
@ -26,13 +26,13 @@ if (!entrypoint) {
|
|||||||
process.exit(EXIT_CODES.GENERAL_ERROR);
|
process.exit(EXIT_CODES.GENERAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Spectron', function() {
|
describe('Spectron', function () {
|
||||||
// Mainly for CI jobs
|
// Mainly for CI jobs
|
||||||
this.timeout(40000);
|
this.timeout(40000);
|
||||||
|
|
||||||
let app: Application;
|
let app: Application;
|
||||||
|
|
||||||
before('app:start', function() {
|
before('app:start', function () {
|
||||||
app = new Application({
|
app = new Application({
|
||||||
path: entrypoint,
|
path: entrypoint,
|
||||||
args: ['--no-sandbox', '.'],
|
args: ['--no-sandbox', '.'],
|
||||||
@ -41,7 +41,7 @@ describe('Spectron', function() {
|
|||||||
return app.start();
|
return app.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
after('app:stop', function() {
|
after('app:stop', function () {
|
||||||
if (app && app.isRunning()) {
|
if (app && app.isRunning()) {
|
||||||
return app.stop();
|
return app.stop();
|
||||||
}
|
}
|
||||||
@ -49,12 +49,12 @@ describe('Spectron', function() {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Browser Window', function() {
|
describe('Browser Window', function () {
|
||||||
it('should open a browser window', async function() {
|
it('should open a browser window', async function () {
|
||||||
return expect(await app.browserWindow.isVisible()).to.be.true;
|
return expect(await app.browserWindow.isVisible()).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set a proper title', async function() {
|
it('should set a proper title', async function () {
|
||||||
// @ts-ignore (SpectronClient.getTitle exists)
|
// @ts-ignore (SpectronClient.getTitle exists)
|
||||||
return expect(await app.client.getTitle()).to.equal('Etcher');
|
return expect(await app.client.getTitle()).to.equal('Etcher');
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user