Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebf421ebed | ||
|
|
41409f578e | ||
|
|
f32fce2f80 | ||
|
|
26e054702a | ||
|
|
16f27132cb | ||
|
|
7613176f69 | ||
|
|
470bea4fca | ||
|
|
363febdb73 | ||
|
|
062ca4e205 | ||
|
|
49053342a5 | ||
|
|
fd582c67a8 | ||
|
|
706b18808b | ||
|
|
a6ae8d5376 | ||
|
|
5034f8c853 | ||
|
|
e916247013 | ||
|
|
c052686de4 | ||
|
|
31aacef131 | ||
|
|
932369b705 | ||
|
|
b835c34786 | ||
|
|
c9e1ba815a |
+1
-1
@@ -4,7 +4,7 @@ os:
|
||||
- linux
|
||||
- osx
|
||||
dist: trusty
|
||||
osx_image: xcode8.3
|
||||
osx_image: xcode10
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
|
||||
+35
-2
@@ -7,7 +7,7 @@ const packageJson = require('./package.json')
|
||||
const { version } = packageJson
|
||||
const iconDir = path.resolve(__dirname, 'assets', 'icons')
|
||||
|
||||
module.exports = {
|
||||
const config = {
|
||||
hooks: {
|
||||
generateAssets: require('./tools/generateAssets')
|
||||
},
|
||||
@@ -32,7 +32,12 @@ module.exports = {
|
||||
OriginalFilename: 'Electron Fiddle',
|
||||
},
|
||||
osxSign: {
|
||||
identity: 'Developer ID Application: Felix Rieseberg (LT94ZKYDCJ)'
|
||||
identity: 'Developer ID Application: Felix Rieseberg (LT94ZKYDCJ)',
|
||||
'hardened-runtime': true,
|
||||
'gatekeeper-assess': false,
|
||||
'entitlements': 'static/entitlements.plist',
|
||||
'entitlements-inherit': 'static/entitlements.plist',
|
||||
'signature-flags': 'library'
|
||||
}
|
||||
},
|
||||
makers: [
|
||||
@@ -95,3 +100,31 @@ module.exports = {
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function notarizeMaybe() {
|
||||
if (process.platform !== 'darwin') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env.CI) {
|
||||
console.log(`Not in CI, skipping notarization`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env.APPLE_ID || !process.env.APPLE_ID_PASSWORD) {
|
||||
console.warn('Should be notarizing, but environment variables APPLE_ID or APPLE_ID_PASSWORD are missing!');
|
||||
return;
|
||||
}
|
||||
|
||||
config.packagerConfig.osxNotarize = {
|
||||
appBundleId: 'com.electron.fiddle',
|
||||
appleId: process.env.APPLE_ID,
|
||||
appleIdPassword: process.env.APPLE_ID_PASSWORD,
|
||||
ascProvider: 'LT94ZKYDCJ'
|
||||
}
|
||||
}
|
||||
|
||||
notarizeMaybe()
|
||||
|
||||
// Finally, export it
|
||||
module.exports = config
|
||||
|
||||
Generated
+900
-282
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "electron-fiddle",
|
||||
"productName": "Electron Fiddle",
|
||||
"version": "0.10.0",
|
||||
"version": "0.11.1",
|
||||
"description": "The easiest way to get started with Electron",
|
||||
"repository": "https://github.com/electron/fiddle",
|
||||
"main": "./dist/src/main/main",
|
||||
@@ -63,12 +63,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.6.4",
|
||||
"@electron-forge/cli": "^6.0.0-beta.45",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.45",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.45",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.45",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.45",
|
||||
"@electron-forge/publisher-github": "^6.0.0-beta.45",
|
||||
"@electron-forge/cli": "^6.0.0-beta.46",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.46",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.46",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.46",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.46",
|
||||
"@electron-forge/publisher-github": "^6.0.0-beta.46",
|
||||
"@types/builtin-modules": "^3.1.1",
|
||||
"@types/classnames": "^2.2.9",
|
||||
"@types/enzyme": "^3.10.3",
|
||||
@@ -82,7 +82,7 @@
|
||||
"@types/tmp": "0.1.0",
|
||||
"chokidar": "^3.2.2",
|
||||
"coveralls": "^3.0.7",
|
||||
"electron": "7.0.0",
|
||||
"electron": "7.1.3",
|
||||
"enzyme": "^3.10.0",
|
||||
"enzyme-adapter-react-16": "^1.15.1",
|
||||
"enzyme-to-json": "^3.4.3",
|
||||
|
||||
+11
-3
@@ -4,7 +4,7 @@ export type Files = Map<string, string>;
|
||||
|
||||
export type FileTransform = (files: Files) => Promise<Files>;
|
||||
|
||||
export enum ElectronVersionState {
|
||||
export enum VersionState {
|
||||
ready = 'ready',
|
||||
downloading = 'downloading',
|
||||
unknown = 'unknown'
|
||||
@@ -14,12 +14,20 @@ export enum ElectronVersionSource {
|
||||
remote = 'remote',
|
||||
local = 'local'
|
||||
}
|
||||
export interface NpmVersion {
|
||||
|
||||
export interface Version {
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface NpmVersion extends Version {
|
||||
name?: string;
|
||||
localPath?: string;
|
||||
}
|
||||
|
||||
export interface NodeVersion extends Version {
|
||||
state: VersionState;
|
||||
}
|
||||
|
||||
export interface EditorValues {
|
||||
main: string;
|
||||
renderer: string;
|
||||
@@ -29,7 +37,7 @@ export interface EditorValues {
|
||||
}
|
||||
|
||||
export interface ElectronVersion extends NpmVersion {
|
||||
state: ElectronVersionState;
|
||||
state: VersionState;
|
||||
source: ElectronVersionSource;
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -19,14 +19,18 @@ export enum IpcEvents {
|
||||
SHOW_WARNING_DIALOG = 'SHOW_WARNING_DIALOG',
|
||||
SHOW_CONFIRMATION_DIALOG = 'SHOW_CONFIRMATION_DIALOG',
|
||||
SHOW_WELCOME_TOUR = 'SHOW_WELCOME_TOUR',
|
||||
CLEAR_CONSOLE = 'CLEAR_CONSOLE'
|
||||
CLEAR_CONSOLE = 'CLEAR_CONSOLE',
|
||||
LOAD_LOCAL_VERSION_FOLDER = 'LOAD_LOCAL_VERSION_FOLDER',
|
||||
SHOW_LOCAL_VERSION_FOLDER_DIALOG = 'SHOW_LOCAL_VERSION_FOLDER_DIALOG',
|
||||
BISECT_COMMANDS_TOGGLE = 'BISECT_COMMANDS_TOGGLE',
|
||||
}
|
||||
|
||||
export const ipcMainEvents = [
|
||||
IpcEvents.FS_SAVE_FIDDLE_DIALOG,
|
||||
IpcEvents.FS_SAVE_FIDDLE,
|
||||
IpcEvents.SHOW_WARNING_DIALOG,
|
||||
IpcEvents.SHOW_CONFIRMATION_DIALOG
|
||||
IpcEvents.SHOW_CONFIRMATION_DIALOG,
|
||||
IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG
|
||||
];
|
||||
|
||||
export const ipcRendererEvents = [
|
||||
@@ -46,7 +50,9 @@ export const ipcRendererEvents = [
|
||||
IpcEvents.FS_SAVE_FIDDLE_FORGE,
|
||||
IpcEvents.FS_SAVE_FIDDLE_GIST,
|
||||
IpcEvents.SHOW_WELCOME_TOUR,
|
||||
IpcEvents.CLEAR_CONSOLE
|
||||
IpcEvents.CLEAR_CONSOLE,
|
||||
IpcEvents.LOAD_LOCAL_VERSION_FOLDER,
|
||||
IpcEvents.BISECT_COMMANDS_TOGGLE
|
||||
];
|
||||
|
||||
export const WEBCONTENTS_READY_FOR_IPC_SIGNAL = 'WEBCONTENTS_READY_FOR_IPC_SIGNAL';
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
button.version-chooser.bp3-button {
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
header {
|
||||
box-shadow: 0 0 0 1px rgba(16, 22, 26, 0.2), 0 0 0 rgba(16, 22, 26, 0), 0 1px 1px rgba(16, 22, 26, 0.4);
|
||||
//border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background-color: @background-3;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
@@ -20,6 +19,7 @@ header {
|
||||
select {
|
||||
-webkit-app-region: no-drag;
|
||||
margin-bottom: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
select {
|
||||
|
||||
@@ -16,5 +16,4 @@
|
||||
@import "components/chrome-mac.less";
|
||||
@import "components/editors.less";
|
||||
@import "components/tour.less";
|
||||
@import "components/commands-version-chooser.less";
|
||||
@import "components/show-me.less";
|
||||
|
||||
@@ -7,7 +7,7 @@ import { isDevMode } from '../utils/devmode';
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function setupDevTools(): Promise<void> {
|
||||
if (!isDevMode) return;
|
||||
if (!isDevMode()) return;
|
||||
|
||||
const {
|
||||
default: installExtension,
|
||||
|
||||
+18
-1
@@ -1,4 +1,4 @@
|
||||
import { dialog } from 'electron';
|
||||
import { dialog, IpcMainEvent } from 'electron';
|
||||
import { IpcEvents } from '../ipc-events';
|
||||
import { ipcMainManager } from './ipc';
|
||||
import { getOrCreateMainWindow } from './windows';
|
||||
@@ -17,6 +17,10 @@ export function setupDialogs() {
|
||||
showConfirmationDialog(args);
|
||||
});
|
||||
|
||||
ipcMainManager.on(IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG, async (event) => {
|
||||
await showOpenDialog(event);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,3 +46,16 @@ function showConfirmationDialog(args: Electron.MessageBoxOptions) {
|
||||
...args
|
||||
});
|
||||
}
|
||||
|
||||
async function showOpenDialog(event: IpcMainEvent) {
|
||||
const { filePaths } = await dialog.showOpenDialog({
|
||||
title: 'Open Folder',
|
||||
properties: ['openDirectory']
|
||||
});
|
||||
|
||||
if (!filePaths || filePaths.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.reply(IpcEvents.LOAD_LOCAL_VERSION_FOLDER, [filePaths[0]]);
|
||||
}
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ export function main() {
|
||||
}
|
||||
|
||||
// Set the app's name
|
||||
app.setName('Electron Fiddle');
|
||||
app.name = 'Electron Fiddle';
|
||||
|
||||
// Ensure that there's only ever one Fiddle running
|
||||
listenForProtocolHandler();
|
||||
|
||||
+12
-7
@@ -144,7 +144,7 @@ function getShowMeMenuItem(key: string, item: string | Templates): MenuItemConst
|
||||
if (typeof item === 'string') {
|
||||
return {
|
||||
label: key,
|
||||
click: () => ipcMainManager.send(IpcEvents.FS_OPEN_TEMPLATE, [ key ])
|
||||
click: () => ipcMainManager.send(IpcEvents.FS_OPEN_TEMPLATE, [key])
|
||||
};
|
||||
}
|
||||
|
||||
@@ -160,10 +160,10 @@ function getShowMeMenu(): MenuItemConstructorOptions {
|
||||
const showMeMenu: Array<MenuItemConstructorOptions> = Object.keys(SHOW_ME_TEMPLATES)
|
||||
.map((key) => getShowMeMenuItem(key, SHOW_ME_TEMPLATES[key]));
|
||||
|
||||
return {
|
||||
label: 'Show Me',
|
||||
submenu: showMeMenu
|
||||
};
|
||||
return {
|
||||
label: 'Show Me',
|
||||
submenu: showMeMenu
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,11 +251,16 @@ export function setupMenu() {
|
||||
item.submenu.push({ type: 'separator' }, { role: 'resetZoom' }, { role: 'zoomIn' }, { role: 'zoomOut' }); // Add zooming actions
|
||||
item.submenu.push({ type: 'separator' }, {
|
||||
label: 'Toggle Soft Wrap',
|
||||
click: () => ipcMainManager.send(IpcEvents.MONACO_TOGGLE_OPTION, [ 'wordWrap' ]),
|
||||
click: () => ipcMainManager.send(IpcEvents.MONACO_TOGGLE_OPTION, ['wordWrap']),
|
||||
});
|
||||
item.submenu.push({ type: 'separator' }, {
|
||||
label: 'Toggle Mini Map',
|
||||
click: () => ipcMainManager.send(IpcEvents.MONACO_TOGGLE_OPTION, [ 'minimap.enabled' ]),
|
||||
click: () => ipcMainManager.send(IpcEvents.MONACO_TOGGLE_OPTION, ['minimap.enabled']),
|
||||
});
|
||||
item.submenu.push({ type: 'separator' }, {
|
||||
label: 'Toggle Bisect Helper',
|
||||
click: () => ipcMainManager.send(IpcEvents.BISECT_COMMANDS_TOGGLE),
|
||||
accelerator: 'CommandorControl+Shift+B',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { ElectronVersion } from '../interfaces';
|
||||
|
||||
export class Bisector {
|
||||
public revList: Array<ElectronVersion>;
|
||||
public minRev: number;
|
||||
public maxRev: number;
|
||||
private pivot: number;
|
||||
|
||||
constructor(revList: Array<ElectronVersion>) {
|
||||
this.getCurrentVersion = this.getCurrentVersion.bind(this);
|
||||
this.continue = this.continue.bind(this);
|
||||
this.calculatePivot = this.calculatePivot.bind(this);
|
||||
|
||||
this.revList = revList;
|
||||
this.minRev = 0;
|
||||
this.maxRev = revList.length - 1;
|
||||
this.calculatePivot();
|
||||
}
|
||||
|
||||
public getCurrentVersion() {
|
||||
return this.revList[this.pivot];
|
||||
}
|
||||
|
||||
public continue(isGoodVersion: boolean) {
|
||||
let isBisectOver = false;
|
||||
if (this.maxRev - this.minRev <= 1) {
|
||||
isBisectOver = true;
|
||||
}
|
||||
|
||||
if (isGoodVersion) {
|
||||
const upPivot = Math.floor((this.maxRev - this.pivot) / 2) + this.pivot;
|
||||
this.minRev = this.pivot;
|
||||
if (upPivot !== this.maxRev && upPivot !== this.pivot) {
|
||||
this.pivot = upPivot;
|
||||
} else {
|
||||
isBisectOver = true;
|
||||
}
|
||||
} else {
|
||||
const downPivot = Math.floor((this.pivot - this.minRev) / 2) + this.minRev;
|
||||
this.maxRev = this.pivot;
|
||||
if (downPivot !== this.minRev && downPivot !== this.pivot) {
|
||||
this.pivot = downPivot;
|
||||
} else {
|
||||
isBisectOver = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isBisectOver) {
|
||||
return [this.revList[this.minRev], this.revList[this.maxRev]];
|
||||
} else {
|
||||
return this.revList[this.pivot];
|
||||
}
|
||||
}
|
||||
|
||||
private calculatePivot() {
|
||||
this.pivot = Math.floor((this.maxRev - this.minRev) / 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Button } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { AppState } from '../state';
|
||||
|
||||
interface BisectHandlerProps {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class BisectHandler extends React.Component<BisectHandlerProps> {
|
||||
constructor(props: BisectHandlerProps) {
|
||||
super(props);
|
||||
|
||||
this.continueBisect = this.continueBisect.bind(this);
|
||||
this.terminateBisect = this.terminateBisect.bind(this);
|
||||
}
|
||||
|
||||
public continueBisect(isGood: boolean) {
|
||||
const { appState } = this.props;
|
||||
const response = appState.Bisector!.continue(isGood);
|
||||
if (Array.isArray(response)) {
|
||||
this.terminateBisect();
|
||||
const [minRev, maxRev] = response;
|
||||
appState.pushOutput(`[BISECT] Complete: Check between versions ${minRev.version} and ${maxRev.version}.`);
|
||||
} else {
|
||||
appState.setVersion(response.version);
|
||||
}
|
||||
}
|
||||
|
||||
public terminateBisect() {
|
||||
const { appState } = this.props;
|
||||
appState.Bisector = undefined;
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { appState } = this.props;
|
||||
if (!!appState.Bisector) {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
icon={'thumbs-up'}
|
||||
onClick={() => this.continueBisect(true)}
|
||||
/>
|
||||
<Button
|
||||
icon={'thumbs-down'}
|
||||
onClick={() => this.continueBisect(false)}
|
||||
/>
|
||||
<Button
|
||||
icon={'cross'}
|
||||
onClick={this.terminateBisect}
|
||||
>
|
||||
Cancel Bisect
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Button
|
||||
icon='git-branch'
|
||||
text='Bisect'
|
||||
onClick={appState.toggleBisectDialog}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
-46
@@ -1,11 +1,11 @@
|
||||
import { Button, MenuItem } from '@blueprintjs/core';
|
||||
import { Button, ButtonGroup, MenuItem } from '@blueprintjs/core';
|
||||
import { ItemPredicate, ItemRenderer, Select } from '@blueprintjs/select';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState } from '../../interfaces';
|
||||
import { ElectronVersion, ElectronVersionSource, VersionState } from '../../interfaces';
|
||||
import { highlightText } from '../../utils/highlight-text';
|
||||
import { sortedElectronMap } from '../../utils/sorted-electron-map';
|
||||
import { sortedElectronMap } from '../../utils/sorted-map';
|
||||
import { AppState } from '../state';
|
||||
import { getReleaseChannel } from '../versions';
|
||||
|
||||
@@ -24,11 +24,11 @@ export function getItemLabel({ source, state, name }: ElectronVersion): string {
|
||||
if (source === ElectronVersionSource.local) {
|
||||
label = name || 'Local';
|
||||
} else {
|
||||
if (state === ElectronVersionState.unknown) {
|
||||
if (state === VersionState.unknown) {
|
||||
label = `Not downloaded`;
|
||||
} else if (state === ElectronVersionState.ready) {
|
||||
} else if (state === VersionState.ready) {
|
||||
label = `Downloaded`;
|
||||
} else if (state === ElectronVersionState.downloading) {
|
||||
} else if (state === VersionState.downloading) {
|
||||
label = `Downloading`;
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export const filterItem: ItemPredicate<ElectronVersion> = (query, { version }) =
|
||||
*/
|
||||
export const renderItem: ItemRenderer<ElectronVersion> = (item, { handleClick, modifiers, query }) => {
|
||||
if (!modifiers.matchesPredicate) {
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -95,6 +95,30 @@ export interface VersionChooserProps {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
|
||||
export const getVersionsFromAppState = (appState: AppState) => {
|
||||
const { versions, versionsToShow, statesToShow } = appState;
|
||||
|
||||
return sortedElectronMap<ElectronVersion>(versions, (_key, item) => item)
|
||||
.filter((item) => {
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we want to show the version
|
||||
if (!versionsToShow.includes(getReleaseChannel(item))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we want to show the state
|
||||
if (!statesToShow.includes(item.state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* A dropdown allowing the selection of Electron versions. The actual
|
||||
* download is managed in the state.
|
||||
@@ -103,7 +127,7 @@ export interface VersionChooserProps {
|
||||
* @extends {React.Component<VersionChooserProps, VersionChooserState>}
|
||||
*/
|
||||
@observer
|
||||
export class VersionChooser extends React.Component<VersionChooserProps, VersionChooserState> {
|
||||
export class ElectronVersionChooser extends React.Component<VersionChooserProps, VersionChooserState> {
|
||||
constructor(props: VersionChooserProps) {
|
||||
super(props);
|
||||
|
||||
@@ -120,48 +144,29 @@ export class VersionChooser extends React.Component<VersionChooserProps, Version
|
||||
this.props.appState.setVersion(version);
|
||||
}
|
||||
|
||||
public getItems(): Array<ElectronVersion> {
|
||||
const { versions, versionsToShow, statesToShow } = this.props.appState;
|
||||
|
||||
return sortedElectronMap<ElectronVersion>(versions, (_key, item) => item)
|
||||
.filter((item) => {
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we want to show the version
|
||||
if (!versionsToShow.includes(getReleaseChannel(item))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we want to show the state
|
||||
if (!statesToShow.includes(item.state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { currentElectronVersion } = this.props.appState;
|
||||
const { currentElectronVersion, Bisector } = this.props.appState;
|
||||
const { version } = currentElectronVersion;
|
||||
|
||||
return (
|
||||
<ElectronVersionSelect
|
||||
filterable={true}
|
||||
items={this.getItems()}
|
||||
itemRenderer={renderItem}
|
||||
itemPredicate={filterItem}
|
||||
onItemSelect={this.onItemSelect}
|
||||
noResults={<MenuItem disabled={true} text='No results.' />}
|
||||
>
|
||||
<Button
|
||||
className='version-chooser'
|
||||
text={`Electron v${version}`}
|
||||
icon={getItemIcon(currentElectronVersion)}
|
||||
/>
|
||||
</ElectronVersionSelect>
|
||||
<ButtonGroup>
|
||||
<ElectronVersionSelect
|
||||
filterable={true}
|
||||
items={getVersionsFromAppState(this.props.appState)}
|
||||
itemRenderer={renderItem}
|
||||
itemPredicate={filterItem}
|
||||
onItemSelect={this.onItemSelect}
|
||||
noResults={<MenuItem disabled={true} text='No results.' />}
|
||||
disabled={!!Bisector}
|
||||
>
|
||||
<Button
|
||||
className='version-chooser'
|
||||
text={`Electron v${version}`}
|
||||
icon={getItemIcon(currentElectronVersion)}
|
||||
disabled={!!Bisector}
|
||||
/>
|
||||
</ElectronVersionSelect>
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import { Button, ButtonGroup, MenuItem } from '@blueprintjs/core';
|
||||
import { ItemPredicate, ItemRenderer, Select } from '@blueprintjs/select';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersion, NodeVersion, VersionState } from '../../interfaces';
|
||||
import { highlightText } from '../../utils/highlight-text';
|
||||
import { sortedNodeMap } from '../../utils/sorted-map';
|
||||
import { AppState } from '../state';
|
||||
|
||||
const ElectronVersionSelect = Select.ofType<NodeVersion>();
|
||||
|
||||
/**
|
||||
* Helper method: Returns the <Select /> label for an Electron
|
||||
* version.
|
||||
*
|
||||
* @param {ElectronVersion} { source, state }
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getItemLabel({ state }: NodeVersion): string {
|
||||
let label = '';
|
||||
|
||||
if (state === VersionState.unknown) {
|
||||
label = `Not downloaded`;
|
||||
} else if (state === VersionState.ready) {
|
||||
label = `Downloaded`;
|
||||
} else if (state === VersionState.downloading) {
|
||||
label = `Downloading`;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method: Returns the <Select /> icon for an Electron
|
||||
* version.
|
||||
*
|
||||
* @param {ElectronVersion} { state }
|
||||
* @returns
|
||||
*/
|
||||
export function getItemIcon({ state }: NodeVersion) {
|
||||
return state === 'ready'
|
||||
? 'saved'
|
||||
: state === 'downloading' ? 'cloud-download' : 'cloud';
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method: Returns the <Select /> predicate for an Electron
|
||||
* version.
|
||||
*
|
||||
* @param {string} query
|
||||
* @param {ElectronVersion} { version }
|
||||
* @returns
|
||||
*/
|
||||
export const filterItem: ItemPredicate<NodeVersion> = (query, { version }) => {
|
||||
return version.toLowerCase().includes(query.toLowerCase());
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper method: Returns the <Select /> <MenuItem /> for Electron
|
||||
* versions.
|
||||
*
|
||||
* @param {ElectronVersion} item
|
||||
* @param {IItemRendererProps} { handleClick, modifiers, query }
|
||||
* @returns
|
||||
*/
|
||||
export const renderItem: ItemRenderer<NodeVersion> = (item, { handleClick, modifiers, query }) => {
|
||||
if (!modifiers.matchesPredicate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
active={modifiers.active}
|
||||
disabled={modifiers.disabled}
|
||||
text={highlightText(item.version, query)}
|
||||
key={item.version}
|
||||
onClick={handleClick}
|
||||
label={getItemLabel(item)}
|
||||
icon={getItemIcon(item)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export interface VersionChooserState {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface VersionChooserProps {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
|
||||
export const getVersionsFromAppState = (appState: AppState) => {
|
||||
const { nodeVersions } = appState;
|
||||
|
||||
return sortedNodeMap<NodeVersion>(nodeVersions, (_key, item) => item)
|
||||
.filter((item) => !!item);
|
||||
};
|
||||
|
||||
/**
|
||||
* A dropdown allowing the selection of Electron versions. The actual
|
||||
* download is managed in the state.
|
||||
*
|
||||
* @class VersionChooser
|
||||
* @extends {React.Component<VersionChooserProps, VersionChooserState>}
|
||||
*/
|
||||
@observer
|
||||
export class NodeVersionChooser extends React.Component<VersionChooserProps, VersionChooserState> {
|
||||
constructor(props: VersionChooserProps) {
|
||||
super(props);
|
||||
|
||||
this.onItemSelect = this.onItemSelect.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle change, which usually means that we'd like update
|
||||
* the selection version.
|
||||
*
|
||||
* @param {React.ChangeEvent<HTMLSelectElement>} event
|
||||
*/
|
||||
public onItemSelect({ version }: ElectronVersion) {
|
||||
this.props.appState.setNodeVersion(version);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { currentNodeVersion, Bisector } = this.props.appState;
|
||||
const { version } = currentNodeVersion;
|
||||
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<ElectronVersionSelect
|
||||
filterable={true}
|
||||
items={getVersionsFromAppState(this.props.appState)}
|
||||
itemRenderer={renderItem}
|
||||
itemPredicate={filterItem}
|
||||
onItemSelect={this.onItemSelect}
|
||||
noResults={<MenuItem disabled={true} text='No results.' />}
|
||||
disabled={!!Bisector}
|
||||
>
|
||||
<Button
|
||||
className='version-chooser'
|
||||
text={`Node v${version}`}
|
||||
icon={getItemIcon(currentNodeVersion)}
|
||||
disabled={!!Bisector}
|
||||
/>
|
||||
</ElectronVersionSelect>
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { Button, IButtonProps, Spinner } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersionState } from '../../interfaces';
|
||||
import { VersionState } from '../../interfaces';
|
||||
import { AppState } from '../state';
|
||||
|
||||
export interface RunnerState {
|
||||
@@ -27,11 +27,11 @@ export class Runner extends React.Component<RunnerProps, RunnerState> {
|
||||
const state = currentElectronVersion && currentElectronVersion.state;
|
||||
const props: IButtonProps = { className: 'button-run' };
|
||||
|
||||
if (state === ElectronVersionState.downloading) {
|
||||
if (state === VersionState.downloading) {
|
||||
props.text = 'Downloading';
|
||||
props.disabled = true;
|
||||
props.icon = <Spinner size={16} />;
|
||||
} else if (state === ElectronVersionState.ready) {
|
||||
} else if (state === VersionState.ready) {
|
||||
if (isRunning) {
|
||||
props.active = true;
|
||||
props.text = 'Stop';
|
||||
|
||||
@@ -4,10 +4,12 @@ import * as React from 'react';
|
||||
|
||||
import { AppState } from '../state';
|
||||
import { AddressBar } from './commands-address-bar';
|
||||
import { BisectHandler } from './commands-bisect';
|
||||
import { EditorDropdown } from './commands-editors';
|
||||
import { ElectronVersionChooser } from './commands-electron-chooser';
|
||||
import { NodeVersionChooser } from './commands-node-chooser';
|
||||
import { PublishButton } from './commands-publish-button';
|
||||
import { Runner } from './commands-runner';
|
||||
import { VersionChooser } from './commands-version-chooser';
|
||||
|
||||
export interface CommandsProps {
|
||||
appState: AppState;
|
||||
@@ -28,14 +30,24 @@ export class Commands extends React.Component<CommandsProps, {}> {
|
||||
|
||||
public render() {
|
||||
const { appState } = this.props;
|
||||
const { isBisectCommandShowing: isBisectCommandShowing } = appState;
|
||||
|
||||
return (
|
||||
<div className='commands'>
|
||||
<div>
|
||||
<ControlGroup fill={true} vertical={false}>
|
||||
<VersionChooser appState={appState} />
|
||||
{this.getVersionChooser()}
|
||||
<Runner appState={appState} />
|
||||
</ControlGroup>
|
||||
{
|
||||
// tslint:disable-next-line jsx-no-multiline-js
|
||||
isBisectCommandShowing &&
|
||||
(
|
||||
<ControlGroup fill={true} vertical={false}>
|
||||
<BisectHandler appState={appState} />
|
||||
</ControlGroup>
|
||||
)
|
||||
}
|
||||
<ControlGroup fill={true} vertical={false}>
|
||||
<Button
|
||||
active={appState.isConsoleShowing}
|
||||
@@ -43,6 +55,12 @@ export class Commands extends React.Component<CommandsProps, {}> {
|
||||
text='Console'
|
||||
onClick={appState.toggleConsole}
|
||||
/>
|
||||
<Button
|
||||
active={appState.isNodeMode}
|
||||
icon='ninja'
|
||||
text='Node.js Mode'
|
||||
onClick={appState.toggleNodeMode}
|
||||
/>
|
||||
<EditorDropdown appState={appState} />
|
||||
</ControlGroup>
|
||||
</div>
|
||||
@@ -53,4 +71,14 @@ export class Commands extends React.Component<CommandsProps, {}> {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public getVersionChooser() {
|
||||
const { appState } = this.props;
|
||||
|
||||
if (appState.isNodeMode) {
|
||||
return <NodeVersionChooser appState={appState} />;
|
||||
} else {
|
||||
return <ElectronVersionChooser appState={appState} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ import * as React from 'react';
|
||||
import * as semver from 'semver';
|
||||
|
||||
import { NpmVersion } from '../../interfaces';
|
||||
import { IpcEvents } from '../../ipc-events';
|
||||
import { getElectronNameForPlatform } from '../../utils/electron-name';
|
||||
import { ipcRendererManager } from '../ipc';
|
||||
import { AppState } from '../state';
|
||||
|
||||
export interface AddVersionDialogProps {
|
||||
@@ -16,7 +18,7 @@ export interface AddVersionDialogProps {
|
||||
export interface AddVersionDialogState {
|
||||
isValidElectron: boolean;
|
||||
isValidVersion: boolean;
|
||||
file?: File;
|
||||
folderPath?: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
@@ -39,8 +41,15 @@ export class AddVersionDialog extends React.Component<AddVersionDialogProps, Add
|
||||
|
||||
this.onSubmit = this.onSubmit.bind(this);
|
||||
this.onClose = this.onClose.bind(this);
|
||||
this.onChangeFile = this.onChangeFile.bind(this);
|
||||
this.onChangeVersion = this.onChangeVersion.bind(this);
|
||||
|
||||
ipcRendererManager.on(IpcEvents.LOAD_LOCAL_VERSION_FOLDER, (_event, [file]) => {
|
||||
this.setFolderPath(file);
|
||||
});
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
ipcRendererManager.removeAllListeners(IpcEvents.LOAD_LOCAL_VERSION_FOLDER);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,16 +57,11 @@ export class AddVersionDialog extends React.Component<AddVersionDialogProps, Add
|
||||
*
|
||||
* @param {React.ChangeEvent<HTMLInputElement>} event
|
||||
*/
|
||||
public async onChangeFile(event: React.FormEvent<HTMLInputElement>) {
|
||||
const { files } = event.target as any;
|
||||
public async setFolderPath(folderPath: string) {
|
||||
const { binaryManager } = this.props.appState;
|
||||
const file = files && files[0] ? files[0] : undefined;
|
||||
const isValidElectron = !!await binaryManager.getIsDownloaded('custom', folderPath);
|
||||
|
||||
const isValidElectron = !!(file
|
||||
&& file.path
|
||||
&& await binaryManager.getIsDownloaded('custom', file.path));
|
||||
|
||||
this.setState({ file, isValidElectron });
|
||||
this.setState({ folderPath, isValidElectron });
|
||||
}
|
||||
|
||||
public onChangeVersion(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
@@ -67,7 +71,7 @@ export class AddVersionDialog extends React.Component<AddVersionDialogProps, Add
|
||||
this.setState({
|
||||
version,
|
||||
isValidVersion,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,18 +80,18 @@ export class AddVersionDialog extends React.Component<AddVersionDialogProps, Add
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async onSubmit(): Promise<void> {
|
||||
const { file, version } = this.state;
|
||||
const { folderPath, version } = this.state;
|
||||
|
||||
if (!file) return;
|
||||
if (!folderPath) return;
|
||||
|
||||
const name = file.path
|
||||
const name = folderPath
|
||||
.slice(-20)
|
||||
.split(path.sep)
|
||||
.slice(1)
|
||||
.join(path.sep);
|
||||
|
||||
const toAdd: NpmVersion = {
|
||||
localPath: file.path,
|
||||
localPath: folderPath,
|
||||
version,
|
||||
name
|
||||
};
|
||||
@@ -129,12 +133,16 @@ export class AddVersionDialog extends React.Component<AddVersionDialogProps, Add
|
||||
|
||||
public render() {
|
||||
const { isAddVersionDialogShowing } = this.props.appState;
|
||||
const inputProps = { webkitdirectory: 'true' };
|
||||
const { file } = this.state;
|
||||
const inputProps = {
|
||||
onClick: (e: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
|
||||
e.preventDefault();
|
||||
ipcRendererManager.send(IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG);
|
||||
}
|
||||
};
|
||||
const { folderPath } = this.state;
|
||||
|
||||
const text = file && file.path
|
||||
? file.path
|
||||
: `Select the folder containing ${getElectronNameForPlatform()}...`;
|
||||
const text = folderPath ||
|
||||
`Select the folder containing ${getElectronNameForPlatform()}...`;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
@@ -145,7 +153,6 @@ export class AddVersionDialog extends React.Component<AddVersionDialogProps, Add
|
||||
>
|
||||
<div className='bp3-dialog-body'>
|
||||
<FileInput
|
||||
onInputChange={this.onChangeFile}
|
||||
id='custom-electron-version'
|
||||
inputProps={inputProps as any}
|
||||
text={text}
|
||||
@@ -163,9 +170,9 @@ export class AddVersionDialog extends React.Component<AddVersionDialogProps, Add
|
||||
}
|
||||
|
||||
private renderPath(): JSX.Element | null {
|
||||
const { file, isValidElectron } = this.state;
|
||||
const { folderPath, isValidElectron } = this.state;
|
||||
|
||||
if (!file || !file.path) return null;
|
||||
if (!folderPath) return null;
|
||||
|
||||
const info = isValidElectron
|
||||
? `We found an ${getElectronNameForPlatform()} in this folder.`
|
||||
@@ -208,7 +215,7 @@ export class AddVersionDialog extends React.Component<AddVersionDialogProps, Add
|
||||
isValidElectron: false,
|
||||
isValidVersion: false,
|
||||
version: '',
|
||||
file: undefined
|
||||
folderPath: undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
import { Button, Dialog, Label, MenuItem } from '@blueprintjs/core';
|
||||
import { Select } from '@blueprintjs/select';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersion } from '../../interfaces';
|
||||
import { Bisector } from '../bisect';
|
||||
import { AppState } from '../state';
|
||||
import { filterItem, getItemIcon, getVersionsFromAppState, renderItem } from './commands-electron-chooser';
|
||||
|
||||
const ElectronVersionSelect = Select.ofType<ElectronVersion>();
|
||||
|
||||
export interface BisectDialogProps {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
export interface BisectDialogState {
|
||||
startIndex?: number;
|
||||
endIndex?: number;
|
||||
allVersions: Array<ElectronVersion>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "add version" dialog allows users to add custom builds of Electron.
|
||||
*
|
||||
* @class AddVersionDialog
|
||||
* @extends {React.Component<BisectDialogProps, BisectDialogState>}
|
||||
*/
|
||||
@observer
|
||||
export class BisectDialog extends React.Component<BisectDialogProps, BisectDialogState> {
|
||||
constructor(props: BisectDialogProps) {
|
||||
super(props);
|
||||
|
||||
this.onSubmit = this.onSubmit.bind(this);
|
||||
this.onClose = this.onClose.bind(this);
|
||||
this.onBeginSelect = this.onBeginSelect.bind(this);
|
||||
this.onEndSelect = this.onEndSelect.bind(this);
|
||||
|
||||
const allVersions = getVersionsFromAppState(this.props.appState);
|
||||
this.state = { allVersions };
|
||||
}
|
||||
|
||||
public onBeginSelect(version: ElectronVersion) {
|
||||
this.setState({ startIndex: this.state.allVersions.indexOf(version) });
|
||||
}
|
||||
|
||||
public onEndSelect(version: ElectronVersion) {
|
||||
this.setState({ endIndex: this.state.allVersions.indexOf(version) });
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the submission of the dialog
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async onSubmit(): Promise<void> {
|
||||
const { endIndex, startIndex, allVersions } = this.state;
|
||||
const { appState } = this.props;
|
||||
|
||||
if (endIndex === undefined || startIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bisectRange = allVersions
|
||||
.slice(endIndex, startIndex + 1)
|
||||
.reverse();
|
||||
|
||||
appState.Bisector = new Bisector(bisectRange);
|
||||
const initialBisectPivot = appState.Bisector.getCurrentVersion().version;
|
||||
appState.setVersion(initialBisectPivot);
|
||||
this.onClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the dialog
|
||||
*/
|
||||
public onClose() {
|
||||
this.props.appState.isBisectDialogShowing = false;
|
||||
}
|
||||
|
||||
get buttons() {
|
||||
const canSubmit =
|
||||
!!this.state.startIndex &&
|
||||
!!this.state.endIndex &&
|
||||
this.state.startIndex > this.state.endIndex;
|
||||
|
||||
return [
|
||||
(
|
||||
<Button
|
||||
icon='play'
|
||||
key='submit'
|
||||
disabled={!canSubmit}
|
||||
onClick={this.onSubmit}
|
||||
text='Begin'
|
||||
/>
|
||||
), (
|
||||
<Button
|
||||
icon='cross'
|
||||
key='cancel'
|
||||
onClick={this.onClose}
|
||||
text='Cancel'
|
||||
/>
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { isBisectDialogShowing } = this.props.appState;
|
||||
const { startIndex, endIndex, allVersions } = this.state;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
isOpen={isBisectDialogShowing}
|
||||
onClose={this.onClose}
|
||||
title='Start a bisect session'
|
||||
className='dialog-add-version'
|
||||
>
|
||||
<div className='bp3-dialog-body'>
|
||||
<Label>
|
||||
Earliest Version
|
||||
<ElectronVersionSelect
|
||||
filterable={true}
|
||||
items={allVersions}
|
||||
itemRenderer={renderItem}
|
||||
itemPredicate={filterItem}
|
||||
onItemSelect={this.onBeginSelect}
|
||||
noResults={<MenuItem disabled={true} text='No results.' />}
|
||||
>
|
||||
<Button
|
||||
text={startIndex ? `v${allVersions[startIndex].version}` : ``}
|
||||
icon={startIndex ? getItemIcon(allVersions[startIndex]) : 'small-minus'}
|
||||
fill={true}
|
||||
/>
|
||||
</ElectronVersionSelect>
|
||||
</Label>
|
||||
<Label>
|
||||
Latest Version
|
||||
<ElectronVersionSelect
|
||||
filterable={true}
|
||||
items={allVersions.slice(0, startIndex!)}
|
||||
itemRenderer={renderItem}
|
||||
itemPredicate={filterItem}
|
||||
onItemSelect={this.onEndSelect}
|
||||
noResults={<MenuItem disabled={true} text='No results.' />}
|
||||
disabled={!startIndex}
|
||||
>
|
||||
<Button
|
||||
text={endIndex ? `v${allVersions[endIndex].version}` : ``}
|
||||
icon={endIndex ? getItemIcon(allVersions[endIndex]) : 'small-minus'}
|
||||
fill={true}
|
||||
disabled={!startIndex}
|
||||
/>
|
||||
</ElectronVersionSelect>
|
||||
</Label>
|
||||
</div>
|
||||
<div className='bp3-dialog-footer'>
|
||||
<div className='bp3-dialog-footer-actions'>
|
||||
{this.buttons}
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import * as React from 'react';
|
||||
import { AppState } from '../state';
|
||||
import { AddThemeDialog } from './dialog-add-theme';
|
||||
import { AddVersionDialog } from './dialog-add-version';
|
||||
import { BisectDialog } from './dialog-bisect';
|
||||
import { ConfirmDialog } from './dialog-confirm';
|
||||
import { TokenDialog } from './dialog-token';
|
||||
import { WarningDialog } from './dialog-warning';
|
||||
@@ -23,7 +24,13 @@ export interface DialogsProps {
|
||||
export class Dialogs extends React.Component<DialogsProps, {}> {
|
||||
public render() {
|
||||
const { appState } = this.props;
|
||||
const { isTokenDialogShowing, isSettingsShowing, isAddVersionDialogShowing, isThemeDialogShowing } = appState;
|
||||
const {
|
||||
isTokenDialogShowing,
|
||||
isSettingsShowing,
|
||||
isAddVersionDialogShowing,
|
||||
isThemeDialogShowing,
|
||||
isBisectDialogShowing
|
||||
} = appState;
|
||||
const maybeToken = isTokenDialogShowing
|
||||
? <TokenDialog key='dialogs' appState={appState} />
|
||||
: null;
|
||||
@@ -35,9 +42,12 @@ export class Dialogs extends React.Component<DialogsProps, {}> {
|
||||
: null;
|
||||
const maybeMonaco = isThemeDialogShowing ? <AddThemeDialog appState={appState} />
|
||||
: null;
|
||||
const maybeBisect = isBisectDialogShowing
|
||||
? <BisectDialog key='bisect-dialog' appState={appState} />
|
||||
: null;
|
||||
const eitherWarningOrPrompt = appState.isWarningDialogShowing
|
||||
? <WarningDialog appState={appState} />
|
||||
: <ConfirmDialog appState={appState}/>;
|
||||
: <ConfirmDialog appState={appState} />;
|
||||
|
||||
return (
|
||||
<div key='dialogs' className='dialogs'>
|
||||
@@ -45,6 +55,7 @@ export class Dialogs extends React.Component<DialogsProps, {}> {
|
||||
{maybeSettings}
|
||||
{maybeAddLocalVersion}
|
||||
{maybeMonaco}
|
||||
{maybeBisect}
|
||||
{eitherWarningOrPrompt}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -12,9 +12,9 @@ import {
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState } from '../../interfaces';
|
||||
import { ElectronVersion, ElectronVersionSource, VersionState } from '../../interfaces';
|
||||
import { normalizeVersion } from '../../utils/normalize-version';
|
||||
import { sortedElectronMap } from '../../utils/sorted-electron-map';
|
||||
import { sortedElectronMap } from '../../utils/sorted-map';
|
||||
import { AppState } from '../state';
|
||||
import { ElectronReleaseChannel, getReleaseChannel } from '../versions';
|
||||
|
||||
@@ -69,7 +69,7 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
if (!checked) {
|
||||
appState.statesToShow = appState.statesToShow.filter((s) => s !== id);
|
||||
} else {
|
||||
appState.statesToShow.push(id as ElectronVersionState);
|
||||
appState.statesToShow.push(id as VersionState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
*/
|
||||
private renderVersionStateOptions(): JSX.Element {
|
||||
const { appState } = this.props;
|
||||
const getIsChecked = (state: ElectronVersionState) => {
|
||||
const getIsChecked = (state: VersionState) => {
|
||||
return appState.statesToShow.includes(state);
|
||||
};
|
||||
|
||||
@@ -211,21 +211,21 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
label='Include Electron versions that are:'
|
||||
>
|
||||
<Checkbox
|
||||
checked={getIsChecked(ElectronVersionState.downloading)}
|
||||
checked={getIsChecked(VersionState.downloading)}
|
||||
label='Downloading'
|
||||
id='downloading'
|
||||
onChange={this.handleStateChange}
|
||||
inline={true}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={getIsChecked(ElectronVersionState.ready)}
|
||||
checked={getIsChecked(VersionState.ready)}
|
||||
label='Downloaded'
|
||||
id='ready'
|
||||
onChange={this.handleStateChange}
|
||||
inline={true}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={getIsChecked(ElectronVersionState.unknown)}
|
||||
checked={getIsChecked(VersionState.unknown)}
|
||||
label='Not Downloaded'
|
||||
id='unknown'
|
||||
onChange={this.handleStateChange}
|
||||
@@ -350,10 +350,10 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
let icon: IconName = 'box';
|
||||
let humanState = 'Downloaded';
|
||||
|
||||
if (state === ElectronVersionState.downloading) {
|
||||
if (state === VersionState.downloading) {
|
||||
icon = 'cloud-download';
|
||||
humanState = 'Downloading';
|
||||
} else if (state === ElectronVersionState.unknown) {
|
||||
} else if (state === VersionState.unknown) {
|
||||
icon = 'cloud';
|
||||
humanState = 'Not downloaded';
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { when } from 'mobx';
|
||||
import { EditorId, EditorValues } from '../interfaces';
|
||||
import { INDEX_HTML_NAME, MAIN_JS_NAME, PRELOAD_JS_NAME, RENDERER_JS_NAME } from '../shared-constants';
|
||||
import { getOctokit } from '../utils/octokit';
|
||||
import { sortedElectronMap } from '../utils/sorted-electron-map';
|
||||
import { sortedElectronMap } from '../utils/sorted-map';
|
||||
import { ELECTRON_ORG, ELECTRON_REPO } from './constants';
|
||||
import { getContent } from './content';
|
||||
import { AppState } from './state';
|
||||
|
||||
+121
-42
@@ -8,11 +8,12 @@ import {
|
||||
EditorId,
|
||||
ElectronVersion,
|
||||
ElectronVersionSource,
|
||||
ElectronVersionState,
|
||||
MosaicId,
|
||||
NodeVersion,
|
||||
NpmVersion,
|
||||
OutputEntry,
|
||||
OutputOptions,
|
||||
VersionState,
|
||||
WarningDialogTexts
|
||||
} from '../interfaces';
|
||||
import { IpcEvents } from '../ipc-events';
|
||||
@@ -24,6 +25,7 @@ import { fancyImport } from '../utils/import';
|
||||
import { normalizeVersion } from '../utils/normalize-version';
|
||||
import { isEditorBackup, isEditorId, isPanelId } from '../utils/type-checks';
|
||||
import { BinaryManager } from './binary';
|
||||
import { Bisector } from './bisect';
|
||||
import { DEFAULT_MOSAIC_ARRANGEMENT } from './constants';
|
||||
import { getContent, isContentUnchanged } from './content';
|
||||
import { getLocalTypePathForVersion, updateEditorTypeDefinitions } from './fetch-types';
|
||||
@@ -33,14 +35,16 @@ import { activateTheme } from './themes';
|
||||
import {
|
||||
addLocalVersion,
|
||||
ElectronReleaseChannel,
|
||||
getAllElectronVersions,
|
||||
getDefaultVersion,
|
||||
getElectronVersions,
|
||||
getUpdatedElectronVersions,
|
||||
getUpdatedNodeVersions,
|
||||
saveLocalVersions
|
||||
} from './versions';
|
||||
|
||||
const knownVersions = getElectronVersions();
|
||||
const knownVersions = getAllElectronVersions();
|
||||
const defaultVersion = getDefaultVersion(knownVersions);
|
||||
const defaultNodeVersion = '12.0.0';
|
||||
|
||||
/**
|
||||
* Editors exist outside of React's world. To make things *a lot*
|
||||
@@ -65,8 +69,8 @@ window.ElectronFiddle = window.ElectronFiddle || {
|
||||
export class AppState {
|
||||
// -- Persisted settings ------------------
|
||||
@observable public version: string = defaultVersion;
|
||||
@observable public nodeVersion: string = defaultNodeVersion;
|
||||
@observable public theme: string | null = localStorage.getItem('theme');
|
||||
@observable public isClearingConsoleOnRun: boolean = !!this.retrieve('isClearingConsoleOnRun');
|
||||
@observable public gitHubAvatarUrl: string | null = localStorage.getItem('gitHubAvatarUrl');
|
||||
@observable public gitHubName: string | null = localStorage.getItem('gitHubName');
|
||||
@observable public gitHubLogin: string | null = localStorage.getItem('gitHubLogin');
|
||||
@@ -74,41 +78,47 @@ export class AppState {
|
||||
@observable public gitHubPublishAsPublic: boolean = !!this.retrieve('gitHubPublishAsPublic');
|
||||
@observable public versionsToShow: Array<ElectronReleaseChannel> =
|
||||
this.retrieve('versionsToShow') as Array<ElectronReleaseChannel>
|
||||
|| [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta ];
|
||||
@observable public statesToShow: Array<ElectronVersionState> =
|
||||
this.retrieve('statesToShow') as Array<ElectronVersionState>
|
||||
|| [ ElectronVersionState.downloading, ElectronVersionState.ready, ElectronVersionState.unknown ];
|
||||
|| [ElectronReleaseChannel.stable, ElectronReleaseChannel.beta];
|
||||
@observable public statesToShow: Array<VersionState> =
|
||||
this.retrieve('statesToShow') as Array<VersionState>
|
||||
|| [VersionState.downloading, VersionState.ready, VersionState.unknown];
|
||||
@observable public isKeepingUserDataDirs: boolean = !!this.retrieve('isKeepingUserDataDirs');
|
||||
@observable public isEnablingElectronLogging: boolean = !!this.retrieve('isEnablingElectronLogging');
|
||||
|
||||
@observable public binaryManager: BinaryManager = new BinaryManager();
|
||||
@observable public isClearingConsoleOnRun: boolean = !!this.retrieve('isClearingConsoleOnRun');
|
||||
|
||||
// -- Various session-only state ------------------
|
||||
@observable public gistId: string = '';
|
||||
@observable public isPublishing: boolean = false;
|
||||
@observable public versions: Record<string, ElectronVersion> = arrayToStringMap(knownVersions);
|
||||
@observable public nodeVersions: Record<string, NodeVersion> = arrayToStringMap([] as Array<NodeVersion>);
|
||||
@observable public output: Array<OutputEntry> = [];
|
||||
@observable public localPath: string | undefined;
|
||||
@observable public isUpdatingElectronVersions = false;
|
||||
@observable public warningDialogTexts = { label: '', ok: 'Okay', cancel: 'Cancel' };
|
||||
@observable public confirmationDialogTexts = { label: '', ok: 'Okay', cancel: 'Cancel' };
|
||||
@observable public warningDialogLastResult: boolean | null = null;
|
||||
@observable public confirmationPromptLastResult: boolean | null = null;
|
||||
@observable public isRunning = false;
|
||||
@observable public mosaicArrangement: MosaicNode<MosaicId> | null = DEFAULT_MOSAIC_ARRANGEMENT;
|
||||
@observable public templateName: string | undefined;
|
||||
@observable public currentDocsDemoPage: DocsDemoPage = DocsDemoPage.DEFAULT;
|
||||
@observable public localTypeWatcher: fsType.FSWatcher | undefined;
|
||||
@observable public binaryManager: BinaryManager = new BinaryManager();
|
||||
@observable public Bisector: Bisector | undefined;
|
||||
|
||||
@observable public isPublishing: boolean = false;
|
||||
@observable public isRunning: boolean = false;
|
||||
@observable public isUnsaved: boolean = false;
|
||||
@observable public isUpdatingElectronVersions: boolean = false;
|
||||
|
||||
// -- Various "isShowing" settings ------------------
|
||||
@observable public isBisectCommandShowing: boolean;
|
||||
@observable public isConsoleShowing: boolean = false;
|
||||
@observable public isTokenDialogShowing: boolean = false;
|
||||
@observable public isWarningDialogShowing: boolean = false;
|
||||
@observable public isConfirmationPromptShowing: boolean = false;
|
||||
@observable public isSettingsShowing: boolean = false;
|
||||
@observable public isUnsaved: boolean = false;
|
||||
@observable public isBisectDialogShowing: boolean = false;
|
||||
@observable public isAddVersionDialogShowing: boolean = false;
|
||||
@observable public isThemeDialogShowing: boolean = false;
|
||||
@observable public isNodeMode: boolean = false;
|
||||
@observable public isTourShowing: boolean = !localStorage.getItem('hasShownTour');
|
||||
|
||||
// -- Editor Values stored when we close the editor ------------------
|
||||
@@ -126,17 +136,22 @@ export class AppState {
|
||||
this.pushOutput = this.pushOutput.bind(this);
|
||||
this.removeVersion = this.removeVersion.bind(this);
|
||||
this.setVersion = this.setVersion.bind(this);
|
||||
this.setNodeVersion = this.setNodeVersion.bind(this);
|
||||
this.showTour = this.showTour.bind(this);
|
||||
this.signOutGitHub = this.signOutGitHub.bind(this);
|
||||
this.toggleBisectCommands = this.toggleBisectCommands.bind(this);
|
||||
this.toggleAuthDialog = this.toggleAuthDialog.bind(this);
|
||||
this.toggleConsole = this.toggleConsole.bind(this);
|
||||
this.toggleNodeMode = this.toggleNodeMode.bind(this);
|
||||
this.clearConsole = this.clearConsole.bind(this);
|
||||
this.toggleSettings = this.toggleSettings.bind(this);
|
||||
this.toggleBisectDialog = this.toggleBisectDialog.bind(this);
|
||||
this.updateElectronVersions = this.updateElectronVersions.bind(this);
|
||||
|
||||
ipcRendererManager.on(IpcEvents.OPEN_SETTINGS, this.toggleSettings);
|
||||
ipcRendererManager.on(IpcEvents.SHOW_WELCOME_TOUR, this.showTour);
|
||||
ipcRendererManager.on(IpcEvents.CLEAR_CONSOLE, this.clearConsole);
|
||||
ipcRendererManager.on(IpcEvents.BISECT_COMMANDS_TOGGLE, this.toggleBisectCommands);
|
||||
|
||||
// Setup auto-runs
|
||||
autorun(() => this.save('theme', this.theme));
|
||||
@@ -189,6 +204,7 @@ export class AppState {
|
||||
|
||||
// Update our known versions
|
||||
this.updateElectronVersions();
|
||||
this.updateNodeVersions();
|
||||
|
||||
// Make sure the console isn't all empty and sad
|
||||
this.pushOutput('Console ready 🔬');
|
||||
@@ -206,6 +222,18 @@ export class AppState {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current NodeVersion or the first
|
||||
* one that can be found.
|
||||
*/
|
||||
@computed get currentNodeVersion(): NodeVersion {
|
||||
if (this.nodeVersions[this.nodeVersion]) {
|
||||
return this.nodeVersions[this.nodeVersion];
|
||||
} else {
|
||||
return this.nodeVersions[defaultNodeVersion];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Electron versions: First, fetch them from GitHub,
|
||||
* then update their respective downloaded state.
|
||||
@@ -226,6 +254,21 @@ export class AppState {
|
||||
this.isUpdatingElectronVersions = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Node versions: First, fetch them from Node.js,
|
||||
* then update their respective downloaded state.
|
||||
*
|
||||
* Fails silently.
|
||||
*/
|
||||
@action public async updateNodeVersions() {
|
||||
try {
|
||||
const nodeVersions = await getUpdatedNodeVersions();
|
||||
this.nodeVersions = arrayToStringMap(nodeVersions);
|
||||
} catch (error) {
|
||||
console.warn(`State: Could not update Node.js versions`, error);
|
||||
}
|
||||
}
|
||||
|
||||
@action public async getName() {
|
||||
if (!this.name) {
|
||||
this.name = await getName(this);
|
||||
@@ -238,10 +281,21 @@ export class AppState {
|
||||
this.isConsoleShowing = !this.isConsoleShowing;
|
||||
}
|
||||
|
||||
@action public toggleNodeMode() {
|
||||
this.isNodeMode = !this.isNodeMode;
|
||||
}
|
||||
|
||||
@action public clearConsole() {
|
||||
this.output = [];
|
||||
}
|
||||
|
||||
@action public toggleBisectCommands() {
|
||||
// guard against hiding the commands when executing a bisect
|
||||
if (!this.Bisector && !this.isBisectDialogShowing) {
|
||||
this.isBisectCommandShowing = !this.isBisectCommandShowing;
|
||||
}
|
||||
}
|
||||
|
||||
@action public toggleAddVersionDialog() {
|
||||
this.isAddVersionDialogShowing = !this.isAddVersionDialogShowing;
|
||||
}
|
||||
@@ -262,6 +316,10 @@ export class AppState {
|
||||
}
|
||||
}
|
||||
|
||||
@action public toggleBisectDialog() {
|
||||
this.isBisectDialogShowing = !this.isBisectDialogShowing;
|
||||
}
|
||||
|
||||
@action public toggleConfirmationPromptDialog() {
|
||||
this.isConfirmationPromptShowing = !this.isConfirmationPromptShowing;
|
||||
|
||||
@@ -314,16 +372,16 @@ export class AppState {
|
||||
@action public addLocalVersion(input: NpmVersion) {
|
||||
addLocalVersion(input);
|
||||
|
||||
this.versions = arrayToStringMap(getElectronVersions());
|
||||
this.versions = arrayToStringMap(getAllElectronVersions());
|
||||
this.updateDownloadedVersionState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a version of Electron
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
/**
|
||||
* Remove a version of Electron
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@action public async removeVersion(input: string) {
|
||||
const version = normalizeVersion(input);
|
||||
const release = this.versions[version];
|
||||
@@ -350,19 +408,19 @@ export class AppState {
|
||||
saveLocalVersions(versionsAsArray);
|
||||
} else {
|
||||
await this.binaryManager.remove(version);
|
||||
updatedVersions[version].state = ElectronVersionState.unknown;
|
||||
updatedVersions[version].state = VersionState.unknown;
|
||||
}
|
||||
|
||||
this.versions = updatedVersions;
|
||||
this.updateDownloadedVersionState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a version of Electron.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
/**
|
||||
* Download a version of Electron.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@action public async downloadVersion(input: string) {
|
||||
const version = normalizeVersion(input);
|
||||
console.log(`State: Downloading Electron ${version}`);
|
||||
@@ -376,7 +434,7 @@ export class AppState {
|
||||
console.log(`State: Instructing BinaryManager to fetch v${version}`);
|
||||
const updatedVersions = { ...this.versions };
|
||||
updatedVersions[version] = updatedVersions[version] || {};
|
||||
updatedVersions[version].state = ElectronVersionState.downloading;
|
||||
updatedVersions[version].state = VersionState.downloading;
|
||||
this.versions = updatedVersions;
|
||||
|
||||
await this.binaryManager.setup(version);
|
||||
@@ -386,12 +444,33 @@ export class AppState {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a version of Electron (and download it if necessary).
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
/**
|
||||
* Select a version of Node.js (and download fi necessary)
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@action public async setNodeVersion(input: string) {
|
||||
const version = normalizeVersion(input);
|
||||
|
||||
if (!this.nodeVersions[version]) {
|
||||
console.warn(`State: Called setNodeVersion() with ${version}, which does not exist.`);
|
||||
this.setVersion(this.nodeVersions[0].version);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`State: Switching to Node ${version}`);
|
||||
|
||||
this.nodeVersion = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a version of Electron (and download it if necessary).
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@action public async setVersion(input: string) {
|
||||
const version = normalizeVersion(input);
|
||||
|
||||
@@ -436,18 +515,18 @@ export class AppState {
|
||||
await this.downloadVersion(version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Go and check which versions have already been downloaded.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
/**
|
||||
* Go and check which versions have already been downloaded.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@action public async updateDownloadedVersionState(): Promise<void> {
|
||||
const downloadedVersions = await this.binaryManager.getDownloadedVersions();
|
||||
const updatedVersions = { ...this.versions };
|
||||
|
||||
(downloadedVersions || []).forEach((version) => {
|
||||
if (updatedVersions[version]) {
|
||||
updatedVersions[version].state = ElectronVersionState.ready;
|
||||
updatedVersions[version].state = VersionState.ready;
|
||||
}
|
||||
});
|
||||
console.log(`State: Updated version state`, updatedVersions);
|
||||
@@ -577,7 +656,7 @@ export class AppState {
|
||||
*/
|
||||
@action public showMosaic(id: MosaicId) {
|
||||
const currentlyVisible = getVisibleMosaics(this.mosaicArrangement);
|
||||
this.setVisibleMosaics([ ...currentlyVisible, id ]);
|
||||
this.setVisibleMosaics([...currentlyVisible, id]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
} from 'electron';
|
||||
import { autorun } from 'mobx';
|
||||
|
||||
import { ElectronVersion, ElectronVersionState } from '../interfaces';
|
||||
import { ElectronVersion, VersionState } from '../interfaces';
|
||||
import { getNiceGreeting } from '../utils/nice-greeting';
|
||||
import { sortedElectronMap } from '../utils/sorted-electron-map';
|
||||
import { sortedElectronMap } from '../utils/sorted-map';
|
||||
import { AppState } from './state';
|
||||
|
||||
const { TouchBar } = remote;
|
||||
@@ -26,7 +26,7 @@ const {
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getItemIcon(
|
||||
{ state }: Partial<ElectronVersion> = { state: ElectronVersionState.unknown }
|
||||
{ state }: Partial<ElectronVersion> = { state: VersionState.unknown }
|
||||
) {
|
||||
return state === 'ready'
|
||||
? '💾'
|
||||
|
||||
+62
-16
@@ -1,4 +1,4 @@
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState, NpmVersion } from '../interfaces';
|
||||
import { ElectronVersion, ElectronVersionSource, NodeVersion, NpmVersion, VersionState } from '../interfaces';
|
||||
import { normalizeVersion } from '../utils/normalize-version';
|
||||
|
||||
export const enum ElectronReleaseChannel {
|
||||
@@ -82,7 +82,7 @@ export const enum VersionKeys {
|
||||
* @param {() => Array<NpmVersion>} fallbackMethod
|
||||
* @returns {Array<NpmVersion>}
|
||||
*/
|
||||
function getVersions(
|
||||
function getElectronVersions(
|
||||
key: VersionKeys, fallbackMethod: () => Array<NpmVersion>
|
||||
): Array<NpmVersion> {
|
||||
const fromLs = window.localStorage.getItem(key);
|
||||
@@ -117,7 +117,7 @@ function getVersions(
|
||||
* @param {VersionKeys} key
|
||||
* @param {Array<NpmVersion} versions
|
||||
*/
|
||||
function saveVersions(key: VersionKeys, versions: Array<NpmVersion>) {
|
||||
function saveElectronVersions(key: VersionKeys, versions: Array<NpmVersion>) {
|
||||
const stringified = JSON.stringify(versions);
|
||||
window.localStorage.setItem(key, stringified);
|
||||
}
|
||||
@@ -127,12 +127,12 @@ function saveVersions(key: VersionKeys, versions: Array<NpmVersion>) {
|
||||
*
|
||||
* @returns {Array<NpmVersion>}
|
||||
*/
|
||||
export function getElectronVersions(): Array<ElectronVersion> {
|
||||
export function getAllElectronVersions(): Array<ElectronVersion> {
|
||||
const known: Array<ElectronVersion> = getKnownVersions().map((version) => {
|
||||
return {
|
||||
...version,
|
||||
source: ElectronVersionSource.remote,
|
||||
state: ElectronVersionState.unknown
|
||||
state: VersionState.unknown
|
||||
};
|
||||
});
|
||||
|
||||
@@ -140,11 +140,11 @@ export function getElectronVersions(): Array<ElectronVersion> {
|
||||
return {
|
||||
...version,
|
||||
source: ElectronVersionSource.local,
|
||||
state: ElectronVersionState.ready
|
||||
state: VersionState.ready
|
||||
};
|
||||
});
|
||||
|
||||
return [ ...known, ...local ];
|
||||
return [...known, ...local];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +171,7 @@ export function addLocalVersion(input: NpmVersion): Array<NpmVersion> {
|
||||
* @returns {Array<NpmVersion>}
|
||||
*/
|
||||
export function getLocalVersions(): Array<NpmVersion> {
|
||||
const versions = getVersions(VersionKeys.local, () => []);
|
||||
const versions = getElectronVersions(VersionKeys.local, () => []);
|
||||
|
||||
return versions;
|
||||
}
|
||||
@@ -190,7 +190,7 @@ export function saveLocalVersions(versions: Array<NpmVersion | ElectronVersion>)
|
||||
return true;
|
||||
});
|
||||
|
||||
return saveVersions(VersionKeys.local, filteredVersions);
|
||||
return saveElectronVersions(VersionKeys.local, filteredVersions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,7 +200,7 @@ export function saveLocalVersions(versions: Array<NpmVersion | ElectronVersion>)
|
||||
* @returns {Array<NpmVersion>}
|
||||
*/
|
||||
export function getKnownVersions(): Array<NpmVersion> {
|
||||
return getVersions(VersionKeys.known, () => require('../../static/releases.json'));
|
||||
return getElectronVersions(VersionKeys.known, () => require('../../static/releases.json'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,7 +209,7 @@ export function getKnownVersions(): Array<NpmVersion> {
|
||||
* @param {Array<NpmVersion>} versions
|
||||
*/
|
||||
export function saveKnownVersions(versions: Array<NpmVersion>) {
|
||||
return saveVersions(VersionKeys.known, versions);
|
||||
return saveElectronVersions(VersionKeys.known, versions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,20 +222,66 @@ export function saveKnownVersions(versions: Array<NpmVersion>) {
|
||||
export async function getUpdatedElectronVersions(
|
||||
): Promise<Array<ElectronVersion>> {
|
||||
try {
|
||||
await fetchVersions();
|
||||
await fetchElectronVersions();
|
||||
} catch (error) {
|
||||
console.warn(`Versions: Failed to fetch versions`, { error });
|
||||
console.warn(`Versions: Failed to fetch Electron versions`, { error });
|
||||
}
|
||||
|
||||
return getElectronVersions();
|
||||
return getAllElectronVersions();
|
||||
}
|
||||
|
||||
export async function getUpdatedNodeVersions(): Promise<Array<NodeVersion>> {
|
||||
try {
|
||||
return await fetchNodeVersions();
|
||||
} catch (error) {
|
||||
console.warn(`Versions: Failed to fetch Node.js versions`, { error });
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the latest known versions directly from npm.
|
||||
* Fetch the latest known Node.js versions directly from Node.js.
|
||||
*
|
||||
* @returns {Promise<Array<NpmVersion>>}
|
||||
*
|
||||
* let entry = {
|
||||
* "headers": 1,
|
||||
* "linux-arm64": 2,
|
||||
* "linux-x64": 6,
|
||||
* "osx-x64-tar": 8,
|
||||
* "win-x64-zip": 14,
|
||||
* "win-x86-zip": 18
|
||||
* }
|
||||
*
|
||||
* nodejs.org/dist/${version}/node-${version}-${files[entry]}.file-ext
|
||||
* ^ for windows, take the plat and extend with the file extension in the identifier above
|
||||
*/
|
||||
export async function fetchNodeVersions() {
|
||||
const channels = [
|
||||
`https://nodejs.org/dist/index.json`
|
||||
];
|
||||
|
||||
let output: Array<NodeVersion> = [];
|
||||
|
||||
for (const channelUrl of channels) {
|
||||
const response = await window.fetch(channelUrl);
|
||||
const versions = await response.json();
|
||||
|
||||
output = versions.map((input: any) => ({
|
||||
version: normalizeVersion(input.version)
|
||||
}));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the latest known Electron versions directly from npm.
|
||||
*
|
||||
* @returns {Promise<Array<NpmVersion>>}
|
||||
*/
|
||||
export async function fetchVersions() {
|
||||
export async function fetchElectronVersions() {
|
||||
const channels = [
|
||||
`https://registry.npmjs.org/electron`, // stable, beta
|
||||
`https://registry.npmjs.org/electron-nightly` // nightly
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ElectronVersion } from '../interfaces';
|
||||
import { Version } from '../interfaces';
|
||||
import { normalizeVersion } from './normalize-version';
|
||||
|
||||
/**
|
||||
@@ -8,9 +8,9 @@ import { normalizeVersion } from './normalize-version';
|
||||
* @param {Array<ElectronVersion>} input
|
||||
* @returns {Record<string, ElectronVersion>}
|
||||
*/
|
||||
export function arrayToStringMap(
|
||||
input: Array<ElectronVersion>
|
||||
): Record<string, ElectronVersion> {
|
||||
export function arrayToStringMap<T extends Version>(
|
||||
input: Array<T>
|
||||
): Record<string, T> {
|
||||
const output = {};
|
||||
|
||||
input.forEach((version) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as semver from 'semver';
|
||||
|
||||
import { ElectronVersion } from '../interfaces';
|
||||
import { ElectronVersion, NodeVersion } from '../interfaces';
|
||||
|
||||
/**
|
||||
* Sorts Electron versions and returns the result of a
|
||||
@@ -13,6 +13,28 @@ import { ElectronVersion } from '../interfaces';
|
||||
export function sortedElectronMap<T>(
|
||||
versions: Record<string, ElectronVersion>,
|
||||
mapFn: (key: string, version: ElectronVersion) => T
|
||||
) {
|
||||
return sortedMap<ElectronVersion, T>(versions, mapFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts Node versions and returns the result of a
|
||||
* map function.
|
||||
*
|
||||
* @param {Record<string, NodeVersion>} versions
|
||||
* @param {(key: string, version: NodeVersion) => void} mapFn
|
||||
* @returns {Array<T>}
|
||||
*/
|
||||
export function sortedNodeMap<T>(
|
||||
versions: Record<string, NodeVersion>,
|
||||
mapFn: (key: string, version: NodeVersion) => T
|
||||
) {
|
||||
return sortedMap<NodeVersion, T>(versions, mapFn);
|
||||
}
|
||||
|
||||
function sortedMap<T1, T2>(
|
||||
versions: Record<string, T1>,
|
||||
mapFn: (key: string, version: T1) => T2
|
||||
) {
|
||||
return Object.keys(versions)
|
||||
.sort((a, b) => {
|
||||
@@ -30,5 +52,5 @@ export function sortedElectronMap<T>(
|
||||
|
||||
return semver.gt(a, b, true) ? -1 : 1;
|
||||
})
|
||||
.map((key) => mapFn(key, versions[key])) as Array<T>;
|
||||
.map((key) => mapFn(key, versions[key])) as Array<T2>;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.debugger</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -12,13 +12,16 @@ app.on('ready', () => {
|
||||
|
||||
// Show an "Open File" dialog and attempt to open
|
||||
// the chosen file in our window.
|
||||
//
|
||||
// If you provide a callback, the method will be asynchronous,
|
||||
// but in thise case, we'll just use the synchronous variant.
|
||||
const file = dialog.showOpenDialog({
|
||||
title: 'Hello!',
|
||||
dialog.showOpenDialog(mainWindow, {
|
||||
properties: ['openFile']
|
||||
}).then(result => {
|
||||
if (result.canceled) {
|
||||
console.log('Dialog was canceled')
|
||||
} else {
|
||||
const file = result.filePaths[0]
|
||||
mainWindow.loadURL(`file://${file}`)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
mainWindow.loadURL(`file://${file}`)
|
||||
})
|
||||
|
||||
+19
-12
@@ -1,12 +1,6 @@
|
||||
import { setupDevTools } from '../../src/main/devtools';
|
||||
|
||||
let mockIsDevMode = false;
|
||||
|
||||
jest.mock('../../src/utils/devmode', () => ({
|
||||
get isDevMode() {
|
||||
return mockIsDevMode;
|
||||
}
|
||||
}));
|
||||
import { isDevMode } from '../../src/utils/devmode';
|
||||
jest.mock('../../src/utils/devmode');
|
||||
|
||||
jest.mock('electron-devtools-installer', () => ({
|
||||
default: jest.fn(),
|
||||
@@ -17,18 +11,31 @@ jest.mock('electron-devtools-installer', () => ({
|
||||
describe('devtools', () => {
|
||||
it('does not set up developer tools if not in dev mode', () => {
|
||||
const devtools = require('electron-devtools-installer');
|
||||
|
||||
(isDevMode as jest.Mock).mockReturnValue(false);
|
||||
setupDevTools();
|
||||
|
||||
expect(devtools.default).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('sets up developer tools if not in dev mode', () => {
|
||||
it('sets up developer tools if in dev mode', () => {
|
||||
const devtools = require('electron-devtools-installer');
|
||||
|
||||
mockIsDevMode = true;
|
||||
(isDevMode as jest.Mock).mockReturnValue(true);
|
||||
setupDevTools();
|
||||
|
||||
expect(devtools.default).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('catch error in setting up developer tools', async (done) => {
|
||||
const devtools = require('electron-devtools-installer');
|
||||
// throw devtool error
|
||||
devtools.default.mockRejectedValue(new Error('devtool error'));
|
||||
(isDevMode as jest.Mock).mockReturnValue(true);
|
||||
|
||||
try {
|
||||
await setupDevTools();
|
||||
done();
|
||||
} catch (e) {
|
||||
expect(e).toMatch('error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,26 +3,96 @@ import { setupDialogs } from '../../src/main/dialogs';
|
||||
import { ipcMainManager } from '../../src/main/ipc';
|
||||
|
||||
import { dialog } from 'electron';
|
||||
import { flushPromises } from '../utils';
|
||||
|
||||
jest.mock('../../src/main/windows');
|
||||
|
||||
describe('dialogs', () => {
|
||||
it('sets up dialogs', () => {
|
||||
setupDialogs();
|
||||
|
||||
beforeEach(() => {
|
||||
setupDialogs();
|
||||
});
|
||||
|
||||
it('sets up dialogs', () => {
|
||||
expect(ipcMainManager.eventNames()).toEqual([
|
||||
IpcEvents.SHOW_WARNING_DIALOG,
|
||||
IpcEvents.SHOW_CONFIRMATION_DIALOG
|
||||
IpcEvents.SHOW_CONFIRMATION_DIALOG,
|
||||
IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG
|
||||
]);
|
||||
});
|
||||
|
||||
it('tries to show a dialog if triggered', () => {
|
||||
setupDialogs();
|
||||
describe('warning dialog', () => {
|
||||
it('shows dialog when triggering IPC event', () => {
|
||||
ipcMainManager.emit(IpcEvents.SHOW_WARNING_DIALOG, {}, { hi: 'hello' });
|
||||
expect(dialog.showMessageBox).toHaveBeenCalledWith(undefined, {
|
||||
type: 'warning',
|
||||
hi: 'hello'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ipcMainManager.emit(IpcEvents.SHOW_WARNING_DIALOG, {}, { hi: 'hello' });
|
||||
expect(dialog.showMessageBox).toHaveBeenCalledWith(undefined, {
|
||||
type: 'warning',
|
||||
hi: 'hello'
|
||||
describe('confirmation dialog', () => {
|
||||
it('shows dialog when triggering IPC event', () => {
|
||||
ipcMainManager.emit(IpcEvents.SHOW_CONFIRMATION_DIALOG, {}, { hi: 'hello' });
|
||||
expect(dialog.showMessageBox).toHaveBeenCalledWith(undefined, {
|
||||
type: 'warning',
|
||||
hi: 'hello'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('local version folder dialog', () => {
|
||||
it('shows dialog when triggering IPC event', () => {
|
||||
(dialog.showOpenDialog as jest.Mock).mockResolvedValue({
|
||||
filePaths: []
|
||||
});
|
||||
|
||||
ipcMainManager.emit(IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG, {
|
||||
reply: jest.fn()
|
||||
});
|
||||
expect(dialog.showOpenDialog).toHaveBeenCalledWith(expect.objectContaining({
|
||||
properties: ['openDirectory']
|
||||
}));
|
||||
});
|
||||
|
||||
it('triggers IPC load local version event', async () => {
|
||||
const replyFn = jest.fn();
|
||||
const paths = ['/test/path/'];
|
||||
|
||||
(dialog.showOpenDialog as jest.Mock).mockResolvedValue({
|
||||
filePaths: paths
|
||||
});
|
||||
|
||||
ipcMainManager.emit(IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG, {
|
||||
reply: replyFn
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
expect(replyFn).toHaveBeenCalledWith(IpcEvents.LOAD_LOCAL_VERSION_FOLDER, paths);
|
||||
});
|
||||
|
||||
it('does nothing if not given a path', async () => {
|
||||
const replyFn = jest.fn();
|
||||
|
||||
// empty array
|
||||
(dialog.showOpenDialog as jest.Mock).mockResolvedValue({
|
||||
filePaths: []
|
||||
});
|
||||
ipcMainManager.emit(IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG, {
|
||||
reply: replyFn
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
expect(replyFn).not.toHaveBeenCalled();
|
||||
|
||||
// nothing in response
|
||||
(dialog.showOpenDialog as jest.Mock).mockResolvedValue({});
|
||||
ipcMainManager.emit(IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG, {
|
||||
reply: replyFn
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
expect(replyFn).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+16
-2
@@ -78,6 +78,20 @@ describe('menu', () => {
|
||||
(toggleMap as any).click();
|
||||
expect(ipcMainManager.send).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('adds Bisect toggle', () => {
|
||||
overridePlatform('linux');
|
||||
|
||||
setupMenu();
|
||||
|
||||
const result = (electron.Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const submenu = result[2].submenu as Array<Electron.MenuItemConstructorOptions>;
|
||||
|
||||
const toggleSoftWrap = submenu.find(({ label }) => label === 'Toggle Bisect Helper');
|
||||
(toggleSoftWrap as any).click();
|
||||
expect(ipcMainManager.send).toHaveBeenCalledWith(IpcEvents.BISECT_COMMANDS_TOGGLE);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('menu groups', () => {
|
||||
@@ -174,7 +188,7 @@ describe('menu', () => {
|
||||
it('attempts to open a template on click', () => {
|
||||
showMe.submenu[0].submenu[0].click();
|
||||
expect(ipcMainManager.send)
|
||||
.toHaveBeenCalledWith(IpcEvents.FS_OPEN_TEMPLATE, [ 'App' ]);
|
||||
.toHaveBeenCalledWith(IpcEvents.FS_OPEN_TEMPLATE, ['App']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -182,7 +196,7 @@ describe('menu', () => {
|
||||
let tasks: any;
|
||||
|
||||
beforeEach(() => {
|
||||
const mock = (electron.Menu.buildFromTemplate as any).mock;
|
||||
const mock = (electron.Menu.buildFromTemplate as any).mock;
|
||||
const menu = mock.calls[0][0];
|
||||
tasks = menu[menu.length - 3];
|
||||
});
|
||||
|
||||
@@ -116,7 +116,6 @@ const app = {
|
||||
quit: jest.fn(),
|
||||
relaunch: jest.fn(),
|
||||
setJumpList: jest.fn(),
|
||||
setName: jest.fn(),
|
||||
requestSingleInstanceLock: jest.fn(),
|
||||
on: jest.fn(),
|
||||
once: jest.fn()
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { ElectronVersionSource, ElectronVersionState } from '../../src/interfaces';
|
||||
import { Bisector } from '../../src/renderer/bisect';
|
||||
|
||||
const generateVersionRange = (rangeLength: number) =>
|
||||
(new Array(rangeLength)).fill(0).map((_, i) => ({
|
||||
state: ElectronVersionState.ready,
|
||||
version: `${i + 1}.0.0`,
|
||||
source: ElectronVersionSource.local
|
||||
}));
|
||||
|
||||
describe('bisect', () => {
|
||||
let bisector: Bisector;
|
||||
|
||||
beforeEach(() => {
|
||||
const versions = generateVersionRange(9);
|
||||
bisector = new Bisector(versions);
|
||||
});
|
||||
|
||||
it('selects a pivot in the middle of the array', () => {
|
||||
const pivot = bisector.getCurrentVersion();
|
||||
const middleIndex = Math.floor(bisector.revList.length / 2);
|
||||
expect(pivot).toBe(bisector.revList[middleIndex]);
|
||||
});
|
||||
|
||||
describe('continue()', () => {
|
||||
it('returns the current version', () => {
|
||||
const result = bisector.continue(true);
|
||||
const version = bisector.getCurrentVersion();
|
||||
expect(result).toBe(version);
|
||||
});
|
||||
|
||||
it('discards lower half of the range if pivot is good version', () => {
|
||||
const pivotVersion = bisector.getCurrentVersion();
|
||||
bisector.continue(true);
|
||||
expect(bisector.revList[bisector.minRev]).toBe(pivotVersion);
|
||||
});
|
||||
|
||||
it('discards upper half of the range if pivot is bad version', () => {
|
||||
const pivotVersion = bisector.getCurrentVersion();
|
||||
bisector.continue(false);
|
||||
expect(bisector.revList[bisector.maxRev]).toBe(pivotVersion);
|
||||
});
|
||||
|
||||
it('terminates if fewer than 2 items are left', () => {
|
||||
const versions = generateVersionRange(2);
|
||||
bisector = new Bisector(versions);
|
||||
|
||||
expect(bisector.revList.length).toBe(2);
|
||||
const responseGood = bisector.continue(true);
|
||||
expect(responseGood).toHaveLength(2);
|
||||
expect(versions).toContain(responseGood[0]);
|
||||
expect(versions).toContain(responseGood[1]);
|
||||
|
||||
bisector = new Bisector(versions);
|
||||
|
||||
expect(bisector.revList.length).toBe(2);
|
||||
const responseBad = bisector.continue(false);
|
||||
expect(responseBad).toHaveLength(2);
|
||||
expect(versions).toContain(responseBad[0]);
|
||||
expect(versions).toContain(responseBad[1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCurrentVersion()', () => {
|
||||
it('returns a version within the range', () => {
|
||||
const version = bisector.getCurrentVersion();
|
||||
expect(bisector.revList).toContain(version);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Bisect commands component renders bisect dialog button if no bisect instance 1`] = `
|
||||
<Blueprint3.Button
|
||||
icon="git-branch"
|
||||
text="Bisect"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Bisect commands component renders helper buttons if bisect instance is active 1`] = `
|
||||
<Fragment>
|
||||
<Blueprint3.Button
|
||||
icon="thumbs-up"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
icon="thumbs-down"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
icon="cross"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Cancel Bisect
|
||||
</Blueprint3.Button>
|
||||
</Fragment>
|
||||
`;
|
||||
@@ -1,46 +1,50 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`VersionChooser component handles corrupt data 1`] = `
|
||||
<Blueprint3.Select
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.2",
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.1",
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "1.8.7",
|
||||
},
|
||||
]
|
||||
}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
<Blueprint3.ButtonGroup>
|
||||
<Blueprint3.Select
|
||||
disabled={false}
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.2",
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.1",
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "1.8.7",
|
||||
},
|
||||
]
|
||||
}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
/>
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
className="version-chooser"
|
||||
disabled={false}
|
||||
icon="saved"
|
||||
text="Electron v2.0.2"
|
||||
/>
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
className="version-chooser"
|
||||
icon="saved"
|
||||
text="Electron v2.0.2"
|
||||
/>
|
||||
</Blueprint3.Select>
|
||||
</Blueprint3.Select>
|
||||
</Blueprint3.ButtonGroup>
|
||||
`;
|
||||
|
||||
exports[`VersionChooser component renderItem() renders an item 1`] = `
|
||||
@@ -62,44 +66,48 @@ exports[`VersionChooser component renderItem() renders an item 1`] = `
|
||||
`;
|
||||
|
||||
exports[`VersionChooser component renders 1`] = `
|
||||
<Blueprint3.Select
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.2",
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.1",
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "1.8.7",
|
||||
},
|
||||
]
|
||||
}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
<Blueprint3.ButtonGroup>
|
||||
<Blueprint3.Select
|
||||
disabled={false}
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.2",
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.1",
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "1.8.7",
|
||||
},
|
||||
]
|
||||
}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
/>
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
className="version-chooser"
|
||||
disabled={false}
|
||||
icon="saved"
|
||||
text="Electron v2.0.2"
|
||||
/>
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
className="version-chooser"
|
||||
icon="saved"
|
||||
text="Electron v2.0.2"
|
||||
/>
|
||||
</Blueprint3.Select>
|
||||
</Blueprint3.Select>
|
||||
</Blueprint3.ButtonGroup>
|
||||
`;
|
||||
|
||||
@@ -16,10 +16,9 @@ exports[`AddVersionDialog component renders 1`] = `
|
||||
id="custom-electron-version"
|
||||
inputProps={
|
||||
Object {
|
||||
"webkitdirectory": "true",
|
||||
"onClick": [Function],
|
||||
}
|
||||
}
|
||||
onInputChange={[Function]}
|
||||
text="/test/file"
|
||||
/>
|
||||
<br />
|
||||
@@ -79,10 +78,9 @@ exports[`AddVersionDialog component renders 2`] = `
|
||||
id="custom-electron-version"
|
||||
inputProps={
|
||||
Object {
|
||||
"webkitdirectory": "true",
|
||||
"onClick": [Function],
|
||||
}
|
||||
}
|
||||
onInputChange={[Function]}
|
||||
text="/test/file"
|
||||
/>
|
||||
<br />
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`BisectDialog component renders 1`] = `
|
||||
<Blueprint3.Dialog
|
||||
canOutsideClickClose={true}
|
||||
className="dialog-add-version"
|
||||
isOpen={false}
|
||||
onClose={[Function]}
|
||||
title="Start a bisect session"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-body"
|
||||
>
|
||||
<Component>
|
||||
Earliest Version
|
||||
<Blueprint3.Select
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "2.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "3.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "4.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "5.0.0",
|
||||
},
|
||||
]
|
||||
}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
/>
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
fill={true}
|
||||
icon="saved"
|
||||
text="v4.0.0"
|
||||
/>
|
||||
</Blueprint3.Select>
|
||||
</Component>
|
||||
<Component>
|
||||
Latest Version
|
||||
<Blueprint3.Select
|
||||
disabled={false}
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "2.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "3.0.0",
|
||||
},
|
||||
]
|
||||
}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
/>
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
disabled={false}
|
||||
fill={true}
|
||||
icon="small-minus"
|
||||
text=""
|
||||
/>
|
||||
</Blueprint3.Select>
|
||||
</Component>
|
||||
</div>
|
||||
<div
|
||||
className="bp3-dialog-footer"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-footer-actions"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
disabled={true}
|
||||
icon="play"
|
||||
key="submit"
|
||||
onClick={[Function]}
|
||||
text="Begin"
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
icon="cross"
|
||||
key="cancel"
|
||||
onClick={[Function]}
|
||||
text="Cancel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Blueprint3.Dialog>
|
||||
`;
|
||||
|
||||
exports[`BisectDialog component renders 2`] = `
|
||||
<Blueprint3.Dialog
|
||||
canOutsideClickClose={true}
|
||||
className="dialog-add-version"
|
||||
isOpen={false}
|
||||
onClose={[Function]}
|
||||
title="Start a bisect session"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-body"
|
||||
>
|
||||
<Component>
|
||||
Earliest Version
|
||||
<Blueprint3.Select
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "2.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "3.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "4.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "5.0.0",
|
||||
},
|
||||
]
|
||||
}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
/>
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
fill={true}
|
||||
icon="small-minus"
|
||||
text=""
|
||||
/>
|
||||
</Blueprint3.Select>
|
||||
</Component>
|
||||
<Component>
|
||||
Latest Version
|
||||
<Blueprint3.Select
|
||||
disabled={true}
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "2.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "3.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "4.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "5.0.0",
|
||||
},
|
||||
]
|
||||
}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
/>
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
disabled={true}
|
||||
fill={true}
|
||||
icon="small-minus"
|
||||
text=""
|
||||
/>
|
||||
</Blueprint3.Select>
|
||||
</Component>
|
||||
</div>
|
||||
<div
|
||||
className="bp3-dialog-footer"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-footer-actions"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
disabled={true}
|
||||
icon="play"
|
||||
key="submit"
|
||||
onClick={[Function]}
|
||||
text="Begin"
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
icon="cross"
|
||||
key="cancel"
|
||||
onClick={[Function]}
|
||||
text="Cancel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Blueprint3.Dialog>
|
||||
`;
|
||||
|
||||
exports[`BisectDialog component renders 3`] = `
|
||||
<Blueprint3.Dialog
|
||||
canOutsideClickClose={true}
|
||||
className="dialog-add-version"
|
||||
isOpen={false}
|
||||
onClose={[Function]}
|
||||
title="Start a bisect session"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-body"
|
||||
>
|
||||
<Component>
|
||||
Earliest Version
|
||||
<Blueprint3.Select
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "2.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "3.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "4.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "5.0.0",
|
||||
},
|
||||
]
|
||||
}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
/>
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
fill={true}
|
||||
icon="saved"
|
||||
text="v4.0.0"
|
||||
/>
|
||||
</Blueprint3.Select>
|
||||
</Component>
|
||||
<Component>
|
||||
Latest Version
|
||||
<Blueprint3.Select
|
||||
disabled={false}
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "2.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "local",
|
||||
"state": "ready",
|
||||
"version": "3.0.0",
|
||||
},
|
||||
]
|
||||
}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
/>
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
disabled={false}
|
||||
fill={true}
|
||||
icon="small-minus"
|
||||
text=""
|
||||
/>
|
||||
</Blueprint3.Select>
|
||||
</Component>
|
||||
</div>
|
||||
<div
|
||||
className="bp3-dialog-footer"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-footer-actions"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
disabled={true}
|
||||
icon="play"
|
||||
key="submit"
|
||||
onClick={[Function]}
|
||||
text="Begin"
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
icon="cross"
|
||||
key="cancel"
|
||||
onClick={[Function]}
|
||||
text="Cancel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Blueprint3.Dialog>
|
||||
`;
|
||||
@@ -0,0 +1,86 @@
|
||||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
import { BisectHandler } from '../../../src/renderer/components/commands-bisect';
|
||||
|
||||
describe('Bisect commands component', () => {
|
||||
let store: any;
|
||||
|
||||
beforeEach(() => {
|
||||
store = {
|
||||
Bisector: {
|
||||
continue: jest.fn(),
|
||||
getCurrentVersion: jest.fn()
|
||||
},
|
||||
setVersion: jest.fn(),
|
||||
version: '1.0.0',
|
||||
pushOutput: jest.fn()
|
||||
};
|
||||
});
|
||||
|
||||
it('renders helper buttons if bisect instance is active', () => {
|
||||
const wrapper = shallow(<BisectHandler appState={store} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders bisect dialog button if no bisect instance', () => {
|
||||
delete store.Bisector;
|
||||
const wrapper = shallow(<BisectHandler appState={store} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('buttons', () => {
|
||||
let wrapper: ShallowWrapper;
|
||||
let instance: BisectHandler;
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<BisectHandler appState={store} />);
|
||||
instance = wrapper.instance() as any;
|
||||
instance.continueBisect = jest.fn();
|
||||
});
|
||||
|
||||
it('passes in Version=Good when thumbs up button is pressed', () => {
|
||||
wrapper.find('[icon="thumbs-up"]').simulate('click');
|
||||
expect(instance.continueBisect).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('passes in Version=Bad when thumbs down button is pressed', () => {
|
||||
wrapper.find('[icon="thumbs-down"]').simulate('click');
|
||||
expect(instance.continueBisect).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('continueBisect()', () => {
|
||||
it('sets version assigned by bisect algorithm', () => {
|
||||
const wrapper = shallow(<BisectHandler appState={store} />);
|
||||
const instance: BisectHandler = wrapper.instance() as any;
|
||||
|
||||
store.Bisector.continue.mockReturnValue({
|
||||
version: '2.0.0'
|
||||
});
|
||||
instance.continueBisect(true);
|
||||
expect(store.setVersion).toHaveBeenCalledWith('2.0.0');
|
||||
});
|
||||
|
||||
it('terminates bisect if algorithm returns array', () => {
|
||||
const wrapper = shallow(<BisectHandler appState={store} />);
|
||||
const instance: BisectHandler = wrapper.instance() as any;
|
||||
instance.terminateBisect = jest.fn();
|
||||
|
||||
// same value is only returned when there is only 1 version left
|
||||
store.Bisector.continue.mockReturnValue(['minRev', 'maxRev']);
|
||||
instance.continueBisect(true);
|
||||
expect(store.setVersion).not.toHaveBeenCalled();
|
||||
expect(instance.terminateBisect).toHaveBeenCalled();
|
||||
expect(store.pushOutput).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('terminateBisect()', () => {
|
||||
it('removes the bisect instance from the app state', () => {
|
||||
const wrapper = shallow(<BisectHandler appState={store} />);
|
||||
const instance: BisectHandler = wrapper.instance() as any;
|
||||
|
||||
instance.terminateBisect();
|
||||
expect(store.Bisector).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,15 +1,16 @@
|
||||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { IpcEvents } from '../../../src/ipc-events';
|
||||
import { AddVersionDialog } from '../../../src/renderer/components/dialog-add-version';
|
||||
import { ipcRendererManager } from '../../../src/renderer/ipc';
|
||||
import { overridePlatform, resetPlatform } from '../../utils';
|
||||
jest.mock('../../../src/renderer/ipc');
|
||||
|
||||
describe('AddVersionDialog component', () => {
|
||||
let store: any;
|
||||
|
||||
const mockFile = {
|
||||
path: '/test/file'
|
||||
};
|
||||
const mockFile = '/test/file';
|
||||
|
||||
beforeAll(() => {
|
||||
// We render the buttons different depending on the
|
||||
@@ -37,7 +38,7 @@ describe('AddVersionDialog component', () => {
|
||||
wrapper.setState({
|
||||
isValidVersion: true,
|
||||
isValidElectron: true,
|
||||
file: mockFile
|
||||
folderPath: mockFile
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
@@ -45,35 +46,32 @@ describe('AddVersionDialog component', () => {
|
||||
wrapper.setState({
|
||||
isValidVersion: false,
|
||||
isValidElectron: true,
|
||||
file: mockFile
|
||||
folderPath: mockFile
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('onChangeFile()', () => {
|
||||
it('handles the change event', async () => {
|
||||
it('overrides default input with Electron dialog', () => {
|
||||
const preventDefault = jest.fn();
|
||||
|
||||
const wrapper = shallow(<AddVersionDialog appState={store} />);
|
||||
const inp = wrapper.find('#custom-electron-version');
|
||||
inp.dive().find('input[type="file"]').simulate('click', { preventDefault });
|
||||
|
||||
expect(ipcRendererManager.send as jest.Mock)
|
||||
.toHaveBeenCalledWith(IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG);
|
||||
expect(preventDefault).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('setFolderPath()', () => {
|
||||
it('does something', async () => {
|
||||
store.binaryManager.getIsDownloaded.mockResolvedValue(true);
|
||||
const wrapper = shallow(<AddVersionDialog appState={store} />);
|
||||
await (wrapper.instance() as any).setFolderPath('/test/');
|
||||
|
||||
await (wrapper.instance() as any).onChangeFile({ target: { files: [ mockFile ] } });
|
||||
expect(wrapper.state('file')).toBe(mockFile);
|
||||
});
|
||||
|
||||
it('handles invalid input', async () => {
|
||||
const wrapper = shallow(<AddVersionDialog appState={store} />);
|
||||
|
||||
await (wrapper.instance() as any).onChangeFile({ target: { files: [] } });
|
||||
expect(wrapper.state('file')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('handles the change event and checks for Electron', async () => {
|
||||
const wrapper = shallow(<AddVersionDialog appState={store} />);
|
||||
|
||||
store.binaryManager.getIsDownloaded.mockReturnValueOnce(false);
|
||||
|
||||
await (wrapper.instance() as any).onChangeFile({ target: { files: [ mockFile ] } });
|
||||
expect(wrapper.state('file')).toBe(mockFile);
|
||||
expect(wrapper.state('isValidElectron')).toBe(false);
|
||||
expect(wrapper.state('isValidElectron')).toBe(true);
|
||||
expect(wrapper.state('folderPath')).toBe('/test/');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -93,7 +91,7 @@ describe('AddVersionDialog component', () => {
|
||||
expect(wrapper.state('isValidVersion')).toBe(false);
|
||||
expect(wrapper.state('version')).toBe('foo');
|
||||
|
||||
(wrapper.instance() as any).onChangeVersion({ target: { } });
|
||||
(wrapper.instance() as any).onChangeVersion({ target: {} });
|
||||
expect(wrapper.state('isValidVersion')).toBe(false);
|
||||
expect(wrapper.state('version')).toBe('');
|
||||
});
|
||||
@@ -113,9 +111,7 @@ describe('AddVersionDialog component', () => {
|
||||
|
||||
wrapper.setState({
|
||||
version: '3.3.3',
|
||||
file: {
|
||||
path: '/test/path'
|
||||
}
|
||||
folderPath: '/test/path'
|
||||
});
|
||||
|
||||
await (wrapper.instance() as any).onSubmit();
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
import { ElectronVersionSource, ElectronVersionState } from '../../../src/interfaces';
|
||||
import { Bisector } from '../../../src/renderer/bisect';
|
||||
import { BisectDialog } from '../../../src/renderer/components/dialog-bisect';
|
||||
import { ElectronReleaseChannel } from '../../../src/renderer/versions';
|
||||
|
||||
jest.mock('../../../src/renderer/bisect');
|
||||
|
||||
describe('BisectDialog component', () => {
|
||||
let store: any;
|
||||
|
||||
const generateVersionRange = (rangeLength: number) =>
|
||||
(new Array(rangeLength)).fill(0).map((_, i) => ({
|
||||
state: ElectronVersionState.ready,
|
||||
version: `${i + 1}.0.0`,
|
||||
source: ElectronVersionSource.local
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
store = {
|
||||
versions: generateVersionRange(5),
|
||||
versionsToShow: [ElectronReleaseChannel.stable],
|
||||
statesToShow: [ElectronVersionState.ready],
|
||||
setVersion: jest.fn()
|
||||
};
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<BisectDialog appState={store} />);
|
||||
// start and end selected
|
||||
wrapper.setState({
|
||||
startIndex: 3,
|
||||
endIndex: 0,
|
||||
allVersions: generateVersionRange(5)
|
||||
});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
// no selection
|
||||
wrapper.setState({
|
||||
startIndex: undefined,
|
||||
endIndex: undefined,
|
||||
allVersions: generateVersionRange(5)
|
||||
});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
// only start selected
|
||||
wrapper.setState({
|
||||
startIndex: 3,
|
||||
endIndex: undefined,
|
||||
allVersions: generateVersionRange(5)
|
||||
});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('onBeginSelect()', () => {
|
||||
it('sets the begin version', () => {
|
||||
const wrapper = shallow(<BisectDialog appState={store} />);
|
||||
const instance: BisectDialog = wrapper.instance() as any;
|
||||
|
||||
expect(instance.state.startIndex).toBeUndefined();
|
||||
instance.onBeginSelect(store.versions[2]);
|
||||
expect(instance.state.startIndex).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onEndSelect()', () => {
|
||||
it('sets the end version', () => {
|
||||
const wrapper = shallow(<BisectDialog appState={store} />);
|
||||
const instance: BisectDialog = wrapper.instance() as any;
|
||||
|
||||
expect(instance.state.endIndex).toBeUndefined();
|
||||
instance.onEndSelect(store.versions[2]);
|
||||
expect(instance.state.endIndex).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onSubmit()', () => {
|
||||
it('initiates a bisect instance and sets a version', async () => {
|
||||
const version = '1.0.0';
|
||||
(Bisector as jest.Mock).mockImplementation(() => {
|
||||
return {
|
||||
getCurrentVersion: () => ({ version })
|
||||
};
|
||||
});
|
||||
|
||||
const versions = generateVersionRange(5);
|
||||
|
||||
const wrapper = shallow(<BisectDialog appState={store} />);
|
||||
wrapper.setState({
|
||||
startIndex: 4,
|
||||
endIndex: 0,
|
||||
allVersions: versions
|
||||
});
|
||||
|
||||
const instance: BisectDialog = wrapper.instance() as any;
|
||||
await instance.onSubmit();
|
||||
expect(Bisector).toHaveBeenCalledWith(versions.slice(0, 5).reverse());
|
||||
expect(store.Bisector).toBeDefined();
|
||||
expect(store.setVersion).toHaveBeenCalledWith(version);
|
||||
});
|
||||
|
||||
it('does nothing if endIndex or startIndex are falsy', async () => {
|
||||
const wrapper = shallow(<BisectDialog appState={store} />);
|
||||
|
||||
wrapper.setState({
|
||||
startIndex: undefined,
|
||||
endIndex: 0
|
||||
});
|
||||
const instance1: BisectDialog = wrapper.instance() as any;
|
||||
await instance1.onSubmit();
|
||||
expect(Bisector).not.toHaveBeenCalled();
|
||||
|
||||
wrapper.setState({
|
||||
startIndex: 4,
|
||||
endIndex: undefined
|
||||
});
|
||||
const instance2: BisectDialog = wrapper.instance() as any;
|
||||
await instance2.onSubmit();
|
||||
expect(Bisector).not.toHaveBeenCalled();
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ALL_MOSAICS, EditorId, ElectronVersionSource, ElectronVersionState, PanelId } from '../../src/interfaces';
|
||||
import { Bisector } from '../../src/renderer/bisect';
|
||||
import { DEFAULT_MOSAIC_ARRANGEMENT } from '../../src/renderer/constants';
|
||||
import { getContent, isContentUnchanged } from '../../src/renderer/content';
|
||||
import { ipcRendererManager } from '../../src/renderer/ipc';
|
||||
@@ -142,7 +143,7 @@ describe('AppState', () => {
|
||||
|
||||
expect(appState.currentElectronVersion).toEqual(mockVersions['2.0.2']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleConsole()', () => {
|
||||
it('toggles the console', () => {
|
||||
@@ -179,6 +180,33 @@ describe('AppState', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleBisectCommands()', () => {
|
||||
it('toggles visibility of the bisect commands', () => {
|
||||
const isVisible = appState.isBisectCommandShowing;
|
||||
appState.toggleBisectCommands();
|
||||
expect(isVisible).not.toBe(appState.isBisectCommandShowing);
|
||||
});
|
||||
|
||||
it('takes no action if bisect dialog is active', () => {
|
||||
const isVisible = appState.isBisectCommandShowing;
|
||||
|
||||
expect(appState.isBisectDialogShowing).toBe(false);
|
||||
appState.toggleBisectDialog();
|
||||
|
||||
appState.toggleBisectCommands();
|
||||
expect(isVisible).toBe(appState.isBisectCommandShowing);
|
||||
});
|
||||
|
||||
it('takes no action if bisect instance is active', () => {
|
||||
const isVisible = appState.isBisectCommandShowing;
|
||||
|
||||
appState.Bisector = new Bisector([]);
|
||||
|
||||
appState.toggleBisectCommands();
|
||||
expect(isVisible).toBe(appState.isBisectCommandShowing);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleAddVersionDialog()', () => {
|
||||
it('toggles the add version dialog', () => {
|
||||
appState.toggleAddVersionDialog();
|
||||
@@ -296,7 +324,7 @@ describe('AppState', () => {
|
||||
await appState.setVersion('v1.0.0');
|
||||
|
||||
expect(getContent).toHaveBeenCalledTimes(1);
|
||||
expect(window.ElectronFiddle.app.setEditorValues).toHaveBeenCalledTimes(1);
|
||||
expect(window.ElectronFiddle.app.setEditorValues).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -341,7 +369,7 @@ describe('AppState', () => {
|
||||
// refreshed - we didn't actually add the local version
|
||||
// above, since versions.ts is mocked
|
||||
expect(Object.keys(appState.versions)).toEqual(
|
||||
[ '2.0.2', '2.0.1', '1.8.7' ]
|
||||
['2.0.2', '2.0.1', '1.8.7']
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -426,7 +454,7 @@ describe('AppState', () => {
|
||||
it('updates the visible editors and creates a backup', () => {
|
||||
appState.mosaicArrangement = createMosaicArrangement(ALL_MOSAICS);
|
||||
appState.closedPanels = {};
|
||||
appState.setVisibleMosaics([ EditorId.main ]);
|
||||
appState.setVisibleMosaics([EditorId.main]);
|
||||
|
||||
expect(appState.mosaicArrangement).toEqual(EditorId.main);
|
||||
expect(appState.closedPanels[EditorId.renderer]).toBeTruthy();
|
||||
|
||||
@@ -13,3 +13,7 @@ export function resetPlatform() {
|
||||
writable: true
|
||||
});
|
||||
}
|
||||
|
||||
export function flushPromises() {
|
||||
return new Promise((resolve) => setImmediate(resolve));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { isDevMode } from '../../src/utils/devmode';
|
||||
|
||||
describe('devMode', () => {
|
||||
const old = process.defaultApp;
|
||||
const old = (process as any).defaultApp; // for tsconfig error
|
||||
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process, 'defaultApp', { value: old });
|
||||
|
||||
Reference in New Issue
Block a user