Codemod tests to waitFor pattern (4/?) (#26302)
This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See #26285 for full context.
Andrew Clark committed
Mar 3, 2023 at 21:24 UTC
faacefb4d0167f8d5973672754fe87afc607397b
13 files changed
+1095
-1274
packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js
+19
-9
@@ -18,6 +18,8 @@ describe('SimpleEventPlugin', function () {
18
19
let onClick;
20
let container;
21
+ let assertLog;
22
+ let waitForAll;
23
24
function expectClickThru(element) {
25
element.click();
@@ -43,6 +45,10 @@ describe('SimpleEventPlugin', function () {
45
ReactDOMClient = require('react-dom/client');
46
Scheduler = require('scheduler');
47
48
+ const InternalTestUtils = require('internal-test-utils');
49
+ assertLog = InternalTestUtils.assertLog;
50
+ waitForAll = InternalTestUtils.waitForAll;
51
+
52
onClick = jest.fn();
53
});
54
@@ -222,12 +228,12 @@ describe('SimpleEventPlugin', function () {
228
229
ReactDOM.render(<Button />, container);
230
expect(button.textContent).toEqual('Count: 0');
225
- expect(Scheduler).toHaveYielded([]);
231
+ assertLog([]);
232
233
click();
234
235
// There should be exactly one update.
230
- expect(Scheduler).toHaveYielded(['didUpdate - Count: 3']);
236
+ assertLog(['didUpdate - Count: 3']);
237
expect(button.textContent).toEqual('Count: 3');
238
});
239
@@ -240,6 +246,10 @@ describe('SimpleEventPlugin', function () {
246
ReactDOMClient = require('react-dom/client');
247
Scheduler = require('scheduler');
248
249
+ const InternalTestUtils = require('internal-test-utils');
250
+ assertLog = InternalTestUtils.assertLog;
251
+ waitForAll = InternalTestUtils.waitForAll;
252
+
253
act = require('jest-react').act;
254
});
255
@@ -274,10 +284,10 @@ describe('SimpleEventPlugin', function () {
284
// Initial mount
285
root.render(<Button />);
286
// Should not have flushed yet because it's async
277
- expect(Scheduler).toHaveYielded([]);
287
+ assertLog([]);
288
expect(button).toBe(undefined);
289
// Flush async work
280
- expect(Scheduler).toFlushAndYield(['render button: enabled']);
290
+ await waitForAll(['render button: enabled']);
291
292
function click() {
293
const event = new MouseEvent('click', {
@@ -292,7 +302,7 @@ describe('SimpleEventPlugin', function () {
302
303
// Click the button to trigger the side-effect
304
await act(async () => click());
295
- expect(Scheduler).toHaveYielded([
305
+ assertLog([
306
// The handler fired
307
'Side-effect',
308
// The component re-rendered synchronously, even in concurrent mode.
@@ -301,7 +311,7 @@ describe('SimpleEventPlugin', function () {
311
312
// Click the button again
313
click();
304
- expect(Scheduler).toHaveYielded([
314
+ assertLog([
315
// The event handler was removed from the button, so there's no effect.
316
]);
317
@@ -312,7 +322,7 @@ describe('SimpleEventPlugin', function () {
322
click();
323
click();
324
click();
315
- expect(Scheduler).toFlushAndYield([]);
325
+ await waitForAll([]);
326
});
327
328
// NOTE: This test was written for the old behavior of discrete updates,
@@ -345,7 +355,7 @@ describe('SimpleEventPlugin', function () {
355
// Should not have flushed yet because it's async
356
expect(button).toBe(undefined);
357
// Flush async work
348
- Scheduler.unstable_flushAll();
358
+ await waitForAll([]);
359
expect(button.textContent).toEqual('Count: 0');
360
361
function click() {
@@ -373,7 +383,7 @@ describe('SimpleEventPlugin', function () {
383
await act(async () => click());
384
385
// Flush the remaining work
376
- Scheduler.unstable_flushAll();
386
+ await waitForAll([]);
387
// The counter should equal the total number of clicks
388
expect(button.textContent).toEqual('Count: 7');
389
});
packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
+58
-88
@@ -18,6 +18,8 @@ let ReactTestRenderer;
18
let Scheduler;
19
let ReactDOMServer;
20
let act;
21
+let assertLog;
22
+let waitForAll;
23
24
describe('ReactHooks', () => {
25
beforeEach(() => {
@@ -30,6 +32,10 @@ describe('ReactHooks', () => {
32
Scheduler = require('scheduler');
33
ReactDOMServer = require('react-dom/server');
34
act = require('jest-react').act;
35
+
36
+ const InternalTestUtils = require('internal-test-utils');
37
+ assertLog = InternalTestUtils.assertLog;
38
+ waitForAll = InternalTestUtils.waitForAll;
39
});
40
41
if (__DEV__) {
@@ -54,7 +60,7 @@ describe('ReactHooks', () => {
60
});
61
}
62
57
- it('bails out in the render phase if all of the state is the same', () => {
63
+ it('bails out in the render phase if all of the state is the same', async () => {
64
const {useState, useLayoutEffect} = React;
65
66
function Child({text}) {
@@ -80,11 +86,7 @@ describe('ReactHooks', () => {
86
87
const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
88
root.update(<Parent />);
83
- expect(Scheduler).toFlushAndYield([
84
- 'Parent: 0, 0',
85
- 'Child: 0, 0',
86
- 'Effect: 0, 0',
87
- ]);
89
+ await waitForAll(['Parent: 0, 0', 'Child: 0, 0', 'Effect: 0, 0']);
90
expect(root).toMatchRenderedOutput('0, 0');
91
92
// Normal update
@@ -93,15 +95,11 @@ describe('ReactHooks', () => {
95
setCounter2(1);
96
});
97
96
- expect(Scheduler).toHaveYielded([
97
- 'Parent: 1, 1',
98
- 'Child: 1, 1',
99
- 'Effect: 1, 1',
100
- ]);
98
+ assertLog(['Parent: 1, 1', 'Child: 1, 1', 'Effect: 1, 1']);
99
100
// Update that bails out.
101
act(() => setCounter1(1));
104
- expect(Scheduler).toHaveYielded(['Parent: 1, 1']);
102
+ assertLog(['Parent: 1, 1']);
103
104
// This time, one of the state updates but the other one doesn't. So we
105
// can't bail out.
@@ -110,11 +108,7 @@ describe('ReactHooks', () => {
108
setCounter2(2);
109
});
110
113
- expect(Scheduler).toHaveYielded([
114
- 'Parent: 1, 2',
115
- 'Child: 1, 2',
116
- 'Effect: 1, 2',
117
- ]);
111
+ assertLog(['Parent: 1, 2', 'Child: 1, 2', 'Effect: 1, 2']);
112
113
// Lots of updates that eventually resolve to the current values.
114
act(() => {
@@ -128,7 +122,7 @@ describe('ReactHooks', () => {
122
123
// Because the final values are the same as the current values, the
124
// component bails out.
131
- expect(Scheduler).toHaveYielded(['Parent: 1, 2']);
125
+ assertLog(['Parent: 1, 2']);
126
127
// prepare to check SameValue
128
act(() => {
@@ -136,11 +130,7 @@ describe('ReactHooks', () => {
130
setCounter2(NaN);
131
});
132
139
- expect(Scheduler).toHaveYielded([
140
- 'Parent: 0, NaN',
141
- 'Child: 0, NaN',
142
- 'Effect: 0, NaN',
143
- ]);
133
+ assertLog(['Parent: 0, NaN', 'Child: 0, NaN', 'Effect: 0, NaN']);
134
135
// check if re-setting to negative 0 / NaN still bails out
136
act(() => {
@@ -150,20 +140,16 @@ describe('ReactHooks', () => {
140
setCounter2(NaN);
141
});
142
153
- expect(Scheduler).toHaveYielded(['Parent: 0, NaN']);
143
+ assertLog(['Parent: 0, NaN']);
144
145
// check if changing negative 0 to positive 0 does not bail out
146
act(() => {
147
setCounter1(0);
148
});
159
- expect(Scheduler).toHaveYielded([
160
- 'Parent: 0, NaN',
161
- 'Child: 0, NaN',
162
- 'Effect: 0, NaN',
163
- ]);
149
+ assertLog(['Parent: 0, NaN', 'Child: 0, NaN', 'Effect: 0, NaN']);
150
});
151
166
- it('bails out in render phase if all the state is the same and props bail out with memo', () => {
152
+ it('bails out in render phase if all the state is the same and props bail out with memo', async () => {
153
const {useState, memo} = React;
154
155
function Child({text}) {
@@ -188,10 +174,7 @@ describe('ReactHooks', () => {
174
175
const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
176
root.update(<Parent theme="light" />);
191
- expect(Scheduler).toFlushAndYield([
192
- 'Parent: 0, 0 (light)',
193
- 'Child: 0, 0 (light)',
194
- ]);
177
+ await waitForAll(['Parent: 0, 0 (light)', 'Child: 0, 0 (light)']);
178
expect(root).toMatchRenderedOutput('0, 0 (light)');
179
180
// Normal update
@@ -200,14 +183,11 @@ describe('ReactHooks', () => {
183
setCounter2(1);
184
});
185
203
- expect(Scheduler).toHaveYielded([
204
- 'Parent: 1, 1 (light)',
205
- 'Child: 1, 1 (light)',
206
- ]);
186
+ assertLog(['Parent: 1, 1 (light)', 'Child: 1, 1 (light)']);
187
188
// Update that bails out.
189
act(() => setCounter1(1));
210
- expect(Scheduler).toHaveYielded(['Parent: 1, 1 (light)']);
190
+ assertLog(['Parent: 1, 1 (light)']);
191
192
// This time, one of the state updates but the other one doesn't. So we
193
// can't bail out.
@@ -216,10 +196,7 @@ describe('ReactHooks', () => {
196
setCounter2(2);
197
});
198
219
- expect(Scheduler).toHaveYielded([
220
- 'Parent: 1, 2 (light)',
221
- 'Child: 1, 2 (light)',
222
- ]);
199
+ assertLog(['Parent: 1, 2 (light)', 'Child: 1, 2 (light)']);
200
201
// Updates bail out, but component still renders because props
202
// have changed
@@ -229,10 +206,7 @@ describe('ReactHooks', () => {
206
root.update(<Parent theme="dark" />);
207
});
208
232
- expect(Scheduler).toHaveYielded([
233
- 'Parent: 1, 2 (dark)',
234
- 'Child: 1, 2 (dark)',
235
- ]);
209
+ assertLog(['Parent: 1, 2 (dark)', 'Child: 1, 2 (dark)']);
210
211
// Both props and state bail out
212
act(() => {
@@ -241,10 +215,10 @@ describe('ReactHooks', () => {
215
root.update(<Parent theme="dark" />);
216
});
217
244
- expect(Scheduler).toHaveYielded(['Parent: 1, 2 (dark)']);
218
+ assertLog(['Parent: 1, 2 (dark)']);
219
});
220
247
- it('warns about setState second argument', () => {
221
+ it('warns about setState second argument', async () => {
222
const {useState} = React;
223
224
let setCounter;
@@ -258,7 +232,7 @@ describe('ReactHooks', () => {
232
233
const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
234
root.update(<Counter />);
261
- expect(Scheduler).toFlushAndYield(['Count: 0']);
235
+ await waitForAll(['Count: 0']);
236
expect(root).toMatchRenderedOutput('0');
237
238
expect(() => {
@@ -274,11 +248,11 @@ describe('ReactHooks', () => {
248
'declare it in the component body with useEffect().',
249
{withoutStack: true},
250
);
277
- expect(Scheduler).toHaveYielded(['Count: 1']);
251
+ assertLog(['Count: 1']);
252
expect(root).toMatchRenderedOutput('1');
253
});
254
281
- it('warns about dispatch second argument', () => {
255
+ it('warns about dispatch second argument', async () => {
256
const {useReducer} = React;
257
258
let dispatch;
@@ -292,7 +266,7 @@ describe('ReactHooks', () => {
266
267
const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
268
root.update(<Counter />);
295
- expect(Scheduler).toFlushAndYield(['Count: 0']);
269
+ await waitForAll(['Count: 0']);
270
expect(root).toMatchRenderedOutput('0');
271
272
expect(() => {
@@ -308,11 +282,11 @@ describe('ReactHooks', () => {
282
'declare it in the component body with useEffect().',
283
{withoutStack: true},
284
);
311
- expect(Scheduler).toHaveYielded(['Count: 1']);
285
+ assertLog(['Count: 1']);
286
expect(root).toMatchRenderedOutput('1');
287
});
288
315
- it('never bails out if context has changed', () => {
289
+ it('never bails out if context has changed', async () => {
290
const {useState, useLayoutEffect, useContext} = React;
291
292
const ThemeContext = React.createContext('light');
@@ -355,7 +329,7 @@ describe('ReactHooks', () => {
329
);
330
});
331
358
- expect(Scheduler).toHaveYielded([
332
+ assertLog([
333
'Theme: light',
334
'Parent: 0 (light)',
335
'Child: 0 (light)',
@@ -366,21 +340,17 @@ describe('ReactHooks', () => {
340
// Updating the theme to the same value doesn't cause the consumers
341
// to re-render.
342
setTheme('light');
369
- expect(Scheduler).toFlushAndYield([]);
343
+ await waitForAll([]);
344
expect(root).toMatchRenderedOutput('0 (light)');
345
346
// Normal update
347
act(() => setCounter(1));
374
- expect(Scheduler).toHaveYielded([
375
- 'Parent: 1 (light)',
376
- 'Child: 1 (light)',
377
- 'Effect: 1 (light)',
378
- ]);
348
+ assertLog(['Parent: 1 (light)', 'Child: 1 (light)', 'Effect: 1 (light)']);
349
expect(root).toMatchRenderedOutput('1 (light)');
350
351
// Update that doesn't change state, so it bails out
352
act(() => setCounter(1));
383
- expect(Scheduler).toHaveYielded(['Parent: 1 (light)']);
353
+ assertLog(['Parent: 1 (light)']);
354
expect(root).toMatchRenderedOutput('1 (light)');
355
356
// Update that doesn't change state, but the context changes, too, so it
@@ -390,7 +360,7 @@ describe('ReactHooks', () => {
360
setTheme('dark');
361
});
362
393
- expect(Scheduler).toHaveYielded([
363
+ assertLog([
364
'Theme: dark',
365
'Parent: 1 (dark)',
366
'Child: 1 (dark)',
@@ -399,7 +369,7 @@ describe('ReactHooks', () => {
369
expect(root).toMatchRenderedOutput('1 (dark)');
370
});
371
402
- it('can bail out without calling render phase (as an optimization) if queue is known to be empty', () => {
372
+ it('can bail out without calling render phase (as an optimization) if queue is known to be empty', async () => {
373
const {useState, useLayoutEffect} = React;
374
375
function Child({text}) {
@@ -420,12 +390,12 @@ describe('ReactHooks', () => {
390
391
const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
392
root.update(<Parent />);
423
- expect(Scheduler).toFlushAndYield(['Parent: 0', 'Child: 0', 'Effect: 0']);
393
+ await waitForAll(['Parent: 0', 'Child: 0', 'Effect: 0']);
394
expect(root).toMatchRenderedOutput('0');
395
396
// Normal update
397
act(() => setCounter(1));
428
- expect(Scheduler).toHaveYielded(['Parent: 1', 'Child: 1', 'Effect: 1']);
398
+ assertLog(['Parent: 1', 'Child: 1', 'Effect: 1']);
399
expect(root).toMatchRenderedOutput('1');
400
401
// Update to the same state. React doesn't know if the queue is empty
@@ -433,25 +403,25 @@ describe('ReactHooks', () => {
403
// enter the render phase before we can bail out. But we bail out before
404
// rendering the child, and we don't fire any effects.
405
act(() => setCounter(1));
436
- expect(Scheduler).toHaveYielded(['Parent: 1']);
406
+ assertLog(['Parent: 1']);
407
expect(root).toMatchRenderedOutput('1');
408
409
// Update to the same state again. This times, neither fiber has pending
410
// update priority, so we can bail out before even entering the render phase.
411
act(() => setCounter(1));
442
- expect(Scheduler).toFlushAndYield([]);
412
+ await waitForAll([]);
413
expect(root).toMatchRenderedOutput('1');
414
415
// This changes the state to something different so it renders normally.
416
act(() => setCounter(2));
447
- expect(Scheduler).toHaveYielded(['Parent: 2', 'Child: 2', 'Effect: 2']);
417
+ assertLog(['Parent: 2', 'Child: 2', 'Effect: 2']);
418
expect(root).toMatchRenderedOutput('2');
419
420
// prepare to check SameValue
421
act(() => {
422
setCounter(0);
423
});
454
- expect(Scheduler).toHaveYielded(['Parent: 0', 'Child: 0', 'Effect: 0']);
424
+ assertLog(['Parent: 0', 'Child: 0', 'Effect: 0']);
425
expect(root).toMatchRenderedOutput('0');
426
427
// Update to the same state for the first time to flush the queue
@@ -459,25 +429,25 @@ describe('ReactHooks', () => {
429
setCounter(0);
430
});
431
462
- expect(Scheduler).toHaveYielded(['Parent: 0']);
432
+ assertLog(['Parent: 0']);
433
expect(root).toMatchRenderedOutput('0');
434
435
// Update again to the same state. Should bail out.
436
act(() => {
437
setCounter(0);
438
});
469
- expect(Scheduler).toFlushAndYield([]);
439
+ await waitForAll([]);
440
expect(root).toMatchRenderedOutput('0');
441
442
// Update to a different state (positive 0 to negative 0)
443
act(() => {
444
setCounter(0 / -1);
445
});
476
- expect(Scheduler).toHaveYielded(['Parent: 0', 'Child: 0', 'Effect: 0']);
446
+ assertLog(['Parent: 0', 'Child: 0', 'Effect: 0']);
447
expect(root).toMatchRenderedOutput('0');
448
});
449
480
- it('bails out multiple times in a row without entering render phase', () => {
450
+ it('bails out multiple times in a row without entering render phase', async () => {
451
const {useState} = React;
452
453
function Child({text}) {
@@ -495,7 +465,7 @@ describe('ReactHooks', () => {
465
466
const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
467
root.update(<Parent />);
498
- expect(Scheduler).toFlushAndYield(['Parent: 0', 'Child: 0']);
468
+ await waitForAll(['Parent: 0', 'Child: 0']);
469
expect(root).toMatchRenderedOutput('0');
470
471
const update = value => {
@@ -515,7 +485,7 @@ describe('ReactHooks', () => {
485
update(3);
486
});
487
518
- expect(Scheduler).toHaveYielded([
488
+ assertLog([
489
// The first four updates were eagerly computed, because the queue is
490
// empty before each one.
491
'Compute state (0 -> 0)',
@@ -527,7 +497,7 @@ describe('ReactHooks', () => {
497
]);
498
499
// Now let's enter the render phase
530
- expect(Scheduler).toFlushAndYield([
500
+ await waitForAll([
501
// We don't need to re-compute the first four updates. Only the final two.
502
'Compute state (1 -> 2)',
503
'Compute state (2 -> 3)',
@@ -537,7 +507,7 @@ describe('ReactHooks', () => {
507
expect(root).toMatchRenderedOutput('3');
508
});
509
540
- it('can rebase on top of a previously skipped update', () => {
510
+ it('can rebase on top of a previously skipped update', async () => {
511
const {useState} = React;
512
513
function Child({text}) {
@@ -555,7 +525,7 @@ describe('ReactHooks', () => {
525
526
const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
527
root.update(<Parent />);
558
- expect(Scheduler).toFlushAndYield(['Parent: 1', 'Child: 1']);
528
+ await waitForAll(['Parent: 1', 'Child: 1']);
529
expect(root).toMatchRenderedOutput('1');
530
531
const update = compute => {
@@ -576,13 +546,13 @@ describe('ReactHooks', () => {
546
ReactTestRenderer.unstable_batchedUpdates(() => update(n => n * 100));
547
}
548
// The new state is eagerly computed.
579
- expect(Scheduler).toHaveYielded(['Compute state (1 -> 100)']);
549
+ assertLog(['Compute state (1 -> 100)']);
550
551
// but before it's flushed, a higher priority update interrupts it.
552
root.unstable_flushSync(() => {
553
update(n => n + 5);
554
});
585
- expect(Scheduler).toHaveYielded([
555
+ assertLog([
556
// The eagerly computed state was completely skipped
557
'Compute state (1 -> 6)',
558
'Parent: 6',
@@ -593,7 +563,7 @@ describe('ReactHooks', () => {
563
// Now when we finish the first update, the second update is rebased on top.
564
// Notice we didn't have to recompute the first update even though it was
565
// skipped in the previous render.
596
- expect(Scheduler).toFlushAndYield([
566
+ await waitForAll([
567
'Compute state (100 -> 105)',
568
'Parent: 105',
569
'Child: 105',
@@ -612,7 +582,7 @@ describe('ReactHooks', () => {
582
return props.dependencies;
583
}
584
const root = ReactTestRenderer.create(<App dependencies={['A']} />);
615
- expect(Scheduler).toHaveYielded(['Did commit: A']);
585
+ assertLog(['Did commit: A']);
586
expect(() => {
587
root.update(<App dependencies={['A', 'B']} />);
588
}).toErrorDev([
@@ -639,7 +609,7 @@ describe('ReactHooks', () => {
609
610
const root = ReactTestRenderer.create(null);
611
root.update(<App text="Hello" hasDeps={true} />);
642
- expect(Scheduler).toHaveYielded(['Compute']);
612
+ assertLog(['Compute']);
613
expect(root).toMatchRenderedOutput('HELLO');
614
615
expect(() => {
@@ -1841,7 +1811,7 @@ describe('ReactHooks', () => {
1811
);
1812
expect(root).toMatchRenderedOutput('loading');
1813
await Promise.resolve();
1844
- Scheduler.unstable_flushAll();
1814
+ await waitForAll([]);
1815
expect(root).toMatchRenderedOutput('hello');
1816
});
1817
@@ -1873,7 +1843,7 @@ describe('ReactHooks', () => {
1843
);
1844
expect(root).toMatchRenderedOutput('loading');
1845
await Promise.resolve();
1876
- Scheduler.unstable_flushAll();
1846
+ await waitForAll([]);
1847
expect(root).toMatchRenderedOutput('hello');
1848
});
1849
@@ -1905,7 +1875,7 @@ describe('ReactHooks', () => {
1875
);
1876
expect(root).toMatchRenderedOutput('loading');
1877
await Promise.resolve();
1908
- Scheduler.unstable_flushAll();
1878
+ await waitForAll([]);
1879
expect(root).toMatchRenderedOutput('hello');
1880
});
1881
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
+433
-594
@@ -35,6 +35,11 @@ let memo;
35
let act;
36
let ContinuousEventPriority;
37
let SuspenseList;
38
+let waitForAll;
39
+let waitFor;
40
+let waitForThrow;
41
+let waitForPaint;
42
+let assertLog;
43
44
describe('ReactHooksWithNoopRenderer', () => {
45
beforeEach(() => {
@@ -65,6 +70,13 @@ describe('ReactHooksWithNoopRenderer', () => {
70
SuspenseList = React.SuspenseList;
71
}
72
73
+ const InternalTestUtils = require('internal-test-utils');
74
+ waitForAll = InternalTestUtils.waitForAll;
75
+ waitFor = InternalTestUtils.waitFor;
76
+ waitForThrow = InternalTestUtils.waitForThrow;
77
+ waitForPaint = InternalTestUtils.waitForPaint;
78
+ assertLog = InternalTestUtils.assertLog;
79
+
80
textCache = new Map();
81
82
readText = text => {
@@ -151,7 +163,7 @@ describe('ReactHooksWithNoopRenderer', () => {
163
return Promise.resolve().then(() => {});
164
}
165
154
- it('resumes after an interruption', () => {
166
+ it('resumes after an interruption', async () => {
167
function Counter(props, ref) {
168
const [count, updateCount] = useState(0);
169
useImperativeHandle(ref, () => ({updateCount}));
@@ -162,33 +174,33 @@ describe('ReactHooksWithNoopRenderer', () => {
174
// Initial mount
175
const counter = React.createRef(null);
176
ReactNoop.render(<Counter label="Count" ref={counter} />);
165
- expect(Scheduler).toFlushAndYield(['Count: 0']);
177
+ await waitForAll(['Count: 0']);
178
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
179
180
// Schedule some updates
169
- act(() => {
181
+ await act(async () => {
182
React.startTransition(() => {
183
counter.current.updateCount(1);
184
counter.current.updateCount(count => count + 10);
185
});
186
187
// Partially flush without committing
176
- expect(Scheduler).toFlushAndYieldThrough(['Count: 11']);
188
+ await waitFor(['Count: 11']);
189
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
190
191
// Interrupt with a high priority update
192
ReactNoop.flushSync(() => {
193
ReactNoop.render(<Counter label="Total" />);
194
});
183
- expect(Scheduler).toHaveYielded(['Total: 0']);
195
+ assertLog(['Total: 0']);
196
197
// Resume rendering
186
- expect(Scheduler).toFlushAndYield(['Total: 11']);
198
+ await waitForAll(['Total: 11']);
199
expect(ReactNoop).toMatchRenderedOutput(<span prop="Total: 11" />);
200
});
201
});
202
191
- it('throws inside class components', () => {
203
+ it('throws inside class components', async () => {
204
class BadCounter extends React.Component {
205
render() {
206
const [count] = useState(0);
@@ -212,11 +224,11 @@ describe('ReactHooksWithNoopRenderer', () => {
224
return <Text text={count} />;
225
}
226
ReactNoop.render(<GoodCounter initialCount={10} />);
215
- expect(Scheduler).toFlushAndYield([10]);
227
+ await waitForAll([10]);
228
});
229
230
if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
219
- it('throws inside module-style components', () => {
231
+ it('throws inside module-style components', async () => {
232
function Counter() {
233
return {
234
render() {
@@ -249,11 +261,11 @@ describe('ReactHooksWithNoopRenderer', () => {
261
return <Text text={count} />;
262
}
263
ReactNoop.render(<GoodCounter initialCount={10} />);
252
- expect(Scheduler).toFlushAndYield([10]);
264
+ await waitForAll([10]);
265
});
266
}
267
256
- it('throws when called outside the render phase', () => {
268
+ it('throws when called outside the render phase', async () => {
269
expect(() => {
270
expect(() => useState(0)).toThrow(
271
"Cannot read property 'useState' of null",
@@ -270,7 +282,7 @@ describe('ReactHooksWithNoopRenderer', () => {
282
});
283
284
describe('useState', () => {
273
- it('simple mount and update', () => {
285
+ it('simple mount and update', async () => {
286
function Counter(props, ref) {
287
const [count, updateCount] = useState(0);
288
useImperativeHandle(ref, () => ({updateCount}));
@@ -279,19 +291,19 @@ describe('ReactHooksWithNoopRenderer', () => {
291
Counter = forwardRef(Counter);
292
const counter = React.createRef(null);
293
ReactNoop.render(<Counter ref={counter} />);
282
- expect(Scheduler).toFlushAndYield(['Count: 0']);
294
+ await waitForAll(['Count: 0']);
295
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
296
297
act(() => counter.current.updateCount(1));
286
- expect(Scheduler).toHaveYielded(['Count: 1']);
298
+ assertLog(['Count: 1']);
299
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
300
301
act(() => counter.current.updateCount(count => count + 10));
290
- expect(Scheduler).toHaveYielded(['Count: 11']);
302
+ assertLog(['Count: 11']);
303
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 11" />);
304
});
305
294
- it('lazy state initializer', () => {
306
+ it('lazy state initializer', async () => {
307
function Counter(props, ref) {
308
const [count, updateCount] = useState(() => {
309
Scheduler.unstable_yieldValue('getInitialState');
@@ -303,15 +315,15 @@ describe('ReactHooksWithNoopRenderer', () => {
315
Counter = forwardRef(Counter);
316
const counter = React.createRef(null);
317
ReactNoop.render(<Counter initialState={42} ref={counter} />);
306
- expect(Scheduler).toFlushAndYield(['getInitialState', 'Count: 42']);
318
+ await waitForAll(['getInitialState', 'Count: 42']);
319
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 42" />);
320
321
act(() => counter.current.updateCount(7));
310
- expect(Scheduler).toHaveYielded(['Count: 7']);
322
+ assertLog(['Count: 7']);
323
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 7" />);
324
});
325
314
- it('multiple states', () => {
326
+ it('multiple states', async () => {
327
function Counter(props, ref) {
328
const [count, updateCount] = useState(0);
329
const [label, updateLabel] = useState('Count');
@@ -321,17 +333,17 @@ describe('ReactHooksWithNoopRenderer', () => {
333
Counter = forwardRef(Counter);
334
const counter = React.createRef(null);
335
ReactNoop.render(<Counter ref={counter} />);
324
- expect(Scheduler).toFlushAndYield(['Count: 0']);
336
+ await waitForAll(['Count: 0']);
337
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
338
339
act(() => counter.current.updateCount(7));
328
- expect(Scheduler).toHaveYielded(['Count: 7']);
340
+ assertLog(['Count: 7']);
341
342
act(() => counter.current.updateLabel('Total'));
331
- expect(Scheduler).toHaveYielded(['Total: 7']);
343
+ assertLog(['Total: 7']);
344
});
345
334
- it('returns the same updater function every time', () => {
346
+ it('returns the same updater function every time', async () => {
347
let updater = null;
348
function Counter() {
349
const [count, updateCount] = useState(0);
@@ -339,25 +351,25 @@ describe('ReactHooksWithNoopRenderer', () => {
351
return <Text text={'Count: ' + count} />;
352
}
353
ReactNoop.render(<Counter />);
342
- expect(Scheduler).toFlushAndYield(['Count: 0']);
354
+ await waitForAll(['Count: 0']);
355
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
356
357
const firstUpdater = updater;
358
359
act(() => firstUpdater(1));
348
- expect(Scheduler).toHaveYielded(['Count: 1']);
360
+ assertLog(['Count: 1']);
361
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
362
363
const secondUpdater = updater;
364
365
act(() => firstUpdater(count => count + 10));
354
- expect(Scheduler).toHaveYielded(['Count: 11']);
366
+ assertLog(['Count: 11']);
367
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 11" />);
368
369
expect(firstUpdater).toBe(secondUpdater);
370
});
371
360
- it('does not warn on set after unmount', () => {
372
+ it('does not warn on set after unmount', async () => {
373
let _updateCount;
374
function Counter(props, ref) {
375
const [, updateCount] = useState(0);
@@ -366,13 +378,13 @@ describe('ReactHooksWithNoopRenderer', () => {
378
}
379
380
ReactNoop.render(<Counter />);
369
- expect(Scheduler).toFlushWithoutYielding();
381
+ await waitForAll([]);
382
ReactNoop.render(null);
371
- expect(Scheduler).toFlushWithoutYielding();
383
+ await waitForAll([]);
384
act(() => _updateCount(1));
385
});
386
375
- it('works with memo', () => {
387
+ it('works with memo', async () => {
388
let _updateCount;
389
function Counter(props) {
390
const [count, updateCount] = useState(0);
@@ -382,21 +394,21 @@ describe('ReactHooksWithNoopRenderer', () => {
394
Counter = memo(Counter);
395
396
ReactNoop.render(<Counter />);
385
- expect(Scheduler).toFlushAndYield(['Count: 0']);
397
+ await waitForAll(['Count: 0']);
398
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
399
400
ReactNoop.render(<Counter />);
389
- expect(Scheduler).toFlushAndYield([]);
401
+ await waitForAll([]);
402
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
403
404
act(() => _updateCount(1));
393
- expect(Scheduler).toHaveYielded(['Count: 1']);
405
+ assertLog(['Count: 1']);
406
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
407
});
408
});
409
410
describe('updates during the render phase', () => {
399
- it('restarts the render function and applies the new updates on top', () => {
411
+ it('restarts the render function and applies the new updates on top', async () => {
412
function ScrollView({row: newRow}) {
413
const [isScrollingDown, setIsScrollingDown] = useState(false);
414
const [row, setRow] = useState(null);
@@ -411,37 +423,37 @@ describe('ReactHooksWithNoopRenderer', () => {
423
}
424
425
ReactNoop.render(<ScrollView row={1} />);
414
- expect(Scheduler).toFlushAndYield(['Scrolling down: false']);
426
+ await waitForAll(['Scrolling down: false']);
427
expect(ReactNoop).toMatchRenderedOutput(
428
<span prop="Scrolling down: false" />,
429
);
430
431
ReactNoop.render(<ScrollView row={5} />);
420
- expect(Scheduler).toFlushAndYield(['Scrolling down: true']);
432
+ await waitForAll(['Scrolling down: true']);
433
expect(ReactNoop).toMatchRenderedOutput(
434
<span prop="Scrolling down: true" />,
435
);
436
437
ReactNoop.render(<ScrollView row={5} />);
426
- expect(Scheduler).toFlushAndYield(['Scrolling down: true']);
438
+ await waitForAll(['Scrolling down: true']);
439
expect(ReactNoop).toMatchRenderedOutput(
440
<span prop="Scrolling down: true" />,
441
);
442
443
ReactNoop.render(<ScrollView row={10} />);
432
- expect(Scheduler).toFlushAndYield(['Scrolling down: true']);
444
+ await waitForAll(['Scrolling down: true']);
445
expect(ReactNoop).toMatchRenderedOutput(
446
<span prop="Scrolling down: true" />,
447
);
448
449
ReactNoop.render(<ScrollView row={2} />);
438
- expect(Scheduler).toFlushAndYield(['Scrolling down: false']);
450
+ await waitForAll(['Scrolling down: false']);
451
expect(ReactNoop).toMatchRenderedOutput(
452
<span prop="Scrolling down: false" />,
453
);
454
455
ReactNoop.render(<ScrollView row={2} />);
444
- expect(Scheduler).toFlushAndYield(['Scrolling down: false']);
456
+ await waitForAll(['Scrolling down: false']);
457
expect(ReactNoop).toMatchRenderedOutput(
458
<span prop="Scrolling down: false" />,
459
);
@@ -472,7 +484,7 @@ describe('ReactHooksWithNoopRenderer', () => {
484
</>,
485
);
486
});
475
- expect(Scheduler).toHaveYielded(['Foo [0]', 'Bar']);
487
+ assertLog(['Foo [0]', 'Bar']);
488
489
// Bar will update Foo during its render phase. React should warn.
490
await act(async () => {
@@ -498,11 +510,11 @@ describe('ReactHooksWithNoopRenderer', () => {
510
<Bar triggerUpdate={true} />
511
</>,
512
);
501
- expect(Scheduler).toFlushAndYield(['Foo [1]', 'Bar', 'Foo [2]']);
513
+ await waitForAll(['Foo [1]', 'Bar', 'Foo [2]']);
514
});
515
});
516
505
- it('keeps restarting until there are no more new updates', () => {
517
+ it('keeps restarting until there are no more new updates', async () => {
518
function Counter({row: newRow}) {
519
const [count, setCount] = useState(0);
520
if (count < 3) {
@@ -513,17 +525,11 @@ describe('ReactHooksWithNoopRenderer', () => {
525
}
526
527
ReactNoop.render(<Counter />);
516
- expect(Scheduler).toFlushAndYield([
517
- 'Render: 0',
518
- 'Render: 1',
519
- 'Render: 2',
520
- 'Render: 3',
521
- 3,
522
- ]);
528
+ await waitForAll(['Render: 0', 'Render: 1', 'Render: 2', 'Render: 3', 3]);
529
expect(ReactNoop).toMatchRenderedOutput(<span prop={3} />);
530
});
531
526
- it('updates multiple times within same render function', () => {
532
+ it('updates multiple times within same render function', async () => {
533
function Counter({row: newRow}) {
534
const [count, setCount] = useState(0);
535
if (count < 12) {
@@ -536,7 +542,7 @@ describe('ReactHooksWithNoopRenderer', () => {
542
}
543
544
ReactNoop.render(<Counter />);
539
- expect(Scheduler).toFlushAndYield([
545
+ await waitForAll([
546
// Should increase by three each time
547
'Render: 0',
548
'Render: 3',
@@ -548,7 +554,7 @@ describe('ReactHooksWithNoopRenderer', () => {
554
expect(ReactNoop).toMatchRenderedOutput(<span prop={12} />);
555
});
556
551
- it('throws after too many iterations', () => {
557
+ it('throws after too many iterations', async () => {
558
function Counter({row: newRow}) {
559
const [count, setCount] = useState(0);
560
setCount(count + 1);
@@ -562,7 +568,7 @@ describe('ReactHooksWithNoopRenderer', () => {
568
);
569
});
570
565
- it('works with useReducer', () => {
571
+ it('works with useReducer', async () => {
572
function reducer(state, action) {
573
return action === 'increment' ? state + 1 : state;
574
}
@@ -576,17 +582,11 @@ describe('ReactHooksWithNoopRenderer', () => {
582
}
583
584
ReactNoop.render(<Counter />);
579
- expect(Scheduler).toFlushAndYield([
580
- 'Render: 0',
581
- 'Render: 1',
582
- 'Render: 2',
583
- 'Render: 3',
584
- 3,
585
- ]);
585
+ await waitForAll(['Render: 0', 'Render: 1', 'Render: 2', 'Render: 3', 3]);
586
expect(ReactNoop).toMatchRenderedOutput(<span prop={3} />);
587
});
588
589
- it('uses reducer passed at time of render, not time of dispatch', () => {
589
+ it('uses reducer passed at time of render, not time of dispatch', async () => {
590
// This test is a bit contrived but it demonstrates a subtle edge case.
591
592
// Reducer A increments by 1. Reducer B increments by 10.
@@ -626,7 +626,7 @@ describe('ReactHooksWithNoopRenderer', () => {
626
Counter = forwardRef(Counter);
627
const counter = React.createRef(null);
628
ReactNoop.render(<Counter ref={counter} />);
629
- expect(Scheduler).toFlushAndYield([
629
+ await waitForAll([
630
// The count should increase by alternating amounts of 10 and 1
631
// until we reach 21.
632
'Render: 0',
@@ -643,7 +643,7 @@ describe('ReactHooksWithNoopRenderer', () => {
643
counter.current.dispatch('reset');
644
});
645
ReactNoop.render(<Counter ref={counter} />);
646
- expect(Scheduler).toHaveYielded([
646
+ assertLog([
647
'Render: 0',
648
'Render: 1',
649
'Render: 11',
@@ -686,20 +686,20 @@ describe('ReactHooksWithNoopRenderer', () => {
686
const root = ReactNoop.createRoot();
687
root.render(<Foo signal={true} />);
688
689
- expect(Scheduler).toFlushAndYield([0]);
689
+ await waitForAll([0]);
690
expect(root).toMatchRenderedOutput(<span prop={0} />);
691
692
React.startTransition(() => {
693
root.render(<Foo signal={false} />);
694
});
695
- expect(Scheduler).toFlushAndYield(['Suspend!']);
695
+ await waitForAll(['Suspend!']);
696
expect(root).toMatchRenderedOutput(<span prop={0} />);
697
698
// Rendering again should suspend again.
699
React.startTransition(() => {
700
root.render(<Foo signal={false} />);
701
});
702
- expect(Scheduler).toFlushAndYield(['Suspend!']);
702
+ await waitForAll(['Suspend!']);
703
});
704
705
it('discards render phase updates if something suspends, but not other updates in the same component', async () => {
@@ -740,7 +740,7 @@ describe('ReactHooksWithNoopRenderer', () => {
740
const root = ReactNoop.createRoot();
741
root.render(<Foo signal={true} />);
742
743
- expect(Scheduler).toFlushAndYield(['A:0']);
743
+ await waitForAll(['A:0']);
744
expect(root).toMatchRenderedOutput(<span prop="A:0" />);
745
746
await act(async () => {
@@ -749,21 +749,21 @@ describe('ReactHooksWithNoopRenderer', () => {
749
setLabel('B');
750
});
751
752
- expect(Scheduler).toFlushAndYield(['Suspend!']);
752
+ await waitForAll(['Suspend!']);
753
expect(root).toMatchRenderedOutput(<span prop="A:0" />);
754
755
// Rendering again should suspend again.
756
React.startTransition(() => {
757
root.render(<Foo signal={false} />);
758
});
759
- expect(Scheduler).toFlushAndYield(['Suspend!']);
759
+ await waitForAll(['Suspend!']);
760
761
// Flip the signal back to "cancel" the update. However, the update to
762
// label should still proceed. It shouldn't have been dropped.
763
React.startTransition(() => {
764
root.render(<Foo signal={true} />);
765
});
766
- expect(Scheduler).toFlushAndYield(['B:0']);
766
+ await waitForAll(['B:0']);
767
expect(root).toMatchRenderedOutput(<span prop="B:0" />);
768
});
769
});
@@ -790,7 +790,7 @@ describe('ReactHooksWithNoopRenderer', () => {
790
await act(async () => {
791
root.render(<ScrollView row={10} />);
792
});
793
- expect(Scheduler).toHaveYielded(['Up']);
793
+ assertLog(['Up']);
794
expect(root).toMatchRenderedOutput(<span prop="Up" />);
795
796
await act(async () => {
@@ -801,7 +801,7 @@ describe('ReactHooksWithNoopRenderer', () => {
801
setRow(20);
802
});
803
});
804
- expect(Scheduler).toHaveYielded(['Up', 'Down']);
804
+ assertLog(['Up', 'Down']);
805
expect(root).toMatchRenderedOutput(<span prop="Down" />);
806
});
807
@@ -821,13 +821,13 @@ describe('ReactHooksWithNoopRenderer', () => {
821
822
const root = ReactNoop.createRoot();
823
root.render(<App />);
824
- expect(Scheduler).toFlushAndYield([1]);
824
+ await waitForAll([1]);
825
expect(root).toMatchRenderedOutput(<span prop={1} />);
826
});
827
});
828
829
describe('useReducer', () => {
830
- it('simple mount and update', () => {
830
+ it('simple mount and update', async () => {
831
const INCREMENT = 'INCREMENT';
832
const DECREMENT = 'DECREMENT';
833
@@ -850,11 +850,11 @@ describe('ReactHooksWithNoopRenderer', () => {
850
Counter = forwardRef(Counter);
851
const counter = React.createRef(null);
852
ReactNoop.render(<Counter ref={counter} />);
853
- expect(Scheduler).toFlushAndYield(['Count: 0']);
853
+ await waitForAll(['Count: 0']);
854
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
855
856
act(() => counter.current.dispatch(INCREMENT));
857
- expect(Scheduler).toHaveYielded(['Count: 1']);
857
+ assertLog(['Count: 1']);
858
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
859
act(() => {
860
counter.current.dispatch(DECREMENT);
@@ -862,11 +862,11 @@ describe('ReactHooksWithNoopRenderer', () => {
862
counter.current.dispatch(DECREMENT);
863
});
864
865
- expect(Scheduler).toHaveYielded(['Count: -2']);
865
+ assertLog(['Count: -2']);
866
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: -2" />);
867
});
868
869
- it('lazy init', () => {
869
+ it('lazy init', async () => {
870
const INCREMENT = 'INCREMENT';
871
const DECREMENT = 'DECREMENT';
872
@@ -892,11 +892,11 @@ describe('ReactHooksWithNoopRenderer', () => {
892
Counter = forwardRef(Counter);
893
const counter = React.createRef(null);
894
ReactNoop.render(<Counter initialCount={10} ref={counter} />);
895
- expect(Scheduler).toFlushAndYield(['Init', 'Count: 10']);
895
+ await waitForAll(['Init', 'Count: 10']);
896
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 10" />);
897
898
act(() => counter.current.dispatch(INCREMENT));
899
- expect(Scheduler).toHaveYielded(['Count: 11']);
899
+ assertLog(['Count: 11']);
900
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 11" />);
901
902
act(() => {
@@ -905,12 +905,12 @@ describe('ReactHooksWithNoopRenderer', () => {
905
counter.current.dispatch(DECREMENT);
906
});
907
908
- expect(Scheduler).toHaveYielded(['Count: 8']);
908
+ assertLog(['Count: 8']);
909
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 8" />);
910
});
911
912
// Regression test for https://github.com/facebook/react/issues/14360
913
- it('handles dispatches with mixed priorities', () => {
913
+ it('handles dispatches with mixed priorities', async () => {
914
const INCREMENT = 'INCREMENT';
915
916
function reducer(state, action) {
@@ -927,7 +927,7 @@ describe('ReactHooksWithNoopRenderer', () => {
927
const counter = React.createRef(null);
928
ReactNoop.render(<Counter ref={counter} />);
929
930
- expect(Scheduler).toFlushAndYield(['Count: 0']);
930
+ await waitForAll(['Count: 0']);
931
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
932
933
ReactNoop.batchedUpdates(() => {
@@ -940,47 +940,47 @@ describe('ReactHooksWithNoopRenderer', () => {
940
counter.current.dispatch(INCREMENT);
941
});
942
if (gate(flags => flags.enableUnifiedSyncLane)) {
943
- expect(Scheduler).toHaveYielded(['Count: 4']);
943
+ assertLog(['Count: 4']);
944
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 4" />);
945
} else {
946
- expect(Scheduler).toHaveYielded(['Count: 1']);
946
+ assertLog(['Count: 1']);
947
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
948
- expect(Scheduler).toFlushAndYield(['Count: 4']);
948
+ await waitForAll(['Count: 4']);
949
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 4" />);
950
}
951
});
952
});
953
954
describe('useEffect', () => {
955
- it('simple mount and update', () => {
955
+ it('simple mount and update', async () => {
956
function Counter(props) {
957
useEffect(() => {
958
Scheduler.unstable_yieldValue(`Passive effect [${props.count}]`);
959
});
960
return <Text text={'Count: ' + props.count} />;
961
}
962
- act(() => {
962
+ await act(async () => {
963
ReactNoop.render(<Counter count={0} />, () =>
964
Scheduler.unstable_yieldValue('Sync effect'),
965
);
966
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
966
+ await waitFor(['Count: 0', 'Sync effect']);
967
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
968
// Effects are deferred until after the commit
969
- expect(Scheduler).toFlushAndYield(['Passive effect [0]']);
969
+ await waitForAll(['Passive effect [0]']);
970
});
971
972
- act(() => {
972
+ await act(async () => {
973
ReactNoop.render(<Counter count={1} />, () =>
974
Scheduler.unstable_yieldValue('Sync effect'),
975
);
976
- expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
976
+ await waitFor(['Count: 1', 'Sync effect']);
977
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
978
// Effects are deferred until after the commit
979
- expect(Scheduler).toFlushAndYield(['Passive effect [1]']);
979
+ await waitForAll(['Passive effect [1]']);
980
});
981
});
982
983
- it('flushes passive effects even with sibling deletions', () => {
983
+ it('flushes passive effects even with sibling deletions', async () => {
984
function LayoutEffect(props) {
985
useLayoutEffect(() => {
986
Scheduler.unstable_yieldValue(`Layout effect`);
@@ -994,13 +994,9 @@ describe('ReactHooksWithNoopRenderer', () => {
994
return <Text text="Passive" />;
995
}
996
const passive = <PassiveEffect key="p" />;
997
- act(() => {
997
+ await act(async () => {
998
ReactNoop.render([<LayoutEffect key="l" />, passive]);
999
- expect(Scheduler).toFlushAndYieldThrough([
1000
- 'Layout',
1001
- 'Passive',
1002
- 'Layout effect',
1003
- ]);
999
+ await waitFor(['Layout', 'Passive', 'Layout effect']);
1000
expect(ReactNoop).toMatchRenderedOutput(
1001
<>
1002
<span prop="Layout" />
@@ -1010,14 +1006,14 @@ describe('ReactHooksWithNoopRenderer', () => {
1006
// Destroying the first child shouldn't prevent the passive effect from
1007
// being executed
1008
ReactNoop.render([passive]);
1013
- expect(Scheduler).toFlushAndYield(['Passive effect']);
1009
+ await waitForAll(['Passive effect']);
1010
expect(ReactNoop).toMatchRenderedOutput(<span prop="Passive" />);
1011
});
1012
// exiting act calls flushPassiveEffects(), but there are none left to flush.
1017
- expect(Scheduler).toHaveYielded([]);
1013
+ assertLog([]);
1014
});
1015
1020
- it('flushes passive effects even if siblings schedule an update', () => {
1016
+ it('flushes passive effects even if siblings schedule an update', async () => {
1017
function PassiveEffect(props) {
1018
useEffect(() => {
1019
Scheduler.unstable_yieldValue('Passive effect');
@@ -1038,8 +1034,8 @@ describe('ReactHooksWithNoopRenderer', () => {
1034
1035
ReactNoop.render([<PassiveEffect key="p" />, <LayoutEffect key="l" />]);
1036
1041
- act(() => {
1042
- expect(Scheduler).toFlushAndYield([
1037
+ await act(async () => {
1038
+ await waitForAll([
1039
'Passive',
1040
'Layout',
1041
'Layout effect 0',
@@ -1057,7 +1053,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1053
);
1054
});
1055
1060
- it('flushes passive effects even if siblings schedule a new root', () => {
1056
+ it('flushes passive effects even if siblings schedule a new root', async () => {
1057
function PassiveEffect(props) {
1058
useEffect(() => {
1059
Scheduler.unstable_yieldValue('Passive effect');
@@ -1072,9 +1068,9 @@ describe('ReactHooksWithNoopRenderer', () => {
1068
});
1069
return <Text text="Layout" />;
1070
}
1075
- act(() => {
1071
+ await act(async () => {
1072
ReactNoop.render([<PassiveEffect key="p" />, <LayoutEffect key="l" />]);
1077
- expect(Scheduler).toFlushAndYield([
1073
+ await waitForAll([
1074
'Passive',
1075
'Layout',
1076
'Layout effect',
@@ -1093,7 +1089,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1089
it(
1090
'flushes effects serially by flushing old effects before flushing ' +
1091
"new ones, if they haven't already fired",
1096
- () => {
1092
+ async () => {
1093
function getCommittedText() {
1094
const children = ReactNoop.getChildrenAsJSX();
1095
if (children === null) {
@@ -1110,17 +1106,17 @@ describe('ReactHooksWithNoopRenderer', () => {
1106
});
1107
return <Text text={props.count} />;
1108
}
1113
- act(() => {
1109
+ await act(async () => {
1110
ReactNoop.render(<Counter count={0} />, () =>
1111
Scheduler.unstable_yieldValue('Sync effect'),
1112
);
1117
- expect(Scheduler).toFlushAndYieldThrough([0, 'Sync effect']);
1113
+ await waitFor([0, 'Sync effect']);
1114
expect(ReactNoop).toMatchRenderedOutput(<span prop={0} />);
1115
// Before the effects have a chance to flush, schedule another update
1116
ReactNoop.render(<Counter count={1} />, () =>
1117
Scheduler.unstable_yieldValue('Sync effect'),
1118
);
1123
- expect(Scheduler).toFlushAndYieldThrough([
1119
+ await waitFor([
1120
// The previous effect flushes before the reconciliation
1121
'Committed state when effect was fired: 0',
1122
1,
@@ -1129,13 +1125,11 @@ describe('ReactHooksWithNoopRenderer', () => {
1125
expect(ReactNoop).toMatchRenderedOutput(<span prop={1} />);
1126
});
1127
1132
- expect(Scheduler).toHaveYielded([
1133
- 'Committed state when effect was fired: 1',
1134
- ]);
1128
+ assertLog(['Committed state when effect was fired: 1']);
1129
},
1130
);
1131
1138
- it('defers passive effect destroy functions during unmount', () => {
1132
+ it('defers passive effect destroy functions during unmount', async () => {
1133
function Child({bar, foo}) {
1134
React.useEffect(() => {
1135
Scheduler.unstable_yieldValue('passive bar create');
@@ -1165,61 +1159,52 @@ describe('ReactHooksWithNoopRenderer', () => {
1159
return null;
1160
}
1161
1168
- act(() => {
1162
+ await act(async () => {
1163
ReactNoop.render(<Child bar={1} foo={1} />, () =>
1164
Scheduler.unstable_yieldValue('Sync effect'),
1165
);
1172
- expect(Scheduler).toFlushAndYieldThrough([
1166
+ await waitFor([
1167
'render',
1168
'layout bar create',
1169
'layout foo create',
1170
'Sync effect',
1171
]);
1172
// Effects are deferred until after the commit
1179
- expect(Scheduler).toFlushAndYield([
1180
- 'passive bar create',
1181
- 'passive foo create',
1182
- ]);
1173
+ await waitForAll(['passive bar create', 'passive foo create']);
1174
});
1175
1176
// This update exists to test an internal implementation detail:
1177
// Effects without updating dependencies lose their layout/passive tag during an update.
1187
- act(() => {
1178
+ await act(async () => {
1179
ReactNoop.render(<Child bar={1} foo={2} />, () =>
1180
Scheduler.unstable_yieldValue('Sync effect'),
1181
);
1191
- expect(Scheduler).toFlushAndYieldThrough([
1182
+ await waitFor([
1183
'render',
1184
'layout foo destroy',
1185
'layout foo create',
1186
'Sync effect',
1187
]);
1188
// Effects are deferred until after the commit
1198
- expect(Scheduler).toFlushAndYield([
1199
- 'passive foo destroy',
1200
- 'passive foo create',
1201
- ]);
1189
+ await waitForAll(['passive foo destroy', 'passive foo create']);
1190
});
1191
1192
// Unmount the component and verify that passive destroy functions are deferred until post-commit.
1205
- act(() => {
1193
+ await act(async () => {
1194
ReactNoop.render(null, () =>
1195
Scheduler.unstable_yieldValue('Sync effect'),
1196
);
1209
- expect(Scheduler).toFlushAndYieldThrough([
1197
+ await waitFor([
1198
'layout bar destroy',
1199
'layout foo destroy',
1200
'Sync effect',
1201
]);
1202
// Effects are deferred until after the commit
1215
- expect(Scheduler).toFlushAndYield([
1216
- 'passive bar destroy',
1217
- 'passive foo destroy',
1218
- ]);
1203
+ await waitForAll(['passive bar destroy', 'passive foo destroy']);
1204
});
1205
});
1206
1222
- it('does not warn about state updates for unmounted components with pending passive unmounts', () => {
1207
+ it('does not warn about state updates for unmounted components with pending passive unmounts', async () => {
1208
let completePendingRequest = null;
1209
function Component() {
1210
Scheduler.unstable_yieldValue('Component');
@@ -1241,32 +1226,28 @@ describe('ReactHooksWithNoopRenderer', () => {
1226
return didLoad;
1227
}
1228
1244
- act(() => {
1229
+ await act(async () => {
1230
ReactNoop.renderToRootWithID(<Component />, 'root', () =>
1231
Scheduler.unstable_yieldValue('Sync effect'),
1232
);
1248
- expect(Scheduler).toFlushAndYieldThrough([
1249
- 'Component',
1250
- 'layout create',
1251
- 'Sync effect',
1252
- ]);
1233
+ await waitFor(['Component', 'layout create', 'Sync effect']);
1234
ReactNoop.flushPassiveEffects();
1254
- expect(Scheduler).toHaveYielded(['passive create']);
1235
+ assertLog(['passive create']);
1236
1237
// Unmount but don't process pending passive destroy function
1238
ReactNoop.unmountRootWithID('root');
1258
- expect(Scheduler).toFlushAndYieldThrough(['layout destroy']);
1239
+ await waitFor(['layout destroy']);
1240
1241
// Simulate an XHR completing, which will cause a state update-
1242
// but should not log a warning.
1243
completePendingRequest();
1244
1245
ReactNoop.flushPassiveEffects();
1265
- expect(Scheduler).toHaveYielded(['passive destroy']);
1246
+ assertLog(['passive destroy']);
1247
});
1248
});
1249
1269
- it('does not warn about state updates for unmounted components with pending passive unmounts for alternates', () => {
1250
+ it('does not warn about state updates for unmounted components with pending passive unmounts for alternates', async () => {
1251
let setParentState = null;
1252
const setChildStates = [];
1253
@@ -1309,9 +1290,9 @@ describe('ReactHooksWithNoopRenderer', () => {
1290
// later tick: schedule unmount for parent
1291
// start process unmount (but don't flush passive effectS)
1292
// State update on child
1312
- act(() => {
1293
+ await act(async () => {
1294
ReactNoop.render(<Parent />);
1314
- expect(Scheduler).toFlushAndYieldThrough([
1295
+ await waitFor([
1296
'Parent true render',
1297
'Child one render',
1298
'Child two render',
@@ -1324,7 +1305,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1305
1306
// Update children.
1307
setChildStates.forEach(setChildState => setChildState(1));
1327
- expect(Scheduler).toFlushAndYieldThrough([
1308
+ await waitFor([
1309
'Child one render',
1310
'Child two render',
1311
'Child one commit',
@@ -1335,27 +1316,24 @@ describe('ReactHooksWithNoopRenderer', () => {
1316
React.startTransition(() => {
1317
setChildStates.forEach(setChildState => setChildState(2));
1318
});
1338
- expect(Scheduler).toFlushAndYieldThrough(['Child one render']);
1319
+ await waitFor(['Child one render']);
1320
1321
// Schedule unmount for the parent that unmounts children with pending update.
1322
ReactNoop.unstable_runWithPriority(ContinuousEventPriority, () => {
1323
setParentState(false);
1324
});
1344
- expect(Scheduler).toFlushUntilNextPaint([
1345
- 'Parent false render',
1346
- 'Parent false commit',
1347
- ]);
1325
+ await waitForPaint(['Parent false render', 'Parent false commit']);
1326
1327
// Schedule updates for children too (which should be ignored)
1328
setChildStates.forEach(setChildState => setChildState(2));
1351
- expect(Scheduler).toFlushAndYield([
1329
+ await waitForAll([
1330
'Child one passive destroy',
1331
'Child two passive destroy',
1332
]);
1333
});
1334
});
1335
1358
- it('does not warn about state updates for unmounted components with no pending passive unmounts', () => {
1336
+ it('does not warn about state updates for unmounted components with no pending passive unmounts', async () => {
1337
let completePendingRequest = null;
1338
function Component() {
1339
Scheduler.unstable_yieldValue('Component');
@@ -1371,26 +1349,22 @@ describe('ReactHooksWithNoopRenderer', () => {
1349
return didLoad;
1350
}
1351
1374
- act(() => {
1352
+ await act(async () => {
1353
ReactNoop.renderToRootWithID(<Component />, 'root', () =>
1354
Scheduler.unstable_yieldValue('Sync effect'),
1355
);
1378
- expect(Scheduler).toFlushAndYieldThrough([
1379
- 'Component',
1380
- 'layout create',
1381
- 'Sync effect',
1382
- ]);
1356
+ await waitFor(['Component', 'layout create', 'Sync effect']);
1357
1358
// Unmount but don't process pending passive destroy function
1359
ReactNoop.unmountRootWithID('root');
1386
- expect(Scheduler).toFlushAndYieldThrough(['layout destroy']);
1360
+ await waitFor(['layout destroy']);
1361
1362
// Simulate an XHR completing.
1363
completePendingRequest();
1364
});
1365
});
1366
1393
- it('does not warn if there are pending passive unmount effects but not for the current fiber', () => {
1367
+ it('does not warn if there are pending passive unmount effects but not for the current fiber', async () => {
1368
let completePendingRequest = null;
1369
function ComponentWithXHR() {
1370
Scheduler.unstable_yieldValue('Component');
@@ -1419,7 +1393,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1393
return null;
1394
}
1395
1422
- act(() => {
1396
+ await act(async () => {
1397
ReactNoop.renderToRootWithID(
1398
<>
1399
<ComponentWithXHR />
@@ -1428,27 +1402,20 @@ describe('ReactHooksWithNoopRenderer', () => {
1402
'root',
1403
() => Scheduler.unstable_yieldValue('Sync effect'),
1404
);
1431
- expect(Scheduler).toFlushAndYieldThrough([
1432
- 'Component',
1433
- 'a:layout create',
1434
- 'Sync effect',
1435
- ]);
1405
+ await waitFor(['Component', 'a:layout create', 'Sync effect']);
1406
ReactNoop.flushPassiveEffects();
1437
- expect(Scheduler).toHaveYielded([
1438
- 'a:passive create',
1439
- 'b:passive create',
1440
- ]);
1407
+ assertLog(['a:passive create', 'b:passive create']);
1408
1409
// Unmount but don't process pending passive destroy function
1410
ReactNoop.unmountRootWithID('root');
1444
- expect(Scheduler).toFlushAndYieldThrough(['a:layout destroy']);
1411
+ await waitFor(['a:layout destroy']);
1412
1413
// Simulate an XHR completing in the component without a pending passive effect..
1414
completePendingRequest();
1415
});
1416
});
1417
1451
- it('does not warn if there are updates after pending passive unmount effects have been flushed', () => {
1418
+ it('does not warn if there are updates after pending passive unmount effects have been flushed', async () => {
1419
let updaterFunction;
1420
1421
function Component() {
@@ -1469,21 +1436,17 @@ describe('ReactHooksWithNoopRenderer', () => {
1436
Scheduler.unstable_yieldValue('Sync effect'),
1437
);
1438
});
1472
- expect(Scheduler).toHaveYielded([
1473
- 'Component',
1474
- 'Sync effect',
1475
- 'passive create',
1476
- ]);
1439
+ assertLog(['Component', 'Sync effect', 'passive create']);
1440
1441
ReactNoop.unmountRootWithID('root');
1479
- expect(Scheduler).toFlushAndYield(['passive destroy']);
1442
+ await waitForAll(['passive destroy']);
1443
1444
act(() => {
1445
updaterFunction(true);
1446
});
1447
});
1448
1486
- it('does not show a warning when a component updates its own state from within passive unmount function', () => {
1449
+ it('does not show a warning when a component updates its own state from within passive unmount function', async () => {
1450
function Component() {
1451
Scheduler.unstable_yieldValue('Component');
1452
const [didLoad, setDidLoad] = React.useState(false);
@@ -1497,23 +1460,19 @@ describe('ReactHooksWithNoopRenderer', () => {
1460
return didLoad;
1461
}
1462
1500
- act(() => {
1463
+ await act(async () => {
1464
ReactNoop.renderToRootWithID(<Component />, 'root', () =>
1465
Scheduler.unstable_yieldValue('Sync effect'),
1466
);
1504
- expect(Scheduler).toFlushAndYieldThrough([
1505
- 'Component',
1506
- 'Sync effect',
1507
- 'passive create',
1508
- ]);
1467
+ await waitFor(['Component', 'Sync effect', 'passive create']);
1468
1469
// Unmount but don't process pending passive destroy function
1470
ReactNoop.unmountRootWithID('root');
1512
- expect(Scheduler).toFlushAndYield(['passive destroy']);
1471
+ await waitForAll(['passive destroy']);
1472
});
1473
});
1474
1516
- it('does not show a warning when a component updates a child state from within passive unmount function', () => {
1475
+ it('does not show a warning when a component updates a child state from within passive unmount function', async () => {
1476
function Parent() {
1477
Scheduler.unstable_yieldValue('Parent');
1478
const updaterRef = useRef(null);
@@ -1537,9 +1496,9 @@ describe('ReactHooksWithNoopRenderer', () => {
1496
return state;
1497
}
1498
1540
- act(() => {
1499
+ await act(async () => {
1500
ReactNoop.renderToRootWithID(<Parent />, 'root');
1542
- expect(Scheduler).toFlushAndYieldThrough([
1501
+ await waitFor([
1502
'Parent',
1503
'Child',
1504
'Child passive create',
@@ -1548,11 +1507,11 @@ describe('ReactHooksWithNoopRenderer', () => {
1507
1508
// Unmount but don't process pending passive destroy function
1509
ReactNoop.unmountRootWithID('root');
1551
- expect(Scheduler).toFlushAndYield(['Parent passive destroy']);
1510
+ await waitForAll(['Parent passive destroy']);
1511
});
1512
});
1513
1555
- it('does not show a warning when a component updates a parents state from within passive unmount function', () => {
1514
+ it('does not show a warning when a component updates a parents state from within passive unmount function', async () => {
1515
function Parent() {
1516
const [state, setState] = React.useState(false);
1517
Scheduler.unstable_yieldValue('Parent');
@@ -1571,21 +1530,17 @@ describe('ReactHooksWithNoopRenderer', () => {
1530
return state;
1531
}
1532
1574
- act(() => {
1533
+ await act(async () => {
1534
ReactNoop.renderToRootWithID(<Parent />, 'root');
1576
- expect(Scheduler).toFlushAndYieldThrough([
1577
- 'Parent',
1578
- 'Child',
1579
- 'Child passive create',
1580
- ]);
1535
+ await waitFor(['Parent', 'Child', 'Child passive create']);
1536
1537
// Unmount but don't process pending passive destroy function
1538
ReactNoop.unmountRootWithID('root');
1584
- expect(Scheduler).toFlushAndYield(['Child passive destroy']);
1539
+ await waitForAll(['Child passive destroy']);
1540
});
1541
});
1542
1588
- it('updates have async priority', () => {
1543
+ it('updates have async priority', async () => {
1544
function Counter(props) {
1545
const [count, updateCount] = useState('(empty)');
1546
useEffect(() => {
@@ -1594,33 +1549,30 @@ describe('ReactHooksWithNoopRenderer', () => {
1549
}, [props.count]);
1550
return <Text text={'Count: ' + count} />;
1551
}
1597
- act(() => {
1552
+ await act(async () => {
1553
ReactNoop.render(<Counter count={0} />, () =>
1554
Scheduler.unstable_yieldValue('Sync effect'),
1555
);
1601
- expect(Scheduler).toFlushAndYieldThrough([
1602
- 'Count: (empty)',
1603
- 'Sync effect',
1604
- ]);
1556
+ await waitFor(['Count: (empty)', 'Sync effect']);
1557
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: (empty)" />);
1558
ReactNoop.flushPassiveEffects();
1607
- expect(Scheduler).toHaveYielded(['Schedule update [0]']);
1608
- expect(Scheduler).toFlushAndYield(['Count: 0']);
1559
+ assertLog(['Schedule update [0]']);
1560
+ await waitForAll(['Count: 0']);
1561
});
1562
1611
- act(() => {
1563
+ await act(async () => {
1564
ReactNoop.render(<Counter count={1} />, () =>
1565
Scheduler.unstable_yieldValue('Sync effect'),
1566
);
1615
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1567
+ await waitFor(['Count: 0', 'Sync effect']);
1568
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1569
ReactNoop.flushPassiveEffects();
1618
- expect(Scheduler).toHaveYielded(['Schedule update [1]']);
1619
- expect(Scheduler).toFlushAndYield(['Count: 1']);
1570
+ assertLog(['Schedule update [1]']);
1571
+ await waitForAll(['Count: 1']);
1572
});
1573
});
1574
1623
- it('updates have async priority even if effects are flushed early', () => {
1575
+ it('updates have async priority even if effects are flushed early', async () => {
1576
function Counter(props) {
1577
const [count, updateCount] = useState('(empty)');
1578
useEffect(() => {
@@ -1629,14 +1581,11 @@ describe('ReactHooksWithNoopRenderer', () => {
1581
}, [props.count]);
1582
return <Text text={'Count: ' + count} />;
1583
}
1632
- act(() => {
1584
+ await act(async () => {
1585
ReactNoop.render(<Counter count={0} />, () =>
1586
Scheduler.unstable_yieldValue('Sync effect'),
1587
);
1636
- expect(Scheduler).toFlushAndYieldThrough([
1637
- 'Count: (empty)',
1638
- 'Sync effect',
1639
- ]);
1588
+ await waitFor(['Count: (empty)', 'Sync effect']);
1589
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: (empty)" />);
1590
1591
// Rendering again should flush the previous commit's effects
@@ -1646,13 +1595,10 @@ describe('ReactHooksWithNoopRenderer', () => {
1595
);
1596
});
1597
1649
- expect(Scheduler).toFlushAndYieldThrough([
1650
- 'Schedule update [0]',
1651
- 'Count: 0',
1652
- ]);
1598
+ await waitFor(['Schedule update [0]', 'Count: 0']);
1599
1600
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1655
- expect(Scheduler).toFlushAndYieldThrough([
1601
+ await waitFor([
1602
'Count: 0',
1603
'Sync effect',
1604
'Schedule update [1]',
@@ -1663,7 +1609,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1609
});
1610
});
1611
1666
- it('does not flush non-discrete passive effects when flushing sync', () => {
1612
+ it('does not flush non-discrete passive effects when flushing sync', async () => {
1613
let _updateCount;
1614
function Counter(props) {
1615
const [count, updateCount] = useState(0);
@@ -1678,7 +1624,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1624
ReactNoop.render(<Counter count={0} />, () =>
1625
Scheduler.unstable_yieldValue('Sync effect'),
1626
);
1681
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1627
+ await waitFor(['Count: 0', 'Sync effect']);
1628
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1629
// A flush sync doesn't cause the passive effects to fire.
1630
// So we haven't added the other update yet.
@@ -1692,13 +1638,9 @@ describe('ReactHooksWithNoopRenderer', () => {
1638
// This should be fine because any non-discrete set of work doesn't guarantee order
1639
// and easily could've happened slightly later too.
1640
if (gate(flags => flags.enableUnifiedSyncLane)) {
1695
- expect(Scheduler).toHaveYielded(['Will set count to 1', 'Count: 1']);
1641
+ assertLog(['Will set count to 1', 'Count: 1']);
1642
} else {
1697
- expect(Scheduler).toHaveYielded([
1698
- 'Will set count to 1',
1699
- 'Count: 2',
1700
- 'Count: 1',
1701
- ]);
1643
+ assertLog(['Will set count to 1', 'Count: 2', 'Count: 1']);
1644
}
1645
1646
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
@@ -1728,7 +1670,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1670
});
1671
1672
// Even in legacy mode, effects are deferred until after paint
1731
- expect(Scheduler).toHaveYielded(['Count: (empty)']);
1673
+ assertLog(['Count: (empty)']);
1674
expect(ReactNoop).toMatchRenderedOutput(
1675
<span prop="Count: (empty)" />,
1676
);
@@ -1737,12 +1679,12 @@ describe('ReactHooksWithNoopRenderer', () => {
1679
// effects get forced on exiting act()
1680
// There were multiple updates, but there should only be a
1681
// single render
1740
- expect(Scheduler).toHaveYielded(['Count: 0']);
1682
+ assertLog(['Count: 0']);
1683
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1684
},
1685
);
1686
1745
- it('flushSync is not allowed', () => {
1687
+ it('flushSync is not allowed', async () => {
1688
function Counter(props) {
1689
const [count, updateCount] = useState('(empty)');
1690
useEffect(() => {
@@ -1750,7 +1692,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1692
ReactNoop.flushSync(() => {
1693
updateCount(props.count);
1694
});
1753
- expect(Scheduler).toHaveYielded([`Schedule update [${props.count}]`]);
1695
+ assertLog([`Schedule update [${props.count}]`]);
1696
// This shouldn't flush synchronously.
1697
expect(ReactNoop).not.toMatchRenderedOutput(
1698
<span prop={`Count: ${props.count}`} />,
@@ -1758,25 +1700,22 @@ describe('ReactHooksWithNoopRenderer', () => {
1700
}, [props.count]);
1701
return <Text text={'Count: ' + count} />;
1702
}
1761
- expect(() => {
1762
- act(() => {
1703
+ await expect(async () => {
1704
+ await act(async () => {
1705
ReactNoop.render(<Counter count={0} />, () =>
1706
Scheduler.unstable_yieldValue('Sync effect'),
1707
);
1766
- expect(Scheduler).toFlushAndYieldThrough([
1767
- 'Count: (empty)',
1768
- 'Sync effect',
1769
- ]);
1708
+ await waitFor(['Count: (empty)', 'Sync effect']);
1709
expect(ReactNoop).toMatchRenderedOutput(
1710
<span prop="Count: (empty)" />,
1711
);
1712
});
1713
}).toErrorDev('flushSync was called from inside a lifecycle method');
1775
- expect(Scheduler).toHaveYielded([`Count: 0`]);
1714
+ assertLog([`Count: 0`]);
1715
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1716
});
1717
1779
- it('unmounts previous effect', () => {
1718
+ it('unmounts previous effect', async () => {
1719
function Counter(props) {
1720
useEffect(() => {
1721
Scheduler.unstable_yieldValue(`Did create [${props.count}]`);
@@ -1786,28 +1725,28 @@ describe('ReactHooksWithNoopRenderer', () => {
1725
});
1726
return <Text text={'Count: ' + props.count} />;
1727
}
1789
- act(() => {
1728
+ await act(async () => {
1729
ReactNoop.render(<Counter count={0} />, () =>
1730
Scheduler.unstable_yieldValue('Sync effect'),
1731
);
1793
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1732
+ await waitFor(['Count: 0', 'Sync effect']);
1733
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1734
});
1735
1797
- expect(Scheduler).toHaveYielded(['Did create [0]']);
1736
+ assertLog(['Did create [0]']);
1737
1799
- act(() => {
1738
+ await act(async () => {
1739
ReactNoop.render(<Counter count={1} />, () =>
1740
Scheduler.unstable_yieldValue('Sync effect'),
1741
);
1803
- expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1742
+ await waitFor(['Count: 1', 'Sync effect']);
1743
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1744
});
1745
1807
- expect(Scheduler).toHaveYielded(['Did destroy [0]', 'Did create [1]']);
1746
+ assertLog(['Did destroy [0]', 'Did create [1]']);
1747
});
1748
1810
- it('unmounts on deletion', () => {
1749
+ it('unmounts on deletion', async () => {
1750
function Counter(props) {
1751
useEffect(() => {
1752
Scheduler.unstable_yieldValue(`Did create [${props.count}]`);
@@ -1817,22 +1756,22 @@ describe('ReactHooksWithNoopRenderer', () => {
1756
});
1757
return <Text text={'Count: ' + props.count} />;
1758
}
1820
- act(() => {
1759
+ await act(async () => {
1760
ReactNoop.render(<Counter count={0} />, () =>
1761
Scheduler.unstable_yieldValue('Sync effect'),
1762
);
1824
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1763
+ await waitFor(['Count: 0', 'Sync effect']);
1764
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1765
});
1766
1828
- expect(Scheduler).toHaveYielded(['Did create [0]']);
1767
+ assertLog(['Did create [0]']);
1768
1769
ReactNoop.render(null);
1831
- expect(Scheduler).toFlushAndYield(['Did destroy [0]']);
1770
+ await waitForAll(['Did destroy [0]']);
1771
expect(ReactNoop).toMatchRenderedOutput(null);
1772
});
1773
1835
- it('unmounts on deletion after skipped effect', () => {
1774
+ it('unmounts on deletion after skipped effect', async () => {
1775
function Counter(props) {
1776
useEffect(() => {
1777
Scheduler.unstable_yieldValue(`Did create [${props.count}]`);
@@ -1842,32 +1781,32 @@ describe('ReactHooksWithNoopRenderer', () => {
1781
}, []);
1782
return <Text text={'Count: ' + props.count} />;
1783
}
1845
- act(() => {
1784
+ await act(async () => {
1785
ReactNoop.render(<Counter count={0} />, () =>
1786
Scheduler.unstable_yieldValue('Sync effect'),
1787
);
1849
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1788
+ await waitFor(['Count: 0', 'Sync effect']);
1789
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1790
});
1791
1853
- expect(Scheduler).toHaveYielded(['Did create [0]']);
1792
+ assertLog(['Did create [0]']);
1793
1855
- act(() => {
1794
+ await act(async () => {
1795
ReactNoop.render(<Counter count={1} />, () =>
1796
Scheduler.unstable_yieldValue('Sync effect'),
1797
);
1859
- expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1798
+ await waitFor(['Count: 1', 'Sync effect']);
1799
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1800
});
1801
1863
- expect(Scheduler).toHaveYielded([]);
1802
+ assertLog([]);
1803
1804
ReactNoop.render(null);
1866
- expect(Scheduler).toFlushAndYield(['Did destroy [0]']);
1805
+ await waitForAll(['Did destroy [0]']);
1806
expect(ReactNoop).toMatchRenderedOutput(null);
1807
});
1808
1870
- it('always fires effects if no dependencies are provided', () => {
1809
+ it('always fires effects if no dependencies are provided', async () => {
1810
function effect() {
1811
Scheduler.unstable_yieldValue(`Did create`);
1812
return () => {
@@ -1878,32 +1817,32 @@ describe('ReactHooksWithNoopRenderer', () => {
1817
useEffect(effect);
1818
return <Text text={'Count: ' + props.count} />;
1819
}
1881
- act(() => {
1820
+ await act(async () => {
1821
ReactNoop.render(<Counter count={0} />, () =>
1822
Scheduler.unstable_yieldValue('Sync effect'),
1823
);
1885
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1824
+ await waitFor(['Count: 0', 'Sync effect']);
1825
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1826
});
1827
1889
- expect(Scheduler).toHaveYielded(['Did create']);
1828
+ assertLog(['Did create']);
1829
1891
- act(() => {
1830
+ await act(async () => {
1831
ReactNoop.render(<Counter count={1} />, () =>
1832
Scheduler.unstable_yieldValue('Sync effect'),
1833
);
1895
- expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1834
+ await waitFor(['Count: 1', 'Sync effect']);
1835
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1836
});
1837
1899
- expect(Scheduler).toHaveYielded(['Did destroy', 'Did create']);
1838
+ assertLog(['Did destroy', 'Did create']);
1839
1840
ReactNoop.render(null);
1902
- expect(Scheduler).toFlushAndYield(['Did destroy']);
1841
+ await waitForAll(['Did destroy']);
1842
expect(ReactNoop).toMatchRenderedOutput(null);
1843
});
1844
1906
- it('skips effect if inputs have not changed', () => {
1845
+ it('skips effect if inputs have not changed', async () => {
1846
function Counter(props) {
1847
const text = `${props.label}: ${props.count}`;
1848
useEffect(() => {
@@ -1914,57 +1853,51 @@ describe('ReactHooksWithNoopRenderer', () => {
1853
}, [props.label, props.count]);
1854
return <Text text={text} />;
1855
}
1917
- act(() => {
1856
+ await act(async () => {
1857
ReactNoop.render(<Counter label="Count" count={0} />, () =>
1858
Scheduler.unstable_yieldValue('Sync effect'),
1859
);
1921
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1860
+ await waitFor(['Count: 0', 'Sync effect']);
1861
});
1862
1924
- expect(Scheduler).toHaveYielded(['Did create [Count: 0]']);
1863
+ assertLog(['Did create [Count: 0]']);
1864
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1865
1927
- act(() => {
1866
+ await act(async () => {
1867
ReactNoop.render(<Counter label="Count" count={1} />, () =>
1868
Scheduler.unstable_yieldValue('Sync effect'),
1869
);
1870
// Count changed
1932
- expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1871
+ await waitFor(['Count: 1', 'Sync effect']);
1872
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1873
});
1874
1936
- expect(Scheduler).toHaveYielded([
1937
- 'Did destroy [Count: 0]',
1938
- 'Did create [Count: 1]',
1939
- ]);
1875
+ assertLog(['Did destroy [Count: 0]', 'Did create [Count: 1]']);
1876
1941
- act(() => {
1877
+ await act(async () => {
1878
ReactNoop.render(<Counter label="Count" count={1} />, () =>
1879
Scheduler.unstable_yieldValue('Sync effect'),
1880
);
1881
// Nothing changed, so no effect should have fired
1946
- expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1882
+ await waitFor(['Count: 1', 'Sync effect']);
1883
});
1884
1949
- expect(Scheduler).toHaveYielded([]);
1885
+ assertLog([]);
1886
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1887
1952
- act(() => {
1888
+ await act(async () => {
1889
ReactNoop.render(<Counter label="Total" count={1} />, () =>
1890
Scheduler.unstable_yieldValue('Sync effect'),
1891
);
1892
// Label changed
1957
- expect(Scheduler).toFlushAndYieldThrough(['Total: 1', 'Sync effect']);
1893
+ await waitFor(['Total: 1', 'Sync effect']);
1894
expect(ReactNoop).toMatchRenderedOutput(<span prop="Total: 1" />);
1895
});
1896
1961
- expect(Scheduler).toHaveYielded([
1962
- 'Did destroy [Count: 1]',
1963
- 'Did create [Total: 1]',
1964
- ]);
1897
+ assertLog(['Did destroy [Count: 1]', 'Did create [Total: 1]']);
1898
});
1899
1967
- it('multiple effects', () => {
1900
+ it('multiple effects', async () => {
1901
function Counter(props) {
1902
useEffect(() => {
1903
Scheduler.unstable_yieldValue(`Did commit 1 [${props.count}]`);
@@ -1974,27 +1907,27 @@ describe('ReactHooksWithNoopRenderer', () => {
1907
});
1908
return <Text text={'Count: ' + props.count} />;
1909
}
1977
- act(() => {
1910
+ await act(async () => {
1911
ReactNoop.render(<Counter count={0} />, () =>
1912
Scheduler.unstable_yieldValue('Sync effect'),
1913
);
1981
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1914
+ await waitFor(['Count: 0', 'Sync effect']);
1915
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1916
});
1917
1985
- expect(Scheduler).toHaveYielded(['Did commit 1 [0]', 'Did commit 2 [0]']);
1918
+ assertLog(['Did commit 1 [0]', 'Did commit 2 [0]']);
1919
1987
- act(() => {
1920
+ await act(async () => {
1921
ReactNoop.render(<Counter count={1} />, () =>
1922
Scheduler.unstable_yieldValue('Sync effect'),
1923
);
1991
- expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1924
+ await waitFor(['Count: 1', 'Sync effect']);
1925
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1926
});
1994
- expect(Scheduler).toHaveYielded(['Did commit 1 [1]', 'Did commit 2 [1]']);
1927
+ assertLog(['Did commit 1 [1]', 'Did commit 2 [1]']);
1928
});
1929
1997
- it('unmounts all previous effects before creating any new ones', () => {
1930
+ it('unmounts all previous effects before creating any new ones', async () => {
1931
function Counter(props) {
1932
useEffect(() => {
1933
Scheduler.unstable_yieldValue(`Mount A [${props.count}]`);
@@ -2010,24 +1943,24 @@ describe('ReactHooksWithNoopRenderer', () => {
1943
});
1944
return <Text text={'Count: ' + props.count} />;
1945
}
2013
- act(() => {
1946
+ await act(async () => {
1947
ReactNoop.render(<Counter count={0} />, () =>
1948
Scheduler.unstable_yieldValue('Sync effect'),
1949
);
2017
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1950
+ await waitFor(['Count: 0', 'Sync effect']);
1951
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1952
});
1953
2021
- expect(Scheduler).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
1954
+ assertLog(['Mount A [0]', 'Mount B [0]']);
1955
2023
- act(() => {
1956
+ await act(async () => {
1957
ReactNoop.render(<Counter count={1} />, () =>
1958
Scheduler.unstable_yieldValue('Sync effect'),
1959
);
2027
- expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1960
+ await waitFor(['Count: 1', 'Sync effect']);
1961
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1962
});
2030
- expect(Scheduler).toHaveYielded([
1963
+ assertLog([
1964
'Unmount A [0]',
1965
'Unmount B [0]',
1966
'Mount A [1]',
@@ -2035,7 +1968,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1968
]);
1969
});
1970
2038
- it('unmounts all previous effects between siblings before creating any new ones', () => {
1971
+ it('unmounts all previous effects between siblings before creating any new ones', async () => {
1972
function Counter({count, label}) {
1973
useEffect(() => {
1974
Scheduler.unstable_yieldValue(`Mount ${label} [${count}]`);
@@ -2045,7 +1978,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1978
});
1979
return <Text text={`${label} ${count}`} />;
1980
}
2048
- act(() => {
1981
+ await act(async () => {
1982
ReactNoop.render(
1983
<>
1984
<Counter label="A" count={0} />
@@ -2053,7 +1986,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1986
</>,
1987
() => Scheduler.unstable_yieldValue('Sync effect'),
1988
);
2056
- expect(Scheduler).toFlushAndYieldThrough(['A 0', 'B 0', 'Sync effect']);
1989
+ await waitFor(['A 0', 'B 0', 'Sync effect']);
1990
expect(ReactNoop).toMatchRenderedOutput(
1991
<>
1992
<span prop="A 0" />
@@ -2062,9 +1995,9 @@ describe('ReactHooksWithNoopRenderer', () => {
1995
);
1996
});
1997
2065
- expect(Scheduler).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
1998
+ assertLog(['Mount A [0]', 'Mount B [0]']);
1999
2067
- act(() => {
2000
+ await act(async () => {
2001
ReactNoop.render(
2002
<>
2003
<Counter label="A" count={1} />
@@ -2072,7 +2005,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2005
</>,
2006
() => Scheduler.unstable_yieldValue('Sync effect'),
2007
);
2075
- expect(Scheduler).toFlushAndYieldThrough(['A 1', 'B 1', 'Sync effect']);
2008
+ await waitFor(['A 1', 'B 1', 'Sync effect']);
2009
expect(ReactNoop).toMatchRenderedOutput(
2010
<>
2011
<span prop="A 1" />
@@ -2080,14 +2013,14 @@ describe('ReactHooksWithNoopRenderer', () => {
2013
</>,
2014
);
2015
});
2083
- expect(Scheduler).toHaveYielded([
2016
+ assertLog([
2017
'Unmount A [0]',
2018
'Unmount B [0]',
2019
'Mount A [1]',
2020
'Mount B [1]',
2021
]);
2022
2090
- act(() => {
2023
+ await act(async () => {
2024
ReactNoop.render(
2025
<>
2026
<Counter label="B" count={2} />
@@ -2095,7 +2028,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2028
</>,
2029
() => Scheduler.unstable_yieldValue('Sync effect'),
2030
);
2098
- expect(Scheduler).toFlushAndYieldThrough(['B 2', 'C 0', 'Sync effect']);
2031
+ await waitFor(['B 2', 'C 0', 'Sync effect']);
2032
expect(ReactNoop).toMatchRenderedOutput(
2033
<>
2034
<span prop="B 2" />
@@ -2103,7 +2036,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2036
</>,
2037
);
2038
});
2106
- expect(Scheduler).toHaveYielded([
2039
+ assertLog([
2040
'Unmount A [1]',
2041
'Unmount B [1]',
2042
'Mount B [2]',
@@ -2111,7 +2044,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2044
]);
2045
});
2046
2114
- it('handles errors in create on mount', () => {
2047
+ it('handles errors in create on mount', async () => {
2048
function Counter(props) {
2049
useEffect(() => {
2050
Scheduler.unstable_yieldValue(`Mount A [${props.count}]`);
@@ -2130,16 +2063,16 @@ describe('ReactHooksWithNoopRenderer', () => {
2063
});
2064
return <Text text={'Count: ' + props.count} />;
2065
}
2133
- act(() => {
2066
+ await act(async () => {
2067
ReactNoop.render(<Counter count={0} />, () =>
2068
Scheduler.unstable_yieldValue('Sync effect'),
2069
);
2137
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
2070
+ await waitFor(['Count: 0', 'Sync effect']);
2071
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
2072
expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
2073
});
2074
2142
- expect(Scheduler).toHaveYielded([
2075
+ assertLog([
2076
'Mount A [0]',
2077
'Oops!',
2078
// Clean up effect A. There's no effect B to clean-up, because it
@@ -2149,7 +2082,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2082
expect(ReactNoop).toMatchRenderedOutput(null);
2083
});
2084
2152
- it('handles errors in create on update', () => {
2085
+ it('handles errors in create on update', async () => {
2086
function Counter(props) {
2087
useEffect(() => {
2088
Scheduler.unstable_yieldValue(`Mount A [${props.count}]`);
@@ -2169,40 +2102,35 @@ describe('ReactHooksWithNoopRenderer', () => {
2102
});
2103
return <Text text={'Count: ' + props.count} />;
2104
}
2172
- act(() => {
2105
+ await act(async () => {
2106
ReactNoop.render(<Counter count={0} />, () =>
2107
Scheduler.unstable_yieldValue('Sync effect'),
2108
);
2176
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
2109
+ await waitFor(['Count: 0', 'Sync effect']);
2110
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
2111
ReactNoop.flushPassiveEffects();
2179
- expect(Scheduler).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
2112
+ assertLog(['Mount A [0]', 'Mount B [0]']);
2113
});
2114
2182
- act(() => {
2115
+ await act(async () => {
2116
// This update will trigger an error
2117
ReactNoop.render(<Counter count={1} />, () =>
2118
Scheduler.unstable_yieldValue('Sync effect'),
2119
);
2187
- expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
2120
+ await waitFor(['Count: 1', 'Sync effect']);
2121
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
2122
expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
2190
- expect(Scheduler).toHaveYielded([
2191
- 'Unmount A [0]',
2192
- 'Unmount B [0]',
2193
- 'Mount A [1]',
2194
- 'Oops!',
2195
- ]);
2123
+ assertLog(['Unmount A [0]', 'Unmount B [0]', 'Mount A [1]', 'Oops!']);
2124
expect(ReactNoop).toMatchRenderedOutput(null);
2125
});
2198
- expect(Scheduler).toHaveYielded([
2126
+ assertLog([
2127
// Clean up effect A runs passively on unmount.
2128
// There's no effect B to clean-up, because it never mounted.
2129
'Unmount A [1]',
2130
]);
2131
});
2132
2205
- it('handles errors in destroy on update', () => {
2133
+ it('handles errors in destroy on update', async () => {
2134
function Counter(props) {
2135
useEffect(() => {
2136
Scheduler.unstable_yieldValue(`Mount A [${props.count}]`);
@@ -2222,22 +2150,22 @@ describe('ReactHooksWithNoopRenderer', () => {
2150
return <Text text={'Count: ' + props.count} />;
2151
}
2152
2225
- act(() => {
2153
+ await act(async () => {
2154
ReactNoop.render(<Counter count={0} />, () =>
2155
Scheduler.unstable_yieldValue('Sync effect'),
2156
);
2229
- expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
2157
+ await waitFor(['Count: 0', 'Sync effect']);
2158
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
2159
ReactNoop.flushPassiveEffects();
2232
- expect(Scheduler).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
2160
+ assertLog(['Mount A [0]', 'Mount B [0]']);
2161
});
2162
2235
- act(() => {
2163
+ await act(async () => {
2164
// This update will trigger an error during passive effect unmount
2165
ReactNoop.render(<Counter count={1} />, () =>
2166
Scheduler.unstable_yieldValue('Sync effect'),
2167
);
2240
- expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
2168
+ await waitFor(['Count: 1', 'Sync effect']);
2169
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
2170
expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
2171
@@ -2245,22 +2173,17 @@ describe('ReactHooksWithNoopRenderer', () => {
2173
// separate pass before flushing any passive creates.
2174
// A result of this two-pass flush is that an error thrown from unmount does
2175
// not block the subsequent create functions from being run.
2248
- expect(Scheduler).toHaveYielded([
2249
- 'Oops!',
2250
- 'Unmount B [0]',
2251
- 'Mount A [1]',
2252
- 'Mount B [1]',
2253
- ]);
2176
+ assertLog(['Oops!', 'Unmount B [0]', 'Mount A [1]', 'Mount B [1]']);
2177
});
2178
2179
// <Counter> gets unmounted because an error is thrown above.
2180
// The remaining destroy functions are run later on unmount, since they're passive.
2181
// In this case, one of them throws again (because of how the test is written).
2259
- expect(Scheduler).toHaveYielded(['Oops!', 'Unmount B [1]']);
2182
+ assertLog(['Oops!', 'Unmount B [1]']);
2183
expect(ReactNoop).toMatchRenderedOutput(null);
2184
});
2185
2263
- it('works with memo', () => {
2186
+ it('works with memo', async () => {
2187
function Counter({count}) {
2188
useLayoutEffect(() => {
2189
Scheduler.unstable_yieldValue('Mount: ' + count);
@@ -2273,26 +2196,17 @@ describe('ReactHooksWithNoopRenderer', () => {
2196
ReactNoop.render(<Counter count={0} />, () =>
2197
Scheduler.unstable_yieldValue('Sync effect'),
2198
);
2276
- expect(Scheduler).toFlushAndYieldThrough([
2277
- 'Count: 0',
2278
- 'Mount: 0',
2279
- 'Sync effect',
2280
- ]);
2199
+ await waitFor(['Count: 0', 'Mount: 0', 'Sync effect']);
2200
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
2201
2202
ReactNoop.render(<Counter count={1} />, () =>
2203
Scheduler.unstable_yieldValue('Sync effect'),
2204
);
2286
- expect(Scheduler).toFlushAndYieldThrough([
2287
- 'Count: 1',
2288
- 'Unmount: 0',
2289
- 'Mount: 1',
2290
- 'Sync effect',
2291
- ]);
2205
+ await waitFor(['Count: 1', 'Unmount: 0', 'Mount: 1', 'Sync effect']);
2206
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
2207
2208
ReactNoop.render(null);
2295
- expect(Scheduler).toFlushAndYieldThrough(['Unmount: 1']);
2209
+ await waitFor(['Unmount: 1']);
2210
expect(ReactNoop).toMatchRenderedOutput(null);
2211
});
2212
@@ -2351,7 +2265,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2265
});
2266
2267
// @gate skipUnmountedBoundaries
2354
- it('should use the nearest still-mounted boundary if there are no unmounted boundaries', () => {
2268
+ it('should use the nearest still-mounted boundary if there are no unmounted boundaries', async () => {
2269
act(() => {
2270
ReactNoop.render(
2271
<LogOnlyErrorBoundary>
@@ -2360,7 +2274,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2274
);
2275
});
2276
2363
- expect(Scheduler).toHaveYielded([
2277
+ assertLog([
2278
'LogOnlyErrorBoundary render',
2279
'BrokenUseEffectCleanup useEffect',
2280
]);
@@ -2369,7 +2283,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2283
ReactNoop.render(<LogOnlyErrorBoundary />);
2284
});
2285
2372
- expect(Scheduler).toHaveYielded([
2286
+ assertLog([
2287
'LogOnlyErrorBoundary render',
2288
'BrokenUseEffectCleanup useEffect destroy',
2289
'LogOnlyErrorBoundary componentDidCatch',
@@ -2377,7 +2291,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2291
});
2292
2293
// @gate skipUnmountedBoundaries
2380
- it('should skip unmounted boundaries and use the nearest still-mounted boundary', () => {
2294
+ it('should skip unmounted boundaries and use the nearest still-mounted boundary', async () => {
2295
function Conditional({showChildren}) {
2296
if (showChildren) {
2297
return (
@@ -2398,7 +2312,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2312
);
2313
});
2314
2401
- expect(Scheduler).toHaveYielded([
2315
+ assertLog([
2316
'LogOnlyErrorBoundary render',
2317
'ErrorBoundary render success',
2318
'BrokenUseEffectCleanup useEffect',
@@ -2412,7 +2326,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2326
);
2327
});
2328
2415
- expect(Scheduler).toHaveYielded([
2329
+ assertLog([
2330
'LogOnlyErrorBoundary render',
2331
'BrokenUseEffectCleanup useEffect destroy',
2332
'LogOnlyErrorBoundary componentDidCatch',
@@ -2420,7 +2334,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2334
});
2335
2336
// @gate skipUnmountedBoundaries
2423
- it('should call getDerivedStateFromError in the nearest still-mounted boundary', () => {
2337
+ it('should call getDerivedStateFromError in the nearest still-mounted boundary', async () => {
2338
function Conditional({showChildren}) {
2339
if (showChildren) {
2340
return <BrokenUseEffectCleanup />;
@@ -2437,7 +2351,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2351
);
2352
});
2353
2440
- expect(Scheduler).toHaveYielded([
2354
+ assertLog([
2355
'ErrorBoundary render success',
2356
'BrokenUseEffectCleanup useEffect',
2357
]);
@@ -2450,7 +2364,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2364
);
2365
});
2366
2453
- expect(Scheduler).toHaveYielded([
2367
+ assertLog([
2368
'ErrorBoundary render success',
2369
'BrokenUseEffectCleanup useEffect destroy',
2370
'ErrorBoundary static getDerivedStateFromError',
@@ -2464,7 +2378,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2378
});
2379
2380
// @gate skipUnmountedBoundaries
2467
- it('should rethrow error if there are no still-mounted boundaries', () => {
2381
+ it('should rethrow error if there are no still-mounted boundaries', async () => {
2382
function Conditional({showChildren}) {
2383
if (showChildren) {
2384
return (
@@ -2481,7 +2395,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2395
ReactNoop.render(<Conditional showChildren={true} />);
2396
});
2397
2484
- expect(Scheduler).toHaveYielded([
2398
+ assertLog([
2399
'ErrorBoundary render success',
2400
'BrokenUseEffectCleanup useEffect',
2401
]);
@@ -2492,15 +2406,13 @@ describe('ReactHooksWithNoopRenderer', () => {
2406
});
2407
}).toThrow('Expected error');
2408
2495
- expect(Scheduler).toHaveYielded([
2496
- 'BrokenUseEffectCleanup useEffect destroy',
2497
- ]);
2409
+ assertLog(['BrokenUseEffectCleanup useEffect destroy']);
2410
2411
expect(ReactNoop).toMatchRenderedOutput(null);
2412
});
2413
});
2414
2503
- it('calls passive effect destroy functions for memoized components', () => {
2415
+ it('calls passive effect destroy functions for memoized components', async () => {
2416
const Wrapper = ({children}) => children;
2417
function Child() {
2418
React.useEffect(() => {
@@ -2530,11 +2442,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2442
</Wrapper>,
2443
);
2444
});
2533
- expect(Scheduler).toHaveYielded([
2534
- 'render',
2535
- 'layout create',
2536
- 'passive create',
2537
- ]);
2445
+ assertLog(['render', 'layout create', 'passive create']);
2446
2447
// Include at least one no-op (memoized) update to trigger original bug.
2448
act(() => {
@@ -2544,7 +2452,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2452
</Wrapper>,
2453
);
2454
});
2547
- expect(Scheduler).toHaveYielded([]);
2455
+ assertLog([]);
2456
2457
act(() => {
2458
ReactNoop.render(
@@ -2553,7 +2461,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2461
</Wrapper>,
2462
);
2463
});
2556
- expect(Scheduler).toHaveYielded([
2464
+ assertLog([
2465
'render',
2466
'layout destroy',
2467
'layout create',
@@ -2564,10 +2472,10 @@ describe('ReactHooksWithNoopRenderer', () => {
2472
act(() => {
2473
ReactNoop.render(null);
2474
});
2567
- expect(Scheduler).toHaveYielded(['layout destroy', 'passive destroy']);
2475
+ assertLog(['layout destroy', 'passive destroy']);
2476
});
2477
2570
- it('calls passive effect destroy functions for descendants of memoized components', () => {
2478
+ it('calls passive effect destroy functions for descendants of memoized components', async () => {
2479
const Wrapper = ({children}) => children;
2480
function Child() {
2481
return <Grandchild />;
@@ -2601,11 +2509,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2509
</Wrapper>,
2510
);
2511
});
2604
- expect(Scheduler).toHaveYielded([
2605
- 'render',
2606
- 'layout create',
2607
- 'passive create',
2608
- ]);
2512
+ assertLog(['render', 'layout create', 'passive create']);
2513
2514
// Include at least one no-op (memoized) update to trigger original bug.
2515
act(() => {
@@ -2615,7 +2519,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2519
</Wrapper>,
2520
);
2521
});
2618
- expect(Scheduler).toHaveYielded([]);
2522
+ assertLog([]);
2523
2524
act(() => {
2525
ReactNoop.render(
@@ -2624,7 +2528,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2528
</Wrapper>,
2529
);
2530
});
2627
- expect(Scheduler).toHaveYielded([
2531
+ assertLog([
2532
'render',
2533
'layout destroy',
2534
'layout create',
@@ -2635,10 +2539,10 @@ describe('ReactHooksWithNoopRenderer', () => {
2539
act(() => {
2540
ReactNoop.render(null);
2541
});
2638
- expect(Scheduler).toHaveYielded(['layout destroy', 'passive destroy']);
2542
+ assertLog(['layout destroy', 'passive destroy']);
2543
});
2544
2641
- it('assumes passive effect destroy function is either a function or undefined', () => {
2545
+ it('assumes passive effect destroy function is either a function or undefined', async () => {
2546
function App(props) {
2547
useEffect(() => {
2548
return props.return;
@@ -2688,7 +2592,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2592
});
2593
2594
describe('useInsertionEffect', () => {
2691
- it('fires insertion effects after snapshots on update', () => {
2595
+ it('fires insertion effects after snapshots on update', async () => {
2596
function CounterA(props) {
2597
useInsertionEffect(() => {
2598
Scheduler.unstable_yieldValue(`Create insertion`);
@@ -2712,7 +2616,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2616
}
2617
}
2618
2715
- act(() => {
2619
+ await act(async () => {
2620
ReactNoop.render(
2621
<>
2622
<CounterA />
@@ -2720,11 +2624,11 @@ describe('ReactHooksWithNoopRenderer', () => {
2624
</>,
2625
);
2626
2723
- expect(Scheduler).toFlushAndYield(['Create insertion']);
2627
+ await waitForAll(['Create insertion']);
2628
});
2629
2630
// Update
2727
- act(() => {
2631
+ await act(async () => {
2632
ReactNoop.render(
2633
<>
2634
<CounterA />
@@ -2732,7 +2636,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2636
</>,
2637
);
2638
2735
- expect(Scheduler).toFlushAndYield([
2639
+ await waitForAll([
2640
'Get Snapshot',
2641
'Destroy insertion',
2642
'Create insertion',
@@ -2740,14 +2644,14 @@ describe('ReactHooksWithNoopRenderer', () => {
2644
});
2645
2646
// Unmount everything
2743
- act(() => {
2647
+ await act(async () => {
2648
ReactNoop.render(null);
2649
2746
- expect(Scheduler).toFlushAndYield(['Destroy insertion']);
2650
+ await waitForAll(['Destroy insertion']);
2651
});
2652
});
2653
2750
- it('fires insertion effects before layout effects', () => {
2654
+ it('fires insertion effects before layout effects', async () => {
2655
let committedText = '(empty)';
2656
2657
function Counter(props) {
@@ -2784,32 +2688,32 @@ describe('ReactHooksWithNoopRenderer', () => {
2688
});
2689
return null;
2690
}
2787
- act(() => {
2691
+ await act(async () => {
2692
ReactNoop.render(<Counter count={0} />);
2693
2790
- expect(Scheduler).toFlushUntilNextPaint([
2694
+ await waitForPaint([
2695
'Create insertion [current: (empty)]',
2696
'Create layout [current: 0]',
2697
]);
2698
expect(committedText).toEqual('0');
2699
});
2700
2797
- expect(Scheduler).toHaveYielded(['Create passive [current: 0]']);
2701
+ assertLog(['Create passive [current: 0]']);
2702
2703
// Unmount everything
2800
- act(() => {
2704
+ await act(async () => {
2705
ReactNoop.render(null);
2706
2803
- expect(Scheduler).toFlushUntilNextPaint([
2707
+ await waitForPaint([
2708
'Destroy insertion [current: 0]',
2709
'Destroy layout [current: 0]',
2710
]);
2711
});
2712
2809
- expect(Scheduler).toHaveYielded(['Destroy passive [current: 0]']);
2713
+ assertLog(['Destroy passive [current: 0]']);
2714
});
2715
2812
- it('force flushes passive effects before firing new insertion effects', () => {
2716
+ it('force flushes passive effects before firing new insertion effects', async () => {
2717
let committedText = '(empty)';
2718
2719
function Counter(props) {
@@ -2848,11 +2752,11 @@ describe('ReactHooksWithNoopRenderer', () => {
2752
return null;
2753
}
2754
2851
- act(() => {
2755
+ await act(async () => {
2756
React.startTransition(() => {
2757
ReactNoop.render(<Counter count={0} />);
2758
});
2855
- expect(Scheduler).toFlushUntilNextPaint([
2759
+ await waitForPaint([
2760
'Create insertion [current: (empty)]',
2761
'Create layout [current: 0]',
2762
]);
@@ -2861,7 +2765,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2765
React.startTransition(() => {
2766
ReactNoop.render(<Counter count={1} />);
2767
});
2864
- expect(Scheduler).toFlushUntilNextPaint([
2768
+ await waitForPaint([
2769
'Create passive [current: 0]',
2770
'Destroy insertion [current: 0]',
2771
'Create insertion [current: 0]',
@@ -2870,13 +2774,13 @@ describe('ReactHooksWithNoopRenderer', () => {
2774
]);
2775
expect(committedText).toEqual('1');
2776
});
2873
- expect(Scheduler).toHaveYielded([
2777
+ assertLog([
2778
'Destroy passive [current: 1]',
2779
'Create passive [current: 1]',
2780
]);
2781
});
2782
2879
- it('fires all insertion effects (interleaved) before firing any layout effects', () => {
2783
+ it('fires all insertion effects (interleaved) before firing any layout effects', async () => {
2784
let committedA = '(empty)';
2785
let committedB = '(empty)';
2786
@@ -2976,14 +2880,14 @@ describe('ReactHooksWithNoopRenderer', () => {
2880
return null;
2881
}
2882
2979
- act(() => {
2883
+ await act(async () => {
2884
ReactNoop.render(
2885
<React.Fragment>
2886
<CounterA count={0} />
2887
<CounterB count={0} />
2888
</React.Fragment>,
2889
);
2986
- expect(Scheduler).toFlushAndYield([
2890
+ await waitForAll([
2891
// All insertion effects fire before all layout effects
2892
'Create Insertion 1 for Component A [A: (empty), B: (empty)]',
2893
'Create Insertion 2 for Component A [A: 0, B: (empty)]',
@@ -2997,14 +2901,14 @@ describe('ReactHooksWithNoopRenderer', () => {
2901
expect([committedA, committedB]).toEqual(['0', '0']);
2902
});
2903
3000
- act(() => {
2904
+ await act(async () => {
2905
ReactNoop.render(
2906
<React.Fragment>
2907
<CounterA count={1} />
2908
<CounterB count={1} />
2909
</React.Fragment>,
2910
);
3007
- expect(Scheduler).toFlushAndYield([
2911
+ await waitForAll([
2912
'Destroy Insertion 1 for Component A [A: 0, B: 0]',
2913
'Destroy Insertion 2 for Component A [A: 0, B: 0]',
2914
'Create Insertion 1 for Component A [A: 0, B: 0]',
@@ -3025,10 +2929,10 @@ describe('ReactHooksWithNoopRenderer', () => {
2929
expect([committedA, committedB]).toEqual(['1', '1']);
2930
2931
// Unmount everything
3028
- act(() => {
2932
+ await act(async () => {
2933
ReactNoop.render(null);
2934
3031
- expect(Scheduler).toFlushAndYield([
2935
+ await waitForAll([
2936
'Destroy Insertion 1 for Component A [A: 1, B: 1]',
2937
'Destroy Insertion 2 for Component A [A: 1, B: 1]',
2938
'Destroy Layout 1 for Component A [A: 1, B: 1]',
@@ -3042,7 +2946,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2946
});
2947
});
2948
3045
- it('assumes insertion effect destroy function is either a function or undefined', () => {
2949
+ it('assumes insertion effect destroy function is either a function or undefined', async () => {
2950
function App(props) {
2951
useInsertionEffect(() => {
2952
return props.return;
@@ -3090,7 +2994,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2994
).toThrow('is not a function');
2995
});
2996
3093
- it('warns when setState is called from insertion effect setup', () => {
2997
+ it('warns when setState is called from insertion effect setup', async () => {
2998
function App(props) {
2999
const [, setX] = useState(0);
3000
useInsertionEffect(() => {
@@ -3128,7 +3032,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3032
});
3033
});
3034
3131
- it('warns when setState is called from insertion effect cleanup', () => {
3035
+ it('warns when setState is called from insertion effect cleanup', async () => {
3036
function App(props) {
3037
const [, setX] = useState(0);
3038
useInsertionEffect(() => {
@@ -3173,7 +3077,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3077
});
3078
3079
describe('useLayoutEffect', () => {
3176
- it('fires layout effects after the host has been mutated', () => {
3080
+ it('fires layout effects after the host has been mutated', async () => {
3081
function getCommittedText() {
3082
const yields = Scheduler.unstable_clearYields();
3083
const children = ReactNoop.getChildrenAsJSX();
@@ -3194,25 +3098,17 @@ describe('ReactHooksWithNoopRenderer', () => {
3098
ReactNoop.render(<Counter count={0} />, () =>
3099
Scheduler.unstable_yieldValue('Sync effect'),
3100
);
3197
- expect(Scheduler).toFlushAndYieldThrough([
3198
- [0],
3199
- 'Current: 0',
3200
- 'Sync effect',
3201
- ]);
3101
+ await waitFor([[0], 'Current: 0', 'Sync effect']);
3102
expect(ReactNoop).toMatchRenderedOutput(<span prop={0} />);
3103
3104
ReactNoop.render(<Counter count={1} />, () =>
3105
Scheduler.unstable_yieldValue('Sync effect'),
3106
);
3207
- expect(Scheduler).toFlushAndYieldThrough([
3208
- [1],
3209
- 'Current: 1',
3210
- 'Sync effect',
3211
- ]);
3107
+ await waitFor([[1], 'Current: 1', 'Sync effect']);
3108
expect(ReactNoop).toMatchRenderedOutput(<span prop={1} />);
3109
});
3110
3215
- it('force flushes passive effects before firing new layout effects', () => {
3111
+ it('force flushes passive effects before firing new layout effects', async () => {
3112
let committedText = '(empty)';
3113
3114
function Counter(props) {
@@ -3243,19 +3139,16 @@ describe('ReactHooksWithNoopRenderer', () => {
3139
return null;
3140
}
3141
3246
- act(() => {
3142
+ await act(async () => {
3143
ReactNoop.render(<Counter count={0} />, () =>
3144
Scheduler.unstable_yieldValue('Sync effect'),
3145
);
3250
- expect(Scheduler).toFlushAndYieldThrough([
3251
- 'Mount layout [current: 0]',
3252
- 'Sync effect',
3253
- ]);
3146
+ await waitFor(['Mount layout [current: 0]', 'Sync effect']);
3147
expect(committedText).toEqual('0');
3148
ReactNoop.render(<Counter count={1} />, () =>
3149
Scheduler.unstable_yieldValue('Sync effect'),
3150
);
3258
- expect(Scheduler).toFlushAndYieldThrough([
3151
+ await waitFor([
3152
'Mount normal [current: 0]',
3153
'Unmount layout [current: 0]',
3154
'Mount layout [current: 1]',
@@ -3264,14 +3157,11 @@ describe('ReactHooksWithNoopRenderer', () => {
3157
expect(committedText).toEqual('1');
3158
});
3159
3267
- expect(Scheduler).toHaveYielded([
3268
- 'Unmount normal [current: 1]',
3269
- 'Mount normal [current: 1]',
3270
- ]);
3160
+ assertLog(['Unmount normal [current: 1]', 'Mount normal [current: 1]']);
3161
});
3162
3163
// @gate skipUnmountedBoundaries
3274
- it('catches errors thrown in useLayoutEffect', () => {
3164
+ it('catches errors thrown in useLayoutEffect', async () => {
3165
class ErrorBoundary extends React.Component {
3166
state = {error: null};
3167
static getDerivedStateFromError(error) {
@@ -3320,7 +3210,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3210
</ErrorBoundary>,
3211
);
3212
3323
- expect(Scheduler).toFlushAndYield([
3213
+ await waitForAll([
3214
'OuterBoundary render success',
3215
'Component render sibling',
3216
'InnerBoundary render success',
@@ -3340,7 +3230,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3230
);
3231
3232
// React should skip over the unmounting boundary and find the nearest still-mounted boundary.
3343
- expect(Scheduler).toFlushAndYield([
3233
+ await waitForAll([
3234
'OuterBoundary render success',
3235
'Component render sibling',
3236
'BrokenLayoutEffectDestroy useLayoutEffect destroy',
@@ -3351,7 +3241,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3241
expect(ReactNoop).toMatchRenderedOutput(<span prop="OuterFallback" />);
3242
});
3243
3354
- it('assumes layout effect destroy function is either a function or undefined', () => {
3244
+ it('assumes layout effect destroy function is either a function or undefined', async () => {
3245
function App(props) {
3246
useLayoutEffect(() => {
3247
return props.return;
@@ -3401,7 +3291,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3291
});
3292
3293
describe('useCallback', () => {
3404
- it('memoizes callback by comparing inputs', () => {
3294
+ it('memoizes callback by comparing inputs', async () => {
3295
class IncrementButton extends React.PureComponent {
3296
increment = () => {
3297
this.props.increment();
@@ -3427,7 +3317,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3317
3318
const button = React.createRef(null);
3319
ReactNoop.render(<Counter incrementBy={1} />);
3430
- expect(Scheduler).toFlushAndYield(['Increment', 'Count: 0']);
3320
+ await waitForAll(['Increment', 'Count: 0']);
3321
expect(ReactNoop).toMatchRenderedOutput(
3322
<>
3323
<span prop="Increment" />
@@ -3436,7 +3326,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3326
);
3327
3328
act(button.current.increment);
3439
- expect(Scheduler).toHaveYielded([
3329
+ assertLog([
3330
// Button should not re-render, because its props haven't changed
3331
// 'Increment',
3332
'Count: 1',
@@ -3450,7 +3340,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3340
3341
// Increase the increment amount
3342
ReactNoop.render(<Counter incrementBy={10} />);
3453
- expect(Scheduler).toFlushAndYield([
3343
+ await waitForAll([
3344
// Inputs did change this time
3345
'Increment',
3346
'Count: 1',
@@ -3464,7 +3354,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3354
3355
// Callback should have updated
3356
act(button.current.increment);
3467
- expect(Scheduler).toHaveYielded(['Count: 11']);
3357
+ assertLog(['Count: 11']);
3358
expect(ReactNoop).toMatchRenderedOutput(
3359
<>
3360
<span prop="Increment" />
@@ -3475,7 +3365,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3365
});
3366
3367
describe('useMemo', () => {
3478
- it('memoizes value by comparing to previous inputs', () => {
3368
+ it('memoizes value by comparing to previous inputs', async () => {
3369
function CapitalizedText(props) {
3370
const text = props.text;
3371
const capitalizedText = useMemo(() => {
@@ -3486,23 +3376,23 @@ describe('ReactHooksWithNoopRenderer', () => {
3376
}
3377
3378
ReactNoop.render(<CapitalizedText text="hello" />);
3489
- expect(Scheduler).toFlushAndYield(["Capitalize 'hello'", 'HELLO']);
3379
+ await waitForAll(["Capitalize 'hello'", 'HELLO']);
3380
expect(ReactNoop).toMatchRenderedOutput(<span prop="HELLO" />);
3381
3382
ReactNoop.render(<CapitalizedText text="hi" />);
3493
- expect(Scheduler).toFlushAndYield(["Capitalize 'hi'", 'HI']);
3383
+ await waitForAll(["Capitalize 'hi'", 'HI']);
3384
expect(ReactNoop).toMatchRenderedOutput(<span prop="HI" />);
3385
3386
ReactNoop.render(<CapitalizedText text="hi" />);
3497
- expect(Scheduler).toFlushAndYield(['HI']);
3387
+ await waitForAll(['HI']);
3388
expect(ReactNoop).toMatchRenderedOutput(<span prop="HI" />);
3389
3390
ReactNoop.render(<CapitalizedText text="goodbye" />);
3501
- expect(Scheduler).toFlushAndYield(["Capitalize 'goodbye'", 'GOODBYE']);
3391
+ await waitForAll(["Capitalize 'goodbye'", 'GOODBYE']);
3392
expect(ReactNoop).toMatchRenderedOutput(<span prop="GOODBYE" />);
3393
});
3394
3505
- it('always re-computes if no inputs are provided', () => {
3395
+ it('always re-computes if no inputs are provided', async () => {
3396
function LazyCompute(props) {
3397
const computed = useMemo(props.compute);
3398
return <Text text={computed} />;
@@ -3519,19 +3409,19 @@ describe('ReactHooksWithNoopRenderer', () => {
3409
}
3410
3411
ReactNoop.render(<LazyCompute compute={computeA} />);
3522
- expect(Scheduler).toFlushAndYield(['compute A', 'A']);
3412
+ await waitForAll(['compute A', 'A']);
3413
3414
ReactNoop.render(<LazyCompute compute={computeA} />);
3525
- expect(Scheduler).toFlushAndYield(['compute A', 'A']);
3415
+ await waitForAll(['compute A', 'A']);
3416
3417
ReactNoop.render(<LazyCompute compute={computeA} />);
3528
- expect(Scheduler).toFlushAndYield(['compute A', 'A']);
3418
+ await waitForAll(['compute A', 'A']);
3419
3420
ReactNoop.render(<LazyCompute compute={computeB} />);
3531
- expect(Scheduler).toFlushAndYield(['compute B', 'B']);
3421
+ await waitForAll(['compute B', 'B']);
3422
});
3423
3534
- it('should not invoke memoized function during re-renders unless inputs change', () => {
3424
+ it('should not invoke memoized function during re-renders unless inputs change', async () => {
3425
function LazyCompute(props) {
3426
const computed = useMemo(
3427
() => props.compute(props.input),
@@ -3550,18 +3440,18 @@ describe('ReactHooksWithNoopRenderer', () => {
3440
}
3441
3442
ReactNoop.render(<LazyCompute compute={compute} input="A" />);
3553
- expect(Scheduler).toFlushAndYield(['compute A', 'A']);
3443
+ await waitForAll(['compute A', 'A']);
3444
3445
ReactNoop.render(<LazyCompute compute={compute} input="A" />);
3556
- expect(Scheduler).toFlushAndYield(['A']);
3446
+ await waitForAll(['A']);
3447
3448
ReactNoop.render(<LazyCompute compute={compute} input="B" />);
3559
- expect(Scheduler).toFlushAndYield(['compute B', 'B']);
3449
+ await waitForAll(['compute B', 'B']);
3450
});
3451
});
3452
3453
describe('useImperativeHandle', () => {
3564
- it('does not update when deps are the same', () => {
3454
+ it('does not update when deps are the same', async () => {
3455
const INCREMENT = 'INCREMENT';
3456
3457
function reducer(state, action) {
@@ -3577,21 +3467,21 @@ describe('ReactHooksWithNoopRenderer', () => {
3467
Counter = forwardRef(Counter);
3468
const counter = React.createRef(null);
3469
ReactNoop.render(<Counter ref={counter} />);
3580
- expect(Scheduler).toFlushAndYield(['Count: 0']);
3470
+ await waitForAll(['Count: 0']);
3471
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
3472
expect(counter.current.count).toBe(0);
3473
3474
act(() => {
3475
counter.current.dispatch(INCREMENT);
3476
});
3587
- expect(Scheduler).toHaveYielded(['Count: 1']);
3477
+ assertLog(['Count: 1']);
3478
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
3479
// Intentionally not updated because of [] deps:
3480
expect(counter.current.count).toBe(0);
3481
});
3482
3483
// Regression test for https://github.com/facebook/react/issues/14782
3594
- it('automatically updates when deps are not specified', () => {
3484
+ it('automatically updates when deps are not specified', async () => {
3485
const INCREMENT = 'INCREMENT';
3486
3487
function reducer(state, action) {
@@ -3607,19 +3497,19 @@ describe('ReactHooksWithNoopRenderer', () => {
3497
Counter = forwardRef(Counter);
3498
const counter = React.createRef(null);
3499
ReactNoop.render(<Counter ref={counter} />);
3610
- expect(Scheduler).toFlushAndYield(['Count: 0']);
3500
+ await waitForAll(['Count: 0']);
3501
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
3502
expect(counter.current.count).toBe(0);
3503
3504
act(() => {
3505
counter.current.dispatch(INCREMENT);
3506
});
3617
- expect(Scheduler).toHaveYielded(['Count: 1']);
3507
+ assertLog(['Count: 1']);
3508
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
3509
expect(counter.current.count).toBe(1);
3510
});
3511
3622
- it('updates when deps are different', () => {
3512
+ it('updates when deps are different', async () => {
3513
const INCREMENT = 'INCREMENT';
3514
3515
function reducer(state, action) {
@@ -3643,7 +3533,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3533
Counter = forwardRef(Counter);
3534
const counter = React.createRef(null);
3535
ReactNoop.render(<Counter ref={counter} />);
3646
- expect(Scheduler).toFlushAndYield(['Count: 0']);
3536
+ await waitForAll(['Count: 0']);
3537
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
3538
expect(counter.current.count).toBe(0);
3539
expect(totalRefUpdates).toBe(1);
@@ -3651,14 +3541,14 @@ describe('ReactHooksWithNoopRenderer', () => {
3541
act(() => {
3542
counter.current.dispatch(INCREMENT);
3543
});
3654
- expect(Scheduler).toHaveYielded(['Count: 1']);
3544
+ assertLog(['Count: 1']);
3545
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
3546
expect(counter.current.count).toBe(1);
3547
expect(totalRefUpdates).toBe(2);
3548
3549
// Update that doesn't change the ref dependencies
3550
ReactNoop.render(<Counter ref={counter} />);
3661
- expect(Scheduler).toFlushAndYield(['Count: 1']);
3551
+ await waitForAll(['Count: 1']);
3552
expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
3553
expect(counter.current.count).toBe(1);
3554
expect(totalRefUpdates).toBe(2); // Should not increase since last time
@@ -3688,7 +3578,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3578
);
3579
}
3580
ReactNoop.render(<App />);
3691
- expect(Scheduler).toFlushAndYield(['Before... Pending: false']);
3581
+ await waitForAll(['Before... Pending: false']);
3582
expect(ReactNoop).toMatchRenderedOutput(
3583
<span prop="Before... Pending: false" />,
3584
);
@@ -3696,7 +3586,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3586
await act(async () => {
3587
transition();
3588
3699
- expect(Scheduler).toFlushAndYield([
3589
+ await waitForAll([
3590
'Before... Pending: true',
3591
'Suspend! [After... Pending: false]',
3592
'Loading... Pending: false',
@@ -3715,10 +3605,8 @@ describe('ReactHooksWithNoopRenderer', () => {
3605
);
3606
3607
await resolveText('After... Pending: false');
3718
- expect(Scheduler).toHaveYielded([
3719
- 'Promise resolved [After... Pending: false]',
3720
- ]);
3721
- expect(Scheduler).toFlushAndYield(['After... Pending: false']);
3608
+ assertLog(['Promise resolved [After... Pending: false]']);
3609
+ await waitForAll(['After... Pending: false']);
3610
expect(ReactNoop).toMatchRenderedOutput(
3611
<span prop="After... Pending: false" />,
3612
);
@@ -3753,7 +3641,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3641
ReactNoop.render(<App />);
3642
});
3643
3756
- expect(Scheduler).toHaveYielded(['A', 'Suspend! [A]', 'Loading']);
3644
+ assertLog(['A', 'Suspend! [A]', 'Loading']);
3645
expect(ReactNoop).toMatchRenderedOutput(
3646
<>
3647
<span prop="A" />
@@ -3762,8 +3650,8 @@ describe('ReactHooksWithNoopRenderer', () => {
3650
);
3651
3652
await resolveText('A');
3765
- expect(Scheduler).toHaveYielded(['Promise resolved [A]']);
3766
- expect(Scheduler).toFlushAndYield(['A']);
3653
+ assertLog(['Promise resolved [A]']);
3654
+ await waitForAll(['A']);
3655
expect(ReactNoop).toMatchRenderedOutput(
3656
<>
3657
<span prop="A" />
@@ -3773,14 +3661,8 @@ describe('ReactHooksWithNoopRenderer', () => {
3661
3662
await act(async () => {
3663
_setText('B');
3776
- expect(Scheduler).toFlushAndYield([
3777
- 'B',
3778
- 'A',
3779
- 'B',
3780
- 'Suspend! [B]',
3781
- 'Loading',
3782
- ]);
3783
- expect(Scheduler).toFlushAndYield([]);
3664
+ await waitForAll(['B', 'A', 'B', 'Suspend! [B]', 'Loading']);
3665
+ await waitForAll([]);
3666
expect(ReactNoop).toMatchRenderedOutput(
3667
<>
3668
<span prop="B" />
@@ -3793,7 +3675,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3675
Scheduler.unstable_advanceTime(250);
3676
await advanceTimers(250);
3677
});
3796
- expect(Scheduler).toHaveYielded([]);
3678
+ assertLog([]);
3679
expect(ReactNoop).toMatchRenderedOutput(
3680
<>
3681
<span prop="B" />
@@ -3804,7 +3686,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3686
// Even after a long amount of time, we don't show a fallback
3687
Scheduler.unstable_advanceTime(100000);
3688
await advanceTimers(100000);
3807
- expect(Scheduler).toFlushAndYield([]);
3689
+ await waitForAll([]);
3690
expect(ReactNoop).toMatchRenderedOutput(
3691
<>
3692
<span prop="B" />
@@ -3815,7 +3697,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3697
await act(async () => {
3698
await resolveText('B');
3699
});
3818
- expect(Scheduler).toHaveYielded(['Promise resolved [B]', 'B', 'B']);
3700
+ assertLog(['Promise resolved [B]', 'B', 'B']);
3701
expect(ReactNoop).toMatchRenderedOutput(
3702
<>
3703
<span prop="B" />
@@ -3826,7 +3708,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3708
});
3709
3710
describe('progressive enhancement (not supported)', () => {
3829
- it('mount additional state', () => {
3711
+ it('mount additional state', async () => {
3712
let updateA;
3713
let updateB;
3714
// let updateC;
@@ -3848,7 +3730,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3730
}
3731
3732
ReactNoop.render(<App loadC={false} />);
3851
- expect(Scheduler).toFlushAndYield(['A: 0, B: 0, C: [not loaded]']);
3733
+ await waitForAll(['A: 0, B: 0, C: [not loaded]']);
3734
expect(ReactNoop).toMatchRenderedOutput(
3735
<span prop="A: 0, B: 0, C: [not loaded]" />,
3736
);
@@ -3858,16 +3740,17 @@ describe('ReactHooksWithNoopRenderer', () => {
3740
updateB(3);
3741
});
3742
3861
- expect(Scheduler).toHaveYielded(['A: 2, B: 3, C: [not loaded]']);
3743
+ assertLog(['A: 2, B: 3, C: [not loaded]']);
3744
expect(ReactNoop).toMatchRenderedOutput(
3745
<span prop="A: 2, B: 3, C: [not loaded]" />,
3746
);
3747
3748
ReactNoop.render(<App loadC={true} />);
3867
- expect(() => {
3868
- expect(() => {
3869
- expect(Scheduler).toFlushAndYield(['A: 2, B: 3, C: 0']);
3870
- }).toThrow('Rendered more hooks than during the previous render');
3749
+ await expect(async () => {
3750
+ await waitForThrow(
3751
+ 'Rendered more hooks than during the previous render.',
3752
+ );
3753
+ assertLog([]);
3754
}).toErrorDev([
3755
'Warning: React has detected a change in the order of Hooks called by App. ' +
3756
'This will lead to bugs and errors if not fixed. For more information, ' +
@@ -3888,7 +3771,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3771
// expect(ReactNoop).toMatchRenderedOutput(<span prop="A: 2, B: 3, C: 4" />]);
3772
});
3773
3891
- it('unmount state', () => {
3774
+ it('unmount state', async () => {
3775
let updateA;
3776
let updateB;
3777
let updateC;
@@ -3912,14 +3795,14 @@ describe('ReactHooksWithNoopRenderer', () => {
3795
}
3796
3797
ReactNoop.render(<App loadC={true} />);
3915
- expect(Scheduler).toFlushAndYield(['A: 0, B: 0, C: 0']);
3798
+ await waitForAll(['A: 0, B: 0, C: 0']);
3799
expect(ReactNoop).toMatchRenderedOutput(<span prop="A: 0, B: 0, C: 0" />);
3800
act(() => {
3801
updateA(2);
3802
updateB(3);
3803
updateC(4);
3804
});
3922
- expect(Scheduler).toHaveYielded(['A: 2, B: 3, C: 4']);
3805
+ assertLog(['A: 2, B: 3, C: 4']);
3806
expect(ReactNoop).toMatchRenderedOutput(<span prop="A: 2, B: 3, C: 4" />);
3807
ReactNoop.render(<App loadC={false} />);
3808
expect(Scheduler).toFlushAndThrow(
@@ -3928,7 +3811,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3811
);
3812
});
3813
3931
- it('unmount effects', () => {
3814
+ it('unmount effects', async () => {
3815
function App(props) {
3816
useEffect(() => {
3817
Scheduler.unstable_yieldValue('Mount A');
@@ -3949,21 +3832,22 @@ describe('ReactHooksWithNoopRenderer', () => {
3832
return null;
3833
}
3834
3952
- act(() => {
3835
+ await act(async () => {
3836
ReactNoop.render(<App showMore={false} />, () =>
3837
Scheduler.unstable_yieldValue('Sync effect'),
3838
);
3956
- expect(Scheduler).toFlushAndYieldThrough(['Sync effect']);
3839
+ await waitFor(['Sync effect']);
3840
});
3841
3959
- expect(Scheduler).toHaveYielded(['Mount A']);
3842
+ assertLog(['Mount A']);
3843
3961
- act(() => {
3844
+ await act(async () => {
3845
ReactNoop.render(<App showMore={true} />);
3963
- expect(() => {
3964
- expect(() => {
3965
- expect(Scheduler).toFlushAndYield([]);
3966
- }).toThrow('Rendered more hooks than during the previous render');
3846
+ await expect(async () => {
3847
+ await waitForThrow(
3848
+ 'Rendered more hooks than during the previous render.',
3849
+ );
3850
+ assertLog([]);
3851
}).toErrorDev([
3852
'Warning: React has detected a change in the order of Hooks called by App. ' +
3853
'This will lead to bugs and errors if not fixed. For more information, ' +
@@ -3988,7 +3872,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3872
});
3873
});
3874
3991
- it('useReducer does not eagerly bail out of state updates', () => {
3875
+ it('useReducer does not eagerly bail out of state updates', async () => {
3876
// Edge case based on a bug report
3877
let setCounter;
3878
function App() {
@@ -4013,30 +3897,20 @@ describe('ReactHooksWithNoopRenderer', () => {
3897
return count;
3898
}
3899
4016
- act(() => {
3900
+ await act(async () => {
3901
ReactNoop.render(<App />);
4018
- expect(Scheduler).toFlushAndYield([
4019
- 'Render: -1',
4020
- 'Effect: 1',
4021
- 'Reducer: 1',
4022
- 'Render: 1',
4023
- ]);
3902
+ await waitForAll(['Render: -1', 'Effect: 1', 'Reducer: 1', 'Render: 1']);
3903
expect(ReactNoop).toMatchRenderedOutput('1');
3904
});
3905
3906
act(() => {
3907
setCounter(2);
3908
});
4030
- expect(Scheduler).toHaveYielded([
4031
- 'Render: 1',
4032
- 'Effect: 2',
4033
- 'Reducer: 2',
4034
- 'Render: 2',
4035
- ]);
3909
+ assertLog(['Render: 1', 'Effect: 2', 'Reducer: 2', 'Render: 2']);
3910
expect(ReactNoop).toMatchRenderedOutput('2');
3911
});
3912
4039
- it('useReducer does not replay previous no-op actions when other state changes', () => {
3913
+ it('useReducer does not replay previous no-op actions when other state changes', async () => {
3914
let increment;
3915
let setDisabled;
3916
@@ -4061,10 +3935,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3935
}
3936
3937
ReactNoop.render(<Counter />);
4064
- expect(Scheduler).toFlushAndYield([
4065
- 'Render disabled: true',
4066
- 'Render count: 0',
4067
- ]);
3938
+ await waitForAll(['Render disabled: true', 'Render count: 0']);
3939
expect(ReactNoop).toMatchRenderedOutput('0');
3940
3941
act(() => {
@@ -4073,24 +3944,18 @@ describe('ReactHooksWithNoopRenderer', () => {
3944
increment();
3945
increment();
3946
});
4076
- expect(Scheduler).toHaveYielded([
4077
- 'Render disabled: true',
4078
- 'Render count: 0',
4079
- ]);
3947
+ assertLog(['Render disabled: true', 'Render count: 0']);
3948
expect(ReactNoop).toMatchRenderedOutput('0');
3949
3950
act(() => {
3951
// Enabling the updater should *not* replay the previous increment() actions
3952
setDisabled(false);
3953
});
4086
- expect(Scheduler).toHaveYielded([
4087
- 'Render disabled: false',
4088
- 'Render count: 0',
4089
- ]);
3954
+ assertLog(['Render disabled: false', 'Render count: 0']);
3955
expect(ReactNoop).toMatchRenderedOutput('0');
3956
});
3957
4093
- it('useReducer does not replay previous no-op actions when props change', () => {
3958
+ it('useReducer does not replay previous no-op actions when props change', async () => {
3959
let setDisabled;
3960
let increment;
3961
@@ -4119,10 +3984,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3984
}
3985
3986
ReactNoop.render(<App />);
4122
- expect(Scheduler).toFlushAndYield([
4123
- 'Render disabled: true',
4124
- 'Render count: 0',
4125
- ]);
3987
+ await waitForAll(['Render disabled: true', 'Render count: 0']);
3988
expect(ReactNoop).toMatchRenderedOutput('0');
3989
3990
act(() => {
@@ -4131,21 +3993,18 @@ describe('ReactHooksWithNoopRenderer', () => {
3993
increment();
3994
increment();
3995
});
4134
- expect(Scheduler).toHaveYielded(['Render count: 0']);
3996
+ assertLog(['Render count: 0']);
3997
expect(ReactNoop).toMatchRenderedOutput('0');
3998
3999
act(() => {
4000
// Enabling the updater should *not* replay the previous increment() actions
4001
setDisabled(false);
4002
});
4141
- expect(Scheduler).toHaveYielded([
4142
- 'Render disabled: false',
4143
- 'Render count: 0',
4144
- ]);
4003
+ assertLog(['Render disabled: false', 'Render count: 0']);
4004
expect(ReactNoop).toMatchRenderedOutput('0');
4005
});
4006
4148
- it('useReducer applies potential no-op changes if made relevant by other updates in the batch', () => {
4007
+ it('useReducer applies potential no-op changes if made relevant by other updates in the batch', async () => {
4008
let setDisabled;
4009
let increment;
4010
@@ -4174,10 +4033,7 @@ describe('ReactHooksWithNoopRenderer', () => {
4033
}
4034
4035
ReactNoop.render(<App />);
4177
- expect(Scheduler).toFlushAndYield([
4178
- 'Render disabled: true',
4179
- 'Render count: 0',
4180
- ]);
4036
+ await waitForAll(['Render disabled: true', 'Render count: 0']);
4037
expect(ReactNoop).toMatchRenderedOutput('0');
4038
4039
act(() => {
@@ -4188,10 +4044,7 @@ describe('ReactHooksWithNoopRenderer', () => {
4044
increment();
4045
setDisabled(false);
4046
});
4191
- expect(Scheduler).toHaveYielded([
4192
- 'Render disabled: false',
4193
- 'Render count: 1',
4194
- ]);
4047
+ assertLog(['Render disabled: false', 'Render count: 1']);
4048
expect(ReactNoop).toMatchRenderedOutput('1');
4049
});
4050
@@ -4230,12 +4083,7 @@ describe('ReactHooksWithNoopRenderer', () => {
4083
</>,
4084
);
4085
});
4233
- expect(Scheduler).toHaveYielded([
4234
- 'Render A: 0',
4235
- 'Render B: 0',
4236
- 'Commit A: 0',
4237
- 'Commit B: 0',
4238
- ]);
4086
+ assertLog(['Render A: 0', 'Render B: 0', 'Commit A: 0', 'Commit B: 0']);
4087
4088
await act(async () => {
4089
setCounterA(1);
@@ -4246,7 +4094,7 @@ describe('ReactHooksWithNoopRenderer', () => {
4094
setCounterB(1);
4095
setCounterB(0);
4096
});
4249
- expect(Scheduler).toHaveYielded([
4097
+ assertLog([
4098
'Render A: 1',
4099
'Render B: 0',
4100
'Commit A: 1',
@@ -4255,7 +4103,7 @@ describe('ReactHooksWithNoopRenderer', () => {
4103
]);
4104
});
4105
4258
- it('should update latest rendered reducer when a preceding state receives a render phase update', () => {
4106
+ it('should update latest rendered reducer when a preceding state receives a render phase update', async () => {
4107
// Similar to previous test, except using a preceding render phase update
4108
// instead of new props.
4109
let dispatch;
@@ -4273,7 +4121,7 @@ describe('ReactHooksWithNoopRenderer', () => {
4121
}
4122
4123
ReactNoop.render(<App />);
4276
- expect(Scheduler).toFlushAndYield([
4124
+ await waitForAll([
4125
'Step: 0, Shadow: 0',
4126
'Step: 1, Shadow: 0',
4127
'Step: 2, Shadow: 0',
@@ -4284,11 +4132,11 @@ describe('ReactHooksWithNoopRenderer', () => {
4132
expect(ReactNoop).toMatchRenderedOutput('0');
4133
4134
act(() => dispatch());
4287
- expect(Scheduler).toHaveYielded(['Step: 5, Shadow: 5']);
4135
+ assertLog(['Step: 5, Shadow: 5']);
4136
expect(ReactNoop).toMatchRenderedOutput('5');
4137
});
4138
4291
- it('should process the rest pending updates after a render phase update', () => {
4139
+ it('should process the rest pending updates after a render phase update', async () => {
4140
// Similar to previous test, except using a preceding render phase update
4141
// instead of new props.
4142
let updateA;
@@ -4346,7 +4194,7 @@ describe('ReactHooksWithNoopRenderer', () => {
4194
</>,
4195
);
4196
});
4349
- expect(Scheduler).toHaveYielded([
4197
+ assertLog([
4198
'Mount layout A',
4199
'Mount layout B',
4200
'Mount passive A',
@@ -4362,13 +4210,13 @@ describe('ReactHooksWithNoopRenderer', () => {
4210
</>,
4211
);
4212
});
4365
- expect(Scheduler).toHaveYielded(['Unmount layout A', 'Unmount passive A']);
4213
+ assertLog(['Unmount layout A', 'Unmount passive A']);
4214
4215
// Now delete and unmount B.
4216
await act(async () => {
4217
root.render(null);
4218
});
4371
- expect(Scheduler).toHaveYielded(['Unmount layout B', 'Unmount passive B']);
4219
+ assertLog(['Unmount layout B', 'Unmount passive B']);
4220
});
4221
4222
it('regression: deleting a tree and unmounting its effects after a reorder', async () => {
@@ -4392,7 +4240,7 @@ describe('ReactHooksWithNoopRenderer', () => {
4240
</>,
4241
);
4242
});
4395
- expect(Scheduler).toHaveYielded(['Mount A', 'Mount B']);
4243
+ assertLog(['Mount A', 'Mount B']);
4244
4245
await act(async () => {
4246
root.render(
@@ -4402,13 +4250,13 @@ describe('ReactHooksWithNoopRenderer', () => {
4250
</>,
4251
);
4252
});
4405
- expect(Scheduler).toHaveYielded([]);
4253
+ assertLog([]);
4254
4255
await act(async () => {
4256
root.render(null);
4257
});
4258
4411
- expect(Scheduler).toHaveYielded([
4259
+ assertLog([
4260
'Unmount B',
4261
// In the regression, the reorder would cause Child A to "forget" that it
4262
// contains passive effects. Then when we deleted the tree, A's unmount
@@ -4446,21 +4294,12 @@ describe('ReactHooksWithNoopRenderer', () => {
4294
await act(async () => {
4295
root.render(<App />);
4296
});
4449
- expect(Scheduler).toHaveYielded([
4450
- 'Suspend! [A]',
4451
- 'Suspend! [B]',
4452
- 'Mount A',
4453
- 'Mount B',
4454
- ]);
4297
+ assertLog(['Suspend! [A]', 'Suspend! [B]', 'Mount A', 'Mount B']);
4298
4299
await act(async () => {
4300
await resolveText('A');
4301
});
4459
- expect(Scheduler).toHaveYielded([
4460
- 'Promise resolved [A]',
4461
- 'A',
4462
- 'Suspend! [B]',
4463
- ]);
4302
+ assertLog(['Promise resolved [A]', 'A', 'Suspend! [B]']);
4303
4304
await act(async () => {
4305
root.render(null);
@@ -4468,10 +4307,10 @@ describe('ReactHooksWithNoopRenderer', () => {
4307
// In the regression, SuspenseList would cause the children to "forget" that
4308
// it contains passive effects. Then when we deleted the tree, these unmount
4309
// effects would not fire.
4471
- expect(Scheduler).toHaveYielded(['Unmount A', 'Unmount B']);
4310
+ assertLog(['Unmount A', 'Unmount B']);
4311
});
4312
4474
- it('effect dependencies are persisted after a render phase update', () => {
4313
+ it('effect dependencies are persisted after a render phase update', async () => {
4314
let handleClick;
4315
function Test() {
4316
const [count, setCount] = useState(0);
@@ -4493,24 +4332,24 @@ describe('ReactHooksWithNoopRenderer', () => {
4332
ReactNoop.render(<Test />);
4333
});
4334
4496
- expect(Scheduler).toHaveYielded(['Render: 0', 'Effect: 0']);
4335
+ assertLog(['Render: 0', 'Effect: 0']);
4336
4337
act(() => {
4338
handleClick();
4339
});
4340
4502
- expect(Scheduler).toHaveYielded(['Render: 0']);
4341
+ assertLog(['Render: 0']);
4342
4343
act(() => {
4344
handleClick();
4345
});
4346
4508
- expect(Scheduler).toHaveYielded(['Render: 0']);
4347
+ assertLog(['Render: 0']);
4348
4349
act(() => {
4350
handleClick();
4351
});
4352
4514
- expect(Scheduler).toHaveYielded(['Render: 0']);
4353
+ assertLog(['Render: 0']);
4354
});
4355
});
packages/react-reconciler/src/__tests__/ReactIncremental-test.js
+207
-213
@@ -14,6 +14,10 @@ let React;
14
let ReactNoop;
15
let Scheduler;
16
let PropTypes;
17
+let waitForAll;
18
+let waitFor;
19
+let waitForThrow;
20
+let assertLog;
21
22
describe('ReactIncremental', () => {
23
beforeEach(() => {
@@ -22,6 +26,12 @@ describe('ReactIncremental', () => {
26
ReactNoop = require('react-noop-renderer');
27
Scheduler = require('scheduler');
28
PropTypes = require('prop-types');
29
+
30
+ const InternalTestUtils = require('internal-test-utils');
31
+ waitForAll = InternalTestUtils.waitForAll;
32
+ waitFor = InternalTestUtils.waitFor;
33
+ waitForThrow = InternalTestUtils.waitForThrow;
34
+ assertLog = InternalTestUtils.assertLog;
35
});
36
37
// Note: This is based on a similar component we use in www. We can delete
@@ -37,7 +47,7 @@ describe('ReactIncremental', () => {
47
);
48
}
49
40
- it('should render a simple component', () => {
50
+ it('should render a simple component', async () => {
51
function Bar() {
52
return <div>Hello World</div>;
53
}
@@ -47,10 +57,10 @@ describe('ReactIncremental', () => {
57
}
58
59
ReactNoop.render(<Foo />);
50
- expect(Scheduler).toFlushWithoutYielding();
60
+ await waitForAll([]);
61
});
62
53
- it('should render a simple component, in steps if needed', () => {
63
+ it('should render a simple component, in steps if needed', async () => {
64
function Bar() {
65
Scheduler.unstable_yieldValue('Bar');
66
return (
@@ -74,10 +84,10 @@ describe('ReactIncremental', () => {
84
expect(ReactNoop.flushNextYield()).toEqual(['Foo']);
85
86
// Do the rest of the work.
77
- expect(Scheduler).toFlushAndYield(['Bar', 'Bar', 'callback']);
87
+ await waitForAll(['Bar', 'Bar', 'callback']);
88
});
89
80
- it('updates a previous render', () => {
90
+ it('updates a previous render', async () => {
91
function Header() {
92
Scheduler.unstable_yieldValue('Header');
93
return <h1>Hi</h1>;
@@ -110,7 +120,7 @@ describe('ReactIncremental', () => {
120
ReactNoop.render(<Foo text="foo" />, () =>
121
Scheduler.unstable_yieldValue('renderCallbackCalled'),
122
);
113
- expect(Scheduler).toFlushAndYield([
123
+ await waitForAll([
124
'Foo',
125
'Header',
126
'Content',
@@ -128,7 +138,7 @@ describe('ReactIncremental', () => {
138
139
// Since this is an update, it should bail out and reuse the work from
140
// Header and Content.
131
- expect(Scheduler).toFlushAndYield([
141
+ await waitForAll([
142
'Foo',
143
'Content',
144
'firstRenderCallbackCalled',
@@ -136,7 +146,7 @@ describe('ReactIncremental', () => {
146
]);
147
});
148
139
- it('can cancel partially rendered work and restart', () => {
149
+ it('can cancel partially rendered work and restart', async () => {
150
function Bar(props) {
151
Scheduler.unstable_yieldValue('Bar');
152
return <div>{props.children}</div>;
@@ -154,13 +164,13 @@ describe('ReactIncremental', () => {
164
165
// Init
166
ReactNoop.render(<Foo text="foo" />);
157
- expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Bar']);
167
+ await waitForAll(['Foo', 'Bar', 'Bar']);
168
169
React.startTransition(() => {
170
ReactNoop.render(<Foo text="bar" />);
171
});
172
// Flush part of the work
163
- expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Bar']);
173
+ await waitFor(['Foo', 'Bar']);
174
175
// This will abort the previous work and restart
176
ReactNoop.flushSync(() => ReactNoop.render(null));
@@ -170,13 +180,13 @@ describe('ReactIncremental', () => {
180
});
181
182
// Flush part of the new work
173
- expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Bar']);
183
+ await waitFor(['Foo', 'Bar']);
184
185
// Flush the rest of the work which now includes the low priority
176
- expect(Scheduler).toFlushAndYield(['Bar']);
186
+ await waitForAll(['Bar']);
187
});
188
179
- it('should call callbacks even if updates are aborted', () => {
189
+ it('should call callbacks even if updates are aborted', async () => {
190
let inst;
191
192
class Foo extends React.Component {
@@ -199,7 +209,7 @@ describe('ReactIncremental', () => {
209
}
210
211
ReactNoop.render(<Foo />);
202
- expect(Scheduler).toFlushWithoutYielding();
212
+ await waitForAll([]);
213
214
React.startTransition(() => {
215
inst.setState(
@@ -212,7 +222,7 @@ describe('ReactIncremental', () => {
222
});
223
224
// Flush part of the work
215
- expect(Scheduler).toFlushAndYieldThrough(['setState1']);
225
+ await waitFor(['setState1']);
226
227
// This will abort the previous work and restart
228
ReactNoop.flushSync(() => ReactNoop.render(<Foo />));
@@ -227,17 +237,12 @@ describe('ReactIncremental', () => {
237
});
238
239
// Flush the rest of the work which now includes the low priority
230
- expect(Scheduler).toFlushAndYield([
231
- 'setState1',
232
- 'setState2',
233
- 'callback1',
234
- 'callback2',
235
- ]);
240
+ await waitForAll(['setState1', 'setState2', 'callback1', 'callback2']);
241
expect(inst.state).toEqual({text: 'bar', text2: 'baz'});
242
});
243
244
// @gate www
240
- it('can deprioritize unfinished work and resume it later', () => {
245
+ it('can deprioritize unfinished work and resume it later', async () => {
246
function Bar(props) {
247
Scheduler.unstable_yieldValue('Bar');
248
return <div>{props.children}</div>;
@@ -266,24 +271,18 @@ describe('ReactIncremental', () => {
271
272
// Init
273
ReactNoop.render(<Foo text="foo" />);
269
- expect(Scheduler).toFlushAndYield([
270
- 'Foo',
271
- 'Bar',
272
- 'Bar',
273
- 'Middle',
274
- 'Middle',
275
- ]);
274
+ await waitForAll(['Foo', 'Bar', 'Bar', 'Middle', 'Middle']);
275
276
// Render part of the work. This should be enough to flush everything except
277
// the middle which has lower priority.
278
ReactNoop.render(<Foo text="bar" />);
280
- expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Bar', 'Bar']);
279
+ await waitFor(['Foo', 'Bar', 'Bar']);
280
// Flush only the remaining work
282
- expect(Scheduler).toFlushAndYield(['Middle', 'Middle']);
281
+ await waitForAll(['Middle', 'Middle']);
282
});
283
284
// @gate www
286
- it('can deprioritize a tree from without dropping work', () => {
285
+ it('can deprioritize a tree from without dropping work', async () => {
286
function Bar(props) {
287
Scheduler.unstable_yieldValue('Bar');
288
return <div>{props.children}</div>;
@@ -314,21 +313,21 @@ describe('ReactIncremental', () => {
313
ReactNoop.flushSync(() => {
314
ReactNoop.render(<Foo text="foo" />);
315
});
317
- expect(Scheduler).toHaveYielded(['Foo', 'Bar', 'Bar']);
318
- expect(Scheduler).toFlushAndYield(['Middle', 'Middle']);
316
+ assertLog(['Foo', 'Bar', 'Bar']);
317
+ await waitForAll(['Middle', 'Middle']);
318
319
// Render the high priority work (everything except the hidden trees).
320
ReactNoop.flushSync(() => {
321
ReactNoop.render(<Foo text="foo" />);
322
});
324
- expect(Scheduler).toHaveYielded(['Foo', 'Bar', 'Bar']);
323
+ assertLog(['Foo', 'Bar', 'Bar']);
324
325
// The hidden content was deprioritized from high to low priority. A low
326
// priority callback should have been scheduled. Flush it now.
328
- expect(Scheduler).toFlushAndYield(['Middle', 'Middle']);
327
+ await waitForAll(['Middle', 'Middle']);
328
});
329
331
- xit('can resume work in a subtree even when a parent bails out', () => {
330
+ xit('can resume work in a subtree even when a parent bails out', async () => {
331
function Bar(props) {
332
Scheduler.unstable_yieldValue('Bar');
333
return <div>{props.children}</div>;
@@ -372,19 +371,19 @@ describe('ReactIncremental', () => {
371
ReactNoop.render(<Foo text="foo" />);
372
ReactNoop.flushDeferredPri(52);
373
375
- expect(Scheduler).toHaveYielded(['Foo', 'Bar', 'Tester', 'Bar']);
374
+ assertLog(['Foo', 'Bar', 'Tester', 'Bar']);
375
376
// We're now rendering an update that will bail out on updating middle.
377
ReactNoop.render(<Foo text="bar" />);
378
ReactNoop.flushDeferredPri(45 + 5);
379
381
- expect(Scheduler).toHaveYielded(['Foo', 'Bar', 'Bar']);
380
+ assertLog(['Foo', 'Bar', 'Bar']);
381
382
// Flush the rest to make sure that the bailout didn't block this work.
384
- expect(Scheduler).toFlushAndYield(['Middle']);
383
+ await waitForAll(['Middle']);
384
});
385
387
- xit('can resume work in a bailed subtree within one pass', () => {
386
+ xit('can resume work in a bailed subtree within one pass', async () => {
387
function Bar(props) {
388
Scheduler.unstable_yieldValue('Bar');
389
return <div>{props.children}</div>;
@@ -440,37 +439,37 @@ describe('ReactIncremental', () => {
439
ReactNoop.render(<Foo text="foo" />);
440
ReactNoop.flushDeferredPri(52 + 5);
441
443
- expect(Scheduler).toHaveYielded(['Foo', 'Bar', 'Tester', 'Bar']);
442
+ assertLog(['Foo', 'Bar', 'Tester', 'Bar']);
443
444
// Make a quick update which will create a low pri tree on top of the
445
// already low pri tree.
446
ReactNoop.render(<Foo text="bar" />);
447
ReactNoop.flushDeferredPri(15);
448
450
- expect(Scheduler).toHaveYielded(['Foo']);
449
+ assertLog(['Foo']);
450
451
// At this point, middle will bail out but it has not yet fully rendered.
452
// Since that is the same priority as its parent tree. This should render
453
// as a single batch. Therefore, it is correct that Middle should be in the
454
// middle. If it occurs after the two "Bar" components then it was flushed
455
// after them which is not correct.
457
- expect(Scheduler).toFlushAndYield(['Bar', 'Middle', 'Bar']);
456
+ await waitForAll(['Bar', 'Middle', 'Bar']);
457
458
// Let us try this again without fully finishing the first time. This will
459
// create a hanging subtree that is reconciling at the normal priority.
460
ReactNoop.render(<Foo text="foo" />);
461
ReactNoop.flushDeferredPri(40);
462
464
- expect(Scheduler).toHaveYielded(['Foo', 'Bar']);
463
+ assertLog(['Foo', 'Bar']);
464
465
// This update will create a tree that aborts that work and down-prioritizes
466
// it. If the priority levels aren't down-prioritized correctly this may
467
// abort rendering of the down-prioritized content.
468
ReactNoop.render(<Foo text="bar" />);
470
- expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Bar']);
469
+ await waitForAll(['Foo', 'Bar', 'Bar']);
470
});
471
473
- xit('can resume mounting a class component', () => {
472
+ xit('can resume mounting a class component', async () => {
473
let foo;
474
class Parent extends React.Component {
475
shouldComponentUpdate() {
@@ -501,14 +500,14 @@ describe('ReactIncremental', () => {
500
501
ReactNoop.render(<Parent prop="foo" />);
502
ReactNoop.flushDeferredPri(20);
504
- expect(Scheduler).toHaveYielded(['Foo constructor: foo', 'Foo']);
503
+ assertLog(['Foo constructor: foo', 'Foo']);
504
505
foo.setState({value: 'bar'});
506
508
- expect(Scheduler).toFlushAndYield(['Foo', 'Bar']);
507
+ await waitForAll(['Foo', 'Bar']);
508
});
509
511
- xit('reuses the same instance when resuming a class instance', () => {
510
+ xit('reuses the same instance when resuming a class instance', async () => {
511
let foo;
512
class Parent extends React.Component {
513
shouldComponentUpdate() {
@@ -560,7 +559,7 @@ describe('ReactIncremental', () => {
559
560
ReactNoop.render(<Parent prop="foo" />);
561
ReactNoop.flushDeferredPri(25);
563
- expect(Scheduler).toHaveYielded([
562
+ assertLog([
563
'constructor: foo',
564
'componentWillMount: foo',
565
'render: foo',
@@ -569,9 +568,9 @@ describe('ReactIncremental', () => {
568
569
foo.setState({value: 'bar'});
570
572
- expect(Scheduler).toFlushWithoutYielding();
571
+ await waitForAll([]);
572
expect(constructorCount).toEqual(1);
574
- expect(Scheduler).toHaveYielded([
573
+ assertLog([
574
'componentWillMount: foo',
575
'render: foo',
576
'Foo did complete',
@@ -579,7 +578,7 @@ describe('ReactIncremental', () => {
578
]);
579
});
580
582
- xit('can reuse work done after being preempted', () => {
581
+ xit('can reuse work done after being preempted', async () => {
582
function Bar(props) {
583
Scheduler.unstable_yieldValue('Bar');
584
return <div>{props.children}</div>;
@@ -622,42 +621,42 @@ describe('ReactIncremental', () => {
621
622
// We only finish the higher priority work. So the low pri content
623
// has not yet finished mounting.
625
- expect(Scheduler).toHaveYielded(['Foo', 'Bar', 'Middle', 'Bar']);
624
+ assertLog(['Foo', 'Bar', 'Middle', 'Bar']);
625
626
// Interrupt the rendering with a quick update. This should not touch the
627
// middle content.
628
ReactNoop.render(<Foo text="foo" text2="bar" step={0} />);
630
- expect(Scheduler).toFlushWithoutYielding();
629
+ await waitForAll([]);
630
631
// We've now rendered the entire tree but we didn't have to redo the work
632
// done by the first Middle and Bar already.
634
- expect(Scheduler).toHaveYielded(['Foo', 'Bar', 'Middle']);
633
+ assertLog(['Foo', 'Bar', 'Middle']);
634
635
// Make a quick update which will schedule low priority work to
636
// update the middle content.
637
ReactNoop.render(<Foo text="bar" text2="bar" step={1} />);
638
ReactNoop.flushDeferredPri(30 + 25 + 5);
639
641
- expect(Scheduler).toHaveYielded(['Foo', 'Bar']);
640
+ assertLog(['Foo', 'Bar']);
641
642
// The middle content is now pending rendering...
643
ReactNoop.flushDeferredPri(30 + 5);
645
- expect(Scheduler).toHaveYielded(['Middle', 'Bar']);
644
+ assertLog(['Middle', 'Bar']);
645
646
// but we'll interrupt it to render some higher priority work.
647
// The middle content will bailout so it remains untouched.
648
ReactNoop.render(<Foo text="foo" text2="bar" step={1} />);
649
ReactNoop.flushDeferredPri(30);
650
652
- expect(Scheduler).toHaveYielded(['Foo', 'Bar']);
651
+ assertLog(['Foo', 'Bar']);
652
653
// Since we did nothing to the middle subtree during the interruption,
654
// we should be able to reuse the reconciliation work that we already did
655
// without restarting.
657
- expect(Scheduler).toFlushAndYield(['Middle']);
656
+ await waitForAll(['Middle']);
657
});
658
660
- xit('can reuse work that began but did not complete, after being preempted', () => {
659
+ xit('can reuse work that began but did not complete, after being preempted', async () => {
660
let child;
661
let sibling;
662
@@ -705,13 +704,13 @@ describe('ReactIncremental', () => {
704
}
705
706
ReactNoop.render(<Parent />);
708
- expect(Scheduler).toFlushWithoutYielding();
707
+ await waitForAll([]);
708
709
// Begin working on a low priority update to Child, but stop before
710
// GreatGrandchild. Child and Grandchild begin but don't complete.
711
child.setState({step: 1});
712
ReactNoop.flushDeferredPri(30);
714
- expect(Scheduler).toHaveYielded(['Child', 'Grandchild']);
713
+ assertLog(['Child', 'Grandchild']);
714
715
// Interrupt the current low pri work with a high pri update elsewhere in
716
// the tree.
@@ -719,19 +718,19 @@ describe('ReactIncremental', () => {
718
ReactNoop.flushSync(() => {
719
sibling.setState({});
720
});
722
- expect(Scheduler).toHaveYielded(['Sibling']);
721
+ assertLog(['Sibling']);
722
723
// Continue the low pri work. The work on Child and GrandChild was memoized
724
// so they should not be worked on again.
725
727
- expect(Scheduler).toFlushAndYield([
726
+ await waitForAll([
727
// No Child
728
// No Grandchild
729
'GreatGrandchild',
730
]);
731
});
732
734
- xit('can reuse work if shouldComponentUpdate is false, after being preempted', () => {
733
+ xit('can reuse work if shouldComponentUpdate is false, after being preempted', async () => {
734
function Bar(props) {
735
Scheduler.unstable_yieldValue('Bar');
736
return <div>{props.children}</div>;
@@ -777,40 +776,33 @@ describe('ReactIncremental', () => {
776
777
// Init
778
ReactNoop.render(<Foo text="foo" step={0} />);
780
- expect(Scheduler).toFlushAndYield([
781
- 'Foo',
782
- 'Bar',
783
- 'Content',
784
- 'Middle',
785
- 'Bar',
786
- 'Middle',
787
- ]);
779
+ await waitForAll(['Foo', 'Bar', 'Content', 'Middle', 'Bar', 'Middle']);
780
781
// Make a quick update which will schedule low priority work to
782
// update the middle content.
783
ReactNoop.render(<Foo text="bar" step={1} />);
784
ReactNoop.flushDeferredPri(30 + 5);
785
794
- expect(Scheduler).toHaveYielded(['Foo', 'Bar']);
786
+ assertLog(['Foo', 'Bar']);
787
788
// The middle content is now pending rendering...
789
ReactNoop.flushDeferredPri(30 + 25 + 5);
798
- expect(Scheduler).toHaveYielded(['Content', 'Middle', 'Bar']); // One more Middle left.
790
+ assertLog(['Content', 'Middle', 'Bar']); // One more Middle left.
791
792
// but we'll interrupt it to render some higher priority work.
793
// The middle content will bailout so it remains untouched.
794
ReactNoop.render(<Foo text="foo" step={1} />);
795
ReactNoop.flushDeferredPri(30);
796
805
- expect(Scheduler).toHaveYielded(['Foo', 'Bar']);
797
+ assertLog(['Foo', 'Bar']);
798
799
// Since we did nothing to the middle subtree during the interruption,
800
// we should be able to reuse the reconciliation work that we already did
801
// without restarting.
810
- expect(Scheduler).toFlushAndYield(['Middle']);
802
+ await waitForAll(['Middle']);
803
});
804
813
- it('memoizes work even if shouldComponentUpdate returns false', () => {
805
+ it('memoizes work even if shouldComponentUpdate returns false', async () => {
806
class Foo extends React.Component {
807
shouldComponentUpdate(nextProps) {
808
// this.props is the memoized props. So this should return true for
@@ -826,13 +818,13 @@ describe('ReactIncremental', () => {
818
}
819
820
ReactNoop.render(<Foo step={1} />);
829
- expect(Scheduler).toFlushAndYield(['render']);
821
+ await waitForAll(['render']);
822
823
ReactNoop.render(<Foo step={2} />);
832
- expect(Scheduler).toFlushAndYield(['shouldComponentUpdate: false']);
824
+ await waitForAll(['shouldComponentUpdate: false']);
825
826
ReactNoop.render(<Foo step={3} />);
835
- expect(Scheduler).toFlushAndYield([
827
+ await waitForAll([
828
// If the memoized props were not updated during last bail out, sCU will
829
// keep returning false.
830
'shouldComponentUpdate: true',
@@ -840,7 +832,7 @@ describe('ReactIncremental', () => {
832
]);
833
});
834
843
- it('can update in the middle of a tree using setState', () => {
835
+ it('can update in the middle of a tree using setState', async () => {
836
let instance;
837
class Bar extends React.Component {
838
constructor() {
@@ -862,14 +854,14 @@ describe('ReactIncremental', () => {
854
}
855
856
ReactNoop.render(<Foo />);
865
- expect(Scheduler).toFlushWithoutYielding();
857
+ await waitForAll([]);
858
expect(instance.state).toEqual({a: 'a'});
859
instance.setState({b: 'b'});
868
- expect(Scheduler).toFlushWithoutYielding();
860
+ await waitForAll([]);
861
expect(instance.state).toEqual({a: 'a', b: 'b'});
862
});
863
872
- it('can queue multiple state updates', () => {
864
+ it('can queue multiple state updates', async () => {
865
let instance;
866
class Bar extends React.Component {
867
constructor() {
@@ -891,16 +883,16 @@ describe('ReactIncremental', () => {
883
}
884
885
ReactNoop.render(<Foo />);
894
- expect(Scheduler).toFlushWithoutYielding();
886
+ await waitForAll([]);
887
// Call setState multiple times before flushing
888
instance.setState({b: 'b'});
889
instance.setState({c: 'c'});
890
instance.setState({d: 'd'});
899
- expect(Scheduler).toFlushWithoutYielding();
891
+ await waitForAll([]);
892
expect(instance.state).toEqual({a: 'a', b: 'b', c: 'c', d: 'd'});
893
});
894
903
- it('can use updater form of setState', () => {
895
+ it('can use updater form of setState', async () => {
896
let instance;
897
class Bar extends React.Component {
898
constructor() {
@@ -926,19 +918,19 @@ describe('ReactIncremental', () => {
918
}
919
920
ReactNoop.render(<Foo multiplier={2} />);
929
- expect(Scheduler).toFlushWithoutYielding();
921
+ await waitForAll([]);
922
expect(instance.state.num).toEqual(1);
923
instance.setState(updater);
932
- expect(Scheduler).toFlushWithoutYielding();
924
+ await waitForAll([]);
925
expect(instance.state.num).toEqual(2);
926
927
instance.setState(updater);
928
ReactNoop.render(<Foo multiplier={3} />);
937
- expect(Scheduler).toFlushWithoutYielding();
929
+ await waitForAll([]);
930
expect(instance.state.num).toEqual(6);
931
});
932
941
- it('can call setState inside update callback', () => {
933
+ it('can call setState inside update callback', async () => {
934
let instance;
935
class Bar extends React.Component {
936
constructor() {
@@ -968,15 +960,15 @@ describe('ReactIncremental', () => {
960
}
961
962
ReactNoop.render(<Foo multiplier={2} />);
971
- expect(Scheduler).toFlushWithoutYielding();
963
+ await waitForAll([]);
964
instance.setState(updater);
965
instance.setState(updater, callback);
974
- expect(Scheduler).toFlushWithoutYielding();
966
+ await waitForAll([]);
967
expect(instance.state.num).toEqual(4);
968
expect(instance.state.called).toEqual(true);
969
});
970
979
- it('can replaceState', () => {
971
+ it('can replaceState', async () => {
972
let instance;
973
class Bar extends React.Component {
974
state = {a: 'a'};
@@ -995,15 +987,15 @@ describe('ReactIncremental', () => {
987
}
988
989
ReactNoop.render(<Foo />);
998
- expect(Scheduler).toFlushWithoutYielding();
990
+ await waitForAll([]);
991
instance.setState({b: 'b'});
992
instance.setState({c: 'c'});
993
instance.updater.enqueueReplaceState(instance, {d: 'd'});
1002
- expect(Scheduler).toFlushWithoutYielding();
994
+ await waitForAll([]);
995
expect(instance.state).toEqual({d: 'd'});
996
});
997
1006
- it('can forceUpdate', () => {
998
+ it('can forceUpdate', async () => {
999
function Baz() {
1000
Scheduler.unstable_yieldValue('Baz');
1001
return <div />;
@@ -1034,12 +1026,12 @@ describe('ReactIncremental', () => {
1026
}
1027
1028
ReactNoop.render(<Foo />);
1037
- expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Baz']);
1029
+ await waitForAll(['Foo', 'Bar', 'Baz']);
1030
instance.forceUpdate();
1039
- expect(Scheduler).toFlushAndYield(['Bar', 'Baz']);
1031
+ await waitForAll(['Bar', 'Baz']);
1032
});
1033
1042
- it('should clear forceUpdate after update is flushed', () => {
1034
+ it('should clear forceUpdate after update is flushed', async () => {
1035
let a = 0;
1036
1037
class Foo extends React.PureComponent {
@@ -1052,14 +1044,14 @@ describe('ReactIncremental', () => {
1044
1045
const foo = React.createRef(null);
1046
ReactNoop.render(<Foo ref={foo} b={0} />);
1055
- expect(Scheduler).toFlushAndYield(['A: 0, B: 0']);
1047
+ await waitForAll(['A: 0, B: 0']);
1048
1049
a = 1;
1050
foo.current.forceUpdate();
1059
- expect(Scheduler).toFlushAndYield(['A: 1, B: 0']);
1051
+ await waitForAll(['A: 1, B: 0']);
1052
1053
ReactNoop.render(<Foo ref={foo} b={0} />);
1062
- expect(Scheduler).toFlushAndYield([]);
1054
+ await waitForAll([]);
1055
});
1056
1057
xit('can call sCU while resuming a partly mounted component', () => {
@@ -1092,7 +1084,7 @@ describe('ReactIncremental', () => {
1084
1085
ReactNoop.render(<Foo step={0} />);
1086
ReactNoop.flushDeferredPri(40);
1095
- expect(Scheduler).toHaveYielded(['Foo', 'Bar:A', 'Bar:B', 'Bar:C']);
1087
+ assertLog(['Foo', 'Bar:A', 'Bar:B', 'Bar:C']);
1088
1089
expect(instances.size).toBe(3);
1090
@@ -1101,13 +1093,13 @@ describe('ReactIncremental', () => {
1093
// A was memoized and reused. B was memoized but couldn't be reused because
1094
// props differences. C was memoized and reused. D never even started so it
1095
// needed a new instance.
1104
- expect(Scheduler).toHaveYielded(['Foo', 'Bar:B2', 'Bar:D']);
1096
+ assertLog(['Foo', 'Bar:B2', 'Bar:D']);
1097
1098
// We expect each rerender to correspond to a new instance.
1099
expect(instances.size).toBe(4);
1100
});
1101
1110
- xit('gets new props when setting state on a partly updated component', () => {
1102
+ xit('gets new props when setting state on a partly updated component', async () => {
1103
const instances = [];
1104
1105
class Bar extends React.Component {
@@ -1151,7 +1143,7 @@ describe('ReactIncremental', () => {
1143
<Baz />
1144
</div>,
1145
);
1154
- expect(Scheduler).toFlushWithoutYielding();
1146
+ await waitForAll([]);
1147
1148
// Flush part way through with new props, fully completing the first Bar.
1149
// However, it doesn't commit yet.
@@ -1163,15 +1155,15 @@ describe('ReactIncremental', () => {
1155
</div>,
1156
);
1157
ReactNoop.flushDeferredPri(45);
1166
- expect(Scheduler).toHaveYielded(['Foo', 'Bar:A-1', 'Bar:B-1', 'Baz']);
1158
+ assertLog(['Foo', 'Bar:A-1', 'Bar:B-1', 'Baz']);
1159
1160
// Make an update to the same Bar.
1161
instances[0].performAction();
1162
1171
- expect(Scheduler).toFlushAndYield(['Bar:A-1', 'Baz']);
1163
+ await waitForAll(['Bar:A-1', 'Baz']);
1164
});
1165
1174
- xit('calls componentWillMount twice if the initial render is aborted', () => {
1166
+ xit('calls componentWillMount twice if the initial render is aborted', async () => {
1167
class LifeCycle extends React.Component {
1168
state = {x: this.props.x};
1169
UNSAFE_componentWillReceiveProps(nextProps) {
@@ -1213,10 +1205,10 @@ describe('ReactIncremental', () => {
1205
ReactNoop.render(<App x={0} />);
1206
ReactNoop.flushDeferredPri(30);
1207
1216
- expect(Scheduler).toHaveYielded(['App', 'componentWillMount:0-0']);
1208
+ assertLog(['App', 'componentWillMount:0-0']);
1209
1210
ReactNoop.render(<App x={1} />);
1219
- expect(Scheduler).toFlushAndYield([
1211
+ await waitForAll([
1212
'App',
1213
'componentWillReceiveProps:0-1',
1214
'componentWillMount:1-1',
@@ -1225,7 +1217,7 @@ describe('ReactIncremental', () => {
1217
]);
1218
});
1219
1228
- xit('uses state set in componentWillMount even if initial render was aborted', () => {
1220
+ xit('uses state set in componentWillMount even if initial render was aborted', async () => {
1221
class LifeCycle extends React.Component {
1222
constructor(props) {
1223
super(props);
@@ -1252,14 +1244,10 @@ describe('ReactIncremental', () => {
1244
ReactNoop.render(<App x={0} />);
1245
ReactNoop.flushDeferredPri(20);
1246
1255
- expect(Scheduler).toHaveYielded([
1256
- 'App',
1257
- 'componentWillMount:0(ctor)',
1258
- 'render:0(willMount)',
1259
- ]);
1247
+ assertLog(['App', 'componentWillMount:0(ctor)', 'render:0(willMount)']);
1248
1249
ReactNoop.render(<App x={1} />);
1262
- expect(Scheduler).toFlushAndYield([
1250
+ await waitForAll([
1251
'App',
1252
'componentWillMount:0(willMount)',
1253
'render:1(willMount)',
@@ -1267,7 +1255,7 @@ describe('ReactIncremental', () => {
1255
]);
1256
});
1257
1270
- xit('calls componentWill* twice if an update render is aborted', () => {
1258
+ xit('calls componentWill* twice if an update render is aborted', async () => {
1259
class LifeCycle extends React.Component {
1260
UNSAFE_componentWillMount() {
1261
Scheduler.unstable_yieldValue('componentWillMount:' + this.props.x);
@@ -1316,7 +1304,7 @@ describe('ReactIncremental', () => {
1304
}
1305
1306
ReactNoop.render(<App x={0} />);
1319
- expect(Scheduler).toFlushAndYield([
1307
+ await waitForAll([
1308
'App',
1309
'componentWillMount:0',
1310
'render:0',
@@ -1327,7 +1315,7 @@ describe('ReactIncremental', () => {
1315
ReactNoop.render(<App x={1} />);
1316
ReactNoop.flushDeferredPri(30);
1317
1330
- expect(Scheduler).toHaveYielded([
1318
+ assertLog([
1319
'App',
1320
'componentWillReceiveProps:0-1',
1321
'shouldComponentUpdate:0-1',
@@ -1338,7 +1326,7 @@ describe('ReactIncremental', () => {
1326
]);
1327
1328
ReactNoop.render(<App x={2} />);
1341
- expect(Scheduler).toFlushAndYield([
1329
+ await waitForAll([
1330
'App',
1331
'componentWillReceiveProps:1-2',
1332
'shouldComponentUpdate:1-2',
@@ -1350,7 +1338,7 @@ describe('ReactIncremental', () => {
1338
]);
1339
});
1340
1353
- it('calls getDerivedStateFromProps even for state-only updates', () => {
1341
+ it('calls getDerivedStateFromProps even for state-only updates', async () => {
1342
let instance;
1343
1344
class LifeCycle extends React.Component {
@@ -1373,11 +1361,11 @@ describe('ReactIncremental', () => {
1361
}
1362
1363
ReactNoop.render(<LifeCycle />);
1376
- expect(Scheduler).toFlushAndYield(['getDerivedStateFromProps', 'render']);
1364
+ await waitForAll(['getDerivedStateFromProps', 'render']);
1365
expect(instance.state).toEqual({foo: 'foo'});
1366
1367
instance.changeState();
1380
- expect(Scheduler).toFlushAndYield([
1368
+ await waitForAll([
1369
'getDerivedStateFromProps',
1370
'render',
1371
'componentDidUpdate',
@@ -1385,7 +1373,7 @@ describe('ReactIncremental', () => {
1373
expect(instance.state).toEqual({foo: 'foo'});
1374
});
1375
1388
- it('does not call getDerivedStateFromProps if neither state nor props have changed', () => {
1376
+ it('does not call getDerivedStateFromProps if neither state nor props have changed', async () => {
1377
class Parent extends React.Component {
1378
state = {parentRenders: 0};
1379
static getDerivedStateFromProps(props, prevState) {
@@ -1407,18 +1395,14 @@ describe('ReactIncremental', () => {
1395
1396
const child = React.createRef(null);
1397
ReactNoop.render(<Parent />);
1410
- expect(Scheduler).toFlushAndYield([
1411
- 'getDerivedStateFromProps',
1412
- 'Parent',
1413
- 'Child',
1414
- ]);
1398
+ await waitForAll(['getDerivedStateFromProps', 'Parent', 'Child']);
1399
1400
// Schedule an update on the child. The parent should not re-render.
1401
child.current.setState({});
1418
- expect(Scheduler).toFlushAndYield(['Child']);
1402
+ await waitForAll(['Child']);
1403
});
1404
1421
- xit('does not call componentWillReceiveProps for state-only updates', () => {
1405
+ xit('does not call componentWillReceiveProps for state-only updates', async () => {
1406
const instances = [];
1407
1408
class LifeCycle extends React.Component {
@@ -1492,7 +1476,7 @@ describe('ReactIncremental', () => {
1476
}
1477
1478
ReactNoop.render(<App y={0} />);
1495
- expect(Scheduler).toFlushAndYield([
1479
+ await waitForAll([
1480
'App',
1481
'Wrap',
1482
'componentWillMount:0',
@@ -1506,7 +1490,7 @@ describe('ReactIncremental', () => {
1490
1491
ReactNoop.flushDeferredPri(25);
1492
1509
- expect(Scheduler).toHaveYielded([
1493
+ assertLog([
1494
// no componentWillReceiveProps
1495
'shouldComponentUpdate:0-1',
1496
'componentWillUpdate:0-1',
@@ -1517,7 +1501,7 @@ describe('ReactIncremental', () => {
1501
// LifeCycle
1502
instances[1].tick();
1503
1520
- expect(Scheduler).toFlushAndYield([
1504
+ await waitForAll([
1505
// no componentWillReceiveProps
1506
'shouldComponentUpdate:1-2',
1507
'componentWillUpdate:1-2',
@@ -1532,7 +1516,7 @@ describe('ReactIncremental', () => {
1516
1517
ReactNoop.flushDeferredPri(30);
1518
1535
- expect(Scheduler).toHaveYielded([
1519
+ assertLog([
1520
'Wrap',
1521
'componentWillReceiveProps',
1522
'shouldComponentUpdate:2-2',
@@ -1544,7 +1528,7 @@ describe('ReactIncremental', () => {
1528
// Next we will update LifeCycle directly but not with new props.
1529
instances[1].tick();
1530
1547
- expect(Scheduler).toFlushAndYield([
1531
+ await waitForAll([
1532
// This should not trigger another componentWillReceiveProps because
1533
// we never got new props.
1534
'shouldComponentUpdate:2-3',
@@ -1557,7 +1541,7 @@ describe('ReactIncremental', () => {
1541
// incomplete parents.
1542
});
1543
1560
- xit('skips will/DidUpdate when bailing unless an update was already in progress', () => {
1544
+ xit('skips will/DidUpdate when bailing unless an update was already in progress', async () => {
1545
class LifeCycle extends React.Component {
1546
UNSAFE_componentWillMount() {
1547
Scheduler.unstable_yieldValue('componentWillMount');
@@ -1595,7 +1579,7 @@ describe('ReactIncremental', () => {
1579
}
1580
1581
ReactNoop.render(<App x={0} />);
1598
- expect(Scheduler).toFlushAndYield([
1582
+ await waitForAll([
1583
'componentWillMount',
1584
'render',
1585
'render sibling',
@@ -1604,7 +1588,7 @@ describe('ReactIncremental', () => {
1588
1589
// Update to same props
1590
ReactNoop.render(<App x={0} />);
1607
- expect(Scheduler).toFlushAndYield([
1591
+ await waitForAll([
1592
'componentWillReceiveProps',
1593
'shouldComponentUpdate',
1594
// no componentWillUpdate
@@ -1617,7 +1601,7 @@ describe('ReactIncremental', () => {
1601
ReactNoop.render(<App x={1} />);
1602
ReactNoop.flushDeferredPri(30);
1603
1620
- expect(Scheduler).toHaveYielded([
1604
+ assertLog([
1605
'componentWillReceiveProps',
1606
'shouldComponentUpdate',
1607
'componentWillUpdate',
@@ -1628,10 +1612,10 @@ describe('ReactIncremental', () => {
1612
1613
// ...but we'll interrupt it to rerender the same props.
1614
ReactNoop.render(<App x={1} />);
1631
- expect(Scheduler).toFlushWithoutYielding();
1615
+ await waitForAll([]);
1616
1617
// We can bail out this time, but we must call componentDidUpdate.
1634
- expect(Scheduler).toHaveYielded([
1618
+ assertLog([
1619
'componentWillReceiveProps',
1620
'shouldComponentUpdate',
1621
// no componentWillUpdate
@@ -1641,7 +1625,7 @@ describe('ReactIncremental', () => {
1625
]);
1626
});
1627
1644
- it('can nest batchedUpdates', () => {
1628
+ it('can nest batchedUpdates', async () => {
1629
let instance;
1630
1631
class Foo extends React.Component {
@@ -1653,7 +1637,7 @@ describe('ReactIncremental', () => {
1637
}
1638
1639
ReactNoop.render(<Foo />);
1656
- expect(Scheduler).toFlushWithoutYielding();
1640
+ await waitForAll([]);
1641
1642
ReactNoop.flushSync(() => {
1643
ReactNoop.batchedUpdates(() => {
@@ -1678,7 +1662,7 @@ describe('ReactIncremental', () => {
1662
1663
// ReactNoop.flush() not needed because updates are synchronous
1664
1681
- expect(Scheduler).toHaveYielded([
1665
+ assertLog([
1666
'end inner batchedUpdates',
1667
'end outer batchedUpdates',
1668
'setState 1',
@@ -1689,7 +1673,7 @@ describe('ReactIncremental', () => {
1673
expect(instance.state.n).toEqual(4);
1674
});
1675
1692
- it('can handle if setState callback throws', () => {
1676
+ it('can handle if setState callback throws', async () => {
1677
let instance;
1678
1679
class Foo extends React.Component {
@@ -1701,7 +1685,7 @@ describe('ReactIncremental', () => {
1685
}
1686
1687
ReactNoop.render(<Foo />);
1704
- expect(Scheduler).toFlushWithoutYielding();
1688
+ await waitForAll([]);
1689
1690
function updater({n}) {
1691
return {n: n + 1};
@@ -1718,16 +1702,14 @@ describe('ReactIncremental', () => {
1702
Scheduler.unstable_yieldValue('third callback'),
1703
);
1704
1721
- expect(() => {
1722
- expect(Scheduler).toFlushWithoutYielding();
1723
- }).toThrow('callback error');
1705
+ await waitForThrow('callback error');
1706
1707
// The third callback isn't called because the second one throws
1726
- expect(Scheduler).toHaveYielded(['first callback', 'second callback']);
1708
+ assertLog(['first callback', 'second callback']);
1709
expect(instance.state.n).toEqual(3);
1710
});
1711
1730
- it('merges and masks context', () => {
1712
+ it('merges and masks context', async () => {
1713
class Intl extends React.Component {
1714
static childContextTypes = {
1715
locale: PropTypes.string,
@@ -1825,7 +1807,7 @@ describe('ReactIncremental', () => {
1807
</div>
1808
</Intl>,
1809
);
1828
- expect(Scheduler).toFlushAndYield([
1810
+ await waitForAll([
1811
'Intl {}',
1812
'ShowLocale {"locale":"fr"}',
1813
'ShowBoth {"locale":"fr"}',
@@ -1839,7 +1821,7 @@ describe('ReactIncremental', () => {
1821
</div>
1822
</Intl>,
1823
);
1842
- expect(Scheduler).toFlushAndYield([
1824
+ await waitForAll([
1825
'Intl {}',
1826
'ShowLocale {"locale":"de"}',
1827
'ShowBoth {"locale":"de"}',
@@ -1854,7 +1836,7 @@ describe('ReactIncremental', () => {
1836
</Intl>,
1837
);
1838
});
1857
- expect(Scheduler).toFlushAndYieldThrough(['Intl {}']);
1839
+ await waitFor(['Intl {}']);
1840
1841
ReactNoop.render(
1842
<Intl locale="en">
@@ -1865,7 +1847,7 @@ describe('ReactIncremental', () => {
1847
<ShowBoth />
1848
</Intl>,
1849
);
1868
- expect(Scheduler).toFlushAndYield([
1850
+ await waitForAll([
1851
'ShowLocale {"locale":"sv"}',
1852
'ShowBoth {"locale":"sv"}',
1853
'Intl {}',
@@ -1882,7 +1864,7 @@ describe('ReactIncremental', () => {
1864
]);
1865
});
1866
1885
- it('does not leak own context into context provider', () => {
1867
+ it('does not leak own context into context provider', async () => {
1868
class Recurse extends React.Component {
1869
static contextTypes = {
1870
n: PropTypes.number,
@@ -1905,7 +1887,7 @@ describe('ReactIncremental', () => {
1887
}
1888
1889
ReactNoop.render(<Recurse />);
1908
- expect(Scheduler).toFlushAndYield([
1890
+ await waitForAll([
1891
'Recurse {}',
1892
'Recurse {"n":2}',
1893
'Recurse {"n":1}',
@@ -1955,7 +1937,7 @@ describe('ReactIncremental', () => {
1937
}
1938
1939
// @gate www
1958
- it('provides context when reusing work', () => {
1940
+ it('provides context when reusing work', async () => {
1941
class Intl extends React.Component {
1942
static childContextTypes = {
1943
locale: PropTypes.string,
@@ -1997,20 +1979,20 @@ describe('ReactIncremental', () => {
1979
</Intl>,
1980
);
1981
});
2000
- expect(Scheduler).toFlushAndYieldThrough([
1982
+ await waitFor([
1983
'Intl {}',
1984
'ShowLocale {"locale":"fr"}',
1985
'ShowLocale {"locale":"fr"}',
1986
]);
1987
2006
- expect(Scheduler).toFlushAndYield([
1988
+ await waitForAll([
1989
'ShowLocale {"locale":"fr"}',
1990
'Intl {}',
1991
'ShowLocale {"locale":"ru"}',
1992
]);
1993
});
1994
2013
- it('reads context when setState is below the provider', () => {
1995
+ it('reads context when setState is below the provider', async () => {
1996
let statefulInst;
1997
1998
class Intl extends React.Component {
@@ -2090,7 +2072,7 @@ describe('ReactIncremental', () => {
2072
</IndirectionFn>
2073
</Intl>,
2074
);
2093
- expect(Scheduler).toFlushAndYield([
2075
+ await waitForAll([
2076
'Intl:read {}',
2077
'Intl:provide {"locale":"fr"}',
2078
'IndirectionFn {}',
@@ -2100,13 +2082,13 @@ describe('ReactIncremental', () => {
2082
]);
2083
2084
statefulInst.setState({x: 1});
2103
- expect(Scheduler).toFlushWithoutYielding();
2085
+ await waitForAll([]);
2086
// All work has been memoized because setState()
2087
// happened below the context and could not have affected it.
2106
- expect(Scheduler).toHaveYielded([]);
2088
+ assertLog([]);
2089
});
2090
2109
- it('reads context when setState is above the provider', () => {
2091
+ it('reads context when setState is above the provider', async () => {
2092
let statefulInst;
2093
2094
class Intl extends React.Component {
@@ -2184,7 +2166,7 @@ describe('ReactIncremental', () => {
2166
</IndirectionFn>
2167
</Stateful>,
2168
);
2187
- expect(Scheduler).toFlushAndYield([
2169
+ await waitForAll([
2170
'Intl:read {}',
2171
'Intl:provide {"locale":"fr"}',
2172
'IndirectionFn {}',
@@ -2194,7 +2176,7 @@ describe('ReactIncremental', () => {
2176
]);
2177
2178
statefulInst.setState({locale: 'gr'});
2197
- expect(Scheduler).toFlushAndYield([
2179
+ await waitForAll([
2180
// Intl is below setState() so it might have been
2181
// affected by it. Therefore we re-render and recompute
2182
// its child context.
@@ -2210,7 +2192,7 @@ describe('ReactIncremental', () => {
2192
]);
2193
});
2194
2213
- it('maintains the correct context when providers bail out due to low priority', () => {
2195
+ it('maintains the correct context when providers bail out due to low priority', async () => {
2196
class Root extends React.Component {
2197
render() {
2198
return <Middle {...this.props} />;
@@ -2246,14 +2228,14 @@ describe('ReactIncremental', () => {
2228
2229
// Init
2230
ReactNoop.render(<Root />);
2249
- expect(Scheduler).toFlushWithoutYielding();
2231
+ await waitForAll([]);
2232
2233
// Trigger an update in the middle of the tree
2234
instance.setState({});
2253
- expect(Scheduler).toFlushWithoutYielding();
2235
+ await waitForAll([]);
2236
});
2237
2256
- it('maintains the correct context when unwinding due to an error in render', () => {
2238
+ it('maintains the correct context when unwinding due to an error in render', async () => {
2239
class Root extends React.Component {
2240
componentDidCatch(error) {
2241
// If context is pushed/popped correctly,
@@ -2292,7 +2274,7 @@ describe('ReactIncremental', () => {
2274
2275
// Init
2276
ReactNoop.render(<Root />);
2295
- expect(Scheduler).toFlushWithoutYielding();
2277
+ await waitForAll([]);
2278
2279
// Trigger an update in the middle of the tree
2280
// This is necessary to reproduce the error as it currently exists.
@@ -2304,7 +2286,7 @@ describe('ReactIncremental', () => {
2286
);
2287
});
2288
2307
- it('should not recreate masked context unless inputs have changed', () => {
2289
+ it('should not recreate masked context unless inputs have changed', async () => {
2290
let scuCounter = 0;
2291
2292
class MyComponent extends React.Component {
@@ -2334,7 +2316,7 @@ describe('ReactIncremental', () => {
2316
}
2317
2318
ReactNoop.render(<MyComponent />);
2337
- expect(Scheduler).toFlushAndYield([
2319
+ await waitForAll([
2320
'render',
2321
'componentDidMount',
2322
'shouldComponentUpdate',
@@ -2346,7 +2328,7 @@ describe('ReactIncremental', () => {
2328
]);
2329
});
2330
2349
- xit('should reuse memoized work if pointers are updated before calling lifecycles', () => {
2331
+ xit('should reuse memoized work if pointers are updated before calling lifecycles', async () => {
2332
const cduNextProps = [];
2333
const cduPrevProps = [];
2334
const scuNextProps = [];
@@ -2395,7 +2377,7 @@ describe('ReactIncremental', () => {
2377
// Initial render of the entire tree.
2378
// Renders: Root, Middle, FirstChild, SecondChild
2379
ReactNoop.render(<Root>A</Root>);
2398
- expect(Scheduler).toFlushWithoutYielding();
2380
+ await waitForAll([]);
2381
2382
expect(renderCounter).toBe(1);
2383
@@ -2417,7 +2399,7 @@ describe('ReactIncremental', () => {
2399
// The in-progress child content will bailout.
2400
// Renders: Root, Middle, FirstChild, SecondChild
2401
ReactNoop.render(<Root>B</Root>);
2420
- expect(Scheduler).toFlushWithoutYielding();
2402
+ await waitForAll([]);
2403
2404
// At this point the higher priority render has completed.
2405
// Since FirstChild props didn't change, sCU returned false.
@@ -2429,7 +2411,7 @@ describe('ReactIncremental', () => {
2411
expect(cduNextProps).toEqual([{children: 'B'}]);
2412
});
2413
2432
- it('updates descendants with new context values', () => {
2414
+ it('updates descendants with new context values', async () => {
2415
let instance;
2416
2417
class TopContextProvider extends React.Component {
@@ -2473,12 +2455,12 @@ describe('ReactIncremental', () => {
2455
</TopContextProvider>,
2456
);
2457
2476
- expect(Scheduler).toFlushAndYield(['count:0']);
2458
+ await waitForAll(['count:0']);
2459
instance.updateCount();
2478
- expect(Scheduler).toFlushAndYield(['count:1']);
2460
+ await waitForAll(['count:1']);
2461
});
2462
2481
- it('updates descendants with multiple context-providing ancestors with new context values', () => {
2463
+ it('updates descendants with multiple context-providing ancestors with new context values', async () => {
2464
let instance;
2465
2466
class TopContextProvider extends React.Component {
@@ -2528,12 +2510,12 @@ describe('ReactIncremental', () => {
2510
</TopContextProvider>,
2511
);
2512
2531
- expect(Scheduler).toFlushAndYield(['count:0']);
2513
+ await waitForAll(['count:0']);
2514
instance.updateCount();
2533
- expect(Scheduler).toFlushAndYield(['count:1']);
2515
+ await waitForAll(['count:1']);
2516
});
2517
2536
- it('should not update descendants with new context values if shouldComponentUpdate returns false', () => {
2518
+ it('should not update descendants with new context values if shouldComponentUpdate returns false', async () => {
2519
let instance;
2520
2521
class TopContextProvider extends React.Component {
@@ -2592,12 +2574,12 @@ describe('ReactIncremental', () => {
2574
</TopContextProvider>,
2575
);
2576
2595
- expect(Scheduler).toFlushAndYield(['count:0']);
2577
+ await waitForAll(['count:0']);
2578
instance.updateCount();
2597
- expect(Scheduler).toFlushWithoutYielding();
2579
+ await waitForAll([]);
2580
});
2581
2600
- it('should update descendants with new context values if setState() is called in the middle of the tree', () => {
2582
+ it('should update descendants with new context values if setState() is called in the middle of the tree', async () => {
2583
let middleInstance;
2584
let topInstance;
2585
@@ -2668,14 +2650,14 @@ describe('ReactIncremental', () => {
2650
</TopContextProvider>,
2651
);
2652
2671
- expect(Scheduler).toFlushAndYield(['count:0, name:brian']);
2653
+ await waitForAll(['count:0, name:brian']);
2654
topInstance.updateCount();
2673
- expect(Scheduler).toFlushWithoutYielding();
2655
+ await waitForAll([]);
2656
middleInstance.updateName('not brian');
2675
- expect(Scheduler).toFlushAndYield(['count:1, name:not brian']);
2657
+ await waitForAll(['count:1, name:not brian']);
2658
});
2659
2678
- it('does not interrupt for update at same priority', () => {
2660
+ it('does not interrupt for update at same priority', async () => {
2661
function Parent(props) {
2662
Scheduler.unstable_yieldValue('Parent: ' + props.step);
2663
return <Child step={props.step} />;
@@ -2689,15 +2671,15 @@ describe('ReactIncremental', () => {
2671
React.startTransition(() => {
2672
ReactNoop.render(<Parent step={1} />);
2673
});
2692
- expect(Scheduler).toFlushAndYieldThrough(['Parent: 1']);
2674
+ await waitFor(['Parent: 1']);
2675
2676
// Interrupt at same priority
2677
ReactNoop.render(<Parent step={2} />);
2678
2697
- expect(Scheduler).toFlushAndYield(['Child: 1', 'Parent: 2', 'Child: 2']);
2679
+ await waitForAll(['Child: 1', 'Parent: 2', 'Child: 2']);
2680
});
2681
2700
- it('does not interrupt for update at lower priority', () => {
2682
+ it('does not interrupt for update at lower priority', async () => {
2683
function Parent(props) {
2684
Scheduler.unstable_yieldValue('Parent: ' + props.step);
2685
return <Child step={props.step} />;
@@ -2711,16 +2693,16 @@ describe('ReactIncremental', () => {
2693
React.startTransition(() => {
2694
ReactNoop.render(<Parent step={1} />);
2695
});
2714
- expect(Scheduler).toFlushAndYieldThrough(['Parent: 1']);
2696
+ await waitFor(['Parent: 1']);
2697
2698
// Interrupt at lower priority
2699
ReactNoop.expire(2000);
2700
ReactNoop.render(<Parent step={2} />);
2701
2720
- expect(Scheduler).toFlushAndYield(['Child: 1', 'Parent: 2', 'Child: 2']);
2702
+ await waitForAll(['Child: 1', 'Parent: 2', 'Child: 2']);
2703
});
2704
2723
- it('does interrupt for update at higher priority', () => {
2705
+ it('does interrupt for update at higher priority', async () => {
2706
function Parent(props) {
2707
Scheduler.unstable_yieldValue('Parent: ' + props.step);
2708
return <Child step={props.step} />;
@@ -2734,20 +2716,20 @@ describe('ReactIncremental', () => {
2716
React.startTransition(() => {
2717
ReactNoop.render(<Parent step={1} />);
2718
});
2737
- expect(Scheduler).toFlushAndYieldThrough(['Parent: 1']);
2719
+ await waitFor(['Parent: 1']);
2720
2721
// Interrupt at higher priority
2722
ReactNoop.flushSync(() => ReactNoop.render(<Parent step={2} />));
2741
- expect(Scheduler).toHaveYielded(['Parent: 2', 'Child: 2']);
2723
+ assertLog(['Parent: 2', 'Child: 2']);
2724
2743
- expect(Scheduler).toFlushAndYield([]);
2725
+ await waitForAll([]);
2726
});
2727
2728
// We sometimes use Maps with Fibers as keys.
2747
- it('does not break with a bad Map polyfill', () => {
2729
+ it('does not break with a bad Map polyfill', async () => {
2730
const realMapSet = Map.prototype.set;
2731
2750
- function triggerCodePathThatUsesFibersAsMapKeys() {
2732
+ async function triggerCodePathThatUsesFibersAsMapKeys() {
2733
function Thing() {
2734
throw new Error('No.');
2735
}
@@ -2770,8 +2752,8 @@ describe('ReactIncremental', () => {
2752
<Boundary />
2753
</React.StrictMode>,
2754
);
2773
- expect(() => {
2774
- expect(Scheduler).toFlushWithoutYielding();
2755
+ await expect(async () => {
2756
+ await waitForAll([]);
2757
}).toErrorDev([
2758
'Legacy context API has been detected within a strict-mode tree',
2759
]);
@@ -2793,9 +2775,15 @@ describe('ReactIncremental', () => {
2775
React = require('react');
2776
ReactNoop = require('react-noop-renderer');
2777
Scheduler = require('scheduler');
2778
+ let InternalTestUtils = require('internal-test-utils');
2779
+ waitForAll = InternalTestUtils.waitForAll;
2780
+ waitFor = InternalTestUtils.waitFor;
2781
+ waitForThrow = InternalTestUtils.waitForThrow;
2782
+ assertLog = InternalTestUtils.assertLog;
2783
+
2784
try {
2785
receivedNonExtensibleObjects = false;
2798
- triggerCodePathThatUsesFibersAsMapKeys();
2786
+ await triggerCodePathThatUsesFibersAsMapKeys();
2787
} finally {
2788
// eslint-disable-next-line no-extend-native
2789
Map.prototype.set = realMapSet;
@@ -2821,8 +2809,14 @@ describe('ReactIncremental', () => {
2809
React = require('react');
2810
ReactNoop = require('react-noop-renderer');
2811
Scheduler = require('scheduler');
2812
+ InternalTestUtils = require('internal-test-utils');
2813
+ waitForAll = InternalTestUtils.waitForAll;
2814
+ waitFor = InternalTestUtils.waitFor;
2815
+ waitForThrow = InternalTestUtils.waitForThrow;
2816
+ assertLog = InternalTestUtils.assertLog;
2817
+
2818
try {
2825
- triggerCodePathThatUsesFibersAsMapKeys();
2819
+ await triggerCodePathThatUsesFibersAsMapKeys();
2820
} finally {
2821
// eslint-disable-next-line no-extend-native
2822
Map.prototype.set = realMapSet;
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+141
-159
@@ -16,6 +16,10 @@ let React;
16
let ReactNoop;
17
let Scheduler;
18
let act;
19
+let assertLog;
20
+let waitForAll;
21
+let waitFor;
22
+let waitForThrow;
23
24
describe('ReactIncrementalErrorHandling', () => {
25
beforeEach(() => {
@@ -27,6 +31,12 @@ describe('ReactIncrementalErrorHandling', () => {
31
ReactNoop = require('react-noop-renderer');
32
Scheduler = require('scheduler');
33
act = require('jest-react').act;
34
+
35
+ const InternalTestUtils = require('internal-test-utils');
36
+ assertLog = InternalTestUtils.assertLog;
37
+ waitForAll = InternalTestUtils.waitForAll;
38
+ waitFor = InternalTestUtils.waitFor;
39
+ waitForThrow = InternalTestUtils.waitForThrow;
40
});
41
42
afterEach(() => {
@@ -55,7 +65,7 @@ describe('ReactIncrementalErrorHandling', () => {
65
);
66
}
67
58
- it('recovers from errors asynchronously', () => {
68
+ it('recovers from errors asynchronously', async () => {
69
class ErrorBoundary extends React.Component {
70
state = {error: null};
71
static getDerivedStateFromError(error) {
@@ -104,7 +114,7 @@ describe('ReactIncrementalErrorHandling', () => {
114
});
115
116
// Start rendering asynchronously
107
- expect(Scheduler).toFlushAndYieldThrough([
117
+ await waitFor([
118
'ErrorBoundary (try)',
119
'Indirection',
120
'Indirection',
@@ -114,9 +124,9 @@ describe('ReactIncrementalErrorHandling', () => {
124
]);
125
126
// Still rendering async...
117
- expect(Scheduler).toFlushAndYieldThrough(['Indirection']);
127
+ await waitFor(['Indirection']);
128
119
- expect(Scheduler).toFlushAndYieldThrough([
129
+ await waitFor([
130
'Indirection',
131
132
// Call getDerivedStateFromError and re-render the error boundary, this
@@ -153,7 +163,7 @@ describe('ReactIncrementalErrorHandling', () => {
163
);
164
});
165
156
- it('recovers from errors asynchronously (legacy, no getDerivedStateFromError)', () => {
166
+ it('recovers from errors asynchronously (legacy, no getDerivedStateFromError)', async () => {
167
class ErrorBoundary extends React.Component {
168
state = {error: null};
169
componentDidCatch(error) {
@@ -202,7 +212,7 @@ describe('ReactIncrementalErrorHandling', () => {
212
});
213
214
// Start rendering asynchronously
205
- expect(Scheduler).toFlushAndYieldThrough([
215
+ await waitFor([
216
'ErrorBoundary (try)',
217
'Indirection',
218
'Indirection',
@@ -212,9 +222,9 @@ describe('ReactIncrementalErrorHandling', () => {
222
]);
223
224
// Still rendering async...
215
- expect(Scheduler).toFlushAndYieldThrough(['Indirection']);
225
+ await waitFor(['Indirection']);
226
217
- expect(Scheduler).toFlushAndYieldThrough([
227
+ await waitFor([
228
'Indirection',
229
// Now that the tree is complete, and there's no remaining work, React
230
// reverts to legacy mode to retry one more time before handling the error.
@@ -254,7 +264,7 @@ describe('ReactIncrementalErrorHandling', () => {
264
React.startTransition(() => {
265
ReactNoop.render(<App isBroken={true} />, onCommit);
266
});
257
- expect(Scheduler).toFlushAndYieldThrough(['error']);
267
+ await waitFor(['error']);
268
269
React.startTransition(() => {
270
// This update is in a separate batch
@@ -268,7 +278,7 @@ describe('ReactIncrementalErrorHandling', () => {
278
// to recover from the error is synchronous, this should be enough to
279
// finish the rest of the work.
280
Scheduler.unstable_flushNumberOfYields(1);
271
- expect(Scheduler).toHaveYielded([
281
+ assertLog([
282
'success',
283
// Nothing commits until the second update completes.
284
'commit',
@@ -280,7 +290,7 @@ describe('ReactIncrementalErrorHandling', () => {
290
});
291
292
// @gate www
283
- it('does not include offscreen work when retrying after an error', () => {
293
+ it('does not include offscreen work when retrying after an error', async () => {
294
function App(props) {
295
if (props.isBroken) {
296
Scheduler.unstable_yieldValue('error');
@@ -304,7 +314,7 @@ describe('ReactIncrementalErrorHandling', () => {
314
React.startTransition(() => {
315
ReactNoop.render(<App isBroken={true} />, onCommit);
316
});
307
- expect(Scheduler).toFlushAndYieldThrough(['error']);
317
+ await waitFor(['error']);
318
319
expect(ReactNoop).toMatchRenderedOutput(null);
320
@@ -320,7 +330,7 @@ describe('ReactIncrementalErrorHandling', () => {
330
// to recover from the error is synchronous, this should be enough to
331
// finish the rest of the work.
332
Scheduler.unstable_flushNumberOfYields(1);
323
- expect(Scheduler).toHaveYielded([
333
+ assertLog([
334
'success',
335
// Nothing commits until the second update completes.
336
'commit',
@@ -335,7 +345,7 @@ describe('ReactIncrementalErrorHandling', () => {
345
);
346
347
// The offscreen content finishes in a subsequent render
338
- expect(Scheduler).toFlushAndYield([]);
348
+ await waitForAll([]);
349
expect(ReactNoop).toMatchRenderedOutput(
350
<>
351
Everything is fine
@@ -346,7 +356,7 @@ describe('ReactIncrementalErrorHandling', () => {
356
);
357
});
358
349
- it('retries one more time before handling error', () => {
359
+ it('retries one more time before handling error', async () => {
360
function BadRender({unused}) {
361
Scheduler.unstable_yieldValue('BadRender');
362
throw new Error('oops');
@@ -374,19 +384,14 @@ describe('ReactIncrementalErrorHandling', () => {
384
});
385
386
// Render the bad component asynchronously
377
- expect(Scheduler).toFlushAndYieldThrough(['Parent', 'BadRender']);
387
+ await waitFor(['Parent', 'BadRender']);
388
389
// Finish the rest of the async work
380
- expect(Scheduler).toFlushAndYieldThrough(['Sibling']);
390
+ await waitFor(['Sibling']);
391
392
// Old scheduler renders, commits, and throws synchronously
393
expect(() => Scheduler.unstable_flushNumberOfYields(1)).toThrow('oops');
384
- expect(Scheduler).toHaveYielded([
385
- 'Parent',
386
- 'BadRender',
387
- 'Sibling',
388
- 'commit',
389
- ]);
394
+ assertLog(['Parent', 'BadRender', 'Sibling', 'commit']);
395
expect(ReactNoop).toMatchRenderedOutput(null);
396
});
397
@@ -418,7 +423,7 @@ describe('ReactIncrementalErrorHandling', () => {
423
});
424
425
// Render part of the tree
421
- expect(Scheduler).toFlushAndYieldThrough(['A', 'B']);
426
+ await waitFor(['A', 'B']);
427
428
// Expire the render midway through
429
Scheduler.unstable_advanceTime(10000);
@@ -428,7 +433,7 @@ describe('ReactIncrementalErrorHandling', () => {
433
ReactNoop.flushSync();
434
}).toThrow('Oops');
435
431
- expect(Scheduler).toHaveYielded([
436
+ assertLog([
437
// The render expired, but we shouldn't throw out the partial work.
438
// Finish the current level.
439
'Oops',
@@ -446,7 +451,7 @@ describe('ReactIncrementalErrorHandling', () => {
451
expect(ReactNoop).toMatchRenderedOutput(null);
452
});
453
449
- it('calls componentDidCatch multiple times for multiple errors', () => {
454
+ it('calls componentDidCatch multiple times for multiple errors', async () => {
455
let id = 0;
456
class BadMount extends React.Component {
457
componentDidMount() {
@@ -481,7 +486,7 @@ describe('ReactIncrementalErrorHandling', () => {
486
</ErrorBoundary>,
487
);
488
484
- expect(Scheduler).toFlushAndYield([
489
+ await waitForAll([
490
'ErrorBoundary',
491
'BadMount',
492
'BadMount',
@@ -495,7 +500,7 @@ describe('ReactIncrementalErrorHandling', () => {
500
);
501
});
502
498
- it('catches render error in a boundary during full deferred mounting', () => {
503
+ it('catches render error in a boundary during full deferred mounting', async () => {
504
class ErrorBoundary extends React.Component {
505
state = {error: null};
506
componentDidCatch(error) {
@@ -520,13 +525,13 @@ describe('ReactIncrementalErrorHandling', () => {
525
<BrokenRender />
526
</ErrorBoundary>,
527
);
523
- expect(Scheduler).toFlushWithoutYielding();
528
+ await waitForAll([]);
529
expect(ReactNoop).toMatchRenderedOutput(
530
<span prop="Caught an error: Hello." />,
531
);
532
});
533
529
- it('catches render error in a boundary during partial deferred mounting', () => {
534
+ it('catches render error in a boundary during partial deferred mounting', async () => {
535
class ErrorBoundary extends React.Component {
536
state = {error: null};
537
componentDidCatch(error) {
@@ -558,10 +563,10 @@ describe('ReactIncrementalErrorHandling', () => {
563
);
564
});
565
561
- expect(Scheduler).toFlushAndYieldThrough(['ErrorBoundary render success']);
566
+ await waitFor(['ErrorBoundary render success']);
567
expect(ReactNoop).toMatchRenderedOutput(null);
568
564
- expect(Scheduler).toFlushAndYield([
569
+ await waitForAll([
570
'BrokenRender',
571
// React retries one more time
572
'ErrorBoundary render success',
@@ -608,7 +613,7 @@ describe('ReactIncrementalErrorHandling', () => {
613
);
614
});
615
611
- expect(Scheduler).toHaveYielded([
616
+ assertLog([
617
'ErrorBoundary render success',
618
'BrokenRender',
619
@@ -658,7 +663,7 @@ describe('ReactIncrementalErrorHandling', () => {
663
);
664
});
665
661
- expect(Scheduler).toHaveYielded([
666
+ assertLog([
667
'ErrorBoundary render success',
668
'BrokenRender',
669
@@ -675,7 +680,7 @@ describe('ReactIncrementalErrorHandling', () => {
680
);
681
});
682
678
- it('propagates an error from a noop error boundary during full deferred mounting', () => {
683
+ it('propagates an error from a noop error boundary during full deferred mounting', async () => {
684
class RethrowErrorBoundary extends React.Component {
685
componentDidCatch(error) {
686
Scheduler.unstable_yieldValue('RethrowErrorBoundary componentDidCatch');
@@ -698,23 +703,22 @@ describe('ReactIncrementalErrorHandling', () => {
703
</RethrowErrorBoundary>,
704
);
705
701
- expect(() => {
702
- expect(Scheduler).toFlushAndYield([
703
- 'RethrowErrorBoundary render',
704
- 'BrokenRender',
706
+ await waitForThrow('Hello');
707
+ assertLog([
708
+ 'RethrowErrorBoundary render',
709
+ 'BrokenRender',
710
706
- // React retries one more time
707
- 'RethrowErrorBoundary render',
708
- 'BrokenRender',
711
+ // React retries one more time
712
+ 'RethrowErrorBoundary render',
713
+ 'BrokenRender',
714
710
- // Errored again on retry. Now handle it.
711
- 'RethrowErrorBoundary componentDidCatch',
712
- ]);
713
- }).toThrow('Hello');
715
+ // Errored again on retry. Now handle it.
716
+ 'RethrowErrorBoundary componentDidCatch',
717
+ ]);
718
expect(ReactNoop.getChildrenAsJSX()).toEqual(null);
719
});
720
717
- it('propagates an error from a noop error boundary during partial deferred mounting', () => {
721
+ it('propagates an error from a noop error boundary during partial deferred mounting', async () => {
722
class RethrowErrorBoundary extends React.Component {
723
componentDidCatch(error) {
724
Scheduler.unstable_yieldValue('RethrowErrorBoundary componentDidCatch');
@@ -739,12 +743,10 @@ describe('ReactIncrementalErrorHandling', () => {
743
);
744
});
745
742
- expect(Scheduler).toFlushAndYieldThrough(['RethrowErrorBoundary render']);
746
+ await waitFor(['RethrowErrorBoundary render']);
747
744
- expect(() => {
745
- expect(Scheduler).toFlushWithoutYielding();
746
- }).toThrow('Hello');
747
- expect(Scheduler).toHaveYielded([
748
+ await waitForThrow('Hello');
749
+ assertLog([
750
'BrokenRender',
751
752
// React retries one more time
@@ -783,7 +785,7 @@ describe('ReactIncrementalErrorHandling', () => {
785
);
786
});
787
}).toThrow('Hello');
786
- expect(Scheduler).toHaveYielded([
788
+ assertLog([
789
'RethrowErrorBoundary render',
790
'BrokenRender',
791
@@ -826,7 +828,7 @@ describe('ReactIncrementalErrorHandling', () => {
828
);
829
});
830
}).toThrow('Hello');
829
- expect(Scheduler).toHaveYielded([
831
+ assertLog([
832
'RethrowErrorBoundary render',
833
'BrokenRender',
834
@@ -840,7 +842,7 @@ describe('ReactIncrementalErrorHandling', () => {
842
expect(ReactNoop).toMatchRenderedOutput(null);
843
});
844
843
- it('applies batched updates regardless despite errors in scheduling', () => {
845
+ it('applies batched updates regardless despite errors in scheduling', async () => {
846
ReactNoop.render(<span prop="a:1" />);
847
expect(() => {
848
ReactNoop.batchedUpdates(() => {
@@ -849,11 +851,11 @@ describe('ReactIncrementalErrorHandling', () => {
851
throw new Error('Hello');
852
});
853
}).toThrow('Hello');
852
- expect(Scheduler).toFlushWithoutYielding();
854
+ await waitForAll([]);
855
expect(ReactNoop).toMatchRenderedOutput(<span prop="a:3" />);
856
});
857
856
- it('applies nested batched updates despite errors in scheduling', () => {
858
+ it('applies nested batched updates despite errors in scheduling', async () => {
859
ReactNoop.render(<span prop="a:1" />);
860
expect(() => {
861
ReactNoop.batchedUpdates(() => {
@@ -866,12 +868,12 @@ describe('ReactIncrementalErrorHandling', () => {
868
});
869
});
870
}).toThrow('Hello');
869
- expect(Scheduler).toFlushWithoutYielding();
871
+ await waitForAll([]);
872
expect(ReactNoop).toMatchRenderedOutput(<span prop="a:5" />);
873
});
874
875
// TODO: Is this a breaking change?
874
- it('defers additional sync work to a separate event after an error', () => {
876
+ it('defers additional sync work to a separate event after an error', async () => {
877
ReactNoop.render(<span prop="a:1" />);
878
expect(() => {
879
ReactNoop.flushSync(() => {
@@ -882,11 +884,11 @@ describe('ReactIncrementalErrorHandling', () => {
884
});
885
});
886
}).toThrow('Hello');
885
- Scheduler.unstable_flushAll();
887
+ await waitForAll([]);
888
expect(ReactNoop).toMatchRenderedOutput(<span prop="a:3" />);
889
});
890
889
- it('can schedule updates after uncaught error in render on mount', () => {
891
+ it('can schedule updates after uncaught error in render on mount', async () => {
892
function BrokenRender({unused}) {
893
Scheduler.unstable_yieldValue('BrokenRender');
894
throw new Error('Hello');
@@ -898,20 +900,18 @@ describe('ReactIncrementalErrorHandling', () => {
900
}
901
902
ReactNoop.render(<BrokenRender />);
901
- expect(() => {
902
- expect(Scheduler).toFlushWithoutYielding();
903
- }).toThrow('Hello');
903
+ await waitForThrow('Hello');
904
ReactNoop.render(<Foo />);
905
- expect(Scheduler).toHaveYielded([
905
+ assertLog([
906
'BrokenRender',
907
// React retries one more time
908
'BrokenRender',
909
// Errored again on retry
910
]);
911
- expect(Scheduler).toFlushAndYield(['Foo']);
911
+ await waitForAll(['Foo']);
912
});
913
914
- it('can schedule updates after uncaught error in render on update', () => {
914
+ it('can schedule updates after uncaught error in render on update', async () => {
915
function BrokenRender({shouldThrow}) {
916
Scheduler.unstable_yieldValue('BrokenRender');
917
if (shouldThrow) {
@@ -926,13 +926,11 @@ describe('ReactIncrementalErrorHandling', () => {
926
}
927
928
ReactNoop.render(<BrokenRender shouldThrow={false} />);
929
- expect(Scheduler).toFlushAndYield(['BrokenRender']);
929
+ await waitForAll(['BrokenRender']);
930
931
- expect(() => {
932
- ReactNoop.render(<BrokenRender shouldThrow={true} />);
933
- expect(Scheduler).toFlushWithoutYielding();
934
- }).toThrow('Hello');
935
- expect(Scheduler).toHaveYielded([
931
+ ReactNoop.render(<BrokenRender shouldThrow={true} />);
932
+ await waitForThrow('Hello');
933
+ assertLog([
934
'BrokenRender',
935
// React retries one more time
936
'BrokenRender',
@@ -940,10 +938,10 @@ describe('ReactIncrementalErrorHandling', () => {
938
]);
939
940
ReactNoop.render(<Foo />);
943
- expect(Scheduler).toFlushAndYield(['Foo']);
941
+ await waitForAll(['Foo']);
942
});
943
946
- it('can schedule updates after uncaught error during unmounting', () => {
944
+ it('can schedule updates after uncaught error during unmounting', async () => {
945
class BrokenComponentWillUnmount extends React.Component {
946
render() {
947
return <div />;
@@ -959,19 +957,17 @@ describe('ReactIncrementalErrorHandling', () => {
957
}
958
959
ReactNoop.render(<BrokenComponentWillUnmount />);
962
- expect(Scheduler).toFlushWithoutYielding();
960
+ await waitForAll([]);
961
964
- expect(() => {
965
- ReactNoop.render(<div />);
966
- expect(Scheduler).toFlushWithoutYielding();
967
- }).toThrow('Hello');
962
+ ReactNoop.render(<div />);
963
+ await waitForThrow('Hello');
964
965
ReactNoop.render(<Foo />);
970
- expect(Scheduler).toFlushAndYield(['Foo']);
966
+ await waitForAll(['Foo']);
967
});
968
969
// @gate skipUnmountedBoundaries
974
- it('should not attempt to recover an unmounting error boundary', () => {
970
+ it('should not attempt to recover an unmounting error boundary', async () => {
971
class Parent extends React.Component {
972
componentWillUnmount() {
973
Scheduler.unstable_yieldValue('Parent componentWillUnmount');
@@ -1001,22 +997,21 @@ describe('ReactIncrementalErrorHandling', () => {
997
}
998
999
ReactNoop.render(<Parent />);
1004
- expect(Scheduler).toFlushWithoutYielding();
1000
+ await waitForAll([]);
1001
1002
// Because the error boundary is also unmounting,
1003
// an error in ThrowsOnUnmount should be rethrown.
1008
- expect(() => {
1009
- ReactNoop.render(null);
1010
- expect(Scheduler).toFlushAndYield([
1011
- 'Parent componentWillUnmount',
1012
- 'ThrowsOnUnmount componentWillUnmount',
1013
- ]);
1014
- }).toThrow('unmount error');
1004
+ ReactNoop.render(null);
1005
+ await waitForThrow('unmount error');
1006
+ await assertLog([
1007
+ 'Parent componentWillUnmount',
1008
+ 'ThrowsOnUnmount componentWillUnmount',
1009
+ ]);
1010
1011
ReactNoop.render(<Parent />);
1012
});
1013
1019
- it('can unmount an error boundary before it is handled', () => {
1014
+ it('can unmount an error boundary before it is handled', async () => {
1015
let parent;
1016
1017
class Parent extends React.Component {
@@ -1045,14 +1040,14 @@ describe('ReactIncrementalErrorHandling', () => {
1040
}
1041
1042
ReactNoop.render(<Parent />);
1048
- expect(Scheduler).toFlushWithoutYielding();
1043
+ await waitForAll([]);
1044
1045
ReactNoop.flushSync(() => {
1046
ReactNoop.render(<Parent />);
1047
});
1048
});
1049
1055
- it('continues work on other roots despite caught errors', () => {
1050
+ it('continues work on other roots despite caught errors', async () => {
1051
class ErrorBoundary extends React.Component {
1052
state = {error: null};
1053
componentDidCatch(error) {
@@ -1079,50 +1074,42 @@ describe('ReactIncrementalErrorHandling', () => {
1074
'a',
1075
);
1076
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
1082
- expect(Scheduler).toFlushWithoutYielding();
1077
+ await waitForAll([]);
1078
expect(ReactNoop.getChildrenAsJSX('a')).toEqual(
1079
<span prop="Caught an error: Hello." />,
1080
);
1086
- expect(Scheduler).toFlushWithoutYielding();
1081
+ await waitForAll([]);
1082
expect(ReactNoop.getChildrenAsJSX('b')).toEqual(<span prop="b:1" />);
1083
});
1084
1090
- it('continues work on other roots despite uncaught errors', () => {
1085
+ it('continues work on other roots despite uncaught errors', async () => {
1086
function BrokenRender(props) {
1087
throw new Error(props.label);
1088
}
1089
1090
ReactNoop.renderToRootWithID(<BrokenRender label="a" />, 'a');
1096
- expect(() => {
1097
- expect(Scheduler).toFlushWithoutYielding();
1098
- }).toThrow('a');
1091
+ await waitForThrow('a');
1092
expect(ReactNoop.getChildrenAsJSX('a')).toEqual(null);
1093
1094
ReactNoop.renderToRootWithID(<BrokenRender label="a" />, 'a');
1095
ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
1103
- expect(() => {
1104
- expect(Scheduler).toFlushWithoutYielding();
1105
- }).toThrow('a');
1096
+ await waitForThrow('a');
1097
1107
- expect(Scheduler).toFlushWithoutYielding();
1098
+ await waitForAll([]);
1099
expect(ReactNoop.getChildrenAsJSX('a')).toEqual(null);
1100
expect(ReactNoop.getChildrenAsJSX('b')).toEqual(<span prop="b:2" />);
1101
1102
ReactNoop.renderToRootWithID(<span prop="a:3" />, 'a');
1103
ReactNoop.renderToRootWithID(<BrokenRender label="b" />, 'b');
1113
- expect(() => {
1114
- expect(Scheduler).toFlushWithoutYielding();
1115
- }).toThrow('b');
1104
+ await waitForThrow('b');
1105
expect(ReactNoop.getChildrenAsJSX('a')).toEqual(<span prop="a:3" />);
1106
expect(ReactNoop.getChildrenAsJSX('b')).toEqual(null);
1107
1108
ReactNoop.renderToRootWithID(<span prop="a:4" />, 'a');
1109
ReactNoop.renderToRootWithID(<BrokenRender label="b" />, 'b');
1110
ReactNoop.renderToRootWithID(<span prop="c:4" />, 'c');
1122
- expect(() => {
1123
- expect(Scheduler).toFlushWithoutYielding();
1124
- }).toThrow('b');
1125
- expect(Scheduler).toFlushWithoutYielding();
1111
+ await waitForThrow('b');
1112
+ await waitForAll([]);
1113
expect(ReactNoop.getChildrenAsJSX('a')).toEqual(<span prop="a:4" />);
1114
expect(ReactNoop.getChildrenAsJSX('b')).toEqual(null);
1115
expect(ReactNoop.getChildrenAsJSX('c')).toEqual(<span prop="c:4" />);
@@ -1132,10 +1119,8 @@ describe('ReactIncrementalErrorHandling', () => {
1119
ReactNoop.renderToRootWithID(<span prop="c:5" />, 'c');
1120
ReactNoop.renderToRootWithID(<span prop="d:5" />, 'd');
1121
ReactNoop.renderToRootWithID(<BrokenRender label="e" />, 'e');
1135
- expect(() => {
1136
- expect(Scheduler).toFlushWithoutYielding();
1137
- }).toThrow('e');
1138
- expect(Scheduler).toFlushWithoutYielding();
1122
+ await waitForThrow('e');
1123
+ await waitForAll([]);
1124
expect(ReactNoop.getChildrenAsJSX('a')).toEqual(<span prop="a:5" />);
1125
expect(ReactNoop.getChildrenAsJSX('b')).toEqual(<span prop="b:5" />);
1126
expect(ReactNoop.getChildrenAsJSX('c')).toEqual(<span prop="c:5" />);
@@ -1149,17 +1134,11 @@ describe('ReactIncrementalErrorHandling', () => {
1134
ReactNoop.renderToRootWithID(<BrokenRender label="e" />, 'e');
1135
ReactNoop.renderToRootWithID(<span prop="f:6" />, 'f');
1136
1152
- expect(() => {
1153
- expect(Scheduler).toFlushWithoutYielding();
1154
- }).toThrow('a');
1155
- expect(() => {
1156
- expect(Scheduler).toFlushWithoutYielding();
1157
- }).toThrow('c');
1158
- expect(() => {
1159
- expect(Scheduler).toFlushWithoutYielding();
1160
- }).toThrow('e');
1137
+ await waitForThrow('a');
1138
+ await waitForThrow('c');
1139
+ await waitForThrow('e');
1140
1162
- expect(Scheduler).toFlushWithoutYielding();
1141
+ await waitForAll([]);
1142
expect(ReactNoop.getChildrenAsJSX('a')).toEqual(null);
1143
expect(ReactNoop.getChildrenAsJSX('b')).toEqual(<span prop="b:6" />);
1144
expect(ReactNoop.getChildrenAsJSX('c')).toEqual(null);
@@ -1173,7 +1152,7 @@ describe('ReactIncrementalErrorHandling', () => {
1152
ReactNoop.unmountRootWithID('d');
1153
ReactNoop.unmountRootWithID('e');
1154
ReactNoop.unmountRootWithID('f');
1176
- expect(Scheduler).toFlushWithoutYielding();
1155
+ await waitForAll([]);
1156
expect(ReactNoop.getChildrenAsJSX('a')).toEqual(null);
1157
expect(ReactNoop.getChildrenAsJSX('b')).toEqual(null);
1158
expect(ReactNoop.getChildrenAsJSX('c')).toEqual(null);
@@ -1182,7 +1161,7 @@ describe('ReactIncrementalErrorHandling', () => {
1161
expect(ReactNoop.getChildrenAsJSX('f')).toEqual(null);
1162
});
1163
1185
- it('unwinds the context stack correctly on error', () => {
1164
+ it('unwinds the context stack correctly on error', async () => {
1165
class Provider extends React.Component {
1166
static childContextTypes = {message: PropTypes.string};
1167
static contextTypes = {message: PropTypes.string};
@@ -1234,7 +1213,7 @@ describe('ReactIncrementalErrorHandling', () => {
1213
<Connector />
1214
</Provider>,
1215
);
1237
- expect(Scheduler).toFlushWithoutYielding();
1216
+ await waitForAll([]);
1217
1218
// If the context stack does not unwind, span will get 'abcde'
1219
expect(ReactNoop).toMatchRenderedOutput(<span prop="a" />);
@@ -1283,7 +1262,7 @@ describe('ReactIncrementalErrorHandling', () => {
1262
);
1263
});
1264
1286
- it('catches reconciler errors in a boundary during update', () => {
1265
+ it('catches reconciler errors in a boundary during update', async () => {
1266
class ErrorBoundary extends React.Component {
1267
state = {error: null};
1268
componentDidCatch(error) {
@@ -1307,7 +1286,7 @@ describe('ReactIncrementalErrorHandling', () => {
1286
<BrokenRender fail={false} />
1287
</ErrorBoundary>,
1288
);
1310
- expect(Scheduler).toFlushWithoutYielding();
1289
+ await waitForAll([]);
1290
1291
ReactNoop.render(
1292
<ErrorBoundary>
@@ -1334,7 +1313,7 @@ describe('ReactIncrementalErrorHandling', () => {
1313
);
1314
});
1315
1337
- it('recovers from uncaught reconciler errors', () => {
1316
+ it('recovers from uncaught reconciler errors', async () => {
1317
const InvalidType = undefined;
1318
expect(() => ReactNoop.render(<InvalidType />)).toErrorDev(
1319
'Warning: React.createElement: type is invalid -- expected a string',
@@ -1350,11 +1329,11 @@ describe('ReactIncrementalErrorHandling', () => {
1329
);
1330
1331
ReactNoop.render(<span prop="hi" />);
1353
- expect(Scheduler).toFlushWithoutYielding();
1332
+ await waitForAll([]);
1333
expect(ReactNoop).toMatchRenderedOutput(<span prop="hi" />);
1334
});
1335
1357
- it('unmounts components with uncaught errors', () => {
1336
+ it('unmounts components with uncaught errors', async () => {
1337
let inst;
1338
1339
class BrokenRenderAndUnmount extends React.Component {
@@ -1390,14 +1369,21 @@ describe('ReactIncrementalErrorHandling', () => {
1369
</Parent>
1370
</Parent>,
1371
);
1393
- expect(Scheduler).toFlushWithoutYielding();
1372
+ await waitForAll([]);
1373
1395
- inst.setState({fail: true});
1374
expect(() => {
1397
- expect(Scheduler).toFlushWithoutYielding();
1398
- }).toThrowError('Hello.');
1375
+ expect(() => {
1376
+ ReactNoop.flushSync(() => {
1377
+ inst.setState({fail: true});
1378
+ });
1379
+ }).toThrow('Hello.');
1380
+
1381
+ // The unmount is queued in a microtask. In order to capture the error
1382
+ // that occurs during unmount, we can flush it early with `flushSync`.
1383
+ ReactNoop.flushSync();
1384
+ }).toThrow('One does not simply unmount me.');
1385
1400
- expect(Scheduler).toHaveYielded([
1386
+ assertLog([
1387
// Attempt to clean up.
1388
// Errors in parents shouldn't stop children from unmounting.
1389
'Parent componentWillUnmount [!]',
@@ -1405,13 +1391,9 @@ describe('ReactIncrementalErrorHandling', () => {
1391
'BrokenRenderAndUnmount componentWillUnmount',
1392
]);
1393
expect(ReactNoop).toMatchRenderedOutput(null);
1408
-
1409
- expect(() => {
1410
- ReactNoop.flushSync();
1411
- }).toThrow('One does not simply unmount me.');
1394
});
1395
1414
- it('does not interrupt unmounting if detaching a ref throws', () => {
1396
+ it('does not interrupt unmounting if detaching a ref throws', async () => {
1397
class Bar extends React.Component {
1398
componentWillUnmount() {
1399
Scheduler.unstable_yieldValue('Bar unmount');
@@ -1434,7 +1416,7 @@ describe('ReactIncrementalErrorHandling', () => {
1416
}
1417
1418
ReactNoop.render(<Foo />);
1437
- expect(Scheduler).toFlushAndYield(['barRef attach']);
1419
+ await waitForAll(['barRef attach']);
1420
expect(ReactNoop).toMatchRenderedOutput(
1421
<div>
1422
<span prop="Bar" />
@@ -1444,7 +1426,7 @@ describe('ReactIncrementalErrorHandling', () => {
1426
// Unmount
1427
ReactNoop.render(<Foo hide={true} />);
1428
expect(Scheduler).toFlushAndThrow('Detach error');
1447
- expect(Scheduler).toHaveYielded([
1429
+ assertLog([
1430
'barRef detach',
1431
// Bar should unmount even though its ref threw an error while detaching
1432
'Bar unmount',
@@ -1465,7 +1447,7 @@ describe('ReactIncrementalErrorHandling', () => {
1447
expect(Scheduler).toFlushAndThrow('Error!');
1448
});
1449
1468
- it('error boundaries capture non-errors', () => {
1450
+ it('error boundaries capture non-errors', async () => {
1451
spyOnProd(console, 'error').mockImplementation(() => {});
1452
spyOnDev(console, 'error').mockImplementation(() => {});
1453
@@ -1509,7 +1491,7 @@ describe('ReactIncrementalErrorHandling', () => {
1491
</ErrorBoundary>,
1492
);
1493
1512
- expect(Scheduler).toFlushAndYield([
1494
+ await waitForAll([
1495
'ErrorBoundary (try)',
1496
'Indirection',
1497
'BadRender',
@@ -1540,7 +1522,7 @@ describe('ReactIncrementalErrorHandling', () => {
1522
1523
// TODO: Error boundary does not catch promises
1524
1543
- it('continues working on siblings of a component that throws', () => {
1525
+ it('continues working on siblings of a component that throws', async () => {
1526
class ErrorBoundary extends React.Component {
1527
state = {error: null};
1528
componentDidCatch(error) {
@@ -1580,7 +1562,7 @@ describe('ReactIncrementalErrorHandling', () => {
1562
</ErrorBoundary>,
1563
);
1564
1583
- expect(Scheduler).toFlushAndYield([
1565
+ await waitForAll([
1566
'ErrorBoundary (try)',
1567
'throw',
1568
// Continue rendering siblings after BadRender throws
@@ -1603,7 +1585,7 @@ describe('ReactIncrementalErrorHandling', () => {
1585
);
1586
});
1587
1606
- it('calls the correct lifecycles on the error boundary after catching an error (mixed)', () => {
1588
+ it('calls the correct lifecycles on the error boundary after catching an error (mixed)', async () => {
1589
// This test seems a bit contrived, but it's based on an actual regression
1590
// where we checked for the existence of didUpdate instead of didMount, and
1591
// didMount was not defined.
@@ -1632,7 +1614,7 @@ describe('ReactIncrementalErrorHandling', () => {
1614
}
1615
1616
ReactNoop.render(<Parent step={1} />);
1635
- expect(Scheduler).toFlushAndYieldThrough([
1617
+ await waitFor([
1618
'render',
1619
'throw',
1620
'render',
@@ -1646,7 +1628,7 @@ describe('ReactIncrementalErrorHandling', () => {
1628
);
1629
});
1630
1649
- it('provides component stack to the error boundary with componentDidCatch', () => {
1631
+ it('provides component stack to the error boundary with componentDidCatch', async () => {
1632
class ErrorBoundary extends React.Component {
1633
state = {error: null, errorInfo: null};
1634
componentDidCatch(error, errorInfo) {
@@ -1676,7 +1658,7 @@ describe('ReactIncrementalErrorHandling', () => {
1658
<BrokenRender />
1659
</ErrorBoundary>,
1660
);
1679
- expect(Scheduler).toFlushAndYield(['render error message']);
1661
+ await waitForAll(['render error message']);
1662
expect(ReactNoop).toMatchRenderedOutput(
1663
<span
1664
prop={
@@ -1688,7 +1670,7 @@ describe('ReactIncrementalErrorHandling', () => {
1670
);
1671
});
1672
1691
- it('does not provide component stack to the error boundary with getDerivedStateFromError', () => {
1673
+ it('does not provide component stack to the error boundary with getDerivedStateFromError', async () => {
1674
class ErrorBoundary extends React.Component {
1675
state = {error: null};
1676
static getDerivedStateFromError(error, errorInfo) {
@@ -1712,13 +1694,13 @@ describe('ReactIncrementalErrorHandling', () => {
1694
<BrokenRender />
1695
</ErrorBoundary>,
1696
);
1715
- expect(Scheduler).toFlushWithoutYielding();
1697
+ await waitForAll([]);
1698
expect(ReactNoop).toMatchRenderedOutput(
1699
<span prop="Caught an error: Hello" />,
1700
);
1701
});
1702
1721
- it('provides component stack even if overriding prepareStackTrace', () => {
1703
+ it('provides component stack even if overriding prepareStackTrace', async () => {
1704
Error.prepareStackTrace = function (error, callsites) {
1705
const stack = ['An error occurred:', error.message];
1706
for (let i = 0; i < callsites.length; i++) {
@@ -1762,7 +1744,7 @@ describe('ReactIncrementalErrorHandling', () => {
1744
<BrokenRender />
1745
</ErrorBoundary>,
1746
);
1765
- expect(Scheduler).toFlushAndYield(['render error message']);
1747
+ await waitForAll(['render error message']);
1748
Error.prepareStackTrace = undefined;
1749
1750
expect(ReactNoop).toMatchRenderedOutput(
@@ -1822,7 +1804,7 @@ describe('ReactIncrementalErrorHandling', () => {
1804
});
1805
1806
// Render past the component that throws, then yield.
1825
- expect(Scheduler).toFlushAndYieldThrough(['Oops']);
1807
+ await waitFor(['Oops']);
1808
expect(root).toMatchRenderedOutput(null);
1809
// Interleaved update. When the root completes, instead of throwing the
1810
// error, it should try rendering again. This update will cause it to
@@ -1868,7 +1850,7 @@ describe('ReactIncrementalErrorHandling', () => {
1850
1851
// Render through just the default pri update. The low pri update remains on
1852
// the queue.
1871
- expect(Scheduler).toFlushAndYieldThrough(['Everything is fine.']);
1853
+ await waitFor(['Everything is fine.']);
1854
1855
// Schedule a discrete update on a child that triggers an error.
1856
// The root should capture this error. But since there's still a pending
@@ -1878,7 +1860,7 @@ describe('ReactIncrementalErrorHandling', () => {
1860
});
1861
});
1862
// Should render the final state without throwing the error.
1881
- expect(Scheduler).toHaveYielded(['Everything is fine.']);
1863
+ assertLog(['Everything is fine.']);
1864
expect(root).toMatchRenderedOutput('Everything is fine.');
1865
});
1866
packages/react-reconciler/src/__tests__/ReactIncrementalErrorLogging-test.js
+6
-2
@@ -13,6 +13,7 @@
13
let React;
14
let ReactNoop;
15
let Scheduler;
16
+let waitForAll;
17
18
describe('ReactIncrementalErrorLogging', () => {
19
beforeEach(() => {
@@ -20,6 +21,9 @@ describe('ReactIncrementalErrorLogging', () => {
21
React = require('react');
22
ReactNoop = require('react-noop-renderer');
23
Scheduler = require('scheduler');
24
+
25
+ const InternalTestUtils = require('internal-test-utils');
26
+ waitForAll = InternalTestUtils.waitForAll;
27
});
28
29
// Note: in this test file we won't be using toErrorDev() matchers
@@ -151,7 +155,7 @@ describe('ReactIncrementalErrorLogging', () => {
155
}).toThrow('logCapturedError error');
156
});
157
154
- it('resets instance variables before unmounting failed node', () => {
158
+ it('resets instance variables before unmounting failed node', async () => {
159
class ErrorBoundary extends React.Component {
160
state = {error: null};
161
componentDidCatch(error) {
@@ -185,7 +189,7 @@ describe('ReactIncrementalErrorLogging', () => {
189
<Foo />
190
</ErrorBoundary>,
191
);
188
- expect(Scheduler).toFlushAndYield(
192
+ await waitForAll(
193
[
194
'render: 0',
195
packages/react-reconciler/src/__tests__/ReactIncrementalErrorReplay-test.js
+6
-2
@@ -13,6 +13,7 @@
13
let React;
14
let ReactNoop;
15
let Scheduler;
16
+let waitForAll;
17
18
describe('ReactIncrementalErrorReplay', () => {
19
beforeEach(() => {
@@ -20,6 +21,9 @@ describe('ReactIncrementalErrorReplay', () => {
21
React = require('react');
22
ReactNoop = require('react-noop-renderer');
23
Scheduler = require('scheduler');
24
+
25
+ const InternalTestUtils = require('internal-test-utils');
26
+ waitForAll = InternalTestUtils.waitForAll;
27
});
28
29
it('should fail gracefully on error in the host environment', () => {
@@ -27,7 +31,7 @@ describe('ReactIncrementalErrorReplay', () => {
31
expect(Scheduler).toFlushAndThrow('Error in host config.');
32
});
33
30
- it("should ignore error if it doesn't throw on retry", () => {
34
+ it("should ignore error if it doesn't throw on retry", async () => {
35
let didInit = false;
36
37
function badLazyInit() {
@@ -45,6 +49,6 @@ describe('ReactIncrementalErrorReplay', () => {
49
}
50
}
51
ReactNoop.render(<App />);
48
- expect(Scheduler).toFlushWithoutYielding();
52
+ await waitForAll([]);
53
});
54
});
packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js
+23
-21
@@ -13,6 +13,8 @@
13
let React;
14
let ReactNoop;
15
let Scheduler;
16
+let waitFor;
17
+let waitForAll;
18
19
describe('ReactIncrementalReflection', () => {
20
beforeEach(() => {
@@ -21,6 +23,10 @@ describe('ReactIncrementalReflection', () => {
23
React = require('react');
24
ReactNoop = require('react-noop-renderer');
25
Scheduler = require('scheduler');
26
+
27
+ const InternalTestUtils = require('internal-test-utils');
28
+ waitFor = InternalTestUtils.waitFor;
29
+ waitForAll = InternalTestUtils.waitForAll;
30
});
31
32
function div(...children) {
@@ -34,7 +40,7 @@ describe('ReactIncrementalReflection', () => {
40
return {type: 'span', children: [], prop, hidden: false};
41
}
42
37
- it('handles isMounted even when the initial render is deferred', () => {
43
+ it('handles isMounted even when the initial render is deferred', async () => {
44
const instances = [];
45
46
class Component extends React.Component {
@@ -68,17 +74,17 @@ describe('ReactIncrementalReflection', () => {
74
});
75
76
// Render part way through but don't yet commit the updates.
71
- expect(Scheduler).toFlushAndYieldThrough(['componentWillMount: false']);
77
+ await waitFor(['componentWillMount: false']);
78
79
expect(instances[0]._isMounted()).toBe(false);
80
81
// Render the rest and commit the updates.
76
- expect(Scheduler).toFlushAndYield(['componentDidMount: true']);
82
+ await waitForAll(['componentDidMount: true']);
83
84
expect(instances[0]._isMounted()).toBe(true);
85
});
86
81
- it('handles isMounted when an unmount is deferred', () => {
87
+ it('handles isMounted when an unmount is deferred', async () => {
88
const instances = [];
89
90
class Component extends React.Component {
@@ -109,7 +115,7 @@ describe('ReactIncrementalReflection', () => {
115
}
116
117
ReactNoop.render(<Foo mount={true} />);
112
- expect(Scheduler).toFlushAndYield(['Component']);
118
+ await waitForAll(['Component']);
119
120
expect(instances[0]._isMounted()).toBe(true);
121
@@ -118,17 +124,17 @@ describe('ReactIncrementalReflection', () => {
124
});
125
// Render part way through but don't yet commit the updates so it is not
126
// fully unmounted yet.
121
- expect(Scheduler).toFlushAndYieldThrough(['Other']);
127
+ await waitFor(['Other']);
128
129
expect(instances[0]._isMounted()).toBe(true);
130
131
// Finish flushing the unmount.
126
- expect(Scheduler).toFlushAndYield(['componentWillUnmount: true']);
132
+ await waitForAll(['componentWillUnmount: true']);
133
134
expect(instances[0]._isMounted()).toBe(false);
135
});
136
131
- it('finds no node before insertion and correct node before deletion', () => {
137
+ it('finds no node before insertion and correct node before deletion', async () => {
138
let classInstance = null;
139
140
function findInstance(inst) {
@@ -202,18 +208,14 @@ describe('ReactIncrementalReflection', () => {
208
ReactNoop.render(<Foo step={0} />);
209
});
210
// Flush past Component but don't complete rendering everything yet.
205
- expect(Scheduler).toFlushAndYieldThrough([
206
- ['componentWillMount', null],
207
- 'render',
208
- 'render sibling',
209
- ]);
211
+ await waitFor([['componentWillMount', null], 'render', 'render sibling']);
212
213
expect(classInstance).toBeDefined();
214
// The instance has been complete but is still not committed so it should
215
// not find any host nodes in it.
216
expect(findInstance(classInstance)).toBe(null);
217
216
- expect(Scheduler).toFlushAndYield([['componentDidMount', span()]]);
218
+ await waitForAll([['componentDidMount', span()]]);
219
220
const hostSpan = classInstance.span;
221
expect(hostSpan).toBeDefined();
@@ -223,7 +225,7 @@ describe('ReactIncrementalReflection', () => {
225
// Flush next step which will cause an update but not yet render a new host
226
// node.
227
ReactNoop.render(<Foo step={1} />);
226
- expect(Scheduler).toFlushAndYield([
228
+ await waitForAll([
229
['componentWillUpdate', hostSpan],
230
'render',
231
'render sibling',
@@ -237,7 +239,7 @@ describe('ReactIncrementalReflection', () => {
239
React.startTransition(() => {
240
ReactNoop.render(<Foo step={2} />);
241
});
240
- expect(Scheduler).toFlushAndYieldThrough([
242
+ await waitFor([
243
['componentWillUpdate', hostSpan],
244
'render',
245
'render sibling',
@@ -247,7 +249,7 @@ describe('ReactIncrementalReflection', () => {
249
expect(ReactNoop.findInstance(classInstance)).toBe(hostSpan);
250
251
// When we finally flush the tree it will get committed.
250
- expect(Scheduler).toFlushAndYield([['componentDidUpdate', div()]]);
252
+ await waitForAll([['componentDidUpdate', div()]]);
253
254
const hostDiv = classInstance.div;
255
expect(hostDiv).toBeDefined();
@@ -260,7 +262,7 @@ describe('ReactIncrementalReflection', () => {
262
React.startTransition(() => {
263
ReactNoop.render(<Foo step={3} />);
264
});
263
- expect(Scheduler).toFlushAndYieldThrough([
265
+ await waitFor([
266
['componentWillUpdate', hostDiv],
267
'render',
268
'render sibling',
@@ -269,14 +271,14 @@ describe('ReactIncrementalReflection', () => {
271
// This should still be the host div since the deletion is not committed.
272
expect(ReactNoop.findInstance(classInstance)).toBe(hostDiv);
273
272
- expect(Scheduler).toFlushAndYield([['componentDidUpdate', null]]);
274
+ await waitForAll([['componentDidUpdate', null]]);
275
276
// This should still be the host div since the deletion is not committed.
277
expect(ReactNoop.findInstance(classInstance)).toBe(null);
278
279
// Render a div again
280
ReactNoop.render(<Foo step={4} />);
279
- expect(Scheduler).toFlushAndYield([
281
+ await waitForAll([
282
['componentWillUpdate', null],
283
'render',
284
'render sibling',
@@ -285,6 +287,6 @@ describe('ReactIncrementalReflection', () => {
287
288
// Unmount the component.
289
ReactNoop.render([]);
288
- expect(Scheduler).toFlushAndYield([['componentWillUnmount', hostDiv]]);
290
+ await waitForAll([['componentWillUnmount', hostDiv]]);
291
});
292
});
packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.js
+35
-25
@@ -14,6 +14,10 @@ let React;
14
let ReactNoop;
15
let Scheduler;
16
let act;
17
+let waitForAll;
18
+let waitFor;
19
+let assertLog;
20
+let waitForPaint;
21
22
describe('ReactIncrementalScheduling', () => {
23
beforeEach(() => {
@@ -23,32 +27,38 @@ describe('ReactIncrementalScheduling', () => {
27
ReactNoop = require('react-noop-renderer');
28
Scheduler = require('scheduler');
29
act = require('jest-react').act;
30
+
31
+ const InternalTestUtils = require('internal-test-utils');
32
+ waitForAll = InternalTestUtils.waitForAll;
33
+ waitFor = InternalTestUtils.waitFor;
34
+ assertLog = InternalTestUtils.assertLog;
35
+ waitForPaint = InternalTestUtils.waitForPaint;
36
});
37
28
- it('schedules and flushes deferred work', () => {
38
+ it('schedules and flushes deferred work', async () => {
39
ReactNoop.render(<span prop="1" />);
40
expect(ReactNoop).toMatchRenderedOutput(null);
41
32
- expect(Scheduler).toFlushWithoutYielding();
42
+ await waitForAll([]);
43
expect(ReactNoop).toMatchRenderedOutput(<span prop="1" />);
44
});
45
36
- it('searches for work on other roots once the current root completes', () => {
46
+ it('searches for work on other roots once the current root completes', async () => {
47
ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
48
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
49
ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
50
41
- expect(Scheduler).toFlushWithoutYielding();
51
+ await waitForAll([]);
52
53
expect(ReactNoop.getChildrenAsJSX('a')).toEqual(<span prop="a:1" />);
54
expect(ReactNoop.getChildrenAsJSX('b')).toEqual(<span prop="b:1" />);
55
expect(ReactNoop.getChildrenAsJSX('c')).toEqual(<span prop="c:1" />);
56
});
57
48
- it('schedules top-level updates in order of priority', () => {
58
+ it('schedules top-level updates in order of priority', async () => {
59
// Initial render.
60
ReactNoop.render(<span prop={1} />);
51
- expect(Scheduler).toFlushWithoutYielding();
61
+ await waitForAll([]);
62
expect(ReactNoop).toMatchRenderedOutput(<span prop={1} />);
63
64
ReactNoop.batchedUpdates(() => {
@@ -64,14 +74,14 @@ describe('ReactIncrementalScheduling', () => {
74
75
// The terminal value should be the last update that was scheduled,
76
// regardless of priority. In this case, that's the last sync update.
67
- expect(Scheduler).toFlushWithoutYielding();
77
+ await waitForAll([]);
78
expect(ReactNoop).toMatchRenderedOutput(<span prop={4} />);
79
});
80
71
- it('schedules top-level updates with same priority in order of insertion', () => {
81
+ it('schedules top-level updates with same priority in order of insertion', async () => {
82
// Initial render.
83
ReactNoop.render(<span prop={1} />);
74
- expect(Scheduler).toFlushWithoutYielding();
84
+ await waitForAll([]);
85
expect(ReactNoop).toMatchRenderedOutput(<span prop={1} />);
86
87
ReactNoop.render(<span prop={2} />);
@@ -79,7 +89,7 @@ describe('ReactIncrementalScheduling', () => {
89
ReactNoop.render(<span prop={4} />);
90
ReactNoop.render(<span prop={5} />);
91
82
- expect(Scheduler).toFlushWithoutYielding();
92
+ await waitForAll([]);
93
expect(ReactNoop).toMatchRenderedOutput(<span prop={5} />);
94
});
95
@@ -97,20 +107,20 @@ describe('ReactIncrementalScheduling', () => {
107
ReactNoop.renderToRootWithID(<Text text="b:1" />, 'b');
108
ReactNoop.renderToRootWithID(<Text text="c:1" />, 'c');
109
});
100
- expect(Scheduler).toHaveYielded(['a:1', 'b:1', 'c:1']);
110
+ assertLog(['a:1', 'b:1', 'c:1']);
111
112
expect(ReactNoop.getChildrenAsJSX('a')).toEqual('a:1');
113
expect(ReactNoop.getChildrenAsJSX('b')).toEqual('b:1');
114
expect(ReactNoop.getChildrenAsJSX('c')).toEqual('c:1');
115
116
// Schedule deferred work in the reverse order
107
- act(() => {
117
+ act(async () => {
118
React.startTransition(() => {
119
ReactNoop.renderToRootWithID(<Text text="c:2" />, 'c');
120
ReactNoop.renderToRootWithID(<Text text="b:2" />, 'b');
121
});
122
// Ensure it starts in the order it was scheduled
113
- expect(Scheduler).toFlushAndYieldThrough(['c:2']);
123
+ await waitFor(['c:2']);
124
125
expect(ReactNoop.getChildrenAsJSX('a')).toEqual('a:1');
126
expect(ReactNoop.getChildrenAsJSX('b')).toEqual('b:1');
@@ -122,19 +132,19 @@ describe('ReactIncrementalScheduling', () => {
132
});
133
134
// Keep performing work in the order it was scheduled
125
- expect(Scheduler).toFlushAndYieldThrough(['b:2']);
135
+ await waitFor(['b:2']);
136
expect(ReactNoop.getChildrenAsJSX('a')).toEqual('a:1');
137
expect(ReactNoop.getChildrenAsJSX('b')).toEqual('b:2');
138
expect(ReactNoop.getChildrenAsJSX('c')).toEqual('c:2');
139
130
- expect(Scheduler).toFlushAndYieldThrough(['a:2']);
140
+ await waitFor(['a:2']);
141
expect(ReactNoop.getChildrenAsJSX('a')).toEqual('a:2');
142
expect(ReactNoop.getChildrenAsJSX('b')).toEqual('b:2');
143
expect(ReactNoop.getChildrenAsJSX('c')).toEqual('c:2');
144
});
145
});
146
137
- it('schedules sync updates when inside componentDidMount/Update', () => {
147
+ it('schedules sync updates when inside componentDidMount/Update', async () => {
148
let instance;
149
150
class Foo extends React.Component {
@@ -176,7 +186,7 @@ describe('ReactIncrementalScheduling', () => {
186
ReactNoop.render(<Foo />);
187
});
188
// Render without committing
179
- expect(Scheduler).toFlushAndYieldThrough(['render: 0']);
189
+ await waitFor(['render: 0']);
190
191
// Do one more unit of work to commit
192
expect(ReactNoop.flushNextYield()).toEqual([
@@ -191,7 +201,7 @@ describe('ReactIncrementalScheduling', () => {
201
React.startTransition(() => {
202
instance.setState({tick: 2});
203
});
194
- expect(Scheduler).toFlushAndYieldThrough(['render: 2']);
204
+ await waitFor(['render: 2']);
205
expect(ReactNoop.flushNextYield()).toEqual([
206
'componentDidUpdate: 2',
207
'componentDidUpdate (before setState): 2',
@@ -203,7 +213,7 @@ describe('ReactIncrementalScheduling', () => {
213
]);
214
});
215
206
- it('can opt-in to async scheduling inside componentDidMount/Update', () => {
216
+ it('can opt-in to async scheduling inside componentDidMount/Update', async () => {
217
let instance;
218
class Foo extends React.Component {
219
state = {tick: 0};
@@ -248,7 +258,7 @@ describe('ReactIncrementalScheduling', () => {
258
ReactNoop.render(<Foo />);
259
});
260
// The cDM update should not have flushed yet because it has async priority.
251
- expect(Scheduler).toHaveYielded([
261
+ assertLog([
262
'render: 0',
263
'componentDidMount (before setState): 0',
264
'componentDidMount (after setState): 0',
@@ -256,14 +266,14 @@ describe('ReactIncrementalScheduling', () => {
266
expect(ReactNoop).toMatchRenderedOutput(<span prop={0} />);
267
268
// Now flush the cDM update.
259
- expect(Scheduler).toFlushAndYield(['render: 1', 'componentDidUpdate: 1']);
269
+ await waitForAll(['render: 1', 'componentDidUpdate: 1']);
270
expect(ReactNoop).toMatchRenderedOutput(<span prop={1} />);
271
272
React.startTransition(() => {
273
instance.setState({tick: 2});
274
});
275
266
- expect(Scheduler).toFlushUntilNextPaint([
276
+ await waitForPaint([
277
'render: 2',
278
'componentDidUpdate: 2',
279
'componentDidUpdate (before setState): 2',
@@ -272,11 +282,11 @@ describe('ReactIncrementalScheduling', () => {
282
expect(ReactNoop).toMatchRenderedOutput(<span prop={2} />);
283
284
// Now flush the cDU update.
275
- expect(Scheduler).toFlushAndYield(['render: 3', 'componentDidUpdate: 3']);
285
+ await waitForAll(['render: 3', 'componentDidUpdate: 3']);
286
expect(ReactNoop).toMatchRenderedOutput(<span prop={3} />);
287
});
288
279
- it('performs Task work even after time runs out', () => {
289
+ it('performs Task work even after time runs out', async () => {
290
class Foo extends React.Component {
291
state = {step: 1};
292
componentDidMount() {
@@ -299,7 +309,7 @@ describe('ReactIncrementalScheduling', () => {
309
310
// This should be just enough to complete all the work, but not enough to
311
// commit it.
302
- expect(Scheduler).toFlushAndYieldThrough(['Foo']);
312
+ await waitFor(['Foo']);
313
expect(ReactNoop).toMatchRenderedOutput(null);
314
315
// Do one more unit of work.
packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
+90
-84
@@ -13,6 +13,8 @@
13
let React;
14
let ReactNoop;
15
let Scheduler;
16
+let waitForAll;
17
+let waitFor;
18
19
describe('ReactIncrementalSideEffects', () => {
20
beforeEach(() => {
@@ -21,6 +23,10 @@ describe('ReactIncrementalSideEffects', () => {
23
React = require('react');
24
ReactNoop = require('react-noop-renderer');
25
Scheduler = require('scheduler');
26
+
27
+ const InternalTestUtils = require('internal-test-utils');
28
+ waitForAll = InternalTestUtils.waitForAll;
29
+ waitFor = InternalTestUtils.waitFor;
30
});
31
32
// Note: This is based on a similar component we use in www. We can delete
@@ -36,7 +42,7 @@ describe('ReactIncrementalSideEffects', () => {
42
);
43
}
44
39
- it('can update child nodes of a host instance', () => {
45
+ it('can update child nodes of a host instance', async () => {
46
function Bar(props) {
47
return <span>{props.text}</span>;
48
}
@@ -51,7 +57,7 @@ describe('ReactIncrementalSideEffects', () => {
57
}
58
59
ReactNoop.render(<Foo text="Hello" />);
54
- expect(Scheduler).toFlushWithoutYielding();
60
+ await waitForAll([]);
61
expect(ReactNoop).toMatchRenderedOutput(
62
<div>
63
<span>Hello</span>
@@ -59,7 +65,7 @@ describe('ReactIncrementalSideEffects', () => {
65
);
66
67
ReactNoop.render(<Foo text="World" />);
62
- expect(Scheduler).toFlushWithoutYielding();
68
+ await waitForAll([]);
69
expect(ReactNoop).toMatchRenderedOutput(
70
<div>
71
<span>World</span>
@@ -68,7 +74,7 @@ describe('ReactIncrementalSideEffects', () => {
74
);
75
});
76
71
- it('can update child nodes of a fragment', function () {
77
+ it('can update child nodes of a fragment', async function () {
78
function Bar(props) {
79
return <span>{props.text}</span>;
80
}
@@ -88,7 +94,7 @@ describe('ReactIncrementalSideEffects', () => {
94
}
95
96
ReactNoop.render(<Foo text="Hello" />);
91
- expect(Scheduler).toFlushWithoutYielding();
97
+ await waitForAll([]);
98
expect(ReactNoop).toMatchRenderedOutput(
99
<div>
100
<span>Hello</span>
@@ -97,7 +103,7 @@ describe('ReactIncrementalSideEffects', () => {
103
);
104
105
ReactNoop.render(<Foo text="World" />);
100
- expect(Scheduler).toFlushWithoutYielding();
106
+ await waitForAll([]);
107
expect(ReactNoop).toMatchRenderedOutput(
108
<div>
109
<span>World</span>
@@ -108,7 +114,7 @@ describe('ReactIncrementalSideEffects', () => {
114
);
115
116
ReactNoop.render(<Foo text="Hi" />);
111
- expect(Scheduler).toFlushWithoutYielding();
117
+ await waitForAll([]);
118
expect(ReactNoop).toMatchRenderedOutput(
119
<div>
120
<span>Hi</span>
@@ -119,7 +125,7 @@ describe('ReactIncrementalSideEffects', () => {
125
);
126
});
127
122
- it('can update child nodes rendering into text nodes', function () {
128
+ it('can update child nodes rendering into text nodes', async function () {
129
function Bar(props) {
130
return props.text;
131
}
@@ -136,15 +142,15 @@ describe('ReactIncrementalSideEffects', () => {
142
}
143
144
ReactNoop.render(<Foo text="Hello" />);
139
- expect(Scheduler).toFlushWithoutYielding();
145
+ await waitForAll([]);
146
expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
147
148
ReactNoop.render(<Foo text="World" />);
143
- expect(Scheduler).toFlushWithoutYielding();
149
+ await waitForAll([]);
150
expect(ReactNoop).toMatchRenderedOutput(<div>WorldWorld!</div>);
151
});
152
147
- it('can deletes children either components, host or text', function () {
153
+ it('can deletes children either components, host or text', async function () {
154
function Bar(props) {
155
return <span prop={props.children} />;
156
}
@@ -160,7 +166,7 @@ describe('ReactIncrementalSideEffects', () => {
166
}
167
168
ReactNoop.render(<Foo show={true} />);
163
- expect(Scheduler).toFlushWithoutYielding();
169
+ await waitForAll([]);
170
expect(ReactNoop).toMatchRenderedOutput(
171
<div>
172
<div />
@@ -170,11 +176,11 @@ describe('ReactIncrementalSideEffects', () => {
176
);
177
178
ReactNoop.render(<Foo show={false} />);
173
- expect(Scheduler).toFlushWithoutYielding();
179
+ await waitForAll([]);
180
expect(ReactNoop).toMatchRenderedOutput(<div />);
181
});
182
177
- it('can delete a child that changes type - implicit keys', function () {
183
+ it('can delete a child that changes type - implicit keys', async function () {
184
let unmounted = false;
185
186
class ClassComponent extends React.Component {
@@ -206,7 +212,7 @@ describe('ReactIncrementalSideEffects', () => {
212
}
213
214
ReactNoop.render(<Foo useClass={true} />);
209
- expect(Scheduler).toFlushWithoutYielding();
215
+ await waitForAll([]);
216
expect(ReactNoop).toMatchRenderedOutput(
217
<div>
218
<span prop="Class" />
@@ -217,7 +223,7 @@ describe('ReactIncrementalSideEffects', () => {
223
expect(unmounted).toBe(false);
224
225
ReactNoop.render(<Foo useFunction={true} />);
220
- expect(Scheduler).toFlushWithoutYielding();
226
+ await waitForAll([]);
227
expect(ReactNoop).toMatchRenderedOutput(
228
<div>
229
<span prop="Function" />
@@ -228,15 +234,15 @@ describe('ReactIncrementalSideEffects', () => {
234
expect(unmounted).toBe(true);
235
236
ReactNoop.render(<Foo useText={true} />);
231
- expect(Scheduler).toFlushWithoutYielding();
237
+ await waitForAll([]);
238
expect(ReactNoop).toMatchRenderedOutput(<div>TextTrail</div>);
239
240
ReactNoop.render(<Foo />);
235
- expect(Scheduler).toFlushWithoutYielding();
241
+ await waitForAll([]);
242
expect(ReactNoop).toMatchRenderedOutput(<div>Trail</div>);
243
});
244
239
- it('can delete a child that changes type - explicit keys', function () {
245
+ it('can delete a child that changes type - explicit keys', async function () {
246
let unmounted = false;
247
248
class ClassComponent extends React.Component {
@@ -266,7 +272,7 @@ describe('ReactIncrementalSideEffects', () => {
272
}
273
274
ReactNoop.render(<Foo useClass={true} />);
269
- expect(Scheduler).toFlushWithoutYielding();
275
+ await waitForAll([]);
276
expect(ReactNoop).toMatchRenderedOutput(
277
<div>
278
<span prop="Class" />
@@ -277,7 +283,7 @@ describe('ReactIncrementalSideEffects', () => {
283
expect(unmounted).toBe(false);
284
285
ReactNoop.render(<Foo useFunction={true} />);
280
- expect(Scheduler).toFlushWithoutYielding();
286
+ await waitForAll([]);
287
expect(ReactNoop).toMatchRenderedOutput(
288
<div>
289
<span prop="Function" />
@@ -288,11 +294,11 @@ describe('ReactIncrementalSideEffects', () => {
294
expect(unmounted).toBe(true);
295
296
ReactNoop.render(<Foo />);
291
- expect(Scheduler).toFlushWithoutYielding();
297
+ await waitForAll([]);
298
expect(ReactNoop).toMatchRenderedOutput(<div>Trail</div>);
299
});
300
295
- it('can delete a child when it unmounts inside a portal', () => {
301
+ it('can delete a child when it unmounts inside a portal', async () => {
302
function Bar(props) {
303
return <span prop={props.children} />;
304
}
@@ -312,7 +318,7 @@ describe('ReactIncrementalSideEffects', () => {
318
<Foo show={true} />
319
</div>,
320
);
315
- expect(Scheduler).toFlushWithoutYielding();
321
+ await waitForAll([]);
322
expect(ReactNoop).toMatchRenderedOutput(<div />);
323
expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(
324
<>
@@ -327,7 +333,7 @@ describe('ReactIncrementalSideEffects', () => {
333
<Foo show={false} />
334
</div>,
335
);
330
- expect(Scheduler).toFlushWithoutYielding();
336
+ await waitForAll([]);
337
expect(ReactNoop).toMatchRenderedOutput(<div />);
338
expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
339
@@ -336,7 +342,7 @@ describe('ReactIncrementalSideEffects', () => {
342
<Foo show={true} />
343
</div>,
344
);
339
- expect(Scheduler).toFlushWithoutYielding();
345
+ await waitForAll([]);
346
expect(ReactNoop).toMatchRenderedOutput(<div />);
347
expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(
348
<>
@@ -347,17 +353,17 @@ describe('ReactIncrementalSideEffects', () => {
353
);
354
355
ReactNoop.render(null);
350
- expect(Scheduler).toFlushWithoutYielding();
356
+ await waitForAll([]);
357
expect(ReactNoop).toMatchRenderedOutput(null);
358
expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
359
360
ReactNoop.render(<Foo show={false} />);
355
- expect(Scheduler).toFlushWithoutYielding();
361
+ await waitForAll([]);
362
expect(ReactNoop).toMatchRenderedOutput(null);
363
expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
364
365
ReactNoop.render(<Foo show={true} />);
360
- expect(Scheduler).toFlushWithoutYielding();
366
+ await waitForAll([]);
367
expect(ReactNoop).toMatchRenderedOutput(null);
368
expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(
369
<>
@@ -368,12 +374,12 @@ describe('ReactIncrementalSideEffects', () => {
374
);
375
376
ReactNoop.render(null);
371
- expect(Scheduler).toFlushWithoutYielding();
377
+ await waitForAll([]);
378
expect(ReactNoop).toMatchRenderedOutput(null);
379
expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
380
});
381
376
- it('can delete a child when it unmounts with a portal', () => {
382
+ it('can delete a child when it unmounts with a portal', async () => {
383
function Bar(props) {
384
return <span prop={props.children} />;
385
}
@@ -393,7 +399,7 @@ describe('ReactIncrementalSideEffects', () => {
399
<Foo />
400
</div>,
401
);
396
- expect(Scheduler).toFlushWithoutYielding();
402
+ await waitForAll([]);
403
expect(ReactNoop).toMatchRenderedOutput(<div />);
404
expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(
405
<>
@@ -404,12 +410,12 @@ describe('ReactIncrementalSideEffects', () => {
410
);
411
412
ReactNoop.render(null);
407
- expect(Scheduler).toFlushWithoutYielding();
413
+ await waitForAll([]);
414
expect(ReactNoop).toMatchRenderedOutput(null);
415
expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
416
417
ReactNoop.render(<Foo />);
412
- expect(Scheduler).toFlushWithoutYielding();
418
+ await waitForAll([]);
419
expect(ReactNoop).toMatchRenderedOutput(null);
420
expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(
421
<>
@@ -420,12 +426,12 @@ describe('ReactIncrementalSideEffects', () => {
426
);
427
428
ReactNoop.render(null);
423
- expect(Scheduler).toFlushWithoutYielding();
429
+ await waitForAll([]);
430
expect(ReactNoop).toMatchRenderedOutput(null);
431
expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
432
});
433
428
- it('does not update child nodes if a flush is aborted', () => {
434
+ it('does not update child nodes if a flush is aborted', async () => {
435
function Bar(props) {
436
Scheduler.unstable_yieldValue('Bar');
437
return <span prop={props.text} />;
@@ -445,7 +451,7 @@ describe('ReactIncrementalSideEffects', () => {
451
}
452
453
ReactNoop.render(<Foo text="Hello" />);
448
- expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Bar', 'Bar']);
454
+ await waitForAll(['Foo', 'Bar', 'Bar', 'Bar']);
455
expect(ReactNoop).toMatchRenderedOutput(
456
<div>
457
<div>
@@ -461,7 +467,7 @@ describe('ReactIncrementalSideEffects', () => {
467
});
468
469
// Flush some of the work without committing
464
- expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Bar']);
470
+ await waitFor(['Foo', 'Bar']);
471
expect(ReactNoop).toMatchRenderedOutput(
472
<div>
473
<div>
@@ -474,7 +480,7 @@ describe('ReactIncrementalSideEffects', () => {
480
});
481
482
// @gate www
477
- it('preserves a previously rendered node when deprioritized', () => {
483
+ it('preserves a previously rendered node when deprioritized', async () => {
484
function Middle(props) {
485
Scheduler.unstable_yieldValue('Middle');
486
return <span prop={props.children} />;
@@ -492,7 +498,7 @@ describe('ReactIncrementalSideEffects', () => {
498
}
499
500
ReactNoop.render(<Foo text="foo" />);
495
- expect(Scheduler).toFlushAndYield(['Foo', 'Middle']);
501
+ await waitForAll(['Foo', 'Middle']);
502
503
expect(ReactNoop.getChildrenAsJSX()).toEqual(
504
<div>
@@ -505,7 +511,7 @@ describe('ReactIncrementalSideEffects', () => {
511
ReactNoop.render(<Foo text="bar" />, () =>
512
Scheduler.unstable_yieldValue('commit'),
513
);
508
- expect(Scheduler).toFlushAndYieldThrough(['Foo', 'commit']);
514
+ await waitFor(['Foo', 'commit']);
515
expect(ReactNoop.getChildrenAsJSX()).toEqual(
516
<div>
517
<div hidden={true}>
@@ -514,7 +520,7 @@ describe('ReactIncrementalSideEffects', () => {
520
</div>,
521
);
522
517
- expect(Scheduler).toFlushAndYield(['Middle']);
523
+ await waitForAll(['Middle']);
524
expect(ReactNoop.getChildrenAsJSX()).toEqual(
525
<div>
526
<div hidden={true}>
@@ -525,7 +531,7 @@ describe('ReactIncrementalSideEffects', () => {
531
});
532
533
// @gate www
528
- it('can reuse side-effects after being preempted', () => {
534
+ it('can reuse side-effects after being preempted', async () => {
535
function Bar(props) {
536
Scheduler.unstable_yieldValue('Bar');
537
return <span prop={props.children} />;
@@ -556,7 +562,7 @@ describe('ReactIncrementalSideEffects', () => {
562
563
// Init
564
ReactNoop.render(<Foo text="foo" step={0} />);
559
- expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Bar']);
565
+ await waitForAll(['Foo', 'Bar', 'Bar']);
566
567
expect(ReactNoop.getChildrenAsJSX()).toEqual(
568
<div hidden={true}>
@@ -572,7 +578,7 @@ describe('ReactIncrementalSideEffects', () => {
578
ReactNoop.render(<Foo text="bar" step={1} />, () =>
579
Scheduler.unstable_yieldValue('commit'),
580
);
575
- expect(Scheduler).toFlushAndYieldThrough(['Foo', 'commit', 'Bar']);
581
+ await waitFor(['Foo', 'commit', 'Bar']);
582
583
// The tree remains unchanged.
584
expect(ReactNoop.getChildrenAsJSX()).toEqual(
@@ -588,7 +594,7 @@ describe('ReactIncrementalSideEffects', () => {
594
// render some higher priority work. The middle content will bailout so
595
// it remains untouched which means that it should reuse it next time.
596
ReactNoop.render(<Foo text="foo" step={1} />);
591
- expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Bar']);
597
+ await waitForAll(['Foo', 'Bar', 'Bar']);
598
599
// Since we did nothing to the middle subtree during the interruption,
600
// we should be able to reuse the reconciliation work that we already did
@@ -605,7 +611,7 @@ describe('ReactIncrementalSideEffects', () => {
611
});
612
613
// @gate www
608
- it('can reuse side-effects after being preempted, if shouldComponentUpdate is false', () => {
614
+ it('can reuse side-effects after being preempted, if shouldComponentUpdate is false', async () => {
615
class Bar extends React.Component {
616
shouldComponentUpdate(nextProps) {
617
return this.props.children !== nextProps.children;
@@ -642,7 +648,7 @@ describe('ReactIncrementalSideEffects', () => {
648
649
// Init
650
ReactNoop.render(<Foo text="foo" step={0} />);
645
- expect(Scheduler).toFlushAndYield(['Foo', 'Content', 'Bar', 'Bar']);
651
+ await waitForAll(['Foo', 'Content', 'Bar', 'Bar']);
652
653
expect(ReactNoop.getChildrenAsJSX()).toEqual(
654
<div hidden={true}>
@@ -656,7 +662,7 @@ describe('ReactIncrementalSideEffects', () => {
662
// Make a quick update which will schedule low priority work to
663
// update the middle content.
664
ReactNoop.render(<Foo text="bar" step={1} />);
659
- expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Content', 'Bar']);
665
+ await waitFor(['Foo', 'Content', 'Bar']);
666
667
// The tree remains unchanged.
668
expect(ReactNoop.getChildrenAsJSX()).toEqual(
@@ -672,7 +678,7 @@ describe('ReactIncrementalSideEffects', () => {
678
// render some higher priority work. The middle content will bailout so
679
// it remains untouched which means that it should reuse it next time.
680
ReactNoop.render(<Foo text="foo" step={1} />);
675
- expect(Scheduler).toFlushAndYield(['Foo', 'Content', 'Bar', 'Bar']);
681
+ await waitForAll(['Foo', 'Content', 'Bar', 'Bar']);
682
683
// Since we did nothing to the middle subtree during the interruption,
684
// we should be able to reuse the reconciliation work that we already did
@@ -688,7 +694,7 @@ describe('ReactIncrementalSideEffects', () => {
694
);
695
});
696
691
- it('can update a completed tree before it has a chance to commit', () => {
697
+ it('can update a completed tree before it has a chance to commit', async () => {
698
function Foo(props) {
699
Scheduler.unstable_yieldValue('Foo');
700
return <span prop={props.step} />;
@@ -697,7 +703,7 @@ describe('ReactIncrementalSideEffects', () => {
703
ReactNoop.render(<Foo step={1} />);
704
});
705
// This should be just enough to complete the tree without committing it
700
- expect(Scheduler).toFlushAndYieldThrough(['Foo']);
706
+ await waitFor(['Foo']);
707
expect(ReactNoop.getChildrenAsJSX()).toEqual(null);
708
// To confirm, perform one more unit of work. The tree should now
709
// be flushed.
@@ -708,7 +714,7 @@ describe('ReactIncrementalSideEffects', () => {
714
ReactNoop.render(<Foo step={2} />);
715
});
716
// This should be just enough to complete the tree without committing it
711
- expect(Scheduler).toFlushAndYieldThrough(['Foo']);
717
+ await waitFor(['Foo']);
718
expect(ReactNoop.getChildrenAsJSX()).toEqual(<span prop={1} />);
719
// This time, before we commit the tree, we update the root component with
720
// new props
@@ -723,12 +729,12 @@ describe('ReactIncrementalSideEffects', () => {
729
expect(ReactNoop.getChildrenAsJSX()).toEqual(<span prop={2} />);
730
// If we flush the rest of the work, we should get another commit that
731
// renders 3. If it renders 2 again, that means an update was dropped.
726
- expect(Scheduler).toFlushWithoutYielding();
732
+ await waitForAll([]);
733
expect(ReactNoop.getChildrenAsJSX()).toEqual(<span prop={3} />);
734
});
735
736
// @gate www
731
- it('updates a child even though the old props is empty', () => {
737
+ it('updates a child even though the old props is empty', async () => {
738
function Foo(props) {
739
return (
740
<LegacyHiddenDiv mode="hidden">
@@ -738,7 +744,7 @@ describe('ReactIncrementalSideEffects', () => {
744
}
745
746
ReactNoop.render(<Foo />);
741
- expect(Scheduler).toFlushWithoutYielding();
747
+ await waitForAll([]);
748
expect(ReactNoop.getChildrenAsJSX()).toEqual(
749
<div hidden={true}>
750
<span prop={1} />
@@ -746,7 +752,7 @@ describe('ReactIncrementalSideEffects', () => {
752
);
753
});
754
749
- xit('can defer side-effects and resume them later on', () => {
755
+ xit('can defer side-effects and resume them later on', async () => {
756
class Bar extends React.Component {
757
shouldComponentUpdate(nextProps) {
758
return this.props.idx !== nextProps.idx;
@@ -809,7 +815,7 @@ describe('ReactIncrementalSideEffects', () => {
815
</div>,
816
);
817
ReactNoop.render(<Foo tick={3} idx={1} />);
812
- expect(Scheduler).toFlushWithoutYielding();
818
+ await waitForAll([]);
819
expect(ReactNoop).toMatchRenderedOutput(
820
<div>
821
<span prop={3} />
@@ -829,7 +835,7 @@ describe('ReactIncrementalSideEffects', () => {
835
expect(innerSpanA).toBe(innerSpanB);
836
});
837
832
- xit('can defer side-effects and reuse them later - complex', function () {
838
+ xit('can defer side-effects and reuse them later - complex', async function () {
839
let ops = [];
840
841
class Bar extends React.Component {
@@ -892,7 +898,7 @@ describe('ReactIncrementalSideEffects', () => {
898
expect(ops).toEqual(['Foo']);
899
ops = [];
900
895
- expect(Scheduler).toFlushWithoutYielding();
901
+ await waitForAll([]);
902
expect(ReactNoop).toMatchRenderedOutput([
903
<div>
904
<span prop={1} />,
@@ -959,7 +965,7 @@ describe('ReactIncrementalSideEffects', () => {
965
966
// We should now be able to reuse some of the work we've already done
967
// and replay those side-effects.
962
- expect(Scheduler).toFlushWithoutYielding();
968
+ await waitForAll([]);
969
expect(ReactNoop).toMatchRenderedOutput([
970
<div>
971
<span prop={3} />,
@@ -979,7 +985,7 @@ describe('ReactIncrementalSideEffects', () => {
985
});
986
987
// @gate www
982
- it('deprioritizes setStates that happens within a deprioritized tree', () => {
988
+ it('deprioritizes setStates that happens within a deprioritized tree', async () => {
989
const barInstances = [];
990
991
class Bar extends React.Component {
@@ -1010,7 +1016,7 @@ describe('ReactIncrementalSideEffects', () => {
1016
);
1017
}
1018
ReactNoop.render(<Foo tick={0} idx={0} />);
1013
- expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Bar', 'Bar']);
1019
+ await waitForAll(['Foo', 'Bar', 'Bar', 'Bar']);
1020
expect(ReactNoop.getChildrenAsJSX()).toEqual(
1021
<div>
1022
<span prop={0} />
@@ -1023,7 +1029,7 @@ describe('ReactIncrementalSideEffects', () => {
1029
);
1030
1031
ReactNoop.render(<Foo tick={1} idx={1} />);
1026
- expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Bar', 'Bar']);
1032
+ await waitFor(['Foo', 'Bar', 'Bar']);
1033
expect(ReactNoop.getChildrenAsJSX()).toEqual(
1034
<div>
1035
{/* Updated */}
@@ -1041,7 +1047,7 @@ describe('ReactIncrementalSideEffects', () => {
1047
// This should not be enough time to render the content of all the hidden
1048
// items. Including the set state since that is deprioritized.
1049
// ReactNoop.flushDeferredPri(35);
1044
- expect(Scheduler).toFlushAndYieldThrough(['Bar']);
1050
+ await waitFor(['Bar']);
1051
expect(ReactNoop.getChildrenAsJSX()).toEqual(
1052
<div>
1053
{/* Updated */}
@@ -1057,7 +1063,7 @@ describe('ReactIncrementalSideEffects', () => {
1063
1064
// However, once we render fully, we will have enough time to finish it all
1065
// at once.
1060
- expect(Scheduler).toFlushAndYield(['Bar', 'Bar']);
1066
+ await waitForAll(['Bar', 'Bar']);
1067
expect(ReactNoop.getChildrenAsJSX()).toEqual(
1068
<div>
1069
<span prop={1} />
@@ -1074,7 +1080,7 @@ describe('ReactIncrementalSideEffects', () => {
1080
// moves to "current" without flushing due to having lower priority. Does this
1081
// even happen? Maybe a child doesn't get processed because it is lower prio?
1082
1077
- it('calls callback after update is flushed', () => {
1083
+ it('calls callback after update is flushed', async () => {
1084
let instance;
1085
class Foo extends React.Component {
1086
constructor() {
@@ -1088,18 +1094,18 @@ describe('ReactIncrementalSideEffects', () => {
1094
}
1095
1096
ReactNoop.render(<Foo />);
1091
- expect(Scheduler).toFlushWithoutYielding();
1097
+ await waitForAll([]);
1098
expect(ReactNoop).toMatchRenderedOutput(<span prop="foo" />);
1099
let called = false;
1100
instance.setState({text: 'bar'}, () => {
1101
expect(ReactNoop).toMatchRenderedOutput(<span prop="bar" />);
1102
called = true;
1103
});
1098
- expect(Scheduler).toFlushWithoutYielding();
1104
+ await waitForAll([]);
1105
expect(called).toBe(true);
1106
});
1107
1102
- it('calls setState callback even if component bails out', () => {
1108
+ it('calls setState callback even if component bails out', async () => {
1109
let instance;
1110
class Foo extends React.Component {
1111
constructor() {
@@ -1116,19 +1122,19 @@ describe('ReactIncrementalSideEffects', () => {
1122
}
1123
1124
ReactNoop.render(<Foo />);
1119
- expect(Scheduler).toFlushWithoutYielding();
1125
+ await waitForAll([]);
1126
expect(ReactNoop).toMatchRenderedOutput(<span prop="foo" />);
1127
let called = false;
1128
instance.setState({}, () => {
1129
called = true;
1130
});
1125
- expect(Scheduler).toFlushWithoutYielding();
1131
+ await waitForAll([]);
1132
expect(called).toBe(true);
1133
});
1134
1135
// TODO: Test that callbacks are not lost if an update is preempted.
1136
1131
- it('calls componentWillUnmount after a deletion, even if nested', () => {
1137
+ it('calls componentWillUnmount after a deletion, even if nested', async () => {
1138
const ops = [];
1139
1140
class Bar extends React.Component {
@@ -1170,11 +1176,11 @@ describe('ReactIncrementalSideEffects', () => {
1176
}
1177
1178
ReactNoop.render(<Foo show={true} />);
1173
- expect(Scheduler).toFlushWithoutYielding();
1179
+ await waitForAll([]);
1180
expect(ops).toEqual([]);
1181
1182
ReactNoop.render(<Foo show={false} />);
1177
- expect(Scheduler).toFlushWithoutYielding();
1183
+ await waitForAll([]);
1184
expect(ops).toEqual([
1185
'A',
1186
'Wrapper',
@@ -1188,7 +1194,7 @@ describe('ReactIncrementalSideEffects', () => {
1194
]);
1195
});
1196
1191
- it('calls componentDidMount/Update after insertion/update', () => {
1197
+ it('calls componentDidMount/Update after insertion/update', async () => {
1198
let ops = [];
1199
1200
class Bar extends React.Component {
@@ -1233,7 +1239,7 @@ describe('ReactIncrementalSideEffects', () => {
1239
}
1240
1241
ReactNoop.render(<Foo />);
1236
- expect(Scheduler).toFlushWithoutYielding();
1242
+ await waitForAll([]);
1243
expect(ops).toEqual([
1244
'mount:A',
1245
'mount:B',
@@ -1249,7 +1255,7 @@ describe('ReactIncrementalSideEffects', () => {
1255
ops = [];
1256
1257
ReactNoop.render(<Foo />);
1252
- expect(Scheduler).toFlushWithoutYielding();
1258
+ await waitForAll([]);
1259
expect(ops).toEqual([
1260
'update:A',
1261
'update:B',
@@ -1263,7 +1269,7 @@ describe('ReactIncrementalSideEffects', () => {
1269
]);
1270
});
1271
1266
- it('invokes ref callbacks after insertion/update/unmount', () => {
1272
+ it('invokes ref callbacks after insertion/update/unmount', async () => {
1273
let classInstance = null;
1274
1275
let ops = [];
@@ -1310,7 +1316,7 @@ describe('ReactIncrementalSideEffects', () => {
1316
1317
// Refs that switch function instances get reinvoked
1318
ReactNoop.render(<Foo show={true} />);
1313
- expect(Scheduler).toFlushWithoutYielding();
1319
+ await waitForAll([]);
1320
expect(ops).toEqual([
1321
// detach all refs that switched handlers first.
1322
null,
@@ -1323,7 +1329,7 @@ describe('ReactIncrementalSideEffects', () => {
1329
ops = [];
1330
1331
ReactNoop.render(<Foo show={false} />);
1326
- expect(Scheduler).toFlushWithoutYielding();
1332
+ await waitForAll([]);
1333
expect(ops).toEqual([
1334
// unmount
1335
null,
@@ -1334,7 +1340,7 @@ describe('ReactIncrementalSideEffects', () => {
1340
// TODO: Test that mounts, updates, refs, unmounts and deletions happen in the
1341
// expected way for aborted and resumed render life-cycles.
1342
1337
- it('supports string refs', () => {
1343
+ it('supports string refs', async () => {
1344
let fooInstance = null;
1345
1346
class Bar extends React.Component {
@@ -1354,8 +1360,8 @@ describe('ReactIncrementalSideEffects', () => {
1360
}
1361
1362
ReactNoop.render(<Foo />);
1357
- expect(() => {
1358
- expect(Scheduler).toFlushWithoutYielding();
1363
+ await expect(async () => {
1364
+ await waitForAll([]);
1365
}).toErrorDev([
1366
'Warning: Component "Foo" contains the string ref "bar". ' +
1367
'Support for string refs will be removed in a future major release. ' +
packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js
+57
-65
@@ -15,6 +15,9 @@ let ReactNoop;
15
let Scheduler;
16
let ContinuousEventPriority;
17
let act;
18
+let waitForAll;
19
+let waitFor;
20
+let assertLog;
21
22
describe('ReactIncrementalUpdates', () => {
23
beforeEach(() => {
@@ -26,6 +29,11 @@ describe('ReactIncrementalUpdates', () => {
29
act = require('jest-react').act;
30
ContinuousEventPriority =
31
require('react-reconciler/constants').ContinuousEventPriority;
32
+
33
+ const InternalTestUtils = require('internal-test-utils');
34
+ waitForAll = InternalTestUtils.waitForAll;
35
+ waitFor = InternalTestUtils.waitFor;
36
+ assertLog = InternalTestUtils.assertLog;
37
});
38
39
function flushNextRenderIfExpired() {
@@ -37,7 +45,7 @@ describe('ReactIncrementalUpdates', () => {
45
ReactNoop.flushSync();
46
}
47
40
- it('applies updates in order of priority', () => {
48
+ it('applies updates in order of priority', async () => {
49
let state;
50
class Foo extends React.Component {
51
state = {};
@@ -58,14 +66,14 @@ describe('ReactIncrementalUpdates', () => {
66
}
67
68
ReactNoop.render(<Foo />);
61
- expect(Scheduler).toFlushAndYieldThrough(['commit']);
69
+ await waitFor(['commit']);
70
71
expect(state).toEqual({a: 'a'});
64
- expect(Scheduler).toFlushWithoutYielding();
72
+ await waitForAll([]);
73
expect(state).toEqual({a: 'a', b: 'b', c: 'c'});
74
});
75
68
- it('applies updates with equal priority in insertion order', () => {
76
+ it('applies updates with equal priority in insertion order', async () => {
77
let state;
78
class Foo extends React.Component {
79
state = {};
@@ -82,11 +90,11 @@ describe('ReactIncrementalUpdates', () => {
90
}
91
92
ReactNoop.render(<Foo />);
85
- expect(Scheduler).toFlushWithoutYielding();
93
+ await waitForAll([]);
94
expect(state).toEqual({a: 'a', b: 'b', c: 'c'});
95
});
96
89
- it('only drops updates with equal or lesser priority when replaceState is called', () => {
97
+ it('only drops updates with equal or lesser priority when replaceState is called', async () => {
98
let instance;
99
class Foo extends React.Component {
100
state = {};
@@ -104,7 +112,7 @@ describe('ReactIncrementalUpdates', () => {
112
}
113
114
ReactNoop.render(<Foo />);
107
- expect(Scheduler).toFlushAndYield(['render', 'componentDidMount']);
115
+ await waitForAll(['render', 'componentDidMount']);
116
117
ReactNoop.flushSync(() => {
118
React.startTransition(() => {
@@ -122,14 +130,14 @@ describe('ReactIncrementalUpdates', () => {
130
// Even though a replaceState has been already scheduled, it hasn't been
131
// flushed yet because it has async priority.
132
expect(instance.state).toEqual({a: 'a', b: 'b'});
125
- expect(Scheduler).toHaveYielded(['render', 'componentDidUpdate']);
133
+ assertLog(['render', 'componentDidUpdate']);
134
127
- expect(Scheduler).toFlushAndYield(['render', 'componentDidUpdate']);
135
+ await waitForAll(['render', 'componentDidUpdate']);
136
// Now the rest of the updates are flushed, including the replaceState.
137
expect(instance.state).toEqual({c: 'c', d: 'd'});
138
});
139
132
- it('can abort an update, schedule additional updates, and resume', () => {
140
+ it('can abort an update, schedule additional updates, and resume', async () => {
141
let instance;
142
class Foo extends React.Component {
143
state = {};
@@ -140,7 +148,7 @@ describe('ReactIncrementalUpdates', () => {
148
}
149
150
ReactNoop.render(<Foo />);
143
- expect(Scheduler).toFlushWithoutYielding();
151
+ await waitForAll([]);
152
153
function createUpdate(letter) {
154
return () => {
@@ -159,7 +167,7 @@ describe('ReactIncrementalUpdates', () => {
167
});
168
169
// Begin the updates but don't flush them yet
162
- expect(Scheduler).toFlushAndYieldThrough(['a', 'b', 'c']);
170
+ await waitFor(['a', 'b', 'c']);
171
expect(ReactNoop).toMatchRenderedOutput(<span prop="" />);
172
173
// Schedule some more updates at different priorities
@@ -174,11 +182,11 @@ describe('ReactIncrementalUpdates', () => {
182
183
// The sync updates should have flushed, but not the async ones.
184
if (gate(flags => flags.enableUnifiedSyncLane)) {
177
- expect(Scheduler).toHaveYielded(['d', 'e', 'f']);
185
+ assertLog(['d', 'e', 'f']);
186
expect(ReactNoop).toMatchRenderedOutput(<span prop="def" />);
187
} else {
188
// Update d was dropped and replaced by e.
181
- expect(Scheduler).toHaveYielded(['e', 'f']);
189
+ assertLog(['e', 'f']);
190
expect(ReactNoop).toMatchRenderedOutput(<span prop="ef" />);
191
}
192
@@ -186,7 +194,7 @@ describe('ReactIncrementalUpdates', () => {
194
// they should be processed again, to ensure that the terminal state
195
// is deterministic.
196
if (gate(flags => !flags.enableUnifiedSyncLane)) {
189
- expect(Scheduler).toFlushAndYield([
197
+ await waitForAll([
198
// Since 'g' is in a transition, we'll process 'd' separately first.
199
// That causes us to process 'd' with 'e' and 'f' rebased.
200
'd',
@@ -202,7 +210,7 @@ describe('ReactIncrementalUpdates', () => {
210
'g',
211
]);
212
} else {
205
- expect(Scheduler).toFlushAndYield([
213
+ await waitForAll([
214
// Then we'll re-process everything for 'g'.
215
'a',
216
'b',
@@ -216,7 +224,7 @@ describe('ReactIncrementalUpdates', () => {
224
expect(ReactNoop).toMatchRenderedOutput(<span prop="abcdefg" />);
225
});
226
219
- it('can abort an update, schedule a replaceState, and resume', () => {
227
+ it('can abort an update, schedule a replaceState, and resume', async () => {
228
let instance;
229
class Foo extends React.Component {
230
state = {};
@@ -227,7 +235,7 @@ describe('ReactIncrementalUpdates', () => {
235
}
236
237
ReactNoop.render(<Foo />);
230
- expect(Scheduler).toFlushWithoutYielding();
238
+ await waitForAll([]);
239
240
function createUpdate(letter) {
241
return () => {
@@ -246,7 +254,7 @@ describe('ReactIncrementalUpdates', () => {
254
});
255
256
// Begin the updates but don't flush them yet
249
- expect(Scheduler).toFlushAndYieldThrough(['a', 'b', 'c']);
257
+ await waitFor(['a', 'b', 'c']);
258
expect(ReactNoop).toMatchRenderedOutput(<span prop="" />);
259
260
// Schedule some more updates at different priorities
@@ -264,10 +272,10 @@ describe('ReactIncrementalUpdates', () => {
272
273
// The sync updates should have flushed, but not the async ones.
274
if (gate(flags => flags.enableUnifiedSyncLane)) {
267
- expect(Scheduler).toHaveYielded(['d', 'e', 'f']);
275
+ assertLog(['d', 'e', 'f']);
276
} else {
277
// Update d was dropped and replaced by e.
270
- expect(Scheduler).toHaveYielded(['e', 'f']);
278
+ assertLog(['e', 'f']);
279
}
280
expect(ReactNoop).toMatchRenderedOutput(<span prop="f" />);
281
@@ -275,7 +283,7 @@ describe('ReactIncrementalUpdates', () => {
283
// they should be processed again, to ensure that the terminal state
284
// is deterministic.
285
if (gate(flags => !flags.enableUnifiedSyncLane)) {
278
- expect(Scheduler).toFlushAndYield([
286
+ await waitForAll([
287
// Since 'g' is in a transition, we'll process 'd' separately first.
288
// That causes us to process 'd' with 'e' and 'f' rebased.
289
'd',
@@ -291,7 +299,7 @@ describe('ReactIncrementalUpdates', () => {
299
'g',
300
]);
301
} else {
294
- expect(Scheduler).toFlushAndYield([
302
+ await waitForAll([
303
// Then we'll re-process everything for 'g'.
304
'a',
305
'b',
@@ -305,7 +313,7 @@ describe('ReactIncrementalUpdates', () => {
313
expect(ReactNoop).toMatchRenderedOutput(<span prop="fg" />);
314
});
315
308
- it('passes accumulation of previous updates to replaceState updater function', () => {
316
+ it('passes accumulation of previous updates to replaceState updater function', async () => {
317
let instance;
318
class Foo extends React.Component {
319
state = {};
@@ -315,7 +323,7 @@ describe('ReactIncrementalUpdates', () => {
323
}
324
}
325
ReactNoop.render(<Foo />);
318
- expect(Scheduler).toFlushWithoutYielding();
326
+ await waitForAll([]);
327
328
instance.setState({a: 'a'});
329
instance.setState({b: 'b'});
@@ -324,11 +332,11 @@ describe('ReactIncrementalUpdates', () => {
332
instance.updater.enqueueReplaceState(instance, previousState => ({
333
previousState,
334
}));
327
- expect(Scheduler).toFlushWithoutYielding();
335
+ await waitForAll([]);
336
expect(instance.state).toEqual({previousState: {a: 'a', b: 'b'}});
337
});
338
331
- it('does not call callbacks that are scheduled by another callback until a later commit', () => {
339
+ it('does not call callbacks that are scheduled by another callback until a later commit', async () => {
340
class Foo extends React.Component {
341
state = {};
342
componentDidMount() {
@@ -347,7 +355,7 @@ describe('ReactIncrementalUpdates', () => {
355
}
356
357
ReactNoop.render(<Foo />);
350
- expect(Scheduler).toFlushAndYield([
358
+ await waitForAll([
359
'render',
360
'did mount',
361
'render',
@@ -357,7 +365,7 @@ describe('ReactIncrementalUpdates', () => {
365
]);
366
});
367
360
- it('gives setState during reconciliation the same priority as whatever level is currently reconciling', () => {
368
+ it('gives setState during reconciliation the same priority as whatever level is currently reconciling', async () => {
369
let instance;
370
371
class Foo extends React.Component {
@@ -373,7 +381,7 @@ describe('ReactIncrementalUpdates', () => {
381
}
382
}
383
ReactNoop.render(<Foo />);
376
- expect(Scheduler).toFlushAndYield(['render']);
384
+ await waitForAll(['render']);
385
386
ReactNoop.flushSync(() => {
387
instance.setState({a: 'a'});
@@ -384,17 +392,13 @@ describe('ReactIncrementalUpdates', () => {
392
expect(instance.state).toEqual({a: 'a', b: 'b'});
393
394
if (gate(flags => flags.deferRenderPhaseUpdateToNextBatch)) {
387
- expect(Scheduler).toHaveYielded([
388
- 'componentWillReceiveProps',
389
- 'render',
390
- 'render',
391
- ]);
395
+ assertLog(['componentWillReceiveProps', 'render', 'render']);
396
} else {
393
- expect(Scheduler).toHaveYielded(['componentWillReceiveProps', 'render']);
397
+ assertLog(['componentWillReceiveProps', 'render']);
398
}
399
});
400
397
- it('updates triggered from inside a class setState updater', () => {
401
+ it('updates triggered from inside a class setState updater', async () => {
402
let instance;
403
class Foo extends React.Component {
404
state = {};
@@ -406,7 +410,7 @@ describe('ReactIncrementalUpdates', () => {
410
}
411
412
ReactNoop.render(<Foo />);
409
- expect(Scheduler).toFlushAndYield([
413
+ await waitForAll([
414
// Initial render
415
'render',
416
]);
@@ -451,7 +455,7 @@ describe('ReactIncrementalUpdates', () => {
455
this.setState({a: 'a'});
456
return {b: 'b'};
457
});
454
- expect(Scheduler).toFlushAndYield(
458
+ await waitForAll(
459
gate(flags =>
460
flags.deferRenderPhaseUpdateToNextBatch
461
? // In the new reconciler, updates inside the render phase are
@@ -529,24 +533,20 @@ describe('ReactIncrementalUpdates', () => {
533
return null;
534
}
535
532
- act(() => {
536
+ act(async () => {
537
React.startTransition(() => {
538
ReactNoop.render(<App />);
539
});
540
flushNextRenderIfExpired();
537
- expect(Scheduler).toHaveYielded([]);
538
- expect(Scheduler).toFlushAndYield([
539
- 'Render: 0',
540
- 'Commit: 0',
541
- 'Render: 1',
542
- ]);
541
+ assertLog([]);
542
+ await waitForAll(['Render: 0', 'Commit: 0', 'Render: 1']);
543
544
Scheduler.unstable_advanceTime(10000);
545
React.startTransition(() => {
546
setCount(2);
547
});
548
flushNextRenderIfExpired();
549
- expect(Scheduler).toHaveYielded([]);
549
+ assertLog([]);
550
});
551
});
552
@@ -559,7 +559,7 @@ describe('ReactIncrementalUpdates', () => {
559
ReactNoop.flushSync(() => {
560
ReactNoop.render(<Text text="A" />);
561
});
562
- expect(Scheduler).toHaveYielded(['A']);
562
+ assertLog(['A']);
563
564
Scheduler.unstable_advanceTime(10000);
565
@@ -567,7 +567,7 @@ describe('ReactIncrementalUpdates', () => {
567
ReactNoop.render(<Text text="B" />);
568
});
569
flushNextRenderIfExpired();
570
- expect(Scheduler).toHaveYielded([]);
570
+ assertLog([]);
571
});
572
573
it('regression: does not expire soon due to previous expired work', () => {
@@ -581,7 +581,7 @@ describe('ReactIncrementalUpdates', () => {
581
});
582
Scheduler.unstable_advanceTime(10000);
583
flushNextRenderIfExpired();
584
- expect(Scheduler).toHaveYielded(['A']);
584
+ assertLog(['A']);
585
586
Scheduler.unstable_advanceTime(10000);
587
@@ -589,7 +589,7 @@ describe('ReactIncrementalUpdates', () => {
589
ReactNoop.render(<Text text="B" />);
590
});
591
flushNextRenderIfExpired();
592
- expect(Scheduler).toHaveYielded([]);
592
+ assertLog([]);
593
});
594
595
it('when rebasing, does not exclude updates that were already committed, regardless of priority', async () => {
@@ -620,7 +620,7 @@ describe('ReactIncrementalUpdates', () => {
620
await act(async () => {
621
root.render(<App />);
622
});
623
- expect(Scheduler).toHaveYielded(['Committed: ']);
623
+ assertLog(['Committed: ']);
624
expect(root).toMatchRenderedOutput(null);
625
626
await act(async () => {
@@ -633,13 +633,9 @@ describe('ReactIncrementalUpdates', () => {
633
);
634
});
635
if (gate(flags => flags.enableUnifiedSyncLane)) {
636
- expect(Scheduler).toHaveYielded([
637
- 'Committed: B',
638
- 'Committed: BCD',
639
- 'Committed: ABCD',
640
- ]);
636
+ assertLog(['Committed: B', 'Committed: BCD', 'Committed: ABCD']);
637
} else {
642
- expect(Scheduler).toHaveYielded([
638
+ assertLog([
639
// A and B are pending. B is higher priority, so we'll render that first.
640
'Committed: B',
641
// Because A comes first in the queue, we're now in rebase mode. B must
@@ -685,7 +681,7 @@ describe('ReactIncrementalUpdates', () => {
681
await act(async () => {
682
root.render(<App />);
683
});
688
- expect(Scheduler).toHaveYielded([]);
684
+ assertLog([]);
685
expect(root).toMatchRenderedOutput(null);
686
687
await act(async () => {
@@ -697,13 +693,9 @@ describe('ReactIncrementalUpdates', () => {
693
);
694
});
695
if (gate(flags => flags.enableUnifiedSyncLane)) {
700
- expect(Scheduler).toHaveYielded([
701
- 'Committed: B',
702
- 'Committed: BCD',
703
- 'Committed: ABCD',
704
- ]);
696
+ assertLog(['Committed: B', 'Committed: BCD', 'Committed: ABCD']);
697
} else {
706
- expect(Scheduler).toHaveYielded([
698
+ assertLog([
699
// A and B are pending. B is higher priority, so we'll render that first.
700
'Committed: B',
701
// Because A comes first in the queue, we're now in rebase mode. B must
packages/react-reconciler/src/__tests__/ReactInterleavedUpdates-test.js
+16
-8
@@ -5,6 +5,9 @@ let startTransition;
5
let useState;
6
let useEffect;
7
let act;
8
+let assertLog;
9
+let waitFor;
10
+let waitForPaint;
11
12
describe('ReactInterleavedUpdates', () => {
13
beforeEach(() => {
@@ -17,6 +20,11 @@ describe('ReactInterleavedUpdates', () => {
20
startTransition = React.startTransition;
21
useState = React.useState;
22
useEffect = React.useEffect;
23
+
24
+ const InternalTestUtils = require('internal-test-utils');
25
+ assertLog = InternalTestUtils.assertLog;
26
+ waitFor = InternalTestUtils.waitFor;
27
+ waitForPaint = InternalTestUtils.waitForPaint;
28
});
29
30
function Text({text}) {
@@ -53,7 +61,7 @@ describe('ReactInterleavedUpdates', () => {
61
</>,
62
);
63
});
56
- expect(Scheduler).toHaveYielded([0, 0, 0]);
64
+ assertLog([0, 0, 0]);
65
expect(root).toMatchRenderedOutput('000');
66
67
await act(async () => {
@@ -61,7 +69,7 @@ describe('ReactInterleavedUpdates', () => {
69
updateChildren(1);
70
});
71
// Partially render the children. Only the first one.
64
- expect(Scheduler).toFlushAndYieldThrough([1]);
72
+ await waitFor([1]);
73
74
// In an interleaved event, schedule an update on each of the children.
75
// Including the two that haven't rendered yet.
@@ -70,11 +78,11 @@ describe('ReactInterleavedUpdates', () => {
78
});
79
80
// We should continue rendering without including the interleaved updates.
73
- expect(Scheduler).toFlushUntilNextPaint([1, 1]);
81
+ await waitForPaint([1, 1]);
82
expect(root).toMatchRenderedOutput('111');
83
});
84
// The interleaved updates flush in a separate render.
77
- expect(Scheduler).toHaveYielded([2, 2, 2]);
85
+ assertLog([2, 2, 2]);
86
expect(root).toMatchRenderedOutput('222');
87
});
88
@@ -96,7 +104,7 @@ describe('ReactInterleavedUpdates', () => {
104
await act(async () => {
105
root.render(<App />);
106
});
99
- expect(Scheduler).toHaveYielded(['A0', 'B0', 'C0']);
107
+ assertLog(['A0', 'B0', 'C0']);
108
expect(root).toMatchRenderedOutput('A0B0C0');
109
110
await act(async () => {
@@ -104,7 +112,7 @@ describe('ReactInterleavedUpdates', () => {
112
startTransition(() => {
113
setStep(1);
114
});
107
- expect(Scheduler).toFlushAndYieldThrough(['A1', 'B1']);
115
+ await waitFor(['A1', 'B1']);
116
117
// Schedule an interleaved update. This gets placed on a special queue.
118
startTransition(() => {
@@ -112,7 +120,7 @@ describe('ReactInterleavedUpdates', () => {
120
});
121
122
// Finish rendering the first update.
115
- expect(Scheduler).toFlushUntilNextPaint(['C1']);
123
+ await waitForPaint(['C1']);
124
125
// Schedule another update. (In the regression case, this was treated
126
// as a normal, non-interleaved update and it was inserted into the queue
@@ -122,7 +130,7 @@ describe('ReactInterleavedUpdates', () => {
130
});
131
});
132
// The last update should win.
125
- expect(Scheduler).toHaveYielded(['A3', 'B3', 'C3']);
133
+ assertLog(['A3', 'B3', 'C3']);
134
expect(root).toMatchRenderedOutput('A3B3C3');
135
});
136
});
packages/react/src/__tests__/ReactProfiler-test.internal.js
+4
-4
@@ -1874,7 +1874,7 @@ describe(`onPostCommit`, () => {
1874
loadModules();
1875
});
1876
1877
- it('should report time spent in passive effects', () => {
1877
+ it('should report time spent in passive effects', async () => {
1878
const callback = jest.fn();
1879
1880
const ComponentWithEffects = () => {
@@ -1910,7 +1910,7 @@ describe(`onPostCommit`, () => {
1910
</React.Profiler>,
1911
);
1912
});
1913
- Scheduler.unstable_flushAll();
1913
+ await waitForAll([]);
1914
1915
expect(callback).toHaveBeenCalledTimes(1);
1916
@@ -1931,7 +1931,7 @@ describe(`onPostCommit`, () => {
1931
</React.Profiler>,
1932
);
1933
});
1934
- Scheduler.unstable_flushAll();
1934
+ await waitForAll([]);
1935
1936
expect(callback).toHaveBeenCalledTimes(2);
1937
@@ -1950,7 +1950,7 @@ describe(`onPostCommit`, () => {
1950
<React.Profiler id="unmount-test" onPostCommit={callback} />,
1951
);
1952
});
1953
- Scheduler.unstable_flushAll();
1953
+ await waitForAll([]);
1954
1955
expect(callback).toHaveBeenCalledTimes(3);
1956