Compare commits
71
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c06a33018 | ||
|
|
af4155b2df | ||
|
|
2a67e7e6cf | ||
|
|
351c5caf23 | ||
|
|
36780a95e7 | ||
|
|
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 | ||
|
|
98a358b756 | ||
|
|
548b68e208 | ||
|
|
683def4215 | ||
|
|
5596c12f19 | ||
|
|
db9c1d5105 | ||
|
|
b52e70ec61 | ||
|
|
305bb9afbe | ||
|
|
d121954018 | ||
|
|
e51b1648cb | ||
|
|
ccc72ee70a | ||
|
|
48c62f4e02 | ||
|
|
9b55b2ef26 | ||
|
|
5b91177348 | ||
|
|
f8c43a07f7 | ||
|
|
5eba9cdbe2 | ||
|
|
8ee42c290e | ||
|
|
c2b3954dfa | ||
|
|
e61599e9c5 | ||
|
|
1fed772a27 | ||
|
|
de85d0f324 | ||
|
|
c89671d99d | ||
|
|
155b901ade | ||
|
|
8e098deab8 | ||
|
|
2b5ea7d467 | ||
|
|
91efa5a009 | ||
|
|
4ec5bec719 | ||
|
|
84ac41f147 | ||
|
|
e9a2985291 | ||
|
|
9b28919203 | ||
|
|
8d43836787 | ||
|
|
48d0dadf5d | ||
|
|
2f9ef21251 |
@@ -0,0 +1,210 @@
|
||||
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.*/
|
||||
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/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
|
||||
@@ -21,7 +21,6 @@
|
||||
/out/
|
||||
node_modules/
|
||||
SHASUMS256.txt
|
||||
**/yarn.lock
|
||||
compile_commands.json
|
||||
.envrc
|
||||
.cache
|
||||
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
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
|
||||
+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).
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
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
|
||||
+14
-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',
|
||||
@@ -106,11 +105,21 @@ function notarizeMaybe() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.env.npm_lifecycle_event !== 'publish') {
|
||||
console.log(`Not in "publish" mode, skipping notarization`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env.CI) {
|
||||
console.log(`Not in CI, skipping notarization`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env.TRAVIS_TAG && !process.env.NOTARIZE_WITHOUT_TAG) {
|
||||
console.log(`Not a tag, not notarizing`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env.APPLE_ID || !process.env.APPLE_ID_PASSWORD) {
|
||||
console.warn('Should be notarizing, but environment variables APPLE_ID or APPLE_ID_PASSWORD are missing!');
|
||||
return;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"testURL": "http://localhost",
|
||||
"testRegex": "(-spec)\\.(ts|tsx)$",
|
||||
"resetMocks": true,
|
||||
"bail": true,
|
||||
"resetModules": true,
|
||||
"snapshotSerializers": [
|
||||
"enzyme-to-json/serializer"
|
||||
|
||||
Generated
-21895
File diff suppressed because it is too large
Load Diff
+9
-9
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "electron-fiddle",
|
||||
"productName": "Electron Fiddle",
|
||||
"version": "0.13.0",
|
||||
"version": "0.15.1",
|
||||
"description": "The easiest way to get started with Electron",
|
||||
"repository": "https://github.com/electron/fiddle",
|
||||
"main": "./dist/src/main/main",
|
||||
@@ -22,7 +22,7 @@
|
||||
"test": "jest --config=jest.json",
|
||||
"test:ci": "jest --config=jest.json --coverage --runInBand",
|
||||
"test:coverage": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
|
||||
"test:report": "jest --config=jest.json --json --outputFile=report.json | exit 0",
|
||||
"test:report": "jest --config=jest.json --json --bail=false --outputFile=report.json | true",
|
||||
"tsc": "tsc --noEmit -p .",
|
||||
"electron-releases": "node --unhandled-rejections=strict ./tools/fetch-releases.js"
|
||||
},
|
||||
@@ -39,12 +39,12 @@
|
||||
"dependencies": {
|
||||
"@blueprintjs/core": "^3.24.0",
|
||||
"@blueprintjs/select": "^3.12.0",
|
||||
"@electron/get": "^1.9.0",
|
||||
"@octokit/rest": "^16.43.1",
|
||||
"@sentry/electron": "^1.2.1",
|
||||
"classnames": "^2.2.6",
|
||||
"electron-default-menu": "^1.0.1",
|
||||
"electron-devtools-installer": "^2.2.4",
|
||||
"electron-download": "^4.1.1",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"extract-zip": "^1.6.7",
|
||||
"fix-path": "^3.0.0",
|
||||
@@ -64,12 +64,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.8.6",
|
||||
"@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",
|
||||
"@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",
|
||||
"@octokit/action": "^2.0.0",
|
||||
"@types/classnames": "^2.2.9",
|
||||
"@types/enzyme": "^3.10.5",
|
||||
|
||||
+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()
|
||||
}
|
||||
|
||||
+7
-5
@@ -4,13 +4,14 @@ export type Files = Map<string, string>;
|
||||
|
||||
export type FileTransform = (files: Files) => Promise<Files>;
|
||||
|
||||
export enum ElectronVersionState {
|
||||
export enum VersionState {
|
||||
ready = 'ready',
|
||||
downloading = 'downloading',
|
||||
unzipping = 'unzipping',
|
||||
unknown = 'unknown'
|
||||
}
|
||||
|
||||
export enum ElectronVersionSource {
|
||||
export enum VersionSource {
|
||||
remote = 'remote',
|
||||
local = 'local'
|
||||
}
|
||||
@@ -29,9 +30,10 @@ export interface EditorValues {
|
||||
package?: string;
|
||||
}
|
||||
|
||||
export interface ElectronVersion extends Version {
|
||||
state: ElectronVersionState;
|
||||
source: ElectronVersionSource;
|
||||
export interface RunnableVersion extends Version {
|
||||
state: VersionState;
|
||||
source: VersionSource;
|
||||
downloadProgress?: number;
|
||||
}
|
||||
|
||||
export interface SetFiddleOptions {
|
||||
|
||||
+10
-2
@@ -23,6 +23,10 @@ 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',
|
||||
}
|
||||
|
||||
export const ipcMainEvents = [
|
||||
@@ -30,7 +34,9 @@ export const ipcMainEvents = [
|
||||
IpcEvents.FS_SAVE_FIDDLE,
|
||||
IpcEvents.SHOW_WARNING_DIALOG,
|
||||
IpcEvents.SHOW_CONFIRMATION_DIALOG,
|
||||
IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG
|
||||
IpcEvents.SHOW_LOCAL_VERSION_FOLDER_DIALOG,
|
||||
IpcEvents.SHOW_INACTIVE,
|
||||
IpcEvents.CONFIRM_QUIT,
|
||||
];
|
||||
|
||||
export const ipcRendererEvents = [
|
||||
@@ -52,7 +58,9 @@ export const ipcRendererEvents = [
|
||||
IpcEvents.SHOW_WELCOME_TOUR,
|
||||
IpcEvents.CLEAR_CONSOLE,
|
||||
IpcEvents.LOAD_LOCAL_VERSION_FOLDER,
|
||||
IpcEvents.BISECT_COMMANDS_TOGGLE
|
||||
IpcEvents.BISECT_COMMANDS_TOGGLE,
|
||||
IpcEvents.BEFORE_QUIT,
|
||||
IpcEvents.SET_APPDATA_DIR,
|
||||
];
|
||||
|
||||
export const WEBCONTENTS_READY_FOR_IPC_SIGNAL = 'WEBCONTENTS_READY_FOR_IPC_SIGNAL';
|
||||
|
||||
+10
-1
@@ -3,11 +3,13 @@ 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';
|
||||
@@ -41,7 +43,14 @@ export async function onReady() {
|
||||
* @export
|
||||
*/
|
||||
export function onBeforeQuit() {
|
||||
(global as any).isQuitting = true;
|
||||
ipcMainManager.send(IpcEvents.BEFORE_QUIT);
|
||||
ipcMainManager.on(IpcEvents.CONFIRM_QUIT, quitAppIfConfirmed);
|
||||
}
|
||||
|
||||
export function quitAppIfConfirmed(_: Electron.IpcMainEvent, quitConfirmed: boolean) {
|
||||
if (quitConfirmed) {
|
||||
app.quit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+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());
|
||||
|
||||
+19
-5
@@ -1,9 +1,11 @@
|
||||
import { BrowserWindow, shell } from 'electron';
|
||||
import { app, 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> = [];
|
||||
export let browserWindows: Array<BrowserWindow | null> = [];
|
||||
|
||||
/**
|
||||
* Gets default options for the main window
|
||||
@@ -35,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);
|
||||
}
|
||||
});
|
||||
@@ -49,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) => {
|
||||
@@ -61,6 +66,15 @@ export function createMainWindow(): Electron.BrowserWindow {
|
||||
shell.openExternal(url);
|
||||
});
|
||||
|
||||
ipcMainManager.on(IpcEvents.SHOW_INACTIVE, () => {
|
||||
if (browserWindow) {
|
||||
browserWindow.showInactive();
|
||||
}
|
||||
});
|
||||
|
||||
const appData = app.getPath('appData');
|
||||
ipcMainManager.send(IpcEvents.SET_APPDATA_DIR, [appData], browserWindow.webContents);
|
||||
|
||||
browserWindows.push(browserWindow);
|
||||
|
||||
return browserWindow;
|
||||
|
||||
+14
-24
@@ -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,8 +87,9 @@ 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;
|
||||
this.setupUnsavedOnChangeListener();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -177,30 +188,9 @@ export class App {
|
||||
|
||||
ipcRenderer.send(WEBCONTENTS_READY_FOR_IPC_SIGNAL);
|
||||
|
||||
// TODO: A timer here is terrible. Let's fix this
|
||||
// and ensure we actually do it once Editors have mounted.
|
||||
setTimeout(() => {
|
||||
this.setupUnsavedOnChangeListener();
|
||||
}, 1500);
|
||||
|
||||
return rendered;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the editor is changed for the first time, we'll
|
||||
* set `isUnsaved` to true. That way, the app can warn you
|
||||
* if you're about to throw things away.
|
||||
*/
|
||||
public setupUnsavedOnChangeListener() {
|
||||
Object.keys(window.ElectronFiddle.editors).forEach((key) => {
|
||||
const editor = window.ElectronFiddle.editors[key];
|
||||
const disposable = editor.onDidChangeModelContent(() => {
|
||||
this.state.isUnsaved = true;
|
||||
disposable.dispose();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads theme CSS into the HTML document.
|
||||
*
|
||||
|
||||
+227
-204
@@ -1,236 +1,259 @@
|
||||
import * as fsType from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
|
||||
import { VersionState } from '../interfaces';
|
||||
import { fancyImport } from '../utils/import';
|
||||
import { normalizeVersion } from '../utils/normalize-version';
|
||||
import { USER_DATA_PATH } from './constants';
|
||||
import { getOfflineTypeDefinitionPath } from './fetch-types';
|
||||
import { removeTypeDefsForVersion } from './fetch-types';
|
||||
import { AppState } from './state';
|
||||
|
||||
/**
|
||||
* The binary manager takes care of downloading Electron versions
|
||||
* General setup, called with a version. Is called during construction
|
||||
* to ensure that we always have or download at least one version.
|
||||
*
|
||||
* @export
|
||||
* @class BinaryManager
|
||||
* @param {string} iVersion
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export class BinaryManager {
|
||||
public state: Record<string, 'ready' | 'downloading'> = {};
|
||||
export async function setupBinary(appState: AppState, iVersion: string): Promise<void> {
|
||||
const version = normalizeVersion(iVersion);
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
|
||||
/**
|
||||
* Remove a version from disk. Does not update state. We'll try up to
|
||||
* four times before giving up if an error occurs.
|
||||
*
|
||||
* @param {string} iVersion
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async remove(iVersion: string): Promise<void> {
|
||||
const version = normalizeVersion(iVersion);
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
let isDeleted = false;
|
||||
await fs.mkdirp(getDownloadPath(version));
|
||||
|
||||
// utility to re-run removal functions upon failure
|
||||
// due to windows filesystem lockfile jank
|
||||
const rerunner = async (func: () => Promise<void>, counter: number = 1) => {
|
||||
try {
|
||||
await func();
|
||||
} catch (error) {
|
||||
console.warn(`Binary Manager: failed to run ${func.name} for ${version}, but failed`, error);
|
||||
if (counter < 4) {
|
||||
console.log(`Binary Manager: Trying again to run ${func.name}`);
|
||||
await rerunner(func, counter + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const binaryCleaner = async () => {
|
||||
if (await this.getIsDownloaded(version)) {
|
||||
// This is necessary since we're messing with .asar files inside
|
||||
// the Electron binaries. Electron, powering Fiddle, will try to
|
||||
// "correct" our calls, but we don't want that right here.
|
||||
process.noAsar = true;
|
||||
await fs.remove(this.getDownloadPath(version));
|
||||
process.noAsar = false;
|
||||
|
||||
isDeleted = true;
|
||||
}
|
||||
};
|
||||
|
||||
const typeDefsCleaner = async () => {
|
||||
await this.removeTypeDefsForVersion(version);
|
||||
};
|
||||
|
||||
await rerunner(binaryCleaner);
|
||||
|
||||
if (isDeleted) {
|
||||
await rerunner(typeDefsCleaner);
|
||||
}
|
||||
const { state } = appState.versions[version];
|
||||
if (state === VersionState.downloading || state === VersionState.unzipping) {
|
||||
console.log(`Binary: Electron ${version} already downloading.`);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* General setup, called with a version. Is called during construction
|
||||
* to ensure that we always have or download at least one version.
|
||||
*
|
||||
* @param {string} iVersion
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async setup(iVersion: string): Promise<void> {
|
||||
const version = normalizeVersion(iVersion);
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
const { promisify } = await import('util');
|
||||
const eDownload = promisify(require('electron-download'));
|
||||
if (await getIsDownloaded(version)) {
|
||||
console.log(`Binary: Electron ${version} already downloaded.`);
|
||||
appState.versions[version].state = VersionState.ready;
|
||||
return;
|
||||
}
|
||||
|
||||
await fs.mkdirp(this.getDownloadPath(version));
|
||||
console.log(`Binary: Electron ${version} not present, downloading`);
|
||||
appState.versions[version].state = VersionState.downloading;
|
||||
|
||||
if (this.state[version] === 'downloading') {
|
||||
console.log(`BinaryManager: Electron ${version} already downloading.`);
|
||||
return;
|
||||
}
|
||||
const zipPath = await download(appState, version);
|
||||
const extractPath = getDownloadPath(version);
|
||||
console.log(`Binary: Electron ${version} downloaded, now unpacking to ${extractPath}`);
|
||||
|
||||
if (await this.getIsDownloaded(version)) {
|
||||
console.log(`BinaryManager: Electron ${version} already downloaded.`);
|
||||
this.state[version] = 'ready';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
appState.versions[version].state = VersionState.unzipping;
|
||||
|
||||
console.log(`BinaryManager: Electron ${version} not present, downloading`);
|
||||
this.state[version] = 'downloading';
|
||||
// Ensure the target path is empty
|
||||
await fs.emptyDir(extractPath);
|
||||
|
||||
const zipPath = await eDownload({ version });
|
||||
const extractPath = this.getDownloadPath(version);
|
||||
console.log(`BinaryManager: Electron ${version} downloaded, now unpacking to ${extractPath}`);
|
||||
const electronFiles = await unzip(zipPath, extractPath);
|
||||
console.log(`Unzipped ${version}`, electronFiles);
|
||||
} catch (error) {
|
||||
console.warn(`Failure while unzipping ${version}`, error);
|
||||
appState.versions[version].state = VersionState.unknown;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
appState.versions[version].state = VersionState.ready;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a version from disk. Does not update state. We'll try up to
|
||||
* four times before giving up if an error occurs.
|
||||
*
|
||||
* @param {string} iVersion
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function removeBinary(iVersion: string) {
|
||||
const version = normalizeVersion(iVersion);
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
let isDeleted = false;
|
||||
|
||||
// utility to re-run removal functions upon failure
|
||||
// due to windows filesystem lockfile jank
|
||||
const rerunner = async (func: () => Promise<void>, counter: number = 1) => {
|
||||
try {
|
||||
// Ensure the target path is empty
|
||||
await fs.emptyDir(extractPath);
|
||||
|
||||
const electronFiles = await this.unzip(zipPath, extractPath);
|
||||
console.log(`Unzipped ${version}`, electronFiles);
|
||||
await func();
|
||||
} catch (error) {
|
||||
console.warn(`Failure while unzipping ${version}`, error);
|
||||
|
||||
// TODO: Handle this case
|
||||
}
|
||||
|
||||
this.state[version] = 'ready';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expected path for the binary of a given Electron version
|
||||
*
|
||||
* @param {string} version
|
||||
* @param {string} dir
|
||||
* @returns {string}
|
||||
*/
|
||||
public getElectronBinaryPath(
|
||||
version: string,
|
||||
dir: string = this.getDownloadPath(version),
|
||||
): string {
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
return path.join(dir, 'Electron.app/Contents/MacOS/Electron');
|
||||
case 'freebsd':
|
||||
case 'linux':
|
||||
return path.join(dir, 'electron');
|
||||
case 'win32':
|
||||
return path.join(dir, 'electron.exe');
|
||||
default:
|
||||
throw new Error(`Electron builds are not available for ${process.platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all versions downloaded to disk
|
||||
*
|
||||
* @returns {Promise<Array<string>>}
|
||||
*/
|
||||
public async getDownloadedVersions(): Promise<Array<string>> {
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
const downloadPath = path.join(USER_DATA_PATH, 'electron-bin');
|
||||
console.log(`BinaryManager: Checking for downloaded versions`);
|
||||
|
||||
try {
|
||||
const directories = await fs.readdir(downloadPath);
|
||||
const knownVersions: Array<string> = [];
|
||||
|
||||
for (const directory of directories) {
|
||||
if (await this.getIsDownloaded(directory)) {
|
||||
knownVersions.push(directory);
|
||||
}
|
||||
}
|
||||
|
||||
return knownVersions;
|
||||
} catch (error) {
|
||||
console.warn(`Could not read known Electron versions`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Did we already download a given version?
|
||||
*
|
||||
* @param {string} version
|
||||
* @param {string} dir
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public async getIsDownloaded(version: string, dir?: string): Promise<boolean> {
|
||||
const expectedPath = this.getElectronBinaryPath(version, dir);
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
|
||||
return fs.existsSync(expectedPath);
|
||||
}
|
||||
|
||||
public async removeTypeDefsForVersion(version: string) {
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
const _version = normalizeVersion(version);
|
||||
const typeDefsDir = path.dirname(getOfflineTypeDefinitionPath(_version));
|
||||
|
||||
if (fs.existsSync(typeDefsDir)) {
|
||||
try {
|
||||
await fs.remove(typeDefsDir);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
console.warn(`Binary Manager: failed to run ${func.name} for ${version}, but failed`, error);
|
||||
if (counter < 4) {
|
||||
console.log(`Binary Manager: Trying again to run ${func.name}`);
|
||||
await rerunner(func, counter + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expected path for a given Electron version
|
||||
*
|
||||
* @param {string} version
|
||||
* @returns {string}
|
||||
*/
|
||||
private getDownloadPath(version: string): string {
|
||||
return path.join(USER_DATA_PATH, 'electron-bin', version);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unzips an electron package so that we can actually use it.
|
||||
*
|
||||
* @param {string} zipPath
|
||||
* @param {string} extractPath
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private unzip(zipPath: string, extractPath: string): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const extract = (await fancyImport<any>('extract-zip')).default;
|
||||
};
|
||||
|
||||
const binaryCleaner = async () => {
|
||||
if (await getIsDownloaded(version)) {
|
||||
// This is necessary since we're messing with .asar files inside
|
||||
// the Electron binaries. Electron, powering Fiddle, will try to
|
||||
// "correct" our calls, but we don't want that right here.
|
||||
process.noAsar = true;
|
||||
await fs.remove(getDownloadPath(version));
|
||||
process.noAsar = false;
|
||||
|
||||
const options = {
|
||||
dir: extractPath,
|
||||
};
|
||||
isDeleted = true;
|
||||
}
|
||||
};
|
||||
|
||||
extract(zipPath, options, (error: Error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
const typeDefsCleaner = async () => {
|
||||
await removeTypeDefsForVersion(version);
|
||||
};
|
||||
|
||||
console.log(`BinaryManager: Unpacked!`);
|
||||
process.noAsar = false;
|
||||
await rerunner(binaryCleaner);
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
if (isDeleted) {
|
||||
await rerunner(typeDefsCleaner);
|
||||
}
|
||||
}
|
||||
|
||||
/* Did we already download a given version?
|
||||
*
|
||||
* @param {string} version
|
||||
* @param {string} dir
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export async function getIsDownloaded(version: string, dir?: string): Promise<boolean> {
|
||||
const expectedPath = getElectronBinaryPath(version, dir);
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
|
||||
return fs.existsSync(expectedPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expected path for the binary of a given Electron version
|
||||
*
|
||||
* @param {string} version
|
||||
* @param {string} dir
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getElectronBinaryPath(
|
||||
version: string,
|
||||
dir: string = getDownloadPath(version),
|
||||
): string {
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
return path.join(dir, 'Electron.app/Contents/MacOS/Electron');
|
||||
case 'freebsd':
|
||||
case 'linux':
|
||||
return path.join(dir, 'electron');
|
||||
case 'win32':
|
||||
return path.join(dir, 'electron.exe');
|
||||
default:
|
||||
throw new Error(`Electron builds are not available for ${process.platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function getDownloadingVersions(appState: AppState) {
|
||||
return Object.entries(appState.versions)
|
||||
.filter(([_, { state }]) => state === 'downloading')
|
||||
.map(([version, _]) => version);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of all versions downloaded to disk
|
||||
*
|
||||
* @returns {Promise<Array<string>>}
|
||||
*/
|
||||
export async function getDownloadedVersions(): Promise<Array<string>> {
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
const downloadPath = path.join(USER_DATA_PATH, 'electron-bin');
|
||||
console.log(`Binary: Checking for downloaded versions`);
|
||||
|
||||
try {
|
||||
const directories = await fs.readdir(downloadPath);
|
||||
const knownVersions: Array<string> = [];
|
||||
|
||||
for (const directory of directories) {
|
||||
if (await getIsDownloaded(directory)) {
|
||||
knownVersions.push(directory);
|
||||
}
|
||||
}
|
||||
|
||||
return knownVersions;
|
||||
} catch (error) {
|
||||
console.warn(`Could not read known Electron versions`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download an Electron version.
|
||||
*
|
||||
* @param {AppState} appState
|
||||
* @param {string} version
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function download(appState: AppState, version: string): Promise<string> {
|
||||
const { download: electronDownload } = await import('@electron/get');
|
||||
const getProgressCallback = (progress: Progress) => {
|
||||
const roundedProgress = Math.round(progress.percent * 100) / 100;
|
||||
|
||||
if (roundedProgress !== appState.versions[version].downloadProgress) {
|
||||
console.debug(`Binary: Version ${version} download progress: ${progress.percent}`);
|
||||
appState.versions[version].downloadProgress = roundedProgress;
|
||||
}
|
||||
};
|
||||
|
||||
if (!appState.versions[version]) {
|
||||
throw new Error(`Version ${version} does not exist in state, cannot download`);
|
||||
}
|
||||
|
||||
const zipFilePath = await electronDownload(version, {
|
||||
downloadOptions: {
|
||||
quiet: true,
|
||||
getProgressCallback
|
||||
}
|
||||
});
|
||||
|
||||
return zipFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expected path for a given Electron version
|
||||
*
|
||||
* @param {string} version
|
||||
* @returns {string}
|
||||
*/
|
||||
function getDownloadPath(version: string): string {
|
||||
return path.join(USER_DATA_PATH, 'electron-bin', version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unzips an electron package so that we can actually use it.
|
||||
*
|
||||
* @param {string} zipPath
|
||||
* @param {string} extractPath
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function unzip(zipPath: string, extractPath: string): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const extract = (await fancyImport<any>('extract-zip')).default;
|
||||
|
||||
process.noAsar = true;
|
||||
|
||||
const options = {
|
||||
dir: extractPath,
|
||||
};
|
||||
|
||||
extract(zipPath, options, (error: Error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Binary: Unpacked!`);
|
||||
process.noAsar = false;
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
interface Progress {
|
||||
percent: number;
|
||||
transferred: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ElectronVersion } from '../interfaces';
|
||||
import { RunnableVersion } from '../interfaces';
|
||||
|
||||
export class Bisector {
|
||||
public revList: Array<ElectronVersion>;
|
||||
public revList: Array<RunnableVersion>;
|
||||
public minRev: number;
|
||||
public maxRev: number;
|
||||
private pivot: number;
|
||||
|
||||
constructor(revList: Array<ElectronVersion>) {
|
||||
constructor(revList: Array<RunnableVersion>) {
|
||||
this.getCurrentVersion = this.getCurrentVersion.bind(this);
|
||||
this.continue = this.continue.bind(this);
|
||||
this.calculatePivot = this.calculatePivot.bind(this);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Button } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { GenericDialogType } from '../../../src/interfaces';
|
||||
import { GenericDialogType, VersionState } from '../../../src/interfaces';
|
||||
import { AppState } from '../state';
|
||||
|
||||
interface BisectHandlerProps {
|
||||
@@ -19,6 +19,8 @@ export class BisectHandler extends React.Component<BisectHandlerProps> {
|
||||
}
|
||||
|
||||
public continueBisect(isGood: boolean) {
|
||||
window.ElectronFiddle.app.runner.stop();
|
||||
|
||||
const { appState } = this.props;
|
||||
const response = appState.Bisector!.continue(isGood);
|
||||
|
||||
@@ -49,15 +51,18 @@ export class BisectHandler extends React.Component<BisectHandlerProps> {
|
||||
public render() {
|
||||
const { appState } = this.props;
|
||||
if (!!appState.Bisector) {
|
||||
const isDownloading = appState.currentElectronVersion.state === VersionState.downloading;
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
icon={'thumbs-up'}
|
||||
onClick={() => this.continueBisect(true)}
|
||||
disabled={isDownloading}
|
||||
/>
|
||||
<Button
|
||||
icon={'thumbs-down'}
|
||||
onClick={() => this.continueBisect(false)}
|
||||
disabled={isDownloading}
|
||||
/>
|
||||
<Button
|
||||
icon={'cross'}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -77,10 +77,23 @@ export class EditorDropdown extends React.Component<EditorDropdownProps, EditorD
|
||||
text={TITLE_MAP[id]}
|
||||
id={id}
|
||||
onClick={this.onItemClick}
|
||||
disabled={appState.mosaicArrangement === id} // can't hide last editor panel
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Button, IButtonProps, Spinner } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersionState } from '../../interfaces';
|
||||
import { VersionState } from '../../interfaces';
|
||||
import { AppState } from '../state';
|
||||
|
||||
export interface RunnerState {
|
||||
@@ -25,13 +25,17 @@ export class Runner extends React.Component<RunnerProps, RunnerState> {
|
||||
const { isRunning, currentElectronVersion } = this.props.appState;
|
||||
|
||||
const state = currentElectronVersion && currentElectronVersion.state;
|
||||
const props: IButtonProps = { className: 'button-run' };
|
||||
const props: IButtonProps = { className: 'button-run', disabled: true };
|
||||
|
||||
if (state === ElectronVersionState.downloading) {
|
||||
if (state === VersionState.downloading) {
|
||||
props.text = 'Downloading';
|
||||
props.disabled = true;
|
||||
props.icon = <Spinner size={16} value={currentElectronVersion?.downloadProgress} />;
|
||||
} else if (state === VersionState.unzipping) {
|
||||
props.text = 'Unzipping';
|
||||
props.icon = <Spinner size={16} />;
|
||||
} else if (state === ElectronVersionState.ready) {
|
||||
} else if (state === VersionState.ready) {
|
||||
props.disabled = false;
|
||||
|
||||
if (isRunning) {
|
||||
props.active = true;
|
||||
props.text = 'Stop';
|
||||
@@ -44,7 +48,6 @@ export class Runner extends React.Component<RunnerProps, RunnerState> {
|
||||
}
|
||||
} else {
|
||||
props.text = 'Checking status';
|
||||
props.disabled = true;
|
||||
props.icon = <Spinner size={16} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as semver from 'semver';
|
||||
import { Version } from '../../interfaces';
|
||||
import { IpcEvents } from '../../ipc-events';
|
||||
import { getElectronNameForPlatform } from '../../utils/electron-name';
|
||||
import { getIsDownloaded } from '../binary';
|
||||
import { ipcRendererManager } from '../ipc';
|
||||
import { AppState } from '../state';
|
||||
|
||||
@@ -58,8 +59,7 @@ export class AddVersionDialog extends React.Component<AddVersionDialogProps, Add
|
||||
* @param {React.ChangeEvent<HTMLInputElement>} event
|
||||
*/
|
||||
public async setFolderPath(folderPath: string) {
|
||||
const { binaryManager } = this.props.appState;
|
||||
const isValidElectron = !!await binaryManager.getIsDownloaded('custom', folderPath);
|
||||
const isValidElectron = !!await getIsDownloaded('custom', folderPath);
|
||||
|
||||
this.setState({ folderPath, isValidElectron });
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Button, ButtonGroup, Callout, Dialog, Label } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersion } from '../../interfaces';
|
||||
import { RunnableVersion } from '../../interfaces';
|
||||
import { Bisector } from '../bisect';
|
||||
import { AppState } from '../state';
|
||||
import { VersionSelect } from './version-select';
|
||||
@@ -14,7 +14,7 @@ export interface BisectDialogProps {
|
||||
export interface BisectDialogState {
|
||||
startIndex: number;
|
||||
endIndex: number;
|
||||
allVersions: Array<ElectronVersion>;
|
||||
allVersions: Array<RunnableVersion>;
|
||||
showHelp?: boolean;
|
||||
}
|
||||
|
||||
@@ -44,11 +44,11 @@ export class BisectDialog extends React.Component<BisectDialogProps, BisectDialo
|
||||
};
|
||||
}
|
||||
|
||||
public onBeginSelect(version: ElectronVersion) {
|
||||
public onBeginSelect(version: RunnableVersion) {
|
||||
this.setState({ startIndex: this.state.allVersions.indexOf(version) });
|
||||
}
|
||||
|
||||
public onEndSelect(version: ElectronVersion) {
|
||||
public onEndSelect(version: RunnableVersion) {
|
||||
this.setState({ endIndex: this.state.allVersions.indexOf(version) });
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -208,10 +209,10 @@ export class BisectDialog extends React.Component<BisectDialogProps, BisectDialo
|
||||
/**
|
||||
* Should an item in the "earliest version" dropdown be disabled?
|
||||
*
|
||||
* @param {ElectronVersion} version
|
||||
* @param {RunnableVersion} version
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isEarliestItemDisabled(version: ElectronVersion): boolean {
|
||||
public isEarliestItemDisabled(version: RunnableVersion): boolean {
|
||||
const { allVersions, endIndex } = this.state;
|
||||
|
||||
// In the array, "newer" versions will have a lower index.
|
||||
@@ -225,10 +226,10 @@ export class BisectDialog extends React.Component<BisectDialogProps, BisectDialo
|
||||
/**
|
||||
* Should an item in the "latest version" dropdown be disabled?
|
||||
*
|
||||
* @param {ElectronVersion} version
|
||||
* @param {RunnableVersion} version
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isLatestItemDisabled(version: ElectronVersion): boolean {
|
||||
public isLatestItemDisabled(version: RunnableVersion): boolean {
|
||||
const { allVersions, startIndex } = this.state;
|
||||
|
||||
return allVersions.indexOf(version) > startIndex - 1;
|
||||
|
||||
@@ -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);
|
||||
@@ -143,8 +143,6 @@ export class Editor extends React.Component<EditorProps> {
|
||||
|
||||
const backup = appState.getAndRemoveEditorValueBackup(id);
|
||||
|
||||
console.log(id);
|
||||
|
||||
if (backup) {
|
||||
console.log(`Editor: Backup found, restoring state`);
|
||||
|
||||
@@ -155,12 +153,12 @@ export class Editor extends React.Component<EditorProps> {
|
||||
// If there's a model, use the model. No model? Use the value
|
||||
if (backup.model) {
|
||||
this.editor.setModel(backup.model);
|
||||
} else if (typeof backup.value !== 'undefined') {
|
||||
this.createModel(backup.value);
|
||||
} else {
|
||||
this.createModel(backup.value ?? '');
|
||||
}
|
||||
} else {
|
||||
const value = await getContent(id, version);
|
||||
await this.createModel(value);
|
||||
this.createModel(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,8 +66,6 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
|
||||
this.setFocused = this.setFocused.bind(this);
|
||||
|
||||
this.state = { monacoOptions: defaultMonacoOptions };
|
||||
|
||||
this.loadMonaco();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +73,7 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
|
||||
*
|
||||
* @memberof Editors
|
||||
*/
|
||||
public componentDidMount() {
|
||||
public async componentDidMount() {
|
||||
ipcRendererManager.on(IpcEvents.MONACO_EXECUTE_COMMAND, (_event, cmd: string) => {
|
||||
this.executeCommand(cmd);
|
||||
});
|
||||
@@ -95,6 +93,8 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
|
||||
});
|
||||
|
||||
this.setState({ isMounted: true });
|
||||
await this.loadMonaco();
|
||||
this.props.appState.isUnsaved = false;
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
@@ -167,10 +167,22 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
|
||||
public renderToolbar(
|
||||
{ title }: MosaicWindowProps<MosaicId>, id: MosaicId
|
||||
): JSX.Element {
|
||||
const { appState } = this.props;
|
||||
const docsDemoGoHomeMaybe = id === PanelId.docsDemo
|
||||
? <DocsDemoGoHomeButton id={id} appState={this.props.appState} />
|
||||
? <DocsDemoGoHomeButton id={id} appState={appState} />
|
||||
: null;
|
||||
|
||||
// only show toolbar controls if we have more than 1 visible editor
|
||||
// Mosaic arrangement is type string if 1 editor, object otherwise
|
||||
const toolbarControlsMaybe =
|
||||
(typeof appState.mosaicArrangement !== 'string') &&
|
||||
(
|
||||
<>
|
||||
<MaximizeButton id={id} appState={appState} />
|
||||
<RemoveButton id={id} appState={appState} />
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Left */}
|
||||
@@ -184,8 +196,7 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
|
||||
{/* Right */}
|
||||
<div className='mosaic-controls'>
|
||||
{docsDemoGoHomeMaybe}
|
||||
<MaximizeButton id={id} appState={this.props.appState} />
|
||||
<RemoveButton id={id} appState={this.props.appState} />
|
||||
{toolbarControlsMaybe}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -280,7 +291,7 @@ export class Editors extends React.Component<EditorsProps, EditorsState> {
|
||||
* We're doing things a bit roundabout to ensure that we're not overloading the
|
||||
* mobx state with a gigantic Monaco tree.
|
||||
*/
|
||||
public async loadMonaco(): Promise<void> {
|
||||
public async loadMonaco() {
|
||||
const { app } = window.ElectronFiddle;
|
||||
const loader = require('monaco-loader');
|
||||
const monaco = app.monaco || await loader();
|
||||
@@ -290,15 +301,15 @@ 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 });
|
||||
}
|
||||
|
||||
activateTheme(monaco, undefined, this.props.appState.theme);
|
||||
await activateTheme(monaco, undefined, this.props.appState.theme);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,12 +7,13 @@ import {
|
||||
HTMLTable,
|
||||
IButtonProps,
|
||||
Icon,
|
||||
IconName
|
||||
IconName,
|
||||
Tooltip
|
||||
} from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState } from '../../interfaces';
|
||||
import { RunnableVersion, VersionSource, VersionState } from '../../interfaces';
|
||||
import { normalizeVersion } from '../../utils/normalize-version';
|
||||
import { sortedElectronMap } from '../../utils/sorted-electron-map';
|
||||
import { AppState } from '../state';
|
||||
@@ -69,7 +70,7 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
if (!checked) {
|
||||
appState.statesToShow = appState.statesToShow.filter((s) => s !== id);
|
||||
} else {
|
||||
appState.statesToShow.push(id as ElectronVersionState);
|
||||
appState.statesToShow.push(id as VersionState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +203,7 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
*/
|
||||
private renderVersionStateOptions(): JSX.Element {
|
||||
const { appState } = this.props;
|
||||
const getIsChecked = (state: ElectronVersionState) => {
|
||||
const getIsChecked = (state: VersionState) => {
|
||||
return appState.statesToShow.includes(state);
|
||||
};
|
||||
|
||||
@@ -210,22 +211,29 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
<FormGroup
|
||||
label='Include Electron versions that are:'
|
||||
>
|
||||
<Tooltip
|
||||
content='Always enabled'
|
||||
position='bottom'
|
||||
intent='primary'
|
||||
>
|
||||
<Checkbox
|
||||
checked={getIsChecked(VersionState.ready)}
|
||||
label='Ready'
|
||||
id='ready'
|
||||
onChange={this.handleStateChange}
|
||||
inline={true}
|
||||
disabled={true}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Checkbox
|
||||
checked={getIsChecked(ElectronVersionState.downloading)}
|
||||
checked={getIsChecked(VersionState.downloading)}
|
||||
label='Downloading'
|
||||
id='downloading'
|
||||
onChange={this.handleStateChange}
|
||||
inline={true}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={getIsChecked(ElectronVersionState.ready)}
|
||||
label='Downloaded'
|
||||
id='ready'
|
||||
onChange={this.handleStateChange}
|
||||
inline={true}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={getIsChecked(ElectronVersionState.unknown)}
|
||||
checked={getIsChecked(VersionState.unknown)}
|
||||
label='Not Downloaded'
|
||||
id='unknown'
|
||||
onChange={this.handleStateChange}
|
||||
@@ -243,42 +251,47 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
*/
|
||||
private renderVersionChannelOptions(): JSX.Element {
|
||||
const { appState } = this.props;
|
||||
|
||||
const getIsChecked = (channel: ElectronReleaseChannel) => {
|
||||
return appState.channelsToShow.includes(channel);
|
||||
};
|
||||
|
||||
const getIsCurrentVersionReleaseChannel = (channel: ElectronReleaseChannel) => {
|
||||
return getReleaseChannel(appState.version) === channel;
|
||||
};
|
||||
|
||||
const channels = {
|
||||
stable: ElectronReleaseChannel.stable,
|
||||
beta: ElectronReleaseChannel.beta,
|
||||
nightly: ElectronReleaseChannel.nightly,
|
||||
unsupported: ElectronReleaseChannel.unsupported
|
||||
};
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
label='Include Electron versions from these release channels:'
|
||||
>
|
||||
<Checkbox
|
||||
checked={getIsChecked(ElectronReleaseChannel.stable)}
|
||||
label='Stable'
|
||||
id='Stable'
|
||||
onChange={this.handleChannelChange}
|
||||
inline={true}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={getIsChecked(ElectronReleaseChannel.beta)}
|
||||
label='Beta'
|
||||
id='Beta'
|
||||
onChange={this.handleChannelChange}
|
||||
inline={true}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={getIsChecked(ElectronReleaseChannel.nightly)}
|
||||
label='Nightly'
|
||||
id='Nightly'
|
||||
onChange={this.handleChannelChange}
|
||||
inline={true}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={getIsChecked(ElectronReleaseChannel.unsupported)}
|
||||
label='Unsupported'
|
||||
id='Unsupported'
|
||||
onChange={this.handleChannelChange}
|
||||
inline={true}
|
||||
/>
|
||||
{
|
||||
// tslint:disable-next-line:jsx-no-multiline-js
|
||||
Object.entries(channels).map(([_, channel]) => (
|
||||
<Tooltip
|
||||
content={`Can't disable channel of selected version (${appState.version})`}
|
||||
disabled={!getIsCurrentVersionReleaseChannel(channel)}
|
||||
position='bottom'
|
||||
intent='primary'
|
||||
key={channel}
|
||||
>
|
||||
<Checkbox
|
||||
checked={getIsChecked(channel)}
|
||||
label={channel}
|
||||
id={channel}
|
||||
onChange={this.handleChannelChange}
|
||||
disabled={getIsCurrentVersionReleaseChannel(channel)}
|
||||
inline={true}
|
||||
/>
|
||||
</Tooltip>
|
||||
))
|
||||
}
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
@@ -342,18 +355,18 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
/**
|
||||
* Returns a human-readable state indicator for an Electron version
|
||||
*
|
||||
* @param {ElectronVersion} item
|
||||
* @param {RunnableVersion} item
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
private renderHumanState(item: ElectronVersion): JSX.Element {
|
||||
private renderHumanState(item: RunnableVersion): JSX.Element {
|
||||
const { state } = item;
|
||||
let icon: IconName = 'box';
|
||||
let humanState = 'Downloaded';
|
||||
|
||||
if (state === ElectronVersionState.downloading) {
|
||||
if (state === VersionState.downloading) {
|
||||
icon = 'cloud-download';
|
||||
humanState = 'Downloading';
|
||||
} else if (state === ElectronVersionState.unknown) {
|
||||
} else if (state === VersionState.unknown) {
|
||||
icon = 'cloud';
|
||||
humanState = 'Not downloaded';
|
||||
}
|
||||
@@ -370,10 +383,10 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
*
|
||||
* @private
|
||||
* @param {string} key
|
||||
* @param {ElectronVersion} item
|
||||
* @param {RunnableVersion} item
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
private renderAction(key: string, item: ElectronVersion): JSX.Element {
|
||||
private renderAction(key: string, item: RunnableVersion): JSX.Element {
|
||||
const { state, source } = item;
|
||||
const { appState } = this.props;
|
||||
const buttonProps: IButtonProps = {
|
||||
@@ -385,7 +398,7 @@ export class ElectronSettings extends React.Component<ElectronSettingsProps, Ele
|
||||
if (state === 'ready') {
|
||||
buttonProps.onClick = () => appState.removeVersion(key);
|
||||
buttonProps.icon = 'trash';
|
||||
buttonProps.text = source === ElectronVersionSource.local
|
||||
buttonProps.text = source === VersionSource.local
|
||||
? 'Remove'
|
||||
: 'Delete';
|
||||
} else if (state === 'downloading') {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -19,7 +19,7 @@ const ThemeSelect = Select.ofType<LoadedFiddleTheme>();
|
||||
* version.
|
||||
*
|
||||
* @param {string} query
|
||||
* @param {ElectronVersion} { version }
|
||||
* @param {RunnableVersion} { version }
|
||||
* @returns
|
||||
*/
|
||||
export const filterItem: ItemPredicate<LoadedFiddleTheme> = (query, { name }) => {
|
||||
@@ -31,7 +31,7 @@ export const filterItem: ItemPredicate<LoadedFiddleTheme> = (query, { name }) =>
|
||||
* Helper method: Returns the <Select /> <MenuItem /> for Electron
|
||||
* versions.
|
||||
*
|
||||
* @param {ElectronVersion} item
|
||||
* @param {RunnableVersion} item
|
||||
* @param {IItemRendererProps} { handleClick, modifiers, query }
|
||||
* @returns
|
||||
*/
|
||||
|
||||
@@ -3,30 +3,30 @@ import { ItemPredicate, ItemRenderer, Select } from '@blueprintjs/select';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState } from '../../interfaces';
|
||||
import { RunnableVersion, VersionSource, VersionState } from '../../interfaces';
|
||||
import { highlightText } from '../../utils/highlight-text';
|
||||
import { AppState } from '../state';
|
||||
|
||||
const ElectronVersionSelect = Select.ofType<ElectronVersion>();
|
||||
const ElectronVersionSelect = Select.ofType<RunnableVersion>();
|
||||
|
||||
/**
|
||||
* Helper method: Returns the <Select /> label for an Electron
|
||||
* version.
|
||||
*
|
||||
* @param {ElectronVersion} { source, state }
|
||||
* @param {RunnableVersion} { source, state }
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getItemLabel({ source, state, name }: ElectronVersion): string {
|
||||
export function getItemLabel({ source, state, name }: RunnableVersion): string {
|
||||
let label = '';
|
||||
|
||||
if (source === ElectronVersionSource.local) {
|
||||
if (source === VersionSource.local) {
|
||||
label = name || 'Local';
|
||||
} else {
|
||||
if (state === ElectronVersionState.unknown) {
|
||||
if (state === VersionState.unknown) {
|
||||
label = `Not downloaded`;
|
||||
} else if (state === ElectronVersionState.ready) {
|
||||
} else if (state === VersionState.ready) {
|
||||
label = `Downloaded`;
|
||||
} else if (state === ElectronVersionState.downloading) {
|
||||
} else if (state === VersionState.downloading) {
|
||||
label = `Downloading`;
|
||||
}
|
||||
}
|
||||
@@ -38,10 +38,10 @@ export function getItemLabel({ source, state, name }: ElectronVersion): string {
|
||||
* Helper method: Returns the <Select /> icon for an Electron
|
||||
* version.
|
||||
*
|
||||
* @param {ElectronVersion} { state }
|
||||
* @param {RunnableVersion} { state }
|
||||
* @returns
|
||||
*/
|
||||
export function getItemIcon({ state }: ElectronVersion) {
|
||||
export function getItemIcon({ state }: RunnableVersion) {
|
||||
return state === 'ready'
|
||||
? 'saved'
|
||||
: state === 'downloading' ? 'cloud-download' : 'cloud';
|
||||
@@ -52,10 +52,10 @@ export function getItemIcon({ state }: ElectronVersion) {
|
||||
* version.
|
||||
*
|
||||
* @param {string} query
|
||||
* @param {ElectronVersion} { version }
|
||||
* @param {RunnableVersion} { version }
|
||||
* @returns
|
||||
*/
|
||||
export const filterItem: ItemPredicate<ElectronVersion> = (query, { version }) => {
|
||||
export const filterItem: ItemPredicate<RunnableVersion> = (query, { version }) => {
|
||||
return version.toLowerCase().includes(query.toLowerCase());
|
||||
};
|
||||
|
||||
@@ -63,11 +63,11 @@ export const filterItem: ItemPredicate<ElectronVersion> = (query, { version }) =
|
||||
* Helper method: Returns the <Select /> <MenuItem /> for Electron
|
||||
* versions.
|
||||
*
|
||||
* @param {ElectronVersion} item
|
||||
* @param {RunnableVersion} item
|
||||
* @param {IItemRendererProps} { handleClick, modifiers, query }
|
||||
* @returns
|
||||
*/
|
||||
export const renderItem: ItemRenderer<ElectronVersion> = (item, { handleClick, modifiers, query }) => {
|
||||
export const renderItem: ItemRenderer<RunnableVersion> = (item, { handleClick, modifiers, query }) => {
|
||||
if (!modifiers.matchesPredicate) {
|
||||
return null;
|
||||
}
|
||||
@@ -92,10 +92,10 @@ export interface VersionSelectState {
|
||||
export interface VersionSelectProps {
|
||||
appState: AppState;
|
||||
disabled?: boolean;
|
||||
currentVersion: ElectronVersion;
|
||||
onVersionSelect: (version: ElectronVersion) => void;
|
||||
currentVersion: RunnableVersion;
|
||||
onVersionSelect: (version: RunnableVersion) => void;
|
||||
buttonGroupProps?: IButtonGroupProps;
|
||||
itemDisabled?: keyof ElectronVersion | ((item: ElectronVersion, index: number) => boolean);
|
||||
itemDisabled?: keyof RunnableVersion | ((item: RunnableVersion, index: number) => boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,8 +22,14 @@ export const DEFAULT_MOSAIC_ARRANGEMENT: MosaicNode<MosaicId> = {
|
||||
|
||||
export const DEFAULT_CLOSED_PANELS: Partial<Record<MosaicId, EditorBackup | true>> = {
|
||||
docsDemo: true,
|
||||
preload: {},
|
||||
css: {}
|
||||
preload: getEditorBackup(EditorId.preload),
|
||||
css: getEditorBackup(EditorId.css),
|
||||
};
|
||||
|
||||
export const EMPTY_EDITOR_CONTENT = {
|
||||
html: '<!-- Empty -->',
|
||||
js: '// Empty',
|
||||
css: '/* Empty */'
|
||||
};
|
||||
|
||||
export const ELECTRON_ORG = 'electron';
|
||||
|
||||
@@ -2,9 +2,10 @@ import * as fsType from 'fs-extra';
|
||||
import * as MonacoType from 'monaco-editor';
|
||||
import * as path from 'path';
|
||||
|
||||
import { ElectronVersion, ElectronVersionSource } from '../interfaces';
|
||||
import { RunnableVersion, VersionSource } from '../interfaces';
|
||||
import { callIn } from '../utils/call-in';
|
||||
import { fancyImport } from '../utils/import';
|
||||
import { normalizeVersion } from '../utils/normalize-version';
|
||||
import { USER_DATA_PATH } from './constants';
|
||||
|
||||
const definitionPath = path.join(USER_DATA_PATH, 'electron-typedef');
|
||||
@@ -36,6 +37,26 @@ export async function fetchTypeDefinitions(version: string): Promise<string> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the type definition for a given version
|
||||
*
|
||||
* @param version
|
||||
*/
|
||||
export async function removeTypeDefsForVersion(version: string) {
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
const _version = normalizeVersion(version);
|
||||
const typeDefsDir = path.dirname(getOfflineTypeDefinitionPath(_version));
|
||||
|
||||
if (fs.existsSync(typeDefsDir)) {
|
||||
try {
|
||||
await fs.remove(typeDefsDir);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the path for offline TypeScript definitions
|
||||
*
|
||||
@@ -64,7 +85,7 @@ export async function getOfflineTypeDefinitions(version: string): Promise<boolea
|
||||
* @param {string} version
|
||||
* @returns {void}
|
||||
*/
|
||||
export async function getDownloadedVersionTypeDefs(version: ElectronVersion): Promise<string | null> {
|
||||
export async function getDownloadedVersionTypeDefs(version: RunnableVersion): Promise<string | null> {
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
await fs.mkdirp(definitionPath);
|
||||
const offlinePath = getOfflineTypeDefinitionPath(version.version);
|
||||
@@ -91,8 +112,8 @@ export async function getDownloadedVersionTypeDefs(version: ElectronVersion): Pr
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLocalVersionTypeDefs(version: ElectronVersion) {
|
||||
if (version.source === ElectronVersionSource.local && !!version.localPath) {
|
||||
export async function getLocalVersionTypeDefs(version: RunnableVersion) {
|
||||
if (version.source === VersionSource.local && !!version.localPath) {
|
||||
const fs = await fancyImport<typeof fsType>('fs-extra');
|
||||
const typesPath = getLocalTypePathForVersion(version);
|
||||
if (!!typesPath && fs.existsSync(typesPath)) {
|
||||
@@ -107,7 +128,7 @@ export async function getLocalVersionTypeDefs(version: ElectronVersion) {
|
||||
*
|
||||
* @param {string} version
|
||||
*/
|
||||
export async function updateEditorTypeDefinitions(version: ElectronVersion, i: number = 0): Promise<void> {
|
||||
export async function updateEditorTypeDefinitions(version: RunnableVersion, i: number = 0): Promise<void> {
|
||||
const defer = async (): Promise<void> => {
|
||||
if (i > 10) {
|
||||
console.warn(`Fetch Types: Failed, dependencies do not exist`);
|
||||
@@ -125,7 +146,7 @@ export async function updateEditorTypeDefinitions(version: ElectronVersion, i: n
|
||||
const monaco: typeof MonacoType = app.monaco!;
|
||||
const typeDefDisposable: MonacoType.IDisposable = app.typeDefDisposable!;
|
||||
|
||||
const getTypeDefs = (version.source === ElectronVersionSource.local) ?
|
||||
const getTypeDefs = (version.source === VersionSource.local) ?
|
||||
getLocalVersionTypeDefs : getDownloadedVersionTypeDefs;
|
||||
|
||||
const typeDefs = await getTypeDefs(version);
|
||||
@@ -143,7 +164,7 @@ export async function updateEditorTypeDefinitions(version: ElectronVersion, i: n
|
||||
}
|
||||
}
|
||||
|
||||
export function getLocalTypePathForVersion(version: ElectronVersion) {
|
||||
export function getLocalTypePathForVersion(version: RunnableVersion) {
|
||||
if (version.localPath) {
|
||||
return path.join(
|
||||
version.localPath,
|
||||
|
||||
@@ -110,7 +110,6 @@ export class FileManager {
|
||||
}
|
||||
|
||||
this.appState.isUnsaved = false;
|
||||
window.ElectronFiddle.app.setupUnsavedOnChangeListener();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ 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';
|
||||
import { ipcRendererManager } from './ipc';
|
||||
import { findModulesInEditors, getIsNpmInstalled, installModules, npmRun } from './npm';
|
||||
import { AppState } from './state';
|
||||
@@ -44,7 +44,7 @@ export class Runner {
|
||||
public async run(): Promise<boolean> {
|
||||
const { fileManager, getEditorValues } = window.ElectronFiddle.app;
|
||||
const options = { includeDependencies: false, includeElectron: false };
|
||||
const { binaryManager, currentElectronVersion } = this.appState;
|
||||
const { currentElectronVersion } = this.appState;
|
||||
const { version, localPath } = currentElectronVersion;
|
||||
|
||||
if (this.appState.isClearingConsoleOnRun) {
|
||||
@@ -65,7 +65,7 @@ export class Runner {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isReady = await binaryManager.getIsDownloaded(version, localPath);
|
||||
const isReady = await getIsDownloaded(version, localPath);
|
||||
|
||||
if (!isReady) {
|
||||
console.warn(`Runner: Binary ${version} not ready`);
|
||||
@@ -184,17 +184,19 @@ export class Runner {
|
||||
* @memberof Runner
|
||||
*/
|
||||
public async execute(dir: string): Promise<void> {
|
||||
const { currentElectronVersion, pushOutput, binaryManager } = this.appState;
|
||||
const { currentElectronVersion, pushOutput } = this.appState;
|
||||
const { version, localPath } = currentElectronVersion;
|
||||
const binaryPath = binaryManager.getElectronBinaryPath(version, localPath);
|
||||
const binaryPath = getElectronBinaryPath(version, localPath);
|
||||
console.log(`Runner: Binary ${binaryPath} ready, launching`);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -278,7 +280,7 @@ export class Runner {
|
||||
}
|
||||
|
||||
const name = await this.appState.getName();
|
||||
const appData = getAppDataDir(name);
|
||||
const appData = path.join(this.appState.appData, name);
|
||||
|
||||
console.log(`Cleanup: Deleting data dir ${appData}`);
|
||||
await window.ElectronFiddle.app.fileManager.cleanup(appData);
|
||||
|
||||
+97
-45
@@ -6,15 +6,15 @@ import {
|
||||
ALL_MOSAICS,
|
||||
DocsDemoPage,
|
||||
EditorId,
|
||||
ElectronVersion,
|
||||
ElectronVersionSource,
|
||||
ElectronVersionState,
|
||||
GenericDialogOptions,
|
||||
GenericDialogType,
|
||||
MosaicId,
|
||||
Version,
|
||||
OutputEntry,
|
||||
OutputOptions
|
||||
OutputOptions,
|
||||
RunnableVersion,
|
||||
Version,
|
||||
VersionSource,
|
||||
VersionState
|
||||
} from '../interfaces';
|
||||
import { IpcEvents } from '../ipc-events';
|
||||
import { arrayToStringMap } from '../utils/array-to-stringmap';
|
||||
@@ -24,7 +24,7 @@ import { getName } from '../utils/get-title';
|
||||
import { fancyImport } from '../utils/import';
|
||||
import { normalizeVersion } from '../utils/normalize-version';
|
||||
import { isEditorBackup, isEditorId, isPanelId } from '../utils/type-checks';
|
||||
import { BinaryManager } from './binary';
|
||||
import { getDownloadedVersions, getDownloadingVersions, removeBinary, setupBinary } from './binary';
|
||||
import { Bisector } from './bisect';
|
||||
import { DEFAULT_CLOSED_PANELS, DEFAULT_MOSAIC_ARRANGEMENT } from './constants';
|
||||
import { getContent, isContentUnchanged } from './content';
|
||||
@@ -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,
|
||||
@@ -78,19 +79,19 @@ export class AppState {
|
||||
@observable public channelsToShow: Array<ElectronReleaseChannel> =
|
||||
this.retrieve('channelsToShow') as Array<ElectronReleaseChannel>
|
||||
|| [ElectronReleaseChannel.stable, ElectronReleaseChannel.beta];
|
||||
@observable public statesToShow: Array<ElectronVersionState> =
|
||||
this.retrieve('statesToShow') as Array<ElectronVersionState>
|
||||
|| [ElectronVersionState.downloading, ElectronVersionState.ready, ElectronVersionState.unknown];
|
||||
@observable public statesToShow: Array<VersionState> =
|
||||
this.retrieve('statesToShow') as Array<VersionState>
|
||||
|| [VersionState.downloading, VersionState.ready, VersionState.unknown];
|
||||
@observable public isKeepingUserDataDirs: boolean = !!this.retrieve('isKeepingUserDataDirs');
|
||||
@observable public isEnablingElectronLogging: boolean = !!this.retrieve('isEnablingElectronLogging');
|
||||
@observable public isClearingConsoleOnRun: boolean = !!this.retrieve('isClearingConsoleOnRun');
|
||||
@observable public executionFlags: Array<string> =
|
||||
this.retrieve('executionFlags') as Array<string> === null ?
|
||||
[] : this.retrieve('executionFlags') as Array<string>;
|
||||
[] : this.retrieve('executionFlags') as Array<string>;
|
||||
|
||||
// -- Various session-only state ------------------
|
||||
@observable public gistId: string = '';
|
||||
@observable public versions: Record<string, ElectronVersion> = arrayToStringMap(knownVersions);
|
||||
@observable public versions: Record<string, RunnableVersion> = arrayToStringMap(knownVersions);
|
||||
@observable public output: Array<OutputEntry> = [];
|
||||
@observable public localPath: string | undefined;
|
||||
@observable public genericDialogOptions = { type: GenericDialogType.warning, label: '', ok: 'Okay', cancel: 'Cancel' };
|
||||
@@ -99,13 +100,13 @@ export class AppState {
|
||||
@observable public templateName: string | undefined;
|
||||
@observable public currentDocsDemoPage: DocsDemoPage = DocsDemoPage.DEFAULT;
|
||||
@observable public localTypeWatcher: fsType.FSWatcher | undefined;
|
||||
@observable public binaryManager: BinaryManager = new BinaryManager();
|
||||
@observable public Bisector: Bisector | undefined;
|
||||
|
||||
@observable public isPublishing: boolean = false;
|
||||
@observable public isRunning: boolean = false;
|
||||
@observable public isUnsaved: boolean = false;
|
||||
@observable public isUnsaved: boolean;
|
||||
@observable public isUpdatingElectronVersions: boolean = false;
|
||||
@observable public isQuitting: boolean = false;
|
||||
|
||||
// -- Various "isShowing" settings ------------------
|
||||
@observable public isBisectCommandShowing: boolean;
|
||||
@@ -123,6 +124,7 @@ export class AppState {
|
||||
|
||||
private outputBuffer: string = '';
|
||||
private name: string;
|
||||
public appData: string;
|
||||
|
||||
constructor() {
|
||||
// Bind all actions
|
||||
@@ -140,6 +142,8 @@ 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);
|
||||
@@ -150,6 +154,8 @@ 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));
|
||||
@@ -167,8 +173,11 @@ export class AppState {
|
||||
autorun(() => this.save('statesToShow', this.statesToShow));
|
||||
|
||||
autorun(() => {
|
||||
if (this.isUnsaved) {
|
||||
if (typeof this.isUnsaved === 'undefined') return;
|
||||
|
||||
if (!!this.isUnsaved) {
|
||||
window.onbeforeunload = () => {
|
||||
ipcRendererManager.send(IpcEvents.SHOW_INACTIVE);
|
||||
this.setGenericDialogOptions({
|
||||
type: GenericDialogType.warning,
|
||||
label: `The current Fiddle is unsaved. Do you want to exit anyway?`,
|
||||
@@ -179,26 +188,36 @@ 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 (this.genericDialogLastResult) {
|
||||
if (quitConfirmed) {
|
||||
window.onbeforeunload = null;
|
||||
window.close();
|
||||
}
|
||||
|
||||
// Should we just close or quit?
|
||||
const remote = require('electron').remote;
|
||||
const isQuitting = remote.getGlobal('isQuitting');
|
||||
|
||||
if (isQuitting) {
|
||||
remote.app.quit();
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// return value doesn't matter, we just want to cancel the event
|
||||
return false;
|
||||
};
|
||||
} else {
|
||||
window.onbeforeunload = null;
|
||||
|
||||
// set up editor listeners to verify if unsaved
|
||||
Object.keys(window.ElectronFiddle.editors).forEach((key) => {
|
||||
const editor = window.ElectronFiddle.editors[key];
|
||||
const disposable = editor.onDidChangeModelContent(() => {
|
||||
this.isUnsaved = true;
|
||||
disposable.dispose();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -210,10 +229,10 @@ export class AppState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current ElectronVersion or the first
|
||||
* Returns the current RunnableVersion or the first
|
||||
* one that can be found.
|
||||
*/
|
||||
@computed get currentElectronVersion(): ElectronVersion {
|
||||
@computed get currentElectronVersion(): RunnableVersion {
|
||||
if (this.versions[this.version]) {
|
||||
return this.versions[this.version];
|
||||
} else {
|
||||
@@ -225,8 +244,8 @@ export class AppState {
|
||||
* Returns an array of Electron versions to show given the
|
||||
* current settings for states and channels to display
|
||||
*/
|
||||
@computed get versionsToShow(): Array<ElectronVersion> {
|
||||
return sortedElectronMap<ElectronVersion>(this.versions, (_key, item) => item)
|
||||
@computed get versionsToShow(): Array<RunnableVersion> {
|
||||
return sortedElectronMap<RunnableVersion>(this.versions, (_key, item) => item)
|
||||
.filter((item) => {
|
||||
if (!item) {
|
||||
return false;
|
||||
@@ -323,6 +342,10 @@ 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');
|
||||
@@ -376,7 +399,7 @@ export class AppState {
|
||||
const updatedVersions = { ...this.versions };
|
||||
|
||||
// Actually remove
|
||||
if (release && release.source === ElectronVersionSource.local) {
|
||||
if (release && release.source === VersionSource.local) {
|
||||
delete updatedVersions[version];
|
||||
|
||||
const versionsAsArray = Object
|
||||
@@ -385,8 +408,8 @@ export class AppState {
|
||||
|
||||
saveLocalVersions(versionsAsArray);
|
||||
} else {
|
||||
await this.binaryManager.remove(version);
|
||||
updatedVersions[version].state = ElectronVersionState.unknown;
|
||||
await removeBinary(version);
|
||||
updatedVersions[version].state = VersionState.unknown;
|
||||
}
|
||||
|
||||
this.versions = updatedVersions;
|
||||
@@ -404,18 +427,17 @@ export class AppState {
|
||||
console.log(`State: Downloading Electron ${version}`);
|
||||
|
||||
const release = this.versions[version] || { state: '', source: '' };
|
||||
const isLocal = release.source === ElectronVersionSource.local;
|
||||
const isLocal = release.source === VersionSource.local;
|
||||
const isReady = release.state === 'ready';
|
||||
|
||||
// Fetch new binaries, maybe?
|
||||
if (!isLocal && !isReady) {
|
||||
console.log(`State: Instructing BinaryManager to fetch v${version}`);
|
||||
console.log(`State: Fetching v${version}`);
|
||||
const updatedVersions = { ...this.versions };
|
||||
updatedVersions[version] = updatedVersions[version] || {};
|
||||
updatedVersions[version].state = ElectronVersionState.downloading;
|
||||
this.versions = updatedVersions;
|
||||
|
||||
await this.binaryManager.setup(version);
|
||||
await setupBinary(this, version);
|
||||
this.updateDownloadedVersionState();
|
||||
} else {
|
||||
console.log(`State: Version ${version} already downloaded, doing nothing.`);
|
||||
@@ -451,14 +473,18 @@ export class AppState {
|
||||
// Update TypeScript definitions
|
||||
const versionObject = this.versions[version];
|
||||
|
||||
if (versionObject.source === ElectronVersionSource.local) {
|
||||
if (versionObject.source === VersionSource.local) {
|
||||
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.`);
|
||||
@@ -478,14 +504,23 @@ export class AppState {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@action public async updateDownloadedVersionState(): Promise<void> {
|
||||
const downloadedVersions = await this.binaryManager.getDownloadedVersions();
|
||||
const updatedVersions = { ...this.versions };
|
||||
|
||||
(downloadedVersions || []).forEach((version) => {
|
||||
// Keep state of currently downloading binaries first
|
||||
const downloadingVersions = getDownloadingVersions(this);
|
||||
(downloadingVersions || []).forEach((version) => {
|
||||
if (updatedVersions[version]) {
|
||||
updatedVersions[version].state = ElectronVersionState.ready;
|
||||
updatedVersions[version].state = VersionState.downloading;
|
||||
}
|
||||
});
|
||||
|
||||
const downloadedVersions = await getDownloadedVersions();
|
||||
(downloadedVersions || []).forEach((version) => {
|
||||
if (updatedVersions[version]) {
|
||||
updatedVersions[version].state = VersionState.ready;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`State: Updated version state`, updatedVersions);
|
||||
|
||||
this.versions = updatedVersions;
|
||||
@@ -520,7 +555,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) {
|
||||
@@ -573,7 +607,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) {
|
||||
@@ -581,6 +615,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
|
||||
@@ -594,6 +631,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -616,6 +659,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.
|
||||
*
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
} from 'electron';
|
||||
import { autorun } from 'mobx';
|
||||
|
||||
import { ElectronVersion, ElectronVersionState } from '../interfaces';
|
||||
import { RunnableVersion, VersionState } from '../interfaces';
|
||||
import { getNiceGreeting } from '../utils/nice-greeting';
|
||||
import { sortedElectronMap } from '../utils/sorted-electron-map';
|
||||
import { AppState } from './state';
|
||||
@@ -22,11 +22,11 @@ const {
|
||||
* Helper method: Returns an icon (emoji, lol) for an Electron
|
||||
* version.
|
||||
*
|
||||
* @param {Partial<ElectronVersion>} { state }
|
||||
* @param {Partial<RunnableVersion>} { state }
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getItemIcon(
|
||||
{ state }: Partial<ElectronVersion> = { state: ElectronVersionState.unknown }
|
||||
{ state }: Partial<RunnableVersion> = { state: VersionState.unknown }
|
||||
) {
|
||||
return state === 'ready'
|
||||
? '💾'
|
||||
|
||||
+19
-20
@@ -1,6 +1,6 @@
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState, Version } from '../interfaces';
|
||||
import { normalizeVersion } from '../utils/normalize-version';
|
||||
import semver from 'semver';
|
||||
import { RunnableVersion, Version, VersionSource, VersionState } from '../interfaces';
|
||||
import { normalizeVersion } from '../utils/normalize-version';
|
||||
|
||||
export const enum ElectronReleaseChannel {
|
||||
stable = 'Stable',
|
||||
@@ -12,11 +12,11 @@ export const enum ElectronReleaseChannel {
|
||||
/**
|
||||
* Returns a sensible default version string.
|
||||
*
|
||||
* @param {Array<ElectronVersion>} knownVersions
|
||||
* @param {Array<RunnableVersion>} knownVersions
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getDefaultVersion(
|
||||
knownVersions: Array<ElectronVersion> = []
|
||||
knownVersions: Array<RunnableVersion> = []
|
||||
): string {
|
||||
const ls = localStorage.getItem('version');
|
||||
|
||||
@@ -52,7 +52,6 @@ export function getDefaultVersion(
|
||||
export function getReleaseChannel(
|
||||
input: Version | string
|
||||
): ElectronReleaseChannel {
|
||||
|
||||
const tag = (typeof input === 'string') ? input : (input.version || '');
|
||||
|
||||
if (tag.includes('beta')) {
|
||||
@@ -128,20 +127,20 @@ function saveVersions(key: VersionKeys, versions: Array<Version>) {
|
||||
*
|
||||
* @returns {Array<Version>}
|
||||
*/
|
||||
export function getElectronVersions(): Array<ElectronVersion> {
|
||||
const known: Array<ElectronVersion> = getKnownVersions().map((version) => {
|
||||
export function getElectronVersions(): Array<RunnableVersion> {
|
||||
const known: Array<RunnableVersion> = getKnownVersions().map((version) => {
|
||||
return {
|
||||
...version,
|
||||
source: ElectronVersionSource.remote,
|
||||
state: ElectronVersionState.unknown
|
||||
source: VersionSource.remote,
|
||||
state: VersionState.unknown
|
||||
};
|
||||
});
|
||||
|
||||
const local: Array<ElectronVersion> = getLocalVersions().map((version) => {
|
||||
const local: Array<RunnableVersion> = getLocalVersions().map((version) => {
|
||||
return {
|
||||
...version,
|
||||
source: ElectronVersionSource.local,
|
||||
state: ElectronVersionState.ready
|
||||
source: VersionSource.local,
|
||||
state: VersionState.ready
|
||||
};
|
||||
});
|
||||
|
||||
@@ -182,10 +181,10 @@ export function getLocalVersions(): Array<Version> {
|
||||
*
|
||||
* @param {Array<Version>} versions
|
||||
*/
|
||||
export function saveLocalVersions(versions: Array<Version | ElectronVersion>) {
|
||||
export function saveLocalVersions(versions: Array<Version | RunnableVersion>) {
|
||||
const filteredVersions = versions.filter((v) => {
|
||||
if (isElectronVersion(v)) {
|
||||
return v.source === ElectronVersionSource.local;
|
||||
return v.source === VersionSource.local;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -218,10 +217,10 @@ export function saveKnownVersions(versions: Array<Version>) {
|
||||
* saved after.
|
||||
*
|
||||
* @export
|
||||
* @returns {Promise<Array<ElectronVersion>>}
|
||||
* @returns {Promise<Array<RunnableVersion>>}
|
||||
*/
|
||||
export async function getUpdatedElectronVersions(
|
||||
): Promise<Array<ElectronVersion>> {
|
||||
): Promise<Array<RunnableVersion>> {
|
||||
try {
|
||||
await fetchVersions();
|
||||
} catch (error) {
|
||||
@@ -241,7 +240,7 @@ export async function fetchVersions() {
|
||||
const data = await response.json();
|
||||
|
||||
// pre-0.24.0 versions were technically 'atom-shell' and cannot
|
||||
// be downloaded with electron-download
|
||||
// be downloaded with @electron/get
|
||||
const MIN_DOWNLOAD_VERSION = '0.24.0';
|
||||
|
||||
const output = data
|
||||
@@ -290,7 +289,7 @@ export function migrateVersions(input: Array<any> = []): Array<Version> {
|
||||
}
|
||||
|
||||
export function isElectronVersion(
|
||||
input: Version | ElectronVersion
|
||||
): input is ElectronVersion {
|
||||
return (input as ElectronVersion).source !== undefined;
|
||||
input: Version | RunnableVersion
|
||||
): input is RunnableVersion {
|
||||
return (input as RunnableVersion).source !== undefined;
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
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,16 +1,16 @@
|
||||
import { ElectronVersion } from '../interfaces';
|
||||
import { RunnableVersion } from '../interfaces';
|
||||
import { normalizeVersion } from './normalize-version';
|
||||
|
||||
/**
|
||||
* Takes an array of GitHub releases and returns a StringMap of
|
||||
* Electron releases.
|
||||
*
|
||||
* @param {Array<ElectronVersion>} input
|
||||
* @returns {Record<string, ElectronVersion>}
|
||||
* @param {Array<RunnableVersion>} input
|
||||
* @returns {Record<string, RunnableVersion>}
|
||||
*/
|
||||
export function arrayToStringMap(
|
||||
input: Array<ElectronVersion>
|
||||
): Record<string, ElectronVersion> {
|
||||
input: Array<RunnableVersion>
|
||||
): Record<string, RunnableVersion> {
|
||||
const output = {};
|
||||
|
||||
input.forEach((version) => {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import * as semver from 'semver';
|
||||
|
||||
import { ElectronVersion } from '../interfaces';
|
||||
import { RunnableVersion } from '../interfaces';
|
||||
|
||||
/**
|
||||
* Sorts Electron versions and returns the result of a
|
||||
* map function.
|
||||
*
|
||||
* @param {Record<string, ElectronVersion>} versions
|
||||
* @param {(key: string, version: ElectronVersion) => void} mapFn
|
||||
* @param {Record<string, RunnableVersion>} versions
|
||||
* @param {(key: string, version: RunnableVersion) => void} mapFn
|
||||
* @returns {Array<T>}
|
||||
*/
|
||||
export function sortedElectronMap<T>(
|
||||
versions: Record<string, ElectronVersion>,
|
||||
mapFn: (key: string, version: ElectronVersion) => T
|
||||
versions: Record<string, RunnableVersion>,
|
||||
mapFn: (key: string, version: RunnableVersion) => T
|
||||
) {
|
||||
return Object.keys(versions)
|
||||
.sort((a, b) => {
|
||||
|
||||
@@ -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": 322,
|
||||
"lastElectronVersion": "9.0.0-beta.6"
|
||||
"expectedVersionCount": 371,
|
||||
"lastElectronVersion": "10.0.0-beta.8"
|
||||
}
|
||||
|
||||
+19
-4
@@ -4,7 +4,9 @@
|
||||
|
||||
import { app } from 'electron';
|
||||
|
||||
import { main, onBeforeQuit, onReady, onWindowsAllClosed } from '../../src/main/main';
|
||||
import { IpcEvents } from '../../src/ipc-events';
|
||||
import { ipcMainManager } from '../../src/main/ipc';
|
||||
import { main, onBeforeQuit, onReady, onWindowsAllClosed, quitAppIfConfirmed } from '../../src/main/main';
|
||||
import { shouldQuit } from '../../src/main/squirrel';
|
||||
import { setupUpdates } from '../../src/main/update';
|
||||
import { getOrCreateMainWindow } from '../../src/main/windows';
|
||||
@@ -26,6 +28,8 @@ 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.
|
||||
@@ -62,11 +66,22 @@ describe('main', () => {
|
||||
});
|
||||
|
||||
describe('onBeforeQuit()', () => {
|
||||
it('sets a global', () => {
|
||||
it('sets up IPC so app can quit if dialog confirmed', () => {
|
||||
onBeforeQuit();
|
||||
expect(ipcMainManager.send).toHaveBeenCalledWith(IpcEvents.BEFORE_QUIT);
|
||||
expect(ipcMainManager.on).toHaveBeenCalledWith(IpcEvents.CONFIRM_QUIT, quitAppIfConfirmed);
|
||||
});
|
||||
});
|
||||
|
||||
expect((global as any).isQuitting).toBe(true);
|
||||
(global as any).isQuitting = false;
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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({}));
|
||||
});
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
import { IpcEvents } from '../../src/ipc-events';
|
||||
import { createContextMenu } from '../../src/main/context-menu';
|
||||
import { ipcMainManager } from '../../src/main/ipc';
|
||||
import {
|
||||
browserWindows, getMainWindowOptions, getOrCreateMainWindow
|
||||
} from '../../src/main/windows';
|
||||
@@ -95,5 +97,11 @@ describe('windows', () => {
|
||||
(getOrCreateMainWindow().webContents as any).emit('will-navigate', e);
|
||||
expect(e.preventDefault).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shows the window on IPC event', () => {
|
||||
const w = getOrCreateMainWindow();
|
||||
ipcMainManager.emit(IpcEvents.SHOW_INACTIVE);
|
||||
expect(w.showInactive).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export class MockBinaryManager {
|
||||
public remove = jest.fn();
|
||||
public setup = jest.fn();
|
||||
public getDownloadedVersions = jest.fn();
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState } from '../../src/interfaces';
|
||||
import { RunnableVersion, VersionSource, VersionState } from '../../src/interfaces';
|
||||
import { arrayToStringMap } from '../../src/utils/array-to-stringmap';
|
||||
|
||||
export const mockVersionsArray = [
|
||||
{
|
||||
state: ElectronVersionState.ready,
|
||||
state: VersionState.ready,
|
||||
version: '2.0.2',
|
||||
source: ElectronVersionSource.remote
|
||||
source: VersionSource.remote
|
||||
}, {
|
||||
state: ElectronVersionState.ready,
|
||||
state: VersionState.ready,
|
||||
version: '2.0.1',
|
||||
source: ElectronVersionSource.remote
|
||||
source: VersionSource.remote
|
||||
}, {
|
||||
state: ElectronVersionState.ready,
|
||||
state: VersionState.ready,
|
||||
version: '1.8.7',
|
||||
source: ElectronVersionSource.remote
|
||||
source: VersionSource.remote
|
||||
}
|
||||
];
|
||||
|
||||
export const mockVersions: Record<string, ElectronVersion> = arrayToStringMap(mockVersionsArray);
|
||||
export const mockVersions: Record<string, RunnableVersion> = arrayToStringMap(mockVersionsArray);
|
||||
|
||||
@@ -40,11 +40,9 @@ describe('Editors component', () => {
|
||||
|
||||
const app = new App();
|
||||
const result = (await app.setup()) as HTMLDivElement;
|
||||
app.setupUnsavedOnChangeListener = jest.fn();
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(result.innerHTML).toBe('Dialogs;Header;OutputEditorsWrapper;');
|
||||
expect(app.setupUnsavedOnChangeListener).toHaveBeenCalled();
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
@@ -116,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 = {
|
||||
@@ -145,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 = {
|
||||
@@ -174,7 +174,7 @@ describe('Editors component', () => {
|
||||
const app = new App();
|
||||
(app.state as Partial<AppState>) = new MockState();
|
||||
app.state.isUnsaved = false;
|
||||
app.setupUnsavedOnChangeListener = jest.fn();
|
||||
app.state.setVisibleMosaics = jest.fn();
|
||||
app.setEditorValues = jest.fn();
|
||||
|
||||
const editorValues = {
|
||||
@@ -190,7 +190,6 @@ describe('Editors component', () => {
|
||||
})
|
||||
.then(() => {
|
||||
expect(app.state.isUnsaved).toBe(false);
|
||||
expect(app.setupUnsavedOnChangeListener).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -199,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();
|
||||
|
||||
@@ -230,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();
|
||||
|
||||
@@ -336,23 +337,6 @@ describe('Editors component', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('setupUnsavedOnChangeListener()', () => {
|
||||
it('listens for model change events', async () => {
|
||||
const app = new App();
|
||||
|
||||
app.setupUnsavedOnChangeListener();
|
||||
|
||||
const fn = window.ElectronFiddle.editors!.renderer!
|
||||
.onDidChangeModelContent;
|
||||
const call = (fn as jest.Mock<any>).mock.calls[0];
|
||||
const cb = call[0];
|
||||
|
||||
cb();
|
||||
|
||||
expect(app.state.isUnsaved).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setupResizeListener()', () => {
|
||||
it('attaches to the handler', () => {
|
||||
window.addEventListener = jest.fn();
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { BinaryManager } from '../../src/renderer/binary';
|
||||
import { USER_DATA_PATH } from '../../src/renderer/constants';
|
||||
import {
|
||||
getDownloadedVersions,
|
||||
getDownloadingVersions,
|
||||
getElectronBinaryPath,
|
||||
removeBinary,
|
||||
setupBinary
|
||||
} from '../../src/renderer/binary';
|
||||
import { removeTypeDefsForVersion } from '../../src/renderer/fetch-types';
|
||||
import { overridePlatform, resetPlatform } from '../utils';
|
||||
|
||||
import * as path from 'path';
|
||||
@@ -22,15 +28,25 @@ jest.mock('../../src/utils/import', () => ({
|
||||
}
|
||||
}
|
||||
}));
|
||||
jest.mock('electron-download', () => {
|
||||
return jest.fn();
|
||||
});
|
||||
jest.mock('../../src/renderer/fetch-types', () => ({
|
||||
removeTypeDefsForVersion: jest.fn()
|
||||
}));
|
||||
jest.mock('@electron/get', () => ({
|
||||
download: jest.fn()
|
||||
}));
|
||||
|
||||
|
||||
describe('binary', () => {
|
||||
let binaryManager = new BinaryManager();
|
||||
let mockState: any = {};
|
||||
|
||||
beforeEach(() => {
|
||||
binaryManager = new BinaryManager();
|
||||
mockState = {
|
||||
versions: {
|
||||
'3.0.0': {
|
||||
state: 'downloading'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -43,7 +59,7 @@ describe('binary', () => {
|
||||
|
||||
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
|
||||
|
||||
await binaryManager.remove('v3.0.0');
|
||||
await removeBinary('v3.0.0');
|
||||
expect(fs.remove).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -55,77 +71,51 @@ describe('binary', () => {
|
||||
throw new Error('Bwap bwap');
|
||||
});
|
||||
|
||||
await binaryManager.remove('v3.0.0');
|
||||
await removeBinary('v3.0.0');
|
||||
expect(fs.remove).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
it('attempts to clean up the version\'s associated typedefs', async () => {
|
||||
const fs = require('fs-extra');
|
||||
binaryManager.removeTypeDefsForVersion = jest.fn();
|
||||
|
||||
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
|
||||
|
||||
await binaryManager.remove('v3.0.0');
|
||||
await removeBinary('v3.0.0');
|
||||
expect(fs.remove).toHaveBeenCalled();
|
||||
expect(binaryManager.removeTypeDefsForVersion).toHaveBeenCalled();
|
||||
expect(removeTypeDefsForVersion).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('retries typedef cleanup upon failure', async () => {
|
||||
const fs = require('fs-extra');
|
||||
binaryManager.removeTypeDefsForVersion = jest.fn().mockImplementation(() => {
|
||||
(removeTypeDefsForVersion as jest.Mock).mockImplementation(() => {
|
||||
throw new Error('Bwap bwap');
|
||||
});
|
||||
|
||||
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
|
||||
|
||||
await binaryManager.remove('v3.0.0');
|
||||
expect(binaryManager.removeTypeDefsForVersion).toHaveBeenCalledTimes(4);
|
||||
await removeBinary('v3.0.0');
|
||||
expect(removeTypeDefsForVersion).toHaveBeenCalledTimes(4);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeTypeDefsForVersion()', () => {
|
||||
it('removes a version\'s typedefs', async () => {
|
||||
const fs = require('fs-extra');
|
||||
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
|
||||
await binaryManager.removeTypeDefsForVersion('v3.0.0');
|
||||
expect(fs.remove).toHaveBeenCalledWith(path.join(USER_DATA_PATH, 'electron-typedef', '3.0.0'));
|
||||
});
|
||||
|
||||
it('throws upon fs failure', async () => {
|
||||
const fs = require('fs-extra');
|
||||
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
|
||||
(fs.remove as jest.Mock<any>).mockImplementation(() => {
|
||||
throw new Error('Bwap bwap');
|
||||
});
|
||||
|
||||
expect.assertions(1);
|
||||
|
||||
try {
|
||||
await binaryManager.removeTypeDefsForVersion('v3.0.0');
|
||||
} catch (e) {
|
||||
expect(e).toEqual(new Error('Bwap bwap'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDownloadedVersions()', () => {
|
||||
it('finds downloaded versions', async () => {
|
||||
const fs = require('fs-extra');
|
||||
|
||||
(fs.readdir as jest.Mock<any>).mockReturnValue([ 'v3.0.0' ]);
|
||||
(fs.readdir as jest.Mock<any>).mockReturnValue(['v3.0.0']);
|
||||
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
|
||||
|
||||
const result = await binaryManager.getDownloadedVersions();
|
||||
const result = await getDownloadedVersions();
|
||||
|
||||
expect(result).toEqual([ 'v3.0.0' ]);
|
||||
expect(result).toEqual(['v3.0.0']);
|
||||
});
|
||||
|
||||
it('is okay without versions ', async () => {
|
||||
const fs = require('fs-extra');
|
||||
|
||||
(fs.readdir as jest.Mock<any>).mockReturnValue([]);
|
||||
const result = await binaryManager.getDownloadedVersions();
|
||||
const result = await getDownloadedVersions();
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -135,30 +125,37 @@ describe('binary', () => {
|
||||
(fs.readdir as jest.Mock<any>).mockImplementationOnce(() => {
|
||||
throw new Error('💩');
|
||||
});
|
||||
const result = await binaryManager.getDownloadedVersions();
|
||||
const result = await getDownloadedVersions();
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDownloadingVersions()', () => {
|
||||
it('returns currently downloading versions', () => {
|
||||
const result = getDownloadingVersions(mockState as any);
|
||||
expect(result).toEqual(['3.0.0']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDownloadPath()', () => {
|
||||
it('returns the correct path on Windows', () => {
|
||||
overridePlatform('win32');
|
||||
|
||||
const result = binaryManager.getElectronBinaryPath('v3.0.0');
|
||||
const result = getElectronBinaryPath('v3.0.0');
|
||||
expect(result).toBe(path.join('user/data/electron-bin/v3.0.0/electron.exe'));
|
||||
});
|
||||
|
||||
it('returns the correct path on Linux', () => {
|
||||
overridePlatform('linux');
|
||||
|
||||
const result = binaryManager.getElectronBinaryPath('v3.0.0');
|
||||
const result = getElectronBinaryPath('v3.0.0');
|
||||
expect(result).toBe(path.join('user/data/electron-bin/v3.0.0/electron'));
|
||||
});
|
||||
|
||||
it('returns the correct path on macOS', () => {
|
||||
overridePlatform('darwin');
|
||||
|
||||
const result = binaryManager.getElectronBinaryPath('v3.0.0');
|
||||
const result = getElectronBinaryPath('v3.0.0');
|
||||
const expected = 'user/data/electron-bin/v3.0.0/Electron.app/Contents/MacOS/Electron';
|
||||
expect(result).toBe(path.join(expected));
|
||||
});
|
||||
@@ -166,51 +163,56 @@ describe('binary', () => {
|
||||
it('throws on other platforms', () => {
|
||||
overridePlatform('bleepbloop');
|
||||
|
||||
expect(() => binaryManager.getElectronBinaryPath('v3.0.0'))
|
||||
expect(() => getElectronBinaryPath('v3.0.0'))
|
||||
.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setup()', () => {
|
||||
describe('setupBinary()', () => {
|
||||
it(`downloads a version it hasn't seen before`, async () => {
|
||||
const eDownload = require('electron-download');
|
||||
eDownload.mockImplementationOnce((_p: any, c: any) => c(undefined, '/fake/path'));
|
||||
const { download } = require('@electron/get');
|
||||
download.mockReturnValue('/fake/path');
|
||||
|
||||
await binaryManager.setup('v3.0.0');
|
||||
mockState.versions['3.0.0'] = { state: 'unknown' };
|
||||
await setupBinary(mockState, 'v3.0.0');
|
||||
|
||||
expect(eDownload).toHaveBeenCalled();
|
||||
expect(binaryManager.state['3.0.0']).toBe('ready');
|
||||
expect(download).toHaveBeenCalled();
|
||||
expect(mockState.versions['3.0.0'].state).toBe('ready');
|
||||
});
|
||||
|
||||
it(`does not download a version again`, async () => {
|
||||
binaryManager.getIsDownloaded = jest.fn(async () => true);
|
||||
const fs = require('fs-extra');
|
||||
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
||||
const { download } = require('@electron/get');
|
||||
|
||||
await binaryManager.setup('v3.0.0');
|
||||
mockState.versions['3.0.0'] = { state: 'unknown' };
|
||||
await setupBinary(mockState, 'v3.0.0');
|
||||
|
||||
expect(binaryManager.getIsDownloaded).toHaveBeenCalled();
|
||||
expect(require('electron-download')).toHaveBeenCalledTimes(0);
|
||||
expect(binaryManager.state['3.0.0']).toBe('ready');
|
||||
expect(download).toHaveBeenCalledTimes(0);
|
||||
expect(mockState.versions['3.0.0'].state).toBe('ready');
|
||||
});
|
||||
|
||||
it(`does not download a version while already downloading`, async () => {
|
||||
binaryManager.getIsDownloaded = jest.fn(async () => true);
|
||||
binaryManager.state['3.0.0'] = 'downloading';
|
||||
const fs = require('fs-extra');
|
||||
const { download } = require('@electron/get');
|
||||
|
||||
await binaryManager.setup('v3.0.0');
|
||||
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
||||
mockState.versions['3.0.0'].state = 'downloading';
|
||||
|
||||
expect(binaryManager.getIsDownloaded).toHaveBeenCalledTimes(0);
|
||||
expect(require('electron-download')).toHaveBeenCalledTimes(0);
|
||||
expect(binaryManager.state['3.0.0']).toBe('downloading');
|
||||
await setupBinary(mockState, 'v3.0.0');
|
||||
|
||||
expect(download).toHaveBeenCalledTimes(0);
|
||||
expect(mockState.versions['3.0.0'].state).toBe('downloading');
|
||||
});
|
||||
|
||||
it('handles an error in the zip file', async () => {
|
||||
const eDownload = require('electron-download');
|
||||
eDownload.mockImplementationOnce((_p: any, c: any) => c(undefined, '/fake/path'));
|
||||
const { download } = require('@electron/get');
|
||||
download.mockReturnValue('/fake/path');
|
||||
|
||||
const mockZip = require('extract-zip');
|
||||
mockZip.mockImplementationOnce((_a: any, _b: any, c: any) => c(new Error('bwap-bwap')));
|
||||
|
||||
await binaryManager.setup('v3.0.0');
|
||||
await setupBinary(mockState, 'v3.0.0');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ElectronVersionSource, ElectronVersionState } from '../../src/interfaces';
|
||||
import { VersionSource, VersionState } from '../../src/interfaces';
|
||||
import { Bisector } from '../../src/renderer/bisect';
|
||||
|
||||
const generateVersionRange = (rangeLength: number) =>
|
||||
(new Array(rangeLength)).fill(0).map((_, i) => ({
|
||||
state: ElectronVersionState.ready,
|
||||
state: VersionState.ready,
|
||||
version: `${i + 1}.0.0`,
|
||||
source: ElectronVersionSource.local
|
||||
source: VersionSource.local
|
||||
}));
|
||||
|
||||
describe('bisect', () => {
|
||||
|
||||
@@ -1,19 +1,42 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Bisect commands component renders bisect dialog button if no bisect instance 1`] = `
|
||||
<Blueprint3.Button
|
||||
icon="git-branch"
|
||||
text="Bisect"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Bisect commands component renders helper buttons if bisect instance is active 1`] = `
|
||||
exports[`Bisect commands component disables helper buttons if Electron binary is downloading 1`] = `
|
||||
<Fragment>
|
||||
<Blueprint3.Button
|
||||
disabled={true}
|
||||
icon="thumbs-up"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
disabled={true}
|
||||
icon="thumbs-down"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
icon="cross"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Cancel Bisect
|
||||
</Blueprint3.Button>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
exports[`Bisect commands component renders bisect dialog button if no bisect instance 1`] = `
|
||||
<Blueprint3.Button
|
||||
icon="git-branch"
|
||||
text="Bisect"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Bisect commands component renders helper buttons if bisect instance is active 1`] = `
|
||||
<Fragment>
|
||||
<Blueprint3.Button
|
||||
disabled={false}
|
||||
icon="thumbs-up"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
disabled={false}
|
||||
icon="thumbs-down"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,71 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`EditorDropdown component disables hide button if only one editor open 1`] = `
|
||||
Array [
|
||||
<Blueprint3.MenuItem
|
||||
disabled={false}
|
||||
icon="eye-off"
|
||||
id="main"
|
||||
multiline={false}
|
||||
onClick={[Function]}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="Main Process (main.js)"
|
||||
/>,
|
||||
<Blueprint3.MenuItem
|
||||
disabled={false}
|
||||
icon="eye-off"
|
||||
id="renderer"
|
||||
multiline={false}
|
||||
onClick={[Function]}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="Renderer Process (renderer.js)"
|
||||
/>,
|
||||
<Blueprint3.MenuItem
|
||||
disabled={false}
|
||||
icon="eye-off"
|
||||
id="preload"
|
||||
multiline={false}
|
||||
onClick={[Function]}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="Preload (preload.js)"
|
||||
/>,
|
||||
<Blueprint3.MenuItem
|
||||
disabled={true}
|
||||
icon="eye-open"
|
||||
id="html"
|
||||
multiline={false}
|
||||
onClick={[Function]}
|
||||
popoverProps={Object {}}
|
||||
shouldDismissPopover={true}
|
||||
text="HTML (index.html)"
|
||||
/>,
|
||||
<Blueprint3.MenuItem
|
||||
disabled={false}
|
||||
icon="eye-off"
|
||||
id="css"
|
||||
multiline={false}
|
||||
onClick={[Function]}
|
||||
popoverProps={Object {}}
|
||||
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>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`EditorDropdown component renders 1`] = `
|
||||
<Fragment>
|
||||
<Blueprint3.Popover
|
||||
@@ -57,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}
|
||||
@@ -148,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>
|
||||
`;
|
||||
|
||||
@@ -16,6 +16,7 @@ exports[`Runner component renders "checking status" 1`] = `
|
||||
exports[`Runner component renders default 1`] = `
|
||||
<Blueprint3.Button
|
||||
className="button-run"
|
||||
disabled={false}
|
||||
icon="play"
|
||||
onClick={[MockFunction]}
|
||||
text="Run"
|
||||
@@ -39,6 +40,7 @@ exports[`Runner component renders running 1`] = `
|
||||
<Blueprint3.Button
|
||||
active={true}
|
||||
className="button-run"
|
||||
disabled={false}
|
||||
icon="stop"
|
||||
onClick={[MockFunction]}
|
||||
text="Stop"
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Editors component does not render toolbar controls if only one editor exists 1`] = `
|
||||
<div>
|
||||
<div>
|
||||
<h5>
|
||||
Main Process (main.js)
|
||||
</h5>
|
||||
</div>
|
||||
<div />
|
||||
<div
|
||||
className="mosaic-controls"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Editors component renders 1`] = `
|
||||
<div
|
||||
className="focused__undefined mosaic mosaic-drop-target"
|
||||
@@ -1200,70 +1214,72 @@ exports[`Editors component renders a toolbar 1`] = `
|
||||
<div
|
||||
className="mosaic-controls"
|
||||
>
|
||||
<MaximizeButton
|
||||
appState={
|
||||
Object {
|
||||
"currentDocsDemoPage": "DEFAULT",
|
||||
"isSettingsShowing": false,
|
||||
"isTokenDialogShowing": false,
|
||||
"mosaicArrangement": Object {
|
||||
"direction": "row",
|
||||
"first": Object {
|
||||
"direction": "column",
|
||||
"first": "main",
|
||||
<React.Fragment>
|
||||
<MaximizeButton
|
||||
appState={
|
||||
Object {
|
||||
"currentDocsDemoPage": "DEFAULT",
|
||||
"isSettingsShowing": false,
|
||||
"isTokenDialogShowing": false,
|
||||
"mosaicArrangement": Object {
|
||||
"direction": "row",
|
||||
"first": Object {
|
||||
"direction": "column",
|
||||
"first": "main",
|
||||
"second": Object {
|
||||
"direction": "column",
|
||||
"first": "renderer",
|
||||
"second": "preload",
|
||||
},
|
||||
},
|
||||
"second": Object {
|
||||
"direction": "column",
|
||||
"first": "renderer",
|
||||
"second": "preload",
|
||||
"first": "html",
|
||||
"second": Object {
|
||||
"direction": "column",
|
||||
"first": "css",
|
||||
"second": "docsDemo",
|
||||
},
|
||||
},
|
||||
},
|
||||
"second": Object {
|
||||
"direction": "column",
|
||||
"first": "html",
|
||||
"second": Object {
|
||||
"direction": "column",
|
||||
"first": "css",
|
||||
"second": "docsDemo",
|
||||
},
|
||||
},
|
||||
},
|
||||
"setGenericDialogOptions": [Function],
|
||||
"setGenericDialogOptions": [Function],
|
||||
}
|
||||
}
|
||||
}
|
||||
id="main"
|
||||
/>
|
||||
<RemoveButton
|
||||
appState={
|
||||
Object {
|
||||
"currentDocsDemoPage": "DEFAULT",
|
||||
"isSettingsShowing": false,
|
||||
"isTokenDialogShowing": false,
|
||||
"mosaicArrangement": Object {
|
||||
"direction": "row",
|
||||
"first": Object {
|
||||
"direction": "column",
|
||||
"first": "main",
|
||||
id="main"
|
||||
/>
|
||||
<RemoveButton
|
||||
appState={
|
||||
Object {
|
||||
"currentDocsDemoPage": "DEFAULT",
|
||||
"isSettingsShowing": false,
|
||||
"isTokenDialogShowing": false,
|
||||
"mosaicArrangement": Object {
|
||||
"direction": "row",
|
||||
"first": Object {
|
||||
"direction": "column",
|
||||
"first": "main",
|
||||
"second": Object {
|
||||
"direction": "column",
|
||||
"first": "renderer",
|
||||
"second": "preload",
|
||||
},
|
||||
},
|
||||
"second": Object {
|
||||
"direction": "column",
|
||||
"first": "renderer",
|
||||
"second": "preload",
|
||||
"first": "html",
|
||||
"second": Object {
|
||||
"direction": "column",
|
||||
"first": "css",
|
||||
"second": "docsDemo",
|
||||
},
|
||||
},
|
||||
},
|
||||
"second": Object {
|
||||
"direction": "column",
|
||||
"first": "html",
|
||||
"second": Object {
|
||||
"direction": "column",
|
||||
"first": "css",
|
||||
"second": "docsDemo",
|
||||
},
|
||||
},
|
||||
},
|
||||
"setGenericDialogOptions": [Function],
|
||||
"setGenericDialogOptions": [Function],
|
||||
}
|
||||
}
|
||||
}
|
||||
id="main"
|
||||
/>
|
||||
id="main"
|
||||
/>
|
||||
</React.Fragment>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -11,38 +11,103 @@ exports[`ElectronSettings component renders 1`] = `
|
||||
<Blueprint3.FormGroup
|
||||
label="Include Electron versions from these release channels:"
|
||||
>
|
||||
<Blueprint3.Checkbox
|
||||
checked={true}
|
||||
id="Stable"
|
||||
inline={true}
|
||||
label="Stable"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
<Blueprint3.Checkbox
|
||||
checked={true}
|
||||
id="Beta"
|
||||
inline={true}
|
||||
label="Beta"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
<Blueprint3.Checkbox
|
||||
checked={false}
|
||||
id="Nightly"
|
||||
inline={true}
|
||||
label="Nightly"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
<Blueprint3.Checkbox
|
||||
checked={false}
|
||||
id="Unsupported"
|
||||
inline={true}
|
||||
label="Unsupported"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
<Blueprint3.Tooltip
|
||||
content="Can't disable channel of selected version (2.0.1)"
|
||||
disabled={false}
|
||||
hoverCloseDelay={0}
|
||||
hoverOpenDelay={100}
|
||||
intent="primary"
|
||||
key="Stable"
|
||||
position="bottom"
|
||||
transitionDuration={100}
|
||||
>
|
||||
<Blueprint3.Checkbox
|
||||
checked={true}
|
||||
disabled={true}
|
||||
id="Stable"
|
||||
inline={true}
|
||||
label="Stable"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</Blueprint3.Tooltip>
|
||||
<Blueprint3.Tooltip
|
||||
content="Can't disable channel of selected version (2.0.1)"
|
||||
disabled={true}
|
||||
hoverCloseDelay={0}
|
||||
hoverOpenDelay={100}
|
||||
intent="primary"
|
||||
key="Beta"
|
||||
position="bottom"
|
||||
transitionDuration={100}
|
||||
>
|
||||
<Blueprint3.Checkbox
|
||||
checked={true}
|
||||
disabled={false}
|
||||
id="Beta"
|
||||
inline={true}
|
||||
label="Beta"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</Blueprint3.Tooltip>
|
||||
<Blueprint3.Tooltip
|
||||
content="Can't disable channel of selected version (2.0.1)"
|
||||
disabled={true}
|
||||
hoverCloseDelay={0}
|
||||
hoverOpenDelay={100}
|
||||
intent="primary"
|
||||
key="Nightly"
|
||||
position="bottom"
|
||||
transitionDuration={100}
|
||||
>
|
||||
<Blueprint3.Checkbox
|
||||
checked={false}
|
||||
disabled={false}
|
||||
id="Nightly"
|
||||
inline={true}
|
||||
label="Nightly"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</Blueprint3.Tooltip>
|
||||
<Blueprint3.Tooltip
|
||||
content="Can't disable channel of selected version (2.0.1)"
|
||||
disabled={true}
|
||||
hoverCloseDelay={0}
|
||||
hoverOpenDelay={100}
|
||||
intent="primary"
|
||||
key="Unsupported"
|
||||
position="bottom"
|
||||
transitionDuration={100}
|
||||
>
|
||||
<Blueprint3.Checkbox
|
||||
checked={false}
|
||||
disabled={false}
|
||||
id="Unsupported"
|
||||
inline={true}
|
||||
label="Unsupported"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</Blueprint3.Tooltip>
|
||||
</Blueprint3.FormGroup>
|
||||
<Blueprint3.FormGroup
|
||||
label="Include Electron versions that are:"
|
||||
>
|
||||
<Blueprint3.Tooltip
|
||||
content="Always enabled"
|
||||
hoverCloseDelay={0}
|
||||
hoverOpenDelay={100}
|
||||
intent="primary"
|
||||
position="bottom"
|
||||
transitionDuration={100}
|
||||
>
|
||||
<Blueprint3.Checkbox
|
||||
checked={true}
|
||||
disabled={true}
|
||||
id="ready"
|
||||
inline={true}
|
||||
label="Ready"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</Blueprint3.Tooltip>
|
||||
<Blueprint3.Checkbox
|
||||
checked={true}
|
||||
id="downloading"
|
||||
@@ -50,13 +115,6 @@ exports[`ElectronSettings component renders 1`] = `
|
||||
label="Downloading"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
<Blueprint3.Checkbox
|
||||
checked={true}
|
||||
id="ready"
|
||||
inline={true}
|
||||
label="Downloaded"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
<Blueprint3.Checkbox
|
||||
checked={false}
|
||||
id="unknown"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
import { VersionState } from '../../../src/interfaces';
|
||||
import { BisectHandler } from '../../../src/renderer/components/commands-bisect';
|
||||
|
||||
describe('Bisect commands component', () => {
|
||||
@@ -11,6 +12,9 @@ describe('Bisect commands component', () => {
|
||||
continue: jest.fn(),
|
||||
getCurrentVersion: jest.fn()
|
||||
},
|
||||
currentElectronVersion: {
|
||||
state: VersionState.ready
|
||||
},
|
||||
setVersion: jest.fn(),
|
||||
version: '1.0.0',
|
||||
pushOutput: jest.fn(),
|
||||
@@ -24,6 +28,12 @@ describe('Bisect commands component', () => {
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('disables helper buttons if Electron binary is downloading', () => {
|
||||
store.currentElectronVersion.state = VersionState.downloading;
|
||||
const wrapper = shallow(<BisectHandler appState={store} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders bisect dialog button if no bisect instance', () => {
|
||||
delete store.Bisector;
|
||||
const wrapper = shallow(<BisectHandler appState={store} />);
|
||||
@@ -51,6 +61,20 @@ describe('Bisect commands component', () => {
|
||||
});
|
||||
|
||||
describe('continueBisect()', () => {
|
||||
it('closes the currently running app', () => {
|
||||
const wrapper = shallow(<BisectHandler appState={store} />);
|
||||
const instance: BisectHandler = wrapper.instance() as any;
|
||||
|
||||
store.Bisector.continue.mockReturnValue({
|
||||
version: '2.0.0'
|
||||
});
|
||||
const mockStop = jest.fn();
|
||||
window.ElectronFiddle.app.runner.stop = mockStop;
|
||||
instance.continueBisect(true);
|
||||
|
||||
expect(mockStop).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('sets version assigned by bisect algorithm', () => {
|
||||
const wrapper = shallow(<BisectHandler appState={store} />);
|
||||
const instance: BisectHandler = wrapper.instance() as any;
|
||||
@@ -68,7 +92,7 @@ describe('Bisect commands component', () => {
|
||||
instance.terminateBisect = jest.fn();
|
||||
|
||||
// same value is only returned when there is only 1 version left
|
||||
store.Bisector.continue.mockReturnValue([{version: 'minVer'}, {version: 'maxVer'}]);
|
||||
store.Bisector.continue.mockReturnValue([{ version: 'minVer' }, { version: 'maxVer' }]);
|
||||
instance.continueBisect(true);
|
||||
expect(store.setVersion).not.toHaveBeenCalled();
|
||||
expect(instance.terminateBisect).toHaveBeenCalled();
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('EditorDropdown component', () => {
|
||||
store = {
|
||||
hideAndBackupMosaic: jest.fn(),
|
||||
showMosaic: jest.fn(),
|
||||
closedPanels: {}
|
||||
closedPanels: {},
|
||||
};
|
||||
|
||||
(getVisibleMosaics as jest.Mock).mockReturnValue([ EditorId.html, EditorId.renderer ]);
|
||||
@@ -46,4 +46,15 @@ describe('EditorDropdown component', () => {
|
||||
expect(store.hideAndBackupMosaic).toHaveBeenCalledTimes(1);
|
||||
expect(store.showMosaic).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('disables hide button if only one editor open', () => {
|
||||
store.mosaicArrangement = 'html';
|
||||
(getVisibleMosaics as jest.Mock).mockReturnValue([ EditorId.html ]);
|
||||
|
||||
const wrapper = mount(<EditorDropdown appState={store} />);
|
||||
const instance = wrapper.instance() as EditorDropdown;
|
||||
const menu = instance.renderMenuItems();
|
||||
|
||||
expect(menu).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersionState } from '../../../src/interfaces';
|
||||
import { VersionState } from '../../../src/interfaces';
|
||||
import { Runner } from '../../../src/renderer/components/commands-runner';
|
||||
import { ipcRendererManager } from '../../../src/renderer/ipc';
|
||||
import { ElectronFiddleMock } from '../../mocks/electron-fiddle';
|
||||
@@ -42,13 +42,13 @@ describe('Runner component', () => {
|
||||
});
|
||||
|
||||
it('renders downloading', () => {
|
||||
store.versions['2.0.2'].state = ElectronVersionState.downloading;
|
||||
store.versions['2.0.2'].state = VersionState.downloading;
|
||||
const wrapper = shallow(<Runner appState={store} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders "checking status"', () => {
|
||||
store.versions['2.0.2'].state = ElectronVersionState.unknown;
|
||||
store.versions['2.0.2'].state = VersionState.unknown;
|
||||
const wrapper = shallow(<Runner appState={store} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersionSource, ElectronVersionState } from '../../../src/interfaces';
|
||||
import { VersionSource, VersionState } from '../../../src/interfaces';
|
||||
import { VersionChooser } from '../../../src/renderer/components/commands-version-chooser';
|
||||
import { ElectronReleaseChannel } from '../../../src/renderer/versions';
|
||||
import { mockVersions } from '../../mocks/electron-versions';
|
||||
|
||||
const { unknown } = ElectronVersionState;
|
||||
const { remote } = ElectronVersionSource;
|
||||
const { unknown } = VersionState;
|
||||
const { remote } = VersionSource;
|
||||
|
||||
describe('VersionSelect component', () => {
|
||||
let store: any;
|
||||
@@ -37,7 +37,7 @@ describe('VersionSelect component', () => {
|
||||
versionsToShow: Object.values(versions).filter((v) => !!v),
|
||||
versions,
|
||||
channelsToShow: [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta ],
|
||||
statesToShow: [ ElectronVersionState.ready, ElectronVersionState.downloading ],
|
||||
statesToShow: [ VersionState.ready, VersionState.downloading ],
|
||||
setVersion: jest.fn(),
|
||||
get currentElectronVersion() {
|
||||
return mockVersions['2.0.2'];
|
||||
|
||||
@@ -2,10 +2,13 @@ import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { IpcEvents } from '../../../src/ipc-events';
|
||||
import { getIsDownloaded } from '../../../src/renderer/binary';
|
||||
import { AddVersionDialog } from '../../../src/renderer/components/dialog-add-version';
|
||||
import { ipcRendererManager } from '../../../src/renderer/ipc';
|
||||
import { overridePlatform, resetPlatform } from '../../utils';
|
||||
|
||||
jest.mock('../../../src/renderer/ipc');
|
||||
jest.mock('../../../src/renderer/binary');
|
||||
|
||||
describe('AddVersionDialog component', () => {
|
||||
let store: any;
|
||||
@@ -66,7 +69,7 @@ describe('AddVersionDialog component', () => {
|
||||
|
||||
describe('setFolderPath()', () => {
|
||||
it('does something', async () => {
|
||||
store.binaryManager.getIsDownloaded.mockResolvedValue(true);
|
||||
(getIsDownloaded as jest.Mock).mockResolvedValue(true);
|
||||
const wrapper = shallow(<AddVersionDialog appState={store} />);
|
||||
await (wrapper.instance() as any).setFolderPath('/test/');
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
import { ElectronVersionSource, ElectronVersionState } from '../../../src/interfaces';
|
||||
import { VersionSource, VersionState } from '../../../src/interfaces';
|
||||
import { Bisector } from '../../../src/renderer/bisect';
|
||||
import { BisectDialog } from '../../../src/renderer/components/dialog-bisect';
|
||||
import { ElectronReleaseChannel } from '../../../src/renderer/versions';
|
||||
@@ -12,9 +12,9 @@ describe('BisectDialog component', () => {
|
||||
|
||||
const generateVersionRange = (rangeLength: number) =>
|
||||
(new Array(rangeLength)).fill(0).map((_, i) => ({
|
||||
state: ElectronVersionState.ready,
|
||||
state: VersionState.ready,
|
||||
version: `${i + 1}.0.0`,
|
||||
source: ElectronVersionSource.local
|
||||
source: VersionSource.local
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -24,7 +24,7 @@ describe('BisectDialog component', () => {
|
||||
versions,
|
||||
versionsToShow: versions,
|
||||
channelsToShow: [ElectronReleaseChannel.stable],
|
||||
statesToShow: [ElectronVersionState.ready],
|
||||
statesToShow: [VersionState.ready],
|
||||
setVersion: jest.fn()
|
||||
};
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -101,56 +101,59 @@ describe('Editor component', () => {
|
||||
expect(monaco.editor.createModel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('attempts to restore a backup if available', async () => {
|
||||
store.getAndRemoveEditorValueBackup.mockReturnValueOnce({
|
||||
model: true,
|
||||
viewState: true
|
||||
describe('backups', async () => {
|
||||
it('attempts to restore a backup if contains a model', async () => {
|
||||
store.getAndRemoveEditorValueBackup.mockReturnValueOnce({
|
||||
model: true,
|
||||
viewState: true
|
||||
});
|
||||
|
||||
const wrapper = shallow(
|
||||
<Editor
|
||||
appState={store}
|
||||
monaco={monaco}
|
||||
monacoOptions={{}}
|
||||
id={EditorId.main}
|
||||
editorDidMount={() => undefined}
|
||||
setFocused={() => undefined}
|
||||
/>
|
||||
);
|
||||
const instance: any = wrapper.instance();
|
||||
|
||||
instance.containerRef.current = 'ref';
|
||||
await instance.initMonaco();
|
||||
|
||||
expect(instance.editor.restoreViewState).toHaveBeenCalledTimes(1);
|
||||
expect(instance.editor.setModel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
const wrapper = shallow(
|
||||
<Editor
|
||||
appState={store}
|
||||
monaco={monaco}
|
||||
monacoOptions={{}}
|
||||
id={EditorId.main}
|
||||
editorDidMount={() => undefined}
|
||||
setFocused={() => undefined}
|
||||
/>
|
||||
);
|
||||
const instance: any = wrapper.instance();
|
||||
it('attempts to restore a backup if contains a string value', async () => {
|
||||
store.getAndRemoveEditorValueBackup.mockReturnValueOnce({
|
||||
value: 'hello'
|
||||
});
|
||||
|
||||
instance.containerRef.current = 'ref';
|
||||
await instance.initMonaco();
|
||||
const wrapper = shallow(
|
||||
<Editor
|
||||
appState={store}
|
||||
monaco={monaco}
|
||||
monacoOptions={{}}
|
||||
id={EditorId.main}
|
||||
editorDidMount={() => undefined}
|
||||
setFocused={() => undefined}
|
||||
/>
|
||||
);
|
||||
const instance: any = wrapper.instance();
|
||||
|
||||
expect(instance.editor.restoreViewState).toHaveBeenCalledTimes(1);
|
||||
expect(instance.editor.setModel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
instance.containerRef.current = 'ref';
|
||||
await instance.initMonaco();
|
||||
|
||||
it('attempts to restore a backup value if available', async () => {
|
||||
store.getAndRemoveEditorValueBackup.mockReturnValueOnce({
|
||||
value: 'hello'
|
||||
expect(instance.editor.restoreViewState).toHaveBeenCalledTimes(0);
|
||||
expect(instance.editor.setModel).toHaveBeenCalledTimes(1);
|
||||
expect(monaco.editor.createModel).toHaveBeenCalledWith('hello', 'javascript');
|
||||
});
|
||||
|
||||
const wrapper = shallow(
|
||||
<Editor
|
||||
appState={store}
|
||||
monaco={monaco}
|
||||
monacoOptions={{}}
|
||||
id={EditorId.main}
|
||||
editorDidMount={() => undefined}
|
||||
setFocused={() => undefined}
|
||||
/>
|
||||
);
|
||||
const instance: any = wrapper.instance();
|
||||
|
||||
instance.containerRef.current = 'ref';
|
||||
await instance.initMonaco();
|
||||
|
||||
expect(instance.editor.restoreViewState).toHaveBeenCalledTimes(0);
|
||||
expect(instance.editor.setModel).toHaveBeenCalledTimes(1);
|
||||
expect(monaco.editor.createModel).toHaveBeenCalledWith('hello', 'javascript');
|
||||
});
|
||||
|
||||
|
||||
it('initializes with a fixed tab size', async () => {
|
||||
const didMount = jest.fn();
|
||||
const wrapper = shallow(
|
||||
|
||||
@@ -122,6 +122,18 @@ describe('Editors component', () => {
|
||||
expect(toolbar).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('does not render toolbar controls if only one editor exists', () => {
|
||||
store.mosaicArrangement = EditorId.main;
|
||||
const wrapper = shallow(<Editors appState={store} />);
|
||||
const instance: Editors = wrapper.instance() as any;
|
||||
const toolbar = instance.renderToolbar(
|
||||
{ title: TITLE_MAP[EditorId.main] } as any,
|
||||
EditorId.main
|
||||
);
|
||||
|
||||
expect(toolbar).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('componentWillUnmount() unsubscribes the layout reaction', () => {
|
||||
const wrapper = shallow(<Editors appState={store} />);
|
||||
const instance: Editors = wrapper.instance() as any;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersionSource, ElectronVersionState } from '../../../src/interfaces';
|
||||
import { VersionSource, VersionState } from '../../../src/interfaces';
|
||||
import { ElectronSettings } from '../../../src/renderer/components/settings-electron';
|
||||
import { ElectronReleaseChannel } from '../../../src/renderer/versions';
|
||||
import { mockVersions } from '../../mocks/electron-versions';
|
||||
@@ -14,7 +14,7 @@ describe('ElectronSettings component', () => {
|
||||
version: '2.0.1',
|
||||
versions: { ...mockVersions },
|
||||
channelsToShow: [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta ],
|
||||
statesToShow: [ ElectronVersionState.ready, ElectronVersionState.downloading ],
|
||||
statesToShow: [ VersionState.ready, VersionState.downloading ],
|
||||
downloadVersion: jest.fn(),
|
||||
removeVersion: jest.fn(),
|
||||
updateElectronVersions: jest.fn(),
|
||||
@@ -29,15 +29,15 @@ describe('ElectronSettings component', () => {
|
||||
|
||||
it('renders', () => {
|
||||
store.versions['3.0.0-nightly.1'] = {
|
||||
state: ElectronVersionState.ready,
|
||||
state: VersionState.ready,
|
||||
version: '3.0.0-nightly.1',
|
||||
source: ElectronVersionSource.local
|
||||
source: VersionSource.local
|
||||
};
|
||||
|
||||
store.versions['3.0.0'] = {
|
||||
state: ElectronVersionState.ready,
|
||||
state: VersionState.ready,
|
||||
version: '3.0.0',
|
||||
source: ElectronVersionSource.local
|
||||
source: VersionSource.local
|
||||
};
|
||||
|
||||
const wrapper = shallow(<ElectronSettings appState={store} />);
|
||||
@@ -47,15 +47,15 @@ describe('ElectronSettings component', () => {
|
||||
|
||||
it('handles removing a version', async () => {
|
||||
store.versions['3.0.0-nightly.1'] = {
|
||||
state: ElectronVersionState.ready,
|
||||
state: VersionState.ready,
|
||||
version: '3.0.0-nightly.1',
|
||||
source: ElectronVersionSource.local
|
||||
source: VersionSource.local
|
||||
};
|
||||
|
||||
store.versions['3.0.0'] = {
|
||||
state: ElectronVersionState.ready,
|
||||
state: VersionState.ready,
|
||||
version: '3.0.0',
|
||||
source: ElectronVersionSource.local
|
||||
source: VersionSource.local
|
||||
};
|
||||
|
||||
const wrapper = mount(<ElectronSettings appState={store} />);
|
||||
@@ -71,13 +71,13 @@ describe('ElectronSettings component', () => {
|
||||
it('handles downloading a version', async () => {
|
||||
store.versions = {
|
||||
'3.0.0': {
|
||||
state: ElectronVersionState.unknown,
|
||||
state: VersionState.unknown,
|
||||
version: '3.0.0',
|
||||
source: ElectronVersionSource.remote
|
||||
source: VersionSource.remote
|
||||
}
|
||||
};
|
||||
|
||||
store.statesToShow.push(ElectronVersionState.unknown);
|
||||
store.statesToShow.push(VersionState.unknown);
|
||||
|
||||
const wrapper = mount(<ElectronSettings appState={store} />);
|
||||
|
||||
@@ -139,21 +139,21 @@ describe('ElectronSettings component', () => {
|
||||
const instance = wrapper.instance() as any;
|
||||
await instance.handleStateChange({
|
||||
currentTarget: {
|
||||
id: ElectronVersionState.ready,
|
||||
id: VersionState.ready,
|
||||
checked: false
|
||||
}
|
||||
});
|
||||
|
||||
await instance.handleStateChange({
|
||||
currentTarget: {
|
||||
id: ElectronVersionState.unknown,
|
||||
id: VersionState.unknown,
|
||||
checked: true
|
||||
}
|
||||
});
|
||||
|
||||
expect(store.statesToShow).toEqual([
|
||||
ElectronVersionState.downloading,
|
||||
ElectronVersionState.unknown
|
||||
VersionState.downloading,
|
||||
VersionState.unknown
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ElectronVersion, ElectronVersionSource, ElectronVersionState } from '../../../src/interfaces';
|
||||
import { RunnableVersion, VersionSource, VersionState } from '../../../src/interfaces';
|
||||
import { filterItem, getItemIcon, getItemLabel, renderItem, VersionSelect } from '../../../src/renderer/components/version-select';
|
||||
import { ElectronReleaseChannel } from '../../../src/renderer/versions';
|
||||
import { mockVersions } from '../../mocks/electron-versions';
|
||||
|
||||
const { ready, unknown, downloading } = ElectronVersionState;
|
||||
const { remote, local } = ElectronVersionSource;
|
||||
const { ready, unknown, downloading } = VersionState;
|
||||
const { remote, local } = VersionSource;
|
||||
|
||||
describe('VersionSelect component', () => {
|
||||
let store: any;
|
||||
@@ -34,15 +34,15 @@ describe('VersionSelect component', () => {
|
||||
'3.0.0-unsupported': { ...mockVersion2 }
|
||||
},
|
||||
channelsToShow: [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta ],
|
||||
statesToShow: [ ElectronVersionState.ready, ElectronVersionState.downloading ],
|
||||
statesToShow: [ VersionState.ready, VersionState.downloading ],
|
||||
setVersion: jest.fn(),
|
||||
get currentElectronVersion() {
|
||||
get currentRunnableVersion() {
|
||||
return mockVersions['2.0.2'];
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const onVersionSelect = () => ({});
|
||||
const onVersionSelect = () => ({});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(
|
||||
@@ -77,7 +77,7 @@ describe('VersionSelect component', () => {
|
||||
|
||||
describe('getItemLabel()', () => {
|
||||
it('returns the correct label for a local version', () => {
|
||||
const input: ElectronVersion = {
|
||||
const input: RunnableVersion = {
|
||||
...mockVersion1,
|
||||
source: local,
|
||||
};
|
||||
@@ -87,7 +87,7 @@ describe('VersionSelect component', () => {
|
||||
});
|
||||
|
||||
it('returns the correct label for a version not downloaded', () => {
|
||||
const input: ElectronVersion = {
|
||||
const input: RunnableVersion = {
|
||||
...mockVersion1,
|
||||
state: unknown
|
||||
};
|
||||
@@ -96,7 +96,7 @@ describe('VersionSelect component', () => {
|
||||
});
|
||||
|
||||
it('returns the correct label for a version downloaded', () => {
|
||||
const input: ElectronVersion = {
|
||||
const input: RunnableVersion = {
|
||||
...mockVersion1,
|
||||
state: ready
|
||||
};
|
||||
@@ -105,7 +105,7 @@ describe('VersionSelect component', () => {
|
||||
});
|
||||
|
||||
it('returns the correct label for a version downloading', () => {
|
||||
const input: ElectronVersion = {
|
||||
const input: RunnableVersion = {
|
||||
...mockVersion1,
|
||||
state: downloading
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
import {
|
||||
ElectronVersion,
|
||||
ElectronVersionSource,
|
||||
ElectronVersionState
|
||||
RunnableVersion,
|
||||
VersionSource,
|
||||
VersionState
|
||||
} from '../../src/interfaces';
|
||||
|
||||
import { USER_DATA_PATH } from '../../src/renderer/constants';
|
||||
import {
|
||||
fetchTypeDefinitions,
|
||||
getDownloadedVersionTypeDefs,
|
||||
@@ -12,12 +12,12 @@ import {
|
||||
getLocalVersionTypeDefs,
|
||||
getOfflineTypeDefinitionPath,
|
||||
getOfflineTypeDefinitions,
|
||||
removeTypeDefsForVersion,
|
||||
updateEditorTypeDefinitions
|
||||
} from '../../src/renderer/fetch-types';
|
||||
|
||||
import * as path from 'path';
|
||||
import { ElectronFiddleMock } from '../mocks/electron-fiddle';
|
||||
|
||||
import * as path from 'path';
|
||||
jest.mock('fs-extra');
|
||||
jest.mock('extract-zip', () => {
|
||||
return jest.fn((_a, _b, c) => c());
|
||||
@@ -92,8 +92,8 @@ describe('fetch-types', () => {
|
||||
|
||||
describe('getDownloadedVersionTypeDefs()', () => {
|
||||
const version = {
|
||||
state: ElectronVersionState.ready,
|
||||
source: ElectronVersionSource.remote,
|
||||
state: VersionState.ready,
|
||||
source: VersionSource.remote,
|
||||
version: '3.0.0'
|
||||
};
|
||||
|
||||
@@ -178,11 +178,11 @@ describe('fetch-types', () => {
|
||||
});
|
||||
|
||||
describe('getLocalVersionTypeDefs()', () => {
|
||||
let version: ElectronVersion;
|
||||
let version: RunnableVersion;
|
||||
beforeEach(() => {
|
||||
version = {
|
||||
state: ElectronVersionState.ready,
|
||||
source: ElectronVersionSource.local,
|
||||
state: VersionState.ready,
|
||||
source: VersionSource.local,
|
||||
version: '3.0.0',
|
||||
localPath: 'somePath'
|
||||
};
|
||||
@@ -198,7 +198,7 @@ describe('fetch-types', () => {
|
||||
});
|
||||
|
||||
it('returns null for remote path', async () => {
|
||||
version.source = ElectronVersionSource.remote;
|
||||
version.source = VersionSource.remote;
|
||||
const result = await getLocalVersionTypeDefs(version);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
@@ -211,11 +211,11 @@ describe('fetch-types', () => {
|
||||
});
|
||||
|
||||
describe('getLocalTypePathForVersion()', () => {
|
||||
let version: ElectronVersion;
|
||||
let version: RunnableVersion;
|
||||
beforeEach(() => {
|
||||
version = {
|
||||
state: ElectronVersionState.ready,
|
||||
source: ElectronVersionSource.local,
|
||||
state: VersionState.ready,
|
||||
source: VersionSource.local,
|
||||
version: '3.0.0',
|
||||
localPath: 'somePath'
|
||||
};
|
||||
@@ -241,13 +241,13 @@ describe('fetch-types', () => {
|
||||
});
|
||||
|
||||
describe('updateEditorTypeDefinitions()', () => {
|
||||
let version: ElectronVersion;
|
||||
let version: RunnableVersion;
|
||||
beforeEach(() => {
|
||||
(global as any).window = window || {};
|
||||
(window as any).ElectronFiddle = new ElectronFiddleMock();
|
||||
version = {
|
||||
state: ElectronVersionState.ready,
|
||||
source: ElectronVersionSource.remote,
|
||||
state: VersionState.ready,
|
||||
source: VersionSource.remote,
|
||||
version: '3.0.0'
|
||||
};
|
||||
});
|
||||
@@ -302,4 +302,29 @@ describe('fetch-types', () => {
|
||||
expect(errored).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeTypeDefsForVersion()', () => {
|
||||
it('removes a version\'s typedefs', async () => {
|
||||
const fs = require('fs-extra');
|
||||
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
|
||||
await removeTypeDefsForVersion('v3.0.0');
|
||||
expect(fs.remove).toHaveBeenCalledWith(path.join(USER_DATA_PATH, 'electron-typedef', '3.0.0'));
|
||||
});
|
||||
|
||||
it('throws upon fs failure', async () => {
|
||||
const fs = require('fs-extra');
|
||||
(fs.existsSync as jest.Mock<any>).mockReturnValue(true);
|
||||
(fs.remove as jest.Mock<any>).mockImplementation(() => {
|
||||
throw new Error('Bwap bwap');
|
||||
});
|
||||
|
||||
expect.assertions(1);
|
||||
|
||||
try {
|
||||
await removeTypeDefsForVersion('v3.0.0');
|
||||
} catch (e) {
|
||||
expect(e).toEqual(new Error('Bwap bwap'));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { spawn } from 'child_process';
|
||||
import * as path from 'path';
|
||||
|
||||
import { IpcEvents } from '../../src/ipc-events';
|
||||
import { getIsDownloaded } from '../../src/renderer/binary';
|
||||
import { ipcRendererManager } from '../../src/renderer/ipc';
|
||||
import {
|
||||
findModulesInEditors,
|
||||
@@ -17,8 +18,13 @@ import { mockVersions } from '../mocks/electron-versions';
|
||||
|
||||
jest.mock('../../src/renderer/npm');
|
||||
jest.mock('../../src/renderer/file-manager');
|
||||
jest.mock('../../src/renderer/binary', () => ({
|
||||
getIsDownloaded: jest.fn(),
|
||||
getElectronBinaryPath: jest.fn()
|
||||
}));
|
||||
jest.mock('fs-extra');
|
||||
jest.mock('child_process');
|
||||
jest.mock('path');
|
||||
|
||||
describe('Runner component', () => {
|
||||
let mockChild: MockChildProcess;
|
||||
@@ -39,17 +45,14 @@ describe('Runner component', () => {
|
||||
pushOutput: jest.fn(),
|
||||
clearConsole: jest.fn(),
|
||||
pushError: jest.fn(),
|
||||
binaryManager: {
|
||||
getIsDownloaded: jest.fn(() => true),
|
||||
getElectronBinaryPath: jest.fn((version: string) => `/fake/path/${version}/electron`)
|
||||
},
|
||||
get currentElectronVersion() {
|
||||
return mockVersions['2.0.2'];
|
||||
},
|
||||
getName: async () => 'test-app-name'
|
||||
getName: async () => 'test-app-name',
|
||||
};
|
||||
|
||||
(window as any).ElectronFiddle = new ElectronFiddleMock();
|
||||
(getIsDownloaded as jest.Mock).mockReturnValue(true);
|
||||
|
||||
instance = new Runner(store as AppState);
|
||||
});
|
||||
@@ -59,7 +62,7 @@ describe('Runner component', () => {
|
||||
(spawn as any).mockReturnValueOnce(mockChild);
|
||||
|
||||
expect(await instance.run()).toBe(true);
|
||||
expect(store.binaryManager.getIsDownloaded).toHaveBeenCalled();
|
||||
expect(getIsDownloaded).toHaveBeenCalled();
|
||||
expect(window.ElectronFiddle.app.fileManager.saveToTemp).toHaveBeenCalled();
|
||||
expect(installModules).toHaveBeenCalled();
|
||||
expect(store.isRunning).toBe(true);
|
||||
@@ -75,7 +78,7 @@ describe('Runner component', () => {
|
||||
});
|
||||
|
||||
expect(await instance.run()).toBe(true);
|
||||
expect(store.binaryManager.getIsDownloaded).toHaveBeenCalled();
|
||||
expect(getIsDownloaded).toHaveBeenCalled();
|
||||
expect(window.ElectronFiddle.app.fileManager.saveToTemp).toHaveBeenCalled();
|
||||
expect(installModules).toHaveBeenCalled();
|
||||
expect(store.isRunning).toBe(true);
|
||||
@@ -161,7 +164,7 @@ describe('Runner component', () => {
|
||||
});
|
||||
|
||||
it('does not run version not yet downloaded', async () => {
|
||||
store.binaryManager.getIsDownloaded.mockReturnValueOnce(false);
|
||||
(getIsDownloaded as jest.Mock).mockReturnValueOnce(false);
|
||||
|
||||
expect(await instance.run()).toBe(false);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { ALL_MOSAICS, EditorId, ElectronVersionSource, ElectronVersionState, GenericDialogType, PanelId } from '../../src/interfaces';
|
||||
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';
|
||||
import { getContent, isContentUnchanged } from '../../src/renderer/content';
|
||||
@@ -16,7 +20,10 @@ jest.mock('../../src/renderer/content', () => ({
|
||||
getContent: jest.fn()
|
||||
}));
|
||||
jest.mock('../../src/renderer/binary', () => ({
|
||||
BinaryManager: require('../mocks/binary').MockBinaryManager
|
||||
removeBinary: jest.fn(),
|
||||
setupBinary: jest.fn(),
|
||||
getDownloadedVersions: jest.fn(),
|
||||
getDownloadingVersions: jest.fn()
|
||||
}));
|
||||
jest.mock('../../src/renderer/fetch-types', () => ({
|
||||
updateEditorTypeDefinitions: jest.fn()
|
||||
@@ -42,6 +49,7 @@ 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();
|
||||
@@ -55,8 +63,8 @@ describe('AppState', () => {
|
||||
expect(appState).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('onbeforeunload handler', () => {
|
||||
it('closes the window', (done) => {
|
||||
describe('isUnsaved autorun handler', () => {
|
||||
it('can close the window if user accepts the dialog', (done) => {
|
||||
window.close = jest.fn();
|
||||
appState.isUnsaved = true;
|
||||
expect(window.onbeforeunload).toBeTruthy();
|
||||
@@ -73,11 +81,9 @@ describe('AppState', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('closes the app', (done) => {
|
||||
const { remote } = require('electron');
|
||||
it('can close the app after user accepts dialog', (done) => {
|
||||
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);
|
||||
@@ -86,14 +92,15 @@ describe('AppState', () => {
|
||||
|
||||
appState.genericDialogLastResult = true;
|
||||
appState.isGenericDialogShowing = false;
|
||||
appState.isQuitting = true;
|
||||
process.nextTick(() => {
|
||||
expect(window.close).toHaveBeenCalledTimes(0);
|
||||
expect(remote.app.quit).toHaveBeenCalledTimes(1);
|
||||
expect(window.close).toHaveBeenCalledTimes(1);
|
||||
expect(ipcRendererManager.send).toHaveBeenCalledWith(IpcEvents.CONFIRM_QUIT, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not close the window', (done) => {
|
||||
it('takes no action if user cancels the dialog', (done) => {
|
||||
window.close = jest.fn();
|
||||
appState.isUnsaved = true;
|
||||
expect(window.onbeforeunload).toBeTruthy();
|
||||
@@ -104,11 +111,28 @@ 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();
|
||||
});
|
||||
});
|
||||
|
||||
it('sets the onDidChangeModelContent handler if saved', () => {
|
||||
appState.isUnsaved = false;
|
||||
|
||||
expect(window.onbeforeunload).toBe(null);
|
||||
|
||||
const fn = window.ElectronFiddle.editors!.renderer!
|
||||
.onDidChangeModelContent;
|
||||
const call = (fn as jest.Mock<any>).mock.calls[0];
|
||||
const cb = call[0];
|
||||
|
||||
cb();
|
||||
|
||||
expect(appState.isUnsaved).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateElectronVersions()', () => {
|
||||
@@ -235,6 +259,13 @@ 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;
|
||||
@@ -271,39 +302,39 @@ describe('AppState', () => {
|
||||
});
|
||||
|
||||
it('excludes states', () => {
|
||||
appState.statesToShow = [ ElectronVersionState.downloading ];
|
||||
appState.statesToShow = [ VersionState.downloading ];
|
||||
expect(appState.versionsToShow.length).toEqual(0);
|
||||
appState.statesToShow = [ ElectronVersionState.ready ];
|
||||
appState.statesToShow = [ VersionState.ready ];
|
||||
expect(appState.versionsToShow.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeVersion()', () => {
|
||||
it('removes a version', async () => {
|
||||
appState.versions['2.0.2'].state = ElectronVersionState.ready;
|
||||
appState.versions['2.0.2'].state = VersionState.ready;
|
||||
await appState.removeVersion('v2.0.2');
|
||||
|
||||
expect(appState.binaryManager.remove).toHaveBeenCalledWith('2.0.2');
|
||||
expect(removeBinary).toHaveBeenCalledWith('2.0.2');
|
||||
});
|
||||
|
||||
it('does not remove it if not necessary', async () => {
|
||||
appState.versions['2.0.2'].state = ElectronVersionState.unknown;
|
||||
appState.versions['2.0.2'].state = VersionState.unknown;
|
||||
await appState.removeVersion('v2.0.2');
|
||||
expect(appState.binaryManager.remove).toHaveBeenCalledTimes(0);
|
||||
expect(removeBinary).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('does not remove it if not necessary (version not existent)', async () => {
|
||||
appState.versions['2.0.2'] = undefined as any;
|
||||
await appState.removeVersion('v2.0.2');
|
||||
expect(appState.binaryManager.remove).toHaveBeenCalledTimes(0);
|
||||
expect(removeBinary).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('removes (and not deletes) a local version', async () => {
|
||||
appState.versions['/local/path'] = {
|
||||
localPath: 'local/path',
|
||||
name: 'local-foo',
|
||||
source: ElectronVersionSource.local,
|
||||
state: ElectronVersionState.ready,
|
||||
source: VersionSource.local,
|
||||
state: VersionState.ready,
|
||||
version: '4.0.0'
|
||||
};
|
||||
|
||||
@@ -311,28 +342,28 @@ describe('AppState', () => {
|
||||
|
||||
expect(saveLocalVersions).toHaveBeenCalledTimes(1);
|
||||
expect(appState.versions['/local/path']).toBeUndefined();
|
||||
expect(appState.binaryManager.remove).toHaveBeenCalledTimes(0);
|
||||
expect(removeBinary).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('downloadVersion()', () => {
|
||||
it('downloads a version', async () => {
|
||||
appState.versions['2.0.2'].state = ElectronVersionState.unknown;
|
||||
appState.versions['2.0.2'].state = VersionState.unknown;
|
||||
|
||||
await appState.downloadVersion('v2.0.2');
|
||||
expect(appState.binaryManager.setup).toHaveBeenCalledWith('2.0.2');
|
||||
expect(setupBinary).toHaveBeenCalledWith(appState, '2.0.2');
|
||||
});
|
||||
|
||||
it('downloads an unknown version', async () => {
|
||||
await appState.downloadVersion('v3.5');
|
||||
expect(appState.binaryManager.setup).toHaveBeenCalledWith('3.5');
|
||||
expect(setupBinary).toHaveBeenCalledWith(appState, '3.5');
|
||||
});
|
||||
|
||||
it('does not download a version if already ready', async () => {
|
||||
appState.versions['2.0.2'].state = ElectronVersionState.ready;
|
||||
appState.versions['2.0.2'].state = VersionState.ready;
|
||||
|
||||
await appState.downloadVersion('v2.0.2');
|
||||
expect(appState.binaryManager.setup).toHaveBeenCalledTimes(0);
|
||||
expect(setupBinary).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -411,10 +442,17 @@ describe('AppState', () => {
|
||||
describe('updateDownloadedVersionState()', () => {
|
||||
it('downloads a version if necessary', async () => {
|
||||
const mockResult = Promise.resolve(['2.0.2']);
|
||||
(appState.binaryManager.getDownloadedVersions as jest.Mock).mockReturnValueOnce(mockResult);
|
||||
(getDownloadedVersions as jest.Mock).mockReturnValueOnce(mockResult);
|
||||
await appState.updateDownloadedVersionState();
|
||||
|
||||
expect(appState.versions['2.0.2'].state).toBe(ElectronVersionState.ready);
|
||||
expect(appState.versions['2.0.2'].state).toBe(VersionState.ready);
|
||||
});
|
||||
|
||||
it('keeps downloading state intact', async () => {
|
||||
(getDownloadingVersions as jest.Mock).mockReturnValueOnce(['2.0.2']);
|
||||
await appState.updateDownloadedVersionState();
|
||||
|
||||
expect(appState.versions['2.0.2'].state).toBe(VersionState.downloading);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -485,10 +523,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();
|
||||
@@ -496,10 +538,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();
|
||||
});
|
||||
@@ -534,4 +583,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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ElectronVersionSource, ElectronVersionState } from '../../src/interfaces';
|
||||
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';
|
||||
@@ -21,7 +21,7 @@ describe('TouchBarManager', () => {
|
||||
appState.versions = mockVersions;
|
||||
appState.isRunning = false;
|
||||
|
||||
appState.versions['2.0.1'].state = ElectronVersionState.unknown;
|
||||
appState.versions['2.0.1'].state = VersionState.unknown;
|
||||
});
|
||||
|
||||
it('creates a touch bar with versions', () => {
|
||||
@@ -38,8 +38,8 @@ describe('TouchBarManager', () => {
|
||||
it('updates the versions when the versions change', () => {
|
||||
const touchBarMgr = new TouchBarManager(appState);
|
||||
appState.versions['3.3.3'] = {
|
||||
state: ElectronVersionState.downloading,
|
||||
source: ElectronVersionSource.remote,
|
||||
state: VersionState.downloading,
|
||||
source: VersionSource.remote,
|
||||
version: '3.3.3'
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { ElectronVersion, ElectronVersionSource } from '../../src/interfaces';
|
||||
import semver from 'semver';
|
||||
import { RunnableVersion, VersionSource } from '../../src/interfaces';
|
||||
import {
|
||||
addLocalVersion,
|
||||
ElectronReleaseChannel,
|
||||
@@ -14,11 +15,10 @@ import {
|
||||
VersionKeys
|
||||
} from '../../src/renderer/versions';
|
||||
import { mockFetchOnce } from '../utils';
|
||||
import semver from 'semver';
|
||||
|
||||
const { expectedVersionCount } = require('../fixtures/releases-metadata.json');
|
||||
|
||||
const mockVersions: Array<Partial<ElectronVersion>> = [
|
||||
const mockVersions: Array<Partial<RunnableVersion>> = [
|
||||
{ version: 'test-0', localPath: '/test/path/0' },
|
||||
{ version: 'test-1', localPath: '/test/path/1' },
|
||||
{ version: 'test-2', localPath: '/test/path/2' },
|
||||
@@ -114,11 +114,11 @@ describe('versions', () => {
|
||||
describe('saveLocalVersions()', () => {
|
||||
it('saves local versions', () => {
|
||||
const mockLocalVersions = mockVersions.map((v) => {
|
||||
v.source = ElectronVersionSource.local;
|
||||
v.source = VersionSource.local;
|
||||
return v;
|
||||
});
|
||||
|
||||
saveLocalVersions(mockLocalVersions as Array<ElectronVersion>);
|
||||
saveLocalVersions(mockLocalVersions as Array<RunnableVersion>);
|
||||
|
||||
const key = (window.localStorage.setItem as jest.Mock).mock.calls[0][0];
|
||||
const value = (window.localStorage.setItem as jest.Mock).mock.calls[0][1];
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ jest.spyOn(global.console, 'log').mockImplementation(() => jest.fn());
|
||||
jest.spyOn(global.console, 'warn').mockImplementation(() => jest.fn());
|
||||
jest.mock('electron', () => require('./mocks/electron'));
|
||||
jest.mock('fs-extra');
|
||||
jest.mock('electron-download');
|
||||
jest.mock('@electron/get');
|
||||
|
||||
expect.addSnapshotSerializer(createSerializer({mode: 'deep'}));
|
||||
|
||||
|
||||
@@ -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