Compare commits

..
Author SHA1 Message Date
Felix Rieseberg 0a423bc294 feat: Add progress to settings page, too 2020-03-24 11:40:09 -07:00
58 changed files with 22225 additions and 13063 deletions
-210
View File
@@ -1,210 +0,0 @@
step-restore-cache: &step-restore-cache
restore_cache:
keys:
- v1-modules-{{ arch }}-{{ checksum "yarn.lock" }}
- v1-modules-{{ arch }}
step-install: &step-install
run: npx yarn --frozen-lockfile && rm -rf node_modules/electron/dist
step-save-cache: &step-save-cache
save_cache:
paths:
- node_modules
key: v1-modules-{{ arch }}-{{ checksum "yarn.lock" }}
step-install-electron: &step-install-electron
run: cd node_modules/electron && node install.js
steps-test: &steps-test
steps:
- checkout
- *step-restore-cache
- *step-install
- *step-save-cache
- *step-install-electron
- run: yarn lint
- run: yarn test --maxWorkers=4
steps-build: &steps-build
steps:
- checkout
- *step-restore-cache
- *step-install
- *step-save-cache
- *step-install-electron
- when:
condition: <<parameters.on_linux>>
steps:
- run:
name: Install Linux Dependencies
command: sudo apt install -y rpm fakeroot
- when:
condition: <<parameters.on_mac>>
steps:
- run:
name: Import macOS Certificate
command: .circleci/import-macos-cert.sh
- when:
condition: <<parameters.on_win>>
steps:
- run:
name: Import Windows Certificate
command: |
if (Test-Path Env:\WINDOWS_CERTIFICATE_P12) {
$workingDirectory = Convert-Path (Resolve-Path -path ".")
$filename = "$workingDirectory\cert.p12"
$bytes = [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_P12)
[IO.File]::WriteAllBytes($filename, $bytes)
}
shell: powershell.exe
- run:
name: Publish dry run
command: yarn run publish --dry-run --arch=$TARGET_ARCH
- store_artifacts:
path: out/make
destination: dist
- persist_to_workspace:
root: .
paths:
- out/make
- out/publish-dry-run
version: 2.1
orbs:
win: circleci/windows@1.0.0
jobs:
test-linux:
docker:
- image: circleci/node:10
<<: *steps-test
build-linux:
docker:
- image: circleci/node:10-buster
environment:
TARGET_ARCH: x64
parameters:
on_linux:
type: boolean
default: true
on_mac:
type: boolean
default: false
on_win:
type: boolean
default: false
<<: *steps-build
test-mac:
macos:
xcode: "10.2.0"
<<: *steps-test
build-mac:
macos:
xcode: "10.2.0"
environment:
TARGET_ARCH: x64
parameters:
on_linux:
type: boolean
default: false
on_mac:
type: boolean
default: true
on_win:
type: boolean
default: false
<<: *steps-build
test-windows:
executor:
name: win/vs2019
shell: bash.exe
<<: *steps-test
build-windows-ia32:
executor:
name: win/vs2019
shell: bash.exe
environment:
TARGET_ARCH: ia32
parameters:
on_linux:
type: boolean
default: false
on_mac:
type: boolean
default: false
on_win:
type: boolean
default: true
<<: *steps-build
build-windows-x64:
executor:
name: win/vs2019
shell: bash.exe
environment:
TARGET_ARCH: x64
parameters:
on_linux:
type: boolean
default: false
on_mac:
type: boolean
default: false
on_win:
type: boolean
default: true
<<: *steps-build
finalize:
docker:
- image: circleci/node:10
steps:
- checkout
- *step-restore-cache
- *step-install
- attach_workspace:
at: .
- store_artifacts:
path: out/make
destination: dist
release:
docker:
- image: circleci/node:10
steps:
- checkout
- *step-restore-cache
- *step-install
- attach_workspace:
at: .
- run: yarn publish --from-dry-run
workflows:
version: 2
build:
jobs:
- test-linux
- test-mac
- test-windows
- build-linux
- build-mac
- build-windows-ia32
- build-windows-x64
- finalize:
requires:
- build-linux
- build-mac
- build-windows-ia32
- build-windows-x64
- release:
requires:
- build-linux
- build-mac
- build-windows-ia32
- build-windows-x64
filters:
branches:
ignore: /.*/
tags:
only: /v.*/
-34
View File
@@ -1,34 +0,0 @@
#!/bin/bash
set -e
if [[ ! -z $MACOS_CERT_P12 ]]
then
export CERTIFICATE_P12=cert.p12;
echo $MACOS_CERT_P12 | base64 --decode > $CERTIFICATE_P12;
export KEYCHAIN=build.keychain;
# Create the keychain with a password
security create-keychain -p travis $KEYCHAIN;
# Make the custom keychain default, so xcodebuild will use it for signing
security default-keychain -s $KEYCHAIN;
# Unlock the keychain
security unlock-keychain -p travis $KEYCHAIN;
# Add certificates to keychain and allow codesign to access them
# Apple Worldwide Developer Relations Certification Authority
security import ./tools/certs/apple.cer -k ~/Library/Keychains/$KEYCHAIN -T /usr/bin/codesign
# Developer Authentication Certification Authority
security import ./tools/certs/dac.cer -k ~/Library/Keychains/$KEYCHAIN -T /usr/bin/codesign
# Developer ID Felix
security import $CERTIFICATE_P12 -k $KEYCHAIN -P $MACOS_CERT_PASSWORD -T /usr/bin/codesign 2>&1 >/dev/null;
rm $CERTIFICATE_P12;
security set-key-partition-list -S apple-tool:,apple: -s -k travis $KEYCHAIN
# Echo the identity
security find-identity -v -p codesigning
fi
+1
View File
@@ -21,6 +21,7 @@
/out/
node_modules/
SHASUMS256.txt
**/yarn.lock
compile_commands.json
.envrc
.cache
+63
View File
@@ -0,0 +1,63 @@
language: node_js
node_js: "12"
os:
- linux
- osx
dist: bionic
osx_image: xcode10
cache:
npm: true
directories:
- $HOME/.cache/electron
addons:
apt:
packages:
- fakeroot
- rpm
branches:
only:
- master
- /^v\d+\.\d+\.\d+/
install:
- npm ci
- |
if [[ "$TRAVIS_OS_NAME" == "osx" && "$TRAVIS_SECURE_ENV_VARS" == "true" ]]; then
export CERTIFICATE_P12=cert.p12;
echo $MACOS_CERT_P12 | base64 --decode > $CERTIFICATE_P12;
export KEYCHAIN=build.keychain;
# Create the keychain with a password
security create-keychain -p travis $KEYCHAIN;
# Make the custom keychain default, so xcodebuild will use it for signing
security default-keychain -s $KEYCHAIN;
# Unlock the keychain
security unlock-keychain -p travis $KEYCHAIN;
# Add certificates to keychain and allow codesign to access them
# Apple Worldwide Developer Relations Certification Authority
security import ./tools/certs/apple.cer -k ~/Library/Keychains/$KEYCHAIN -T /usr/bin/codesign
# Developer Authentication Certification Authority
security import ./tools/certs/dac.cer -k ~/Library/Keychains/$KEYCHAIN -T /usr/bin/codesign
# Developer ID Felix
security import $CERTIFICATE_P12 -k $KEYCHAIN -P $MACOS_CERT_PASSWORD -T /usr/bin/codesign 2>&1 >/dev/null;
rm $CERTIFICATE_P12;
security set-key-partition-list -S apple-tool:,apple: -s -k travis $KEYCHAIN
# Echo the identity
security find-identity -v -p codesigning
fi
script:
- npm run lint
- npm run test:ci
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then npm run test:coverage; fi
- if test -z "$TRAVIS_TAG"; then npm run make; fi
after_success: if test -n "$TRAVIS_TAG"; then npm run publish; fi
+1 -18
View File
@@ -39,27 +39,10 @@ Here are some things to keep in mind as you file pull requests to fix bugs, add
### Running Fiddle From Source
1. Clone the Electron Fiddle repository locally.
```sh
git clone https://github.com/electron/fiddle.git
```
2. Change directory to where Fiddle has been cloned.
```sh
git clone https://github.com/electron/fiddle
cd fiddle
```
3. Install dependencies.
```sh
npm install
```
4. Start Fiddle and Explore!
```sh
npm start
```
-4
View File
@@ -56,10 +56,6 @@ fiddle has grown up, export it as a project with or without
[electron-forge][electron-forge]. Then, use your favorite editor and take on
the world!
## Contributing
Electron Fiddle is a community-driven project that welcomes all sorts of contributions. Please check out our [Contributing Guide](https://github.com/electron/fiddle/blob/master/CONTRIBUTING.md) for more details.
## License
[MIT, please see the LICENSE file for full details](https://github.com/electron/fiddle/blob/master/LICENSE.md).
+37
View File
@@ -0,0 +1,37 @@
environment:
matrix:
- nodejs_version: "12"
init:
- git config --global core.symlinks true
install:
# Setup the code signing certificate
- ps: >-
if (Test-Path Env:\WINDOWS_CERTIFICATE_P12) {
$workingDirectory = Convert-Path (Resolve-Path -path ".")
$filename = "$workingDirectory\cert.p12"
$bytes = [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_P12)
[IO.File]::WriteAllBytes($filename, $bytes)
}
- ps: Install-Product node $env:nodejs_version x64
- node --version
- npm ci
cache:
- '%APPDATA%\npm-cache -> appveyor.yml'
test_script:
- node --version
- npm --version
- npm run lint
- npm run test
artifacts:
- path: 'out\make\squirrel.windows\**\*.exe'
build_script:
- if %APPVEYOR_REPO_TAG% EQU false npm run make
- if %APPVEYOR_REPO_TAG% EQU true npm run publish
- if %APPVEYOR_REPO_TAG% EQU true npm run publish -- --arch=ia32
- ps: Tree ./out/make /F
+5 -4
View File
@@ -16,11 +16,12 @@ const config = {
executableName: 'electron-fiddle',
asar: true,
icon: path.resolve(__dirname, 'assets', 'icons', 'fiddle'),
// TODO: FIXME?
// ignore: [
// /^\/\.vscode\//,
// /^\/tools\//
// ],
appBundleId: 'com.electron.fiddle',
usageDescription: {
Camera: 'Access is needed by certain built-in fiddles in addition to any custom fiddles that use the Camera',
Microphone: 'Access is needed by certain built-in fiddles in addition to any custom fiddles that use the Microphone'
},
appCategoryType: 'public.app-category.developer-tools',
protocols: [{
name: 'Electron Fiddle Launch Protocol',
+21843
View File
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -1,7 +1,7 @@
{
"name": "electron-fiddle",
"productName": "Electron Fiddle",
"version": "0.15.1",
"version": "0.13.1",
"description": "The easiest way to get started with Electron",
"repository": "https://github.com/electron/fiddle",
"main": "./dist/src/main/main",
@@ -64,12 +64,12 @@
},
"devDependencies": {
"@babel/core": "^7.8.6",
"@electron-forge/cli": "^6.0.0-beta.52",
"@electron-forge/maker-deb": "^6.0.0-beta.52",
"@electron-forge/maker-rpm": "^6.0.0-beta.52",
"@electron-forge/maker-squirrel": "^6.0.0-beta.52",
"@electron-forge/maker-zip": "^6.0.0-beta.52",
"@electron-forge/publisher-github": "^6.0.0-beta.52",
"@electron-forge/cli": "^6.0.0-beta.50",
"@electron-forge/maker-deb": "^6.0.0-beta.50",
"@electron-forge/maker-rpm": "^6.0.0-beta.50",
"@electron-forge/maker-squirrel": "^6.0.0-beta.50",
"@electron-forge/maker-zip": "^6.0.0-beta.50",
"@electron-forge/publisher-github": "^6.0.0-beta.50",
"@octokit/action": "^2.0.0",
"@types/classnames": "^2.2.9",
"@types/enzyme": "^3.10.5",
+3 -3
View File
@@ -23,10 +23,10 @@ function createWindow () {
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
+3 -9
View File
@@ -23,10 +23,7 @@ export enum IpcEvents {
LOAD_LOCAL_VERSION_FOLDER = 'LOAD_LOCAL_VERSION_FOLDER',
SHOW_LOCAL_VERSION_FOLDER_DIALOG = 'SHOW_LOCAL_VERSION_FOLDER_DIALOG',
BISECT_COMMANDS_TOGGLE = 'BISECT_COMMANDS_TOGGLE',
SHOW_INACTIVE = 'SHOW_INACTIVE',
BEFORE_QUIT = 'BEFORE_QUIT',
CONFIRM_QUIT = 'CONFIRM_QUIT',
SET_APPDATA_DIR = 'SET_APPDATA_DIR',
SHOW_INACTIVE = 'SHOW_INACTIVE'
}
export const ipcMainEvents = [
@@ -35,8 +32,7 @@ export const ipcMainEvents = [
IpcEvents.SHOW_WARNING_DIALOG,
IpcEvents.SHOW_CONFIRMATION_DIALOG,
IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG,
IpcEvents.SHOW_INACTIVE,
IpcEvents.CONFIRM_QUIT,
IpcEvents.SHOW_INACTIVE
];
export const ipcRendererEvents = [
@@ -58,9 +54,7 @@ export const ipcRendererEvents = [
IpcEvents.SHOW_WELCOME_TOUR,
IpcEvents.CLEAR_CONSOLE,
IpcEvents.LOAD_LOCAL_VERSION_FOLDER,
IpcEvents.BISECT_COMMANDS_TOGGLE,
IpcEvents.BEFORE_QUIT,
IpcEvents.SET_APPDATA_DIR,
IpcEvents.BISECT_COMMANDS_TOGGLE
];
export const WEBCONTENTS_READY_FOR_IPC_SIGNAL = 'WEBCONTENTS_READY_FOR_IPC_SIGNAL';
+4
View File
@@ -52,6 +52,10 @@
.action {
text-align: right;
}
.bp3-progress-bar {
margin-top: 8px;
}
}
.settings-content {
+1 -10
View File
@@ -3,13 +3,11 @@ initSentry();
import { app } from 'electron';
import { IpcEvents } from '../ipc-events';
import { isDevMode } from '../utils/devmode';
import { setupAboutPanel } from '../utils/set-about-panel';
import { setupDevTools } from './devtools';
import { setupDialogs } from './dialogs';
import { onFirstRunMaybe } from './first-run';
import { ipcMainManager } from './ipc';
import { listenForProtocolHandler, setupProtocolHandler } from './protocol';
import { shouldQuit } from './squirrel';
import { setupUpdates } from './update';
@@ -43,14 +41,7 @@ export async function onReady() {
* @export
*/
export function onBeforeQuit() {
ipcMainManager.send(IpcEvents.BEFORE_QUIT);
ipcMainManager.on(IpcEvents.CONFIRM_QUIT, quitAppIfConfirmed);
}
export function quitAppIfConfirmed(_: Electron.IpcMainEvent, quitConfirmed: boolean) {
if (quitConfirmed) {
app.quit();
}
(global as any).isQuitting = true;
}
/**
+4 -2
View File
@@ -105,7 +105,9 @@ function getQuitItems(): Array<MenuItemConstructorOptions> {
{
type: 'separator'
}, {
role: 'quit'
label: 'Exit',
accelerator: 'Ctrl+Q,',
click: app.quit
}
];
}
@@ -237,7 +239,7 @@ export function setupMenu() {
// Append the "Settings" item
if (
process.platform === 'darwin'
&& label === app.name
&& label === app.getName()
&& isSubmenu(item.submenu)
) {
item.submenu.splice(2, 0, ...getPreferencesItems());
+6 -14
View File
@@ -1,11 +1,11 @@
import { app, BrowserWindow, shell } from 'electron';
import { BrowserWindow, shell } from 'electron';
import { IpcEvents } from '../ipc-events';
import { createContextMenu } from './context-menu';
import { ipcMainManager } from './ipc';
// Keep a global reference of the window objects, if we don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
export let browserWindows: Array<BrowserWindow | null> = [];
export let browserWindows: Array<BrowserWindow> = [];
/**
* Gets default options for the main window
@@ -37,14 +37,13 @@ export function getMainWindowOptions(): Electron.BrowserWindowConstructorOptions
*/
export function createMainWindow(): Electron.BrowserWindow {
console.log(`Creating main window`);
let browserWindow: BrowserWindow | null;
browserWindow = new BrowserWindow(getMainWindowOptions());
const browserWindow = new BrowserWindow(getMainWindowOptions());
browserWindow.loadFile('./dist/static/index.html');
browserWindow.webContents.once('dom-ready', () => {
if (browserWindow) {
browserWindow.show();
browserWindow.show();
if (browserWindow) {
createContextMenu(browserWindow);
}
});
@@ -52,8 +51,6 @@ export function createMainWindow(): Electron.BrowserWindow {
browserWindow.on('closed', () => {
browserWindows = browserWindows
.filter((bw) => browserWindow !== bw);
browserWindow = null;
});
browserWindow.webContents.on('new-window', (event, url) => {
@@ -67,14 +64,9 @@ export function createMainWindow(): Electron.BrowserWindow {
});
ipcMainManager.on(IpcEvents.SHOW_INACTIVE, () => {
if (browserWindow) {
browserWindow.showInactive();
}
browserWindow.showInactive();
});
const appData = app.getPath('appData');
ipcMainManager.send(IpcEvents.SET_APPDATA_DIR, [appData], browserWindow.webContents);
browserWindows.push(browserWindow);
return browserWindow;
+2 -14
View File
@@ -18,7 +18,6 @@ import { getEditorValue } from '../utils/editor-value';
import { getPackageJson, PackageJsonOptions } from '../utils/get-package';
import { getTitle } from '../utils/get-title';
import { isEditorBackup } from '../utils/type-checks';
import { EMPTY_EDITOR_CONTENT } from './constants';
import { FileManager } from './file-manager';
import { RemoteLoader } from './remote-loader';
import { Runner } from './runner';
@@ -69,17 +68,8 @@ export class App {
}
}
// display all editors that have content
const visibleEditors = [];
for (const id of Object.keys(editorValues)) {
const content = editorValues[id];
// if the gist content matches the empty file output, don't show it
if (!Object.values(EMPTY_EDITOR_CONTENT).includes(content)) {
visibleEditors.push(id as EditorId);
}
}
// set values once prompt approves
await this.setEditorValues(editorValues);
document.title = getTitle(this.state);
this.state.gistId = gistId || '';
@@ -87,8 +77,6 @@ export class App {
this.state.templateName = templateName;
// once loaded, we have a "saved" state
await this.state.setVisibleMosaics(visibleEditors);
await this.setEditorValues(editorValues);
this.state.isUnsaved = false;
return true;
+1 -13
View File
@@ -1,4 +1,4 @@
import { Button, Menu, MenuDivider, MenuItem, Popover, Position } from '@blueprintjs/core';
import { Button, Menu, MenuItem, Popover, Position } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -82,18 +82,6 @@ export class EditorDropdown extends React.Component<EditorDropdownProps, EditorD
);
}
result.push(
<React.Fragment key={'fragment-reset-layout'}>
<MenuDivider />
<MenuItem
icon='grid-view'
key='reset-layout'
text='Reset Layout'
onClick={appState.resetEditorLayout}
/>
</React.Fragment>
);
return result;
}
@@ -1,13 +1,11 @@
import { Button, ButtonGroup, IToastProps, Menu, MenuItem, Popover, Position, Toaster } from '@blueprintjs/core';
import { Button, ButtonGroup, Menu, MenuItem, Popover, Position } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
import { when } from 'mobx';
import { EditorValues } from '../../interfaces';
import { IpcEvents } from '../../ipc-events';
import { INDEX_HTML_NAME, MAIN_JS_NAME, PRELOAD_JS_NAME, RENDERER_JS_NAME, STYLES_CSS_NAME } from '../../shared-constants';
import { getOctokit } from '../../utils/octokit';
import { EMPTY_EDITOR_CONTENT } from '../constants';
import { ipcRendererManager } from '../ipc';
import { AppState } from '../state';
@@ -15,11 +13,6 @@ export interface PublishButtonProps {
appState: AppState;
}
interface IPublishButtonState {
readonly isUpdating: boolean;
readonly wouldPublish: boolean;
}
/**
* The "publish" button takes care of logging you in.
*
@@ -28,25 +21,15 @@ interface IPublishButtonState {
* @extends {React.Component<PublishButtonProps, PublishButtonState>}
*/
@observer
export class PublishButton extends React.Component<PublishButtonProps, IPublishButtonState> {
public constructor(props: PublishButtonProps) {
export class PublishButton extends React.Component<PublishButtonProps> {
constructor(props: PublishButtonProps) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.publishOrUpdateFiddle = this.publishOrUpdateFiddle.bind(this);
this.publishFiddle = this.publishFiddle.bind(this);
this.setPrivate = this.setPrivate.bind(this);
this.setPublic = this.setPublic.bind(this);
this.state = {
isUpdating: false,
wouldPublish: false,
};
}
private toaster: Toaster;
private refHandlers = {
toaster: (ref: Toaster) => this.toaster = ref,
};
public componentDidMount() {
ipcRendererManager.on(IpcEvents.FS_SAVE_FIDDLE_GIST, this.handleClick);
}
@@ -77,7 +60,7 @@ export class PublishButton extends React.Component<PublishButtonProps, IPublishB
await when(() => !!appState.gitHubToken || !appState.isTokenDialogShowing);
if (appState.gitHubToken) {
return this.publishOrUpdateFiddle();
return this.publishFiddle();
}
}
@@ -85,52 +68,50 @@ export class PublishButton extends React.Component<PublishButtonProps, IPublishB
* Connect with GitHub, publish the current Fiddle as a gist,
* and update all related properties in the app state.
*/
public async publishOrUpdateFiddle(): Promise<void> {
public async publishFiddle(): Promise<void> {
const { appState } = this.props;
const { wouldPublish } = this.state;
appState.isPublishing = true;
const octo = await getOctokit(this.props.appState);
const { gitHubPublishAsPublic, gistId } = this.props.appState;
const { gitHubPublishAsPublic } = this.props.appState;
const options = { includeDependencies: true, includeElectron: true };
const values = await window.ElectronFiddle.app.getEditorValues(options);
if (gistId && !wouldPublish) {
this.setState({
isUpdating: true
});
const gist = await octo.gists.update({
gist_id: appState.gistId,
files: this.gistFilesList(values) as any,
});
try {
const gist = await octo.gists.create({
public: !!gitHubPublishAsPublic,
description: 'Electron Fiddle Gist',
files: {
[INDEX_HTML_NAME]: {
content: values.html || '<!-- Empty -->',
},
[MAIN_JS_NAME]: {
content: values.main || '// Empty',
},
[RENDERER_JS_NAME]: {
content: values.renderer || '// Empty',
},
[PRELOAD_JS_NAME]: {
content: values.preload || '// Empty',
},
[STYLES_CSS_NAME]: {
content: values.css || '/* Empty */',
},
},
} as any); // Note: GitHub messed up, GistsCreateParamsFiles is an incorrect interface
console.log('Updating: Updating done', { gist });
this.renderToast({ message: 'Successfully updated gist!' });
this.setState({
isUpdating: false
});
} else {
try {
const gist = await octo.gists.create({
public: !!gitHubPublishAsPublic,
description: 'Electron Fiddle Gist',
files: this.gistFilesList(values) as any, // Note: GitHub messed up, GistsCreateParamsFiles is an incorrect interface
});
appState.gistId = gist.data.id;
appState.gistId = gist.data.id;
console.log(`Publish Button: Publishing done`, { gist });
} catch (error) {
console.warn(`Could not publish gist`, { error });
console.log(`Publish Button: Publishing done`, { gist });
this.renderToast({ message: 'Publishing done successfully!' });
} catch (error) {
console.warn(`Could not publish gist`, { error });
const messageBoxOptions: Electron.MessageBoxOptions = {
message: 'Publishing Fiddle to GitHub failed. Are you connected to the Internet?',
detail: `GitHub encountered the following error: ${error.message}`
};
const messageBoxOptions: Electron.MessageBoxOptions = {
message: 'Publishing Fiddle to GitHub failed. Are you connected to the Internet?',
detail: `GitHub encountered the following error: ${error.message}`
};
ipcRendererManager.send(IpcEvents.SHOW_WARNING_DIALOG, messageBoxOptions);
}
ipcRendererManager.send(IpcEvents.SHOW_WARNING_DIALOG, messageBoxOptions);
}
appState.isPublishing = false;
@@ -155,77 +136,8 @@ export class PublishButton extends React.Component<PublishButtonProps, IPublishB
}
public render() {
const { isPublishing, gistId } = this.props.appState;
const { isUpdating, wouldPublish } = this.state;
const getTextForButton = gistId && !wouldPublish
? 'Update'
: isUpdating
? 'Updating...'
: isPublishing
? 'Publishing...'
: 'Publish';
return (
<>
<fieldset disabled={isPublishing}>
<ButtonGroup className='button-publish'>
{this.renderPrivaryMenu()}
<Button
onClick={this.handleClick}
loading={isPublishing}
icon='upload'
text={getTextForButton}
/>
{this.renderMaybePublishMenu()}
</ButtonGroup>
</fieldset>
<Toaster position={Position.BOTTOM_RIGHT} ref={this.refHandlers.toaster} />
</>
);
}
private renderMaybePublishMenu = () => {
const { gistId } = this.props.appState;
const { wouldPublish } = this.state;
if (!gistId) {
return null;
}
const menu = (
<Menu>
<MenuItem
text='Publish'
active={wouldPublish}
onClick={() => this.setWouldPublish(true)}
/>
<MenuItem
text='Update'
active={!wouldPublish}
onClick={() => this.setWouldPublish(false)}
/>
</Menu>
);
return (
<Popover
content={menu}
position={Position.BOTTOM}
>
<Button
icon='wrench'
/>
</Popover>
);
}
private renderPrivaryMenu = () => {
const { gitHubPublishAsPublic, gistId } = this.props.appState;
if (gistId) {
return null;
}
const { gitHubPublishAsPublic } = this.props.appState;
const { isPublishing } = this.props.appState;
const privacyIcon = gitHubPublishAsPublic ? 'unlock' : 'lock';
const privacyMenu = (
@@ -246,48 +158,28 @@ export class PublishButton extends React.Component<PublishButtonProps, IPublishB
);
return (
<Popover
content={privacyMenu}
position={Position.BOTTOM}
>
<Button
icon={privacyIcon}
/>
</Popover>
<fieldset disabled={isPublishing}>
<ButtonGroup className='button-publish'>
<Popover
content={privacyMenu}
position={Position.BOTTOM}
>
<Button
icon={privacyIcon}
/>
</Popover>
<Button
onClick={this.handleClick}
loading={isPublishing}
icon='upload'
text={isPublishing ? 'Publishing...' : 'Publish'}
/>
</ButtonGroup>
</fieldset>
);
}
private setWouldPublish = (wouldPublish: boolean) => {
this.setState({
wouldPublish,
});
}
private setPrivacy(publishAsPublic: boolean) {
this.props.appState.gitHubPublishAsPublic = publishAsPublic;
}
private renderToast = (toast: IToastProps) => {
this.toaster.show(toast);
}
private gistFilesList = (values: EditorValues) => {
return {
[INDEX_HTML_NAME]: {
content: values.html || EMPTY_EDITOR_CONTENT.html,
},
[MAIN_JS_NAME]: {
content: values.main || EMPTY_EDITOR_CONTENT.js,
},
[RENDERER_JS_NAME]: {
content: values.renderer || EMPTY_EDITOR_CONTENT.js,
},
[PRELOAD_JS_NAME]: {
content: values.preload || EMPTY_EDITOR_CONTENT.js,
},
[STYLES_CSS_NAME]: {
content: values.css || EMPTY_EDITOR_CONTENT.css,
},
};
}
}
+1 -2
View File
@@ -66,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;
-1
View File
@@ -69,7 +69,6 @@ export class TokenDialog extends React.Component<TokenDialogProps, TokenDialogSt
} catch (error) {
console.warn(`Authenticating against GitHub failed`, error);
this.setState({ verifying: false, error: true });
this.props.appState.gitHubToken = null;
return;
}
+4 -4
View File
@@ -45,8 +45,8 @@ export class Editor extends React.Component<EditorProps> {
return false;
}
public async componentDidMount() {
await this.initMonaco();
public componentDidMount() {
this.initMonaco();
}
public componentWillUnmount() {
@@ -120,7 +120,7 @@ export class Editor extends React.Component<EditorProps> {
* @private
* @param {string} value
*/
private createModel(value: string) {
private async createModel(value: string) {
const { monaco } = this.props;
const model = monaco.editor.createModel(value, this.language);
@@ -158,7 +158,7 @@ export class Editor extends React.Component<EditorProps> {
}
} else {
const value = await getContent(id, version);
this.createModel(value);
await this.createModel(value);
}
}
}
+2 -2
View File
@@ -301,10 +301,10 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
}
if (!this.state || !this.state.isMounted) {
this.setState({
this.state = {
monaco,
monacoOptions: defaultMonacoOptions
});
};
} else {
this.setState({ monaco });
}
+17 -10
View File
@@ -8,7 +8,8 @@ import {
IButtonProps,
Icon,
IconName,
Tooltip
Tooltip,
ProgressBar
} from '@blueprintjs/core';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -360,20 +361,20 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
*/
private renderHumanState(item: RunnableVersion): JSX.Element {
const { state } = item;
let icon: IconName = 'box';
if (state === VersionState.downloading || state === VersionState.unzipping) {
return <ProgressBar value={item.downloadProgress} />;
}
let humanState = 'Downloaded';
if (state === VersionState.downloading) {
icon = 'cloud-download';
humanState = 'Downloading';
} else if (state === VersionState.unknown) {
icon = 'cloud';
if (state === VersionState.unknown) {
humanState = 'Not downloaded';
}
return (
<span>
<Icon icon={icon} /> {humanState}
{humanState}
</span>
);
}
@@ -395,17 +396,23 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
};
// Already downloaded
if (state === 'ready') {
if (state === VersionState.ready) {
buttonProps.disabled = false;
buttonProps.onClick = () => appState.removeVersion(key);
buttonProps.icon = 'trash';
buttonProps.text = source === VersionSource.local
? 'Remove'
: 'Delete';
} else if (state === 'downloading') {
} else if (state === VersionState.downloading) {
buttonProps.disabled = true;
buttonProps.loading = true;
buttonProps.text = 'Downloading';
buttonProps.icon = 'cloud-download';
} else if (state === VersionState.unzipping) {
buttonProps.disabled = true;
buttonProps.loading = true;
buttonProps.text = 'Unzipping';
buttonProps.icon = 'cloud-download';
} else {
buttonProps.disabled = false;
buttonProps.loading = false;
@@ -110,7 +110,7 @@ export class ExecutionSettings extends React.Component<ExecutionSettingsProps, {
<FormGroup>
<p>
Electron allows starting the executable with <a
href='https://www.electronjs.org/docs/api/command-line-switches'
href='https://electronjs.org/docs/api/chrome-command-line-switches'
>
user-provided flags
</a>
+2 -10
View File
@@ -3,7 +3,7 @@ import * as path from 'path';
import { MosaicNode } from 'react-mosaic-component';
import { EditorId, MosaicId } from '../interfaces';
import { EditorBackup, getEditorBackup } from '../utils/editor-backup';
import { EditorBackup } from '../utils/editor-backup';
// Reminder: When testing, this file is mocked in tests/setup.js
@@ -21,15 +21,7 @@ export const DEFAULT_MOSAIC_ARRANGEMENT: MosaicNode<MosaicId> = {
};
export const DEFAULT_CLOSED_PANELS: Partial<Record<MosaicId, EditorBackup | true>> = {
docsDemo: true,
preload: getEditorBackup(EditorId.preload),
css: getEditorBackup(EditorId.css),
};
export const EMPTY_EDITOR_CONTENT = {
html: '<!-- Empty -->',
js: '// Empty',
css: '/* Empty */'
docsDemo: true
};
export const ELECTRON_ORG = 'electron';
+2 -3
View File
@@ -3,6 +3,7 @@ import * as path from 'path';
import { EditorValues, FileTransform } from '../interfaces';
import { IpcEvents } from '../ipc-events';
import { getAppDataDir } from '../utils/app-data-dir';
import { PackageJsonOptions } from '../utils/get-package';
import { maybePlural } from '../utils/plural-maybe';
import { getElectronBinaryPath, getIsDownloaded } from './binary';
@@ -192,11 +193,9 @@ export class Runner {
const env = { ...process.env };
if (this.appState.isEnablingElectronLogging) {
env.ELECTRON_ENABLE_LOGGING = 'true';
env.ELECTRON_DEBUG_NOTIFICATIONS = 'true';
env.ELECTRON_ENABLE_STACK_DUMPING = 'true';
} else {
delete env.ELECTRON_ENABLE_LOGGING;
delete env.ELECTRON_DEBUG_NOTIFICATIONS;
delete env.ELECTRON_ENABLE_STACK_DUMPING;
}
@@ -280,7 +279,7 @@ export class Runner {
}
const name = await this.appState.getName();
const appData = path.join(this.appState.appData, name);
const appData = getAppDataDir(name);
console.log(`Cleanup: Deleting data dir ${appData}`);
await window.ElectronFiddle.app.fileManager.cleanup(appData);
+16 -49
View File
@@ -32,7 +32,6 @@ import { getLocalTypePathForVersion, updateEditorTypeDefinitions } from './fetch
import { ipcRendererManager } from './ipc';
import { activateTheme } from './themes';
import { waitForEditorsToMount } from '../utils/editor-mounted';
import { sortedElectronMap } from '../utils/sorted-electron-map';
import {
addLocalVersion,
@@ -106,7 +105,6 @@ export class AppState {
@observable public isRunning: boolean = false;
@observable public isUnsaved: boolean;
@observable public isUpdatingElectronVersions: boolean = false;
@observable public isQuitting: boolean = false;
// -- Various "isShowing" settings ------------------
@observable public isBisectCommandShowing: boolean;
@@ -124,7 +122,6 @@ export class AppState {
private outputBuffer: string = '';
private name: string;
public appData: string;
constructor() {
// Bind all actions
@@ -142,8 +139,6 @@ export class AppState {
this.toggleSettings = this.toggleSettings.bind(this);
this.toggleBisectDialog = this.toggleBisectDialog.bind(this);
this.updateElectronVersions = this.updateElectronVersions.bind(this);
this.resetEditorLayout = this.resetEditorLayout.bind(this);
this.setIsQuitting = this.setIsQuitting.bind(this);
ipcRendererManager.removeAllListeners(IpcEvents.OPEN_SETTINGS);
ipcRendererManager.removeAllListeners(IpcEvents.SHOW_WELCOME_TOUR);
@@ -154,8 +149,6 @@ export class AppState {
ipcRendererManager.on(IpcEvents.SHOW_WELCOME_TOUR, this.showTour);
ipcRendererManager.on(IpcEvents.CLEAR_CONSOLE, this.clearConsole);
ipcRendererManager.on(IpcEvents.BISECT_COMMANDS_TOGGLE, this.toggleBisectCommands);
ipcRendererManager.on(IpcEvents.BEFORE_QUIT, this.setIsQuitting);
ipcRendererManager.once(IpcEvents.SET_APPDATA_DIR, (_event, dir) => { this.appData = dir; });
// Setup auto-runs
autorun(() => this.save('theme', this.theme));
@@ -188,23 +181,22 @@ export class AppState {
// We'll wait until the warning dialog was closed
when(() => !this.isGenericDialogShowing).then(() => {
const quitConfirmed = this.genericDialogLastResult;
// The user confirmed, let's close for real.
if (quitConfirmed) {
if (this.genericDialogLastResult) {
window.onbeforeunload = null;
window.close();
}
// isQuitting checks if we're trying to quit the app
// or just close the window
if (this.isQuitting) {
ipcRendererManager.send(IpcEvents.CONFIRM_QUIT, quitConfirmed);
this.isQuitting = false;
// Should we just close or quit?
const remote = require('electron').remote;
const isQuitting = remote.getGlobal('isQuitting');
if (isQuitting) {
remote.app.quit();
} else {
window.close();
}
}
});
// return value doesn't matter, we just want to cancel the event
return false;
};
} else {
@@ -342,10 +334,6 @@ export class AppState {
this.resetView({ isSettingsShowing: !this.isSettingsShowing });
}
@action public setIsQuitting() {
this.isQuitting = true;
}
@action public disableTour() {
this.resetView({ isTourShowing: false });
localStorage.setItem('hasShownTour', 'true');
@@ -477,14 +465,10 @@ export class AppState {
const fs = await fancyImport<typeof fsType>('fs-extra');
const typePath = getLocalTypePathForVersion(versionObject);
console.info(`TypeDefs: Watching file for local version ${version} at path ${typePath}`);
try {
this.localTypeWatcher = fs.watch(typePath!, async () => {
console.info(`TypeDefs: Noticed file change at ${typePath}. Updating editor typedefs.`);
await updateEditorTypeDefinitions(versionObject);
});
} catch (err) {
console.info('TypeDefs: Unable to start watching.');
}
this.localTypeWatcher = fs.watch(typePath!, async () => {
console.info(`TypeDefs: Noticed file change at ${typePath}. Updating editor typedefs.`);
await updateEditorTypeDefinitions(versionObject);
});
} else {
if (!!this.localTypeWatcher) {
console.info(`TypeDefs: Switched to downloaded version ${version}. Unwatching local typedefs.`);
@@ -555,6 +539,7 @@ export class AppState {
this.outputBuffer += strData;
strData = this.outputBuffer;
const parts = strData.split('\r\n');
for (let partIndex = 0; partIndex < parts.length; partIndex += 1) {
const part = parts[partIndex];
if (partIndex === parts.length - 1) {
@@ -607,7 +592,7 @@ export class AppState {
return null;
}
@action public async setVisibleMosaics(visible: Array<MosaicId>) {
@action public setVisibleMosaics(visible: Array<MosaicId>) {
const currentlyVisible = getVisibleMosaics(this.mosaicArrangement);
for (const id of ALL_MOSAICS) {
@@ -615,9 +600,6 @@ export class AppState {
this.closedPanels[id] = isEditorId(id)
? getEditorBackup(id)
: true;
// if we have backup, remove active editor
delete window.ElectronFiddle.editors[id];
}
// Remove the backup for panels now. Editors will remove their
@@ -631,12 +613,6 @@ export class AppState {
console.log(`State: Setting visible mosaic panels`, visible, updatedArrangement);
this.mosaicArrangement = updatedArrangement;
// after the mosaicArrangement loads, we want to wait for the Mosaic editors to
// mount to ensure that we can load content into the editors as soon as they're
// declared visible.
await waitForEditorsToMount(visible);
}
/**
@@ -659,15 +635,6 @@ export class AppState {
this.setVisibleMosaics([...currentlyVisible, id]);
}
/**
* Resets editor view to default layout
*
*
*/
@action public resetEditorLayout() {
this.mosaicArrangement = DEFAULT_MOSAIC_ARRANGEMENT;
}
/**
* Resets the view, optionally with certain view flags enabled.
*
+16
View File
@@ -0,0 +1,16 @@
import * as path from 'path';
import { remote } from 'electron';
let appData = '';
/**
* Returns an appData path for a given input
*
* @param input {string}
* @returns {string}
*/
export function getAppDataDir(input: string): string {
appData = appData || remote.app.getPath('appData');
return path.join(appData, input);
}
+1 -1
View File
@@ -12,7 +12,7 @@ export function getEditorModel(id: EditorId): editor.ITextModel | null {
const { ElectronFiddle: fiddle } = window;
if (!fiddle) {
return null;
throw new Error('Fiddle not ready');
}
if (fiddle.editors[id] && fiddle.editors[id]!.getModel) {
-24
View File
@@ -1,24 +0,0 @@
import { MosaicId } from '../interfaces';
/**
* Waits for editors to mount on a list of Mosaic IDs
* @param editors
*/
export function waitForEditorsToMount(editors: Array<MosaicId>) {
let time = 0;
const maxTime = 3000;
const interval = 100;
return new Promise((resolve, reject) => {
(function checkMountedEditors() {
const allMounted = editors.every((id) => !!window.ElectronFiddle.editors[id]);
if (allMounted) {
return resolve();
}
time += interval;
if (time > maxTime) {
return reject(`Timed out after ${maxTime}ms: can't mount editors onto mosaics.`);
}
setTimeout(checkMountedEditors, 100);
})();
});
}
+2 -8
View File
@@ -1,5 +1,4 @@
import { EditorId } from '../interfaces';
import { EditorBackup } from './editor-backup';
/**
* Return the value for a given editor
@@ -11,16 +10,11 @@ export function getEditorValue(id: EditorId): string {
const { ElectronFiddle: fiddle } = window;
if (!fiddle) {
return '';
throw new Error('Fiddle not ready');
}
const editor = fiddle.editors[id];
const backup = fiddle.app?.state?.closedPanels[id] as EditorBackup;
if (editor?.getValue) {
if (fiddle.editors[id] && fiddle.editors[id]!.getValue) {
return fiddle.editors[id]!.getValue();
} else if (backup?.value) {
return backup.value;
}
return '';
+1 -1
View File
@@ -13,7 +13,7 @@ export function getEditorViewState(id: EditorId): editor.ICodeEditorViewState |
const { ElectronFiddle: fiddle } = window;
if (!fiddle) {
return null;
throw new Error('Fiddle not ready');
}
if (fiddle.editors[id] && fiddle.editors[id]!.saveViewState) {
-4
View File
@@ -8,9 +8,5 @@
<true/>
<key>com.apple.security.cs.debugger</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
</dict>
</plist>
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -13,5 +13,5 @@ app.on('browser-window-created', () => console.log('A window was created!'))
app.on('browser-window-focus', () => console.log('...and focused!'))
console.log(`The app lives at: ${app.getAppPath()}`)
console.log(`It's named: ${app.name}`)
console.log(`It's named: ${app.getName()}`)
console.log(`It has the version: ${app.getVersion()}`)
+2 -2
View File
@@ -1,4 +1,4 @@
{
"expectedVersionCount": 371,
"lastElectronVersion": "10.0.0-beta.8"
"expectedVersionCount": 325,
"lastElectronVersion": "9.0.0-beta.9"
}
+4 -19
View File
@@ -4,9 +4,7 @@
import { app } from 'electron';
import { IpcEvents } from '../../src/ipc-events';
import { ipcMainManager } from '../../src/main/ipc';
import { main, onBeforeQuit, onReady, onWindowsAllClosed, quitAppIfConfirmed } from '../../src/main/main';
import { main, onBeforeQuit, onReady, onWindowsAllClosed } from '../../src/main/main';
import { shouldQuit } from '../../src/main/squirrel';
import { setupUpdates } from '../../src/main/update';
import { getOrCreateMainWindow } from '../../src/main/windows';
@@ -28,8 +26,6 @@ jest.mock('../../src/main/squirrel', () => ({
shouldQuit: jest.fn(() => false)
}));
jest.mock('../../src/main/ipc');
/**
* This test is very basic and some might say that it's
* just testing that methods are being called as written.
@@ -66,22 +62,11 @@ describe('main', () => {
});
describe('onBeforeQuit()', () => {
it('sets up IPC so app can quit if dialog confirmed', () => {
it('sets a global', () => {
onBeforeQuit();
expect(ipcMainManager.send).toHaveBeenCalledWith(IpcEvents.BEFORE_QUIT);
expect(ipcMainManager.on).toHaveBeenCalledWith(IpcEvents.CONFIRM_QUIT, quitAppIfConfirmed);
});
});
describe('quitAppIfConfirmed()', () => {
it('quits app if passed a truthy value', () => {
quitAppIfConfirmed({} as any, true);
expect(app.quit).toHaveBeenCalledTimes(1);
});
it('does nothing if passed a value', () => {
quitAppIfConfirmed({} as any, false);
expect(app.quit).toHaveBeenCalledTimes(0);
expect((global as any).isQuitting).toBe(true);
(global as any).isQuitting = false;
});
});
-2
View File
@@ -15,8 +15,6 @@ jest.mock('../../src/main/ipc');
describe('menu', () => {
beforeEach(() => {
electron.app.name = 'Electron Fiddle';
// need to keep this deprecated API for electron-default-menu mock as well
(electron.app.getName as any).mockReturnValue('Electron Fiddle');
(electron.dialog.showOpenDialog as any).mockReturnValue(Promise.resolve({}));
});
+4
View File
@@ -14,6 +14,10 @@ export const mockVersionsArray = [
state: VersionState.ready,
version: '1.8.7',
source: VersionSource.remote
}, {
state: VersionState.ready,
version: '1.8.6',
source: VersionSource.remote
}
];
-5
View File
@@ -114,7 +114,6 @@ describe('Editors component', () => {
const app = new App();
(app.state as Partial<AppState>) = new MockState();
app.state.isUnsaved = false;
app.state.setVisibleMosaics = jest.fn();
app.setEditorValues = jest.fn();
const editorValues = {
@@ -144,7 +143,6 @@ describe('Editors component', () => {
app.state.isUnsaved = true;
app.state.localPath = '/fake/path';
app.state.setGenericDialogOptions = jest.fn();
app.state.setVisibleMosaics = jest.fn();
app.setEditorValues = jest.fn();
const editorValues = {
@@ -174,7 +172,6 @@ describe('Editors component', () => {
const app = new App();
(app.state as Partial<AppState>) = new MockState();
app.state.isUnsaved = false;
app.state.setVisibleMosaics = jest.fn();
app.setEditorValues = jest.fn();
const editorValues = {
@@ -198,7 +195,6 @@ describe('Editors component', () => {
const app = new App();
(app.state as Partial<AppState>) = new MockState();
app.state.isUnsaved = true;
app.state.setVisibleMosaics = jest.fn();
app.state.setGenericDialogOptions = jest.fn();
app.setEditorValues = jest.fn();
@@ -230,7 +226,6 @@ describe('Editors component', () => {
const app = new App();
(app.state as Partial<AppState>) = new MockState();
app.state.isUnsaved = true;
app.state.setVisibleMosaics = jest.fn();
app.state.setGenericDialogOptions = jest.fn();
app.setEditorValues = jest.fn();
@@ -52,17 +52,6 @@ Array [
shouldDismissPopover={true}
text="Stylesheet (styles.css)"
/>,
<React.Fragment>
<Blueprint3.MenuDivider />
<Blueprint3.MenuItem
disabled={false}
icon="grid-view"
multiline={false}
popoverProps={Object {}}
shouldDismissPopover={true}
text="Reset Layout"
/>
</React.Fragment>,
]
`;
@@ -123,17 +112,6 @@ exports[`EditorDropdown component renders 1`] = `
shouldDismissPopover={true}
text="Stylesheet (styles.css)"
/>
<React.Fragment>
<Blueprint3.MenuDivider />
<Blueprint3.MenuItem
disabled={false}
icon="grid-view"
multiline={false}
popoverProps={Object {}}
shouldDismissPopover={true}
text="Reset Layout"
/>
</React.Fragment>
</Blueprint3.Menu>
}
defaultIsOpen={false}
@@ -225,17 +203,6 @@ exports[`EditorDropdown component renders the extra button if the FIDDLE_DOCS_DE
shouldDismissPopover={true}
text="Stylesheet (styles.css)"
/>
<React.Fragment>
<Blueprint3.MenuDivider />
<Blueprint3.MenuItem
disabled={false}
icon="grid-view"
multiline={false}
popoverProps={Object {}}
shouldDismissPopover={true}
text="Reset Layout"
/>
</React.Fragment>
</Blueprint3.Menu>
}
defaultIsOpen={false}
@@ -1,71 +1,63 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Publish button component renders 1`] = `
<Fragment>
<fieldset>
<Blueprint3.ButtonGroup
className="button-publish"
<fieldset>
<Blueprint3.ButtonGroup
className="button-publish"
>
<Blueprint3.Popover
boundary="scrollParent"
captureDismiss={false}
content={
<Blueprint3.Menu>
<Blueprint3.MenuItem
active={false}
disabled={false}
icon="lock"
multiline={false}
onClick={[Function]}
popoverProps={Object {}}
shouldDismissPopover={true}
text="Private"
/>
<Blueprint3.MenuItem
active={true}
disabled={false}
icon="unlock"
multiline={false}
onClick={[Function]}
popoverProps={Object {}}
shouldDismissPopover={true}
text="Public"
/>
</Blueprint3.Menu>
}
defaultIsOpen={false}
disabled={false}
fill={false}
hasBackdrop={false}
hoverCloseDelay={300}
hoverOpenDelay={150}
inheritDarkTheme={true}
interactionKind="click"
minimal={false}
modifiers={Object {}}
openOnTargetFocus={true}
position="bottom"
targetTagName="span"
transitionDuration={300}
usePortal={true}
wrapperTagName="span"
>
<Blueprint3.Popover
boundary="scrollParent"
captureDismiss={false}
content={
<Blueprint3.Menu>
<Blueprint3.MenuItem
active={false}
disabled={false}
icon="lock"
multiline={false}
onClick={[Function]}
popoverProps={Object {}}
shouldDismissPopover={true}
text="Private"
/>
<Blueprint3.MenuItem
active={true}
disabled={false}
icon="unlock"
multiline={false}
onClick={[Function]}
popoverProps={Object {}}
shouldDismissPopover={true}
text="Public"
/>
</Blueprint3.Menu>
}
defaultIsOpen={false}
disabled={false}
fill={false}
hasBackdrop={false}
hoverCloseDelay={300}
hoverOpenDelay={150}
inheritDarkTheme={true}
interactionKind="click"
minimal={false}
modifiers={Object {}}
openOnTargetFocus={true}
position="bottom"
targetTagName="span"
transitionDuration={300}
usePortal={true}
wrapperTagName="span"
>
<Blueprint3.Button
icon="unlock"
/>
</Blueprint3.Popover>
<Blueprint3.Button
icon="upload"
onClick={[Function]}
text="Publish"
icon="unlock"
/>
</Blueprint3.ButtonGroup>
</fieldset>
<Blueprint3.Toaster
autoFocus={false}
canEscapeKeyClear={true}
position="bottom-right"
usePortal={true}
/>
</Fragment>
</Blueprint3.Popover>
<Blueprint3.Button
icon="upload"
onClick={[Function]}
text="Publish"
/>
</Blueprint3.ButtonGroup>
</fieldset>
`;
@@ -26,6 +26,11 @@ exports[`VersionSelect component renders 1`] = `
"state": "unknown",
"version": "1.0.0",
},
"1.8.6": Object {
"source": "remote",
"state": "ready",
"version": "1.8.6",
},
"1.8.7": Object {
"source": "remote",
"state": "ready",
@@ -64,6 +69,11 @@ exports[`VersionSelect component renders 1`] = `
"state": "ready",
"version": "1.8.7",
},
Object {
"source": "remote",
"state": "ready",
"version": "1.8.6",
},
Object {
"source": "remote",
"state": "unknown",
@@ -180,10 +180,6 @@ exports[`ElectronSettings component renders 1`] = `
</td>
<td>
<span>
<Blueprint3.Icon
icon="box"
/>
Downloaded
</span>
</td>
@@ -191,6 +187,7 @@ exports[`ElectronSettings component renders 1`] = `
className="action"
>
<Blueprint3.Button
disabled={false}
fill={true}
icon="trash"
onClick={[Function]}
@@ -206,13 +203,7 @@ exports[`ElectronSettings component renders 1`] = `
2.0.2
</td>
<td>
<span>
<Blueprint3.Icon
icon="cloud-download"
/>
Downloading
</span>
<Blueprint3.ProgressBar />
</td>
<td
className="action"
@@ -235,10 +226,6 @@ exports[`ElectronSettings component renders 1`] = `
</td>
<td>
<span>
<Blueprint3.Icon
icon="box"
/>
Downloaded
</span>
</td>
@@ -246,6 +233,7 @@ exports[`ElectronSettings component renders 1`] = `
className="action"
>
<Blueprint3.Button
disabled={false}
fill={true}
icon="trash"
onClick={[Function]}
@@ -40,7 +40,7 @@ exports[`ExecutionSettings component renders 1`] = `
<p>
Electron allows starting the executable with
<a
href="https://www.electronjs.org/docs/api/command-line-switches"
href="https://electronjs.org/docs/api/chrome-command-line-switches"
>
user-provided flags
</a>
@@ -47,10 +47,10 @@ describe('Publish button component', () => {
const wrapper = shallow(<PublishButton appState={store} />);
const instance: PublishButton = wrapper.instance() as any;
instance.publishOrUpdateFiddle = jest.fn();
instance.publishFiddle = jest.fn();
await instance.handleClick();
expect(instance.publishOrUpdateFiddle).toHaveBeenCalled();
expect(instance.publishFiddle).toHaveBeenCalled();
});
it('attempts to publish to Gist', async () => {
@@ -66,7 +66,7 @@ describe('Publish button component', () => {
const wrapper = shallow(<PublishButton appState={store} />);
const instance: PublishButton = wrapper.instance() as any;
await instance.publishOrUpdateFiddle();
await instance.publishFiddle();
expect(mockOctokit.gists.create).toHaveBeenCalledWith({
description: 'Electron Fiddle Gist',
@@ -96,7 +96,7 @@ describe('Publish button component', () => {
(window as any).ElectronFiddle.app.getEditorValues.mockReturnValueOnce({});
await instance.publishOrUpdateFiddle();
await instance.publishFiddle();
expect(mockOctokit.gists.create).toHaveBeenCalledWith({
description: 'Electron Fiddle Gist',
@@ -126,7 +126,7 @@ describe('Publish button component', () => {
const wrapper = shallow(<PublishButton appState={store} />);
const instance: PublishButton = wrapper.instance() as any;
await instance.publishOrUpdateFiddle();
await instance.publishFiddle();
expect(store.isPublishing).toBe(false);
});
@@ -147,7 +147,7 @@ describe('Publish button component', () => {
const instance: PublishButton = wrapper.instance() as any;
instance.setPrivate();
await instance.publishOrUpdateFiddle();
await instance.publishFiddle();
expect(mockOctokit.gists.create).toHaveBeenCalledWith({
...expectedGistCreateOpts,
@@ -155,7 +155,7 @@ describe('Publish button component', () => {
});
instance.setPublic();
await instance.publishOrUpdateFiddle();
await instance.publishFiddle();
expect(mockOctokit.gists.create).toHaveBeenCalledWith({
...expectedGistCreateOpts,
@@ -170,7 +170,7 @@ describe('Publish button component', () => {
expect(wrapper.find('fieldset').prop('disabled')).toBe(false);
instance.publishOrUpdateFiddle = jest.fn().mockImplementationOnce(() => {
instance.publishFiddle = jest.fn().mockImplementationOnce(() => {
return new Promise((resolve) => {
wrapper.setProps({ appState: { store, isPublishing: true } }, () => {
expect(wrapper.find('fieldset').prop('disabled')).toBe(true);
@@ -182,7 +182,7 @@ describe('Publish button component', () => {
});
});
await instance.publishOrUpdateFiddle();
await instance.publishFiddle();
});
describe('privacy menu', () => {
@@ -110,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);
});
@@ -181,7 +181,6 @@ describe('TokenDialog component', () => {
await instance.onSubmitToken();
expect(wrapper.state('error')).toBe(true);
expect(store.gitHubToken).toEqual(null);
});
});
});
@@ -25,6 +25,7 @@ describe('ElectronSettings component', () => {
store.versions['2.0.2'].state = 'downloading';
store.versions['2.0.1'].state = 'ready';
store.versions['1.8.7'].state = 'unknown';
store.versions['1.8.6'].state = 'unzipping';
});
it('renders', () => {
@@ -94,7 +95,7 @@ describe('ElectronSettings component', () => {
const instance = wrapper.instance() as any;
await instance.handleDeleteAll();
expect(store.removeVersion).toHaveBeenCalledTimes(2);
expect(store.removeVersion).toHaveBeenCalledTimes(3);
});
it('handles the downloadAll()', async () => {
+1 -2
View File
@@ -24,7 +24,6 @@ jest.mock('../../src/renderer/binary', () => ({
}));
jest.mock('fs-extra');
jest.mock('child_process');
jest.mock('path');
describe('Runner component', () => {
let mockChild: MockChildProcess;
@@ -48,7 +47,7 @@ describe('Runner component', () => {
get currentElectronVersion() {
return mockVersions['2.0.2'];
},
getName: async () => 'test-app-name',
getName: async () => 'test-app-name'
};
(window as any).ElectronFiddle = new ElectronFiddleMock();
+15 -50
View File
@@ -1,7 +1,4 @@
import * as MonacoType from 'monaco-editor';
import { ALL_MOSAICS, EditorId, GenericDialogType, PanelId, VersionSource, VersionState } from '../../src/interfaces';
import { IpcEvents } from '../../src/ipc-events';
import { getDownloadedVersions, getDownloadingVersions, removeBinary, setupBinary } from '../../src/renderer/binary';
import { Bisector } from '../../src/renderer/bisect';
import { DEFAULT_MOSAIC_ARRANGEMENT } from '../../src/renderer/constants';
@@ -49,7 +46,6 @@ jest.mock('../../src/renderer/versions', () => {
jest.mock('../../src/utils/get-title', () => ({
getName: jest.fn()
}));
jest.mock('../../src/renderer/ipc');
describe('AppState', () => {
let appState = new AppState();
@@ -82,8 +78,10 @@ describe('AppState', () => {
});
it('can close the app after user accepts dialog', (done) => {
const { remote } = require('electron');
window.close = jest.fn();
appState.isUnsaved = true;
(remote.getGlobal as jest.Mock).mockReturnValueOnce(true);
expect(window.onbeforeunload).toBeTruthy();
const result = window.onbeforeunload!(undefined as any);
@@ -92,10 +90,9 @@ describe('AppState', () => {
appState.genericDialogLastResult = true;
appState.isGenericDialogShowing = false;
appState.isQuitting = true;
process.nextTick(() => {
expect(window.close).toHaveBeenCalledTimes(1);
expect(ipcRendererManager.send).toHaveBeenCalledWith(IpcEvents.CONFIRM_QUIT, true);
expect(window.close).toHaveBeenCalledTimes(0);
expect(remote.app.quit).toHaveBeenCalledTimes(1);
done();
});
});
@@ -111,10 +108,8 @@ describe('AppState', () => {
appState.genericDialogLastResult = false;
appState.isGenericDialogShowing = false;
appState.isQuitting = true;
process.nextTick(() => {
expect(window.close).toHaveBeenCalledTimes(0);
expect(ipcRendererManager.send).toHaveBeenCalledWith(IpcEvents.CONFIRM_QUIT, false);
done();
});
});
@@ -259,13 +254,6 @@ describe('AppState', () => {
});
});
describe('setIsQuitting()', () => {
it('sets isQuitting variable as true', () => {
appState.setIsQuitting();
expect(appState.isQuitting).toBe(true);
});
});
describe('disableTour()', () => {
it('disables the tour', () => {
appState.isTourShowing = false;
@@ -295,17 +283,21 @@ describe('AppState', () => {
});
it('excludes channels', () => {
const expectedLength = Object.keys(appState.versions).length;
appState.channelsToShow = [ 'Unsupported' as any ];
expect(appState.versionsToShow.length).toEqual(0);
appState.channelsToShow = [ 'Stable' as any ];
expect(appState.versionsToShow.length).toEqual(3);
expect(appState.versionsToShow.length).toEqual(expectedLength);
});
it('excludes states', () => {
const expectedLength = Object.keys(appState.versions).length;
appState.statesToShow = [ VersionState.downloading ];
expect(appState.versionsToShow.length).toEqual(0);
appState.statesToShow = [ VersionState.ready ];
expect(appState.versionsToShow.length).toEqual(3);
expect(appState.versionsToShow.length).toEqual(expectedLength);
});
});
@@ -434,7 +426,7 @@ describe('AppState', () => {
// refreshed - we didn't actually add the local version
// above, since versions.ts is mocked
expect(Object.keys(appState.versions)).toEqual(
['2.0.2', '2.0.1', '1.8.7']
['2.0.2', '2.0.1', '1.8.7', '1.8.6']
);
});
});
@@ -523,14 +515,10 @@ describe('AppState', () => {
});
describe('setVisibleMosaics()', () => {
it('updates the visible editors and creates a backup', async () => {
it('updates the visible editors and creates a backup', () => {
appState.mosaicArrangement = createMosaicArrangement(ALL_MOSAICS);
appState.closedPanels = {};
await appState.setVisibleMosaics([EditorId.main]);
// we just need to mock something truthy here
// tslint:disable-next-line:no-object-literal-type-assertion
window.ElectronFiddle.editors[EditorId.main] = ({} as MonacoType.editor.IStandaloneCodeEditor);
appState.setVisibleMosaics([EditorId.main]);
expect(appState.mosaicArrangement).toEqual(EditorId.main);
expect(appState.closedPanels[EditorId.renderer]).toBeTruthy();
@@ -538,17 +526,10 @@ describe('AppState', () => {
expect(appState.closedPanels[EditorId.main]).toBeUndefined();
});
it('removes the backup for a non-editor right away', async () => {
it('removes the backup for a non-editor right away', () => {
appState.closedPanels = {};
appState.closedPanels[PanelId.docsDemo] = true;
for (const mosaic of ALL_MOSAICS) {
// we just need to mock something truthy here
// tslint:disable-next-line:no-object-literal-type-assertion
window.ElectronFiddle.editors[mosaic] = ({} as MonacoType.editor.IStandaloneCodeEditor);
}
await appState.setVisibleMosaics(ALL_MOSAICS);
appState.setVisibleMosaics(ALL_MOSAICS);
expect(appState.closedPanels[PanelId.docsDemo]).toBeUndefined();
});
@@ -583,20 +564,4 @@ describe('AppState', () => {
});
});
});
describe('resetEditorLayout()', () => {
it('Puts editors in default arrangement', () => {
appState.hideAndBackupMosaic(EditorId.main);
expect(appState.mosaicArrangement).toEqual({
direction: 'row',
first: EditorId.renderer,
second: EditorId.html
});
appState.resetEditorLayout();
expect(appState.mosaicArrangement).toEqual(DEFAULT_MOSAIC_ARRANGEMENT);
});
});
});
+5 -3
View File
@@ -1,7 +1,7 @@
import { VersionSource, VersionState } from '../../src/interfaces';
import { AppState } from '../../src/renderer/state';
import { TouchBarManager } from '../../src/renderer/touch-bar-manager';
import { mockVersions } from '../mocks/electron-versions';
import { mockVersions, mockVersionsArray } from '../mocks/electron-versions';
import { overridePlatform, resetPlatform } from '../utils';
const { lastElectronVersion } = require('../fixtures/releases-metadata.json');
@@ -27,7 +27,8 @@ describe('TouchBarManager', () => {
it('creates a touch bar with versions', () => {
const touchBarMgr = new TouchBarManager(appState);
expect(touchBarMgr.versionSelector.items).toHaveLength(3);
const expectedLength = Object.keys(appState.versions).length;
expect(touchBarMgr.versionSelector.items).toHaveLength(expectedLength);
const [ item ] = touchBarMgr.versionSelector.items;
@@ -43,7 +44,8 @@ describe('TouchBarManager', () => {
version: '3.3.3'
};
expect(touchBarMgr.versionSelector.items).toHaveLength(4);
const expectedLength = Object.keys(appState.versions).length;
expect(touchBarMgr.versionSelector.items).toHaveLength(expectedLength);
const [ item ] = touchBarMgr.versionSelector.items;
+2 -2
View File
@@ -16,11 +16,11 @@ describe('getEditorModel()', () => {
fiddle.editors[EditorId.html] = oldEditor;
});
it('returns null if window.Fiddle is not ready', () => {
it('throws if window.Fiddle is not ready', () => {
const { ElectronFiddle: fiddle } = window as any;
(window as any).ElectronFiddle = undefined;
expect(getEditorModel(EditorId.html)).toEqual(null);
expect(() => getEditorModel(EditorId.html)).toThrow('Fiddle not ready');
window.ElectronFiddle = fiddle;
});
-23
View File
@@ -1,23 +0,0 @@
import { waitForEditorsToMount } from '../../src/utils/editor-mounted';
import { EditorId } from '../../src/interfaces';
describe('waitforEditorsToMount', () => {
it('resolves when editors match list', async () => {
// these editors should be mounted by default.
const { ElectronFiddle: fiddle } = window as any;
await waitForEditorsToMount([EditorId.html, EditorId.main, EditorId.renderer]);
expect(fiddle.editors).toHaveProperty(EditorId.html);
expect(fiddle.editors).toHaveProperty(EditorId.main);
expect(fiddle.editors).toHaveProperty(EditorId.renderer);
});
it('rejects if editors fail to match list', async () => {
try {
// css editor shouldn't be mounted by default.
await waitForEditorsToMount([EditorId.css]);
} catch (error) {
expect(error).toMatch('Timed out');
}
});
});
+3 -26
View File
@@ -1,39 +1,16 @@
import { EditorId } from '../../src/interfaces';
import { getEditorValue } from '../../src/utils/editor-value';
import { MockState } from '../mocks/state';
describe('getEditorValue()', () => {
it('returns the value for an editor if it exists', () => {
it('returns the value for an editor', () => {
expect(getEditorValue(EditorId.html)).toBe('editor-value');
});
it('returns the value for the editor backup if it exists', () => {
// set up mock state that has the editor deleted and a backup
const oldEditor = window.ElectronFiddle.editors[EditorId.html];
window.ElectronFiddle.editors[EditorId.html] = null;
window.ElectronFiddle.app.state = (new MockState() as any);
const mockState = window.ElectronFiddle.app.state;
mockState.closedPanels = {
[EditorId.html]: {
value: 'editor-backup-value'
}
};
// assert
expect(getEditorValue(EditorId.html)).toBe('editor-backup-value');
// revert to initial state
delete window.ElectronFiddle.app.state;
window.ElectronFiddle.editors[EditorId.html] = oldEditor;
});
it('returns an empty string if window.Fiddle is not ready', () => {
it('throws if window.Fiddle is not ready', () => {
const oldFiddle = window.ElectronFiddle;
(window as any).ElectronFiddle = undefined;
expect(getEditorValue(EditorId.html)).toBe('');
expect(() => getEditorValue(EditorId.html)).toThrow('Fiddle not ready');
window.ElectronFiddle = oldFiddle;
});
+1 -1
View File
@@ -20,7 +20,7 @@ describe('getEditorViewState()', () => {
const { ElectronFiddle: fiddle } = window as any;
(window as any).ElectronFiddle = undefined;
expect(getEditorViewState(EditorId.html)).toBeNull();
expect(() => getEditorViewState(EditorId.html)).toThrow('Fiddle not ready');
window.ElectronFiddle = fiddle;
});
-12173
View File
File diff suppressed because it is too large Load Diff