Skip to content

Commit 1d79d75

Browse files
authored
Upgrade React to v18 (#1093)
There were a few minor code changes needed for React 18 to work, and a *lot* of changes needed to keep tests working (most really have no major runtime impact, although a handful do probably make things slightly more robust). A lot more stuff under the hood in React has become async, so most of these test changes are about timing, making sure a component fully renders and goes through its loading lifecycle (and cancels async API calls if the component is prematurely unloaded, as they are in some tests). Similarly, lots of test code also needed be wrapped in `act(() => doSomething())`. I also updated some of the JS class syntax for a couple components that needed more invasive work for data/state loading while I was in there (for example, `diff-view.jsx`).
1 parent 4714d0c commit 1d79d75

File tree

13 files changed

+211
-666
lines changed

13 files changed

+211
-666
lines changed

package-lock.json

Lines changed: 50 additions & 536 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
@@ -19,7 +19,7 @@
1919
"@gfx/zopfli": "^1.0.15",
2020
"@testing-library/dom": "^10.4.0",
2121
"@testing-library/jest-dom": "^6.6.3",
22-
"@testing-library/react": "^12.1.5",
22+
"@testing-library/react": "^16.3.0",
2323
"autoprefixer": "^10.4.21",
2424
"babel-core": "^7.0.0-bridge.0",
2525
"babel-loader": "^9.2.1",
@@ -35,9 +35,9 @@
3535
"mini-css-extract-plugin": "^2.9.2",
3636
"postcss": "^8.5.6",
3737
"postcss-loader": "^8.1.1",
38-
"react": "^17.0.2",
38+
"react": "^18.3.1",
3939
"react-aria-modal": "^5.0.2",
40-
"react-dom": "^17.0.2",
40+
"react-dom": "^18.3.1",
4141
"react-router-dom": "^5.3.4",
4242
"react-tooltip": "^5.29.1",
4343
"style-loader": "^4.0.0",

src/components/__tests__/__snapshots__/page-url-details.test.jsx.snap

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ exports[`PageUrlDetails Component shows separate URL histories for each version
3434
3535
/ earth-science-conference-convenes
3636
37-
3837
39-
4038
</a>
4139
<i
4240
class="fa fa-no-hover fa-right-icon fa-angle-right"
@@ -67,9 +65,7 @@ exports[`PageUrlDetails Component shows separate URL histories for each version
6765
/ something
6866
</ins>
6967
70-
7168
72-
7369
</a>
7470
</li>
7571
</ol>
@@ -99,9 +95,7 @@ exports[`PageUrlDetails Component shows separate URL histories for each version
9995
10096
/ earth-science-conference-convenes
10197
102-
10398
104-
10599
</a>
106100
<i
107101
class="fa fa-no-hover fa-right-icon fa-angle-right"
@@ -136,9 +130,7 @@ exports[`PageUrlDetails Component shows separate URL histories for each version
136130
/ else
137131
</ins>
138132
139-
140133
141-
142134
</a>
143135
</li>
144136
</ol>
@@ -177,9 +169,7 @@ exports[`PageUrlDetails Component shows the versions' URL if it differs from the
177169
/ something
178170
</ins>
179171
180-
181172
182-
183173
</a>
184174
</details>
185175
</div>
@@ -216,9 +206,7 @@ exports[`PageUrlDetails Component shows the versions' redirects 1`] = `
216206
217207
/ earth-science-conference-convenes
218208
219-
220209
221-
222210
</a>
223211
<i
224212
class="fa fa-no-hover fa-right-icon fa-angle-right"
@@ -249,9 +237,7 @@ exports[`PageUrlDetails Component shows the versions' redirects 1`] = `
249237
/ something
250238
</ins>
251239
252-
253240
254-
255241
</a>
256242
</li>
257243
</ol>

src/components/__tests__/change-view.test.jsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-env jest */
22

3-
import { render, screen, fireEvent } from '@testing-library/react';
3+
import { render, screen, fireEvent, act } from '@testing-library/react';
44
import { ApiContext } from '../api-context';
55
import ChangeView, { defaultDiffType, diffTypeStorage } from '../change-view/change-view';
66
import layeredStorage from '../../scripts/layered-storage';
@@ -86,13 +86,13 @@ describe('change-view', () => {
8686

8787
describe('initial diffType', () => {
8888
describe('when a diffType has been stored in layeredStorage and is relevant to the pages being compared', () => {
89-
it('sets state.diffType to the stored value', () => {
89+
it('sets state.diffType to the stored value', async () => {
9090
const storedDiffType = 'CHANGES_ONLY_TEXT';
9191
layeredStorage.setItem(diffTypeStorage, storedDiffType);
9292

9393
renderBasicChangeView({ mediaType: 'text/html' });
9494

95-
screen.getByText(`diffType="${storedDiffType}"`);
95+
await screen.findByText(`diffType="${storedDiffType}"`);
9696
});
9797

9898
describe('when a diffType has been stored in layeredStorage and is is NOT relevant to the pages being compared', () => {
@@ -222,19 +222,24 @@ describe('change-view', () => {
222222
// sanity check
223223
expect(newType).not.toEqual(defaultDiffType);
224224

225-
const diffSelector = screen.getByLabelText('Comparison:');
226-
diffSelector.value = newType;
227-
fireEvent.change(diffSelector);
225+
await act(() => {
226+
const diffSelector = screen.getByLabelText('Comparison:');
227+
diffSelector.value = newType;
228+
fireEvent.change(diffSelector);
229+
});
230+
228231
screen.getByText(`diffType="${newType}"`);
229232
});
230233

231-
it('stores the new diffType in layeredStorage', () => {
234+
it('stores the new diffType in layeredStorage', async () => {
232235
renderBasicChangeView({ mediaType: 'text/html' });
233236

234237
const newType = diffTypesFor('text/html')[0].value;
235-
const diffSelector = screen.getByLabelText('Comparison:');
236-
diffSelector.value = newType;
237-
fireEvent.change(diffSelector);
238+
await act(() => {
239+
const diffSelector = screen.getByLabelText('Comparison:');
240+
diffSelector.value = newType;
241+
fireEvent.change(diffSelector);
242+
});
238243

239244
expect(layeredStorage.getItem(diffTypeStorage)).toBe(newType);
240245
});

src/components/__tests__/diff-view.test.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ describe('diff-view', () => {
3131
);
3232

3333
expect(container).not.toBeEmptyDOMElement();
34+
await screen.findByText(/loading/i);
35+
await waitFor(() => expect(screen.queryByText(/loading/i)).toBeNull());
3436
});
3537

3638
it('renders an alert if there are no changes in the diff', async () => {
@@ -47,7 +49,7 @@ describe('diff-view', () => {
4749
</ApiContext.Provider>
4850
);
4951

50-
await waitFor(() => screen.getByRole('alert'));
52+
await screen.findByRole('alert');
5153
});
5254

5355
it('renders no alert if there are changes in the diff', async () => {
@@ -63,7 +65,7 @@ describe('diff-view', () => {
6365
);
6466

6567
await waitFor(() => expect(mockApi.getDiff).toHaveBeenCalled());
68+
await screen.findByText('Hi');
6669
expect(screen.queryByRole('alert')).toBeNull();
67-
screen.getByText('Hi');
6870
});
6971
});

src/components/__tests__/login-form.test.jsx

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-env jest */
22

3-
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3+
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
44
import LoginPanel from '../login-form/login-form';
55
import WebMonitoringDb from '../../services/web-monitoring-db';
66
import { ApiContext } from '../api-context';
@@ -35,10 +35,14 @@ describe('login-form', () => {
3535
screen.getByRole('button', { name: 'Cancel' });
3636
});
3737

38-
it('Calls props.cancelLogin when the cancel button is clicked', () => {
38+
it('Calls props.cancelLogin when the cancel button is clicked', async () => {
3939
const cancelLogin = jest.fn();
4040
render(<LoginPanel cancelLogin={cancelLogin} />);
41-
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
41+
42+
await act(() => {
43+
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
44+
});
45+
4246
expect(cancelLogin).toHaveBeenCalled();
4347
});
4448

@@ -51,9 +55,11 @@ describe('login-form', () => {
5155
</ApiContext.Provider>
5256
);
5357

54-
fireEvent.change(screen.getByLabelText(/e-?mail/i), { target: { value: 'aaa@aaa.aaa' } });
55-
fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'password' } });
56-
fireEvent.click(screen.getByRole('button', { name: 'Log In' }));
58+
await act(() => {
59+
fireEvent.change(screen.getByLabelText(/e-?mail/i), { target: { value: 'aaa@aaa.aaa' } });
60+
fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'password' } });
61+
fireEvent.click(screen.getByRole('button', { name: 'Log In' }));
62+
});
5763

5864
expect(api.logIn).toHaveBeenCalledWith('aaa@aaa.aaa', 'password');
5965
});
@@ -67,9 +73,11 @@ describe('login-form', () => {
6773
</ApiContext.Provider>
6874
);
6975

70-
fireEvent.change(screen.getByLabelText(/e-?mail/i), { target: { value: 'aaa@aaa.aaa' } });
71-
fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'password' } });
72-
fireEvent.click(screen.getByRole('button', { name: 'Log In' }));
76+
await act(() => {
77+
fireEvent.change(screen.getByLabelText(/e-?mail/i), { target: { value: 'aaa@aaa.aaa' } });
78+
fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'password' } });
79+
fireEvent.click(screen.getByRole('button', { name: 'Log In' }));
80+
});
7381

7482
await waitFor(() => expect(onLogin).toHaveBeenCalledWith({ id: 5 }));
7583
});
@@ -83,9 +91,11 @@ describe('login-form', () => {
8391
</ApiContext.Provider>
8492
);
8593

86-
fireEvent.change(screen.getByLabelText(/e-?mail/i), { target: { value: 'aaa@aaa.aaa' } });
87-
fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'password' } });
88-
fireEvent.click(screen.getByRole('button', { name: 'Log In' }));
94+
await act(() => {
95+
fireEvent.change(screen.getByLabelText(/e-?mail/i), { target: { value: 'aaa@aaa.aaa' } });
96+
fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'password' } });
97+
fireEvent.click(screen.getByRole('button', { name: 'Log In' }));
98+
});
8999

90100
await screen.findByText('Login unsuccessful');
91101
});
@@ -98,8 +108,10 @@ describe('login-form', () => {
98108
</ApiContext.Provider>
99109
);
100110

101-
fireEvent.change(screen.getByLabelText(/e-?mail/i), { target: { value: 'aaa@aaa.aaa' } });
102-
fireEvent.click(screen.getByRole('button', { name: 'Log In' }));
111+
await act(() => {
112+
fireEvent.change(screen.getByLabelText(/e-?mail/i), { target: { value: 'aaa@aaa.aaa' } });
113+
fireEvent.click(screen.getByRole('button', { name: 'Log In' }));
114+
});
103115

104116
expect(api.logIn).not.toHaveBeenCalled();
105117
await screen.findByText('Please enter an e-mail and password.');

src/components/__tests__/page-details.test.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-env jest */
2-
import { render, waitFor } from '@testing-library/react';
2+
import { render, waitFor, screen } from '@testing-library/react';
33
import PageDetails from '../page-details/page-details';
44
import simplePage from '../../__mocks__/simple-page.json';
55
import { ApiContext } from '../api-context';
@@ -45,15 +45,15 @@ describe('page-details', () => {
4545
});
4646
};
4747

48-
it('can render', () => {
48+
it('can render', async () => {
4949
const mockApi = createMockApi();
50-
const { container } = render(
50+
render(
5151
<ApiContext.Provider value={{ api: mockApi }}>
5252
<PageDetails match={match} />
5353
</ApiContext.Provider>
5454
);
5555

56-
expect(container).not.toBeEmptyDOMElement();
56+
await screen.findByText(simplePage.title);
5757
});
5858

5959
it('shows correct title', async () => {

src/components/__tests__/page-list.test.jsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-env jest */
22

3-
import { fireEvent, render, screen } from '@testing-library/react';
3+
import { fireEvent, render, screen, act } from '@testing-library/react';
44
import PageList from '../page-list/page-list';
55
import simplePages from '../../__mocks__/simple-pages.json';
66

@@ -56,25 +56,29 @@ describe('page-list', () => {
5656
expect(screen.queryByText(/loading/i)).toBeNull();
5757
});
5858

59-
it('opens a new window when a user control clicks on a page row', () => {
59+
it('opens a new window when a user control clicks on a page row', async () => {
6060
global.open = jest.fn();
6161
render(<PageList pages={simplePages} />);
6262

6363
const page = simplePages[0];
64-
const row = screen.getByRole('link', { name: page.url }).closest('tr');
65-
fireEvent.click(row, { ctrlKey : true });
64+
await act(() => {
65+
const row = screen.getByRole('link', { name: page.url }).closest('tr');
66+
fireEvent.click(row, { ctrlKey : true });
67+
});
6668

6769
expect(global.open.mock.calls[0][0]).toBe(`/page/${page.uuid}`);
6870
expect(global.open.mock.calls[0][1]).toBe('_blank');
6971
});
7072

71-
it('opens a new window when a user command clicks on a page row', () => {
73+
it('opens a new window when a user command clicks on a page row', async () => {
7274
global.open = jest.fn();
7375
render(<PageList pages={simplePages} />);
7476

7577
const page = simplePages[0];
76-
const row = screen.getByRole('link', { name: page.url }).closest('tr');
77-
fireEvent.click(row, { metaKey : true });
78+
await act(() => {
79+
const row = screen.getByRole('link', { name: page.url }).closest('tr');
80+
fireEvent.click(row, { metaKey : true });
81+
});
7882

7983
expect(global.open.mock.calls.length).toBe(1);
8084
});

0 commit comments

Comments
 (0)