Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3153cfc4b7 | ||
|
|
9065a8678a | ||
|
|
016d3ee78a | ||
|
|
4b40420bba | ||
|
|
0e872b0dd0 | ||
|
|
7638111598 | ||
|
|
a0e59ad288 | ||
|
|
3c6103dc50 | ||
|
|
b98472f973 | ||
|
|
3602eacc88 | ||
|
|
8b84c040c7 | ||
|
|
6aa19eada6 | ||
|
|
8e92183de2 | ||
|
|
1a951f3f44 | ||
|
|
66ead93f05 | ||
|
|
d22899b4a5 | ||
|
|
77d4bc6fc5 | ||
|
|
961d42d74f | ||
|
|
c58ade2132 | ||
|
|
592c220f18 | ||
|
|
4da0e82e9b | ||
|
|
f766bef10c | ||
|
|
30ba9fb023 | ||
|
|
f3e847da85 | ||
|
|
0f854441e4 | ||
|
|
f31a4e38bb | ||
|
|
26bb3e4944 | ||
|
|
fb1fb7869c | ||
|
|
a1e67400bf | ||
|
|
a26aa1e70c | ||
|
|
78395412f4 | ||
|
|
1f72db3cfc | ||
|
|
c333a1e132 | ||
|
|
a0a989d7a7 | ||
|
|
5b07b8a706 |
+18
-1
@@ -39,10 +39,27 @@ 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
|
||||
```
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@ 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).
|
||||
|
||||
+4
-5
@@ -16,12 +16,11 @@ 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',
|
||||
|
||||
Generated
+4658
-8350
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "electron-fiddle",
|
||||
"productName": "Electron Fiddle",
|
||||
"version": "0.13.1",
|
||||
"version": "0.15.1",
|
||||
"description": "The easiest way to get started with Electron",
|
||||
"repository": "https://github.com/electron/fiddle",
|
||||
"main": "./dist/src/main/main",
|
||||
|
||||
+3
-3
@@ -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.
|
||||
// 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.
|
||||
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()
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ initSentry();
|
||||
import { app } from 'electron';
|
||||
|
||||
import { isDevMode } from '../utils/devmode';
|
||||
import { setupAboutPanel } from '../utils/set-about-panel';
|
||||
import { setupAboutPanel } from './about-panel';
|
||||
import { setupDevTools } from './devtools';
|
||||
import { setupDialogs } from './dialogs';
|
||||
import { onFirstRunMaybe } from './first-run';
|
||||
|
||||
+2
-4
@@ -105,9 +105,7 @@ function getQuitItems(): Array<MenuItemConstructorOptions> {
|
||||
{
|
||||
type: 'separator'
|
||||
}, {
|
||||
label: 'Exit',
|
||||
accelerator: 'Ctrl+Q,',
|
||||
click: app.quit
|
||||
role: 'quit'
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -239,7 +237,7 @@ export function setupMenu() {
|
||||
// Append the "Settings" item
|
||||
if (
|
||||
process.platform === 'darwin'
|
||||
&& label === app.getName()
|
||||
&& label === app.name
|
||||
&& isSubmenu(item.submenu)
|
||||
) {
|
||||
item.submenu.splice(2, 0, ...getPreferencesItems());
|
||||
|
||||
+10
-5
@@ -5,7 +5,7 @@ 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> = [];
|
||||
export let browserWindows: Array<BrowserWindow | null> = [];
|
||||
|
||||
/**
|
||||
* Gets default options for the main window
|
||||
@@ -37,13 +37,14 @@ export function getMainWindowOptions(): Electron.BrowserWindowConstructorOptions
|
||||
*/
|
||||
export function createMainWindow(): Electron.BrowserWindow {
|
||||
console.log(`Creating main window`);
|
||||
const browserWindow = new BrowserWindow(getMainWindowOptions());
|
||||
let browserWindow: BrowserWindow | null;
|
||||
browserWindow = new BrowserWindow(getMainWindowOptions());
|
||||
browserWindow.loadFile('./dist/static/index.html');
|
||||
|
||||
browserWindow.webContents.once('dom-ready', () => {
|
||||
browserWindow.show();
|
||||
|
||||
if (browserWindow) {
|
||||
browserWindow.show();
|
||||
|
||||
createContextMenu(browserWindow);
|
||||
}
|
||||
});
|
||||
@@ -51,6 +52,8 @@ export function createMainWindow(): Electron.BrowserWindow {
|
||||
browserWindow.on('closed', () => {
|
||||
browserWindows = browserWindows
|
||||
.filter((bw) => browserWindow !== bw);
|
||||
|
||||
browserWindow = null;
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('new-window', (event, url) => {
|
||||
@@ -64,7 +67,9 @@ export function createMainWindow(): Electron.BrowserWindow {
|
||||
});
|
||||
|
||||
ipcMainManager.on(IpcEvents.SHOW_INACTIVE, () => {
|
||||
browserWindow.showInactive();
|
||||
if (browserWindow) {
|
||||
browserWindow.showInactive();
|
||||
}
|
||||
});
|
||||
|
||||
browserWindows.push(browserWindow);
|
||||
|
||||
+14
-2
@@ -18,6 +18,7 @@ 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';
|
||||
@@ -68,8 +69,17 @@ export class App {
|
||||
}
|
||||
}
|
||||
|
||||
// set values once prompt approves
|
||||
await this.setEditorValues(editorValues);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
document.title = getTitle(this.state);
|
||||
this.state.gistId = gistId || '';
|
||||
@@ -77,6 +87,8 @@ 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,4 +1,4 @@
|
||||
import { Button, Menu, MenuItem, Popover, Position } from '@blueprintjs/core';
|
||||
import { Button, Menu, MenuDivider, MenuItem, Popover, Position } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
@@ -82,6 +82,18 @@ 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,11 +1,13 @@
|
||||
import { Button, ButtonGroup, Menu, MenuItem, Popover, Position } from '@blueprintjs/core';
|
||||
import { Button, ButtonGroup, IToastProps, Menu, MenuItem, Popover, Position, Toaster } 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';
|
||||
|
||||
@@ -13,6 +15,11 @@ export interface PublishButtonProps {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
interface IPublishButtonState {
|
||||
readonly isUpdating: boolean;
|
||||
readonly wouldPublish: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "publish" button takes care of logging you in.
|
||||
*
|
||||
@@ -21,15 +28,25 @@ export interface PublishButtonProps {
|
||||
* @extends {React.Component<PublishButtonProps, PublishButtonState>}
|
||||
*/
|
||||
@observer
|
||||
export class PublishButton extends React.Component<PublishButtonProps> {
|
||||
constructor(props: PublishButtonProps) {
|
||||
export class PublishButton extends React.Component<PublishButtonProps, IPublishButtonState> {
|
||||
public constructor(props: PublishButtonProps) {
|
||||
super(props);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
this.publishFiddle = this.publishFiddle.bind(this);
|
||||
this.publishOrUpdateFiddle = this.publishOrUpdateFiddle.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);
|
||||
}
|
||||
@@ -60,7 +77,7 @@ export class PublishButton extends React.Component<PublishButtonProps> {
|
||||
await when(() => !!appState.gitHubToken || !appState.isTokenDialogShowing);
|
||||
|
||||
if (appState.gitHubToken) {
|
||||
return this.publishFiddle();
|
||||
return this.publishOrUpdateFiddle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,50 +85,52 @@ export class PublishButton extends React.Component<PublishButtonProps> {
|
||||
* Connect with GitHub, publish the current Fiddle as a gist,
|
||||
* and update all related properties in the app state.
|
||||
*/
|
||||
public async publishFiddle(): Promise<void> {
|
||||
public async publishOrUpdateFiddle(): Promise<void> {
|
||||
const { appState } = this.props;
|
||||
const { wouldPublish } = this.state;
|
||||
appState.isPublishing = true;
|
||||
|
||||
const octo = await getOctokit(this.props.appState);
|
||||
const { gitHubPublishAsPublic } = this.props.appState;
|
||||
const { gitHubPublishAsPublic, gistId } = this.props.appState;
|
||||
const options = { includeDependencies: true, includeElectron: true };
|
||||
const values = await window.ElectronFiddle.app.getEditorValues(options);
|
||||
|
||||
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
|
||||
if (gistId && !wouldPublish) {
|
||||
this.setState({
|
||||
isUpdating: true
|
||||
});
|
||||
const gist = await octo.gists.update({
|
||||
gist_id: appState.gistId,
|
||||
files: this.gistFilesList(values) as any,
|
||||
});
|
||||
|
||||
appState.gistId = gist.data.id;
|
||||
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
|
||||
});
|
||||
|
||||
console.log(`Publish Button: Publishing done`, { gist });
|
||||
} catch (error) {
|
||||
console.warn(`Could not publish gist`, { error });
|
||||
appState.gistId = gist.data.id;
|
||||
|
||||
const messageBoxOptions: Electron.MessageBoxOptions = {
|
||||
message: 'Publishing Fiddle to GitHub failed. Are you connected to the Internet?',
|
||||
detail: `GitHub encountered the following error: ${error.message}`
|
||||
};
|
||||
console.log(`Publish Button: Publishing done`, { gist });
|
||||
this.renderToast({ message: 'Publishing done successfully!' });
|
||||
} catch (error) {
|
||||
console.warn(`Could not publish gist`, { error });
|
||||
|
||||
ipcRendererManager.send(IpcEvents.SHOW_WARNING_DIALOG, messageBoxOptions);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
appState.isPublishing = false;
|
||||
@@ -136,8 +155,77 @@ export class PublishButton extends React.Component<PublishButtonProps> {
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { gitHubPublishAsPublic } = this.props.appState;
|
||||
const { isPublishing } = this.props.appState;
|
||||
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 privacyIcon = gitHubPublishAsPublic ? 'unlock' : 'lock';
|
||||
const privacyMenu = (
|
||||
@@ -158,28 +246,48 @@ export class PublishButton extends React.Component<PublishButtonProps> {
|
||||
);
|
||||
|
||||
return (
|
||||
<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>
|
||||
<Popover
|
||||
content={privacyMenu}
|
||||
position={Position.BOTTOM}
|
||||
>
|
||||
<Button
|
||||
icon={privacyIcon}
|
||||
/>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,8 @@ export class BisectDialog extends React.Component<BisectDialogProps, BisectDialo
|
||||
}
|
||||
|
||||
const bisectRange = allVersions
|
||||
.slice(endIndex, startIndex + 1);
|
||||
.slice(endIndex, startIndex + 1)
|
||||
.reverse();
|
||||
|
||||
appState.Bisector = new Bisector(bisectRange);
|
||||
const initialBisectPivot = appState.Bisector.getCurrentVersion().version;
|
||||
|
||||
@@ -69,6 +69,7 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,8 @@ export class Editor extends React.Component<EditorProps> {
|
||||
return false;
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.initMonaco();
|
||||
public async componentDidMount() {
|
||||
await this.initMonaco();
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
@@ -120,7 +120,7 @@ export class Editor extends React.Component<EditorProps> {
|
||||
* @private
|
||||
* @param {string} value
|
||||
*/
|
||||
private async createModel(value: string) {
|
||||
private 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);
|
||||
await this.createModel(value);
|
||||
this.createModel(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,10 +301,10 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
|
||||
}
|
||||
|
||||
if (!this.state || !this.state.isMounted) {
|
||||
this.state = {
|
||||
this.setState({
|
||||
monaco,
|
||||
monacoOptions: defaultMonacoOptions
|
||||
};
|
||||
});
|
||||
} else {
|
||||
this.setState({ monaco });
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ export class ExecutionSettings extends React.Component<ExecutionSettingsProps, {
|
||||
<FormGroup>
|
||||
<p>
|
||||
Electron allows starting the executable with <a
|
||||
href='https://electronjs.org/docs/api/chrome-command-line-switches'
|
||||
href='https://www.electronjs.org/docs/api/command-line-switches'
|
||||
>
|
||||
user-provided flags
|
||||
</a>
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as path from 'path';
|
||||
import { MosaicNode } from 'react-mosaic-component';
|
||||
|
||||
import { EditorId, MosaicId } from '../interfaces';
|
||||
import { EditorBackup } from '../utils/editor-backup';
|
||||
import { EditorBackup, getEditorBackup } from '../utils/editor-backup';
|
||||
|
||||
// Reminder: When testing, this file is mocked in tests/setup.js
|
||||
|
||||
@@ -21,7 +21,15 @@ export const DEFAULT_MOSAIC_ARRANGEMENT: MosaicNode<MosaicId> = {
|
||||
};
|
||||
|
||||
export const DEFAULT_CLOSED_PANELS: Partial<Record<MosaicId, EditorBackup | true>> = {
|
||||
docsDemo: true
|
||||
docsDemo: true,
|
||||
preload: getEditorBackup(EditorId.preload),
|
||||
css: getEditorBackup(EditorId.css),
|
||||
};
|
||||
|
||||
export const EMPTY_EDITOR_CONTENT = {
|
||||
html: '<!-- Empty -->',
|
||||
js: '// Empty',
|
||||
css: '/* Empty */'
|
||||
};
|
||||
|
||||
export const ELECTRON_ORG = 'electron';
|
||||
|
||||
@@ -193,9 +193,11 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
+29
-6
@@ -32,6 +32,7 @@ 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,
|
||||
@@ -139,6 +140,7 @@ 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);
|
||||
|
||||
ipcRendererManager.removeAllListeners(IpcEvents.OPEN_SETTINGS);
|
||||
ipcRendererManager.removeAllListeners(IpcEvents.SHOW_WELCOME_TOUR);
|
||||
@@ -465,10 +467,14 @@ 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}`);
|
||||
this.localTypeWatcher = fs.watch(typePath!, async () => {
|
||||
console.info(`TypeDefs: Noticed file change at ${typePath}. Updating editor typedefs.`);
|
||||
await updateEditorTypeDefinitions(versionObject);
|
||||
});
|
||||
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.');
|
||||
}
|
||||
} else {
|
||||
if (!!this.localTypeWatcher) {
|
||||
console.info(`TypeDefs: Switched to downloaded version ${version}. Unwatching local typedefs.`);
|
||||
@@ -539,7 +545,6 @@ 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) {
|
||||
@@ -592,7 +597,7 @@ export class AppState {
|
||||
return null;
|
||||
}
|
||||
|
||||
@action public setVisibleMosaics(visible: Array<MosaicId>) {
|
||||
@action public async setVisibleMosaics(visible: Array<MosaicId>) {
|
||||
const currentlyVisible = getVisibleMosaics(this.mosaicArrangement);
|
||||
|
||||
for (const id of ALL_MOSAICS) {
|
||||
@@ -600,6 +605,9 @@ 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
|
||||
@@ -613,6 +621,12 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -635,6 +649,15 @@ 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.
|
||||
*
|
||||
|
||||
@@ -12,7 +12,7 @@ export function getEditorModel(id: EditorId): editor.ITextModel | null {
|
||||
const { ElectronFiddle: fiddle } = window;
|
||||
|
||||
if (!fiddle) {
|
||||
throw new Error('Fiddle not ready');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (fiddle.editors[id] && fiddle.editors[id]!.getModel) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
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);
|
||||
})();
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { EditorId } from '../interfaces';
|
||||
import { EditorBackup } from './editor-backup';
|
||||
|
||||
/**
|
||||
* Return the value for a given editor
|
||||
@@ -10,11 +11,16 @@ export function getEditorValue(id: EditorId): string {
|
||||
const { ElectronFiddle: fiddle } = window;
|
||||
|
||||
if (!fiddle) {
|
||||
throw new Error('Fiddle not ready');
|
||||
return '';
|
||||
}
|
||||
|
||||
if (fiddle.editors[id] && fiddle.editors[id]!.getValue) {
|
||||
const editor = fiddle.editors[id];
|
||||
const backup = fiddle.app?.state?.closedPanels[id] as EditorBackup;
|
||||
|
||||
if (editor?.getValue) {
|
||||
return fiddle.editors[id]!.getValue();
|
||||
} else if (backup?.value) {
|
||||
return backup.value;
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
@@ -13,7 +13,7 @@ export function getEditorViewState(id: EditorId): editor.ICodeEditorViewState |
|
||||
const { ElectronFiddle: fiddle } = window;
|
||||
|
||||
if (!fiddle) {
|
||||
throw new Error('Fiddle not ready');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (fiddle.editors[id] && fiddle.editors[id]!.saveViewState) {
|
||||
|
||||
@@ -8,5 +8,9 @@
|
||||
<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>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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.getName()}`)
|
||||
console.log(`It's named: ${app.name}`)
|
||||
console.log(`It has the version: ${app.getVersion()}`)
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"expectedVersionCount": 325,
|
||||
"lastElectronVersion": "9.0.0-beta.9"
|
||||
"expectedVersionCount": 370,
|
||||
"lastElectronVersion": "8.3.4"
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@ import { main, onBeforeQuit, onReady, onWindowsAllClosed } from '../../src/main/
|
||||
import { shouldQuit } from '../../src/main/squirrel';
|
||||
import { setupUpdates } from '../../src/main/update';
|
||||
import { getOrCreateMainWindow } from '../../src/main/windows';
|
||||
import { setupAboutPanel } from '../../src/utils/set-about-panel';
|
||||
import { setupAboutPanel } from '../../src/main/about-panel';
|
||||
|
||||
jest.mock('../../src/main/windows', () => ({
|
||||
getOrCreateMainWindow: jest.fn()
|
||||
}));
|
||||
|
||||
jest.mock('../../src/utils/set-about-panel', () => ({
|
||||
jest.mock('../../src/main/about-panel', () => ({
|
||||
setupAboutPanel: jest.fn()
|
||||
}));
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ 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({}));
|
||||
});
|
||||
|
||||
@@ -114,6 +114,7 @@ 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 = {
|
||||
@@ -143,6 +144,7 @@ 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 = {
|
||||
@@ -172,6 +174,7 @@ 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 = {
|
||||
@@ -195,6 +198,7 @@ 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();
|
||||
|
||||
@@ -226,6 +230,7 @@ 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,6 +52,17 @@ 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>,
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -112,6 +123,17 @@ 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}
|
||||
@@ -203,6 +225,17 @@ 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,63 +1,71 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Publish button component renders 1`] = `
|
||||
<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"
|
||||
<Fragment>
|
||||
<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.Button
|
||||
icon="unlock"
|
||||
/>
|
||||
</Blueprint3.Popover>
|
||||
<Blueprint3.Button
|
||||
icon="unlock"
|
||||
icon="upload"
|
||||
onClick={[Function]}
|
||||
text="Publish"
|
||||
/>
|
||||
</Blueprint3.Popover>
|
||||
<Blueprint3.Button
|
||||
icon="upload"
|
||||
onClick={[Function]}
|
||||
text="Publish"
|
||||
/>
|
||||
</Blueprint3.ButtonGroup>
|
||||
</fieldset>
|
||||
</Blueprint3.ButtonGroup>
|
||||
</fieldset>
|
||||
<Blueprint3.Toaster
|
||||
autoFocus={false}
|
||||
canEscapeKeyClear={true}
|
||||
position="bottom-right"
|
||||
usePortal={true}
|
||||
/>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
@@ -40,7 +40,7 @@ exports[`ExecutionSettings component renders 1`] = `
|
||||
<p>
|
||||
Electron allows starting the executable with
|
||||
<a
|
||||
href="https://electronjs.org/docs/api/chrome-command-line-switches"
|
||||
href="https://www.electronjs.org/docs/api/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.publishFiddle = jest.fn();
|
||||
instance.publishOrUpdateFiddle = jest.fn();
|
||||
await instance.handleClick();
|
||||
|
||||
expect(instance.publishFiddle).toHaveBeenCalled();
|
||||
expect(instance.publishOrUpdateFiddle).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.publishFiddle();
|
||||
await instance.publishOrUpdateFiddle();
|
||||
|
||||
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.publishFiddle();
|
||||
await instance.publishOrUpdateFiddle();
|
||||
|
||||
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.publishFiddle();
|
||||
await instance.publishOrUpdateFiddle();
|
||||
|
||||
expect(store.isPublishing).toBe(false);
|
||||
});
|
||||
@@ -147,7 +147,7 @@ describe('Publish button component', () => {
|
||||
const instance: PublishButton = wrapper.instance() as any;
|
||||
|
||||
instance.setPrivate();
|
||||
await instance.publishFiddle();
|
||||
await instance.publishOrUpdateFiddle();
|
||||
|
||||
expect(mockOctokit.gists.create).toHaveBeenCalledWith({
|
||||
...expectedGistCreateOpts,
|
||||
@@ -155,7 +155,7 @@ describe('Publish button component', () => {
|
||||
});
|
||||
|
||||
instance.setPublic();
|
||||
await instance.publishFiddle();
|
||||
await instance.publishOrUpdateFiddle();
|
||||
|
||||
expect(mockOctokit.gists.create).toHaveBeenCalledWith({
|
||||
...expectedGistCreateOpts,
|
||||
@@ -170,7 +170,7 @@ describe('Publish button component', () => {
|
||||
|
||||
expect(wrapper.find('fieldset').prop('disabled')).toBe(false);
|
||||
|
||||
instance.publishFiddle = jest.fn().mockImplementationOnce(() => {
|
||||
instance.publishOrUpdateFiddle = 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.publishFiddle();
|
||||
await instance.publishOrUpdateFiddle();
|
||||
});
|
||||
|
||||
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));
|
||||
expect(Bisector).toHaveBeenCalledWith(versions.slice(0, 5).reverse());
|
||||
expect(store.Bisector).toBeDefined();
|
||||
expect(store.setVersion).toHaveBeenCalledWith(version);
|
||||
});
|
||||
|
||||
@@ -181,6 +181,7 @@ describe('TokenDialog component', () => {
|
||||
await instance.onSubmitToken();
|
||||
|
||||
expect(wrapper.state('error')).toBe(true);
|
||||
expect(store.gitHubToken).toEqual(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import * as MonacoType from 'monaco-editor';
|
||||
|
||||
import { ALL_MOSAICS, EditorId, GenericDialogType, PanelId, VersionSource, VersionState } from '../../src/interfaces';
|
||||
import { getDownloadedVersions, getDownloadingVersions, removeBinary, setupBinary } from '../../src/renderer/binary';
|
||||
import { Bisector } from '../../src/renderer/bisect';
|
||||
@@ -511,10 +513,14 @@ describe('AppState', () => {
|
||||
});
|
||||
|
||||
describe('setVisibleMosaics()', () => {
|
||||
it('updates the visible editors and creates a backup', () => {
|
||||
it('updates the visible editors and creates a backup', async () => {
|
||||
appState.mosaicArrangement = createMosaicArrangement(ALL_MOSAICS);
|
||||
appState.closedPanels = {};
|
||||
appState.setVisibleMosaics([EditorId.main]);
|
||||
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);
|
||||
|
||||
expect(appState.mosaicArrangement).toEqual(EditorId.main);
|
||||
expect(appState.closedPanels[EditorId.renderer]).toBeTruthy();
|
||||
@@ -522,10 +528,17 @@ describe('AppState', () => {
|
||||
expect(appState.closedPanels[EditorId.main]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('removes the backup for a non-editor right away', () => {
|
||||
it('removes the backup for a non-editor right away', async () => {
|
||||
appState.closedPanels = {};
|
||||
appState.closedPanels[PanelId.docsDemo] = true;
|
||||
appState.setVisibleMosaics(ALL_MOSAICS);
|
||||
|
||||
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);
|
||||
|
||||
expect(appState.closedPanels[PanelId.docsDemo]).toBeUndefined();
|
||||
});
|
||||
@@ -560,4 +573,20 @@ 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,11 +16,11 @@ describe('getEditorModel()', () => {
|
||||
fiddle.editors[EditorId.html] = oldEditor;
|
||||
});
|
||||
|
||||
it('throws if window.Fiddle is not ready', () => {
|
||||
it('returns null if window.Fiddle is not ready', () => {
|
||||
const { ElectronFiddle: fiddle } = window as any;
|
||||
(window as any).ElectronFiddle = undefined;
|
||||
|
||||
expect(() => getEditorModel(EditorId.html)).toThrow('Fiddle not ready');
|
||||
expect(getEditorModel(EditorId.html)).toEqual(null);
|
||||
|
||||
window.ElectronFiddle = fiddle;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
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');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,16 +1,39 @@
|
||||
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', () => {
|
||||
it('returns the value for an editor if it exists', () => {
|
||||
expect(getEditorValue(EditorId.html)).toBe('editor-value');
|
||||
});
|
||||
|
||||
it('throws if window.Fiddle is not ready', () => {
|
||||
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', () => {
|
||||
const oldFiddle = window.ElectronFiddle;
|
||||
(window as any).ElectronFiddle = undefined;
|
||||
|
||||
expect(() => getEditorValue(EditorId.html)).toThrow('Fiddle not ready');
|
||||
expect(getEditorValue(EditorId.html)).toBe('');
|
||||
|
||||
window.ElectronFiddle = oldFiddle;
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('getEditorViewState()', () => {
|
||||
const { ElectronFiddle: fiddle } = window as any;
|
||||
(window as any).ElectronFiddle = undefined;
|
||||
|
||||
expect(() => getEditorViewState(EditorId.html)).toThrow('Fiddle not ready');
|
||||
expect(getEditorViewState(EditorId.html)).toBeNull();
|
||||
|
||||
window.ElectronFiddle = fiddle;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user