From 57ec0534b27c5522bf9702a68b973f743f048ff0 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Fri, 22 Oct 2021 00:18:03 -0400 Subject: [PATCH] Replace enzyme with React testing library in ui (#917) * Replace enzyme with React testing library in ui * Mock Math.random() --- packages/redux-devtools-ui/jest.config.js | 1 - packages/redux-devtools-ui/package.json | 12 +- .../src/ContextMenu/ContextMenu.tsx | 21 +- .../redux-devtools-ui/tests/Button.test.tsx | 14 +- .../tests/Container.test.tsx | 7 +- .../tests/ContextMenu.test.tsx | 16 +- .../redux-devtools-ui/tests/Dialog.test.tsx | 28 +- .../redux-devtools-ui/tests/Editor.test.tsx | 11 +- .../redux-devtools-ui/tests/Form.test.tsx | 34 +- .../tests/Notification.test.tsx | 20 +- .../tests/SegmentedControl.test.tsx | 14 +- .../redux-devtools-ui/tests/Select.test.tsx | 40 +- .../redux-devtools-ui/tests/Slider.test.tsx | 19 +- .../redux-devtools-ui/tests/Tabs.test.tsx | 22 +- .../redux-devtools-ui/tests/Toolbar.test.tsx | 11 +- .../__snapshots__/ContextMenu.test.tsx.snap | 1 + .../tests/__snapshots__/Editor.test.tsx.snap | 144 +- .../tests/__snapshots__/Form.test.tsx.snap | 1387 +++-- .../tests/__snapshots__/Select.test.tsx.snap | 4515 +---------------- .../tests/__snapshots__/Slider.test.tsx.snap | 3 +- .../tests/__snapshots__/Tabs.test.tsx.snap | 5 +- packages/redux-devtools-ui/tests/setup.ts | 4 - yarn.lock | 23 +- 23 files changed, 1493 insertions(+), 4859 deletions(-) delete mode 100644 packages/redux-devtools-ui/tests/setup.ts diff --git a/packages/redux-devtools-ui/jest.config.js b/packages/redux-devtools-ui/jest.config.js index 1db14ea5..e4f61740 100644 --- a/packages/redux-devtools-ui/jest.config.js +++ b/packages/redux-devtools-ui/jest.config.js @@ -1,6 +1,5 @@ module.exports = { preset: 'ts-jest', - setupFilesAfterEnv: ['/tests/setup.ts'], testEnvironment: 'jsdom', moduleNameMapper: { '\\.css$': '/tests/__mocks__/styleMock.ts', diff --git a/packages/redux-devtools-ui/package.json b/packages/redux-devtools-ui/package.json index f50eb088..733d490b 100644 --- a/packages/redux-devtools-ui/package.json +++ b/packages/redux-devtools-ui/package.json @@ -58,18 +58,17 @@ "@babel/preset-typescript": "^7.15.0", "@storybook/addon-essentials": "^6.3.12", "@storybook/react": "^6.3.12", + "@testing-library/dom": "^8.10.1", + "@testing-library/react": "^12.1.2", + "@testing-library/user-event": "^13.5.0", "@types/color": "^3.0.2", - "@types/enzyme": "^3.10.10", - "@types/enzyme-adapter-react-16": "^1.0.6", "@types/jest": "^27.0.2", "@types/react": "^16.14.18", "@types/styled-components": "^5.1.15", "@typescript-eslint/eslint-plugin": "^5.1.0", "@typescript-eslint/parser": "^5.1.0", + "babel-loader": "^8.2.3", "csstype": "^3.0.9", - "enzyme": "^3.11.0", - "enzyme-adapter-react-16": "^1.15.6", - "enzyme-to-json": "^3.6.2", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-jest": "^25.2.2", @@ -86,7 +85,8 @@ "stylelint-config-styled-components": "^0.1.1", "stylelint-processor-styled-components": "^1.10.0", "ts-jest": "^27.0.7", - "typescript": "~4.4.4" + "typescript": "~4.4.4", + "webpack": "^5.59.1" }, "peerDependencies": { "@types/react": "^16.3.0 || ^17.0.0", diff --git a/packages/redux-devtools-ui/src/ContextMenu/ContextMenu.tsx b/packages/redux-devtools-ui/src/ContextMenu/ContextMenu.tsx index ba9d357c..c32f9f30 100644 --- a/packages/redux-devtools-ui/src/ContextMenu/ContextMenu.tsx +++ b/packages/redux-devtools-ui/src/ContextMenu/ContextMenu.tsx @@ -24,22 +24,7 @@ export interface ContextMenuProps { } export default class ContextMenu extends Component { - constructor(props: ContextMenuProps) { - super(props); - this.updateItems(props.items); - } - menu?: HTMLDivElement | null; - items?: React.ReactNode[]; - - UNSAFE_componentWillReceiveProps(nextProps: ContextMenuProps) { - if ( - nextProps.items !== this.props.items || - nextProps.visible !== this.props.visible - ) { - this.updateItems(nextProps.items); - } - } componentDidMount() { this.amendPosition(); @@ -84,8 +69,8 @@ export default class ContextMenu extends Component { this.menu!.style.left = `${left}px`; } - updateItems(items: Item[]) { - this.items = items.map((item) => { + renderItems() { + return this.props.items.map((item) => { if (isReactButtonElement(item)) return item; const value = item.value || item.name; return ( @@ -113,7 +98,7 @@ export default class ContextMenu extends Component { top={this.props.y} visible={this.props.visible} > - {this.items} + {this.renderItems()} ); } diff --git a/packages/redux-devtools-ui/tests/Button.test.tsx b/packages/redux-devtools-ui/tests/Button.test.tsx index 6ae53b8c..1d0769d0 100644 --- a/packages/redux-devtools-ui/tests/Button.test.tsx +++ b/packages/redux-devtools-ui/tests/Button.test.tsx @@ -1,19 +1,19 @@ import React from 'react'; -import { render, mount } from 'enzyme'; -import { renderToJson } from 'enzyme-to-json'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { Button } from '../src'; describe('Button', function () { it('renders correctly', () => { - const wrapper = render(); - expect(renderToJson(wrapper)).toMatchSnapshot(); + const { container } = render(); + expect(container.firstChild).toMatchSnapshot(); }); it('should handle the click event', () => { const onClick = jest.fn(); - const wrapper = mount(); + render(); - wrapper.find('button').simulate('click'); - expect(onClick).toBeCalled(); + userEvent.click(screen.getByRole('button')); + expect(onClick).toHaveBeenCalled(); }); }); diff --git a/packages/redux-devtools-ui/tests/Container.test.tsx b/packages/redux-devtools-ui/tests/Container.test.tsx index ca077f1d..fb8bb28f 100644 --- a/packages/redux-devtools-ui/tests/Container.test.tsx +++ b/packages/redux-devtools-ui/tests/Container.test.tsx @@ -1,17 +1,16 @@ import React from 'react'; -import { render } from 'enzyme'; -import { renderToJson } from 'enzyme-to-json'; +import { render } from '@testing-library/react'; import { Container } from '../src'; describe('Container', function () { it('renders correctly', () => { - const wrapper = render( + const { container } = render( Text ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); }); diff --git a/packages/redux-devtools-ui/tests/ContextMenu.test.tsx b/packages/redux-devtools-ui/tests/ContextMenu.test.tsx index 0875e97d..d50a81e2 100644 --- a/packages/redux-devtools-ui/tests/ContextMenu.test.tsx +++ b/packages/redux-devtools-ui/tests/ContextMenu.test.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { render, mount } from 'enzyme'; -import { renderToJson } from 'enzyme-to-json'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { ContextMenu } from '../src'; import { items } from '../src/ContextMenu/data'; describe('ContextMenu', function () { it('renders correctly', () => { - const wrapper = render( + const { container } = render( { @@ -16,15 +16,15 @@ describe('ContextMenu', function () { y={100} /> ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('should handle the click event', () => { const onClick = jest.fn(); - const wrapper = mount( - + render( + ); - wrapper.find('button').first().simulate('click'); - expect(onClick).toBeCalled(); + userEvent.click(screen.getByRole('button', { name: 'Menu Item 1' })); + expect(onClick).toHaveBeenCalled(); }); }); diff --git a/packages/redux-devtools-ui/tests/Dialog.test.tsx b/packages/redux-devtools-ui/tests/Dialog.test.tsx index eb9efe33..6397d478 100644 --- a/packages/redux-devtools-ui/tests/Dialog.test.tsx +++ b/packages/redux-devtools-ui/tests/Dialog.test.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import { render, mount } from 'enzyme'; -import { renderToJson } from 'enzyme-to-json'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { Dialog } from '../src'; describe('Dialog', function () { it('renders correctly', () => { - const wrapper = render( + const { container } = render( { // noop @@ -15,11 +15,11 @@ describe('Dialog', function () { }} /> ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('renders with props', () => { - const wrapper = render( + const { container } = render( ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('renders modal', () => { - const wrapper = render( + const { container } = render( { @@ -49,12 +49,12 @@ describe('Dialog', function () { }} /> ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('should handle dismiss event', () => { const onDismiss = jest.fn(); - const wrapper = mount( + render( ); - wrapper.find('button').first().simulate('click'); - expect(onDismiss).toBeCalled(); + userEvent.click(screen.getByRole('button', { name: 'Cancel' })); + expect(onDismiss).toHaveBeenCalled(); }); it('should handle submit event', () => { const onSubmit = jest.fn(); - const wrapper = mount( + render( { @@ -80,7 +80,7 @@ describe('Dialog', function () { /> ); - wrapper.find('button').last().simulate('click'); - expect(onSubmit).toBeCalled(); + userEvent.click(screen.getByRole('button', { name: 'Submit' })); + expect(onSubmit).toHaveBeenCalled(); }); }); diff --git a/packages/redux-devtools-ui/tests/Editor.test.tsx b/packages/redux-devtools-ui/tests/Editor.test.tsx index fd922108..82269b85 100644 --- a/packages/redux-devtools-ui/tests/Editor.test.tsx +++ b/packages/redux-devtools-ui/tests/Editor.test.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import { mount } from 'enzyme'; -import { mountToJson } from 'enzyme-to-json'; +import { render } from '@testing-library/react'; import { Editor } from '../src'; import 'codemirror/mode/javascript/javascript'; @@ -25,17 +24,17 @@ describe('Editor', function () { return range; }; - const wrapper = mount(); + const { container } = render(); it('renders correctly', () => { - expect(mountToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('calls getBoundingClientRect', () => { - expect(getBoundingClientRect).toBeCalled(); + expect(getBoundingClientRect).toHaveBeenCalled(); }); it('calls getClientRects', () => { - expect(getClientRects).toBeCalled(); + expect(getClientRects).toHaveBeenCalled(); }); }); diff --git a/packages/redux-devtools-ui/tests/Form.test.tsx b/packages/redux-devtools-ui/tests/Form.test.tsx index 5ade339f..6521ccea 100644 --- a/packages/redux-devtools-ui/tests/Form.test.tsx +++ b/packages/redux-devtools-ui/tests/Form.test.tsx @@ -1,19 +1,31 @@ import React from 'react'; -import { shallow, mount } from 'enzyme'; -import { shallowToJson } from 'enzyme-to-json'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { Form } from '../src'; import { schema, uiSchema, formData } from '../src/Form/schema'; describe('Form', function () { + let random: () => number; + + beforeAll(() => { + random = Math.random; + Math.random = jest.fn(() => 0.25546350798039463); + }); + + afterAll(() => { + Math.random = random; + console.log(Math.random()); + }); + it('renders correctly', () => { - const wrapper = shallow( + const { container } = render(
); - expect(shallowToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('renders with primary button', () => { - const wrapper = shallow( + const { container } = render( ); - expect(shallowToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('renders with no button', () => { - const wrapper = shallow( + const { container } = render( ); - expect(shallowToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('should handle the submit event', () => { const onSubmit = jest.fn(); - const wrapper = mount( + render( ); - wrapper.find('form').simulate('submit'); - expect(onSubmit).toBeCalled(); + userEvent.click(screen.getByRole('button', { name: 'Submit' })); + expect(onSubmit).toHaveBeenCalled(); }); }); diff --git a/packages/redux-devtools-ui/tests/Notification.test.tsx b/packages/redux-devtools-ui/tests/Notification.test.tsx index 1b7674a2..7b92cdfa 100644 --- a/packages/redux-devtools-ui/tests/Notification.test.tsx +++ b/packages/redux-devtools-ui/tests/Notification.test.tsx @@ -1,16 +1,16 @@ import React from 'react'; -import { render, mount } from 'enzyme'; -import { renderToJson } from 'enzyme-to-json'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { Notification } from '../src'; describe('Notification', function () { it('renders correctly', () => { - const wrapper = render(Message); - expect(renderToJson(wrapper)).toMatchSnapshot(); + const { container } = render(Message); + expect(container.firstChild).toMatchSnapshot(); }); it('renders with props', () => { - const wrapper = render( + const { container } = render( { @@ -20,16 +20,14 @@ describe('Notification', function () { Message ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('should handle the click event', () => { const onClose = jest.fn(); - const wrapper = mount( - Message - ); + render(Message); - wrapper.find('button').simulate('click'); - expect(onClose).toBeCalled(); + userEvent.click(screen.getByRole('button')); + expect(onClose).toHaveBeenCalled(); }); }); diff --git a/packages/redux-devtools-ui/tests/SegmentedControl.test.tsx b/packages/redux-devtools-ui/tests/SegmentedControl.test.tsx index 937e7cea..5d2c8460 100644 --- a/packages/redux-devtools-ui/tests/SegmentedControl.test.tsx +++ b/packages/redux-devtools-ui/tests/SegmentedControl.test.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import { render, mount } from 'enzyme'; -import { renderToJson } from 'enzyme-to-json'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { SegmentedControl } from '../src'; describe('SegmentedControl', function () { it('renders correctly', () => { - const wrapper = render( + const { container } = render( ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('should handle the click event', () => { const onClick = jest.fn(); - const wrapper = mount( + render( ); - wrapper.find('button').first().simulate('click'); - expect(onClick).toBeCalled(); + userEvent.click(screen.getByRole('button', { name: 'Button1' })); + expect(onClick).toHaveBeenCalled(); }); }); diff --git a/packages/redux-devtools-ui/tests/Select.test.tsx b/packages/redux-devtools-ui/tests/Select.test.tsx index f76fc3db..8e6e1f68 100644 --- a/packages/redux-devtools-ui/tests/Select.test.tsx +++ b/packages/redux-devtools-ui/tests/Select.test.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { render, mount, CommonWrapper, ReactWrapper } from 'enzyme'; -import { renderToJson, mountToJson } from 'enzyme-to-json'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { Select } from '../src'; import { options } from '../src/Select/options'; describe('Select', function () { it('renders correctly', () => { - const wrapper = render( + const { container } = render( { @@ -34,32 +34,30 @@ describe('Select', function () { menuPlacement="top" /> ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('should select another option', () => { const onChange = jest.fn(); - const wrapper = mount( - ); - const input = wrapper.find('input'); - (input.at(0).instance() as unknown as HTMLInputElement).value = 'two'; - input.first().simulate('change'); - expect(mountToJson(wrapper)).toMatchSnapshot(); - input.first().simulate('keyDown', { keyCode: 13 }); - expect(onChange).toBeCalled(); + userEvent.type(screen.getByRole('combobox'), 'two'); + expect(container.firstChild).toMatchSnapshot(); + userEvent.type(screen.getByRole('combobox'), '{enter}'); + expect(onChange).toHaveBeenCalled(); }); it("shouldn't find any results", () => { const onChange = jest.fn(); - const wrapper = mount( + ); - const input = wrapper.find('input'); - (input.at(0).instance() as unknown as HTMLInputElement).value = 'text'; - input.first().simulate('change'); - expect(mountToJson(wrapper)).toMatchSnapshot(); // 'No results found' - input.first().simulate('keyDown', { keyCode: 13 }); - expect(onChange).not.toBeCalled(); + userEvent.type(screen.getByRole('combobox'), 'text'); + expect(container.firstChild).toMatchSnapshot(); + userEvent.type(screen.getByRole('combobox'), '{enter}'); + expect(onChange).not.toHaveBeenCalled(); }); }); diff --git a/packages/redux-devtools-ui/tests/Slider.test.tsx b/packages/redux-devtools-ui/tests/Slider.test.tsx index d5d70699..e43d5b86 100644 --- a/packages/redux-devtools-ui/tests/Slider.test.tsx +++ b/packages/redux-devtools-ui/tests/Slider.test.tsx @@ -1,22 +1,21 @@ import React from 'react'; -import { render, mount } from 'enzyme'; -import { renderToJson } from 'enzyme-to-json'; +import { fireEvent, render, screen } from '@testing-library/react'; import { Slider } from '../src'; describe('Slider', function () { it('renders correctly', () => { - const wrapper = render( + const { container } = render( { // noop }} /> ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('renders with props', () => { - const wrapper = render( + const { container } = render( ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('should handle the change event', () => { const onChange = jest.fn(); - const wrapper = mount(); + render(); - wrapper.find('input').simulate('change'); - expect(onChange).toBeCalled(); - expect(onChange).toBeCalledWith(1); + fireEvent.change(screen.getByRole('slider'), { target: { value: '2' } }); + expect(onChange).toHaveBeenCalled(); + expect(onChange).toHaveBeenCalledWith(2); }); }); diff --git a/packages/redux-devtools-ui/tests/Tabs.test.tsx b/packages/redux-devtools-ui/tests/Tabs.test.tsx index 0a37ba1e..257d9de0 100644 --- a/packages/redux-devtools-ui/tests/Tabs.test.tsx +++ b/packages/redux-devtools-ui/tests/Tabs.test.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { render, mount } from 'enzyme'; -import { renderToJson } from 'enzyme-to-json'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { Tabs } from '../src'; import { tabs, simple10Tabs } from '../src/Tabs/data'; describe('Tabs', function () { it('renders correctly', () => { - const wrapper = render( + const { container } = render( { @@ -14,11 +14,11 @@ describe('Tabs', function () { }} /> ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('renders with props', () => { - const wrapper = render( + const { container } = render( { @@ -27,11 +27,11 @@ describe('Tabs', function () { selected="Tab2" /> ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('renders tabs without inner components', () => { - const wrapper = render( + const { container } = render( { @@ -40,14 +40,14 @@ describe('Tabs', function () { selected="5" /> ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('should select tab', () => { const onClick = jest.fn(); - const wrapper = mount(); + render(); - wrapper.find('button').first().simulate('click'); - expect(onClick).toBeCalled(); + userEvent.click(screen.getByRole('button', { name: 'Tab1' })); + expect(onClick).toHaveBeenCalled(); }); }); diff --git a/packages/redux-devtools-ui/tests/Toolbar.test.tsx b/packages/redux-devtools-ui/tests/Toolbar.test.tsx index 13f56990..8b38bc8a 100644 --- a/packages/redux-devtools-ui/tests/Toolbar.test.tsx +++ b/packages/redux-devtools-ui/tests/Toolbar.test.tsx @@ -1,11 +1,10 @@ import React from 'react'; -import { render } from 'enzyme'; -import { renderToJson } from 'enzyme-to-json'; +import { render } from '@testing-library/react'; import { Toolbar, Divider, Spacer, Button } from '../src'; describe('Toolbar', function () { it('renders correctly', () => { - const wrapper = render( + const { container } = render( @@ -13,11 +12,11 @@ describe('Toolbar', function () { ); - expect(renderToJson(wrapper)).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('renders with props', () => { - const wrapper = render(); - expect(renderToJson(wrapper)).toMatchSnapshot(); + const { container } = render(); + expect(container.firstChild).toMatchSnapshot(); }); }); diff --git a/packages/redux-devtools-ui/tests/__snapshots__/ContextMenu.test.tsx.snap b/packages/redux-devtools-ui/tests/__snapshots__/ContextMenu.test.tsx.snap index fde70ac7..e4d98639 100644 --- a/packages/redux-devtools-ui/tests/__snapshots__/ContextMenu.test.tsx.snap +++ b/packages/redux-devtools-ui/tests/__snapshots__/ContextMenu.test.tsx.snap @@ -3,6 +3,7 @@ exports[`ContextMenu renders correctly 1`] = `
+
+ `; exports[`Form renders with no button 1`] = ` - +
+
+ + Example form + +

+ A simple form example. +

+
+ + +
+
+ + +
+
+ + +
+
+ + +

+ Hint: Make it strong! +

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ + +
+
+
+
+
+ +
+ + + +
+
+
+ +
+
+ +
+ 52 +
+
+
+
+
+