diff --git a/src/renderer/binary.ts b/src/renderer/binary.ts index 60a6c376f6..eb2d74c077 100644 --- a/src/renderer/binary.ts +++ b/src/renderer/binary.ts @@ -79,8 +79,15 @@ export class BinaryManager { const zipPath = await eDownload({ version }); const extractPath = this.getDownloadPath(version); console.log(`BinaryManager: Electron ${version} downloaded, now unpacking`); - const electronFiles = await this.unzip(zipPath, extractPath); - console.log(electronFiles); + + try { + const electronFiles = await this.unzip(zipPath, extractPath); + console.log(`Unzipped ${version}`, electronFiles); + } catch (error) { + console.warn(`Failure while unzipping ${version}`, error); + + // Todo: Handle this case + } this.state[version] = 'ready'; } diff --git a/src/renderer/components/add-version-dialog.tsx b/src/renderer/components/add-version-dialog.tsx index eb148eb328..6ff157187d 100644 --- a/src/renderer/components/add-version-dialog.tsx +++ b/src/renderer/components/add-version-dialog.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import * as semver from 'semver'; import { GitHubVersion } from '../../interfaces'; -import { getElectroNameForPlatform } from '../../utils/electron-name'; +import { getElectronNameForPlatform } from '../../utils/electron-name'; import { AppState } from '../state'; import { Dialog } from './dialog'; @@ -165,7 +165,7 @@ export class AddVersionDialog extends React.Component - Select the folder containing {getElectroNameForPlatform()} + Select the folder containing {getElectronNameForPlatform()} diff --git a/src/renderer/components/publish-button.tsx b/src/renderer/components/publish-button.tsx index 577f277bd9..e4faf1c6fe 100644 --- a/src/renderer/components/publish-button.tsx +++ b/src/renderer/components/publish-button.tsx @@ -82,6 +82,7 @@ export class PublishButton extends React.Component { export function addLocalVersion(input: GitHubVersion): Array { const versions = getLocalVersions(); - if (!versions.find((v) => v.url !== input.url)) { + if (!versions.find((v) => v.url === input.url)) { versions.push(input); } diff --git a/src/utils/electron-name.ts b/src/utils/electron-name.ts index 8928655e22..29b3bb4b12 100644 --- a/src/utils/electron-name.ts +++ b/src/utils/electron-name.ts @@ -3,7 +3,7 @@ * * @returns {string} */ -export function getElectroNameForPlatform(): string { +export function getElectronNameForPlatform(): string { if (process.platform === 'win32') { return 'electron.exe'; } else if (process.platform === 'darwin') { diff --git a/tests/renderer/binary-spec.ts b/tests/renderer/binary-spec.ts index 52da7ad8a4..26b11ead78 100644 --- a/tests/renderer/binary-spec.ts +++ b/tests/renderer/binary-spec.ts @@ -152,5 +152,15 @@ describe('binary', () => { expect(require('electron-download')).toHaveBeenCalledTimes(0); expect(binaryManager.state['3.0.0']).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 mockZip = require('extract-zip'); + mockZip.mockImplementationOnce((_a: any, _b: any, c: any) => c(new Error('bwap-bwap'))); + + await binaryManager.setup('v3.0.0'); + }); }); }); diff --git a/tests/renderer/components/__snapshots__/add-version-dialog-spec.tsx.snap b/tests/renderer/components/__snapshots__/add-version-dialog-spec.tsx.snap new file mode 100644 index 0000000000..9ab7b373f8 --- /dev/null +++ b/tests/renderer/components/__snapshots__/add-version-dialog-spec.tsx.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AddVersionDialog component renders 1`] = ` + + Add + , + , + ] + } + className="add-version-dialog" + isCentered={true} + isShowing={true} + isShowingBackdrop={true} + key="add-version-dialog" + onClose={[Function]} +> + + + +`; diff --git a/tests/renderer/components/__snapshots__/settings-spec.tsx.snap b/tests/renderer/components/__snapshots__/settings-spec.tsx.snap index 7019f4b78f..db8f4df6e6 100644 --- a/tests/renderer/components/__snapshots__/settings-spec.tsx.snap +++ b/tests/renderer/components/__snapshots__/settings-spec.tsx.snap @@ -72,6 +72,401 @@ exports[`CreditsSettings component renders only the menu if page unknown 1`] = ` `; +exports[`CreditsSettings component renders the C page after a click 1`] = ` +
+
+
    +
  • + General +
  • +
  • + Electron +
  • +
  • + Credits +
  • +
+
+
+
+ +
+ +
+
+`; + +exports[`CreditsSettings component renders the Cred page after a click 1`] = ` +
+
+
    +
  • + General +
  • +
  • + Electron +
  • +
  • + Credits +
  • +
+
+
+
+ +
+ +
+
+`; + +exports[`CreditsSettings component renders the Credit page after a click 1`] = ` +
+
+
    +
  • + General +
  • +
  • + Electron +
  • +
  • + Credits +
  • +
+
+
+
+ +
+ +
+
+`; + +exports[`CreditsSettings component renders the Credits page after a click 1`] = ` +
+
+
    +
  • + General +
  • +
  • + Electron +
  • +
  • + Credits +
  • +
+
+
+
+ +
+ +
+
+`; + +exports[`CreditsSettings component renders the Electron page after a click 1`] = ` +
+
+
    +
  • + General +
  • +
  • + Electron +
  • +
  • + Credits +
  • +
+
+
+
+ +
+ +
+
+`; + exports[`CreditsSettings component renders the Electron page by default 1`] = `
{ + let store: any; + + const mockFile = { + path: '/test/file' + }; + + beforeAll(() => { + // We render the buttons different depending on the + // platform, so let' have a uniform platform for unit tests + overridePlatform('darwin'); + }); + + afterAll(() => { + resetPlatform(); + }); + + beforeEach(() => { + store = { + isAddVersionDialogShowing: true, + addLocalVersion: jest.fn(), + binaryManager: { + getIsDownloaded: jest.fn() + } + }; + }); + + it('renders', () => { + const wrapper = shallow(); + + wrapper.setState({ isValidVersion: true, isValidElectron: true }); + + expect(wrapper).toMatchSnapshot(); + }); + + describe('onChangeFile()', () => { + it('handles the change event', async () => { + const wrapper = shallow(); + + await (wrapper.instance() as any).onChangeFile({ target: { files: [ mockFile ] } }); + expect(wrapper.state('file')).toBe(mockFile); + }); + + it('handles invalid input', async () => { + const wrapper = shallow(); + + await (wrapper.instance() as any).onChangeFile({ target: { files: [] } }); + expect(wrapper.state('file')).toBe(undefined); + }); + + it('handles the change event and checks for Electron', async () => { + const wrapper = shallow(); + + store.binaryManager.getIsDownloaded.mockReturnValueOnce(false); + + await (wrapper.instance() as any).onChangeFile({ target: { files: [ mockFile ] } }); + expect(wrapper.state('file')).toBe(mockFile); + expect(wrapper.state('isValidElectron')).toBe(false); + }); + }); + + describe('onChangeVersion()', () => { + it('handles valid input', () => { + const wrapper = shallow(); + + (wrapper.instance() as any).onChangeVersion({ target: { value: '3.3.3' } }); + expect(wrapper.state('isValidVersion')).toBe(true); + expect(wrapper.state('version')).toBe('3.3.3'); + }); + + it('handles invalid input', () => { + const wrapper = shallow(); + + (wrapper.instance() as any).onChangeVersion({ target: { value: 'foo' } }); + expect(wrapper.state('isValidVersion')).toBe(false); + expect(wrapper.state('version')).toBe('foo'); + + (wrapper.instance() as any).onChangeVersion({ target: { } }); + expect(wrapper.state('isValidVersion')).toBe(false); + expect(wrapper.state('version')).toBe(''); + }); + }); + + describe('onSubmit', () => { + it('does not do anything without a file', async () => { + const wrapper = shallow(); + + await (wrapper.instance() as any).onSubmit(); + + expect(store.addLocalVersion).toHaveBeenCalledTimes(0); + }); + + it('adds a local version using the given data', async () => { + const wrapper = shallow(); + + wrapper.setState({ + version: '3.3.3', + file: { + path: '/test/path' + } + }); + + await (wrapper.instance() as any).onSubmit(); + + expect(store.addLocalVersion).toHaveBeenCalledTimes(1); + + const result = store.addLocalVersion.mock.calls[0][0]; + + expect(result.url).toBe('/test/path'); + expect(result.assets_url).toBe('/test/path'); + expect(result.tag_name).toBe('3.3.3'); + }); + }); +}); diff --git a/tests/renderer/components/dialogs-spec.tsx b/tests/renderer/components/dialogs-spec.tsx index 84f02c7bdf..11333439ab 100644 --- a/tests/renderer/components/dialogs-spec.tsx +++ b/tests/renderer/components/dialogs-spec.tsx @@ -16,7 +16,8 @@ describe('Dialogs component', () => { beforeEach(() => { store = { isTokenDialogShowing: false, - isSettingsShowing: false + isSettingsShowing: false, + isAddVersionDialogShowing: false, }; }); @@ -41,4 +42,10 @@ describe('Dialogs component', () => { const wrapper = shallow(); expect(wrapper.text()).toBe(''); }); + + it('renders the settings dialog', () => { + store.isAddVersionDialogShowing = true; + const wrapper = shallow(); + expect(wrapper.text()).toBe(''); + }); }); diff --git a/tests/renderer/components/publish-button-spec.tsx b/tests/renderer/components/publish-button-spec.tsx index 183d72e28a..d440396a08 100644 --- a/tests/renderer/components/publish-button-spec.tsx +++ b/tests/renderer/components/publish-button-spec.tsx @@ -64,6 +64,35 @@ describe('Publish button component', () => { }); }); + it('handles missing content', async () => { + const mockOctokit = { + authenticate: jest.fn(), + gists: { + create: jest.fn(async () => ({ data: { id: '123' } })) + } + }; + + (getOctokit as any).mockReturnValue(mockOctokit); + + const wrapper = shallow(); + const instance: PublishButton = wrapper.instance() as any; + + (window as any).ElectronFiddle.app.getValues.mockReturnValueOnce({}); + + await instance.publishFiddle(); + + expect(mockOctokit.authenticate).toHaveBeenCalled(); + expect(mockOctokit.gists.create).toHaveBeenCalledWith({ + description: 'Electron Fiddle Gist', + files: { + 'index.html': { content: '' }, + 'renderer.js': { content: '// Empty' }, + 'main.js': { content: '// Empty' }, + }, + public: true + }); + }); + it('handles an error in Gist publishing', async () => { const mockOctokit = { authenticate: jest.fn(), diff --git a/tests/renderer/components/settings-electron-spec.tsx b/tests/renderer/components/settings-electron-spec.tsx index 8d55a3b383..59a0956316 100644 --- a/tests/renderer/components/settings-electron-spec.tsx +++ b/tests/renderer/components/settings-electron-spec.tsx @@ -16,7 +16,8 @@ describe('ElectronSettings component', () => { versionsToShow: [ ElectronReleaseChannel.stable, ElectronReleaseChannel.beta ], downloadVersion: jest.fn(), removeVersion: jest.fn(), - updateElectronVersions: jest.fn() + updateElectronVersions: jest.fn(), + toggleAddVersionDialog: jest.fn() }; // Render all the states @@ -64,6 +65,18 @@ describe('ElectronSettings component', () => { }); }); + describe('handleAddVersion()', () => { + it('toggles the add version dialog', () => { + const wrapper = shallow( + + ); + const instance = wrapper.instance() as any; + instance.handleAddVersion(); + + expect(store.toggleAddVersionDialog).toHaveBeenCalled(); + }); + }); + describe('handlePagesChange()', () => { it('handles a new selection', async () => { const wrapper = shallow( diff --git a/tests/renderer/components/settings-spec.tsx b/tests/renderer/components/settings-spec.tsx index 6e62552870..83279fa128 100644 --- a/tests/renderer/components/settings-spec.tsx +++ b/tests/renderer/components/settings-spec.tsx @@ -60,7 +60,16 @@ describe('CreditsSettings component', () => { expect(wrapper).toMatchSnapshot(); }); - it('renders the General page after a click', () => { + it('renders the Electron page after a click', () => { + const wrapper = shallow( + + ); + + wrapper.find('.Electron').simulate('click'); + expect(wrapper).toMatchSnapshot(); + }); + + it('renders the Credits page after a click', () => { const wrapper = shallow( ); diff --git a/tests/renderer/fetch-types-spec.ts b/tests/renderer/fetch-types-spec.ts index e393d04e42..12a969b46d 100644 --- a/tests/renderer/fetch-types-spec.ts +++ b/tests/renderer/fetch-types-spec.ts @@ -193,5 +193,19 @@ describe('fetch-types', () => { await updateEditorTypeDefinitions('3.0.0', 11); }); + + it('handles definitions not existing', async () => { + (fetch as any).mockResponse(null); + + let errored = false; + + try { + await updateEditorTypeDefinitions('3.0.0', 11); + } catch (error) { + errored = true; + } + + expect(errored).toBe(false); + }); }); }); diff --git a/tests/renderer/state-spec.ts b/tests/renderer/state-spec.ts index cb26105bb7..5e32c43bf4 100644 --- a/tests/renderer/state-spec.ts +++ b/tests/renderer/state-spec.ts @@ -124,6 +124,12 @@ describe('AppState', () => { }); describe('setVersion()', () => { + it('falls back if a version does not exist', async () => { + await appState.setVersion('v999.99.99'); + + expect(appState.version).toBe('2.0.2'); + }); + it('downloads a version if necessary', async () => { appState.downloadVersion = jest.fn(); await appState.setVersion('v2.0.2'); diff --git a/tests/renderer/versions-spec.ts b/tests/renderer/versions-spec.ts index 2aed05b94d..50e91993f0 100644 --- a/tests/renderer/versions-spec.ts +++ b/tests/renderer/versions-spec.ts @@ -1,11 +1,22 @@ +import { ElectronVersion, ElectronVersionSource } from '../../src/interfaces'; import { + addLocalVersion, ElectronReleaseChannel, fetchVersions, getKnownVersions, + getLocalVersions, getReleaseChannel, - getUpdatedElectronVersions + getUpdatedElectronVersions, + saveLocalVersions, + VersionKeys } from '../../src/renderer/versions'; +const mockVersions: Array> = [ + { tag_name: 'test-0', url: '/test/path/0' }, + { tag_name: 'test-1', url: '/test/path/1' }, + { tag_name: 'test-2', url: '/test/path/2' }, +]; + describe('versions', () => { describe('getReleaseChannel()', () => { it('identifies a nightly release', () => { @@ -31,6 +42,49 @@ describe('versions', () => { tag_name: 'v3.0.0' } as any)).toBe(ElectronReleaseChannel.stable); }); + + it('identifies an unknown release as stable', () => { + expect(getReleaseChannel({} as any)).toBe(ElectronReleaseChannel.stable); + }); + }); + + describe('addLocalVersion()', () => { + beforeEach(() => { + (window.localStorage.getItem as jest.Mock).mockReturnValue( + JSON.stringify([mockVersions[0]]) + ); + }); + + it('adds a local version', () => { + expect(addLocalVersion(mockVersions[1] as any)).toEqual([ mockVersions[0], mockVersions[1] ]); + }); + + it('does not add duplicates', () => { + expect(addLocalVersion(mockVersions[0] as any)).toEqual([ mockVersions[0] ]); + }); + }); + + describe('saveLocalVersions()', () => { + it('saves local versions', () => { + const mockLocalVersions = mockVersions.map((v) => { + v.source = ElectronVersionSource.local; + return v; + }); + + saveLocalVersions(mockLocalVersions as Array); + + const key = (window.localStorage.setItem as jest.Mock).mock.calls[0][0]; + const value = (window.localStorage.setItem as jest.Mock).mock.calls[0][1]; + + expect(key).toBe(VersionKeys.local); + expect(value).toBe(JSON.stringify(mockLocalVersions)); + }); + }); + + describe('getLocalVersions', () => { + it('returns an empty array if none can be found', () => { + expect(getLocalVersions()).toEqual([]); + }); }); describe('fetchVersions()', () => { diff --git a/tests/utils/electron-name-spec.ts b/tests/utils/electron-name-spec.ts new file mode 100644 index 0000000000..5cf5f1e64e --- /dev/null +++ b/tests/utils/electron-name-spec.ts @@ -0,0 +1,19 @@ +import { getElectronNameForPlatform } from '../../src/utils/electron-name'; +import { overridePlatform, resetPlatform } from '../utils'; + +describe('electron-name', () => { + afterAll(() => { + resetPlatform(); + }); + + it('returns the right name for each platform', () => { + [ + { platform: 'win32', expected: 'electron.exe' }, + { platform: 'darwin', expected: 'Electron.app' }, + { platform: 'linux', expected: 'electron' } + ].forEach(({ platform, expected }) => { + overridePlatform(platform); + expect(getElectronNameForPlatform()).toBe(expected); + }); + }); +}); diff --git a/tests/utils/sorted-electron-map-spec.ts b/tests/utils/sorted-electron-map-spec.ts index 63c2e84e72..d0ac70bbd4 100644 --- a/tests/utils/sorted-electron-map-spec.ts +++ b/tests/utils/sorted-electron-map-spec.ts @@ -19,4 +19,27 @@ describe('sorted-eletron-map', () => { expect(result[1]).toEqual({ tag_name: 'v2.0.0' }); expect(result[2]).toEqual({ tag_name: 'v1.0.0' }); }); + + it('handles invalid versions', () => { + const map: any = { + '1.0.0': { + tag_name: 'v1.0.0' + }, + '3.0.0': { + tag_name: 'v3.0.0' + }, + garbage: { + tag_name: 'garbage' + }, + trash: { + tag_name: 'trash' + } + }; + const result = sortedElectronMap(map, (_k, e) => e); + + expect(result[0]).toEqual({ tag_name: 'garbage' }); + expect(result[1]).toEqual({ tag_name: 'trash' }); + expect(result[2]).toEqual({ tag_name: 'v3.0.0' }); + expect(result[3]).toEqual({ tag_name: 'v1.0.0' }); + }); }); diff --git a/tsconfig.json b/tsconfig.json index 79421d8325..691939b8b7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,14 +21,13 @@ "noEmitHelpers": true, "module": "commonjs", "moduleResolution": "node", - "sourceMap": true, "pretty": true, "target": "es2017", "jsx": "react", "typeRoots": [ "./node_modules/@types" ], - "baseUrl": ".", + "baseUrl": "." }, "include": [ "src/**/*"