Skip to content

Commit c23059c

Browse files
authored
Merge pull request #1199 from rvsia/enzymeRemoval
Remove enzyme from DOCs and DEPs
2 parents 2dc8235 + 1863d18 commit c23059c

File tree

6 files changed

+97
-329
lines changed

6 files changed

+97
-329
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ Run `yarn build` in root folder to build all packages. You can run this command
327327
Run `yarn lint` in root folder to check if the code is correctly formatted. You can use `yarn lint --fix` to automatically correct issues.
328328
### Write tests
329329

330-
All new code should be properly tested. Run `yarn test` in root folder to test all files. Check codecoverage report to see uncovered lines of code. We are using [Jest](https://jestjs.io/) and [Enzyme](https://enzymejs.github.io/enzyme/).
330+
All new code should be properly tested. Run `yarn test` in root folder to test all files. Check codecoverage report to see uncovered lines of code. We are using [Jest](https://jestjs.io/) and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/).
331331

332332
### Documentation update
333333

package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,12 @@
7878
"@types/react": "^17.0.21",
7979
"@types/react-dom": "^17.0.9",
8080
"@typescript-eslint/eslint-plugin": "^4.31.1",
81-
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.6",
8281
"babel-eslint": "^10.1.0",
8382
"babel-jest": "^27.4.6",
8483
"babel-loader": "^8.2.3",
8584
"babel-plugin-transform-imports": "^2.0.0",
8685
"css-loader": "^6.5.1",
8786
"dtslint": "^4.2.1",
88-
"enzyme": "^3.11.0",
89-
"enzyme-to-json": "^3.6.2",
9087
"eslint": "^7.32.0",
9188
"eslint-config-prettier": "^8.3.0",
9289
"eslint-config-react-app": "^7.0.0",

packages/react-renderer-demo/src/pages/development-setup.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ Run `yarn build` in root folder to build all packages. You can run this command
9696
Run `yarn lint` in root folder to check if the code is correctly formatted. You can use `yarn lint --fix` to automatically correct issues.
9797
### Write tests
9898

99-
All new code should be properly tested. Run `yarn test` in root folder to test all files. Check codecoverage report to see uncovered lines of code. We are using [Jest](https://jestjs.io/) and [Enzyme](https://enzymejs.github.io/enzyme/).
99+
All new code should be properly tested. Run `yarn test` in root folder to test all files. Check codecoverage report to see uncovered lines of code. We are using [Jest](https://jestjs.io/) and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/).
100100

101101
### Documentation update
102102

packages/react-renderer-demo/src/pages/testing.md

Lines changed: 77 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import CodeExample from '@docs/code-example';
77

88
Always make sure that your **custom components** and their features are tested to avoid bugs and runtime crashes.
99

10-
In these examples, we will use [Jest](https://jestjs.io/) and [Enzyme](https://enzymejs.github.io/enzyme/docs/api/) but the same rules apply to any other testing libraries.
10+
In these examples, we will use [Jest](https://jestjs.io/) and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) but the same rules apply to any other testing libraries.
1111

1212

1313
## Testing the renderer
@@ -18,8 +18,8 @@ Below is an example of a form with an async validation and a conditional field.
1818

1919
```jsx
2020
import React from 'react';
21-
import { act } from 'react-dom/test-utils';
22-
import { mount } from 'enzyme';
21+
import { render, screen, waitFor } from '@testing-library/react';
22+
import userEvent from '@testing-library/user-event';
2323

2424
import { FormRenderer } from '@data-driven-forms/react-form-renderer';
2525
import { componentMapper, FormTemplate } from '@data-driven-forms/mui-component-mapper';
@@ -48,12 +48,14 @@ describe('<FormRendererTest />', () => {
4848
name: 'username',
4949
label: 'Username',
5050
isRequired: true,
51-
validate: [{ type: 'required', message: 'Username is required' }]
51+
validate: [{ type: 'required', message: 'Username is required' }],
52+
inputProps: { 'aria-label': 'Username field' },
5253
},
5354
{
5455
component: 'switch',
5556
name: 'enable-emails',
56-
label: 'Do you wish to receive promotinal emails?'
57+
label: 'Do you wish to receive promotinal emails?',
58+
inputProps: { 'aria-label': 'Enable emails' },
5759
},
5860
{
5961
component: 'text-field',
@@ -62,30 +64,33 @@ describe('<FormRendererTest />', () => {
6264
label: 'Email adress',
6365
condition: {
6466
when: 'enable-emails',
65-
is: true
67+
is: true,
6668
},
67-
validate: [validate, { type: 'required' }] // validation will be run immediatelly after the component is mounted and after changes
68-
}
69-
]
69+
validate: [validate, { type: 'required' }], // validation will be run immediatelly after the component is mounted and after changes
70+
inputProps: { 'aria-label': 'Email field' },
71+
},
72+
],
7073
};
7174

7275
it('should validate and submit the form', async () => {
7376
/**
74-
* we will be using mount because we will need the DOM updates
77+
* we will be using render because we will need the DOM updates
7578
*/
76-
const wrapper = mount(<FormRenderer onSubmit={submitSpy} componentMapper={componentMapper} FormTemplate={FormTemplate} schema={schema} />);
79+
render(<FormRenderer onSubmit={submitSpy} componentMapper={componentMapper} FormTemplate={FormTemplate} schema={schema} />);
7780

7881
/**
7982
* we can try submit the form when the validation is not met
8083
*/
81-
wrapper.find('form').simulate('submit');
84+
userEvent.click(screen.getByText('Submit'));
8285
expect(submitSpy).not.toHaveBeenCalled(); // true
8386

8487
/**
8588
* fill the user name to pass the validation
8689
*/
87-
wrapper.find('input[name="username"]').simulate('change', { target: { value: 'John' } });
88-
wrapper.find('form').simulate('submit');
90+
userEvent.type(screen.getByLabelText('Username field'), 'John');
91+
92+
userEvent.click(screen.getByText('Submit'));
93+
8994
/**
9095
* first argument are the values and the second one is formApi
9196
*/
@@ -94,46 +99,45 @@ describe('<FormRendererTest />', () => {
9499
/**
95100
* now lets check the email subscription
96101
*/
97-
expect(wrapper.find('input[name="email"]')).toHaveLength(0);
98-
wrapper.find('input[name="enable-emails"]').simulate('change', { target: { checked: true } });
99-
wrapper.update();
102+
expect(() => screen.getByLabelText('Email field')).toThrow();
103+
104+
userEvent.click(screen.getByLabelText('Enable emails'));
105+
100106
/**
101107
* there should be new form field
108+
* we need to use waitFor because of the async validation
102109
*/
103-
expect(wrapper.find('input[name="email"]')).toHaveLength(1);
110+
await waitFor(() => expect(screen.getByLabelText('Email field')).toBeInTheDocument());
104111
/**
105112
* submit should not occur
106113
*/
107-
wrapper.find('form').simulate('submit');
114+
userEvent.click(screen.getByText('Submit'), undefined, { skipPointerEventsCheck: true });
108115
expect(submitSpy).not.toHaveBeenCalled(); // true
109116

110117
/**
111118
* field should be in error state
112119
* we only allow value of John
113120
*/
114-
await act(async () => {
115-
wrapper.find('input[name="email"]').simulate('change', { target: { value: 'Marty' } });
116-
});
117-
wrapper.find('form').simulate('submit');
118-
expect(submitSpy).not.toHaveBeenCalled(); // true
119-
expect(wrapper.find('input[name="email"]').props()['aria-invalid']).toEqual(true);
121+
userEvent.type(screen.getByLabelText('Email field'), 'Marty');
122+
userEvent.click(screen.getByText('Submit'), undefined, { skipPointerEventsCheck: true });
123+
await waitFor(() => expect(screen.getByLabelText('Email field')).toBeInvalid());
120124
/**
121125
* set value to John and submit the form
122126
*/
123-
await act(async () => {
124-
wrapper.find('input[name="email"]').simulate('change', { target: { value: 'John' } });
125-
});
126-
wrapper.update();
127-
wrapper.find('input[name="email"]').simulate('focus', { target: { value: 'John' } });
128-
wrapper.find('form').simulate('submit');
129-
expect(submitSpy).toHaveBeenCalledWith(
130-
{
131-
email: 'John',
132-
username: 'John',
133-
'enable-emails': true
134-
},
135-
expect.any(Object),
136-
expect.any(Function)
127+
userEvent.type(screen.getByLabelText('Email field'), '{selectall}{backspace}John');
128+
await waitFor(() => expect(screen.getByLabelText('Email field')).toHaveAttribute('aria-invalid', 'false'));
129+
130+
userEvent.click(screen.getByText('Submit'));
131+
await waitFor(() =>
132+
expect(submitSpy).toHaveBeenCalledWith(
133+
{
134+
email: 'John',
135+
username: 'John',
136+
'enable-emails': true,
137+
},
138+
expect.any(Object),
139+
expect.any(Function)
140+
)
137141
); // true
138142
});
139143
});
@@ -151,20 +155,22 @@ Set up your renderer to make it easier to test the component-specific features.
151155

152156
```jsx
153157
import React from 'react';
154-
import { mount } from 'enzyme';
158+
import { render, screen } from '@testing-library/react';
159+
import userEvent from '@testing-library/user-event';
155160

156161
import { FormRenderer, useFieldApi } from '@data-driven-forms/react-form-renderer';
157162
import { FormTemplate } from '@data-driven-forms/mui-component-mapper';
158163

159-
import toJson from 'enzyme-to-json';
160-
161164
const CustomComponent = (props) => {
162165
const { input, meta, label, sideEffect } = useFieldApi(props);
163166
return (
164167
<div className="input-wrapper">
165-
<label className="input-label">{label}</label>
168+
<label className="input-label" htmlFor={input.name}>
169+
{label}
170+
</label>
166171
<input
167172
{...input}
173+
id={input.name}
168174
onChange={(...args) => {
169175
sideEffect(...args); // do something in addition to just changing the value in form state
170176
input.onChange(...args);
@@ -180,7 +186,7 @@ const CustomComponent = (props) => {
180186
};
181187

182188
CustomComponent.defaultProps = {
183-
sideEffect: () => {}
189+
sideEffect: () => {},
184190
};
185191

186192
const createSchema = ({ label = 'Custom label', validate = [], ...rest }) => ({
@@ -190,17 +196,17 @@ const createSchema = ({ label = 'Custom label', validate = [], ...rest }) => ({
190196
component: 'custom-component',
191197
label,
192198
validate,
193-
...rest
194-
}
195-
]
199+
...rest,
200+
},
201+
],
196202
});
197203

198204
const RendererWrapper = (props) => (
199205
<FormRenderer
200206
onSubmit={() => {}}
201207
FormTemplate={FormTemplate}
202208
componentMapper={{
203-
'custom-component': CustomComponent
209+
'custom-component': CustomComponent,
204210
}}
205211
schema={{ fields: [] }}
206212
{...props}
@@ -209,19 +215,19 @@ const RendererWrapper = (props) => (
209215

210216
describe('<CustomComponent /> with renderer', () => {
211217
it('should render component to snapshot', () => {
212-
const wrapper = mount(<RendererWrapper schema={createSchema({})} />);
213-
expect(toJson(wrapper.find(CustomComponent))).toMatchSnapshot();
218+
const { asFragment } = render(<RendererWrapper schema={createSchema({})} />);
219+
expect(asFragment()).toMatchSnapshot();
214220
});
215221
it('should render component in error state to snapshot', () => {
216-
const wrapper = mount(<RendererWrapper schema={createSchema({ validate: [{ type: 'required' }] })} />);
217-
expect(toJson(wrapper.find(CustomComponent))).toMatchSnapshot();
222+
const { asFragment } = render(<RendererWrapper schema={createSchema({ validate: [{ type: 'required' }] })} />);
223+
expect(asFragment()).toMatchSnapshot();
218224
});
219225

220226
it('should call sideEffect when the input change', () => {
221227
const sideEffect = jest.fn();
222-
const wrapper = mount(<RendererWrapper schema={createSchema({ sideEffect })} />);
223-
wrapper.find('input[name="custom-component"]').simulate('change', { target: { value: 'foo' } });
224-
expect(sideEffect).toHaveBeenCalledTimes(1);
228+
render(<RendererWrapper schema={createSchema({ sideEffect })} />);
229+
userEvent.type(screen.getByLabelText('Custom label'), 'foo');
230+
expect(sideEffect).toHaveBeenCalledTimes(3);
225231
});
226232
});
227233

@@ -234,19 +240,21 @@ Rendering components outside of the renderer will require some additional set up
234240

235241
```jsx
236242
import React from 'react';
237-
import { mount } from 'enzyme';
243+
import { render, screen } from '@testing-library/react';
244+
import userEvent from '@testing-library/user-event';
238245

239246
import { Form, RendererContext, useFieldApi } from '@data-driven-forms/react-form-renderer';
240247

241-
import toJson from 'enzyme-to-json';
242-
243248
const CustomComponent = (props) => {
244249
const { input, meta, label, sideEffect } = useFieldApi(props);
245250
return (
246251
<div className="input-wrapper">
247-
<label className="input-label">{label}</label>
252+
<label className="input-label" htmlFor={input.name}>
253+
{label}
254+
</label>
248255
<input
249256
{...input}
257+
id={input.name}
250258
onChange={(...args) => {
251259
sideEffect(...args); // do something in addition to just changing the value in form state
252260
input.onChange(...args);
@@ -262,7 +270,7 @@ const CustomComponent = (props) => {
262270
};
263271

264272
CustomComponent.defaultProps = {
265-
sideEffect: () => {}
273+
sideEffect: () => {},
266274
};
267275

268276
const FormWrapper = ({ props, children }) => (
@@ -271,8 +279,8 @@ const FormWrapper = ({ props, children }) => (
271279
<form>
272280
<RendererContext.Provider
273281
value={{
274-
formOptions: {},
275-
validatorMapper: { required: () => (value) => (value ? undefined : 'required') }
282+
formOptions: { internalRegisterField: jest.fn(), internalUnRegisterField: jest.fn() },
283+
validatorMapper: { required: () => (value) => value ? undefined : 'required' },
276284
}}
277285
>
278286
{children}
@@ -284,31 +292,31 @@ const FormWrapper = ({ props, children }) => (
284292

285293
describe('<CustomComponent /> outside renderer', () => {
286294
it('should render component to snapshot', () => {
287-
const wrapper = mount(
295+
const { asFragment } = render(
288296
<FormWrapper>
289297
<CustomComponent name="custom-component" label="custom-component" />
290298
</FormWrapper>
291299
);
292-
expect(toJson(wrapper.find(CustomComponent))).toMatchSnapshot();
300+
expect(asFragment()).toMatchSnapshot();
293301
});
294302
it('should render component in error state to snapshot', () => {
295-
const wrapper = mount(
303+
const { asFragment } = render(
296304
<FormWrapper>
297305
<CustomComponent name="custom-component" label="custom-component" validate={[{ type: 'required' }]} />
298306
</FormWrapper>
299307
);
300-
expect(toJson(wrapper.find(CustomComponent))).toMatchSnapshot();
308+
expect(asFragment()).toMatchSnapshot();
301309
});
302310

303311
it('should call sideEffect when the input change', () => {
304312
const sideEffect = jest.fn();
305-
const wrapper = mount(
313+
render(
306314
<FormWrapper>
307315
<CustomComponent name="custom-component" label="custom-component" sideEffect={sideEffect} />
308316
</FormWrapper>
309317
);
310-
wrapper.find('input[name="custom-component"]').simulate('change', { target: { value: 'foo' } });
311-
expect(sideEffect).toHaveBeenCalledTimes(1);
318+
userEvent.type(screen.getByLabelText('custom-component'), 'foo');
319+
expect(sideEffect).toHaveBeenCalledTimes(3);
312320
});
313321
});
314322

templates/component-mapper/package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,12 @@
3434
"@babel/preset-react": "^7.0.0",
3535
"@semantic-release/git": "^7.0.5",
3636
"@semantic-release/npm": "^5.1.1",
37-
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.3",
3837
"babel-core": "^7.0.0-bridge.0",
3938
"babel-eslint": "9.x",
4039
"babel-jest": "^23.6.0",
4140
"babel-loader": "^8.0.4",
4241
"babel-plugin-lodash": "^3.3.4",
4342
"css-loader": "^1.0.1",
44-
"enzyme": "^3.7.0",
45-
"enzyme-to-json": "^3.3.4",
4643
"glob": "^7.1.6",
4744
"html-webpack-plugin": "^3.2.0",
4845
"mini-css-extract-plugin": "^0.4.4",

0 commit comments

Comments
 (0)