import { mount, shallow } from 'enzyme'; import * as React from 'react'; import * as content from '../../../src/renderer/content'; import { DefaultEditorId, EditorValues, MAIN_JS, } from '../../../src/interfaces'; import { Editors } from '../../../src/renderer/components/editors'; import { IpcEvents } from '../../../src/ipc-events'; import { EditorMosaic } from '../../../src/renderer/editor-mosaic'; import { ipcRendererManager } from '../../../src/renderer/ipc'; import { AppMock, MonacoEditorMock, StateMock, createEditorValues, } from '../../mocks/mocks'; jest.mock('monaco-loader', () => jest.fn(async () => { return { monaco: true }; }), ); jest.mock('../../../src/renderer/components/editor', () => ({ Editor: () => 'Editor', })); describe('Editors component', () => { let app: AppMock; let monaco: any; let store: StateMock; let editorMosaic: EditorMosaic; let editorValues: EditorValues; beforeEach(() => { ({ app, monaco } = (window as any).ElectronFiddle); ({ state: store } = app); editorValues = createEditorValues(); editorMosaic = new EditorMosaic(); editorMosaic.set(editorValues); store.editorMosaic = editorMosaic as any; }); it('renders', () => { const wrapper = mount(); wrapper.setState({ monaco }); expect(wrapper).toMatchSnapshot(); }); it('does not execute command if not supported', () => { const wrapper = shallow(); const instance: Editors = wrapper.instance() as any; const editor = new MonacoEditorMock(); const action = editor.getAction(); action.isSupported.mockReturnValue(false); editorMosaic.focusedEditor = jest.fn().mockReturnValue(editor); instance.executeCommand('hello'); expect(editor.getAction).toHaveBeenCalled(); expect(action.isSupported).toHaveBeenCalled(); expect(action.run).toHaveBeenCalledTimes(0); }); describe('toggleEditorOption()', () => { const filename = DefaultEditorId.html; it('handles an error', () => { const editor = new MonacoEditorMock(); editorMosaic.addEditor(filename, editor as any); editor.updateOptions.mockImplementationOnce(() => { throw new Error('Bwap bwap'); }); const wrapper = shallow(); const instance: Editors = wrapper.instance() as any; expect(instance.toggleEditorOption('wordWrap')).toBe(false); }); it('updates a setting', () => { const wrapper = shallow(); const instance: Editors = wrapper.instance() as any; const editor = new MonacoEditorMock(); editorMosaic.addEditor(filename, editor as any); expect(instance.toggleEditorOption('wordWrap')).toBe(true); expect(editor.updateOptions).toHaveBeenCalledWith({ minimap: { enabled: false }, wordWrap: 'off', }); }); }); it('renders a toolbar', () => { const wrapper = shallow(); const instance: Editors = wrapper.instance() as any; const toolbar = instance.renderToolbar( { title: DefaultEditorId.main } as any, DefaultEditorId.main, ); expect(toolbar).toMatchSnapshot(); }); it('does not render toolbar controls if only one editor exists', () => { const id = MAIN_JS; editorMosaic.set({ [id]: '// content' }); const wrapper = shallow(); const instance: Editors = wrapper.instance() as any; const toolbar = instance.renderToolbar({ title: id } as any, id); expect(toolbar).toMatchSnapshot(); }); it('onChange() updates the mosaic arrangement in the appState', () => { const wrapper = shallow(); const instance: Editors = wrapper.instance() as any; const arrangement = { testArrangement: true }; instance.onChange(arrangement as any); expect(editorMosaic.mosaic).toStrictEqual(arrangement); }); describe('IPC commands', () => { it('handles a MONACO_EXECUTE_COMMAND command', () => { shallow(); const editor = new MonacoEditorMock(); const action = editor.getAction(); action.isSupported.mockReturnValue(true); editorMosaic.focusedEditor = jest.fn().mockReturnValue(editor); ipcRendererManager.emit(IpcEvents.MONACO_EXECUTE_COMMAND, null, 'hello'); expect(editor.getAction).toHaveBeenCalled(); expect(action.isSupported).toHaveBeenCalled(); expect(action.run).toHaveBeenCalled(); }); const fakeValues = { [DefaultEditorId.css]: '', [DefaultEditorId.html]: '', [DefaultEditorId.main]: 'hi', [DefaultEditorId.preload]: '', [DefaultEditorId.renderer]: '', } as const; it('handles an FS_NEW_FIDDLE command', async () => { let resolve: any; const replacePromise = new Promise((r) => { resolve = r; }); // setup const getTemplateSpy = jest .spyOn(content, 'getTemplate') .mockImplementation(() => Promise.resolve(fakeValues)); const replaceFiddleSpy = jest .spyOn(app, 'replaceFiddle') .mockImplementation(() => resolve()); // invoke the call ipcRendererManager.emit(IpcEvents.FS_NEW_FIDDLE, null); await replacePromise; // check the results expect(getTemplateSpy).toHaveBeenCalledTimes(1); expect(replaceFiddleSpy).toHaveBeenCalledTimes(1); // cleanup getTemplateSpy.mockRestore(); replaceFiddleSpy.mockRestore(); }); describe('SELECT_ALL_IN_EDITOR handler', () => { it('selects all in the focused editor', async () => { shallow(); const range = 'range'; const editor = new MonacoEditorMock(); const model = editor.getModel(); model.getFullModelRange.mockReturnValue(range); editorMosaic.focusedEditor = jest.fn().mockReturnValue(editor); ipcRendererManager.emit(IpcEvents.SELECT_ALL_IN_EDITOR, null); await process.nextTick; expect(editor.setSelection).toHaveBeenCalledWith('range'); }); it('does not change selection if the selected editor has no model', async () => { shallow(); const editor = new MonacoEditorMock(); delete (editor as any).model; editorMosaic.focusedEditor = jest.fn().mockReturnValue(editor); ipcRendererManager.emit(IpcEvents.SELECT_ALL_IN_EDITOR, null); await process.nextTick; expect(editor.getModel).toHaveBeenCalledTimes(1); expect(editor.setSelection).not.toHaveBeenCalled(); }); it('does not crash if there is no selected editor', () => { shallow(); editorMosaic.focusedEditor = jest.fn().mockReturnValue(null); ipcRendererManager.emit(IpcEvents.SELECT_ALL_IN_EDITOR, null); }); }); it('handles an FS_NEW_TEST command', async () => { // setup const getTestTemplateSpy = jest .spyOn(content, 'getTestTemplate') .mockImplementation(() => Promise.resolve(fakeValues)); let replaceResolve: any; const replacePromise = new Promise((r) => { replaceResolve = r; }); const replaceFiddleSpy = jest .spyOn(app, 'replaceFiddle') .mockImplementation(() => replaceResolve()); // invoke the call ipcRendererManager.emit(IpcEvents.FS_NEW_TEST); await replacePromise; // check the results expect(getTestTemplateSpy).toHaveBeenCalled(); expect(replaceFiddleSpy).toHaveBeenCalled(); // cleanup getTestTemplateSpy.mockRestore(); replaceFiddleSpy.mockRestore(); }); it('handles a SELECT_ALL_IN_EDITOR command', async () => { shallow(); const range = 'range'; const editor = new MonacoEditorMock(); editor.getModel().getFullModelRange.mockReturnValue(range); editorMosaic.focusedEditor = jest.fn().mockReturnValue(editor); ipcRendererManager.emit(IpcEvents.SELECT_ALL_IN_EDITOR, null); await process.nextTick; expect(editor.setSelection).toHaveBeenCalledWith(range); }); it('handles the monaco editor option commands', () => { const id = MAIN_JS; const editor = new MonacoEditorMock(); editorMosaic.addEditor(id, editor as any); shallow(); ipcRendererManager.emit(IpcEvents.MONACO_TOGGLE_OPTION, null, 'wordWrap'); expect(editor.updateOptions).toHaveBeenCalled(); }); }); describe('setFocused()', () => { it('sets the "focused" property', () => { const wrapper = shallow(); const instance: Editors = wrapper.instance() as any; const spy = jest.spyOn(instance, 'setState'); const id = DefaultEditorId.html; instance.setFocused(id); expect(spy).toHaveBeenCalledWith({ focused: id }); }); }); });