Compare commits
52
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64ece1b364 | ||
|
|
cae7222636 | ||
|
|
2aabe80f2b | ||
|
|
d0683f1392 | ||
|
|
389352cefa | ||
|
|
d04b654c3e | ||
|
|
1369c9b35c | ||
|
|
9957090d25 | ||
|
|
8a002fb0a8 | ||
|
|
cae8dde4c8 | ||
|
|
cb176534f9 | ||
|
|
f7fa9a8ffc | ||
|
|
347384a547 | ||
|
|
e272efb3b9 | ||
|
|
5255bcb842 | ||
|
|
d3cd53f3e0 | ||
|
|
6d28c4c81b | ||
|
|
a45354e1af | ||
|
|
28245d36ba | ||
|
|
582ef280b5 | ||
|
|
56b7f2ffb7 | ||
|
|
cb3ba4f0a7 | ||
|
|
e238b10f1e | ||
|
|
bbc22d9ca8 | ||
|
|
7b3c960c1f | ||
|
|
4dbc74fccb | ||
|
|
f71b1bc8d4 | ||
|
|
00c2e1f9a6 | ||
|
|
50f69613dd | ||
|
|
4a23b53b1b | ||
|
|
dcd21935a9 | ||
|
|
ff37d5cfb9 | ||
|
|
bb8053d86d | ||
|
|
5efaa54255 | ||
|
|
b38507230d | ||
|
|
7451083c70 | ||
|
|
be46780674 | ||
|
|
99b5d067df | ||
|
|
f34eb225ea | ||
|
|
18cc36a837 | ||
|
|
da403e3cbd | ||
|
|
b4431cf426 | ||
|
|
e714269b9b | ||
|
|
b9518a63f8 | ||
|
|
0627801bb0 | ||
|
|
3adeece64d | ||
|
|
aed45078c5 | ||
|
|
55bfb0241b | ||
|
|
7e90679706 | ||
|
|
4897bdbf84 | ||
|
|
7a02f8cacd | ||
|
|
a65d594fbc |
@@ -0,0 +1,57 @@
|
||||
# Contributing to Electron Fiddle
|
||||
|
||||
Electron Fiddle is a community-driven project, overseen by the [Electron Ecosystem Working
|
||||
Group](https://github.com/electron/governance/tree/master/wg-ecosystem#readme). As such, we welcome
|
||||
and encourage all sorts of contributions. They include, but are not limited to:
|
||||
|
||||
- Constructive feedback
|
||||
- Bug reports / technical issues
|
||||
- Documentation changes
|
||||
- Feature requests
|
||||
- [Pull requests](#filing-pull-requests)
|
||||
|
||||
We strongly suggest that before filing an issue, you search through the existing issues to see
|
||||
if it has already been filed by someone else.
|
||||
|
||||
This project is a part of the Electron ecosystem. As such, all contributions to this project follow
|
||||
[Electron's code of conduct](https://github.com/electron/electron/blob/master/CODE_OF_CONDUCT.md)
|
||||
where appropriate.
|
||||
|
||||
## Contribution Suggestions
|
||||
|
||||
We use the label [`good first issue`](https://github.com/electron/fiddle/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) in the issue tracker to denote fairly-well-scoped-out bugs or feature requests that the community can pick up and work on. If any of those labeled issues do not have enough information, please feel free to ask constructive questions. (This applies to any open issue.)
|
||||
|
||||
## Filing Pull Requests
|
||||
|
||||
Here are some things to keep in mind as you file pull requests to fix bugs, add new features, etc.:
|
||||
|
||||
* If you're unfamiliar with forking, branching, and pull requests, please see [GitHub's extensive
|
||||
documentation](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests).
|
||||
* Travis CI and AppVeyor are used to make sure that any new or changed code meets the project's
|
||||
style guidelines, and that the project's testsuite passes for each new commit.
|
||||
* Unless it's impractical, please write tests for your changes. This will help us so that we can
|
||||
spot regressions much easier.
|
||||
* When creating commit messages and pull request titles, please adhere to the [conventional
|
||||
commits](https://www.conventionalcommits.org/en/v1.0.0/) standard.
|
||||
* Please **do not** bump the version number in your pull requests, the maintainers will do that.
|
||||
Feel free to indicate whether the changes require a major, minor, or patch version bump, as
|
||||
prescribed by the [semantic versioning specification](http://semver.org/).
|
||||
|
||||
### Running Fiddle From Source
|
||||
|
||||
```sh
|
||||
git clone https://github.com/electron/fiddle
|
||||
cd fiddle
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
```sh
|
||||
npm test
|
||||
```
|
||||
|
||||
## Release Process
|
||||
|
||||
<!-- TODO @felix add your release process here 😁 -->
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2013-2018 GitHub Inc.
|
||||
Copyright (c) 2013-2020 GitHub Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"transform": {
|
||||
".(js|jsx|ts|tsx)": "ts-jest"
|
||||
"\\.(js|jsx|ts|tsx)$": "ts-jest"
|
||||
},
|
||||
"testURL": "http://localhost",
|
||||
"testRegex": "(-spec)\\.(ts|tsx)$",
|
||||
|
||||
Generated
+2545
-969
File diff suppressed because it is too large
Load Diff
+35
-36
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "electron-fiddle",
|
||||
"productName": "Electron Fiddle",
|
||||
"version": "0.11.1",
|
||||
"version": "0.12.0",
|
||||
"description": "The easiest way to get started with Electron",
|
||||
"repository": "https://github.com/electron/fiddle",
|
||||
"main": "./dist/src/main/main",
|
||||
"scripts": {
|
||||
"contributors": "node ./tools/contributors.js",
|
||||
"less": "node ./tools/lessc.js",
|
||||
"lint:style": "stylelint ./src/less/*.less --fix",
|
||||
"lint:style": "stylelint \"./src/less/*.less\" --fix",
|
||||
"lint:ts": "tslint -c tslint.json -p tsconfig.json -e \"node_modules/**/*.ts\" --fix",
|
||||
"lint:tests": "tslint ./tests/**/*.ts{,x} -c tslint.json --fix",
|
||||
"lint:templates": "standard ./static/show-me/**/*.js",
|
||||
"lint": "npm-run-all lint:*",
|
||||
"lint:tests": "tslint \"./tests/**/*.ts{,x}\" -c tslint.json --fix",
|
||||
"lint:templates": "standard \"./static/show-me/**/*.js\"",
|
||||
"lint": "npm-run-all \"lint:*\"",
|
||||
"make": "electron-forge make",
|
||||
"package": "electron-forge package",
|
||||
"parcel:build": "node ./tools/parcel-build.js",
|
||||
@@ -36,10 +36,9 @@
|
||||
"forge": "./forge.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blueprintjs/core": "^3.19.1",
|
||||
"@blueprintjs/select": "^3.11.1",
|
||||
"@octokit/rest": "^16.34.0",
|
||||
"builtin-modules": "^3.1.0",
|
||||
"@blueprintjs/core": "^3.22.3",
|
||||
"@blueprintjs/select": "^3.11.2",
|
||||
"@octokit/rest": "^16.36.0",
|
||||
"classnames": "^2.2.6",
|
||||
"electron-default-menu": "^1.0.1",
|
||||
"electron-devtools-installer": "^2.2.4",
|
||||
@@ -48,61 +47,61 @@
|
||||
"extract-zip": "^1.6.7",
|
||||
"fix-path": "^2.1.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"mobx": "^5.14.2",
|
||||
"mobx-react": "^6.1.4",
|
||||
"monaco-editor": "^0.18.1",
|
||||
"mobx": "^5.15.2",
|
||||
"mobx-react": "^6.1.5",
|
||||
"monaco-editor": "^0.19.2",
|
||||
"monaco-loader": "1.0.0",
|
||||
"namor": "^1.1.3",
|
||||
"react": "^16.11.0",
|
||||
"react-dom": "^16.11.0",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-mosaic-component": "^3.2.0",
|
||||
"semver": "^6.3.0",
|
||||
"semver": "^7.1.1",
|
||||
"tmp": "0.1.0",
|
||||
"tslib": "^1.10.0",
|
||||
"update-electron-app": "^1.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.6.4",
|
||||
"@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",
|
||||
"@babel/core": "^7.8.0",
|
||||
"@electron-forge/cli": "^6.0.0-beta.47",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.47",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.47",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.47",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.47",
|
||||
"@electron-forge/publisher-github": "^6.0.0-beta.47",
|
||||
"@sentry/electron": "^1.2.0",
|
||||
"@types/classnames": "^2.2.9",
|
||||
"@types/enzyme": "^3.10.3",
|
||||
"@types/enzyme": "^3.10.4",
|
||||
"@types/fs-extra": "^8.0.1",
|
||||
"@types/jest": "^24.0.19",
|
||||
"@types/jest": "^24.0.25",
|
||||
"@types/log-symbols": "^3.0.0",
|
||||
"@types/node": "^12.11.7",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-dom": "^16.9.3",
|
||||
"@types/semver": "^6.0.2",
|
||||
"@types/tmp": "0.1.0",
|
||||
"chokidar": "^3.2.2",
|
||||
"coveralls": "^3.0.7",
|
||||
"electron": "7.1.3",
|
||||
"enzyme": "^3.10.0",
|
||||
"enzyme-adapter-react-16": "^1.15.1",
|
||||
"chokidar": "^3.3.1",
|
||||
"coveralls": "^3.0.9",
|
||||
"electron": "7.1.8",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-react-16": "^1.15.2",
|
||||
"enzyme-to-json": "^3.4.3",
|
||||
"jest": "^24.9.0",
|
||||
"jest-fetch-mock": "^2.1.2",
|
||||
"less": "^3.10.3",
|
||||
"log-symbols": "^3.0.0",
|
||||
"node-abi": "^2.12.0",
|
||||
"node-abi": "^2.13.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"parcel-bundler": "^1.12.4",
|
||||
"react-test-renderer": "^16.11.0",
|
||||
"react-test-renderer": "^16.12.0",
|
||||
"rimraf": "^3.0.0",
|
||||
"standard": "^14.3.1",
|
||||
"stylelint": "^11.1.1",
|
||||
"stylelint": "^13.0.0",
|
||||
"stylelint-config-standard": "^19.0.0",
|
||||
"ts-jest": "^24.1.0",
|
||||
"tslint": "^5.20.0",
|
||||
"ts-jest": "^24.3.0",
|
||||
"tslint": "^5.20.1",
|
||||
"tslint-microsoft-contrib": "^6.2.0",
|
||||
"tslint-react": "^4.1.0",
|
||||
"typescript": "^3.6.4"
|
||||
"typescript": "^3.7.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,9 @@ const {app, BrowserWindow} = require('electron')
|
||||
const path = require('path')
|
||||
const url = require('url')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({width: 800, height: 600})
|
||||
const mainWindow = new BrowserWindow({width: 800, height: 600})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadURL(url.format({
|
||||
@@ -20,14 +16,6 @@ function createWindow () {
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
@@ -47,7 +35,7 @@ app.on('window-all-closed', function () {
|
||||
app.on('activate', function () {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
+2
-14
@@ -1,13 +1,9 @@
|
||||
export const main = `// Modules to control application life and create native browser window
|
||||
const {app, BrowserWindow} = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
@@ -20,14 +16,6 @@ function createWindow () {
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
@@ -47,7 +35,7 @@ app.on('window-all-closed', function () {
|
||||
app.on('activate', function () {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
+11
-12
@@ -4,7 +4,7 @@ export type Files = Map<string, string>;
|
||||
|
||||
export type FileTransform = (files: Files) => Promise<Files>;
|
||||
|
||||
export enum VersionState {
|
||||
export enum ElectronVersionState {
|
||||
ready = 'ready',
|
||||
downloading = 'downloading',
|
||||
unknown = 'unknown'
|
||||
@@ -14,20 +14,12 @@ export enum ElectronVersionSource {
|
||||
remote = 'remote',
|
||||
local = 'local'
|
||||
}
|
||||
|
||||
export interface Version {
|
||||
export interface NpmVersion {
|
||||
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;
|
||||
@@ -37,7 +29,7 @@ export interface EditorValues {
|
||||
}
|
||||
|
||||
export interface ElectronVersion extends NpmVersion {
|
||||
state: VersionState;
|
||||
state: ElectronVersionState;
|
||||
source: ElectronVersionSource;
|
||||
}
|
||||
|
||||
@@ -58,7 +50,8 @@ export interface OutputOptions {
|
||||
isNotPre?: boolean;
|
||||
}
|
||||
|
||||
export interface WarningDialogTexts {
|
||||
export interface GenericDialogOptions {
|
||||
type: GenericDialogType;
|
||||
ok?: string;
|
||||
cancel?: string;
|
||||
label: string;
|
||||
@@ -68,6 +61,12 @@ export interface Templates {
|
||||
[index: string]: string | Templates;
|
||||
}
|
||||
|
||||
export const enum GenericDialogType {
|
||||
'confirm' = 'confirm',
|
||||
'warning' = 'warning',
|
||||
'success' = 'success',
|
||||
}
|
||||
|
||||
// Editors
|
||||
export const enum EditorId {
|
||||
'main' = 'main',
|
||||
|
||||
@@ -24,6 +24,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
// update if list of Editor ID changes
|
||||
@editor-ids: main, renderer, html, preload;
|
||||
|
||||
// takes each editor ID and increase its z-index if the parent Mosiac root
|
||||
// has focused__id class for that specific id.
|
||||
each(@editor-ids, {
|
||||
.focused__@{value}.mosaic .mosaic-window.@{value} {
|
||||
z-index: 2;
|
||||
}
|
||||
});
|
||||
|
||||
.mosaic-window {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
.bp3-fill {
|
||||
.version-chooser {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -17,3 +17,4 @@
|
||||
@import "components/editors.less";
|
||||
@import "components/tour.less";
|
||||
@import "components/show-me.less";
|
||||
@import "components/version-select.less";
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import * as Sentry from '@sentry/electron';
|
||||
Sentry.init({dsn: 'https://966a5b01ac8d4941b81e4ebd0ab4c991@sentry.io/1882540'});
|
||||
|
||||
import { app } from 'electron';
|
||||
|
||||
import { isDevMode } from '../utils/devmode';
|
||||
|
||||
+21
-10
@@ -1,3 +1,6 @@
|
||||
import * as Sentry from '@sentry/electron';
|
||||
Sentry.init({dsn: 'https://966a5b01ac8d4941b81e4ebd0ab4c991@sentry.io/1882540'});
|
||||
|
||||
import { when } from 'mobx';
|
||||
import * as MonacoType from 'monaco-editor';
|
||||
|
||||
@@ -6,6 +9,7 @@ import {
|
||||
ALL_EDITORS,
|
||||
EditorId,
|
||||
EditorValues,
|
||||
GenericDialogType,
|
||||
SetFiddleOptions
|
||||
} from '../interfaces';
|
||||
import { WEBCONTENTS_READY_FOR_IPC_SIGNAL } from '../ipc-events';
|
||||
@@ -51,14 +55,15 @@ export class App {
|
||||
) {
|
||||
// if unsaved, prompt user to make sure they're okay with overwriting and changing directory
|
||||
if (this.state.isUnsaved) {
|
||||
this.state.setWarningDialogTexts({
|
||||
this.state.setGenericDialogOptions({
|
||||
type: GenericDialogType.warning,
|
||||
label: `Opening this Fiddle will replace your unsaved changes. Do you want to proceed?`,
|
||||
ok: 'Yes'
|
||||
});
|
||||
this.state.isWarningDialogShowing = true;
|
||||
await when(() => !this.state.isWarningDialogShowing);
|
||||
this.state.isGenericDialogShowing = true;
|
||||
await when(() => !this.state.isGenericDialogShowing);
|
||||
|
||||
if (!this.state.warningDialogLastResult) {
|
||||
if (!this.state.genericDialogLastResult) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -79,10 +84,9 @@ export class App {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the values on all three editors.
|
||||
* Sets the contents of all editor panes.
|
||||
*
|
||||
* @param {EditorValues} values
|
||||
* @param {warn} warn - Should we warn before overwriting unsaved data?
|
||||
*/
|
||||
public async setEditorValues(values: Partial<EditorValues>): Promise<void> {
|
||||
const { ElectronFiddle: fiddle } = window;
|
||||
@@ -96,9 +100,16 @@ export class App {
|
||||
const backup = this.state.closedPanels[name];
|
||||
|
||||
if (typeof values[name] !== 'undefined') {
|
||||
if (isEditorBackup(backup) && backup.model) {
|
||||
// The editor does not exist, attempt to set it on the backup
|
||||
backup.model.setValue(values[name]!);
|
||||
if (isEditorBackup(backup)) {
|
||||
// The editor does not exist, attempt to set it on the backup.
|
||||
// If there's a model, we'll do it on the model. Else, we'll
|
||||
// set the value.
|
||||
|
||||
if (backup.model) {
|
||||
backup.model.setValue(values[name]!);
|
||||
} else {
|
||||
backup.value = values[name]!;
|
||||
}
|
||||
} else if (editor && editor.setValue) {
|
||||
// The editor exists, set the value directly
|
||||
editor.setValue(values[name]!);
|
||||
@@ -108,7 +119,7 @@ export class App {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the values on all three editors.
|
||||
* Retrieves the contents of all editor panes.
|
||||
*
|
||||
* @returns {EditorValues}
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Button } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { GenericDialogType } from '../../../src/interfaces';
|
||||
import { AppState } from '../state';
|
||||
|
||||
interface BisectHandlerProps {
|
||||
@@ -20,10 +21,21 @@ export class BisectHandler extends React.Component<BisectHandlerProps> {
|
||||
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}.`);
|
||||
const [minVer, maxVer] = [minRev.version, maxRev.version];
|
||||
const message = `Check between versions ${minVer} and ${maxVer}.`;
|
||||
|
||||
appState.pushOutput(`[BISECT] Complete: ${message}`);
|
||||
appState.setGenericDialogOptions({
|
||||
type: GenericDialogType.success,
|
||||
label: `Bisect complete. ${message}`,
|
||||
cancel: undefined
|
||||
});
|
||||
appState.isGenericDialogShowing = true;
|
||||
} else {
|
||||
appState.setVersion(response.version);
|
||||
}
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
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, VersionState } from '../../interfaces';
|
||||
import { highlightText } from '../../utils/highlight-text';
|
||||
import { sortedElectronMap } from '../../utils/sorted-map';
|
||||
import { AppState } from '../state';
|
||||
import { getReleaseChannel } from '../versions';
|
||||
|
||||
const ElectronVersionSelect = Select.ofType<ElectronVersion>();
|
||||
|
||||
/**
|
||||
* Helper method: Returns the <Select /> label for an Electron
|
||||
* version.
|
||||
*
|
||||
* @param {ElectronVersion} { source, state }
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getItemLabel({ source, state, name }: ElectronVersion): string {
|
||||
let label = '';
|
||||
|
||||
if (source === ElectronVersionSource.local) {
|
||||
label = name || 'Local';
|
||||
} else {
|
||||
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 }: ElectronVersion) {
|
||||
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<ElectronVersion> = (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<ElectronVersion> = (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 { 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.
|
||||
*
|
||||
* @class VersionChooser
|
||||
* @extends {React.Component<VersionChooserProps, VersionChooserState>}
|
||||
*/
|
||||
@observer
|
||||
export class ElectronVersionChooser 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.setVersion(version);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { currentElectronVersion, Bisector } = this.props.appState;
|
||||
const { version } = currentElectronVersion;
|
||||
|
||||
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={`Electron v${version}`}
|
||||
icon={getItemIcon(currentElectronVersion)}
|
||||
disabled={!!Bisector}
|
||||
/>
|
||||
</ElectronVersionSelect>
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import * as React from 'react';
|
||||
|
||||
import { when } from 'mobx';
|
||||
import { IpcEvents } from '../../ipc-events';
|
||||
import { INDEX_HTML_NAME, MAIN_JS_NAME, RENDERER_JS_NAME } from '../../shared-constants';
|
||||
import { INDEX_HTML_NAME, MAIN_JS_NAME, PRELOAD_JS_NAME, RENDERER_JS_NAME } from '../../shared-constants';
|
||||
import { getOctokit } from '../../utils/octokit';
|
||||
import { ipcRendererManager } from '../ipc';
|
||||
import { AppState } from '../state';
|
||||
@@ -87,6 +87,9 @@ export class PublishButton extends React.Component<PublishButtonProps> {
|
||||
[RENDERER_JS_NAME]: {
|
||||
content: values.renderer || '// Empty',
|
||||
},
|
||||
[PRELOAD_JS_NAME]: {
|
||||
content: values.preload || '// Empty',
|
||||
},
|
||||
},
|
||||
} as any); // Note: GitHub messed up, GistsCreateParamsFiles is an incorrect interface
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Button, IButtonProps, Spinner } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { VersionState } from '../../interfaces';
|
||||
import { ElectronVersionState } 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 === VersionState.downloading) {
|
||||
if (state === ElectronVersionState.downloading) {
|
||||
props.text = 'Downloading';
|
||||
props.disabled = true;
|
||||
props.icon = <Spinner size={16} />;
|
||||
} else if (state === VersionState.ready) {
|
||||
} else if (state === ElectronVersionState.ready) {
|
||||
if (isRunning) {
|
||||
props.active = true;
|
||||
props.text = 'Stop';
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { ButtonGroup } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { AppState } from '../state';
|
||||
import { VersionSelect } from './version-select';
|
||||
|
||||
export interface VersionChooserProps {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
/**
|
||||
* A dropdown allowing the selection of Electron versions. The actual
|
||||
* download is managed in the state.
|
||||
*/
|
||||
export const VersionChooser = observer((props: VersionChooserProps) => {
|
||||
const { currentElectronVersion, Bisector, setVersion } = props.appState;
|
||||
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<VersionSelect
|
||||
appState={props.appState}
|
||||
onVersionSelect={({ version }) => setVersion(version)}
|
||||
currentVersion={currentElectronVersion}
|
||||
disabled={!!Bisector}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
);
|
||||
});
|
||||
@@ -6,10 +6,9 @@ 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;
|
||||
@@ -36,7 +35,7 @@ export class Commands extends React.Component<CommandsProps, {}> {
|
||||
<div className='commands'>
|
||||
<div>
|
||||
<ControlGroup fill={true} vertical={false}>
|
||||
{this.getVersionChooser()}
|
||||
<VersionChooser appState={appState} />
|
||||
<Runner appState={appState} />
|
||||
</ControlGroup>
|
||||
{
|
||||
@@ -55,12 +54,6 @@ 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>
|
||||
@@ -71,14 +64,4 @@ 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} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as path from 'path';
|
||||
import * as React from 'react';
|
||||
import { getTheme, THEMES_PATH } from '../themes';
|
||||
|
||||
import { GenericDialogType } from '../../../src/interfaces';
|
||||
import { AppState } from '../state';
|
||||
import { defaultDark, LoadedFiddleTheme } from '../themes-defaults';
|
||||
|
||||
@@ -68,8 +69,11 @@ export class AddThemeDialog extends React.Component<AddThemeDialogProps, AddThem
|
||||
const name = editor.name ? editor.name : file.name;
|
||||
await this.createNewThemeFromMonaco(name, newTheme);
|
||||
} catch (error) {
|
||||
this.props.appState.setWarningDialogTexts({label: `Error: ${error}, please pick a different file.`});
|
||||
this.props.appState.isWarningDialogShowing = true;
|
||||
this.props.appState.setGenericDialogOptions({
|
||||
type: GenericDialogType.warning,
|
||||
label: `Error: ${error}, please pick a different file.`
|
||||
});
|
||||
this.props.appState.isGenericDialogShowing = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
import { Button, Dialog, Label, MenuItem } from '@blueprintjs/core';
|
||||
import { Select } from '@blueprintjs/select';
|
||||
import { Button, ButtonGroup, Callout, Dialog, Label } from '@blueprintjs/core';
|
||||
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>();
|
||||
import { VersionSelect } from './version-select';
|
||||
|
||||
export interface BisectDialogProps {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
export interface BisectDialogState {
|
||||
startIndex?: number;
|
||||
endIndex?: number;
|
||||
startIndex: number;
|
||||
endIndex: number;
|
||||
allVersions: Array<ElectronVersion>;
|
||||
showHelp?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "add version" dialog allows users to add custom builds of Electron.
|
||||
*
|
||||
* @class AddVersionDialog
|
||||
* @class BisectDialog
|
||||
* @extends {React.Component<BisectDialogProps, BisectDialogState>}
|
||||
*/
|
||||
@observer
|
||||
@@ -35,9 +33,15 @@ export class BisectDialog extends React.Component<BisectDialogProps, BisectDialo
|
||||
this.onClose = this.onClose.bind(this);
|
||||
this.onBeginSelect = this.onBeginSelect.bind(this);
|
||||
this.onEndSelect = this.onEndSelect.bind(this);
|
||||
this.showHelp = this.showHelp.bind(this);
|
||||
this.isEarliestItemDisabled = this.isEarliestItemDisabled.bind(this);
|
||||
this.isLatestItemDisabled = this.isLatestItemDisabled.bind(this);
|
||||
|
||||
const allVersions = getVersionsFromAppState(this.props.appState);
|
||||
this.state = { allVersions };
|
||||
this.state = {
|
||||
allVersions: this.props.appState.versionsToShow,
|
||||
startIndex: 10,
|
||||
endIndex: 0
|
||||
};
|
||||
}
|
||||
|
||||
public onBeginSelect(version: ElectronVersion) {
|
||||
@@ -62,8 +66,7 @@ export class BisectDialog extends React.Component<BisectDialogProps, BisectDialo
|
||||
}
|
||||
|
||||
const bisectRange = allVersions
|
||||
.slice(endIndex, startIndex + 1)
|
||||
.reverse();
|
||||
.slice(endIndex, startIndex + 1);
|
||||
|
||||
appState.Bisector = new Bisector(bisectRange);
|
||||
const initialBisectPivot = appState.Bisector.getCurrentVersion().version;
|
||||
@@ -78,18 +81,30 @@ export class BisectDialog extends React.Component<BisectDialogProps, BisectDialo
|
||||
this.props.appState.isBisectDialogShowing = false;
|
||||
}
|
||||
|
||||
get buttons() {
|
||||
const canSubmit =
|
||||
!!this.state.startIndex &&
|
||||
!!this.state.endIndex &&
|
||||
this.state.startIndex > this.state.endIndex;
|
||||
/**
|
||||
* Shows the additional help
|
||||
*/
|
||||
public showHelp() {
|
||||
this.setState({ showHelp: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we get this show on the road?
|
||||
*/
|
||||
get canSubmit(): boolean {
|
||||
return this.state.startIndex > this.state.endIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the buttons
|
||||
*/
|
||||
get buttons() {
|
||||
return [
|
||||
(
|
||||
<Button
|
||||
icon='play'
|
||||
key='submit'
|
||||
disabled={!canSubmit}
|
||||
disabled={!this.canSubmit}
|
||||
onClick={this.onSubmit}
|
||||
text='Begin'
|
||||
/>
|
||||
@@ -104,6 +119,47 @@ export class BisectDialog extends React.Component<BisectDialogProps, BisectDialo
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the help
|
||||
*/
|
||||
get help() {
|
||||
let moreHelp = (
|
||||
<Button
|
||||
icon='help'
|
||||
text='Show help'
|
||||
onClick={this.showHelp}
|
||||
/>
|
||||
);
|
||||
|
||||
if (this.state.showHelp) {
|
||||
moreHelp = (
|
||||
<>
|
||||
<p>
|
||||
First, write a fiddle that reproduces a bug or an issue. Then, select the earliest version to
|
||||
start your search with. Typically, that's the "last known good" version that did not have the bug.
|
||||
Then, select that latest version to end the search with, usually the "first known bad" version.
|
||||
</p>
|
||||
<p>
|
||||
Once you begin your bisect, Fiddle will run your fiddle with a number of Electron versions, closing
|
||||
in on the version that introduced the bug. Once completed, you will know which Electron version
|
||||
introduced your issue.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Callout style={{ marginTop: 0, marginBottom: '1rem' }}>
|
||||
<p>
|
||||
A "bisect" is a popular method <a href='https://git-scm.com/docs/git-bisect' target='_blank'>
|
||||
borrowed from <code>git</code></a> for learning which version of Electron introduced a bug. This
|
||||
tool helps you perform a bisect.
|
||||
</p>
|
||||
{moreHelp}
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { isBisectDialogShowing } = this.props.appState;
|
||||
const { startIndex, endIndex, allVersions } = this.state;
|
||||
@@ -116,41 +172,28 @@ export class BisectDialog extends React.Component<BisectDialogProps, BisectDialo
|
||||
className='dialog-add-version'
|
||||
>
|
||||
<div className='bp3-dialog-body'>
|
||||
{this.help}
|
||||
<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}
|
||||
Earliest Version (Last "known good" version)
|
||||
<ButtonGroup fill={true}>
|
||||
<VersionSelect
|
||||
currentVersion={allVersions[startIndex]}
|
||||
appState={this.props.appState}
|
||||
onVersionSelect={this.onBeginSelect}
|
||||
itemDisabled={this.isEarliestItemDisabled}
|
||||
/>
|
||||
</ElectronVersionSelect>
|
||||
</ButtonGroup>
|
||||
</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}
|
||||
Latest Version (First "known bad" version)
|
||||
<ButtonGroup fill={true}>
|
||||
<VersionSelect
|
||||
currentVersion={allVersions[endIndex]}
|
||||
appState={this.props.appState}
|
||||
onVersionSelect={this.onEndSelect}
|
||||
itemDisabled={this.isLatestItemDisabled}
|
||||
/>
|
||||
</ElectronVersionSelect>
|
||||
</ButtonGroup>
|
||||
</Label>
|
||||
</div>
|
||||
<div className='bp3-dialog-footer'>
|
||||
@@ -161,4 +204,33 @@ export class BisectDialog extends React.Component<BisectDialogProps, BisectDialo
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should an item in the "earliest version" dropdown be disabled?
|
||||
*
|
||||
* @param {ElectronVersion} version
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isEarliestItemDisabled(version: ElectronVersion): boolean {
|
||||
const { allVersions, endIndex } = this.state;
|
||||
|
||||
// In the array, "newer" versions will have a lower index.
|
||||
// 0: 5.0.0
|
||||
// 1: 4.0.0
|
||||
// 2: 3.0.0
|
||||
// ...
|
||||
return allVersions.indexOf(version) < endIndex + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should an item in the "latest version" dropdown be disabled?
|
||||
*
|
||||
* @param {ElectronVersion} version
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isLatestItemDisabled(version: ElectronVersion): boolean {
|
||||
const { allVersions, startIndex } = this.state;
|
||||
|
||||
return allVersions.indexOf(version) > startIndex - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
import { Alert, Intent } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { AppState } from '../state';
|
||||
|
||||
export interface ConfirmDialogProps {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
export interface ConfirmDialogState {
|
||||
}
|
||||
|
||||
/**
|
||||
* The token dialog prompts the user to either continue or cancel the operation.
|
||||
*
|
||||
* @export
|
||||
* @class ConfirmDialog
|
||||
* @extends {React.Component<ConfirmDialogProps, ConfirmDialogState>}
|
||||
*/
|
||||
@observer
|
||||
export class ConfirmDialog extends React.Component<ConfirmDialogProps, ConfirmDialogState> {
|
||||
constructor(props: ConfirmDialogProps) {
|
||||
super(props);
|
||||
|
||||
this.onClose = this.onClose.bind(this);
|
||||
}
|
||||
|
||||
public onClose(result: boolean) {
|
||||
this.props.appState.confirmationPromptLastResult = result;
|
||||
this.props.appState.toggleConfirmationPromptDialog();
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { isConfirmationPromptShowing, confirmationDialogTexts } = this.props.appState;
|
||||
|
||||
return (
|
||||
<Alert
|
||||
isOpen={isConfirmationPromptShowing}
|
||||
onClose={this.onClose}
|
||||
icon='help'
|
||||
confirmButtonText={confirmationDialogTexts.ok}
|
||||
cancelButtonText={confirmationDialogTexts.cancel}
|
||||
intent={Intent.PRIMARY}
|
||||
>
|
||||
<p>{confirmationDialogTexts.label}</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
import { Alert, IconName, Intent} from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { GenericDialogType } from '../../../src/interfaces';
|
||||
import { AppState } from '../state';
|
||||
|
||||
export interface GenericDialogProps {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
export interface GenericDialogState {
|
||||
}
|
||||
|
||||
/**
|
||||
* The token dialog prompts the user to either continue or cancel the operation.
|
||||
*
|
||||
* @export
|
||||
* @class GenericDialog
|
||||
* @extends {React.Component<GenericDialogProps, GenericDialogState>}
|
||||
*/
|
||||
@observer
|
||||
export class GenericDialog extends React.Component<GenericDialogProps, GenericDialogState> {
|
||||
constructor(props: GenericDialogProps) {
|
||||
super(props);
|
||||
|
||||
this.onClose = this.onClose.bind(this);
|
||||
}
|
||||
|
||||
public onClose(result: boolean) {
|
||||
this.props.appState.genericDialogLastResult = result;
|
||||
this.props.appState.toggleGenericDialog();
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { isGenericDialogShowing, genericDialogOptions } = this.props.appState;
|
||||
const {type, ok, cancel, label} = genericDialogOptions;
|
||||
|
||||
let intent: Intent;
|
||||
let icon: IconName;
|
||||
switch (type) {
|
||||
case GenericDialogType.warning:
|
||||
intent = Intent.DANGER;
|
||||
icon = 'warning-sign';
|
||||
break;
|
||||
case GenericDialogType.confirm:
|
||||
intent = Intent.PRIMARY;
|
||||
icon = 'help';
|
||||
break;
|
||||
case GenericDialogType.success:
|
||||
intent = Intent.SUCCESS;
|
||||
icon = 'info-sign';
|
||||
break;
|
||||
default:
|
||||
intent = Intent.NONE;
|
||||
icon = 'help';
|
||||
break;
|
||||
}
|
||||
return (
|
||||
<Alert
|
||||
isOpen={isGenericDialogShowing}
|
||||
onClose={this.onClose}
|
||||
icon={icon}
|
||||
confirmButtonText={ok}
|
||||
cancelButtonText={cancel}
|
||||
intent={intent}
|
||||
>
|
||||
<p>{label}</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
|
||||
import { Alert, Intent } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { AppState } from '../state';
|
||||
|
||||
export interface WarningDialogProps {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
export interface WarningDialogState {
|
||||
}
|
||||
|
||||
/**
|
||||
* The token dialog prompts the user to either continue or cancel the operation.
|
||||
*
|
||||
* @export
|
||||
* @class WarningDialog
|
||||
* @extends {React.Component<WarningDialogProps, WarningDialogState>}
|
||||
*/
|
||||
@observer
|
||||
export class WarningDialog extends React.Component<WarningDialogProps, WarningDialogState> {
|
||||
constructor(props: WarningDialogProps) {
|
||||
super(props);
|
||||
|
||||
this.onClose = this.onClose.bind(this);
|
||||
}
|
||||
|
||||
public onClose(result: boolean) {
|
||||
this.props.appState.warningDialogLastResult = result;
|
||||
this.props.appState.toggleWarningDialog();
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { isWarningDialogShowing, warningDialogTexts } = this.props.appState;
|
||||
return (
|
||||
<Alert
|
||||
isOpen={isWarningDialogShowing}
|
||||
onClose={this.onClose}
|
||||
icon='warning-sign'
|
||||
confirmButtonText={warningDialogTexts.ok}
|
||||
cancelButtonText={warningDialogTexts.cancel}
|
||||
intent={Intent.DANGER}
|
||||
>
|
||||
<p>{warningDialogTexts.label}</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,8 @@ 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 { GenericDialog } from './dialog-generic';
|
||||
import { TokenDialog } from './dialog-token';
|
||||
import { WarningDialog } from './dialog-warning';
|
||||
import { Settings } from './settings';
|
||||
|
||||
export interface DialogsProps {
|
||||
@@ -29,7 +28,8 @@ export class Dialogs extends React.Component<DialogsProps, {}> {
|
||||
isSettingsShowing,
|
||||
isAddVersionDialogShowing,
|
||||
isThemeDialogShowing,
|
||||
isBisectDialogShowing
|
||||
isBisectDialogShowing,
|
||||
isGenericDialogShowing
|
||||
} = appState;
|
||||
const maybeToken = isTokenDialogShowing
|
||||
? <TokenDialog key='dialogs' appState={appState} />
|
||||
@@ -45,9 +45,9 @@ export class Dialogs extends React.Component<DialogsProps, {}> {
|
||||
const maybeBisect = isBisectDialogShowing
|
||||
? <BisectDialog key='bisect-dialog' appState={appState} />
|
||||
: null;
|
||||
const eitherWarningOrPrompt = appState.isWarningDialogShowing
|
||||
? <WarningDialog appState={appState} />
|
||||
: <ConfirmDialog appState={appState} />;
|
||||
const genericDialog = isGenericDialogShowing
|
||||
? <GenericDialog appState={appState} />
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div key='dialogs' className='dialogs'>
|
||||
@@ -56,7 +56,7 @@ export class Dialogs extends React.Component<DialogsProps, {}> {
|
||||
{maybeAddLocalVersion}
|
||||
{maybeMonaco}
|
||||
{maybeBisect}
|
||||
{eitherWarningOrPrompt}
|
||||
{genericDialog}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface EditorProps {
|
||||
options?: Partial<MonacoType.editor.IEditorConstructionOptions>;
|
||||
editorDidMount?: (editor: MonacoType.editor.IStandaloneCodeEditor) => void;
|
||||
onChange?: (value: string, event: MonacoType.editor.IModelContentChangedEvent) => void;
|
||||
setFocused: (id: EditorId) => void;
|
||||
}
|
||||
|
||||
export class Editor extends React.Component<EditorProps> {
|
||||
@@ -27,7 +28,6 @@ export class Editor extends React.Component<EditorProps> {
|
||||
|
||||
constructor(props: EditorProps) {
|
||||
super(props);
|
||||
|
||||
this.language = props.id === 'html' ? 'html' : 'javascript';
|
||||
}
|
||||
|
||||
@@ -80,6 +80,12 @@ export class Editor extends React.Component<EditorProps> {
|
||||
...monacoOptions
|
||||
});
|
||||
|
||||
// mark this editor as focused whenever it is
|
||||
this.editor.onDidFocusEditorText(() => {
|
||||
const { id, setFocused } = this.props;
|
||||
setFocused(id);
|
||||
});
|
||||
|
||||
await this.editorDidMount(this.editor);
|
||||
}
|
||||
}
|
||||
@@ -98,6 +104,23 @@ export class Editor extends React.Component<EditorProps> {
|
||||
return <div className='editorContainer' ref={this.containerRef} />;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a model and attach it to the editor
|
||||
*
|
||||
* @private
|
||||
* @param {string} value
|
||||
*/
|
||||
private async createModel(value: string) {
|
||||
const { monaco } = this.props;
|
||||
|
||||
const model = monaco.editor.createModel(value, this.language);
|
||||
model.updateOptions({
|
||||
tabSize: 2
|
||||
});
|
||||
|
||||
this.editor.setModel(model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content on the editor, including the model and the view state.
|
||||
*
|
||||
@@ -105,27 +128,29 @@ export class Editor extends React.Component<EditorProps> {
|
||||
* @memberof Editor
|
||||
*/
|
||||
private async setContent() {
|
||||
const { appState, id, monaco } = this.props;
|
||||
const { appState, id } = this.props;
|
||||
const { version } = appState;
|
||||
|
||||
const backup = appState.getAndRemoveEditorValueBackup(id);
|
||||
|
||||
if (backup && backup.model) {
|
||||
console.log(id);
|
||||
|
||||
if (backup) {
|
||||
console.log(`Editor: Backup found, restoring state`);
|
||||
|
||||
if (backup.viewState) {
|
||||
this.editor.restoreViewState(backup.viewState);
|
||||
}
|
||||
|
||||
this.editor.setModel(backup.model);
|
||||
// If there's a model, use the model. No model? Use the value
|
||||
if (backup.model) {
|
||||
this.editor.setModel(backup.model);
|
||||
} else if (typeof backup.value !== 'undefined') {
|
||||
this.createModel(backup.value);
|
||||
}
|
||||
} else {
|
||||
const value = await getContent(id, version);
|
||||
const model = monaco.editor.createModel(value, this.language);
|
||||
model.updateOptions({
|
||||
tabSize: 2
|
||||
});
|
||||
|
||||
this.editor.setModel(model);
|
||||
await this.createModel(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ export interface EditorsState {
|
||||
monaco?: typeof MonacoType;
|
||||
isMounted?: boolean;
|
||||
monacoOptions: MonacoType.editor.IEditorOptions;
|
||||
focused?: EditorId;
|
||||
}
|
||||
|
||||
@observer
|
||||
@@ -61,6 +62,7 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
|
||||
this.renderEditor = this.renderEditor.bind(this);
|
||||
this.renderTile = this.renderTile.bind(this);
|
||||
this.renderGenericPanel = this.renderGenericPanel.bind(this);
|
||||
this.setFocused = this.setFocused.bind(this);
|
||||
|
||||
this.state = { monacoOptions: defaultMonacoOptions };
|
||||
|
||||
@@ -237,6 +239,7 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
|
||||
monaco={monaco!}
|
||||
appState={appState}
|
||||
monacoOptions={defaultMonacoOptions}
|
||||
setFocused={this.setFocused}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -249,6 +252,7 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
|
||||
|
||||
return (
|
||||
<Mosaic<EditorId | PanelId>
|
||||
className={`focused__${this.state.focused}`}
|
||||
onChange={this.onChange}
|
||||
value={appState.mosaicArrangement}
|
||||
zeroStateView={renderNonIdealState(appState)}
|
||||
@@ -291,4 +295,14 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
|
||||
|
||||
activateTheme(monaco, undefined, this.props.appState.theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the currently-focused editor. This will impact the editor's
|
||||
* z-index, ensuring that its intellisense menus don't get clipped
|
||||
* by the other editor windows.
|
||||
* @param id Editor ID
|
||||
*/
|
||||
public setFocused(id: EditorId): void {
|
||||
this.setState({ focused: id });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ import {
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersion, ElectronVersionSource, VersionState } from '../../interfaces';
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState } from '../../interfaces';
|
||||
import { normalizeVersion } from '../../utils/normalize-version';
|
||||
import { sortedElectronMap } from '../../utils/sorted-map';
|
||||
import { sortedElectronMap } from '../../utils/sorted-electron-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 VersionState);
|
||||
appState.statesToShow.push(id as ElectronVersionState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,9 +85,9 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
const { appState } = this.props;
|
||||
|
||||
if (!checked) {
|
||||
appState.versionsToShow = appState.versionsToShow.filter((c) => c !== id);
|
||||
appState.channelsToShow = appState.channelsToShow.filter((c) => c !== id);
|
||||
} else {
|
||||
appState.versionsToShow.push(id as ElectronReleaseChannel);
|
||||
appState.channelsToShow.push(id as ElectronReleaseChannel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
*/
|
||||
private renderVersionStateOptions(): JSX.Element {
|
||||
const { appState } = this.props;
|
||||
const getIsChecked = (state: VersionState) => {
|
||||
const getIsChecked = (state: ElectronVersionState) => {
|
||||
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(VersionState.downloading)}
|
||||
checked={getIsChecked(ElectronVersionState.downloading)}
|
||||
label='Downloading'
|
||||
id='downloading'
|
||||
onChange={this.handleStateChange}
|
||||
inline={true}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={getIsChecked(VersionState.ready)}
|
||||
checked={getIsChecked(ElectronVersionState.ready)}
|
||||
label='Downloaded'
|
||||
id='ready'
|
||||
onChange={this.handleStateChange}
|
||||
inline={true}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={getIsChecked(VersionState.unknown)}
|
||||
checked={getIsChecked(ElectronVersionState.unknown)}
|
||||
label='Not Downloaded'
|
||||
id='unknown'
|
||||
onChange={this.handleStateChange}
|
||||
@@ -244,7 +244,7 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
private renderVersionChannelOptions(): JSX.Element {
|
||||
const { appState } = this.props;
|
||||
const getIsChecked = (channel: ElectronReleaseChannel) => {
|
||||
return appState.versionsToShow.includes(channel);
|
||||
return appState.channelsToShow.includes(channel);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -316,11 +316,11 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
* @returns {Array<JSX.Element>}
|
||||
*/
|
||||
private renderTableRows(): Array<JSX.Element | null> {
|
||||
const { versions, versionsToShow, statesToShow } = this.props.appState;
|
||||
const { versions, channelsToShow, statesToShow } = this.props.appState;
|
||||
|
||||
return sortedElectronMap<JSX.Element | null>(versions, (key, item) => {
|
||||
// Check if we want to show the version
|
||||
if (!versionsToShow.includes(getReleaseChannel(item))) {
|
||||
if (!channelsToShow.includes(getReleaseChannel(item))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -350,10 +350,10 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
let icon: IconName = 'box';
|
||||
let humanState = 'Downloaded';
|
||||
|
||||
if (state === VersionState.downloading) {
|
||||
if (state === ElectronVersionState.downloading) {
|
||||
icon = 'cloud-download';
|
||||
humanState = 'Downloading';
|
||||
} else if (state === VersionState.unknown) {
|
||||
} else if (state === ElectronVersionState.unknown) {
|
||||
icon = 'cloud';
|
||||
humanState = 'Not downloaded';
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Callout, Checkbox, FormGroup } from '@blueprintjs/core';
|
||||
import { Callout, Checkbox, FormGroup, InputGroup } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
@@ -21,6 +21,7 @@ export class ExecutionSettings extends React.Component<ExecutionSettingsProps, {
|
||||
|
||||
this.handleDeleteDataChange = this.handleDeleteDataChange.bind(this);
|
||||
this.handleElectronLoggingChange = this.handleElectronLoggingChange.bind(this);
|
||||
this.handleExecutionFlagChange = this.handleExecutionFlagChange.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,8 +49,25 @@ export class ExecutionSettings extends React.Component<ExecutionSettingsProps, {
|
||||
this.props.appState.isEnablingElectronLogging = checked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a change in the execution flags run with the Electron executable
|
||||
*
|
||||
* @param {React.ChangeEvent<HTMLInputElement>} event
|
||||
*/
|
||||
public handleExecutionFlagChange(
|
||||
event: React.FormEvent<HTMLInputElement>
|
||||
) {
|
||||
const { value } = event.currentTarget;
|
||||
const flags = value.split('|');
|
||||
this.props.appState.executionFlags = flags;
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { isKeepingUserDataDirs, isEnablingElectronLogging } = this.props.appState;
|
||||
const {
|
||||
isKeepingUserDataDirs,
|
||||
isEnablingElectronLogging,
|
||||
executionFlags = []
|
||||
} = this.props.appState;
|
||||
|
||||
const deleteUserDirLabel = `
|
||||
Whenever Electron runs, it creates a user data directory for cookies, the cache,
|
||||
@@ -87,6 +105,26 @@ export class ExecutionSettings extends React.Component<ExecutionSettingsProps, {
|
||||
/>
|
||||
</FormGroup>
|
||||
</Callout>
|
||||
<br />
|
||||
<Callout>
|
||||
<FormGroup>
|
||||
<p>
|
||||
Electron allows starting the executable with <a
|
||||
href='https://electronjs.org/docs/api/chrome-command-line-switches'
|
||||
>
|
||||
user-provided flags
|
||||
</a>
|
||||
, such as '--js-flags=--expose-gc'. Those can be added here as bar-separated (|)
|
||||
flags to run when you start your Fiddles.
|
||||
</p>
|
||||
<br />
|
||||
<InputGroup
|
||||
placeholder='--js-flags=--expose-gc|--lang=es'
|
||||
value={executionFlags.join('|')}
|
||||
onChange={this.handleExecutionFlagChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Callout>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ export function getWelcomeTour(): Set<TourScriptStep> {
|
||||
<>
|
||||
<p>
|
||||
Electron Fiddle allows you to build little experiments and mini-apps with
|
||||
Electron. Each Fiddle has three files: A main script, a renderer script,
|
||||
and an HTML file.
|
||||
Electron. Each Fiddle has at least three of these files: A main script, a
|
||||
renderer script, a preload script, and an HTML file.
|
||||
</p>
|
||||
<p>
|
||||
If you <code>require()</code> a module, Fiddle will install
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import { Button, IButtonGroupProps, 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 { highlightText } from '../../utils/highlight-text';
|
||||
import { AppState } from '../state';
|
||||
|
||||
const ElectronVersionSelect = Select.ofType<ElectronVersion>();
|
||||
|
||||
/**
|
||||
* Helper method: Returns the <Select /> label for an Electron
|
||||
* version.
|
||||
*
|
||||
* @param {ElectronVersion} { source, state }
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getItemLabel({ source, state, name }: ElectronVersion): string {
|
||||
let label = '';
|
||||
|
||||
if (source === ElectronVersionSource.local) {
|
||||
label = name || 'Local';
|
||||
} else {
|
||||
if (state === ElectronVersionState.unknown) {
|
||||
label = `Not downloaded`;
|
||||
} else if (state === ElectronVersionState.ready) {
|
||||
label = `Downloaded`;
|
||||
} else if (state === ElectronVersionState.downloading) {
|
||||
label = `Downloading`;
|
||||
}
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method: Returns the <Select /> icon for an Electron
|
||||
* version.
|
||||
*
|
||||
* @param {ElectronVersion} { state }
|
||||
* @returns
|
||||
*/
|
||||
export function getItemIcon({ state }: ElectronVersion) {
|
||||
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<ElectronVersion> = (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<ElectronVersion> = (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 VersionSelectState {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface VersionSelectProps {
|
||||
appState: AppState;
|
||||
disabled?: boolean;
|
||||
currentVersion: ElectronVersion;
|
||||
onVersionSelect: (version: ElectronVersion) => void;
|
||||
buttonGroupProps?: IButtonGroupProps;
|
||||
itemDisabled?: keyof ElectronVersion | ((item: ElectronVersion, index: number) => boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* A dropdown allowing the selection of Electron versions. The actual
|
||||
* download is managed in the state.
|
||||
*
|
||||
* @class VersionSelect
|
||||
* @extends {React.Component<VersionSelectProps, VersionSelectState>}
|
||||
*/
|
||||
@observer
|
||||
export class VersionSelect extends React.Component<VersionSelectProps, VersionSelectState> {
|
||||
public render() {
|
||||
const { currentVersion, itemDisabled } = this.props;
|
||||
const { version } = currentVersion;
|
||||
|
||||
return (
|
||||
<ElectronVersionSelect
|
||||
filterable={true}
|
||||
items={this.props.appState.versionsToShow}
|
||||
itemRenderer={renderItem}
|
||||
itemPredicate={filterItem}
|
||||
itemDisabled={itemDisabled}
|
||||
onItemSelect={this.props.onVersionSelect}
|
||||
noResults={<MenuItem disabled={true} text='No results.' />}
|
||||
disabled={!!this.props.disabled}
|
||||
>
|
||||
<Button
|
||||
className='version-chooser'
|
||||
text={`Electron v${version}`}
|
||||
icon={getItemIcon(currentVersion)}
|
||||
disabled={!!this.props.disabled}
|
||||
/>
|
||||
</ElectronVersionSelect>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import * as path from 'path';
|
||||
import { MosaicNode } from 'react-mosaic-component';
|
||||
|
||||
import { EditorId, MosaicId } from '../interfaces';
|
||||
import { EditorBackup } from '../utils/editor-backup';
|
||||
|
||||
// Reminder: When testing, this file is mocked in tests/setup.js
|
||||
|
||||
@@ -19,5 +20,10 @@ export const DEFAULT_MOSAIC_ARRANGEMENT: MosaicNode<MosaicId> = {
|
||||
}
|
||||
};
|
||||
|
||||
export const DEFAULT_CLOSED_PANELS: Partial<Record<MosaicId, EditorBackup | true>> = {
|
||||
docsDemo: true,
|
||||
preload: {}
|
||||
};
|
||||
|
||||
export const ELECTRON_ORG = 'electron';
|
||||
export const ELECTRON_REPO = 'electron';
|
||||
|
||||
+4
-2
@@ -1,6 +1,8 @@
|
||||
import { EditorValues } from '../interfaces';
|
||||
import { exec } from '../utils/exec';
|
||||
|
||||
const { builtinModules } = require('module');
|
||||
|
||||
export interface NpmOperationOptions {
|
||||
dir: string;
|
||||
}
|
||||
@@ -11,8 +13,8 @@ export let isInstalled: boolean | null = null;
|
||||
/* perhaps we can expose this to the settings module?*/
|
||||
const ignoredModules: Array<string> = [
|
||||
'electron',
|
||||
// tslint:disable-next-line:no-submodule-imports
|
||||
...require('builtin-modules/static')
|
||||
'original-fs',
|
||||
...builtinModules
|
||||
];
|
||||
|
||||
/* regular expression to both match and extract module names */
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as Octokit from '@octokit/rest';
|
||||
import { when } from 'mobx';
|
||||
import { EditorId, EditorValues } from '../interfaces';
|
||||
import { EditorId, EditorValues, GenericDialogType } 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-map';
|
||||
import { sortedElectronMap } from '../utils/sorted-electron-map';
|
||||
import { ELECTRON_ORG, ELECTRON_REPO } from './constants';
|
||||
import { getContent } from './content';
|
||||
import { AppState } from './state';
|
||||
@@ -25,17 +25,20 @@ export class RemoteLoader {
|
||||
|
||||
public async loadFiddleFromElectronExample(_: any, exampleInfo: { path: string; ref: string }) {
|
||||
console.log(`Loading fiddle from Electron example`, _, exampleInfo);
|
||||
const ok = await this.verifyRemoteLoad('example from the Electron docs', exampleInfo.ref);
|
||||
const {path, ref} = exampleInfo;
|
||||
const prettyName = path.replace('docs/fiddles/', '');
|
||||
const ok = await this.verifyRemoteLoad(`'${prettyName}' example from the Electron docs for version ${ref}`);
|
||||
if (!ok) return;
|
||||
|
||||
this.fetchExampleAndLoad(exampleInfo.ref, exampleInfo.path);
|
||||
this.fetchExampleAndLoad(ref, path);
|
||||
}
|
||||
|
||||
public async loadFiddleFromGist(_: any, gistInfo: { id: string }) {
|
||||
const ok = await this.verifyRemoteLoad('gist');
|
||||
const {id} = gistInfo;
|
||||
const ok = await this.verifyRemoteLoad(`gist`);
|
||||
if (!ok) return;
|
||||
|
||||
this.fetchGistAndLoad(gistInfo.id);
|
||||
this.fetchGistAndLoad(id);
|
||||
}
|
||||
|
||||
public async fetchExampleAndLoad(ref: string, path: string): Promise<boolean> {
|
||||
@@ -159,11 +162,11 @@ export class RemoteLoader {
|
||||
// check if version is part of release channel
|
||||
const versionReleaseChannel: ElectronReleaseChannel = getReleaseChannel(version);
|
||||
|
||||
if (!this.appState.versionsToShow.includes(versionReleaseChannel)) {
|
||||
if (!this.appState.channelsToShow.includes(versionReleaseChannel)) {
|
||||
const ok = await this.verifyReleaseChannelEnabled(versionReleaseChannel);
|
||||
if (!ok) return false;
|
||||
|
||||
this.appState.versionsToShow.push(versionReleaseChannel);
|
||||
this.appState.channelsToShow.push(versionReleaseChannel);
|
||||
}
|
||||
|
||||
this.appState.setVersion(version);
|
||||
@@ -197,26 +200,28 @@ export class RemoteLoader {
|
||||
*
|
||||
* @param what What are we loading from (gist, example, etc.)
|
||||
*/
|
||||
public async verifyRemoteLoad(what: string, fiddlePath?: string): Promise<boolean> {
|
||||
this.appState.setConfirmationPromptTexts({
|
||||
label: `Are you sure you sure you want to load this '${what}' from fiddle path '${fiddlePath}'? Only load and run it if you trust the source.`
|
||||
public async verifyRemoteLoad(what: string): Promise<boolean> {
|
||||
this.appState.setGenericDialogOptions({
|
||||
type: GenericDialogType.confirm,
|
||||
label: `Are you sure you want to load this ${what}? Only load and run it if you trust the source.`
|
||||
});
|
||||
this.appState.isConfirmationPromptShowing = true;
|
||||
await when(() => !this.appState.isConfirmationPromptShowing);
|
||||
this.appState.isGenericDialogShowing = true;
|
||||
await when(() => !this.appState.isGenericDialogShowing);
|
||||
|
||||
return !!this.appState.confirmationPromptLastResult;
|
||||
return !!this.appState.genericDialogLastResult;
|
||||
}
|
||||
|
||||
public async verifyReleaseChannelEnabled(channel: string): Promise<boolean> {
|
||||
this.appState.setWarningDialogTexts({
|
||||
this.appState.setGenericDialogOptions({
|
||||
type: GenericDialogType.warning,
|
||||
label: `You're loading an example with a version of Electron with an unincluded release
|
||||
channel (${channel}). Do you want to enable the release channel to load the
|
||||
version of Electron from the example?`
|
||||
});
|
||||
this.appState.isWarningDialogShowing = true;
|
||||
await when(() => !this.appState.isWarningDialogShowing);
|
||||
this.appState.isGenericDialogShowing = true;
|
||||
await when(() => !this.appState.isGenericDialogShowing);
|
||||
|
||||
return !!this.appState.warningDialogLastResult;
|
||||
return !!this.appState.genericDialogLastResult;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,18 +245,20 @@ export class RemoteLoader {
|
||||
*/
|
||||
private handleLoadingFailed(error: Error): false {
|
||||
if (navigator.onLine) {
|
||||
this.appState.setWarningDialogTexts({
|
||||
this.appState.setGenericDialogOptions({
|
||||
type: GenericDialogType.warning,
|
||||
label: `Loading the fiddle failed: ${error}`,
|
||||
cancel: undefined
|
||||
});
|
||||
} else {
|
||||
this.appState.setWarningDialogTexts({
|
||||
this.appState.setGenericDialogOptions({
|
||||
type: GenericDialogType.warning,
|
||||
label: `Loading the fiddle failed. Your computer seems to be offline. Error: ${error}`,
|
||||
cancel: undefined
|
||||
});
|
||||
}
|
||||
|
||||
this.appState.toggleWarningDialog();
|
||||
this.appState.toggleGenericDialog();
|
||||
|
||||
console.warn(`Loading Fiddle failed`, error);
|
||||
return false;
|
||||
|
||||
@@ -193,10 +193,10 @@ export class Runner {
|
||||
delete env.ELECTRON_ENABLE_STACK_DUMPING;
|
||||
}
|
||||
|
||||
this.child = spawn(binaryPath, [ dir, '--inspect' ], {
|
||||
cwd: dir,
|
||||
env,
|
||||
});
|
||||
// Add user-specified cli flags if any have been set.
|
||||
const options = [ dir, '--inspect' ].concat(this.appState.executionFlags);
|
||||
|
||||
this.child = spawn(binaryPath, options, { cwd: dir, env });
|
||||
this.appState.isRunning = true;
|
||||
pushOutput(`Electron v${version} started.`);
|
||||
|
||||
|
||||
+61
-109
@@ -8,13 +8,13 @@ import {
|
||||
EditorId,
|
||||
ElectronVersion,
|
||||
ElectronVersionSource,
|
||||
ElectronVersionState,
|
||||
GenericDialogOptions,
|
||||
GenericDialogType,
|
||||
MosaicId,
|
||||
NodeVersion,
|
||||
NpmVersion,
|
||||
OutputEntry,
|
||||
OutputOptions,
|
||||
VersionState,
|
||||
WarningDialogTexts
|
||||
OutputOptions
|
||||
} from '../interfaces';
|
||||
import { IpcEvents } from '../ipc-events';
|
||||
import { arrayToStringMap } from '../utils/array-to-stringmap';
|
||||
@@ -26,25 +26,25 @@ 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 { DEFAULT_CLOSED_PANELS, DEFAULT_MOSAIC_ARRANGEMENT } from './constants';
|
||||
import { getContent, isContentUnchanged } from './content';
|
||||
import { getLocalTypePathForVersion, updateEditorTypeDefinitions } from './fetch-types';
|
||||
import { ipcRendererManager } from './ipc';
|
||||
import { activateTheme } from './themes';
|
||||
|
||||
import { sortedElectronMap } from '../utils/sorted-electron-map';
|
||||
import {
|
||||
addLocalVersion,
|
||||
ElectronReleaseChannel,
|
||||
getAllElectronVersions,
|
||||
getDefaultVersion,
|
||||
getElectronVersions,
|
||||
getReleaseChannel,
|
||||
getUpdatedElectronVersions,
|
||||
getUpdatedNodeVersions,
|
||||
saveLocalVersions
|
||||
} from './versions';
|
||||
|
||||
const knownVersions = getAllElectronVersions();
|
||||
const knownVersions = getElectronVersions();
|
||||
const defaultVersion = getDefaultVersion(knownVersions);
|
||||
const defaultNodeVersion = '12.0.0';
|
||||
|
||||
/**
|
||||
* Editors exist outside of React's world. To make things *a lot*
|
||||
@@ -69,33 +69,32 @@ 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 gitHubAvatarUrl: string | null = localStorage.getItem('gitHubAvatarUrl');
|
||||
@observable public gitHubName: string | null = localStorage.getItem('gitHubName');
|
||||
@observable public gitHubLogin: string | null = localStorage.getItem('gitHubLogin');
|
||||
@observable public gitHubToken: string | null = localStorage.getItem('gitHubToken') || null;
|
||||
@observable public gitHubPublishAsPublic: boolean = !!this.retrieve('gitHubPublishAsPublic');
|
||||
@observable public versionsToShow: Array<ElectronReleaseChannel> =
|
||||
this.retrieve('versionsToShow') as Array<ElectronReleaseChannel>
|
||||
@observable public channelsToShow: Array<ElectronReleaseChannel> =
|
||||
this.retrieve('channelsToShow') as Array<ElectronReleaseChannel>
|
||||
|| [ElectronReleaseChannel.stable, ElectronReleaseChannel.beta];
|
||||
@observable public statesToShow: Array<VersionState> =
|
||||
this.retrieve('statesToShow') as Array<VersionState>
|
||||
|| [VersionState.downloading, VersionState.ready, VersionState.unknown];
|
||||
@observable public statesToShow: Array<ElectronVersionState> =
|
||||
this.retrieve('statesToShow') as Array<ElectronVersionState>
|
||||
|| [ElectronVersionState.downloading, ElectronVersionState.ready, ElectronVersionState.unknown];
|
||||
@observable public isKeepingUserDataDirs: boolean = !!this.retrieve('isKeepingUserDataDirs');
|
||||
@observable public isEnablingElectronLogging: boolean = !!this.retrieve('isEnablingElectronLogging');
|
||||
@observable public isClearingConsoleOnRun: boolean = !!this.retrieve('isClearingConsoleOnRun');
|
||||
@observable public executionFlags: Array<string> =
|
||||
this.retrieve('executionFlags') as Array<string> === null ?
|
||||
[] : this.retrieve('executionFlags') as Array<string>;
|
||||
|
||||
// -- Various session-only state ------------------
|
||||
@observable public gistId: string = '';
|
||||
@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 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 genericDialogOptions = { type: GenericDialogType.warning, label: '', ok: 'Okay', cancel: 'Cancel' };
|
||||
@observable public genericDialogLastResult: boolean | null = null;
|
||||
@observable public mosaicArrangement: MosaicNode<MosaicId> | null = DEFAULT_MOSAIC_ARRANGEMENT;
|
||||
@observable public templateName: string | undefined;
|
||||
@observable public currentDocsDemoPage: DocsDemoPage = DocsDemoPage.DEFAULT;
|
||||
@@ -112,19 +111,15 @@ export class AppState {
|
||||
@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 isGenericDialogShowing: boolean = false;
|
||||
@observable public isSettingsShowing: 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 ------------------
|
||||
@observable public closedPanels: Partial<Record<MosaicId, EditorBackup | true>> = {
|
||||
docsDemo: true // Closed by default
|
||||
};
|
||||
@observable public closedPanels: Partial<Record<MosaicId, EditorBackup | true>> = DEFAULT_CLOSED_PANELS;
|
||||
|
||||
private outputBuffer: string = '';
|
||||
private name: string;
|
||||
@@ -136,13 +131,11 @@ 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);
|
||||
@@ -163,24 +156,26 @@ export class AppState {
|
||||
autorun(() => this.save('gitHubPublishAsPublic', this.gitHubPublishAsPublic));
|
||||
autorun(() => this.save('isKeepingUserDataDirs', this.isKeepingUserDataDirs));
|
||||
autorun(() => this.save('isEnablingElectronLogging', this.isEnablingElectronLogging));
|
||||
autorun(() => this.save('executionFlags', this.executionFlags));
|
||||
autorun(() => this.save('version', this.version));
|
||||
autorun(() => this.save('versionsToShow', this.versionsToShow));
|
||||
autorun(() => this.save('channelsToShow', this.channelsToShow));
|
||||
autorun(() => this.save('statesToShow', this.statesToShow));
|
||||
|
||||
autorun(() => {
|
||||
if (this.isUnsaved) {
|
||||
window.onbeforeunload = () => {
|
||||
this.setWarningDialogTexts({
|
||||
this.setGenericDialogOptions({
|
||||
type: GenericDialogType.warning,
|
||||
label: `The current Fiddle is unsaved. Do you want to exit anyway?`,
|
||||
ok: 'Quit'
|
||||
});
|
||||
|
||||
this.isWarningDialogShowing = true;
|
||||
this.isGenericDialogShowing = true;
|
||||
|
||||
// We'll wait until the warning dialog was closed
|
||||
when(() => !this.isWarningDialogShowing).then(() => {
|
||||
when(() => !this.isGenericDialogShowing).then(() => {
|
||||
// The user confirmed, let's close for real.
|
||||
if (this.warningDialogLastResult) {
|
||||
if (this.genericDialogLastResult) {
|
||||
window.onbeforeunload = null;
|
||||
|
||||
// Should we just close or quit?
|
||||
@@ -204,7 +199,6 @@ 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 🔬');
|
||||
@@ -223,15 +217,28 @@ export class AppState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current NodeVersion or the first
|
||||
* one that can be found.
|
||||
* Returns an array of Electron versions to show given the
|
||||
* current settings for states and channels to display
|
||||
*/
|
||||
@computed get currentNodeVersion(): NodeVersion {
|
||||
if (this.nodeVersions[this.nodeVersion]) {
|
||||
return this.nodeVersions[this.nodeVersion];
|
||||
} else {
|
||||
return this.nodeVersions[defaultNodeVersion];
|
||||
}
|
||||
@computed get versionsToShow(): Array<ElectronVersion> {
|
||||
return sortedElectronMap<ElectronVersion>(this.versions, (_key, item) => item)
|
||||
.filter((item) => {
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we want to show the version
|
||||
if (!this.channelsToShow.includes(getReleaseChannel(item))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we want to show the state
|
||||
if (!this.statesToShow.includes(item.state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,21 +261,6 @@ 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);
|
||||
@@ -281,10 +273,6 @@ export class AppState {
|
||||
this.isConsoleShowing = !this.isConsoleShowing;
|
||||
}
|
||||
|
||||
@action public toggleNodeMode() {
|
||||
this.isNodeMode = !this.isNodeMode;
|
||||
}
|
||||
|
||||
@action public clearConsole() {
|
||||
this.output = [];
|
||||
}
|
||||
@@ -308,11 +296,11 @@ export class AppState {
|
||||
this.isTokenDialogShowing = !this.isTokenDialogShowing;
|
||||
}
|
||||
|
||||
@action public toggleWarningDialog() {
|
||||
this.isWarningDialogShowing = !this.isWarningDialogShowing;
|
||||
@action public toggleGenericDialog() {
|
||||
this.isGenericDialogShowing = !this.isGenericDialogShowing;
|
||||
|
||||
if (this.isWarningDialogShowing) {
|
||||
this.warningDialogLastResult = null;
|
||||
if (this.isGenericDialogShowing) {
|
||||
this.genericDialogLastResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,14 +308,6 @@ export class AppState {
|
||||
this.isBisectDialogShowing = !this.isBisectDialogShowing;
|
||||
}
|
||||
|
||||
@action public toggleConfirmationPromptDialog() {
|
||||
this.isConfirmationPromptShowing = !this.isConfirmationPromptShowing;
|
||||
|
||||
if (this.isConfirmationPromptShowing) {
|
||||
this.confirmationPromptLastResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
@action public toggleSettings() {
|
||||
// We usually don't lose editor focus,
|
||||
// so you can still type. Let's force-blur.
|
||||
@@ -353,26 +333,19 @@ export class AppState {
|
||||
window.ElectronFiddle.app.setupTheme();
|
||||
}
|
||||
|
||||
@action public setWarningDialogTexts(input: WarningDialogTexts) {
|
||||
this.warningDialogTexts = {
|
||||
@action public setGenericDialogOptions(opts: GenericDialogOptions) {
|
||||
this.genericDialogOptions = {
|
||||
type: GenericDialogType.warning,
|
||||
ok: 'Okay',
|
||||
cancel: 'Cancel',
|
||||
...input
|
||||
};
|
||||
}
|
||||
|
||||
@action public setConfirmationPromptTexts(input: WarningDialogTexts) {
|
||||
this.confirmationDialogTexts = {
|
||||
ok: 'Okay',
|
||||
cancel: 'Cancel',
|
||||
...input
|
||||
...opts
|
||||
};
|
||||
}
|
||||
|
||||
@action public addLocalVersion(input: NpmVersion) {
|
||||
addLocalVersion(input);
|
||||
|
||||
this.versions = arrayToStringMap(getAllElectronVersions());
|
||||
this.versions = arrayToStringMap(getElectronVersions());
|
||||
this.updateDownloadedVersionState();
|
||||
}
|
||||
|
||||
@@ -408,7 +381,7 @@ export class AppState {
|
||||
saveLocalVersions(versionsAsArray);
|
||||
} else {
|
||||
await this.binaryManager.remove(version);
|
||||
updatedVersions[version].state = VersionState.unknown;
|
||||
updatedVersions[version].state = ElectronVersionState.unknown;
|
||||
}
|
||||
|
||||
this.versions = updatedVersions;
|
||||
@@ -434,7 +407,7 @@ export class AppState {
|
||||
console.log(`State: Instructing BinaryManager to fetch v${version}`);
|
||||
const updatedVersions = { ...this.versions };
|
||||
updatedVersions[version] = updatedVersions[version] || {};
|
||||
updatedVersions[version].state = VersionState.downloading;
|
||||
updatedVersions[version].state = ElectronVersionState.downloading;
|
||||
this.versions = updatedVersions;
|
||||
|
||||
await this.binaryManager.setup(version);
|
||||
@@ -444,27 +417,6 @@ export class AppState {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
@@ -526,7 +478,7 @@ export class AppState {
|
||||
|
||||
(downloadedVersions || []).forEach((version) => {
|
||||
if (updatedVersions[version]) {
|
||||
updatedVersions[version].state = VersionState.ready;
|
||||
updatedVersions[version].state = ElectronVersionState.ready;
|
||||
}
|
||||
});
|
||||
console.log(`State: Updated version state`, updatedVersions);
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
} from 'electron';
|
||||
import { autorun } from 'mobx';
|
||||
|
||||
import { ElectronVersion, VersionState } from '../interfaces';
|
||||
import { ElectronVersion, ElectronVersionState } from '../interfaces';
|
||||
import { getNiceGreeting } from '../utils/nice-greeting';
|
||||
import { sortedElectronMap } from '../utils/sorted-map';
|
||||
import { sortedElectronMap } from '../utils/sorted-electron-map';
|
||||
import { AppState } from './state';
|
||||
|
||||
const { TouchBar } = remote;
|
||||
@@ -26,7 +26,7 @@ const {
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getItemIcon(
|
||||
{ state }: Partial<ElectronVersion> = { state: VersionState.unknown }
|
||||
{ state }: Partial<ElectronVersion> = { state: ElectronVersionState.unknown }
|
||||
) {
|
||||
return state === 'ready'
|
||||
? '💾'
|
||||
|
||||
+16
-62
@@ -1,4 +1,4 @@
|
||||
import { ElectronVersion, ElectronVersionSource, NodeVersion, NpmVersion, VersionState } from '../interfaces';
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState, NpmVersion } 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 getElectronVersions(
|
||||
function getVersions(
|
||||
key: VersionKeys, fallbackMethod: () => Array<NpmVersion>
|
||||
): Array<NpmVersion> {
|
||||
const fromLs = window.localStorage.getItem(key);
|
||||
@@ -117,7 +117,7 @@ function getElectronVersions(
|
||||
* @param {VersionKeys} key
|
||||
* @param {Array<NpmVersion} versions
|
||||
*/
|
||||
function saveElectronVersions(key: VersionKeys, versions: Array<NpmVersion>) {
|
||||
function saveVersions(key: VersionKeys, versions: Array<NpmVersion>) {
|
||||
const stringified = JSON.stringify(versions);
|
||||
window.localStorage.setItem(key, stringified);
|
||||
}
|
||||
@@ -127,12 +127,12 @@ function saveElectronVersions(key: VersionKeys, versions: Array<NpmVersion>) {
|
||||
*
|
||||
* @returns {Array<NpmVersion>}
|
||||
*/
|
||||
export function getAllElectronVersions(): Array<ElectronVersion> {
|
||||
export function getElectronVersions(): Array<ElectronVersion> {
|
||||
const known: Array<ElectronVersion> = getKnownVersions().map((version) => {
|
||||
return {
|
||||
...version,
|
||||
source: ElectronVersionSource.remote,
|
||||
state: VersionState.unknown
|
||||
state: ElectronVersionState.unknown
|
||||
};
|
||||
});
|
||||
|
||||
@@ -140,11 +140,11 @@ export function getAllElectronVersions(): Array<ElectronVersion> {
|
||||
return {
|
||||
...version,
|
||||
source: ElectronVersionSource.local,
|
||||
state: VersionState.ready
|
||||
state: ElectronVersionState.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 = getElectronVersions(VersionKeys.local, () => []);
|
||||
const versions = getVersions(VersionKeys.local, () => []);
|
||||
|
||||
return versions;
|
||||
}
|
||||
@@ -190,7 +190,7 @@ export function saveLocalVersions(versions: Array<NpmVersion | ElectronVersion>)
|
||||
return true;
|
||||
});
|
||||
|
||||
return saveElectronVersions(VersionKeys.local, filteredVersions);
|
||||
return saveVersions(VersionKeys.local, filteredVersions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,7 +200,7 @@ export function saveLocalVersions(versions: Array<NpmVersion | ElectronVersion>)
|
||||
* @returns {Array<NpmVersion>}
|
||||
*/
|
||||
export function getKnownVersions(): Array<NpmVersion> {
|
||||
return getElectronVersions(VersionKeys.known, () => require('../../static/releases.json'));
|
||||
return getVersions(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 saveElectronVersions(VersionKeys.known, versions);
|
||||
return saveVersions(VersionKeys.known, versions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,66 +222,20 @@ export function saveKnownVersions(versions: Array<NpmVersion>) {
|
||||
export async function getUpdatedElectronVersions(
|
||||
): Promise<Array<ElectronVersion>> {
|
||||
try {
|
||||
await fetchElectronVersions();
|
||||
await fetchVersions();
|
||||
} catch (error) {
|
||||
console.warn(`Versions: Failed to fetch Electron versions`, { error });
|
||||
console.warn(`Versions: Failed to fetch versions`, { error });
|
||||
}
|
||||
|
||||
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 [];
|
||||
return getElectronVersions();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Fetch the latest known versions directly from npm.
|
||||
*
|
||||
* @returns {Promise<Array<NpmVersion>>}
|
||||
*/
|
||||
export async function fetchElectronVersions() {
|
||||
export async function fetchVersions() {
|
||||
const channels = [
|
||||
`https://registry.npmjs.org/electron`, // stable, beta
|
||||
`https://registry.npmjs.org/electron-nightly` // nightly
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Version } from '../interfaces';
|
||||
import { ElectronVersion } 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<T extends Version>(
|
||||
input: Array<T>
|
||||
): Record<string, T> {
|
||||
export function arrayToStringMap(
|
||||
input: Array<ElectronVersion>
|
||||
): Record<string, ElectronVersion> {
|
||||
const output = {};
|
||||
|
||||
input.forEach((version) => {
|
||||
|
||||
@@ -6,9 +6,9 @@ import { getEditorValue } from './editor-value';
|
||||
import { getEditorViewState } from './editor-viewstate';
|
||||
|
||||
export interface EditorBackup {
|
||||
value: string;
|
||||
model: editor.ITextModel | null;
|
||||
viewState: editor.ICodeEditorViewState | null;
|
||||
value?: string;
|
||||
model?: editor.ITextModel | null;
|
||||
viewState?: editor.ICodeEditorViewState | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as semver from 'semver';
|
||||
|
||||
import { ElectronVersion, NodeVersion } from '../interfaces';
|
||||
import { ElectronVersion } from '../interfaces';
|
||||
|
||||
/**
|
||||
* Sorts Electron versions and returns the result of a
|
||||
@@ -13,28 +13,6 @@ import { ElectronVersion, NodeVersion } 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) => {
|
||||
@@ -52,5 +30,5 @@ function sortedMap<T1, T2>(
|
||||
|
||||
return semver.gt(a, b, true) ? -1 : 1;
|
||||
})
|
||||
.map((key) => mapFn(key, versions[key])) as Array<T2>;
|
||||
.map((key) => mapFn(key, versions[key])) as Array<T>;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -25,10 +25,6 @@ app.on('ready', () => {
|
||||
console.log('The autoUpdater is checking for an update')
|
||||
})
|
||||
|
||||
autoUpdater.on('update-available', () => {
|
||||
console.log('The autoUpdater has found an update!')
|
||||
})
|
||||
|
||||
autoUpdater.on('update-available', () => {
|
||||
console.log('The autoUpdater has found an update and is now downloading it!')
|
||||
})
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
height: 600,
|
||||
width: 600,
|
||||
webPreferences: {
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
const { app, BrowserWindow, session } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
height: 600,
|
||||
width: 600,
|
||||
webPreferences: {
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
height: 600,
|
||||
width: 600,
|
||||
webPreferences: {
|
||||
|
||||
@@ -8,10 +8,8 @@
|
||||
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({ height: 600, width: 600 })
|
||||
const mainWindow = new BrowserWindow({ height: 600, width: 600 })
|
||||
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
|
||||
@@ -6,10 +6,8 @@
|
||||
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
height: 600,
|
||||
width: 600,
|
||||
webPreferences: {
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
const { app, BrowserWindow, dialog } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({ height: 600, width: 600 })
|
||||
const mainWindow = new BrowserWindow({ height: 600, width: 600 })
|
||||
|
||||
// Show an "Open File" dialog and attempt to open
|
||||
// the chosen file in our window.
|
||||
|
||||
@@ -7,10 +7,8 @@
|
||||
|
||||
const { app, BrowserWindow, ipcMain } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
height: 600,
|
||||
width: 600,
|
||||
webPreferences: {
|
||||
|
||||
@@ -4,6 +4,6 @@ const { ipcRenderer } = require('electron')
|
||||
console.log(ipcRenderer.sendSync('synchronous-message', 'ping'))
|
||||
|
||||
// prints "pong"
|
||||
ipcRenderer.on('asynchronous-reply', (...args) => console.log(args))
|
||||
ipcRenderer.on('asynchronous-reply', (_, ...args) => console.log(...args))
|
||||
|
||||
ipcRenderer.send('asynchronous-message', 'ping')
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
const { app, BrowserWindow, Menu } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({ height: 600, width: 600 })
|
||||
const mainWindow = new BrowserWindow({ height: 600, width: 600 })
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
const template = [
|
||||
|
||||
@@ -6,10 +6,8 @@
|
||||
|
||||
const { app, BrowserWindow, Notification } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
height: 600,
|
||||
width: 600,
|
||||
webPreferences: {
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
height: 600,
|
||||
width: 600,
|
||||
webPreferences: {
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
// We cannot require the screen module until the
|
||||
// app is ready
|
||||
@@ -17,7 +15,7 @@ app.on('ready', () => {
|
||||
const primaryDisplay = screen.getPrimaryDisplay()
|
||||
const { width, height } = primaryDisplay.workAreaSize
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
width,
|
||||
height,
|
||||
webPreferences: {
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 600,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
|
||||
@@ -6,10 +6,8 @@
|
||||
const { app, BrowserWindow, TouchBar } = require('electron')
|
||||
const { TouchBarLabel, TouchBarButton, TouchBarSpacer } = TouchBar
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({ height: 600, width: 600 })
|
||||
const mainWindow = new BrowserWindow({ height: 600, width: 600 })
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// This API only works on macOS devices with a TouchBar.
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
const { app, BrowserWindow, webContents } = require('electron')
|
||||
|
||||
let mainWindow
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({ height: 600, width: 600 })
|
||||
const mainWindow = new BrowserWindow({ height: 600, width: 600 })
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let mainWindow = null
|
||||
|
||||
app.on('ready', () => {
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 600,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
|
||||
@@ -8,6 +8,7 @@ export class AppMock {
|
||||
public setEditorValues = jest.fn();
|
||||
public getEditorValues = jest.fn(() => ({
|
||||
main: 'main-content',
|
||||
preload: 'preload-content',
|
||||
renderer: 'renderer-content',
|
||||
html: 'html-content'
|
||||
}));
|
||||
|
||||
@@ -155,6 +155,9 @@ const electronMock = {
|
||||
writeText: jest.fn(),
|
||||
writeImage: jest.fn()
|
||||
},
|
||||
crashReporter: {
|
||||
start: jest.fn(),
|
||||
},
|
||||
dialog: {
|
||||
showOpenDialog: jest.fn(() => Promise.resolve({})),
|
||||
showMessageBox: jest.fn(() => Promise.resolve({}))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { observable } from 'mobx';
|
||||
|
||||
export class MockState {
|
||||
@observable public isWarningDialogShowing = false;
|
||||
@observable public isGenericDialogShowing = false;
|
||||
@observable public gistId = '';
|
||||
@observable public closedPanels = {};
|
||||
@observable public isConsoleShowing = true;
|
||||
|
||||
+23
-14
@@ -26,13 +26,16 @@ jest.mock('../../src/renderer/components/output-editors-wrapper', () => ({
|
||||
}));
|
||||
|
||||
describe('Editors component', () => {
|
||||
beforeAll(() => {
|
||||
document.body.innerHTML = '<div id="app" />';
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
(window as any).ElectronFiddle = new ElectronFiddleMock();
|
||||
});
|
||||
|
||||
describe('setup()', () => {
|
||||
it('renders the app', async () => {
|
||||
document.body.innerHTML = '<div id="app" />';
|
||||
jest.useFakeTimers();
|
||||
|
||||
const app = new App();
|
||||
@@ -141,7 +144,7 @@ describe('Editors component', () => {
|
||||
(app.state as Partial<AppState>) = new MockState();
|
||||
app.state.isUnsaved = true;
|
||||
app.state.localPath = '/fake/path';
|
||||
app.state.setWarningDialogTexts = jest.fn();
|
||||
app.state.setGenericDialogOptions = jest.fn();
|
||||
app.setEditorValues = jest.fn();
|
||||
|
||||
const editorValues = {
|
||||
@@ -161,9 +164,9 @@ describe('Editors component', () => {
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
expect(app.state.isWarningDialogShowing).toBe(true);
|
||||
app.state.warningDialogLastResult = true;
|
||||
app.state.isWarningDialogShowing = false;
|
||||
expect(app.state.isGenericDialogShowing).toBe(true);
|
||||
app.state.genericDialogLastResult = true;
|
||||
app.state.isGenericDialogShowing = false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -196,7 +199,7 @@ describe('Editors component', () => {
|
||||
const app = new App();
|
||||
(app.state as Partial<AppState>) = new MockState();
|
||||
app.state.isUnsaved = true;
|
||||
app.state.setWarningDialogTexts = jest.fn();
|
||||
app.state.setGenericDialogOptions = jest.fn();
|
||||
app.setEditorValues = jest.fn();
|
||||
|
||||
expect(app.state.localPath).toBeUndefined();
|
||||
@@ -217,9 +220,9 @@ describe('Editors component', () => {
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
expect(app.state.isWarningDialogShowing).toBe(true);
|
||||
app.state.warningDialogLastResult = false;
|
||||
app.state.isWarningDialogShowing = false;
|
||||
expect(app.state.isGenericDialogShowing).toBe(true);
|
||||
app.state.genericDialogLastResult = false;
|
||||
app.state.isGenericDialogShowing = false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -227,7 +230,7 @@ describe('Editors component', () => {
|
||||
const app = new App();
|
||||
(app.state as Partial<AppState>) = new MockState();
|
||||
app.state.isUnsaved = true;
|
||||
app.state.setWarningDialogTexts = jest.fn();
|
||||
app.state.setGenericDialogOptions = jest.fn();
|
||||
app.setEditorValues = jest.fn();
|
||||
|
||||
const editorValues = {
|
||||
@@ -250,9 +253,9 @@ describe('Editors component', () => {
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
expect(app.state.isWarningDialogShowing).toBe(true);
|
||||
app.state.warningDialogLastResult = true;
|
||||
app.state.isWarningDialogShowing = false;
|
||||
expect(app.state.isGenericDialogShowing).toBe(true);
|
||||
app.state.genericDialogLastResult = true;
|
||||
app.state.isGenericDialogShowing = false;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -283,16 +286,22 @@ describe('Editors component', () => {
|
||||
delete window.ElectronFiddle.editors.main;
|
||||
|
||||
const app = new App();
|
||||
(app.state.closedPanels as any).main = { model: { setValue: jest.fn() } };
|
||||
(app.state.closedPanels as any).main = {
|
||||
model: { setValue: jest.fn() }
|
||||
};
|
||||
app.state.closedPanels.preload = {};
|
||||
|
||||
app.setEditorValues({
|
||||
html: 'html-value',
|
||||
main: 'main-value',
|
||||
renderer: 'renderer-value',
|
||||
preload: 'preload-value'
|
||||
});
|
||||
|
||||
expect(
|
||||
(app.state.closedPanels.main as EditorBackup)!.model!.setValue
|
||||
).toHaveBeenCalledWith('main-value');
|
||||
expect(app.state.closedPanels.preload).toEqual({ value: 'preload-value' });
|
||||
|
||||
window.ElectronFiddle.editors.main = oldMainEditor;
|
||||
});
|
||||
|
||||
@@ -1,113 +1,91 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`VersionChooser component handles corrupt data 1`] = `
|
||||
exports[`VersionSelect component renders 1`] = `
|
||||
<Blueprint3.ButtonGroup>
|
||||
<Blueprint3.Select
|
||||
disabled={false}
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
<VersionSelect
|
||||
appState={
|
||||
Object {
|
||||
"channelsToShow": Array [
|
||||
"Stable",
|
||||
"Beta",
|
||||
],
|
||||
"currentElectronVersion": Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.2",
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.1",
|
||||
"setVersion": [MockFunction],
|
||||
"statesToShow": Array [
|
||||
"ready",
|
||||
"downloading",
|
||||
],
|
||||
"version": "2.0.2",
|
||||
"versions": Object {
|
||||
"1.0.0": Object {
|
||||
"source": "remote",
|
||||
"state": "unknown",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
"1.8.7": Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "1.8.7",
|
||||
},
|
||||
"2.0.1": Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.1",
|
||||
},
|
||||
"2.0.2": Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.2",
|
||||
},
|
||||
"3.0.0-unsupported": Object {
|
||||
"source": "remote",
|
||||
"state": "unknown",
|
||||
"version": "3.0.0-unsupported",
|
||||
},
|
||||
"3.1.3": undefined,
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "1.8.7",
|
||||
},
|
||||
]
|
||||
"versionsToShow": 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",
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "unknown",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "unknown",
|
||||
"version": "3.0.0-unsupported",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
/>
|
||||
currentVersion={
|
||||
Object {
|
||||
"source": "remote",
|
||||
"state": "ready",
|
||||
"version": "2.0.2",
|
||||
}
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
className="version-chooser"
|
||||
disabled={false}
|
||||
icon="saved"
|
||||
text="Electron v2.0.2"
|
||||
/>
|
||||
</Blueprint3.Select>
|
||||
</Blueprint3.ButtonGroup>
|
||||
`;
|
||||
|
||||
exports[`VersionChooser component renderItem() renders an item 1`] = `
|
||||
<Blueprint3.MenuItem
|
||||
active={true}
|
||||
disabled={false}
|
||||
icon="cloud"
|
||||
label="Not downloaded"
|
||||
multiline={false}
|
||||
onClick={[Function]}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text={
|
||||
Array [
|
||||
"1.0.0",
|
||||
]
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`VersionChooser component renders 1`] = `
|
||||
<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"
|
||||
/>
|
||||
</Blueprint3.Select>
|
||||
onVersionSelect={[Function]}
|
||||
/>
|
||||
</Blueprint3.ButtonGroup>
|
||||
`;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,52 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`GenericDialog component renders a confirmation 1`] = `
|
||||
<Blueprint3.Alert
|
||||
canEscapeKeyCancel={false}
|
||||
canOutsideClickCancel={false}
|
||||
cancelButtonText=""
|
||||
confirmButtonText="Ok"
|
||||
icon="help"
|
||||
intent="primary"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
>
|
||||
<p>
|
||||
Some message
|
||||
</p>
|
||||
</Blueprint3.Alert>
|
||||
`;
|
||||
|
||||
exports[`GenericDialog component renders a success message 1`] = `
|
||||
<Blueprint3.Alert
|
||||
canEscapeKeyCancel={false}
|
||||
canOutsideClickCancel={false}
|
||||
cancelButtonText=""
|
||||
confirmButtonText="Ok"
|
||||
icon="info-sign"
|
||||
intent="success"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
>
|
||||
<p>
|
||||
Some message
|
||||
</p>
|
||||
</Blueprint3.Alert>
|
||||
`;
|
||||
|
||||
exports[`GenericDialog component renders a warning 1`] = `
|
||||
<Blueprint3.Alert
|
||||
canEscapeKeyCancel={false}
|
||||
canOutsideClickCancel={false}
|
||||
cancelButtonText=""
|
||||
confirmButtonText="Ok"
|
||||
icon="warning-sign"
|
||||
intent="danger"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
>
|
||||
<p>
|
||||
Some message
|
||||
</p>
|
||||
</Blueprint3.Alert>
|
||||
`;
|
||||
@@ -1,16 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TokenDialog component renders 1`] = `
|
||||
<Blueprint3.Alert
|
||||
canEscapeKeyCancel={false}
|
||||
canOutsideClickCancel={false}
|
||||
cancelButtonText=""
|
||||
confirmButtonText=""
|
||||
icon="warning-sign"
|
||||
intent="danger"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
>
|
||||
<p />
|
||||
</Blueprint3.Alert>
|
||||
`;
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
exports[`Editors component renders 1`] = `
|
||||
<div
|
||||
className="mosaic-blueprint-theme mosaic mosaic-drop-target"
|
||||
className="focused__undefined mosaic mosaic-drop-target"
|
||||
>
|
||||
<div
|
||||
className="mosaic-root"
|
||||
@@ -1050,7 +1050,7 @@ exports[`Editors component renders a toolbar 1`] = `
|
||||
},
|
||||
},
|
||||
},
|
||||
"setWarningDialogTexts": [Function],
|
||||
"setGenericDialogOptions": [Function],
|
||||
}
|
||||
}
|
||||
id="main"
|
||||
@@ -1078,7 +1078,7 @@ exports[`Editors component renders a toolbar 1`] = `
|
||||
},
|
||||
},
|
||||
},
|
||||
"setWarningDialogTexts": [Function],
|
||||
"setGenericDialogOptions": [Function],
|
||||
}
|
||||
}
|
||||
id="main"
|
||||
|
||||
@@ -34,5 +34,25 @@ exports[`ExecutionSettings component renders 1`] = `
|
||||
/>
|
||||
</Blueprint3.FormGroup>
|
||||
</Blueprint3.Callout>
|
||||
<br />
|
||||
<Blueprint3.Callout>
|
||||
<Blueprint3.FormGroup>
|
||||
<p>
|
||||
Electron allows starting the executable with
|
||||
<a
|
||||
href="https://electronjs.org/docs/api/chrome-command-line-switches"
|
||||
>
|
||||
user-provided flags
|
||||
</a>
|
||||
, such as '--js-flags=--expose-gc'. Those can be added here as bar-separated (|) flags to run when you start your Fiddles.
|
||||
</p>
|
||||
<br />
|
||||
<Blueprint3.InputGroup
|
||||
onChange={[Function]}
|
||||
placeholder="--js-flags=--expose-gc|--lang=es"
|
||||
value=""
|
||||
/>
|
||||
</Blueprint3.FormGroup>
|
||||
</Blueprint3.Callout>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -56,7 +56,7 @@ exports[`Header component renders the tour once started 1`] = `
|
||||
Object {
|
||||
"content": <React.Fragment>
|
||||
<p>
|
||||
Electron Fiddle allows you to build little experiments and mini-apps with Electron. Each Fiddle has three files: A main script, a renderer script, and an HTML file.
|
||||
Electron Fiddle allows you to build little experiments and mini-apps with Electron. Each Fiddle has at least three of these files: A main script, a renderer script, a preload script, and an HTML file.
|
||||
</p>
|
||||
<p>
|
||||
If you
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`VersionSelect component renderItem() renders an item 1`] = `
|
||||
<Blueprint3.MenuItem
|
||||
active={true}
|
||||
disabled={false}
|
||||
icon="cloud"
|
||||
label="Not downloaded"
|
||||
multiline={false}
|
||||
onClick={[Function]}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text={
|
||||
Array [
|
||||
"1.0.0",
|
||||
]
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`VersionSelect component renders 1`] = `
|
||||
<Blueprint3.Select
|
||||
disabled={false}
|
||||
filterable={true}
|
||||
itemPredicate={[Function]}
|
||||
itemRenderer={[Function]}
|
||||
noResults={
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
multiline={false}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="No results."
|
||||
/>
|
||||
}
|
||||
onItemSelect={[Function]}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
className="version-chooser"
|
||||
disabled={false}
|
||||
icon="cloud"
|
||||
text="Electron v1.0.0"
|
||||
/>
|
||||
</Blueprint3.Select>
|
||||
`;
|
||||
@@ -16,9 +16,7 @@ describe('AddressBar component', () => {
|
||||
@observable public gistId: string | null = null;
|
||||
@observable public isWarningDialogShowing: boolean = false;
|
||||
@observable public isConfirmationPromptShowing: boolean = false;
|
||||
public setWarningDialogTexts = jest.fn();
|
||||
public setConfirmationDialogTexts = jest.fn();
|
||||
public setConfirmationPromptTexts = jest.fn();
|
||||
public setGenericDialogOptions = jest.fn();
|
||||
public toggleWarningDialog = jest.fn();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@ describe('Bisect commands component', () => {
|
||||
},
|
||||
setVersion: jest.fn(),
|
||||
version: '1.0.0',
|
||||
pushOutput: jest.fn()
|
||||
pushOutput: jest.fn(),
|
||||
setGenericDialogOptions: jest.fn(),
|
||||
isGenericDialogShowing: false
|
||||
};
|
||||
});
|
||||
|
||||
@@ -66,11 +68,17 @@ describe('Bisect commands component', () => {
|
||||
instance.terminateBisect = jest.fn();
|
||||
|
||||
// same value is only returned when there is only 1 version left
|
||||
store.Bisector.continue.mockReturnValue(['minRev', 'maxRev']);
|
||||
store.Bisector.continue.mockReturnValue([{version: 'minVer'}, {version: 'maxVer'}]);
|
||||
instance.continueBisect(true);
|
||||
expect(store.setVersion).not.toHaveBeenCalled();
|
||||
expect(instance.terminateBisect).toHaveBeenCalled();
|
||||
expect(store.pushOutput).toHaveBeenCalled();
|
||||
expect(store.isGenericDialogShowing).toEqual(true);
|
||||
expect(store.setGenericDialogOptions).toHaveBeenCalledWith({
|
||||
cancel: undefined,
|
||||
label: 'Bisect complete. Check between versions minVer and maxVer.',
|
||||
type: 'success'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import { shallow } from "enzyme";
|
||||
import * as React from "react";
|
||||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { PublishButton } from "../../../src/renderer/components/commands-publish-button";
|
||||
import { getOctokit } from "../../../src/utils/octokit";
|
||||
import { PublishButton } from '../../../src/renderer/components/commands-publish-button';
|
||||
import { getOctokit } from '../../../src/utils/octokit';
|
||||
|
||||
jest.mock("../../../src/utils/octokit");
|
||||
jest.mock('../../../src/utils/octokit');
|
||||
|
||||
describe("Publish button component", () => {
|
||||
describe('Publish button component', () => {
|
||||
let store: any;
|
||||
|
||||
const expectedGistCreateOpts = {
|
||||
description: "Electron Fiddle Gist",
|
||||
description: 'Electron Fiddle Gist',
|
||||
files: {
|
||||
"index.html": { content: "html-content" },
|
||||
"renderer.js": { content: "renderer-content" },
|
||||
"main.js": { content: "main-content" }
|
||||
'index.html': { content: 'html-content' },
|
||||
'renderer.js': { content: 'renderer-content' },
|
||||
'main.js': { content: 'main-content' },
|
||||
'preload.js': { content: 'preload-content' },
|
||||
},
|
||||
public: true
|
||||
};
|
||||
@@ -25,12 +26,12 @@ describe("Publish button component", () => {
|
||||
};
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<PublishButton appState={store} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("toggles the auth dialog on click if not authed", async () => {
|
||||
it('toggles the auth dialog on click if not authed', async () => {
|
||||
store.toggleAuthDialog = jest.fn();
|
||||
|
||||
const wrapper = shallow(<PublishButton appState={store} />);
|
||||
@@ -40,8 +41,8 @@ describe("Publish button component", () => {
|
||||
expect(store.toggleAuthDialog).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("toggles the publish method on click if authed", async () => {
|
||||
store.gitHubToken = "github-token";
|
||||
it('toggles the publish method on click if authed', async () => {
|
||||
store.gitHubToken = 'github-token';
|
||||
|
||||
const wrapper = shallow(<PublishButton appState={store} />);
|
||||
const instance: PublishButton = wrapper.instance() as any;
|
||||
@@ -51,11 +52,11 @@ describe("Publish button component", () => {
|
||||
expect(instance.publishFiddle).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("attempts to publish to Gist", async () => {
|
||||
it('attempts to publish to Gist', async () => {
|
||||
const mockOctokit = {
|
||||
authenticate: jest.fn(),
|
||||
gists: {
|
||||
create: jest.fn(async () => ({ data: { id: "123" } }))
|
||||
create: jest.fn(async () => ({ data: { id: '123' } }))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -67,21 +68,22 @@ describe("Publish button component", () => {
|
||||
await instance.publishFiddle();
|
||||
|
||||
expect(mockOctokit.gists.create).toHaveBeenCalledWith({
|
||||
description: "Electron Fiddle Gist",
|
||||
description: 'Electron Fiddle Gist',
|
||||
files: {
|
||||
"index.html": { content: "html-content" },
|
||||
"renderer.js": { content: "renderer-content" },
|
||||
"main.js": { content: "main-content" }
|
||||
'index.html': { content: 'html-content' },
|
||||
'renderer.js': { content: 'renderer-content' },
|
||||
'main.js': { content: 'main-content' },
|
||||
'preload.js': { content: 'preload-content' },
|
||||
},
|
||||
public: true
|
||||
});
|
||||
});
|
||||
|
||||
it("handles missing content", async () => {
|
||||
it('handles missing content', async () => {
|
||||
const mockOctokit = {
|
||||
authenticate: jest.fn(),
|
||||
gists: {
|
||||
create: jest.fn(async () => ({ data: { id: "123" } }))
|
||||
create: jest.fn(async () => ({ data: { id: '123' } }))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -95,22 +97,23 @@ describe("Publish button component", () => {
|
||||
await instance.publishFiddle();
|
||||
|
||||
expect(mockOctokit.gists.create).toHaveBeenCalledWith({
|
||||
description: "Electron Fiddle Gist",
|
||||
description: 'Electron Fiddle Gist',
|
||||
files: {
|
||||
"index.html": { content: "<!-- Empty -->" },
|
||||
"renderer.js": { content: "// Empty" },
|
||||
"main.js": { content: "// Empty" }
|
||||
'index.html': { content: '<!-- Empty -->' },
|
||||
'renderer.js': { content: '// Empty' },
|
||||
'main.js': { content: '// Empty' },
|
||||
'preload.js': { content: '// Empty' }
|
||||
},
|
||||
public: true
|
||||
});
|
||||
});
|
||||
|
||||
it("handles an error in Gist publishing", async () => {
|
||||
it('handles an error in Gist publishing', async () => {
|
||||
const mockOctokit = {
|
||||
authenticate: jest.fn(),
|
||||
gists: {
|
||||
create: jest.fn(() => {
|
||||
throw new Error("bwap bwap");
|
||||
throw new Error('bwap bwap');
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -125,12 +128,12 @@ describe("Publish button component", () => {
|
||||
expect(store.isPublishing).toBe(false);
|
||||
});
|
||||
|
||||
it("uses the privacy setting correctly", async () => {
|
||||
it('uses the privacy setting correctly', async () => {
|
||||
const mockOctokit = {
|
||||
authenticate: jest.fn(),
|
||||
gists: {
|
||||
create: jest.fn(() => {
|
||||
throw new Error("bwap bwap");
|
||||
throw new Error('bwap bwap');
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -157,20 +160,20 @@ describe("Publish button component", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("disables during gist publishing", async () => {
|
||||
it('disables during gist publishing', async () => {
|
||||
store.isPublishing = false;
|
||||
const wrapper = shallow(<PublishButton appState={store} />);
|
||||
const instance: PublishButton = wrapper.instance() as any;
|
||||
|
||||
expect(wrapper.find("fieldset").prop("disabled")).toBe(false);
|
||||
expect(wrapper.find('fieldset').prop('disabled')).toBe(false);
|
||||
|
||||
instance.publishFiddle = jest.fn().mockImplementationOnce(() => {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
wrapper.setProps({ appState: { store, isPublishing: true } }, () => {
|
||||
expect(wrapper.find("fieldset").prop("disabled")).toBe(true);
|
||||
expect(wrapper.find('fieldset').prop('disabled')).toBe(true);
|
||||
});
|
||||
wrapper.setProps({ appState: { store, isPublishing: false } }, () => {
|
||||
expect(wrapper.find("fieldset").prop("disabled")).toBe(false);
|
||||
expect(wrapper.find('fieldset').prop('disabled')).toBe(false);
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
@@ -179,8 +182,8 @@ describe("Publish button component", () => {
|
||||
await instance.publishFiddle();
|
||||
});
|
||||
|
||||
describe("privacy menu", () => {
|
||||
it("toggles the privacy setting", () => {
|
||||
describe('privacy menu', () => {
|
||||
it('toggles the privacy setting', () => {
|
||||
const wrapper = shallow(<PublishButton appState={store} />);
|
||||
const instance: PublishButton = wrapper.instance() as any;
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { shallow } from 'enzyme';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState } from '../../../src/interfaces';
|
||||
import { filterItem, getItemIcon, getItemLabel, renderItem, VersionChooser } from '../../../src/renderer/components/commands-version-chooser';
|
||||
import { ElectronVersionSource, ElectronVersionState } from '../../../src/interfaces';
|
||||
import { VersionChooser } from '../../../src/renderer/components/commands-version-chooser';
|
||||
import { ElectronReleaseChannel } from '../../../src/renderer/versions';
|
||||
import { mockVersions } from '../../mocks/electron-versions';
|
||||
|
||||
const { ready, unknown, downloading } = ElectronVersionState;
|
||||
const { remote, local } = ElectronVersionSource;
|
||||
const { unknown } = ElectronVersionState;
|
||||
const { remote } = ElectronVersionSource;
|
||||
|
||||
describe('VersionChooser component', () => {
|
||||
describe('VersionSelect component', () => {
|
||||
let store: any;
|
||||
|
||||
const mockVersion1 = {
|
||||
@@ -25,15 +25,18 @@ describe('VersionChooser component', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
const versions = {
|
||||
...mockVersions,
|
||||
'3.1.3': undefined,
|
||||
'1.0.0': { ...mockVersion1 },
|
||||
'3.0.0-unsupported': { ...mockVersion2 }
|
||||
};
|
||||
|
||||
store = {
|
||||
version: '2.0.2',
|
||||
versions: {
|
||||
...mockVersions,
|
||||
'3.1.3': undefined,
|
||||
'1.0.0': { ...mockVersion1 },
|
||||
'3.0.0-unsupported': { ...mockVersion2 }
|
||||
},
|
||||
versionsToShow: [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta ],
|
||||
versionsToShow: Object.values(versions).filter((v) => !!v),
|
||||
versions,
|
||||
channelsToShow: [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta ],
|
||||
statesToShow: [ ElectronVersionState.ready, ElectronVersionState.downloading ],
|
||||
setVersion: jest.fn(),
|
||||
get currentElectronVersion() {
|
||||
@@ -43,108 +46,19 @@ describe('VersionChooser component', () => {
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<VersionChooser appState={store} />);
|
||||
const wrapper = shallow(
|
||||
<VersionChooser appState={store} />
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('handles a change appropriately', () => {
|
||||
const wrapper = shallow(<VersionChooser appState={store} />);
|
||||
const instance: VersionChooser = wrapper.instance() as any;
|
||||
it('selects a new version', () => {
|
||||
const wrapper = mount(
|
||||
<VersionChooser appState={store} />
|
||||
);
|
||||
|
||||
instance.onItemSelect({ version: 'v2.0.1' } as any);
|
||||
|
||||
expect(store.setVersion).toHaveBeenCalledWith('v2.0.1');
|
||||
});
|
||||
|
||||
it('handles corrupt data', () => {
|
||||
store.version = 'blub';
|
||||
|
||||
const wrapper = shallow(<VersionChooser appState={store} />);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.html()).toContain('Electron v2.0.2');
|
||||
});
|
||||
|
||||
describe('renderItem()', () => {
|
||||
it('renders an item', () => {
|
||||
const item = renderItem(mockVersion1, {
|
||||
handleClick: () => ({}),
|
||||
index: 0,
|
||||
modifiers: { active: true, disabled: false, matchesPredicate: true },
|
||||
query: ''
|
||||
});
|
||||
|
||||
expect(item).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('returns null if it does not match predicate', () => {
|
||||
const item = renderItem(mockVersion1, {
|
||||
handleClick: () => ({}),
|
||||
index: 0,
|
||||
modifiers: { active: true, disabled: false, matchesPredicate: false },
|
||||
query: ''
|
||||
});
|
||||
|
||||
expect(item).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getItemLabel()', () => {
|
||||
it('returns the correct label for a local version', () => {
|
||||
const input: ElectronVersion = {
|
||||
...mockVersion1,
|
||||
source: local,
|
||||
};
|
||||
|
||||
expect(getItemLabel(input)).toBe('Local');
|
||||
expect(getItemLabel({ ...input, name: 'Hi' })).toBe('Hi');
|
||||
});
|
||||
|
||||
it('returns the correct label for a version not downloaded', () => {
|
||||
const input: ElectronVersion = {
|
||||
...mockVersion1,
|
||||
state: unknown
|
||||
};
|
||||
|
||||
expect(getItemLabel(input)).toBe('Not downloaded');
|
||||
});
|
||||
|
||||
it('returns the correct label for a version downloaded', () => {
|
||||
const input: ElectronVersion = {
|
||||
...mockVersion1,
|
||||
state: ready
|
||||
};
|
||||
|
||||
expect(getItemLabel(input)).toBe('Downloaded');
|
||||
});
|
||||
|
||||
it('returns the correct label for a version downloading', () => {
|
||||
const input: ElectronVersion = {
|
||||
...mockVersion1,
|
||||
state: downloading
|
||||
};
|
||||
|
||||
expect(getItemLabel(input)).toBe('Downloading');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getItemIcon()', () => {
|
||||
it('returns the correct icon', () => {
|
||||
const vDownloaded = { ...mockVersion1, state: ready };
|
||||
expect(getItemIcon(vDownloaded)).toBe('saved');
|
||||
|
||||
const vDownloading = { ...mockVersion1, state: downloading };
|
||||
expect(getItemIcon(vDownloading)).toBe('cloud-download');
|
||||
|
||||
const vUnknown = { ...mockVersion1, state: unknown };
|
||||
expect(getItemIcon(vUnknown)).toBe('cloud');
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterItem()', () => {
|
||||
it('correctly matches a query', () => {
|
||||
expect(filterItem('test', mockVersion1)).toBe(false);
|
||||
expect(filterItem('1.0.0', mockVersion1)).toBe(true);
|
||||
});
|
||||
const onVersionSelect: any = wrapper.find('VersionSelect').prop('onVersionSelect');
|
||||
onVersionSelect(mockVersion1);
|
||||
expect(store.setVersion).toHaveBeenCalledWith(mockVersion1.version);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,9 +18,12 @@ describe('BisectDialog component', () => {
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
const versions = generateVersionRange(5);
|
||||
|
||||
store = {
|
||||
versions: generateVersionRange(5),
|
||||
versionsToShow: [ElectronReleaseChannel.stable],
|
||||
versions,
|
||||
versionsToShow: versions,
|
||||
channelsToShow: [ElectronReleaseChannel.stable],
|
||||
statesToShow: [ElectronVersionState.ready],
|
||||
setVersion: jest.fn()
|
||||
};
|
||||
@@ -51,6 +54,18 @@ describe('BisectDialog component', () => {
|
||||
allVersions: generateVersionRange(5)
|
||||
});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
// Incorrect order
|
||||
wrapper.setState({
|
||||
startIndex: 3,
|
||||
endIndex: 4,
|
||||
allVersions: generateVersionRange(5)
|
||||
});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
// Help displayed
|
||||
(wrapper.instance() as any).showHelp();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('onBeginSelect()', () => {
|
||||
@@ -58,7 +73,7 @@ describe('BisectDialog component', () => {
|
||||
const wrapper = shallow(<BisectDialog appState={store} />);
|
||||
const instance: BisectDialog = wrapper.instance() as any;
|
||||
|
||||
expect(instance.state.startIndex).toBeUndefined();
|
||||
expect(instance.state.startIndex).toBe(10);
|
||||
instance.onBeginSelect(store.versions[2]);
|
||||
expect(instance.state.startIndex).toBe(2);
|
||||
});
|
||||
@@ -69,7 +84,7 @@ describe('BisectDialog component', () => {
|
||||
const wrapper = shallow(<BisectDialog appState={store} />);
|
||||
const instance: BisectDialog = wrapper.instance() as any;
|
||||
|
||||
expect(instance.state.endIndex).toBeUndefined();
|
||||
expect(instance.state.endIndex).toBe(0);
|
||||
instance.onEndSelect(store.versions[2]);
|
||||
expect(instance.state.endIndex).toBe(2);
|
||||
});
|
||||
@@ -95,7 +110,7 @@ describe('BisectDialog component', () => {
|
||||
|
||||
const instance: BisectDialog = wrapper.instance() as any;
|
||||
await instance.onSubmit();
|
||||
expect(Bisector).toHaveBeenCalledWith(versions.slice(0, 5).reverse());
|
||||
expect(Bisector).toHaveBeenCalledWith(versions.slice(0, 5));
|
||||
expect(store.Bisector).toBeDefined();
|
||||
expect(store.setVersion).toHaveBeenCalledWith(version);
|
||||
});
|
||||
@@ -115,10 +130,51 @@ describe('BisectDialog component', () => {
|
||||
startIndex: 4,
|
||||
endIndex: undefined
|
||||
});
|
||||
|
||||
const instance2: BisectDialog = wrapper.instance() as any;
|
||||
await instance2.onSubmit();
|
||||
expect(Bisector).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('items disabled', () => {
|
||||
let instance: BisectDialog;
|
||||
|
||||
beforeEach(() => {
|
||||
const wrapper = shallow(<BisectDialog appState={store} />);
|
||||
instance = wrapper.instance() as any;
|
||||
});
|
||||
|
||||
describe('isEarliestItemDisabled', () => {
|
||||
it('enables a version older than the "latest version"', () => {
|
||||
instance.setState({ endIndex: 2 });
|
||||
|
||||
const result = instance.isEarliestItemDisabled(store.versions[3]);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
it('disables a version newer than the "latest version"', () => {
|
||||
instance.setState({ endIndex: 2 });
|
||||
|
||||
const result = instance.isEarliestItemDisabled(store.versions[1]);
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isLatestItemDisabled', () => {
|
||||
it('enables a version newer than the "earliest version"', () => {
|
||||
instance.setState({ startIndex: 2 });
|
||||
|
||||
const result = instance.isLatestItemDisabled(store.versions[1]);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
it('disables a version older than the "earliest version"', () => {
|
||||
instance.setState({ startIndex: 2 });
|
||||
|
||||
const result = instance.isLatestItemDisabled(store.versions[4]);
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { GenericDialogType } from '../../../src/interfaces';
|
||||
import { GenericDialog } from '../../../src/renderer/components/dialog-generic';
|
||||
import { AppState } from '../../../src/renderer/state';
|
||||
import { overridePlatform, resetPlatform } from '../../utils';
|
||||
|
||||
describe('GenericDialog component', () => {
|
||||
// tslint isn't able to parse the casted use below and thinks this is unused
|
||||
// tslint:disable-next-line: prefer-const
|
||||
let store: AppState;
|
||||
|
||||
beforeAll(() => {
|
||||
// We render the buttons different depending on the
|
||||
// platform, so let' have a uniform platform for unit tests
|
||||
overridePlatform('darwin');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
(store as Partial<AppState>) = {
|
||||
isGenericDialogShowing: false,
|
||||
genericDialogOptions: {
|
||||
type: GenericDialogType.warning,
|
||||
label: 'Some message',
|
||||
ok: 'Ok',
|
||||
cancel: ''
|
||||
},
|
||||
toggleGenericDialog: jest.fn()
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
resetPlatform();
|
||||
});
|
||||
|
||||
it('renders a warning', () => {
|
||||
store.isGenericDialogShowing = true;
|
||||
const wrapper = shallow(<GenericDialog appState={store} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders a confirmation', () => {
|
||||
store.genericDialogOptions.type = GenericDialogType.confirm;
|
||||
store.isGenericDialogShowing = true;
|
||||
const wrapper = shallow(<GenericDialog appState={store} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders a success message', () => {
|
||||
store.genericDialogOptions.type = GenericDialogType.success;
|
||||
store.isGenericDialogShowing = true;
|
||||
const wrapper = shallow(<GenericDialog appState={store} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('onClose() closes itself', () => {
|
||||
store.isGenericDialogShowing = true;
|
||||
const wrapper = shallow(<GenericDialog appState={store} />);
|
||||
const instance: GenericDialog = wrapper.instance() as any;
|
||||
|
||||
instance.onClose(true);
|
||||
expect(store.toggleGenericDialog).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { WarningDialog } from '../../../src/renderer/components/dialog-warning';
|
||||
import { overridePlatform, resetPlatform } from '../../utils';
|
||||
|
||||
describe('TokenDialog component', () => {
|
||||
let store: any;
|
||||
|
||||
beforeAll(() => {
|
||||
// We render the buttons different depending on the
|
||||
// platform, so let' have a uniform platform for unit tests
|
||||
overridePlatform('darwin');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
store = {
|
||||
isWarningDialogShowing: false,
|
||||
warningDialogTexts: { label: '', ok: '', cancel: '' },
|
||||
toggleWarningDialog: jest.fn()
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
resetPlatform();
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
store.isWarningDialogShowing = true;
|
||||
const wrapper = shallow(<WarningDialog appState={store} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('onClose() closes itself', () => {
|
||||
store.isWarningDialogShowing = true;
|
||||
const wrapper = shallow(<WarningDialog appState={store} />);
|
||||
const instance: WarningDialog = wrapper.instance() as any;
|
||||
|
||||
instance.onClose(true);
|
||||
expect(store.toggleWarningDialog).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -1,11 +1,15 @@
|
||||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { GenericDialogType } from '../../../src/interfaces';
|
||||
import { Dialogs } from '../../../src/renderer/components/dialogs';
|
||||
import { AppState } from '../../../src/renderer/state';
|
||||
import { overridePlatform, resetPlatform } from '../../utils';
|
||||
|
||||
describe('Dialogs component', () => {
|
||||
let store: any = {};
|
||||
// tslint isn't able to parse the casted use below and thinks this is unused
|
||||
// tslint:disable-next-line: prefer-const
|
||||
let store: AppState;
|
||||
|
||||
beforeAll(() => {
|
||||
// We render the buttons different depending on the
|
||||
@@ -14,11 +18,12 @@ describe('Dialogs component', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
store = {
|
||||
(store as Partial<AppState>) = {
|
||||
isTokenDialogShowing: false,
|
||||
isSettingsShowing: false,
|
||||
isAddVersionDialogShowing: false,
|
||||
warningDialogTexts: { label: '', ok: '', cancel: '' }
|
||||
genericDialogOptions: { type: GenericDialogType.confirm, label: '', ok: '', cancel: '' },
|
||||
isGenericDialogShowing: true
|
||||
};
|
||||
});
|
||||
|
||||
@@ -29,18 +34,18 @@ describe('Dialogs component', () => {
|
||||
it('renders the token dialog', () => {
|
||||
store.isTokenDialogShowing = true;
|
||||
const wrapper = shallow(<Dialogs appState={store} />);
|
||||
expect(wrapper.text()).toBe('<TokenDialog /><ConfirmDialog />');
|
||||
expect(wrapper.text()).toBe('<TokenDialog /><GenericDialog />');
|
||||
});
|
||||
|
||||
it('renders the settings dialog', () => {
|
||||
store.isSettingsShowing = true;
|
||||
const wrapper = shallow(<Dialogs appState={store} />);
|
||||
expect(wrapper.text()).toBe('<Settings /><ConfirmDialog />');
|
||||
expect(wrapper.text()).toBe('<Settings /><GenericDialog />');
|
||||
});
|
||||
|
||||
it('renders the settings dialog', () => {
|
||||
store.isAddVersionDialogShowing = true;
|
||||
const wrapper = shallow(<Dialogs appState={store} />);
|
||||
expect(wrapper.text()).toBe('<AddVersionDialog /><ConfirmDialog />');
|
||||
expect(wrapper.text()).toBe('<AddVersionDialog /><GenericDialog />');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ describe('Editor component', () => {
|
||||
let monaco: any;
|
||||
const editorDispose = jest.fn();
|
||||
const updateOptions = jest.fn();
|
||||
const onDidFocusEditorText = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
store = {
|
||||
@@ -24,8 +25,9 @@ describe('Editor component', () => {
|
||||
editor: {
|
||||
create: jest.fn(() => ({
|
||||
dispose: editorDispose,
|
||||
onDidFocusEditorText,
|
||||
setModel: jest.fn(),
|
||||
restoreViewState: jest.fn()
|
||||
restoreViewState: jest.fn(),
|
||||
})),
|
||||
createModel: jest.fn(() => ({
|
||||
updateOptions
|
||||
@@ -38,7 +40,7 @@ describe('Editor component', () => {
|
||||
|
||||
it('renders the editor container', () => {
|
||||
const wrapper = shallow(
|
||||
<Editor appState={store} monaco={monaco} monacoOptions={{}} id={EditorId.main} />
|
||||
<Editor appState={store} monaco={monaco} monacoOptions={{}} id={EditorId.main} setFocused={() => undefined} />
|
||||
);
|
||||
|
||||
expect(wrapper.html()).toBe('<div class="editorContainer"></div>');
|
||||
@@ -46,13 +48,13 @@ describe('Editor component', () => {
|
||||
|
||||
it('correctly sets the language', () => {
|
||||
let wrapper = shallow(
|
||||
<Editor appState={store} monaco={monaco} monacoOptions={{}} id={EditorId.main} />
|
||||
<Editor appState={store} monaco={monaco} monacoOptions={{}} id={EditorId.main} setFocused={() => undefined} />
|
||||
);
|
||||
|
||||
expect((wrapper.instance() as any).language).toBe('javascript');
|
||||
|
||||
wrapper = shallow(
|
||||
<Editor appState={store} monaco={monaco} monacoOptions={{}} id={EditorId.html} />
|
||||
<Editor appState={store} monaco={monaco} monacoOptions={{}} id={EditorId.html} setFocused={() => undefined} />
|
||||
);
|
||||
|
||||
expect((wrapper.instance() as any).language).toBe('html');
|
||||
@@ -60,7 +62,7 @@ describe('Editor component', () => {
|
||||
|
||||
it('denies updates', () => {
|
||||
const wrapper = shallow(
|
||||
<Editor appState={store} monaco={monaco} monacoOptions={{}} id={EditorId.main} />
|
||||
<Editor appState={store} monaco={monaco} monacoOptions={{}} id={EditorId.main} setFocused={() => undefined} />
|
||||
);
|
||||
|
||||
expect((wrapper as any)
|
||||
@@ -79,6 +81,7 @@ describe('Editor component', () => {
|
||||
monacoOptions={{}}
|
||||
id={EditorId.main}
|
||||
editorDidMount={didMount}
|
||||
setFocused={() => undefined}
|
||||
/>
|
||||
);
|
||||
const instance: any = wrapper.instance();
|
||||
@@ -104,6 +107,7 @@ describe('Editor component', () => {
|
||||
monacoOptions={{}}
|
||||
id={EditorId.main}
|
||||
editorDidMount={() => undefined}
|
||||
setFocused={() => undefined}
|
||||
/>
|
||||
);
|
||||
const instance: any = wrapper.instance();
|
||||
@@ -115,6 +119,31 @@ describe('Editor component', () => {
|
||||
expect(instance.editor.setModel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('attempts to restore a backup value if available', async () => {
|
||||
store.getAndRemoveEditorValueBackup.mockReturnValueOnce({
|
||||
value: 'hello'
|
||||
});
|
||||
|
||||
const wrapper = shallow(
|
||||
<Editor
|
||||
appState={store}
|
||||
monaco={monaco}
|
||||
monacoOptions={{}}
|
||||
id={EditorId.main}
|
||||
editorDidMount={() => undefined}
|
||||
setFocused={() => undefined}
|
||||
/>
|
||||
);
|
||||
const instance: any = wrapper.instance();
|
||||
|
||||
instance.containerRef.current = 'ref';
|
||||
await instance.initMonaco();
|
||||
|
||||
expect(instance.editor.restoreViewState).toHaveBeenCalledTimes(0);
|
||||
expect(instance.editor.setModel).toHaveBeenCalledTimes(1);
|
||||
expect(monaco.editor.createModel).toHaveBeenCalledWith('hello', 'javascript');
|
||||
});
|
||||
|
||||
it('initializes with a fixed tab size', async () => {
|
||||
const didMount = jest.fn();
|
||||
const wrapper = shallow(
|
||||
@@ -124,6 +153,7 @@ describe('Editor component', () => {
|
||||
monacoOptions={{}}
|
||||
id={EditorId.main}
|
||||
editorDidMount={didMount}
|
||||
setFocused={() => undefined}
|
||||
/>
|
||||
);
|
||||
const instance: any = wrapper.instance();
|
||||
@@ -135,6 +165,24 @@ describe('Editor component', () => {
|
||||
tabSize: 2
|
||||
}));
|
||||
});
|
||||
|
||||
it('sets up a listener on focused text editor', async () => {
|
||||
const wrapper = shallow(
|
||||
<Editor
|
||||
appState={store}
|
||||
monaco={monaco}
|
||||
monacoOptions={{}}
|
||||
id={EditorId.main}
|
||||
editorDidMount={() => undefined}
|
||||
setFocused={() => undefined}
|
||||
/>
|
||||
);
|
||||
const instance: any = wrapper.instance();
|
||||
|
||||
instance.containerRef.current = 'ref';
|
||||
await instance.initMonaco();
|
||||
expect(onDidFocusEditorText).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('componentWillUnmount() attempts to dispose the editor', async () => {
|
||||
@@ -146,6 +194,7 @@ describe('Editor component', () => {
|
||||
monacoOptions={{}}
|
||||
id={EditorId.main}
|
||||
editorDidMount={didMount}
|
||||
setFocused={() => undefined}
|
||||
/>
|
||||
);
|
||||
const instance: any = wrapper.instance();
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('Editors component', () => {
|
||||
store = {
|
||||
isTokenDialogShowing: false,
|
||||
isSettingsShowing: false,
|
||||
setWarningDialogTexts: () => ({}),
|
||||
setGenericDialogOptions: () => ({}),
|
||||
mosaicArrangement: createMosaicArrangement(ALL_MOSAICS),
|
||||
currentDocsDemoPage: DocsDemoPage.DEFAULT
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ describe('ElectronSettings component', () => {
|
||||
store = {
|
||||
version: '2.0.1',
|
||||
versions: { ...mockVersions },
|
||||
versionsToShow: [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta ],
|
||||
channelsToShow: [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta ],
|
||||
statesToShow: [ ElectronVersionState.ready, ElectronVersionState.downloading ],
|
||||
downloadVersion: jest.fn(),
|
||||
removeVersion: jest.fn(),
|
||||
@@ -178,7 +178,7 @@ describe('ElectronSettings component', () => {
|
||||
}
|
||||
});
|
||||
|
||||
expect(store.versionsToShow).toEqual([
|
||||
expect(store.channelsToShow).toEqual([
|
||||
ElectronReleaseChannel.beta,
|
||||
ElectronReleaseChannel.nightly
|
||||
]);
|
||||
|
||||
@@ -56,4 +56,27 @@ describe('ExecutionSettings component', () => {
|
||||
expect(store.isEnablingElectronLogging).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleExecutionFlagChange()', () => {
|
||||
it('handles new flags', async () => {
|
||||
const wrapper = shallow(
|
||||
<ExecutionSettings appState={store} />
|
||||
);
|
||||
const instance = wrapper.instance() as any;
|
||||
await instance.handleExecutionFlagChange({
|
||||
currentTarget: { value: '--lang=es' }
|
||||
});
|
||||
|
||||
expect(store.executionFlags).toEqual(['--lang=es']);
|
||||
|
||||
await instance.handleExecutionFlagChange({
|
||||
currentTarget: { value: '--lang=es|--js-flags=--expose-gc' }
|
||||
});
|
||||
|
||||
expect(store.executionFlags).toEqual([
|
||||
'--lang=es',
|
||||
'--js-flags=--expose-gc'
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState } from '../../../src/interfaces';
|
||||
import { filterItem, getItemIcon, getItemLabel, renderItem, VersionSelect } from '../../../src/renderer/components/version-select';
|
||||
import { ElectronReleaseChannel } from '../../../src/renderer/versions';
|
||||
import { mockVersions } from '../../mocks/electron-versions';
|
||||
|
||||
const { ready, unknown, downloading } = ElectronVersionState;
|
||||
const { remote, local } = ElectronVersionSource;
|
||||
|
||||
describe('VersionSelect component', () => {
|
||||
let store: any;
|
||||
|
||||
const mockVersion1 = {
|
||||
source: remote,
|
||||
state: unknown,
|
||||
version: '1.0.0'
|
||||
};
|
||||
|
||||
const mockVersion2 = {
|
||||
source: remote,
|
||||
state: unknown,
|
||||
version: '3.0.0-unsupported'
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
store = {
|
||||
version: '2.0.2',
|
||||
versions: {
|
||||
...mockVersions,
|
||||
'3.1.3': undefined,
|
||||
'1.0.0': { ...mockVersion1 },
|
||||
'3.0.0-unsupported': { ...mockVersion2 }
|
||||
},
|
||||
channelsToShow: [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta ],
|
||||
statesToShow: [ ElectronVersionState.ready, ElectronVersionState.downloading ],
|
||||
setVersion: jest.fn(),
|
||||
get currentElectronVersion() {
|
||||
return mockVersions['2.0.2'];
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const onVersionSelect = () => ({});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(
|
||||
<VersionSelect appState={store} currentVersion={mockVersion1} onVersionSelect={onVersionSelect} />
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('renderItem()', () => {
|
||||
it('renders an item', () => {
|
||||
const item = renderItem(mockVersion1, {
|
||||
handleClick: () => ({}),
|
||||
index: 0,
|
||||
modifiers: { active: true, disabled: false, matchesPredicate: true },
|
||||
query: ''
|
||||
});
|
||||
|
||||
expect(item).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('returns null if it does not match predicate', () => {
|
||||
const item = renderItem(mockVersion1, {
|
||||
handleClick: () => ({}),
|
||||
index: 0,
|
||||
modifiers: { active: true, disabled: false, matchesPredicate: false },
|
||||
query: ''
|
||||
});
|
||||
|
||||
expect(item).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getItemLabel()', () => {
|
||||
it('returns the correct label for a local version', () => {
|
||||
const input: ElectronVersion = {
|
||||
...mockVersion1,
|
||||
source: local,
|
||||
};
|
||||
|
||||
expect(getItemLabel(input)).toBe('Local');
|
||||
expect(getItemLabel({ ...input, name: 'Hi' })).toBe('Hi');
|
||||
});
|
||||
|
||||
it('returns the correct label for a version not downloaded', () => {
|
||||
const input: ElectronVersion = {
|
||||
...mockVersion1,
|
||||
state: unknown
|
||||
};
|
||||
|
||||
expect(getItemLabel(input)).toBe('Not downloaded');
|
||||
});
|
||||
|
||||
it('returns the correct label for a version downloaded', () => {
|
||||
const input: ElectronVersion = {
|
||||
...mockVersion1,
|
||||
state: ready
|
||||
};
|
||||
|
||||
expect(getItemLabel(input)).toBe('Downloaded');
|
||||
});
|
||||
|
||||
it('returns the correct label for a version downloading', () => {
|
||||
const input: ElectronVersion = {
|
||||
...mockVersion1,
|
||||
state: downloading
|
||||
};
|
||||
|
||||
expect(getItemLabel(input)).toBe('Downloading');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getItemIcon()', () => {
|
||||
it('returns the correct icon', () => {
|
||||
const vDownloaded = { ...mockVersion1, state: ready };
|
||||
expect(getItemIcon(vDownloaded)).toBe('saved');
|
||||
|
||||
const vDownloading = { ...mockVersion1, state: downloading };
|
||||
expect(getItemIcon(vDownloading)).toBe('cloud-download');
|
||||
|
||||
const vUnknown = { ...mockVersion1, state: unknown };
|
||||
expect(getItemIcon(vUnknown)).toBe('cloud');
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterItem()', () => {
|
||||
it('correctly matches a query', () => {
|
||||
expect(filterItem('test', mockVersion1)).toBe(false);
|
||||
expect(filterItem('1.0.0', mockVersion1)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -32,7 +32,7 @@ describe('FileManager', () => {
|
||||
ipcRendererManager.send = jest.fn();
|
||||
|
||||
fm = new FileManager({
|
||||
setWarningDialogTexts: jest.fn()
|
||||
setGenericDialogOptions: jest.fn()
|
||||
} as any);
|
||||
});
|
||||
|
||||
@@ -87,7 +87,7 @@ describe('FileManager', () => {
|
||||
|
||||
await fm.saveFiddle('/fake/path');
|
||||
|
||||
expect(fs.outputFile).toHaveBeenCalledTimes(3);
|
||||
expect(fs.outputFile).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
it('removes a file that is newly empty', async () => {
|
||||
@@ -95,7 +95,7 @@ describe('FileManager', () => {
|
||||
|
||||
await fm.saveFiddle('/fake/path');
|
||||
|
||||
expect(fs.remove).toHaveBeenCalledTimes(2);
|
||||
expect(fs.remove).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('handles an error (output)', async () => {
|
||||
@@ -106,8 +106,8 @@ describe('FileManager', () => {
|
||||
|
||||
await fm.saveFiddle('/fake/path');
|
||||
|
||||
expect(fs.outputFile).toHaveBeenCalledTimes(3);
|
||||
expect(ipcRendererManager.send).toHaveBeenCalledTimes(3);
|
||||
expect(fs.outputFile).toHaveBeenCalledTimes(4);
|
||||
expect(ipcRendererManager.send).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
it('handles an error (remove)', async () => {
|
||||
@@ -118,8 +118,8 @@ describe('FileManager', () => {
|
||||
|
||||
await fm.saveFiddle('/fake/path');
|
||||
|
||||
expect(fs.remove).toHaveBeenCalledTimes(2);
|
||||
expect(ipcRendererManager.send).toHaveBeenCalledTimes(2);
|
||||
expect(fs.remove).toHaveBeenCalledTimes(1);
|
||||
expect(ipcRendererManager.send).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('runs saveFiddle (normal) on IPC event', () => {
|
||||
|
||||
@@ -20,6 +20,7 @@ describe('npm', () => {
|
||||
|
||||
function hello() {
|
||||
const electron = require('electron');
|
||||
const originalFs = require('original-fs');
|
||||
const fs = require('fs');
|
||||
const privateModule = require('./hi');
|
||||
}
|
||||
@@ -81,7 +82,7 @@ describe('npm', () => {
|
||||
});
|
||||
|
||||
describe('findModulesInEditors()', () => {
|
||||
it('finds modules', async () => {
|
||||
it('finds modules, ignoring node and electron builtins', async () => {
|
||||
const result = await findModulesInEditors({
|
||||
html: '',
|
||||
main: mockMain,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { observable } from 'mobx';
|
||||
import { GenericDialogType } from '../../src/interfaces';
|
||||
import { ipcRendererManager } from '../../src/renderer/ipc';
|
||||
import { RemoteLoader } from '../../src/renderer/remote-loader';
|
||||
import { ElectronReleaseChannel } from '../../src/renderer/versions';
|
||||
@@ -54,12 +55,9 @@ const mockGetRepos = {
|
||||
};
|
||||
|
||||
class MockStore {
|
||||
@observable public isWarningDialogShowing: boolean = false;
|
||||
@observable public isConfirmationPromptShowing: boolean = false;
|
||||
public setWarningDialogTexts = jest.fn();
|
||||
public toggleWarningDialog = jest.fn();
|
||||
public setConfirmationDialogTexts = jest.fn();
|
||||
public setConfirmationPromptTexts = jest.fn();
|
||||
@observable public isGenericDialogShowing: boolean = false;
|
||||
public setGenericDialogOptions = jest.fn();
|
||||
public toggleGenericDialog = jest.fn();
|
||||
public versions = {
|
||||
'4.0.0': {
|
||||
version: '4.0.0'
|
||||
@@ -68,7 +66,7 @@ class MockStore {
|
||||
version: '4.0.0-beta'
|
||||
}
|
||||
};
|
||||
public versionsToShow = [ElectronReleaseChannel.stable];
|
||||
public channelsToShow = [ElectronReleaseChannel.stable];
|
||||
public setVersion = jest.fn();
|
||||
}
|
||||
|
||||
@@ -169,7 +167,7 @@ describe('RemoteLoader', () => {
|
||||
|
||||
const result = await instance.fetchExampleAndLoad('4.0.0', 'test/path');
|
||||
expect(result).toBe(false);
|
||||
expect(store.setWarningDialogTexts.mock.calls[0][0].label).toEqual(
|
||||
expect(store.setGenericDialogOptions.mock.calls[0][0].label).toEqual(
|
||||
'Loading the fiddle failed: Error: The example Fiddle tried to launch is not a valid Electron example'
|
||||
);
|
||||
});
|
||||
@@ -190,7 +188,7 @@ describe('RemoteLoader', () => {
|
||||
|
||||
const result = await instance.setElectronVersionWithRef('4.0.0-beta');
|
||||
expect(result).toBe(true);
|
||||
expect(store.versionsToShow).toContain(ElectronReleaseChannel.beta);
|
||||
expect(store.channelsToShow).toContain(ElectronReleaseChannel.beta);
|
||||
});
|
||||
|
||||
it('does not load unsupported versions of Fiddle', async () => {
|
||||
@@ -198,7 +196,8 @@ describe('RemoteLoader', () => {
|
||||
|
||||
const result = await instance.setElectronVersionWithRef('5.0.0');
|
||||
expect(result).toBe(false);
|
||||
expect(store.setWarningDialogTexts).toBeCalledWith({
|
||||
expect(store.setGenericDialogOptions).toBeCalledWith({
|
||||
type: GenericDialogType.warning,
|
||||
label: 'Loading the fiddle failed: Error: Version of Electron in example not supported',
|
||||
cancel: undefined
|
||||
});
|
||||
@@ -226,16 +225,16 @@ describe('RemoteLoader', () => {
|
||||
describe('verifyRemoteLoad()', () => {
|
||||
it('asks the user if they want to load remote content', (done) => {
|
||||
instance.verifyRemoteLoad('test').then(done);
|
||||
expect(store.isConfirmationPromptShowing).toBe(true);
|
||||
store.isConfirmationPromptShowing = false;
|
||||
expect(store.isGenericDialogShowing).toBe(true);
|
||||
store.isGenericDialogShowing = false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyReleaseChannelEnabled', () => {
|
||||
it('asks the user if they want to enable a release channel', (done) => {
|
||||
instance.verifyReleaseChannelEnabled(ElectronReleaseChannel.beta).then(done);
|
||||
expect(store.isWarningDialogShowing).toBe(true);
|
||||
store.isWarningDialogShowing = false;
|
||||
expect(store.isGenericDialogShowing).toBe(true);
|
||||
store.isGenericDialogShowing = false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -249,7 +248,8 @@ describe('RemoteLoader', () => {
|
||||
{ path: 'test/path', ref: '4.0.0' }
|
||||
);
|
||||
|
||||
expect(instance.verifyRemoteLoad).toHaveBeenCalled();
|
||||
expect(instance.verifyRemoteLoad)
|
||||
.toHaveBeenCalledWith(`'test/path' example from the Electron docs for version 4.0.0`);
|
||||
expect(instance.fetchExampleAndLoad).toHaveBeenCalledWith('4.0.0', 'test/path');
|
||||
});
|
||||
|
||||
@@ -276,7 +276,7 @@ describe('RemoteLoader', () => {
|
||||
{ id: 'gist' }
|
||||
);
|
||||
|
||||
expect(instance.verifyRemoteLoad).toHaveBeenCalled();
|
||||
expect(instance.verifyRemoteLoad).toHaveBeenCalledWith('gist');
|
||||
expect(instance.fetchGistAndLoad).toHaveBeenCalledWith('gist');
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ALL_MOSAICS, EditorId, ElectronVersionSource, ElectronVersionState, PanelId } from '../../src/interfaces';
|
||||
import { ALL_MOSAICS, EditorId, ElectronVersionSource, ElectronVersionState, GenericDialogType, 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';
|
||||
@@ -10,6 +10,7 @@ import { getName } from '../../src/utils/get-title';
|
||||
import { mockVersions } from '../mocks/electron-versions';
|
||||
import { overridePlatform, resetPlatform } from '../utils';
|
||||
|
||||
|
||||
jest.mock('../../src/renderer/content', () => ({
|
||||
isContentUnchanged: jest.fn(),
|
||||
getContent: jest.fn()
|
||||
@@ -20,19 +21,24 @@ jest.mock('../../src/renderer/binary', () => ({
|
||||
jest.mock('../../src/renderer/fetch-types', () => ({
|
||||
updateEditorTypeDefinitions: jest.fn()
|
||||
}));
|
||||
jest.mock('../../src/renderer/versions', () => ({
|
||||
getUpdatedElectronVersions: jest.fn().mockImplementation(async () => {
|
||||
return require('../mocks/electron-versions').mockVersionsArray;
|
||||
}),
|
||||
getElectronVersions: () => require('../mocks/electron-versions').mockVersionsArray,
|
||||
getDefaultVersion: () => '2.0.2',
|
||||
ElectronReleaseChannel: {
|
||||
stable: 'Stable',
|
||||
beta: 'Beta'
|
||||
},
|
||||
addLocalVersion: jest.fn(),
|
||||
saveLocalVersions: jest.fn()
|
||||
}));
|
||||
jest.mock('../../src/renderer/versions', () => {
|
||||
const { getReleaseChannel } = jest.requireActual('../../src/renderer/versions');
|
||||
|
||||
return {
|
||||
getUpdatedElectronVersions: jest.fn().mockImplementation(async () => {
|
||||
return require('../mocks/electron-versions').mockVersionsArray;
|
||||
}),
|
||||
getElectronVersions: () => require('../mocks/electron-versions').mockVersionsArray,
|
||||
getDefaultVersion: () => '2.0.2',
|
||||
ElectronReleaseChannel: {
|
||||
stable: 'Stable',
|
||||
beta: 'Beta'
|
||||
},
|
||||
addLocalVersion: jest.fn(),
|
||||
saveLocalVersions: jest.fn(),
|
||||
getReleaseChannel
|
||||
};
|
||||
});
|
||||
jest.mock('../../src/utils/get-title', () => ({
|
||||
getName: jest.fn()
|
||||
}));
|
||||
@@ -57,10 +63,10 @@ describe('AppState', () => {
|
||||
|
||||
const result = window.onbeforeunload!(undefined as any);
|
||||
expect(result).toBe(false);
|
||||
expect(appState.isWarningDialogShowing).toBe(true);
|
||||
expect(appState.isGenericDialogShowing).toBe(true);
|
||||
|
||||
appState.warningDialogLastResult = true;
|
||||
appState.isWarningDialogShowing = false;
|
||||
appState.genericDialogLastResult = true;
|
||||
appState.isGenericDialogShowing = false;
|
||||
process.nextTick(() => {
|
||||
expect(window.close).toHaveBeenCalled();
|
||||
done();
|
||||
@@ -76,10 +82,10 @@ describe('AppState', () => {
|
||||
|
||||
const result = window.onbeforeunload!(undefined as any);
|
||||
expect(result).toBe(false);
|
||||
expect(appState.isWarningDialogShowing).toBe(true);
|
||||
expect(appState.isGenericDialogShowing).toBe(true);
|
||||
|
||||
appState.warningDialogLastResult = true;
|
||||
appState.isWarningDialogShowing = false;
|
||||
appState.genericDialogLastResult = true;
|
||||
appState.isGenericDialogShowing = false;
|
||||
process.nextTick(() => {
|
||||
expect(window.close).toHaveBeenCalledTimes(0);
|
||||
expect(remote.app.quit).toHaveBeenCalledTimes(1);
|
||||
@@ -94,10 +100,10 @@ describe('AppState', () => {
|
||||
|
||||
const result = window.onbeforeunload!(undefined as any);
|
||||
expect(result).toBe(false);
|
||||
expect(appState.isWarningDialogShowing).toBe(true);
|
||||
expect(appState.isGenericDialogShowing).toBe(true);
|
||||
|
||||
appState.warningDialogLastResult = false;
|
||||
appState.isWarningDialogShowing = false;
|
||||
appState.genericDialogLastResult = false;
|
||||
appState.isGenericDialogShowing = false;
|
||||
process.nextTick(() => {
|
||||
expect(window.close).toHaveBeenCalledTimes(0);
|
||||
done();
|
||||
@@ -216,16 +222,16 @@ describe('AppState', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleWarningDialog()', () => {
|
||||
describe('toggleGenericDialog()', () => {
|
||||
it('toggles the warnign dialog', () => {
|
||||
appState.warningDialogLastResult = true;
|
||||
appState.genericDialogLastResult = true;
|
||||
|
||||
appState.toggleWarningDialog();
|
||||
expect(appState.isWarningDialogShowing).toBe(true);
|
||||
expect(appState.warningDialogLastResult).toBe(null);
|
||||
appState.toggleGenericDialog();
|
||||
expect(appState.isGenericDialogShowing).toBe(true);
|
||||
expect(appState.genericDialogLastResult).toBe(null);
|
||||
|
||||
appState.toggleWarningDialog();
|
||||
expect(appState.isWarningDialogShowing).toBe(false);
|
||||
appState.toggleGenericDialog();
|
||||
expect(appState.isGenericDialogShowing).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -245,6 +251,33 @@ describe('AppState', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('versionsToShow()', () => {
|
||||
it('returns all versions for no filters', () => {
|
||||
expect(appState.versionsToShow.length).toEqual(Object.keys(appState.versions).length);
|
||||
});
|
||||
|
||||
it('handles empty items', () => {
|
||||
const originalLength = Object.keys(appState.versions).length;
|
||||
appState.versions.foo = undefined as any;
|
||||
|
||||
expect(appState.versionsToShow.length).toEqual(originalLength);
|
||||
});
|
||||
|
||||
it('excludes channels', () => {
|
||||
appState.channelsToShow = [ 'Unsupported' as any ];
|
||||
expect(appState.versionsToShow.length).toEqual(0);
|
||||
appState.channelsToShow = [ 'Stable' as any ];
|
||||
expect(appState.versionsToShow.length).toEqual(3);
|
||||
});
|
||||
|
||||
it('excludes states', () => {
|
||||
appState.statesToShow = [ ElectronVersionState.downloading ];
|
||||
expect(appState.versionsToShow.length).toEqual(0);
|
||||
appState.statesToShow = [ ElectronVersionState.ready ];
|
||||
expect(appState.versionsToShow.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeVersion()', () => {
|
||||
it('removes a version', async () => {
|
||||
appState.versions['2.0.2'].state = ElectronVersionState.ready;
|
||||
@@ -344,10 +377,11 @@ describe('AppState', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('setWarningDialogTexts()', () => {
|
||||
it('sets the warning dialog texts', () => {
|
||||
appState.setWarningDialogTexts({ label: 'foo' });
|
||||
expect(appState.warningDialogTexts).toEqual({
|
||||
describe('setGenericDialogOptions()', () => {
|
||||
it('sets the warning dialog options', () => {
|
||||
appState.setGenericDialogOptions({ type: GenericDialogType.warning, label: 'foo' });
|
||||
expect(appState.genericDialogOptions).toEqual({
|
||||
type: GenericDialogType.warning,
|
||||
label: 'foo',
|
||||
ok: 'Okay',
|
||||
cancel: 'Cancel'
|
||||
|
||||
@@ -86,9 +86,9 @@ describe('TouchBarManager', () => {
|
||||
const { click } = touchBarMgr.getVersionSelectorEscButtonOptions();
|
||||
|
||||
touchBarMgr.selectedVersion = '3.3.3';
|
||||
expect(appState.version).toBe('5.0.0-beta.7');
|
||||
expect(appState.version).toBe('7.1.8');
|
||||
click!();
|
||||
expect(appState.version).toBe('5.0.0-beta.7');
|
||||
expect(appState.version).toBe('7.1.8');
|
||||
});
|
||||
|
||||
it('version scrubber "select" method goes from index to version', () => {
|
||||
|
||||
@@ -178,13 +178,13 @@ describe('versions', () => {
|
||||
it('falls back to a local require', () => {
|
||||
(window as any).localStorage.getItem.mockReturnValueOnce(`garbage`);
|
||||
|
||||
expect(getKnownVersions().length).toBe(201);
|
||||
expect(getKnownVersions().length).toBe(299);
|
||||
});
|
||||
|
||||
it('falls back to a local require', () => {
|
||||
(window as any).localStorage.getItem.mockReturnValueOnce(`[{ "garbage": "true" }]`);
|
||||
|
||||
expect(getKnownVersions().length).toBe(201);
|
||||
expect(getKnownVersions().length).toBe(299);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ const CONTRIBUTORS_WHITELIST = [
|
||||
'ada-lovecraft',
|
||||
'ajphukan',
|
||||
'deermichel',
|
||||
'erickzhao'
|
||||
'erickzhao',
|
||||
'issacgerges'
|
||||
]
|
||||
|
||||
async function maybeFetchContributors () {
|
||||
|
||||
@@ -25,10 +25,8 @@
|
||||
"no-reference": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-var-keyword": true,
|
||||
"no-var-requires": false,
|
||||
"prefer-const": true,
|
||||
"interface-name": [
|
||||
false
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user