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 8c7d666 commit 5a02bbf
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 1,071 deletions.
15 changes: 10 additions & 5 deletions rtl-spec/components/editors-non-ideal-state.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { renderNonIdealState } from '../../src/renderer/components/editors-non-ideal-state';
import { EditorMosaic } from '../../src/renderer/editor-mosaic';
Expand All @@ -11,13 +12,17 @@ describe('renderNonIdealState()', () => {
});

it('renders a non-ideal state', () => {
expect(renderNonIdealState({} as EditorMosaic)).toMatchSnapshot();
const renderResult = render(renderNonIdealState({} as EditorMosaic));

expect(
renderResult.getByTestId('editors-non-ideal-state'),
).toBeInTheDocument();
});

it('handles a click', () => {
it('handles a click', async () => {
const resetLayoutSpy = jest.spyOn(editorMosaic, 'resetLayout');
const wrapper = mount(renderNonIdealState(editorMosaic));
wrapper.find('button').simulate('click');
const renderResult = render(renderNonIdealState(editorMosaic));
await userEvent.click(renderResult.getByRole('button'));
expect(resetLayoutSpy).toHaveBeenCalledTimes(1);
});
});
51 changes: 29 additions & 22 deletions rtl-spec/components/editors-toolbar-button.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import * as React from 'react';

import { shallow } from 'enzyme';
import userEvent from '@testing-library/user-event';
import { MosaicContext, MosaicWindowContext } from 'react-mosaic-component';

import { EditorId, MAIN_JS } from '../../src/interfaces';
import {
MaximizeButton,
RemoveButton,
} from '../../src/renderer/components/editors-toolbar-button';
import { AppState } from '../../src/renderer/state';
import { renderClassComponentWithInstanceRef } from '../test-utils/renderClassComponentWithInstanceRef';

let mockContext: any = {};
let mockContext = {} as MosaicWindowContext & MosaicContext<string>;

jest.mock('react-mosaic-component', () => {
const { MosaicContext, MosaicRootActions, MosaicWindowContext } =
Expand Down Expand Up @@ -52,44 +52,51 @@ describe('Editor toolbar button component', () => {

describe('MaximizeButton', () => {
function createMaximizeButton(id: EditorId) {
const wrapper = shallow(<MaximizeButton id={id} appState={store} />, {
context: mockContext,
return renderClassComponentWithInstanceRef(MaximizeButton, {
id,
appState: store,
});
const instance = wrapper.instance();
return { instance, wrapper };

// const wrapper = shallow(<MaximizeButton id={id} appState={store} />, {
// context: mockContext,
// });
}

it('renders', () => {
const { wrapper } = createMaximizeButton(MAIN_JS);
expect(wrapper).toMatchSnapshot();
const { renderResult } = createMaximizeButton(MAIN_JS);
expect(
renderResult.getByTestId('editors-toolbar-maximize-button'),
).toBeInTheDocument();
});

it('handles a click', () => {
const { instance, wrapper } = createMaximizeButton(MAIN_JS);
instance.context = mockContext as unknown;
wrapper.dive().dive().find('button').simulate('click');
it('handles a click', async () => {
const { instance, renderResult } = createMaximizeButton(MAIN_JS);
instance.context = mockContext;
await userEvent.click(renderResult.getByRole('button'));
expect(mockContext.mosaicActions.expand).toHaveBeenCalledTimes(1);
});
});

describe('RemoveButton', () => {
function createRemoveButton(id: EditorId) {
const wrapper = shallow(<RemoveButton id={id} appState={store} />, {
context: mockContext,
return renderClassComponentWithInstanceRef(RemoveButton, {
id,
appState: store,
});
return { wrapper };
}

it('renders', () => {
const { wrapper } = createRemoveButton(MAIN_JS);
expect(wrapper).toMatchSnapshot();
const { renderResult } = createRemoveButton(MAIN_JS);
expect(
renderResult.getByTestId('editors-toolbar-remove-button'),
).toBeInTheDocument();
});

it('handles a click', () => {
it('handles a click', async () => {
const { editorMosaic } = store;
const hideSpy = jest.spyOn(editorMosaic, 'hide');
const { wrapper } = createRemoveButton(MAIN_JS);
wrapper.dive().dive().find('button').simulate('click');
const { renderResult } = createRemoveButton(MAIN_JS);
await userEvent.click(renderResult.getByRole('button'));
expect(hideSpy).toHaveBeenCalledTimes(1);
});
});
Expand Down
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>
);
}
13 changes: 8 additions & 5 deletions src/renderer/components/editors-non-ideal-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';

import { Button, NonIdealState } from '@blueprintjs/core';

import { TestIdContainer } from './TestIdContainer';
import { EditorMosaic } from '../editor-mosaic';

export function renderNonIdealState(editorMosaic: EditorMosaic) {
Expand All @@ -10,10 +11,12 @@ export function renderNonIdealState(editorMosaic: EditorMosaic) {
);

return (
<NonIdealState
action={resolveButton}
icon="applications"
description="You have closed all editors. You can open them again with the button below or in the sidebar menu!"
/>
<TestIdContainer testId="editors-non-ideal-state">
<NonIdealState
action={resolveButton}
icon="applications"
description="You have closed all editors. You can open them again with the button below or in the sidebar menu!"
/>
</TestIdContainer>
);
}
Loading

0 comments on commit 5a02bbf

Please sign in to comment.