Skip to content

Commit

Permalink
test: migrate Editors tests to RTL
Browse files Browse the repository at this point in the history
  • Loading branch information
erikian committed Aug 4, 2024
1 parent 1f38b72 commit 9449eec
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 1,010 deletions.
86 changes: 45 additions & 41 deletions rtl-spec/components/editors.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import * as React from 'react';

import { mount, shallow } from 'enzyme';
import { MosaicWindowProps } from 'react-mosaic-component';
import { MosaicNode } from 'react-mosaic-component';

import { EditorId, EditorValues, MAIN_JS } from '../../src/interfaces';
import { App } from '../../src/renderer/app';
Expand All @@ -10,26 +7,24 @@ import { Editor, EditorMosaic } from '../../src/renderer/editor-mosaic';
import { AppState } from '../../src/renderer/state';
import {
MonacoEditorMock,
MonacoMock,
StateMock,
createEditorValues,
} from '../../tests/mocks/mocks';
import { emitEvent } from '../../tests/utils';
import { renderClassComponentWithInstanceRef } from '../test-utils/renderClassComponentWithInstanceRef';

jest.mock('../../src/renderer/components/editor', () => ({
Editor: () => 'Editor',
}));

describe('Editors component', () => {
let app: App;
let monaco: MonacoMock;
let store: AppState;
let editorMosaic: EditorMosaic;
let editorValues: EditorValues;

beforeEach(() => {
({ app } = window);
monaco = window.monaco as unknown as MonacoMock;
({ state: store } = window.app);
editorValues = createEditorValues();
editorMosaic = new EditorMosaic();
Expand All @@ -38,15 +33,20 @@ describe('Editors component', () => {
(store as unknown as StateMock).editorMosaic = editorMosaic;
});

function renderEditors() {
return renderClassComponentWithInstanceRef(Editors, {
appState: store,
});
}

it('renders', () => {
const wrapper = mount(<Editors appState={store} />);
wrapper.setState({ monaco });
expect(wrapper).toMatchSnapshot();
const { renderResult } = renderEditors();

expect(renderResult.getByTestId('editors')).toBeInTheDocument();
});

it('does not execute command if not supported', () => {
const wrapper = shallow(<Editors appState={store} />);
const instance: any = wrapper.instance();
const { instance } = renderEditors();

const editor = new MonacoEditorMock();
const action = editor.getAction();
Expand All @@ -70,15 +70,13 @@ describe('Editors component', () => {
throw new Error('Bwap bwap');
});

const wrapper = shallow(<Editors appState={store} />);
const instance: any = wrapper.instance();
const { instance } = renderEditors();

expect(instance.toggleEditorOption('wordWrap')).toBe(false);
});

it('updates a setting', () => {
const wrapper = shallow(<Editors appState={store} />);
const instance: any = wrapper.instance();
const { instance } = renderEditors();

const editor = new MonacoEditorMock();
editorMosaic.addEditor(filename, editor as unknown as Editor);
Expand All @@ -90,29 +88,35 @@ describe('Editors component', () => {
});
});

it('renders a toolbar', () => {
const wrapper = shallow(<Editors appState={store} />);
const instance: any = wrapper.instance();
const toolbar = instance.renderToolbar(
{ title: MAIN_JS } as MosaicWindowProps<EditorId>,
MAIN_JS,
);

expect(toolbar).toMatchSnapshot();
it('renders toolbars', () => {
const { renderResult } = renderEditors();

const [
mainToolbar,
rendererToolbar,
htmlToolbar,
preloadToolbar,
stylesheetToolbar,
] = renderResult.getAllByTestId('editors-toolbar');

expect(mainToolbar).toHaveTextContent('Main Process (main.js)');
expect(rendererToolbar).toHaveTextContent('Renderer Process (renderer.js)');
expect(htmlToolbar).toHaveTextContent('HTML (index.html)');
expect(preloadToolbar).toHaveTextContent('Preload (preload.js)');
expect(stylesheetToolbar).toHaveTextContent('Stylesheet (styles.css)');
});

it('onChange() updates the mosaic arrangement in the appState', () => {
const wrapper = shallow(<Editors appState={store} />);
const instance: any = wrapper.instance();
const { instance } = renderEditors();

const arrangement = { testArrangement: true };
instance.onChange(arrangement as any);
const arrangement: MosaicNode<EditorId> = 'testArrangement.js';
instance.onChange(arrangement);
expect(editorMosaic.mosaic).toStrictEqual(arrangement);
});

describe('events', () => {
it('handles a "execute-monaco-command" event', () => {
shallow(<Editors appState={store} />);
renderEditors();

const editor = new MonacoEditorMock();
const action = editor.getAction();
Expand All @@ -129,7 +133,7 @@ describe('Editors component', () => {
const fakeValues = { [MAIN_JS]: 'hi' } as const;

it('handles a "new-fiddle" event', async () => {
shallow(<Editors appState={store} />);
renderEditors();

let resolve: (value?: unknown) => void;
const replacePromise = new Promise((r) => {
Expand Down Expand Up @@ -162,7 +166,7 @@ describe('Editors component', () => {

describe('"select-all-in-editor" handler', () => {
it('selects all in the focused editor', async () => {
shallow(<Editors appState={store} />);
renderEditors();

const range = 'range';
const editor = new MonacoEditorMock();
Expand All @@ -177,7 +181,7 @@ describe('Editors component', () => {
});

it('does not change selection if the selected editor has no model', async () => {
shallow(<Editors appState={store} />);
renderEditors();

const editor = new MonacoEditorMock();
delete (editor as any).model;
Expand All @@ -191,14 +195,14 @@ describe('Editors component', () => {
});

it('does not crash if there is no selected editor', () => {
shallow(<Editors appState={store} />);
renderEditors();
editorMosaic.focusedEditor = jest.fn().mockReturnValue(null);
emitEvent('select-all-in-editor');
});
});

it('handles a "new-test" event', async () => {
shallow(<Editors appState={store} />);
renderEditors();

// setup
const getTestTemplateSpy = jest
Expand Down Expand Up @@ -229,7 +233,7 @@ describe('Editors component', () => {
});

it('handles a "select-all-in-editor" event', async () => {
shallow(<Editors appState={store} />);
renderEditors();

const range = 'range';
const editor = new MonacoEditorMock();
Expand All @@ -247,16 +251,16 @@ describe('Editors component', () => {
const editor = new MonacoEditorMock();
editorMosaic.addEditor(id, editor as unknown as Editor);

shallow(<Editors appState={store} />);
renderEditors();
emitEvent('toggle-monaco-option', 'wordWrap');
expect(editor.updateOptions).toHaveBeenCalled();
});
});

describe('setFocused()', () => {
it('sets the "focused" property', () => {
const wrapper = shallow(<Editors appState={store} />);
const instance: any = wrapper.instance();
const { instance } = renderEditors();

const spy = jest.spyOn(instance, 'setState');

const id = MAIN_JS;
Expand All @@ -265,8 +269,8 @@ describe('Editors component', () => {
});

it('focus sidebar file', () => {
const wrapper = shallow(<Editors appState={store} />);
const instance: any = wrapper.instance();
const { instance } = renderEditors();

const spy = jest.spyOn(instance, 'setState');

const id = MAIN_JS;
Expand Down
18 changes: 18 additions & 0 deletions src/renderer/components/TestIdContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { PropsWithChildren } from 'react';

type TestIdContainerProps = PropsWithChildren<{
testId: string;
}>;

/**
* A wrapper for third-party components that don't allow us to pass arbitrary
* DOM attributes like `data-testid`. It uses `display: contents` in the
* wrapping `div` so it has no CSS side effects.
*/
export function TestIdContainer({ testId, children }: TestIdContainerProps) {
return (
<div data-testid={testId} style={{ display: 'contents' }}>
{children}
</div>
);
}
19 changes: 11 additions & 8 deletions src/renderer/components/editors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { Editor } from './editor';
import { renderNonIdealState } from './editors-non-ideal-state';
import { MaximizeButton, RemoveButton } from './editors-toolbar-button';
import { TestIdContainer } from './TestIdContainer';
import { EditorId, SetFiddleOptions } from '../../interfaces';
import { AppState } from '../state';
import { getEditorTitle } from '../utils/editor-utils';
Expand Down Expand Up @@ -191,7 +192,7 @@ export const Editors = observer(
const { appState } = this.props;

return (
<div>
<div data-testid="editors-toolbar">
{/* Left */}
<div>
<h5>{title}</h5>
Expand Down Expand Up @@ -258,13 +259,15 @@ export const Editors = observer(
const { editorMosaic } = this.props.appState;

return (
<Mosaic<EditorId>
className={`focused__${this.state.focused}`}
onChange={this.onChange}
value={editorMosaic.mosaic}
zeroStateView={renderNonIdealState(editorMosaic)}
renderTile={this.renderTile}
/>
<TestIdContainer testId="editors">
<Mosaic<EditorId>
className={`focused__${this.state.focused}`}
onChange={this.onChange}
value={editorMosaic.mosaic}
zeroStateView={renderNonIdealState(editorMosaic)}
renderTile={this.renderTile}
/>
</TestIdContainer>
);
}

Expand Down
8 changes: 3 additions & 5 deletions src/renderer/components/tour-welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from 'react';
import { Button, Classes, Dialog } from '@blueprintjs/core';
import { observer } from 'mobx-react';

import { TestIdContainer } from './TestIdContainer';
import { Tour, TourScriptStep, TourStepGetButtonParams } from './tour';
import { AppState } from '../state';

Expand Down Expand Up @@ -238,10 +239,7 @@ export const WelcomeTour = observer(
if (!isTourStarted) {
return (
<Dialog key="welcome-tour-dialog" isOpen={true}>
<div
data-testid="welcome-tour-dialog"
style={{ display: 'contents' }}
>
<TestIdContainer testId="welcome-tour-dialog">
<div className={Classes.DIALOG_HEADER}>
<h4 className={Classes.HEADING}>🙋‍ Hey There!</h4>
</div>
Expand All @@ -260,7 +258,7 @@ export const WelcomeTour = observer(
{this.buttons}
</div>
</div>
</div>
</TestIdContainer>
</Dialog>
);
} else {
Expand Down
Loading

0 comments on commit 9449eec

Please sign in to comment.