Convert a few more tests to waitFor test helpers (#26509)
Continuing my journey to migrate all the Scheduler flush* methods to async versions of the same helpers.
Andrew Clark committed
Mar 29, 2023 at 17:02 UTC
f0aafa1a7e3338871f60ac3ea8c1c92b8671520c
5 files changed
+49
-44
packages/internal-test-utils/ReactInternalTestUtils.js
+10
-10
@@ -12,25 +12,25 @@ import enqueueTask from './enqueueTask';
12
13
export {act} from './internalAct';
14
15
-function assertYieldsWereCleared(Scheduler) {
16
- const actualYields = Scheduler.unstable_clearLog();
15
+function assertYieldsWereCleared(caller) {
16
+ const actualYields = SchedulerMock.unstable_clearLog();
17
if (actualYields.length !== 0) {
18
const error = Error(
19
'The event log is not empty. Call assertLog(...) first.',
20
);
21
- Error.captureStackTrace(error, assertYieldsWereCleared);
21
+ Error.captureStackTrace(error, caller);
22
throw error;
23
}
24
}
25
26
-async function waitForMicrotasks() {
26
+export async function waitForMicrotasks() {
27
return new Promise(resolve => {
28
enqueueTask(() => resolve());
29
});
30
}
31
32
export async function waitFor(expectedLog, options) {
33
- assertYieldsWereCleared(SchedulerMock);
33
+ assertYieldsWereCleared(waitFor);
34
35
// Create the error object before doing any async work, to get a better
36
// stack trace.
@@ -79,7 +79,7 @@ ${diff(expectedLog, actualLog)}
79
}
80
81
export async function waitForAll(expectedLog) {
82
- assertYieldsWereCleared(SchedulerMock);
82
+ assertYieldsWereCleared(waitForAll);
83
84
// Create the error object before doing any async work, to get a better
85
// stack trace.
@@ -110,7 +110,7 @@ ${diff(expectedLog, actualLog)}
110
}
111
112
export async function waitForThrow(expectedError: mixed): mixed {
113
- assertYieldsWereCleared(SchedulerMock);
113
+ assertYieldsWereCleared(waitForThrow);
114
115
// Create the error object before doing any async work, to get a better
116
// stack trace.
@@ -160,7 +160,7 @@ ${diff(expectedError, x)}
160
// avoid using it in tests. It's really only for testing a particular
161
// implementation detail (update starvation prevention).
162
export async function unstable_waitForExpired(expectedLog): mixed {
163
- assertYieldsWereCleared(SchedulerMock);
163
+ assertYieldsWereCleared(unstable_waitForExpired);
164
165
// Create the error object before doing any async work, to get a better
166
// stack trace.
@@ -189,7 +189,7 @@ ${diff(expectedLog, actualLog)}
189
// now because that's how untable_flushUntilNextPaint already worked, but maybe
190
// we should split these use cases into separate APIs.
191
export async function waitForPaint(expectedLog) {
192
- assertYieldsWereCleared(SchedulerMock);
192
+ assertYieldsWereCleared(waitForPaint);
193
194
// Create the error object before doing any async work, to get a better
195
// stack trace.
@@ -219,7 +219,7 @@ ${diff(expectedLog, actualLog)}
219
}
220
221
export async function waitForDiscrete(expectedLog) {
222
- assertYieldsWereCleared(SchedulerMock);
222
+ assertYieldsWereCleared(waitForDiscrete);
223
224
// Create the error object before doing any async work, to get a better
225
// stack trace.
packages/react-dom/src/__tests__/ReactMultiChildText-test.js
+11
-7
@@ -10,11 +10,12 @@
10
'use strict';
11
12
const React = require('react');
13
-const ReactDOM = require('react-dom');
13
+const ReactDOMClient = require('react-dom/client');
14
const ReactTestUtils = require('react-dom/test-utils');
15
+const act = require('internal-test-utils').act;
16
17
// Helpers
17
-const testAllPermutations = function (testCases) {
18
+const testAllPermutations = async function (testCases) {
19
for (let i = 0; i < testCases.length; i += 2) {
20
const renderWithChildren = testCases[i];
21
const expectedResultAfterRender = testCases[i + 1];
@@ -24,10 +25,11 @@ const testAllPermutations = function (testCases) {
25
const expectedResultAfterUpdate = testCases[j + 1];
26
27
const container = document.createElement('div');
27
- ReactDOM.render(<div>{renderWithChildren}</div>, container);
28
+ const root = ReactDOMClient.createRoot(container);
29
+ await act(() => root.render(<div>{renderWithChildren}</div>));
30
expectChildren(container, expectedResultAfterRender);
31
30
- ReactDOM.render(<div>{updateWithChildren}</div>, container);
32
+ await act(() => root.render(<div>{updateWithChildren}</div>));
33
expectChildren(container, expectedResultAfterUpdate);
34
}
35
}
@@ -75,10 +77,12 @@ const expectChildren = function (container, children) {
77
* faster to render and update.
78
*/
79
describe('ReactMultiChildText', () => {
78
- it('should correctly handle all possible children for render and update', () => {
79
- expect(() => {
80
+ jest.setTimeout(20000);
81
+
82
+ it('should correctly handle all possible children for render and update', async () => {
83
+ await expect(async () => {
84
// prettier-ignore
81
- testAllPermutations([
85
+ await testAllPermutations([
86
// basic values
87
undefined, [],
88
null, [],
packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
+8
-11
@@ -1703,17 +1703,14 @@ describe('ReactHooks', () => {
1703
return null;
1704
}
1705
1706
- await act(() => {
1707
- ReactTestRenderer.unstable_batchedUpdates(() => {
1708
- ReactTestRenderer.create(
1709
- <>
1710
- <A />
1711
- <B />
1712
- </>,
1713
- );
1714
- expect(() => Scheduler.unstable_flushAll()).toThrow('Hello');
1715
- });
1716
- });
1706
+ expect(() => {
1707
+ ReactTestRenderer.create(
1708
+ <>
1709
+ <A />
1710
+ <B />
1711
+ </>,
1712
+ );
1713
+ }).toThrow('Hello');
1714
1715
if (__DEV__) {
1716
expect(console.error).toHaveBeenCalledTimes(2);
packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js
+6
-7
@@ -16,6 +16,7 @@ let use;
16
let Suspense;
17
let DiscreteEventPriority;
18
let startTransition;
19
+let waitForMicrotasks;
20
21
describe('isomorphic act()', () => {
22
beforeEach(() => {
@@ -28,6 +29,8 @@ describe('isomorphic act()', () => {
29
use = React.use;
30
Suspense = React.Suspense;
31
startTransition = React.startTransition;
32
+
33
+ waitForMicrotasks = require('internal-test-utils').waitForMicrotasks;
34
});
35
36
beforeEach(() => {
@@ -51,7 +54,7 @@ describe('isomorphic act()', () => {
54
// Nothing has rendered yet
55
expect(root).toMatchRenderedOutput(null);
56
// Flush the microtasks by awaiting
54
- await null;
57
+ await waitForMicrotasks();
58
expect(root).toMatchRenderedOutput('A');
59
60
// Now do the same thing but wrap the update with `act`. No
@@ -229,9 +232,7 @@ describe('isomorphic act()', () => {
232
//
233
// The exact number of microtasks is an implementation detail; just needs
234
// to happen when the microtask queue is flushed.
232
- await null;
233
- await null;
234
- await null;
235
+ await waitForMicrotasks();
236
237
expect(console.error).toHaveBeenCalledTimes(1);
238
expect(console.error.mock.calls[0][0]).toContain(
@@ -282,9 +283,7 @@ describe('isomorphic act()', () => {
283
//
284
// The exact number of microtasks is an implementation detail; just needs
285
// to happen when the microtask queue is flushed.
285
- await null;
286
- await null;
287
- await null;
286
+ await waitForMicrotasks();
287
288
expect(console.error).toHaveBeenCalledTimes(0);
289
packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.js
+14
-9
@@ -102,6 +102,7 @@ describe('ReactSchedulerIntegration', () => {
102
const root = ReactNoop.createRoot();
103
root.render('Initial');
104
await waitForAll([]);
105
+ expect(root).toMatchRenderedOutput('Initial');
106
107
scheduleCallback(NormalPriority, () => Scheduler.log('A'));
108
scheduleCallback(NormalPriority, () => Scheduler.log('B'));
@@ -112,16 +113,22 @@ describe('ReactSchedulerIntegration', () => {
113
root.render('Update');
114
});
115
115
- // Advance time just to be sure the next tasks have lower priority
116
- Scheduler.unstable_advanceTime(2000);
116
+ // Perform just a little bit of work. By now, the React task will have
117
+ // already been scheduled, behind A, B, and C.
118
+ await waitFor(['A']);
119
120
+ // Schedule some additional tasks. These won't fire until after the React
121
+ // update has finished.
122
scheduleCallback(NormalPriority, () => Scheduler.log('D'));
123
scheduleCallback(NormalPriority, () => Scheduler.log('E'));
124
125
// Flush everything up to the next paint. Should yield after the
126
// React commit.
123
- Scheduler.unstable_flushUntilNextPaint();
124
- assertLog(['A', 'B', 'C']);
127
+ await waitForPaint(['B', 'C']);
128
+ expect(root).toMatchRenderedOutput('Update');
129
+
130
+ // Now flush the rest of the work.
131
+ await waitForAll(['D', 'E']);
132
});
133
134
// @gate www
@@ -213,6 +220,7 @@ describe(
220
waitForPaint = InternalTestUtils.waitForPaint;
221
assertLog = InternalTestUtils.assertLog;
222
waitFor = InternalTestUtils.waitFor;
223
+ act = InternalTestUtils.act;
224
});
225
226
afterEach(() => {
@@ -293,8 +301,7 @@ describe(
301
// Start logging whenever shouldYield is called
302
logDuringShouldYield = true;
303
// Let's call it once to confirm the mock actually works
296
- Scheduler.unstable_shouldYield();
297
- assertLog(['shouldYield']);
304
+ await waitFor(['shouldYield']);
305
306
// Expire the task
307
Scheduler.unstable_advanceTime(10000);
@@ -307,11 +314,9 @@ describe(
314
startTransition(() => {
315
ReactNoop.render(<App />);
316
});
310
-
317
// Because the render expired, React should finish the tree without
318
// consulting `shouldYield` again
313
- Scheduler.unstable_flushNumberOfYields(1);
314
- assertLog(['B', 'C']);
319
+ await waitFor(['B', 'C']);
320
});
321
});
322
},