Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit c9fa4d1

Browse files
feat: migrated tests from enzyme to RTL (#160)
* feat: migrated tests from enzyme to RTL * refactor: used react-unit-test-utils to replace enzyme shallow * chore: remove react-test-render --------- Co-authored-by: Syed Ali Abbas Zaidi <[email protected]>
1 parent 715eebc commit c9fa4d1

File tree

9 files changed

+2677
-2073
lines changed

9 files changed

+2677
-2073
lines changed

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const { createConfig } = require('@edx/frontend-build');
22

33
const config = createConfig('jest', {
4-
setupFiles: [
4+
setupFilesAfterEnv: [
55
'<rootDir>/src/setupTest.js',
66
],
77
});

package-lock.json

Lines changed: 1252 additions & 347 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,13 @@
7171
"devDependencies": {
7272
"@edx/browserslist-config": "^1.2.0",
7373
"@edx/frontend-build": "13.0.1",
74+
"@edx/react-unit-test-utils": "1.7.0",
7475
"@edx/reactifex": "1.0.3",
75-
"@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
76+
"@testing-library/jest-dom": "5.17.0",
77+
"@testing-library/react": "12.1.5",
7678
"autoprefixer": "10.2.6",
7779
"axios-mock-adapter": "^1.15.0",
78-
"enzyme": "^3.3.0",
7980
"husky": "^0.14.3",
80-
"react-test-renderer": "^17.0.2",
8181
"travis-deploy-once": "^5.0.9"
8282
}
8383
}

src/console/ConsolePage.jsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export class ConsolePage extends React.Component {
6464
<Collapsible
6565
title="Manage Enrollments"
6666
defaultOpen
67+
data-testid="collapsible"
6768
>
6869
<div className="container p-0">
6970
<div className={`${rowClasses} mt-2`}>
@@ -117,6 +118,7 @@ export class ConsolePage extends React.Component {
117118
variant="danger"
118119
dismissible={false}
119120
show={!!this.props.loadingError}
121+
data-testid="alert-danger"
120122
>
121123
<div>
122124
<p>
@@ -132,6 +134,7 @@ export class ConsolePage extends React.Component {
132134
dismissible={false}
133135
show={!this.props.authorized && !this.props.loadingError}
134136
variant="warning"
137+
data-testid="alert-warning"
135138
>
136139
<p>
137140
It appears you do not have proper permissions to access this application.
@@ -151,7 +154,7 @@ export class ConsolePage extends React.Component {
151154
variant="danger"
152155
dismissible
153156
show={!!this.props.filterError}
154-
data-testid="filter-alert"
157+
data-testid="error-alert"
155158
>
156159
<p>Invalid program title.</p>
157160
</Alert>
@@ -174,6 +177,7 @@ export class ConsolePage extends React.Component {
174177
key={banner.id}
175178
variant={banner.bannerType}
176179
onClose={() => this.props.removeBanner(program.programKey, banner.id)}
180+
data-testid="alert-info"
177181
>
178182
<div className="modal-alert">
179183
{`${banner.message} `}
@@ -187,6 +191,7 @@ export class ConsolePage extends React.Component {
187191
<ConnectedReportSection
188192
programKey={program.programKey}
189193
isFirstSection={!program.areEnrollmentsWritable}
194+
data-testid="report-section"
190195
/>
191196
)}
192197
</div>

src/console/ConsolePage.test.jsx

Lines changed: 78 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { mount, shallow } from 'enzyme';
2-
import { Collapsible } from '@edx/paragon';
1+
/* eslint-disable import/no-extraneous-dependencies */
2+
import { fireEvent, render, screen } from '@testing-library/react';
3+
import { shallow } from '@edx/react-unit-test-utils';
34
import React from 'react';
4-
import renderer from 'react-test-renderer';
55
import { IntlProvider } from 'react-intl';
66
import { ConsolePage } from './ConsolePage';
7-
import ConnectedReportSection from '../report/reportSection';
87

98
let apiData = [];
109

@@ -41,13 +40,11 @@ describe('ConsolePage', () => {
4140
/>
4241
</IntlProvider>
4342
);
44-
const wrapper = mount(consolePageComponent);
45-
const tree = renderer.create(consolePageComponent);
43+
const { container } = render(consolePageComponent);
4644

47-
expect(tree).toMatchSnapshot();
48-
expect(wrapper.exists('.alert.alert-warning.show')).toEqual(false);
49-
expect(wrapper.exists('.alert.alert-danger.show')).toEqual(false);
50-
wrapper.unmount();
45+
expect(container).toMatchSnapshot();
46+
expect(container.querySelector('.alert-warning.show')).toBeNull();
47+
expect(container.querySelector('.alert-danger.show')).toBeNull();
5148
});
5249

5350
it('renders a warning banner if there is not an authorized user', () => {
@@ -66,13 +63,11 @@ describe('ConsolePage', () => {
6663
/>
6764
</IntlProvider>
6865
);
69-
const wrapper = mount(consolePageComponent);
70-
const tree = renderer.create(consolePageComponent);
66+
const { container } = render(consolePageComponent);
7167

72-
expect(tree).toMatchSnapshot();
73-
expect(wrapper.exists('.alert.alert-warning.show')).toEqual(true);
74-
expect(wrapper.exists('.alert.alert-danger.show')).toEqual(false);
75-
wrapper.unmount();
68+
expect(container).toMatchSnapshot();
69+
expect(container.querySelector('.alert-warning.show')).toBeInTheDocument();
70+
expect(container.querySelector('.alert-danger.show')).toBeNull();
7671
});
7772

7873
it('renders an error banner when there was an error loading programs', () => {
@@ -92,13 +87,11 @@ describe('ConsolePage', () => {
9287
/>
9388
</IntlProvider>
9489
);
95-
const wrapper = mount(consolePageComponent);
96-
const tree = renderer.create(consolePageComponent);
90+
const { container } = render(consolePageComponent);
9791

98-
expect(tree).toMatchSnapshot();
99-
expect(wrapper.exists('.alert.alert-warning.show')).toEqual(false);
100-
expect(wrapper.exists('.alert.alert-danger.show')).toEqual(true);
101-
wrapper.unmount();
92+
expect(container).toMatchSnapshot();
93+
expect(container.querySelector('.alert-warning.show')).toBeNull();
94+
expect(container.querySelector('.alert-danger.show')).toBeInTheDocument();
10295
});
10396

10497
it('renders programs when there is data passed in', () => {
@@ -117,20 +110,17 @@ describe('ConsolePage', () => {
117110
/>
118111
</IntlProvider>
119112
);
120-
const wrapper = mount(consolePageComponent);
121-
const tree = renderer.create(consolePageComponent).toJSON();
113+
const { container } = render(consolePageComponent);
122114

123-
expect(tree).toMatchSnapshot();
115+
expect(container).toMatchSnapshot();
124116
apiData.forEach((program, idx) => {
125-
expect(wrapper.find('h2').at(idx).text()).toEqual(program.programTitle);
126-
expect(wrapper.find(Collapsible).at(idx).prop('defaultOpen')).toEqual(true);
117+
expect(screen.getByText(program.programTitle)).toBeInTheDocument();
118+
expect(container.querySelectorAll('.pgn_collapsible')[idx]).toHaveClass('is-open');
127119
});
128120

129-
expect(wrapper.exists('.alert-danger.show')).toEqual(false);
130-
expect(wrapper.exists('.alert-info.show')).toEqual(false);
131-
expect(wrapper.exists('.alert-warning.show')).toEqual(false);
132-
133-
wrapper.unmount();
121+
expect(container.querySelector('.alert-danger.show')).toBeNull();
122+
expect(container.querySelector('.alert-info.show')).toBeNull();
123+
expect(container.querySelector('.alert-warning.show')).toBeNull();
134124
});
135125

136126
it('passes isFirstSection=true to report section when there is no enrollment section', () => {
@@ -157,20 +147,18 @@ describe('ConsolePage', () => {
157147

158148
// shallow render ConsolePage to avoid fully rendering the ConnectedReportSection,
159149
// which requires a Redux store
160-
const wrapper = shallow(consolePageComponent);
150+
const { instance } = shallow(consolePageComponent);
161151

162-
expect(wrapper.find('h2').text()).toEqual(apiData[0].programTitle);
152+
expect(instance.findByType('h2')[0].el.children[0]).toEqual(apiData[0].programTitle);
163153

164154
// we don't expect to see the enrollment section
165-
expect(wrapper.exists(Collapsible)).toEqual(false);
166-
167-
expect(wrapper.find(ConnectedReportSection).prop('isFirstSection')).toEqual(true);
155+
expect(instance.findByTestId('collapsible')).toEqual([]);
168156

169-
expect(wrapper.exists('.alert-danger')).toEqual(false);
170-
expect(wrapper.exists('.alert-info')).toEqual(false);
171-
expect(wrapper.exists('.alert-warning.show')).toEqual(false);
157+
expect(instance.findByTestId('report-section')[0].props.isFirstSection).toEqual(true);
172158

173-
wrapper.unmount();
159+
expect(instance.findByTestId('alert-danger')[0].props.show).toEqual(false);
160+
expect(instance.findByTestId('alert-info')).toEqual([]);
161+
expect(instance.findByTestId('alert-warning')[0].props.show).toEqual(false);
174162
});
175163

176164
it('passes isFirstSection=false to report section when there is an enrollment section', () => {
@@ -197,21 +185,19 @@ describe('ConsolePage', () => {
197185

198186
// shallow render ConsolePage to avoid fully rendering the ConnectedReportSection,
199187
// which requires a Redux store
200-
const wrapper = shallow(consolePageComponent);
201-
202-
expect(wrapper.find('h2').text()).toEqual(apiData[0].programTitle);
188+
const { instance } = shallow(consolePageComponent);
203189

204-
const collapsible = wrapper.find(Collapsible);
205-
expect(collapsible.prop('title')).toEqual('Manage Enrollments');
206-
expect(collapsible.prop('defaultOpen')).toEqual(true);
190+
expect(instance.findByType('h2')[0].el.children[0]).toEqual(apiData[0].programTitle);
207191

208-
expect(wrapper.find(ConnectedReportSection).prop('isFirstSection')).toEqual(false);
192+
const collapsible = instance.findByTestId('collapsible');
193+
expect(collapsible[0].props.title).toEqual('Manage Enrollments');
194+
expect(collapsible[0].props.defaultOpen).toEqual(true);
209195

210-
expect(wrapper.exists('.alert-danger')).toEqual(false);
211-
expect(wrapper.exists('.alert-info')).toEqual(false);
212-
expect(wrapper.exists('.alert-warning.show')).toEqual(false);
196+
expect(instance.findByTestId('report-section')[0].props.isFirstSection).toEqual(false);
213197

214-
wrapper.unmount();
198+
expect(instance.findByTestId('alert-danger')[0].props.show).toEqual(false);
199+
expect(instance.findByTestId('alert-info')).toEqual([]);
200+
expect(instance.findByTestId('alert-warning')[0].props.show).toEqual(false);
215201
});
216202

217203
it('renders program banners when they are included', () => {
@@ -242,22 +228,19 @@ describe('ConsolePage', () => {
242228
/>
243229
</IntlProvider>
244230
);
245-
const wrapper = mount(consolePageComponent);
246-
const tree = renderer.create(consolePageComponent).toJSON();
231+
const { container } = render(consolePageComponent);
247232

248-
expect(tree).toMatchSnapshot();
249-
expect(wrapper.find('.alert-danger.show .modal-alert').at(0).text()).toEqual('Sorry something went wrong ');
250-
expect(wrapper.find('.alert-success.show .modal-alert').at(0).text()).toEqual('You did it! ');
251-
252-
wrapper.unmount();
233+
expect(container).toMatchSnapshot();
234+
expect(container.querySelector('.alert-danger.show')).toHaveTextContent('Sorry something went wrong ');
235+
expect(container.querySelector('.alert-success.show')).toHaveTextContent('You did it! ');
253236
});
254237

255238
it('calls the fetchPrograms function on pageload', () => {
256239
const mock = jest.fn();
257240

258241
expect(mock).not.toHaveBeenCalled();
259242

260-
mount(
243+
render(
261244
<IntlProvider locale="en">
262245
<ConsolePage
263246
authorized
@@ -281,7 +264,7 @@ describe('ConsolePage', () => {
281264

282265
expect(mock).not.toHaveBeenCalled();
283266

284-
const wrapper = mount(
267+
const { container } = render(
285268
<IntlProvider locale="en">
286269
<ConsolePage
287270
authorized
@@ -297,8 +280,8 @@ describe('ConsolePage', () => {
297280
</IntlProvider>,
298281
);
299282

300-
const filterForm = wrapper.find('form');
301-
filterForm.simulate('submit');
283+
const filterForm = container.querySelector('form');
284+
fireEvent.submit(filterForm);
302285
expect(mock).toHaveBeenCalled();
303286
});
304287

@@ -319,19 +302,17 @@ describe('ConsolePage', () => {
319302
/>
320303
</IntlProvider>
321304
);
322-
const wrapper = mount(consolePageComponent);
323-
const tree = renderer.create(consolePageComponent);
305+
const { container: tree } = render(consolePageComponent);
324306

325307
expect(tree).toMatchSnapshot();
326-
const alert = wrapper.find('[data-testid="filter-alert"]').first();
327-
expect(alert.text()).toContain('Invalid');
328-
wrapper.unmount();
308+
const alert = screen.getByTestId('error-alert');
309+
expect(alert).toHaveTextContent('Invalid');
329310
});
330311

331312
it('calls the correct action with the correct program key on download button clicks', () => {
332313
const mock = jest.fn();
333314

334-
const wrapper = mount(
315+
const { container } = render(
335316
<IntlProvider locale="en">
336317
<ConsolePage
337318
authorized
@@ -347,31 +328,31 @@ describe('ConsolePage', () => {
347328
</IntlProvider>,
348329
);
349330

350-
const programADownloadProgramButton = wrapper.find('button.btn.btn-outline-primary').at(0);
351-
expect(programADownloadProgramButton.text()).toEqual('Download Program Enrollments');
352-
programADownloadProgramButton.simulate('click');
331+
const programADownloadProgramButton = container.querySelectorAll('button.btn.btn-outline-primary')[0];
332+
expect(programADownloadProgramButton).toHaveTextContent('Download Program Enrollments');
333+
fireEvent.click(programADownloadProgramButton);
353334
expect(mock).toHaveBeenCalledWith('a', false);
354335

355-
const programADownloadCourseButton = wrapper.find('button.btn.btn-outline-primary').at(1);
356-
expect(programADownloadCourseButton.text()).toEqual('Download Course Enrollments');
357-
programADownloadCourseButton.simulate('click');
336+
const programADownloadCourseButton = container.querySelectorAll('button.btn.btn-outline-primary')[1];
337+
expect(programADownloadCourseButton).toHaveTextContent('Download Course Enrollments');
338+
fireEvent.click(programADownloadCourseButton);
358339
expect(mock).toHaveBeenCalledWith('a', true);
359340

360-
const programBDownloadProgramButton = wrapper.find('button.btn.btn-outline-primary').at(2);
361-
expect(programBDownloadProgramButton.text()).toEqual('Download Program Enrollments');
362-
programBDownloadProgramButton.simulate('click');
341+
const programBDownloadProgramButton = container.querySelectorAll('button.btn.btn-outline-primary')[2];
342+
expect(programBDownloadProgramButton).toHaveTextContent('Download Program Enrollments');
343+
fireEvent.click(programBDownloadProgramButton);
363344
expect(mock).toHaveBeenCalledWith('b', false);
364345

365-
const programBDownloadCourseButton = wrapper.find('button.btn.btn-outline-primary').at(3);
366-
expect(programBDownloadCourseButton.text()).toEqual('Download Course Enrollments');
367-
programBDownloadCourseButton.simulate('click');
346+
const programBDownloadCourseButton = container.querySelectorAll('button.btn.btn-outline-primary')[3];
347+
expect(programBDownloadCourseButton).toHaveTextContent('Download Course Enrollments');
348+
fireEvent.click(programBDownloadCourseButton);
368349
expect(mock).toHaveBeenCalledWith('b', true);
369350
});
370351

371352
it('calls the correct action with the correct program key onchange of the file inputs', () => {
372353
const mock = jest.fn();
373354

374-
const wrapper = mount(
355+
const { container } = render(
375356
<IntlProvider locale="en">
376357
<ConsolePage
377358
authorized
@@ -390,13 +371,18 @@ describe('ConsolePage', () => {
390371
const file = new File([], 'test');
391372

392373
expect(mock).not.toHaveBeenCalled();
393-
wrapper.find('.input-overlay-hack').at(0).simulate('change', { target: { name: 'pollName', files: [file] } });
374+
const fileInputs = container.querySelectorAll('.input-overlay-hack');
375+
376+
fireEvent.change(fileInputs[0], { target: { files: [file] } });
394377
expect(mock).toHaveBeenCalledWith('a', false, file);
395-
wrapper.find('.input-overlay-hack').at(1).simulate('change', { target: { name: 'pollName', files: [file] } });
378+
379+
fireEvent.change(fileInputs[1], { target: { files: [file] } });
396380
expect(mock).toHaveBeenCalledWith('a', true, file);
397-
wrapper.find('.input-overlay-hack').at(2).simulate('change', { target: { name: 'pollName', files: [file] } });
381+
382+
fireEvent.change(fileInputs[2], { target: { files: [file] } });
398383
expect(mock).toHaveBeenCalledWith('b', false, file);
399-
wrapper.find('.input-overlay-hack').at(3).simulate('change', { target: { name: 'pollName', files: [file] } });
384+
385+
fireEvent.change(fileInputs[3], { target: { files: [file] } });
400386
expect(mock).toHaveBeenCalledWith('b', true, file);
401387
});
402388

@@ -428,10 +414,9 @@ describe('ConsolePage', () => {
428414
/>
429415
</IntlProvider>
430416
);
431-
const wrapperOne = mount(consolePageComponentOne);
432-
const programTitleZero = wrapperOne.find('h2').first();
433-
expect(programTitleZero.text()).toEqual('program 0');
434-
wrapperOne.unmount();
417+
const wrapperOne = render(consolePageComponentOne);
418+
const programTitleZero = wrapperOne.container.querySelector('h2');
419+
expect(programTitleZero).toHaveTextContent('program 0');
435420

436421
const consolePageComponentTwo = (
437422
<IntlProvider locale="en">
@@ -449,9 +434,8 @@ describe('ConsolePage', () => {
449434
/>
450435
</IntlProvider>
451436
);
452-
const wrapperTwo = mount(consolePageComponentTwo);
453-
const programTitleTen = wrapperTwo.find('h2').first();
454-
expect(programTitleTen.text()).toEqual('program 10');
455-
wrapperTwo.unmount();
437+
const wrapperTwo = render(consolePageComponentTwo);
438+
const programTitleTen = wrapperTwo.container.querySelector('h2');
439+
expect(programTitleTen).toHaveTextContent('program 10');
456440
});
457441
});

0 commit comments

Comments
 (0)