Codemod tests to waitFor pattern (3/?) (#26299)
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 17:02 UTC
e64a8f4035024ce749bcbcf93b2618e66200a7f7
15 files changed
+406
-400
packages/react-dom/src/__tests__/ReactDOMImageLoad-test.internal.js
+41
-74
@@ -24,6 +24,10 @@ let images = [];
24
let onLoadSpy = null;
25
let actualLoadSpy = null;
26
27
+let waitForAll;
28
+let waitFor;
29
+let assertLog;
30
+
31
function PhaseMarkers({children}) {
32
Scheduler.unstable_yieldValue('render start');
33
React.useLayoutEffect(() => {
@@ -94,6 +98,11 @@ describe('ReactDOMImageLoad', () => {
98
ReactDOMClient = require('react-dom/client');
99
// Suspense = React.Suspense;
100
101
+ const InternalTestUtils = require('internal-test-utils');
102
+ waitForAll = InternalTestUtils.waitForAll;
103
+ waitFor = InternalTestUtils.waitFor;
104
+ assertLog = InternalTestUtils.assertLog;
105
+
106
onLoadSpy = jest.fn(reactEvent => {
107
const src = reactEvent.target.getAttribute('src');
108
Scheduler.unstable_yieldValue('onLoadSpy [' + src + ']');
@@ -206,26 +215,17 @@ describe('ReactDOMImageLoad', () => {
215
),
216
);
217
209
- expect(Scheduler).toFlushAndYieldThrough([
210
- 'render start',
211
- 'Img default',
212
- 'Yield',
213
- ]);
218
+ await waitFor(['render start', 'Img default', 'Yield']);
219
const img = last(images);
220
loadImage(img);
216
- expect(Scheduler).toHaveYielded([
221
+ assertLog([
222
'actualLoadSpy [default]',
223
// no onLoadSpy since we have not completed render
224
]);
220
- expect(Scheduler).toFlushAndYield([
221
- 'a',
222
- 'load triggered',
223
- 'last layout',
224
- 'last passive',
225
- ]);
225
+ await waitForAll(['a', 'load triggered', 'last layout', 'last passive']);
226
expect(img.__needsDispatch).toBe(true);
227
loadImage(img);
228
- expect(Scheduler).toHaveYielded([
228
+ assertLog([
229
'actualLoadSpy [default]', // the browser reloading of the image causes this to yield again
230
'onLoadSpy [default]',
231
]);
@@ -244,7 +244,7 @@ describe('ReactDOMImageLoad', () => {
244
),
245
);
246
247
- expect(Scheduler).toFlushAndYieldThrough([
247
+ await waitFor([
248
'render start',
249
'Img default',
250
'load triggered',
@@ -253,11 +253,8 @@ describe('ReactDOMImageLoad', () => {
253
Scheduler.unstable_requestPaint();
254
const img = last(images);
255
loadImage(img);
256
- expect(Scheduler).toHaveYielded([
257
- 'actualLoadSpy [default]',
258
- 'onLoadSpy [default]',
259
- ]);
260
- expect(Scheduler).toFlushAndYield(['last passive']);
256
+ assertLog(['actualLoadSpy [default]', 'onLoadSpy [default]']);
257
+ await waitForAll(['last passive']);
258
expect(img.__needsDispatch).toBe(false);
259
expect(onLoadSpy).toHaveBeenCalledTimes(1);
260
});
@@ -286,16 +283,12 @@ describe('ReactDOMImageLoad', () => {
283
284
React.startTransition(() => root.render(<Base />));
285
289
- expect(Scheduler).toFlushAndYieldThrough([
290
- 'render start',
291
- 'Img a',
292
- 'Yield',
293
- ]);
286
+ await waitFor(['render start', 'Img a', 'Yield']);
287
const img = last(images);
288
loadImage(img);
296
- expect(Scheduler).toHaveYielded(['actualLoadSpy [a]']);
289
+ assertLog(['actualLoadSpy [a]']);
290
298
- expect(Scheduler).toFlushAndYieldThrough([
291
+ await waitFor([
292
'load triggered',
293
'last layout',
294
// the update in layout causes a passive effects flush before a sync render
@@ -309,7 +302,7 @@ describe('ReactDOMImageLoad', () => {
302
]);
303
expect(images.length).toBe(1);
304
loadImage(img);
312
- expect(Scheduler).toHaveYielded(['actualLoadSpy [b]', 'onLoadSpy [b]']);
305
+ assertLog(['actualLoadSpy [b]', 'onLoadSpy [b]']);
306
expect(onLoadSpy).toHaveBeenCalledTimes(1);
307
});
308
@@ -323,7 +316,7 @@ describe('ReactDOMImageLoad', () => {
316
</PhaseMarkers>,
317
);
318
326
- expect(Scheduler).toFlushAndYield([
319
+ await waitForAll([
320
'render start',
321
'Img default',
322
'load triggered',
@@ -332,10 +325,7 @@ describe('ReactDOMImageLoad', () => {
325
]);
326
const img = last(images);
327
loadImage(img);
335
- expect(Scheduler).toHaveYielded([
336
- 'actualLoadSpy [default]',
337
- 'onLoadSpy [default]',
338
- ]);
328
+ assertLog(['actualLoadSpy [default]', 'onLoadSpy [default]']);
329
expect(onLoadSpy).toHaveBeenCalledTimes(1);
330
});
331
@@ -365,26 +355,17 @@ describe('ReactDOMImageLoad', () => {
355
),
356
);
357
368
- expect(Scheduler).toFlushAndYieldThrough([
369
- 'render start',
370
- 'Img default',
371
- 'Yield',
372
- ]);
358
+ await waitFor(['render start', 'Img default', 'Yield']);
359
const img = last(images);
360
loadImage(img);
375
- expect(Scheduler).toHaveYielded(['actualLoadSpy [default]']);
376
- expect(Scheduler).toFlushAndYield([
377
- 'a',
378
- 'load triggered',
379
- 'last layout',
380
- 'last passive',
381
- ]);
361
+ assertLog(['actualLoadSpy [default]']);
362
+ await waitForAll(['a', 'load triggered', 'last layout', 'last passive']);
363
expect(img.__needsDispatch).toBe(true);
364
loadImage(img);
365
// we expect the browser to load the image again but since we are no longer rendering
366
// the img there will be no onLoad called
386
- expect(Scheduler).toHaveYielded(['actualLoadSpy [default]']);
387
- expect(Scheduler).toFlushWithoutYielding();
367
+ assertLog(['actualLoadSpy [default]']);
368
+ await waitForAll([]);
369
expect(onLoadSpy).not.toHaveBeenCalled();
370
});
371
@@ -426,7 +407,7 @@ describe('ReactDOMImageLoad', () => {
407
),
408
);
409
429
- expect(Scheduler).toFlushAndYieldThrough([
410
+ await waitFor([
411
// initial render
412
'render start',
413
'Img default',
@@ -434,8 +415,8 @@ describe('ReactDOMImageLoad', () => {
415
]);
416
const img = last(images);
417
loadImage(img);
437
- expect(Scheduler).toHaveYielded(['actualLoadSpy [default]']);
438
- expect(Scheduler).toFlushAndYield([
418
+ assertLog(['actualLoadSpy [default]']);
419
+ await waitForAll([
420
'a',
421
'load triggered',
422
// img is present at first
@@ -450,8 +431,8 @@ describe('ReactDOMImageLoad', () => {
431
loadImage(img);
432
// we expect the browser to load the image again but since we are no longer rendering
433
// the img there will be no onLoad called
453
- expect(Scheduler).toHaveYielded(['actualLoadSpy [default]']);
454
- expect(Scheduler).toFlushWithoutYielding();
434
+ assertLog(['actualLoadSpy [default]']);
435
+ await waitForAll([]);
436
expect(onLoadSpy).not.toHaveBeenCalled();
437
});
438
@@ -548,22 +529,18 @@ describe('ReactDOMImageLoad', () => {
529
530
root.render(<Base />);
531
551
- expect(Scheduler).toFlushWithoutYielding();
532
+ await waitForAll([]);
533
534
React.startTransition(() => externalSetSrc('a'));
535
555
- expect(Scheduler).toFlushAndYieldThrough([
556
- 'YieldingWithImage',
557
- 'Img a',
558
- 'Yield',
559
- ]);
536
+ await waitFor(['YieldingWithImage', 'Img a', 'Yield']);
537
let img = last(images);
538
loadImage(img);
562
- expect(Scheduler).toHaveYielded(['actualLoadSpy [a]']);
539
+ assertLog(['actualLoadSpy [a]']);
540
541
ReactDOM.flushSync(() => externalSetSrcAlt('b'));
542
566
- expect(Scheduler).toHaveYielded([
543
+ assertLog([
544
'YieldingWithImage',
545
'Img b',
546
'Yield',
@@ -576,18 +553,12 @@ describe('ReactDOMImageLoad', () => {
553
expect(img.__needsDispatch).toBe(true);
554
loadImage(img);
555
579
- expect(Scheduler).toHaveYielded(['actualLoadSpy [b]', 'onLoadSpy [b]']);
556
+ assertLog(['actualLoadSpy [b]', 'onLoadSpy [b]']);
557
// why is there another update here?
581
- expect(Scheduler).toFlushAndYield([
582
- 'YieldingWithImage',
583
- 'Img b',
584
- 'Yield',
585
- 'b',
586
- 'Committed',
587
- ]);
558
+ await waitForAll(['YieldingWithImage', 'Img b', 'Yield', 'b', 'Committed']);
559
});
560
590
- it('preserves the src property / attribute when triggering a potential new load event', () => {
561
+ it('preserves the src property / attribute when triggering a potential new load event', async () => {
562
// this test covers a regression identified in https://github.com/mui/material-ui/pull/31263
563
// where the resetting of the src property caused the property to change from relative to fully qualified
564
@@ -612,17 +583,13 @@ describe('ReactDOMImageLoad', () => {
583
);
584
585
// render to yield to capture state of img src attribute and property before commit
615
- expect(Scheduler).toFlushAndYieldThrough([
616
- 'render start',
617
- 'Img default',
618
- 'Yield',
619
- ]);
586
+ await waitFor(['render start', 'Img default', 'Yield']);
587
const img = last(images);
588
const renderSrcProperty = img.src;
589
const renderSrcAttr = img.getAttribute('src');
590
591
// finish render and commit causing the src property to be rewritten
625
- expect(Scheduler).toFlushAndYield(['a', 'last layout', 'last passive']);
592
+ await waitForAll(['a', 'last layout', 'last passive']);
593
const commitSrcProperty = img.src;
594
const commitSrcAttr = img.getAttribute('src');
595
packages/react-dom/src/__tests__/ReactDOMNativeEventHeuristic-test.js
+9
-3
@@ -15,6 +15,8 @@ let ReactDOM;
15
let ReactDOMClient;
16
let Scheduler;
17
let act;
18
+let assertLog;
19
+let waitFor;
20
21
describe('ReactDOMNativeEventHeuristic-test', () => {
22
let container;
@@ -28,6 +30,10 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
30
Scheduler = require('scheduler');
31
act = require('jest-react').act;
32
33
+ const InternalTestUtils = require('internal-test-utils');
34
+ assertLog = InternalTestUtils.assertLog;
35
+ waitFor = InternalTestUtils.waitFor;
36
+
37
document.body.appendChild(container);
38
});
39
@@ -301,10 +307,10 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
307
dispatchAndSetCurrentEvent(target.current, mouseEnterEvent);
308
309
// Since mouse end is not discrete, should not have updated yet
304
- expect(Scheduler).toHaveYielded(['not hovered']);
310
+ assertLog(['not hovered']);
311
expect(container.textContent).toEqual('not hovered');
312
307
- expect(Scheduler).toFlushAndYieldThrough(['hovered']);
313
+ await waitFor(['hovered']);
314
expect(container.textContent).toEqual('hovered');
315
});
316
expect(container.textContent).toEqual('hovered');
@@ -381,7 +387,7 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
387
pressEvent.initEvent('click', true, true);
388
dispatchAndSetCurrentEvent(target, pressEvent);
389
384
- expect(Scheduler).toHaveYielded(['Count: 0 [after batchedUpdates]']);
390
+ assertLog(['Count: 0 [after batchedUpdates]']);
391
expect(container.textContent).toEqual('Count: 0');
392
393
// Intentionally not using `act` so we can observe in between the click
packages/react-dom/src/__tests__/ReactDOMNestedEvents-test.js
+5
-3
@@ -15,6 +15,7 @@ describe('ReactDOMNestedEvents', () => {
15
let Scheduler;
16
let act;
17
let useState;
18
+ let assertLog;
19
20
beforeEach(() => {
21
jest.resetModules();
@@ -23,6 +24,9 @@ describe('ReactDOMNestedEvents', () => {
24
Scheduler = require('scheduler');
25
act = require('jest-react').act;
26
useState = React.useState;
27
+
28
+ const InternalTestUtils = require('internal-test-utils');
29
+ assertLog = InternalTestUtils.assertLog;
30
});
31
32
test('nested event dispatches should not cause updates to flush', async () => {
@@ -67,9 +71,7 @@ describe('ReactDOMNestedEvents', () => {
71
await act(async () => {
72
buttonRef.current.click();
73
});
70
- expect(Scheduler).toHaveYielded([
71
- 'Value right after focus call: Clicked: false, Focused: false',
72
- ]);
74
+ assertLog(['Value right after focus call: Clicked: false, Focused: false']);
75
expect(buttonRef.current.innerHTML).toEqual('Clicked: true, Focused: true');
76
});
77
});
packages/react-dom/src/__tests__/ReactDOMRoot-test.js
+9
-3
@@ -16,6 +16,8 @@ let ReactDOMServer = require('react-dom/server');
16
let Scheduler = require('scheduler');
17
let act;
18
let useEffect;
19
+let assertLog;
20
+let waitFor;
21
22
describe('ReactDOMRoot', () => {
23
let container;
@@ -30,6 +32,10 @@ describe('ReactDOMRoot', () => {
32
Scheduler = require('scheduler');
33
act = require('jest-react').act;
34
useEffect = React.useEffect;
35
+
36
+ const InternalTestUtils = require('internal-test-utils');
37
+ assertLog = InternalTestUtils.assertLog;
38
+ waitFor = InternalTestUtils.waitFor;
39
});
40
41
it('renders children', () => {
@@ -255,7 +261,7 @@ describe('ReactDOMRoot', () => {
261
Scheduler.unstable_yieldValue('callback');
262
});
263
expect(container.textContent).toEqual('Hi');
258
- expect(Scheduler).toHaveYielded(['callback']);
264
+ assertLog(['callback']);
265
});
266
267
it('warns when unmounting with legacy API (no previous content)', () => {
@@ -401,10 +407,10 @@ describe('ReactDOMRoot', () => {
407
await act(async () => {
408
root.render(<Foo value="b" />);
409
404
- expect(Scheduler).toHaveYielded(['a']);
410
+ assertLog(['a']);
411
expect(container.textContent).toEqual('a');
412
407
- expect(Scheduler).toFlushAndYieldThrough(['b']);
413
+ await waitFor(['b']);
414
if (gate(flags => flags.allowConcurrentByDefault)) {
415
expect(container.textContent).toEqual('a');
416
} else {
packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
+44
-40
@@ -21,6 +21,10 @@ let SuspenseList;
21
let Offscreen;
22
let act;
23
let IdleEventPriority;
24
+let waitForAll;
25
+let waitFor;
26
+let waitForPaint;
27
+let assertLog;
28
29
function normalizeCodeLocInfo(strOrErr) {
30
if (strOrErr && strOrErr.replace) {
@@ -113,6 +117,12 @@ describe('ReactDOMServerPartialHydration', () => {
117
SuspenseList = React.SuspenseList;
118
}
119
120
+ const InternalTestUtils = require('internal-test-utils');
121
+ waitForAll = InternalTestUtils.waitForAll;
122
+ assertLog = InternalTestUtils.assertLog;
123
+ waitForPaint = InternalTestUtils.waitForPaint;
124
+ waitFor = InternalTestUtils.waitFor;
125
+
126
IdleEventPriority = require('react-reconciler/constants').IdleEventPriority;
127
});
128
@@ -290,13 +300,7 @@ describe('ReactDOMServerPartialHydration', () => {
300
const finalHTML = ReactDOMServer.renderToString(<App />);
301
const container = document.createElement('section');
302
container.innerHTML = finalHTML;
293
- expect(Scheduler).toHaveYielded([
294
- 'Hello',
295
- 'Component',
296
- 'Component',
297
- 'Component',
298
- 'Component',
299
- ]);
303
+ assertLog(['Hello', 'Component', 'Component', 'Component', 'Component']);
304
305
expect(container.innerHTML).toBe(
306
'<!--$-->Hello<div>Component</div><div>Component</div><div>Component</div><div>Component</div><!--/$-->',
@@ -310,7 +314,7 @@ describe('ReactDOMServerPartialHydration', () => {
314
Scheduler.unstable_yieldValue(error.message);
315
},
316
});
313
- expect(Scheduler).toFlushAndYield([
317
+ await waitForAll([
318
'Suspend',
319
'Component',
320
'Component',
@@ -327,7 +331,7 @@ describe('ReactDOMServerPartialHydration', () => {
331
suspend = false;
332
resolve();
333
await promise;
330
- expect(Scheduler).toFlushAndYield([
334
+ await waitForAll([
335
// first pass, mismatches at end
336
'Hello',
337
'Component',
@@ -434,7 +438,7 @@ describe('ReactDOMServerPartialHydration', () => {
438
Scheduler.unstable_yieldValue(error.message);
439
},
440
});
437
- expect(Scheduler).toFlushAndYield([]);
441
+ await waitForAll([]);
442
443
expect(hydrated.length).toBe(0);
444
expect(deleted.length).toBe(0);
@@ -520,7 +524,7 @@ describe('ReactDOMServerPartialHydration', () => {
524
expect(container.innerHTML).toContain('<span>A</span>');
525
expect(container.innerHTML).not.toContain('<span>B</span>');
526
523
- expect(Scheduler).toHaveYielded([
527
+ assertLog([
528
'There was an error while hydrating this Suspense boundary. ' +
529
'Switched to client rendering.',
530
]);
@@ -640,7 +644,7 @@ describe('ReactDOMServerPartialHydration', () => {
644
});
645
});
646
}).toErrorDev('Did not expect server HTML to contain a <span> in <div>');
643
- expect(Scheduler).toHaveYielded([
647
+ assertLog([
648
'Hydration failed because the initial UI does not match what was rendered on the server.',
649
'There was an error while hydrating this Suspense boundary. Switched to client rendering.',
650
]);
@@ -1389,7 +1393,7 @@ describe('ReactDOMServerPartialHydration', () => {
1393
1394
suspend = false;
1395
const finalHTML = ReactDOMServer.renderToString(<App />);
1392
- expect(Scheduler).toHaveYielded(['Child', 'Sibling']);
1396
+ assertLog(['Child', 'Sibling']);
1397
1398
const container = document.createElement('div');
1399
container.innerHTML = finalHTML;
@@ -1401,7 +1405,7 @@ describe('ReactDOMServerPartialHydration', () => {
1405
1406
await act(async () => {
1407
suspend = true;
1404
- expect(Scheduler).toFlushAndYieldThrough(['Child']);
1408
+ await waitFor(['Child']);
1409
1410
// While we're part way through the hydration, we update the state.
1411
// This will schedule an update on the children of the suspense boundary.
@@ -1410,7 +1414,7 @@ describe('ReactDOMServerPartialHydration', () => {
1414
);
1415
1416
// This will throw it away and rerender.
1413
- expect(Scheduler).toFlushAndYield(['Child', 'Sibling']);
1417
+ await waitForAll(['Child', 'Sibling']);
1418
1419
expect(container.textContent).toBe('Hello');
1420
@@ -1418,7 +1422,7 @@ describe('ReactDOMServerPartialHydration', () => {
1422
resolve();
1423
await promise;
1424
});
1421
- expect(Scheduler).toHaveYielded(['Child', 'Sibling']);
1425
+ assertLog(['Child', 'Sibling']);
1426
1427
expect(container.textContent).toBe('Hello');
1428
});
@@ -1635,7 +1639,7 @@ describe('ReactDOMServerPartialHydration', () => {
1639
},
1640
});
1641
if (__DEV__) {
1638
- expect(Scheduler).toFlushAndYield([
1642
+ await waitForAll([
1643
'The server did not finish this Suspense boundary: The server used' +
1644
' "renderToString" which does not support Suspense. If you intended' +
1645
' for this Suspense boundary to render the fallback content on the' +
@@ -1644,7 +1648,7 @@ describe('ReactDOMServerPartialHydration', () => {
1648
' please switch to "renderToPipeableStream" which supports Suspense on the server',
1649
]);
1650
} else {
1647
- expect(Scheduler).toFlushAndYield([
1651
+ await waitForAll([
1652
'The server could not finish this Suspense boundary, likely due to ' +
1653
'an error during server rendering. Switched to client rendering.',
1654
]);
@@ -1708,7 +1712,7 @@ describe('ReactDOMServerPartialHydration', () => {
1712
},
1713
});
1714
if (__DEV__) {
1711
- expect(Scheduler).toFlushAndYield([
1715
+ await waitForAll([
1716
'The server did not finish this Suspense boundary: The server used' +
1717
' "renderToString" which does not support Suspense. If you intended' +
1718
' for this Suspense boundary to render the fallback content on the' +
@@ -1717,7 +1721,7 @@ describe('ReactDOMServerPartialHydration', () => {
1721
' please switch to "renderToPipeableStream" which supports Suspense on the server',
1722
]);
1723
} else {
1720
- expect(Scheduler).toFlushAndYield([
1724
+ await waitForAll([
1725
'The server could not finish this Suspense boundary, likely due to ' +
1726
'an error during server rendering. Switched to client rendering.',
1727
]);
@@ -1786,7 +1790,7 @@ describe('ReactDOMServerPartialHydration', () => {
1790
},
1791
});
1792
if (__DEV__) {
1789
- expect(Scheduler).toFlushAndYield([
1793
+ await waitForAll([
1794
'The server did not finish this Suspense boundary: The server used' +
1795
' "renderToString" which does not support Suspense. If you intended' +
1796
' for this Suspense boundary to render the fallback content on the' +
@@ -1795,7 +1799,7 @@ describe('ReactDOMServerPartialHydration', () => {
1799
' please switch to "renderToPipeableStream" which supports Suspense on the server',
1800
]);
1801
} else {
1798
- expect(Scheduler).toFlushAndYield([
1802
+ await waitForAll([
1803
'The server could not finish this Suspense boundary, likely due to ' +
1804
'an error during server rendering. Switched to client rendering.',
1805
]);
@@ -2028,7 +2032,7 @@ describe('ReactDOMServerPartialHydration', () => {
2032
2033
suspend = false;
2034
const html = ReactDOMServer.renderToString(<App />);
2031
- expect(Scheduler).toHaveYielded(['Before', 'After']);
2035
+ assertLog(['Before', 'After']);
2036
2037
const container = document.createElement('div');
2038
container.innerHTML = html;
@@ -2044,7 +2048,7 @@ describe('ReactDOMServerPartialHydration', () => {
2048
suspend = true;
2049
2050
await act(async () => {
2047
- expect(Scheduler).toFlushAndYieldThrough(['Before', 'After']);
2051
+ await waitFor(['Before', 'After']);
2052
2053
// This will cause us to skip the second row completely.
2054
});
@@ -2108,7 +2112,7 @@ describe('ReactDOMServerPartialHydration', () => {
2112
2113
suspend = true;
2114
if (__DEV__) {
2111
- expect(Scheduler).toFlushAndYield([
2115
+ await waitForAll([
2116
'The server did not finish this Suspense boundary: The server used' +
2117
' "renderToString" which does not support Suspense. If you intended' +
2118
' for this Suspense boundary to render the fallback content on the' +
@@ -2117,7 +2121,7 @@ describe('ReactDOMServerPartialHydration', () => {
2121
' please switch to "renderToPipeableStream" which supports Suspense on the server',
2122
]);
2123
} else {
2120
- expect(Scheduler).toFlushAndYield([
2124
+ await waitForAll([
2125
'The server could not finish this Suspense boundary, likely due to ' +
2126
'an error during server rendering. Switched to client rendering.',
2127
]);
@@ -2182,7 +2186,7 @@ describe('ReactDOMServerPartialHydration', () => {
2186
},
2187
});
2188
if (__DEV__) {
2185
- expect(Scheduler).toFlushAndYield([
2189
+ await waitForAll([
2190
'The server did not finish this Suspense boundary: The server used' +
2191
' "renderToString" which does not support Suspense. If you intended' +
2192
' for this Suspense boundary to render the fallback content on the' +
@@ -2191,7 +2195,7 @@ describe('ReactDOMServerPartialHydration', () => {
2195
' please switch to "renderToPipeableStream" which supports Suspense on the server',
2196
]);
2197
} else {
2194
- expect(Scheduler).toFlushAndYield([
2198
+ await waitForAll([
2199
'The server could not finish this Suspense boundary, likely due to ' +
2200
'an error during server rendering. Switched to client rendering.',
2201
]);
@@ -3009,7 +3013,7 @@ describe('ReactDOMServerPartialHydration', () => {
3013
3014
suspend = false;
3015
const finalHTML = ReactDOMServer.renderToString(<App />);
3012
- expect(Scheduler).toHaveYielded(['Child']);
3016
+ assertLog(['Child']);
3017
3018
const container = document.createElement('div');
3019
container.innerHTML = finalHTML;
@@ -3019,7 +3023,7 @@ describe('ReactDOMServerPartialHydration', () => {
3023
container,
3024
<App showSibling={false} />,
3025
);
3022
- expect(Scheduler).toFlushAndYield([]);
3026
+ await waitForAll([]);
3027
3028
expect(ref.current).toBe(null);
3029
expect(container.textContent).toBe('Hello');
@@ -3036,14 +3040,14 @@ describe('ReactDOMServerPartialHydration', () => {
3040
3041
// When we flush we expect the Normal pri render to take priority
3042
// over hydration.
3039
- expect(Scheduler).toFlushAndYieldThrough(['Sibling', 'Commit Sibling']);
3043
+ await waitFor(['Sibling', 'Commit Sibling']);
3044
3045
// We shouldn't have hydrated the child yet.
3046
expect(ref.current).toBe(null);
3047
// But we did have a chance to update the content.
3048
expect(container.textContent).toBe('HelloWorld');
3049
3046
- expect(Scheduler).toFlushAndYield(['Child']);
3050
+ await waitForAll(['Child']);
3051
3052
// Now we're hydrated.
3053
expect(ref.current).not.toBe(null);
@@ -3248,7 +3252,7 @@ describe('ReactDOMServerPartialHydration', () => {
3252
}
3253
3254
const finalHTML = ReactDOMServer.renderToString(<App />);
3251
- expect(Scheduler).toHaveYielded([]);
3255
+ assertLog([]);
3256
3257
const container = document.createElement('div');
3258
container.innerHTML = finalHTML;
@@ -3267,7 +3271,7 @@ describe('ReactDOMServerPartialHydration', () => {
3271
3272
// The tree successfully hydrates
3273
ReactDOMClient.hydrateRoot(container, <App />);
3270
- expect(Scheduler).toFlushAndYield([]);
3274
+ await waitForAll([]);
3275
expect(ref.current).toBe(span);
3276
});
3277
@@ -3295,7 +3299,7 @@ describe('ReactDOMServerPartialHydration', () => {
3299
// During server rendering, the Child component should not be evaluated,
3300
// because it's inside a hidden tree.
3301
const finalHTML = ReactDOMServer.renderToString(<App />);
3298
- expect(Scheduler).toHaveYielded(['App']);
3302
+ assertLog(['App']);
3303
3304
const container = document.createElement('div');
3305
container.innerHTML = finalHTML;
@@ -3313,11 +3317,11 @@ describe('ReactDOMServerPartialHydration', () => {
3317
3318
// The visible span successfully hydrates
3319
ReactDOMClient.hydrateRoot(container, <App />);
3316
- expect(Scheduler).toFlushUntilNextPaint(['App']);
3320
+ await waitForPaint(['App']);
3321
expect(visibleRef.current).toBe(visibleSpan);
3322
3323
// Subsequently, the hidden child is prerendered on the client
3320
- expect(Scheduler).toFlushUntilNextPaint(['HiddenChild']);
3324
+ await waitForPaint(['HiddenChild']);
3325
expect(container).toMatchInlineSnapshot(`
3326
<div>
3327
<span>
@@ -3434,7 +3438,7 @@ describe('ReactDOMServerPartialHydration', () => {
3438
],
3439
{withoutStack: 1},
3440
);
3437
- expect(Scheduler).toHaveYielded([
3441
+ assertLog([
3442
'Log recoverable error: Hydration failed because the initial UI does not match what was rendered on the server.',
3443
// TODO: There were multiple mismatches in a single container. Should
3444
// we attempt to de-dupe them?
@@ -3482,7 +3486,7 @@ describe('ReactDOMServerPartialHydration', () => {
3486
],
3487
{withoutStack: 1},
3488
);
3485
- expect(Scheduler).toHaveYielded([
3489
+ assertLog([
3490
'Text content does not match server-rendered HTML.',
3491
'There was an error while hydrating. Because the error happened outside ' +
3492
'of a Suspense boundary, the entire root will switch to client rendering.',
@@ -3527,7 +3531,7 @@ describe('ReactDOMServerPartialHydration', () => {
3531
],
3532
{withoutStack: 1},
3533
);
3530
- expect(Scheduler).toHaveYielded([
3534
+ assertLog([
3535
'Text content does not match server-rendered HTML.',
3536
'There was an error while hydrating. Because the error happened outside ' +
3537
'of a Suspense boundary, the entire root will switch to client rendering.',
packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
+104
-126
@@ -19,6 +19,10 @@ let ReactFeatureFlags;
19
let Scheduler;
20
let Suspense;
21
let act;
22
+let assertLog;
23
+let waitForAll;
24
+let waitFor;
25
+let waitForPaint;
26
27
let IdleEventPriority;
28
let ContinuousEventPriority;
@@ -137,6 +141,12 @@ describe('ReactDOMServerSelectiveHydration', () => {
141
Scheduler = require('scheduler');
142
Suspense = React.Suspense;
143
144
+ const InternalTestUtils = require('internal-test-utils');
145
+ assertLog = InternalTestUtils.assertLog;
146
+ waitForAll = InternalTestUtils.waitForAll;
147
+ waitFor = InternalTestUtils.waitFor;
148
+ waitForPaint = InternalTestUtils.waitForPaint;
149
+
150
IdleEventPriority = require('react-reconciler/constants').IdleEventPriority;
151
ContinuousEventPriority =
152
require('react-reconciler/constants').ContinuousEventPriority;
@@ -172,7 +182,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
182
183
const finalHTML = ReactDOMServer.renderToString(<App />);
184
175
- expect(Scheduler).toHaveYielded(['App', 'A', 'B']);
185
+ assertLog(['App', 'A', 'B']);
186
187
const container = document.createElement('div');
188
// We need this to be in the document since we'll dispatch events on it.
@@ -185,7 +195,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
195
ReactDOMClient.hydrateRoot(container, <App />);
196
197
// Nothing has been hydrated so far.
188
- expect(Scheduler).toHaveYielded([]);
198
+ assertLog([]);
199
200
// This should synchronously hydrate the root App and the second suspense
201
// boundary.
@@ -195,10 +205,10 @@ describe('ReactDOMServerSelectiveHydration', () => {
205
expect(result).toBe(false);
206
207
// We rendered App, B and then invoked the event without rendering A.
198
- expect(Scheduler).toHaveYielded(['App', 'B', 'Clicked B']);
208
+ assertLog(['App', 'B', 'Clicked B']);
209
210
// After continuing the scheduler, we finally hydrate A.
201
- expect(Scheduler).toFlushAndYield(['A']);
211
+ await waitForAll(['A']);
212
213
document.body.removeChild(container);
214
});
@@ -246,7 +256,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
256
257
const finalHTML = ReactDOMServer.renderToString(<App />);
258
249
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
259
+ assertLog(['App', 'A', 'B', 'C', 'D']);
260
261
const container = document.createElement('div');
262
// We need this to be in the document since we'll dispatch events on it.
@@ -263,14 +273,14 @@ describe('ReactDOMServerSelectiveHydration', () => {
273
ReactDOMClient.hydrateRoot(container, <App />);
274
275
// Nothing has been hydrated so far.
266
- expect(Scheduler).toHaveYielded([]);
276
+ assertLog([]);
277
278
// This click target cannot be hydrated yet because it's suspended.
279
await act(async () => {
280
const result = dispatchClickEvent(spanD);
281
expect(result).toBe(true);
282
});
273
- expect(Scheduler).toHaveYielded([
283
+ assertLog([
284
'App',
285
// Continuing rendering will render B next.
286
'B',
@@ -289,11 +299,11 @@ describe('ReactDOMServerSelectiveHydration', () => {
299
flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
300
)
301
) {
292
- expect(Scheduler).toHaveYielded(['D', 'A']);
302
+ assertLog(['D', 'A']);
303
} else {
304
// After the click, we should prioritize D and the Click first,
305
// and only after that render A and C.
296
- expect(Scheduler).toHaveYielded(['D', 'Clicked D', 'A']);
306
+ assertLog(['D', 'Clicked D', 'A']);
307
}
308
309
document.body.removeChild(container);
@@ -342,7 +352,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
352
353
const finalHTML = ReactDOMServer.renderToString(<App />);
354
345
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
355
+ assertLog(['App', 'A', 'B', 'C', 'D']);
356
357
const container = document.createElement('div');
358
// We need this to be in the document since we'll dispatch events on it.
@@ -361,7 +371,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
371
ReactDOMClient.hydrateRoot(container, <App />);
372
373
// Nothing has been hydrated so far.
364
- expect(Scheduler).toHaveYielded([]);
374
+ assertLog([]);
375
376
// This click target cannot be hydrated yet because the first is Suspended.
377
dispatchClickEvent(spanA);
@@ -374,9 +384,9 @@ describe('ReactDOMServerSelectiveHydration', () => {
384
flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
385
)
386
) {
377
- expect(Scheduler).toHaveYielded(['App', 'C', 'Clicked C']);
387
+ assertLog(['App', 'C', 'Clicked C']);
388
} else {
379
- expect(Scheduler).toHaveYielded(['App']);
389
+ assertLog(['App']);
390
}
391
392
await act(async () => {
@@ -388,7 +398,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
398
if (
399
ReactFeatureFlags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay
400
) {
391
- expect(Scheduler).toHaveYielded([
401
+ assertLog([
402
'A',
403
'D',
404
// B should render last since it wasn't clicked.
@@ -397,7 +407,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
407
} else {
408
// We should prioritize hydrating A, C and D first since we clicked in
409
// them. Only after they're done will we hydrate B.
400
- expect(Scheduler).toHaveYielded([
410
+ assertLog([
411
'A',
412
'Clicked A',
413
'C',
@@ -447,7 +457,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
457
458
const finalHTML = ReactDOMServer.renderToString(<App />);
459
450
- expect(Scheduler).toHaveYielded(['App', 'A', 'B']);
460
+ assertLog(['App', 'A', 'B']);
461
462
const container = document.createElement('div');
463
// We need this to be in the document since we'll dispatch events on it.
@@ -460,7 +470,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
470
ReactDOMClient.hydrateRoot(container, <App />);
471
472
// Nothing has been hydrated so far.
463
- expect(Scheduler).toHaveYielded([]);
473
+ assertLog([]);
474
475
const span = container.getElementsByTagName('span')[1];
476
@@ -471,10 +481,10 @@ describe('ReactDOMServerSelectiveHydration', () => {
481
target.virtualclick();
482
483
// We rendered App, B and then invoked the event without rendering A.
474
- expect(Scheduler).toHaveYielded(['App', 'B', 'Clicked B']);
484
+ assertLog(['App', 'B', 'Clicked B']);
485
486
// After continuing the scheduler, we finally hydrate A.
477
- expect(Scheduler).toFlushAndYield(['A']);
487
+ await waitForAll(['A']);
488
489
document.body.removeChild(container);
490
});
@@ -527,7 +537,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
537
538
const finalHTML = ReactDOMServer.renderToString(<App />);
539
530
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
540
+ assertLog(['App', 'A', 'B', 'C', 'D']);
541
542
const container = document.createElement('div');
543
// We need this to be in the document since we'll dispatch events on it.
@@ -545,14 +555,14 @@ describe('ReactDOMServerSelectiveHydration', () => {
555
ReactDOMClient.hydrateRoot(container, <App />);
556
557
// Nothing has been hydrated so far.
548
- expect(Scheduler).toHaveYielded([]);
558
+ assertLog([]);
559
560
// Continuing rendering will render B next.
561
await act(async () => {
562
const target = createEventTarget(spanD);
563
target.virtualclick();
564
});
555
- expect(Scheduler).toHaveYielded(['App', 'B', 'C']);
565
+ assertLog(['App', 'B', 'C']);
566
567
// After the click, we should prioritize D and the Click first,
568
// and only after that render A and C.
@@ -568,9 +578,9 @@ describe('ReactDOMServerSelectiveHydration', () => {
578
)
579
) {
580
// no replay
571
- expect(Scheduler).toHaveYielded(['D', 'A']);
581
+ assertLog(['D', 'A']);
582
} else {
573
- expect(Scheduler).toHaveYielded(['D', 'Clicked D', 'A']);
583
+ assertLog(['D', 'Clicked D', 'A']);
584
}
585
586
document.body.removeChild(container);
@@ -623,7 +633,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
633
634
const finalHTML = ReactDOMServer.renderToString(<App />);
635
626
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
636
+ assertLog(['App', 'A', 'B', 'C', 'D']);
637
638
const container = document.createElement('div');
639
// We need this to be in the document since we'll dispatch events on it.
@@ -643,7 +653,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
653
ReactDOMClient.hydrateRoot(container, <App />);
654
655
// Nothing has been hydrated so far.
646
- expect(Scheduler).toHaveYielded([]);
656
+ assertLog([]);
657
658
// This click target cannot be hydrated yet because the first is Suspended.
659
createEventTarget(spanA).virtualclick();
@@ -653,9 +663,9 @@ describe('ReactDOMServerSelectiveHydration', () => {
663
if (
664
ReactFeatureFlags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay
665
) {
656
- expect(Scheduler).toHaveYielded(['App', 'C', 'Clicked C']);
666
+ assertLog(['App', 'C', 'Clicked C']);
667
} else {
658
- expect(Scheduler).toHaveYielded(['App']);
668
+ assertLog(['App']);
669
}
670
await act(async () => {
671
suspend = false;
@@ -666,7 +676,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
676
if (
677
ReactFeatureFlags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay
678
) {
669
- expect(Scheduler).toHaveYielded([
679
+ assertLog([
680
'A',
681
'D',
682
// B should render last since it wasn't clicked.
@@ -675,7 +685,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
685
} else {
686
// We should prioritize hydrating A, C and D first since we clicked in
687
// them. Only after they're done will we hydrate B.
678
- expect(Scheduler).toHaveYielded([
688
+ assertLog([
689
'A',
690
'Clicked A',
691
'C',
@@ -734,7 +744,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
744
);
745
}
746
const finalHTML = ReactDOMServer.renderToString(<App />);
737
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
747
+ assertLog(['App', 'A', 'B', 'C', 'D']);
748
const container = document.createElement('div');
749
// We need this to be in the document since we'll dispatch events on it.
750
document.body.appendChild(container);
@@ -752,14 +762,14 @@ describe('ReactDOMServerSelectiveHydration', () => {
762
ReactDOMClient.hydrateRoot(container, <App />);
763
764
// Nothing has been hydrated so far.
755
- expect(Scheduler).toHaveYielded([]);
765
+ assertLog([]);
766
// Click D
767
dispatchMouseHoverEvent(spanD, null);
768
dispatchClickEvent(spanD);
769
// Hover over B and then C.
770
dispatchMouseHoverEvent(spanB, spanD);
771
dispatchMouseHoverEvent(spanC, spanB);
762
- expect(Scheduler).toHaveYielded(['App']);
772
+ assertLog(['App']);
773
await act(async () => {
774
suspend = false;
775
resolve();
@@ -773,7 +783,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
783
) {
784
// We should prioritize hydrating D first because we clicked it.
785
// but event isnt replayed
776
- expect(Scheduler).toHaveYielded([
786
+ assertLog([
787
'D',
788
'B', // Ideally this should be later.
789
'C',
@@ -787,7 +797,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
797
// the same time since B was already scheduled.
798
// This is ok because it will at least not continue for nested
799
// boundary. See the next test below.
790
- expect(Scheduler).toHaveYielded([
800
+ assertLog([
801
'D',
802
'Clicked D',
803
'B', // Ideally this should be later.
@@ -883,7 +893,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
893
894
const finalHTML = ReactDOMServer.renderToString(<App />);
895
886
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
896
+ assertLog(['App', 'A', 'B', 'C', 'D']);
897
898
const container = document.createElement('div');
899
// We need this to be in the document since we'll dispatch events on it.
@@ -902,7 +912,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
912
ReactDOMClient.hydrateRoot(container, <App />);
913
914
// Nothing has been hydrated so far.
905
- expect(Scheduler).toHaveYielded([]);
915
+ assertLog([]);
916
917
// Click D
918
dispatchMouseHoverEvent(spanD, null);
@@ -911,7 +921,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
921
dispatchMouseHoverEvent(spanB, spanD);
922
dispatchMouseHoverEvent(spanC, spanB);
923
914
- expect(Scheduler).toHaveYielded(['App']);
924
+ assertLog(['App']);
925
926
await act(async () => {
927
suspend = false;
@@ -927,7 +937,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
937
) {
938
// We should prioritize hydrating D first because we clicked it.
939
// but event isnt replayed
930
- expect(Scheduler).toHaveYielded([
940
+ assertLog([
941
'D',
942
'B', // Ideally this should be later.
943
'C',
@@ -948,7 +958,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
958
// the same time since B was already scheduled.
959
// This is ok because it will at least not continue for nested
960
// boundary. See the next test below.
951
- expect(Scheduler).toHaveYielded([
961
+ assertLog([
962
'D',
963
'Clicked D',
964
'B', // Ideally this should be later.
@@ -964,7 +974,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
974
// This test shows existing quirk where stopPropagation on mouseout
975
// prevents mouseEnter from firing
976
dispatchMouseHoverEvent(spanC, spanB);
967
- expect(Scheduler).toHaveYielded([
977
+ assertLog([
978
'Mouse Out Capture B',
979
// stopPropagation stops these
980
// 'Mouse Out B',
@@ -1120,7 +1130,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1130
expect(InnerScheduler).toHaveYielded(['Suspend Inner']);
1131
}
1132
1123
- expect(Scheduler).toHaveYielded([]);
1133
+ assertLog([]);
1134
});
1135
afterEach(async () => {
1136
document.body.innerHTML = '';
@@ -1143,13 +1153,13 @@ describe('ReactDOMServerSelectiveHydration', () => {
1153
// Inner App renders because it is unblocked
1154
expect(InnerScheduler).toHaveYielded(['Inner']);
1155
// No event is replayed yet
1146
- expect(Scheduler).toHaveYielded([]);
1156
+ assertLog([]);
1157
1158
dispatchMouseHoverEvent(innerDiv);
1159
expect(OuterScheduler).toHaveYielded([]);
1160
expect(InnerScheduler).toHaveYielded([]);
1161
// No event is replayed yet
1152
- expect(Scheduler).toHaveYielded([]);
1162
+ assertLog([]);
1163
1164
await act(async () => {
1165
resolveOuter();
@@ -1166,7 +1176,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1176
// Outer hydrates and schedules Replay
1177
expect(OuterScheduler).toHaveYielded(['Outer']);
1178
// No event is replayed yet
1169
- expect(Scheduler).toHaveYielded([]);
1179
+ assertLog([]);
1180
1181
// fire scheduled Replay
1182
await act(async () => {
@@ -1177,10 +1187,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1187
});
1188
1189
// First Inner Mouse Enter fires then Outer Mouse Enter
1180
- expect(Scheduler).toHaveYielded([
1181
- 'Inner Mouse Enter',
1182
- 'Outer Mouse Enter',
1183
- ]);
1190
+ assertLog(['Inner Mouse Enter', 'Outer Mouse Enter']);
1191
});
1192
1193
// @gate enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay
@@ -1209,14 +1216,14 @@ describe('ReactDOMServerSelectiveHydration', () => {
1216
1217
// Inner is still blocked so when Outer replays the event in capture phase
1218
// inner ends up caling stopPropagation
1212
- expect(Scheduler).toHaveYielded([]);
1219
+ assertLog([]);
1220
expect(OuterScheduler).toHaveYielded([]);
1221
expect(InnerScheduler).toHaveYielded(['Suspend Inner']);
1222
1223
dispatchMouseHoverEvent(innerDiv);
1224
expect(OuterScheduler).toHaveYielded([]);
1225
expect(InnerScheduler).toHaveYielded([]);
1219
- expect(Scheduler).toHaveYielded([]);
1226
+ assertLog([]);
1227
1228
await act(async () => {
1229
resolveInner();
@@ -1238,10 +1245,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1245
});
1246
1247
// First Inner Mouse Enter fires then Outer Mouse Enter
1241
- expect(Scheduler).toHaveYielded([
1242
- 'Inner Mouse Enter',
1243
- 'Outer Mouse Enter',
1244
- ]);
1248
+ assertLog(['Inner Mouse Enter', 'Outer Mouse Enter']);
1249
});
1250
});
1251
@@ -1280,7 +1284,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1284
}
1285
1286
const finalHTML = ReactDOMServer.renderToString(<App />);
1283
- expect(Scheduler).toHaveYielded(['Child']);
1287
+ assertLog(['Child']);
1288
1289
const container = document.createElement('div');
1290
@@ -1294,11 +1298,11 @@ describe('ReactDOMServerSelectiveHydration', () => {
1298
dispatchMouseHoverEvent(childDiv);
1299
1300
// Not hydrated so event is saved for replay and stopPropagation is called
1297
- expect(Scheduler).toHaveYielded([]);
1301
+ assertLog([]);
1302
1303
resolve();
1304
Scheduler.unstable_flushNumberOfYields(1);
1301
- expect(Scheduler).toHaveYielded(['Child']);
1305
+ assertLog(['Child']);
1306
1307
Scheduler.unstable_scheduleCallback(
1308
Scheduler.unstable_ImmediatePriority,
@@ -1316,7 +1320,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1320
1321
// Even though the tree is remove the event is still dispatched with native event handler
1322
// on the container firing.
1319
- expect(Scheduler).toHaveYielded(['container2 mouse over']);
1323
+ assertLog(['container2 mouse over']);
1324
1325
document.body.removeChild(container);
1326
});
@@ -1366,7 +1370,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1370
1371
const finalHTML = ReactDOMServer.renderToString(<App />);
1372
1369
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
1373
+ assertLog(['App', 'A', 'B', 'C', 'D']);
1374
1375
const container = document.createElement('div');
1376
// We need this to be in the document since we'll dispatch events on it.
@@ -1385,7 +1389,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1389
ReactDOMClient.hydrateRoot(container, <App />);
1390
1391
// Nothing has been hydrated so far.
1388
- expect(Scheduler).toHaveYielded([]);
1392
+ assertLog([]);
1393
1394
// Hover over B and then C.
1395
dispatchMouseHoverEvent(spanB, spanD);
@@ -1402,7 +1406,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1406
// Next it doesn't matter if we hydrate A or B first but as an
1407
// implementation detail we're currently hydrating B first since
1408
// we at one point hovered over it and we never deprioritized it.
1405
- expect(Scheduler).toHaveYielded(['App', 'C', 'Hover C', 'A', 'B', 'D']);
1409
+ assertLog(['App', 'C', 'Hover C', 'A', 'B', 'D']);
1410
1411
document.body.removeChild(container);
1412
});
@@ -1432,7 +1436,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1436
1437
const finalHTML = ReactDOMServer.renderToString(<App />);
1438
1435
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C']);
1439
+ assertLog(['App', 'A', 'B', 'C']);
1440
1441
const container = document.createElement('div');
1442
container.innerHTML = finalHTML;
@@ -1443,7 +1447,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1447
const root = ReactDOMClient.hydrateRoot(container, <App />);
1448
1449
// Nothing has been hydrated so far.
1446
- expect(Scheduler).toHaveYielded([]);
1450
+ assertLog([]);
1451
1452
// Increase priority of B and then C.
1453
root.unstable_scheduleHydration(spanB);
@@ -1451,7 +1455,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1455
1456
// We should prioritize hydrating C first because the last added
1457
// gets highest priority followed by the next added.
1454
- expect(Scheduler).toFlushAndYield(['App', 'C', 'B', 'A']);
1458
+ await waitForAll(['App', 'C', 'B', 'A']);
1459
});
1460
1461
// @gate experimental || www
@@ -1485,7 +1489,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1489
1490
const finalHTML = ReactDOMServer.renderToString(<App a="A" />);
1491
1488
- expect(Scheduler).toHaveYielded(['App', 'A', 'a', 'B', 'b', 'C', 'c']);
1492
+ assertLog(['App', 'A', 'a', 'B', 'b', 'C', 'c']);
1493
1494
const container = document.createElement('div');
1495
container.innerHTML = finalHTML;
@@ -1497,10 +1501,10 @@ describe('ReactDOMServerSelectiveHydration', () => {
1501
const spanB = container.getElementsByTagName('span')[2];
1502
const spanC = container.getElementsByTagName('span')[4];
1503
1500
- act(() => {
1504
+ await act(async () => {
1505
const root = ReactDOMClient.hydrateRoot(container, <App a="A" />);
1506
// Hydrate the shell.
1503
- expect(Scheduler).toFlushAndYieldThrough(['App', 'Commit']);
1507
+ await waitFor(['App', 'Commit']);
1508
1509
// Render an update at Idle priority that needs to update A.
1510
@@ -1510,7 +1514,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1514
1515
// Start rendering. This will force the first boundary to hydrate
1516
// by scheduling it at one higher pri than Idle.
1513
- expect(Scheduler).toFlushAndYieldThrough([
1517
+ await waitFor([
1518
'App',
1519
1520
// Start hydrating A
@@ -1532,13 +1536,13 @@ describe('ReactDOMServerSelectiveHydration', () => {
1536
// priority levels.
1537
dispatchClickEvent(spanC);
1538
1535
- expect(Scheduler).toHaveYielded([
1539
+ assertLog([
1540
// Hydrate C first since we clicked it.
1541
'C',
1542
'c',
1543
]);
1544
1541
- expect(Scheduler).toFlushAndYield([
1545
+ await waitForAll([
1546
// Finish hydration of A since we forced it to hydrate.
1547
'A',
1548
'a',
@@ -1612,7 +1616,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1616
'useLayoutEffect does nothing on the server',
1617
]);
1618
1615
- expect(Scheduler).toHaveYielded(['App', 'A', 'B']);
1619
+ assertLog(['App', 'A', 'B']);
1620
1621
const container = document.createElement('div');
1622
// We need this to be in the document since we'll dispatch events on it.
@@ -1625,23 +1629,17 @@ describe('ReactDOMServerSelectiveHydration', () => {
1629
ReactDOMClient.hydrateRoot(container, <App />);
1630
1631
// Nothing has been hydrated so far.
1628
- expect(Scheduler).toHaveYielded([]);
1632
+ assertLog([]);
1633
1634
// This should synchronously hydrate the root App and the second suspense
1635
// boundary.
1636
dispatchClickEvent(span);
1637
1638
// We rendered App, B and then invoked the event without rendering A.
1635
- expect(Scheduler).toHaveYielded([
1636
- 'App',
1637
- 'B',
1638
- 'Capture Clicked B',
1639
- 'Native Click B',
1640
- 'Clicked B',
1641
- ]);
1639
+ assertLog(['App', 'B', 'Capture Clicked B', 'Native Click B', 'Clicked B']);
1640
1641
// After continuing the scheduler, we finally hydrate A.
1644
- expect(Scheduler).toFlushAndYield(['A']);
1642
+ await waitForAll(['A']);
1643
1644
document.body.removeChild(container);
1645
});
@@ -1686,7 +1684,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1684
}
1685
const finalHTML = ReactDOMServer.renderToString(<App />);
1686
1689
- expect(Scheduler).toHaveYielded(['App', 'Child']);
1687
+ assertLog(['App', 'Child']);
1688
1689
const container = document.createElement('div');
1690
document.body.appendChild(container);
@@ -1696,12 +1694,12 @@ describe('ReactDOMServerSelectiveHydration', () => {
1694
1695
ReactDOMClient.hydrateRoot(container, <App />);
1696
// Nothing has been hydrated so far.
1699
- expect(Scheduler).toHaveYielded([]);
1697
+ assertLog([]);
1698
1699
const span = container.getElementsByTagName('span')[0];
1700
dispatchClickEvent(span);
1701
1704
- expect(Scheduler).toHaveYielded(['App']);
1702
+ assertLog(['App']);
1703
1704
dispatchClickEvent(span);
1705
@@ -1740,7 +1738,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1738
1739
const finalHTML = ReactDOMServer.renderToString(<App />);
1740
1743
- expect(Scheduler).toHaveYielded(['App', 'A']);
1741
+ assertLog(['App', 'A']);
1742
1743
const container = document.createElement('div');
1744
// We need this to be in the document since we'll dispatch events on it.
@@ -1756,12 +1754,12 @@ describe('ReactDOMServerSelectiveHydration', () => {
1754
React.startTransition(() => {
1755
ReactDOMClient.hydrateRoot(container, <App />);
1756
});
1759
- expect(Scheduler).toFlushAndYieldThrough(['App']);
1757
+ await waitFor(['App']);
1758
1759
// This should attempt to synchronously hydrate the root, then pause
1760
// because it still suspended
1761
const result = dispatchClickEvent(span);
1764
- expect(Scheduler).toHaveYielded(['App']);
1762
+ assertLog(['App']);
1763
// The event should not have been cancelled because we didn't hydrate.
1764
expect(result).toBe(true);
1765
@@ -1778,9 +1776,9 @@ describe('ReactDOMServerSelectiveHydration', () => {
1776
flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
1777
)
1778
) {
1781
- expect(Scheduler).toHaveYielded(['App', 'A']);
1779
+ assertLog(['App', 'A']);
1780
} else {
1783
- expect(Scheduler).toHaveYielded(['App', 'A', 'Clicked A']);
1781
+ assertLog(['App', 'A', 'Clicked A']);
1782
}
1783
1784
document.body.removeChild(container);
@@ -1804,20 +1802,20 @@ describe('ReactDOMServerSelectiveHydration', () => {
1802
1803
let spanRef;
1804
const finalHTML = ReactDOMServer.renderToString(<App text="A" />);
1807
- expect(Scheduler).toHaveYielded(['App A', 'Child A']);
1805
+ assertLog(['App A', 'Child A']);
1806
const container = document.createElement('div');
1807
document.body.appendChild(container);
1808
container.innerHTML = finalHTML;
1809
const initialSpan = container.getElementsByTagName('span')[0];
1810
const root = ReactDOMClient.hydrateRoot(container, <App text="A" />);
1813
- expect(Scheduler).toFlushUntilNextPaint(['App A']);
1811
+ await waitForPaint(['App A']);
1812
1813
await act(async () => {
1814
ReactDOM.flushSync(() => {
1815
root.render(<App text="B" />);
1816
});
1817
});
1820
- expect(Scheduler).toHaveYielded(['App B', 'Child A', 'App B', 'Child B']);
1818
+ assertLog(['App B', 'Child A', 'App B', 'Child B']);
1819
expect(initialSpan).toBe(spanRef);
1820
});
1821
@@ -1840,13 +1838,13 @@ describe('ReactDOMServerSelectiveHydration', () => {
1838
1839
let spanRef;
1840
const finalHTML = ReactDOMServer.renderToString(<App text="A" />);
1843
- expect(Scheduler).toHaveYielded(['App A', 'Child A']);
1841
+ assertLog(['App A', 'Child A']);
1842
const container = document.createElement('div');
1843
document.body.appendChild(container);
1844
container.innerHTML = finalHTML;
1845
const initialSpan = container.getElementsByTagName('span')[0];
1846
const root = ReactDOMClient.hydrateRoot(container, <App text="A" />);
1849
- expect(Scheduler).toFlushUntilNextPaint(['App A']);
1847
+ await waitForPaint(['App A']);
1848
1849
await act(async () => {
1850
TODO_scheduleContinuousSchedulerTask(() => {
@@ -1854,7 +1852,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1852
});
1853
});
1854
1857
- expect(Scheduler).toHaveYielded(['App B', 'Child A', 'App B', 'Child B']);
1855
+ assertLog(['App B', 'Child A', 'App B', 'Child B']);
1856
expect(initialSpan).toBe(spanRef);
1857
});
1858
@@ -1876,17 +1874,17 @@ describe('ReactDOMServerSelectiveHydration', () => {
1874
1875
let spanRef;
1876
const finalHTML = ReactDOMServer.renderToString(<App text="A" />);
1879
- expect(Scheduler).toHaveYielded(['App A', 'Child A']);
1877
+ assertLog(['App A', 'Child A']);
1878
const container = document.createElement('div');
1879
document.body.appendChild(container);
1880
container.innerHTML = finalHTML;
1881
const initialSpan = container.getElementsByTagName('span')[0];
1882
const root = ReactDOMClient.hydrateRoot(container, <App text="A" />);
1885
- expect(Scheduler).toFlushUntilNextPaint(['App A']);
1883
+ await waitForPaint(['App A']);
1884
await act(async () => {
1885
root.render(<App text="B" />);
1886
});
1889
- expect(Scheduler).toHaveYielded(['App B', 'Child A', 'App B', 'Child B']);
1887
+ assertLog(['App B', 'Child A', 'App B', 'Child B']);
1888
expect(initialSpan).toBe(spanRef);
1889
});
1890
@@ -1927,7 +1925,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
1925
);
1926
}
1927
const finalHTML = ReactDOMServer.renderToString(<App a="A" />);
1930
- expect(Scheduler).toHaveYielded(['App', 'A', 'DefaultContext']);
1928
+ assertLog(['App', 'A', 'DefaultContext']);
1929
const container = document.createElement('div');
1930
container.innerHTML = finalHTML;
1931
document.body.appendChild(container);
@@ -1936,25 +1934,16 @@ describe('ReactDOMServerSelectiveHydration', () => {
1934
1935
await act(async () => {
1936
const root = ReactDOMClient.hydrateRoot(container, <App a="A" />);
1939
- expect(Scheduler).toFlushAndYieldThrough([
1940
- 'App',
1941
- 'DefaultContext',
1942
- 'Commit',
1943
- ]);
1937
+ await waitFor(['App', 'DefaultContext', 'Commit']);
1938
1939
TODO_scheduleIdleDOMSchedulerTask(() => {
1940
root.render(<App a="AA" />);
1941
});
1948
- expect(Scheduler).toFlushAndYieldThrough(['App', 'A']);
1942
+ await waitFor(['App', 'A']);
1943
1944
dispatchClickEvent(spanA);
1951
- expect(Scheduler).toHaveYielded(['A']);
1952
- expect(Scheduler).toFlushAndYield([
1953
- 'App',
1954
- 'AA',
1955
- 'DefaultContext',
1956
- 'Commit',
1957
- ]);
1945
+ assertLog(['A']);
1946
+ await waitForAll(['App', 'AA', 'DefaultContext', 'Commit']);
1947
});
1948
});
1949
@@ -1994,29 +1983,18 @@ describe('ReactDOMServerSelectiveHydration', () => {
1983
);
1984
}
1985
const finalHTML = ReactDOMServer.renderToString(<App a="A" />);
1997
- expect(Scheduler).toHaveYielded(['App', 'A', 'DefaultContext']);
1986
+ assertLog(['App', 'A', 'DefaultContext']);
1987
const container = document.createElement('div');
1988
container.innerHTML = finalHTML;
1989
1990
await act(async () => {
1991
const root = ReactDOMClient.hydrateRoot(container, <App a="A" />);
2003
- expect(Scheduler).toFlushAndYieldThrough([
2004
- 'App',
2005
- 'DefaultContext',
2006
- 'Commit',
2007
- ]);
1992
+ await waitFor(['App', 'DefaultContext', 'Commit']);
1993
1994
ReactDOM.flushSync(() => {
1995
root.render(<App a="AA" />);
1996
});
2012
- expect(Scheduler).toHaveYielded([
2013
- 'App',
2014
- 'A',
2015
- 'App',
2016
- 'AA',
2017
- 'DefaultContext',
2018
- 'Commit',
2019
- ]);
1997
+ assertLog(['App', 'A', 'App', 'AA', 'DefaultContext', 'Commit']);
1998
});
1999
});
2000
});
packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js
+31
-29
@@ -12,7 +12,6 @@
12
13
let JSDOM;
14
let Stream;
15
-let Scheduler;
15
let React;
16
let ReactDOM;
17
let ReactDOMClient;
@@ -23,18 +22,21 @@ let container;
22
let buffer = '';
23
let hasErrored = false;
24
let fatalError = undefined;
25
+let waitForAll;
26
27
describe('ReactDOM HostSingleton', () => {
28
beforeEach(() => {
29
jest.resetModules();
30
JSDOM = require('jsdom').JSDOM;
31
- Scheduler = require('scheduler');
31
React = require('react');
32
ReactDOM = require('react-dom');
33
ReactDOMClient = require('react-dom/client');
34
ReactDOMFizzServer = require('react-dom/server');
35
Stream = require('stream');
36
37
+ const InternalTestUtils = require('internal-test-utils');
38
+ waitForAll = InternalTestUtils.waitForAll;
39
+
40
// Test Environment
41
const jsdom = new JSDOM(
42
'<!DOCTYPE html><html><head></head><body><div id="container">',
@@ -130,7 +132,7 @@ describe('ReactDOM HostSingleton', () => {
132
<body />
133
</html>,
134
);
133
- expect(Scheduler).toFlushWithoutYielding();
135
+ await waitForAll([]);
136
expect(getVisibleChildren(document)).toEqual(
137
<html>
138
<head lang="en">
@@ -150,8 +152,8 @@ describe('ReactDOM HostSingleton', () => {
152
<body />
153
</html>,
154
);
153
- expect(() => {
154
- expect(Scheduler).toFlushWithoutYielding();
155
+ await expect(async () => {
156
+ await waitForAll([]);
157
}).toErrorDev(
158
'Warning: You are mounting a new head component when a previous one has not first unmounted. It is an error to render more than one head component at a time and attributes and children of these components will likely fail in unpredictable ways. Please only render a single instance of <head> and if you need to mount a new one, ensure any previous ones have unmounted first',
159
);
@@ -175,7 +177,7 @@ describe('ReactDOM HostSingleton', () => {
177
<body />
178
</html>,
179
);
178
- expect(Scheduler).toFlushWithoutYielding();
180
+ await waitForAll([]);
181
expect(getVisibleChildren(document)).toEqual(
182
<html>
183
<head lang="fr">
@@ -193,7 +195,7 @@ describe('ReactDOM HostSingleton', () => {
195
<body />
196
</html>,
197
);
196
- expect(Scheduler).toFlushWithoutYielding();
198
+ await waitForAll([]);
199
expect(getVisibleChildren(document)).toEqual(
200
<html>
201
<head lang="en">
@@ -280,7 +282,7 @@ describe('ReactDOM HostSingleton', () => {
282
</body>
283
</html>,
284
);
283
- expect(Scheduler).toFlushWithoutYielding();
285
+ await waitForAll([]);
286
expect(persistentElements).toEqual([
287
document.documentElement,
288
document.head,
@@ -320,7 +322,7 @@ describe('ReactDOM HostSingleton', () => {
322
</body>
323
</html>,
324
);
323
- expect(Scheduler).toFlushWithoutYielding();
325
+ await waitForAll([]);
326
expect(persistentElements).toEqual([
327
document.documentElement,
328
document.head,
@@ -359,7 +361,7 @@ describe('ReactDOM HostSingleton', () => {
361
</body>
362
</html>,
363
);
362
- expect(Scheduler).toFlushWithoutYielding();
364
+ await waitForAll([]);
365
expect(persistentElements).toEqual([
366
document.documentElement,
367
document.head,
@@ -395,7 +397,7 @@ describe('ReactDOM HostSingleton', () => {
397
</head>
398
</html>,
399
);
398
- expect(Scheduler).toFlushWithoutYielding();
400
+ await waitForAll([]);
401
expect(persistentElements).toEqual([
402
document.documentElement,
403
document.head,
@@ -422,7 +424,7 @@ describe('ReactDOM HostSingleton', () => {
424
425
// unmount the root
426
root.unmount();
425
- expect(Scheduler).toFlushWithoutYielding();
427
+ await waitForAll([]);
428
expect(persistentElements).toEqual([
429
document.documentElement,
430
document.head,
@@ -471,8 +473,8 @@ describe('ReactDOM HostSingleton', () => {
473
},
474
},
475
);
474
- expect(() => {
475
- expect(Scheduler).toFlushWithoutYielding();
476
+ await expect(async () => {
477
+ await waitForAll([]);
478
}).toErrorDev(
479
[
480
`Warning: Expected server HTML to contain a matching <div> in <body>.
@@ -555,7 +557,7 @@ describe('ReactDOM HostSingleton', () => {
557
},
558
);
559
expect(hydrationErrors).toEqual([]);
558
- expect(Scheduler).toFlushWithoutYielding();
560
+ await waitForAll([]);
561
expect(persistentElements).toEqual([
562
document.documentElement,
563
document.head,
@@ -627,7 +629,7 @@ describe('ReactDOM HostSingleton', () => {
629
</body>
630
</html>,
631
);
630
- expect(Scheduler).toFlushWithoutYielding();
632
+ await waitForAll([]);
633
634
// We construct and insert some artificial stylesheets mimicing what a 3rd party script might do
635
// In the future we could hydrate with these already in the document but the rules are restrictive
@@ -683,7 +685,7 @@ describe('ReactDOM HostSingleton', () => {
685
</html>,
686
);
687
686
- expect(Scheduler).toFlushWithoutYielding();
688
+ await waitForAll([]);
689
expect(getVisibleChildren(document)).toEqual(
690
<html>
691
<head>
@@ -711,7 +713,7 @@ describe('ReactDOM HostSingleton', () => {
713
</head>
714
</html>,
715
);
714
- expect(Scheduler).toFlushWithoutYielding();
716
+ await waitForAll([]);
717
expect(getVisibleChildren(document)).toEqual(
718
<html>
719
<head>
@@ -763,7 +765,7 @@ describe('ReactDOM HostSingleton', () => {
765
</body>
766
</>,
767
);
766
- expect(Scheduler).toFlushWithoutYielding();
768
+ await waitForAll([]);
769
expect(getVisibleChildren(document)).toEqual(
770
<html>
771
<head>
@@ -799,7 +801,7 @@ describe('ReactDOM HostSingleton', () => {
801
802
const root = ReactDOMClient.createRoot(container);
803
root.render(<title>something new</title>);
802
- expect(Scheduler).toFlushWithoutYielding();
804
+ await waitForAll([]);
805
expect(getVisibleChildren(document)).toEqual(
806
<html>
807
<head>
@@ -831,7 +833,7 @@ describe('ReactDOM HostSingleton', () => {
833
834
const root = ReactDOMClient.createRoot(container);
835
root.render(<div>something new</div>);
834
- expect(Scheduler).toFlushWithoutYielding();
836
+ await waitForAll([]);
837
expect(getVisibleChildren(document)).toEqual(
838
<html>
839
<head>
@@ -865,7 +867,7 @@ describe('ReactDOM HostSingleton', () => {
867
<body>foo</body>
868
</html>,
869
);
868
- expect(Scheduler).toFlushWithoutYielding();
870
+ await waitForAll([]);
871
expect(getVisibleChildren(document)).toEqual(
872
<html>
873
<head />
@@ -879,7 +881,7 @@ describe('ReactDOM HostSingleton', () => {
881
<body>bar</body>
882
</html>,
883
);
882
- expect(Scheduler).toFlushWithoutYielding();
884
+ await waitForAll([]);
885
expect(getVisibleChildren(document)).toEqual(
886
<html>
887
<head />
@@ -896,7 +898,7 @@ describe('ReactDOM HostSingleton', () => {
898
<body>baz</body>
899
</html>,
900
);
899
- expect(Scheduler).toFlushWithoutYielding();
901
+ await waitForAll([]);
902
expect(getVisibleChildren(document)).toEqual(
903
<html>
904
<head />
@@ -913,7 +915,7 @@ describe('ReactDOM HostSingleton', () => {
915
<body>foo</body>
916
</html>,
917
);
916
- expect(Scheduler).toFlushWithoutYielding();
918
+ await waitForAll([]);
919
expect(getVisibleChildren(document)).toEqual(
920
<html>
921
<head />
@@ -929,7 +931,7 @@ describe('ReactDOM HostSingleton', () => {
931
</body>
932
</html>,
933
);
932
- expect(Scheduler).toFlushWithoutYielding();
934
+ await waitForAll([]);
935
expect(getVisibleChildren(document)).toEqual(
936
<html>
937
<head />
@@ -945,7 +947,7 @@ describe('ReactDOM HostSingleton', () => {
947
<body>foo</body>
948
</html>,
949
);
948
- expect(Scheduler).toFlushWithoutYielding();
950
+ await waitForAll([]);
951
expect(getVisibleChildren(document)).toEqual(
952
<html>
953
<head />
@@ -961,7 +963,7 @@ describe('ReactDOM HostSingleton', () => {
963
</body>
964
</html>,
965
);
964
- expect(Scheduler).toFlushWithoutYielding();
966
+ await waitForAll([]);
967
expect(getVisibleChildren(document)).toEqual(
968
<html>
969
<head />
@@ -996,7 +998,7 @@ describe('ReactDOM HostSingleton', () => {
998
<body>foo</body>
999
</html>,
1000
);
999
- expect(Scheduler).toFlushWithoutYielding();
1001
+ await waitForAll([]);
1002
expect(getVisibleChildren(document)).toEqual(
1003
<html>
1004
<head />
packages/react-dom/src/__tests__/ReactDOMUseId-test.js
+6
-4
@@ -11,7 +11,6 @@
11
let JSDOM;
12
let React;
13
let ReactDOMClient;
14
-let Scheduler;
14
let clientAct;
15
let ReactDOMFizzServer;
16
let Stream;
@@ -24,6 +23,7 @@ let container;
23
let buffer = '';
24
let hasErrored = false;
25
let fatalError = undefined;
26
+let waitForPaint;
27
28
describe('useId', () => {
29
beforeEach(() => {
@@ -31,7 +31,6 @@ describe('useId', () => {
31
JSDOM = require('jsdom').JSDOM;
32
React = require('react');
33
ReactDOMClient = require('react-dom/client');
34
- Scheduler = require('scheduler');
34
clientAct = require('jest-react').act;
35
ReactDOMFizzServer = require('react-dom/server');
36
Stream = require('stream');
@@ -39,6 +38,9 @@ describe('useId', () => {
38
useId = React.useId;
39
useState = React.useState;
40
41
+ const InternalTestUtils = require('internal-test-utils');
42
+ waitForPaint = InternalTestUtils.waitForPaint;
43
+
44
// Test Environment
45
const jsdom = new JSDOM(
46
'<!DOCTYPE html><html><head></head><body><div id="container">',
@@ -443,7 +445,7 @@ describe('useId', () => {
445
const dehydratedSpan = container.getElementsByTagName('span')[0];
446
await clientAct(async () => {
447
const root = ReactDOMClient.hydrateRoot(container, <App />);
446
- expect(Scheduler).toFlushUntilNextPaint([]);
448
+ await waitForPaint([]);
449
expect(container).toMatchInlineSnapshot(`
450
<div
451
id="container"
@@ -524,7 +526,7 @@ describe('useId', () => {
526
const dehydratedSpan = container.getElementsByTagName('span')[0];
527
await clientAct(async () => {
528
const root = ReactDOMClient.hydrateRoot(container, <App />);
527
- expect(Scheduler).toFlushUntilNextPaint([]);
529
+ await waitForPaint([]);
530
expect(container).toMatchInlineSnapshot(`
531
<div
532
id="container"
packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
+70
-66
@@ -35,6 +35,7 @@ describe('ReactErrorBoundaries', () => {
35
let NoopErrorBoundary;
36
let RetryErrorBoundary;
37
let Normal;
38
+ let assertLog;
39
40
beforeEach(() => {
41
jest.useFakeTimers();
@@ -48,6 +49,9 @@ describe('ReactErrorBoundaries', () => {
49
act = require('jest-react').act;
50
Scheduler = require('scheduler');
51
52
+ const InternalTestUtils = require('internal-test-utils');
53
+ assertLog = InternalTestUtils.assertLog;
54
+
55
BrokenConstructor = class extends React.Component {
56
constructor(props) {
57
super(props);
@@ -805,7 +809,7 @@ describe('ReactErrorBoundaries', () => {
809
}
810
811
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
808
- expect(Scheduler).toHaveYielded([
812
+ assertLog([
813
'ErrorBoundary constructor',
814
'ErrorBoundary componentWillMount',
815
'ErrorBoundary render success',
@@ -820,7 +824,7 @@ describe('ReactErrorBoundaries', () => {
824
]);
825
826
ReactDOM.unmountComponentAtNode(container);
823
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
827
+ assertLog(['ErrorBoundary componentWillUnmount']);
828
});
829
830
it('renders an error state if child throws in render', () => {
@@ -832,7 +836,7 @@ describe('ReactErrorBoundaries', () => {
836
container,
837
);
838
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
835
- expect(Scheduler).toHaveYielded([
839
+ assertLog([
840
'ErrorBoundary constructor',
841
'ErrorBoundary componentWillMount',
842
'ErrorBoundary render success',
@@ -847,7 +851,7 @@ describe('ReactErrorBoundaries', () => {
851
]);
852
853
ReactDOM.unmountComponentAtNode(container);
850
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
854
+ assertLog(['ErrorBoundary componentWillUnmount']);
855
});
856
857
it('renders an error state if child throws in constructor', () => {
@@ -859,7 +863,7 @@ describe('ReactErrorBoundaries', () => {
863
container,
864
);
865
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
862
- expect(Scheduler).toHaveYielded([
866
+ assertLog([
867
'ErrorBoundary constructor',
868
'ErrorBoundary componentWillMount',
869
'ErrorBoundary render success',
@@ -872,7 +876,7 @@ describe('ReactErrorBoundaries', () => {
876
]);
877
878
ReactDOM.unmountComponentAtNode(container);
875
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
879
+ assertLog(['ErrorBoundary componentWillUnmount']);
880
});
881
882
it('renders an error state if child throws in componentWillMount', () => {
@@ -884,7 +888,7 @@ describe('ReactErrorBoundaries', () => {
888
container,
889
);
890
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
887
- expect(Scheduler).toHaveYielded([
891
+ assertLog([
892
'ErrorBoundary constructor',
893
'ErrorBoundary componentWillMount',
894
'ErrorBoundary render success',
@@ -898,7 +902,7 @@ describe('ReactErrorBoundaries', () => {
902
]);
903
904
ReactDOM.unmountComponentAtNode(container);
901
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
905
+ assertLog(['ErrorBoundary componentWillUnmount']);
906
});
907
908
it('renders an error state if context provider throws in componentWillMount', () => {
@@ -977,7 +981,7 @@ describe('ReactErrorBoundaries', () => {
981
</ErrorBoundary>,
982
container,
983
);
980
- expect(Scheduler).toHaveYielded([
984
+ assertLog([
985
'ErrorBoundary constructor',
986
'ErrorBoundary componentWillMount',
987
'ErrorBoundary render success',
@@ -995,7 +999,7 @@ describe('ReactErrorBoundaries', () => {
999
]);
1000
1001
ReactDOM.unmountComponentAtNode(container);
998
- expect(Scheduler).toHaveYielded([
1002
+ assertLog([
1003
'ErrorBoundary componentWillUnmount',
1004
'ErrorMessage componentWillUnmount',
1005
]);
@@ -1012,7 +1016,7 @@ describe('ReactErrorBoundaries', () => {
1016
container,
1017
);
1018
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
1015
- expect(Scheduler).toHaveYielded([
1019
+ assertLog([
1020
'ErrorBoundary constructor',
1021
'ErrorBoundary componentWillMount',
1022
'ErrorBoundary render success',
@@ -1037,7 +1041,7 @@ describe('ReactErrorBoundaries', () => {
1041
]);
1042
1043
ReactDOM.unmountComponentAtNode(container);
1040
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1044
+ assertLog(['ErrorBoundary componentWillUnmount']);
1045
});
1046
1047
it('propagates errors inside boundary during componentWillMount', () => {
@@ -1049,7 +1053,7 @@ describe('ReactErrorBoundaries', () => {
1053
container,
1054
);
1055
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
1052
- expect(Scheduler).toHaveYielded([
1056
+ assertLog([
1057
'ErrorBoundary constructor',
1058
'ErrorBoundary componentWillMount',
1059
'ErrorBoundary render success',
@@ -1063,7 +1067,7 @@ describe('ReactErrorBoundaries', () => {
1067
]);
1068
1069
ReactDOM.unmountComponentAtNode(container);
1066
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1070
+ assertLog(['ErrorBoundary componentWillUnmount']);
1071
});
1072
1073
it('propagates errors inside boundary while rendering error state', () => {
@@ -1077,7 +1081,7 @@ describe('ReactErrorBoundaries', () => {
1081
container,
1082
);
1083
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
1080
- expect(Scheduler).toHaveYielded([
1084
+ assertLog([
1085
'ErrorBoundary constructor',
1086
'ErrorBoundary componentWillMount',
1087
'ErrorBoundary render success',
@@ -1099,7 +1103,7 @@ describe('ReactErrorBoundaries', () => {
1103
]);
1104
1105
ReactDOM.unmountComponentAtNode(container);
1102
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1106
+ assertLog(['ErrorBoundary componentWillUnmount']);
1107
});
1108
1109
it('does not call componentWillUnmount when aborting initial mount', () => {
@@ -1113,7 +1117,7 @@ describe('ReactErrorBoundaries', () => {
1117
container,
1118
);
1119
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
1116
- expect(Scheduler).toHaveYielded([
1120
+ assertLog([
1121
'ErrorBoundary constructor',
1122
'ErrorBoundary componentWillMount',
1123
'ErrorBoundary render success',
@@ -1137,7 +1141,7 @@ describe('ReactErrorBoundaries', () => {
1141
]);
1142
1143
ReactDOM.unmountComponentAtNode(container);
1140
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1144
+ assertLog(['ErrorBoundary componentWillUnmount']);
1145
});
1146
1147
it('resets callback refs if mounting aborts', () => {
@@ -1157,7 +1161,7 @@ describe('ReactErrorBoundaries', () => {
1161
container,
1162
);
1163
expect(container.textContent).toBe('Caught an error: Hello.');
1160
- expect(Scheduler).toHaveYielded([
1164
+ assertLog([
1165
'ErrorBoundary constructor',
1166
'ErrorBoundary componentWillMount',
1167
'ErrorBoundary render success',
@@ -1173,7 +1177,7 @@ describe('ReactErrorBoundaries', () => {
1177
]);
1178
1179
ReactDOM.unmountComponentAtNode(container);
1176
- expect(Scheduler).toHaveYielded([
1180
+ assertLog([
1181
'ErrorBoundary componentWillUnmount',
1182
'Error message ref is set to null',
1183
]);
@@ -1192,7 +1196,7 @@ describe('ReactErrorBoundaries', () => {
1196
container,
1197
);
1198
expect(container.textContent).toBe('Caught an error: Hello.');
1195
- expect(Scheduler).toHaveYielded([
1199
+ assertLog([
1200
'ErrorBoundary constructor',
1201
'ErrorBoundary componentWillMount',
1202
'ErrorBoundary render success',
@@ -1210,7 +1214,7 @@ describe('ReactErrorBoundaries', () => {
1214
);
1215
1216
ReactDOM.unmountComponentAtNode(container);
1213
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1217
+ assertLog(['ErrorBoundary componentWillUnmount']);
1218
expect(errorMessageRef.current).toEqual(null);
1219
});
1220
@@ -1223,7 +1227,7 @@ describe('ReactErrorBoundaries', () => {
1227
container,
1228
);
1229
expect(container.firstChild.textContent).toBe('Mounted successfully.');
1226
- expect(Scheduler).toHaveYielded([
1230
+ assertLog([
1231
'ErrorBoundary constructor',
1232
'ErrorBoundary componentWillMount',
1233
'ErrorBoundary render success',
@@ -1231,7 +1235,7 @@ describe('ReactErrorBoundaries', () => {
1235
]);
1236
1237
ReactDOM.unmountComponentAtNode(container);
1234
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1238
+ assertLog(['ErrorBoundary componentWillUnmount']);
1239
});
1240
1241
it('catches if child throws in constructor during update', () => {
@@ -1252,7 +1256,7 @@ describe('ReactErrorBoundaries', () => {
1256
container,
1257
);
1258
expect(container.textContent).toBe('Caught an error: Hello.');
1255
- expect(Scheduler).toHaveYielded([
1259
+ assertLog([
1260
'ErrorBoundary componentWillReceiveProps',
1261
'ErrorBoundary componentWillUpdate',
1262
'ErrorBoundary render success',
@@ -1275,7 +1279,7 @@ describe('ReactErrorBoundaries', () => {
1279
]);
1280
1281
ReactDOM.unmountComponentAtNode(container);
1278
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1282
+ assertLog(['ErrorBoundary componentWillUnmount']);
1283
});
1284
1285
it('catches if child throws in componentWillMount during update', () => {
@@ -1297,7 +1301,7 @@ describe('ReactErrorBoundaries', () => {
1301
container,
1302
);
1303
expect(container.textContent).toBe('Caught an error: Hello.');
1300
- expect(Scheduler).toHaveYielded([
1304
+ assertLog([
1305
'ErrorBoundary componentWillReceiveProps',
1306
'ErrorBoundary componentWillUpdate',
1307
'ErrorBoundary render success',
@@ -1321,7 +1325,7 @@ describe('ReactErrorBoundaries', () => {
1325
]);
1326
1327
ReactDOM.unmountComponentAtNode(container);
1324
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1328
+ assertLog(['ErrorBoundary componentWillUnmount']);
1329
});
1330
1331
it('catches if child throws in componentWillReceiveProps during update', () => {
@@ -1343,7 +1347,7 @@ describe('ReactErrorBoundaries', () => {
1347
container,
1348
);
1349
expect(container.textContent).toBe('Caught an error: Hello.');
1346
- expect(Scheduler).toHaveYielded([
1350
+ assertLog([
1351
'ErrorBoundary componentWillReceiveProps',
1352
'ErrorBoundary componentWillUpdate',
1353
'ErrorBoundary render success',
@@ -1363,7 +1367,7 @@ describe('ReactErrorBoundaries', () => {
1367
]);
1368
1369
ReactDOM.unmountComponentAtNode(container);
1366
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1370
+ assertLog(['ErrorBoundary componentWillUnmount']);
1371
});
1372
1373
it('catches if child throws in componentWillUpdate during update', () => {
@@ -1385,7 +1389,7 @@ describe('ReactErrorBoundaries', () => {
1389
container,
1390
);
1391
expect(container.textContent).toBe('Caught an error: Hello.');
1388
- expect(Scheduler).toHaveYielded([
1392
+ assertLog([
1393
'ErrorBoundary componentWillReceiveProps',
1394
'ErrorBoundary componentWillUpdate',
1395
'ErrorBoundary render success',
@@ -1405,7 +1409,7 @@ describe('ReactErrorBoundaries', () => {
1409
]);
1410
1411
ReactDOM.unmountComponentAtNode(container);
1408
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1412
+ assertLog(['ErrorBoundary componentWillUnmount']);
1413
});
1414
1415
it('catches if child throws in render during update', () => {
@@ -1427,7 +1431,7 @@ describe('ReactErrorBoundaries', () => {
1431
container,
1432
);
1433
expect(container.textContent).toBe('Caught an error: Hello.');
1430
- expect(Scheduler).toHaveYielded([
1434
+ assertLog([
1435
'ErrorBoundary componentWillReceiveProps',
1436
'ErrorBoundary componentWillUpdate',
1437
'ErrorBoundary render success',
@@ -1451,7 +1455,7 @@ describe('ReactErrorBoundaries', () => {
1455
]);
1456
1457
ReactDOM.unmountComponentAtNode(container);
1454
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1458
+ assertLog(['ErrorBoundary componentWillUnmount']);
1459
});
1460
1461
it('keeps refs up-to-date during updates', () => {
@@ -1472,7 +1476,7 @@ describe('ReactErrorBoundaries', () => {
1476
</ErrorBoundary>,
1477
container,
1478
);
1475
- expect(Scheduler).toHaveYielded([
1479
+ assertLog([
1480
'ErrorBoundary constructor',
1481
'ErrorBoundary componentWillMount',
1482
'ErrorBoundary render success',
@@ -1489,7 +1493,7 @@ describe('ReactErrorBoundaries', () => {
1493
container,
1494
);
1495
expect(container.textContent).toBe('Caught an error: Hello.');
1492
- expect(Scheduler).toHaveYielded([
1496
+ assertLog([
1497
'ErrorBoundary componentWillReceiveProps',
1498
'ErrorBoundary componentWillUpdate',
1499
'ErrorBoundary render success',
@@ -1509,7 +1513,7 @@ describe('ReactErrorBoundaries', () => {
1513
]);
1514
1515
ReactDOM.unmountComponentAtNode(container);
1512
- expect(Scheduler).toHaveYielded([
1516
+ assertLog([
1517
'ErrorBoundary componentWillUnmount',
1518
'Error message ref is set to null',
1519
]);
@@ -1534,7 +1538,7 @@ describe('ReactErrorBoundaries', () => {
1538
container,
1539
);
1540
expect(container.textContent).toBe('Caught an error: Hello.');
1537
- expect(Scheduler).toHaveYielded([
1541
+ assertLog([
1542
'ErrorBoundary componentWillReceiveProps',
1543
'ErrorBoundary componentWillUpdate',
1544
'ErrorBoundary render success',
@@ -1567,7 +1571,7 @@ describe('ReactErrorBoundaries', () => {
1571
]);
1572
1573
ReactDOM.unmountComponentAtNode(container);
1570
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1574
+ assertLog(['ErrorBoundary componentWillUnmount']);
1575
});
1576
1577
it('recovers from nested componentWillUnmount errors on update', () => {
@@ -1592,7 +1596,7 @@ describe('ReactErrorBoundaries', () => {
1596
container,
1597
);
1598
expect(container.textContent).toBe('Caught an error: Hello.');
1595
- expect(Scheduler).toHaveYielded([
1599
+ assertLog([
1600
'ErrorBoundary componentWillReceiveProps',
1601
'ErrorBoundary componentWillUpdate',
1602
'ErrorBoundary render success',
@@ -1626,7 +1630,7 @@ describe('ReactErrorBoundaries', () => {
1630
]);
1631
1632
ReactDOM.unmountComponentAtNode(container);
1629
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1633
+ assertLog(['ErrorBoundary componentWillUnmount']);
1634
});
1635
1636
it('picks the right boundary when handling unmounting errors', () => {
@@ -1664,7 +1668,7 @@ describe('ReactErrorBoundaries', () => {
1668
container,
1669
);
1670
expect(container.textContent).toBe('Caught an inner error: Hello.');
1667
- expect(Scheduler).toHaveYielded([
1671
+ assertLog([
1672
// Update outer boundary
1673
'OuterErrorBoundary componentWillReceiveProps',
1674
'OuterErrorBoundary componentWillUpdate',
@@ -1689,7 +1693,7 @@ describe('ReactErrorBoundaries', () => {
1693
]);
1694
1695
ReactDOM.unmountComponentAtNode(container);
1692
- expect(Scheduler).toHaveYielded([
1696
+ assertLog([
1697
'OuterErrorBoundary componentWillUnmount',
1698
'InnerErrorBoundary componentWillUnmount',
1699
]);
@@ -1722,7 +1726,7 @@ describe('ReactErrorBoundaries', () => {
1726
container,
1727
);
1728
expect(container.textContent).not.toContain('Caught an error');
1725
- expect(Scheduler).toHaveYielded([
1729
+ assertLog([
1730
'ErrorBoundary componentWillReceiveProps',
1731
'ErrorBoundary componentWillUpdate',
1732
'ErrorBoundary render success',
@@ -1736,7 +1740,7 @@ describe('ReactErrorBoundaries', () => {
1740
]);
1741
1742
ReactDOM.unmountComponentAtNode(container);
1739
- expect(Scheduler).toHaveYielded([
1743
+ assertLog([
1744
'ErrorBoundary componentWillUnmount',
1745
'Normal componentWillUnmount',
1746
]);
@@ -1782,7 +1786,7 @@ describe('ReactErrorBoundaries', () => {
1786
1787
Scheduler.unstable_clearYields();
1788
ReactDOM.unmountComponentAtNode(container);
1785
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1789
+ assertLog(['ErrorBoundary componentWillUnmount']);
1790
});
1791
1792
it("doesn't get into inconsistent state during additions", () => {
@@ -1800,7 +1804,7 @@ describe('ReactErrorBoundaries', () => {
1804
1805
Scheduler.unstable_clearYields();
1806
ReactDOM.unmountComponentAtNode(container);
1803
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1807
+ assertLog(['ErrorBoundary componentWillUnmount']);
1808
});
1809
1810
it("doesn't get into inconsistent state during reorders", () => {
@@ -1848,7 +1852,7 @@ describe('ReactErrorBoundaries', () => {
1852
1853
Scheduler.unstable_clearYields();
1854
ReactDOM.unmountComponentAtNode(container);
1851
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1855
+ assertLog(['ErrorBoundary componentWillUnmount']);
1856
});
1857
1858
it('catches errors originating downstream', () => {
@@ -1880,7 +1884,7 @@ describe('ReactErrorBoundaries', () => {
1884
statefulInst.forceUpdate();
1885
}).not.toThrow();
1886
1883
- expect(Scheduler).toHaveYielded([
1887
+ assertLog([
1888
'Stateful render [!]',
1889
'ErrorBoundary static getDerivedStateFromError',
1890
'ErrorBoundary componentWillUpdate',
@@ -1889,7 +1893,7 @@ describe('ReactErrorBoundaries', () => {
1893
]);
1894
1895
ReactDOM.unmountComponentAtNode(container);
1892
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1896
+ assertLog(['ErrorBoundary componentWillUnmount']);
1897
});
1898
1899
it('catches errors in componentDidMount', () => {
@@ -1904,7 +1908,7 @@ describe('ReactErrorBoundaries', () => {
1908
</ErrorBoundary>,
1909
container,
1910
);
1907
- expect(Scheduler).toHaveYielded([
1911
+ assertLog([
1912
'ErrorBoundary constructor',
1913
'ErrorBoundary componentWillMount',
1914
'ErrorBoundary render success',
@@ -1947,7 +1951,7 @@ describe('ReactErrorBoundaries', () => {
1951
]);
1952
1953
ReactDOM.unmountComponentAtNode(container);
1950
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1954
+ assertLog(['ErrorBoundary componentWillUnmount']);
1955
});
1956
1957
it('catches errors in componentDidUpdate', () => {
@@ -1966,7 +1970,7 @@ describe('ReactErrorBoundaries', () => {
1970
</ErrorBoundary>,
1971
container,
1972
);
1969
- expect(Scheduler).toHaveYielded([
1973
+ assertLog([
1974
'ErrorBoundary componentWillReceiveProps',
1975
'ErrorBoundary componentWillUpdate',
1976
'ErrorBoundary render success',
@@ -1985,7 +1989,7 @@ describe('ReactErrorBoundaries', () => {
1989
]);
1990
1991
ReactDOM.unmountComponentAtNode(container);
1988
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
1992
+ assertLog(['ErrorBoundary componentWillUnmount']);
1993
});
1994
1995
it('catches errors in useEffect', () => {
@@ -1997,7 +2001,7 @@ describe('ReactErrorBoundaries', () => {
2001
</ErrorBoundary>,
2002
container,
2003
);
2000
- expect(Scheduler).toHaveYielded([
2004
+ assertLog([
2005
'ErrorBoundary constructor',
2006
'ErrorBoundary componentWillMount',
2007
'ErrorBoundary render success',
@@ -2010,7 +2014,7 @@ describe('ReactErrorBoundaries', () => {
2014
});
2015
2016
// verify flushed passive effects and handle the error
2013
- expect(Scheduler).toHaveYielded([
2017
+ assertLog([
2018
'BrokenUseEffect useEffect [!]',
2019
// Handle the error
2020
'ErrorBoundary static getDerivedStateFromError',
@@ -2030,7 +2034,7 @@ describe('ReactErrorBoundaries', () => {
2034
</ErrorBoundary>,
2035
container,
2036
);
2033
- expect(Scheduler).toHaveYielded([
2037
+ assertLog([
2038
'ErrorBoundary constructor',
2039
'ErrorBoundary componentWillMount',
2040
'ErrorBoundary render success',
@@ -2062,7 +2066,7 @@ describe('ReactErrorBoundaries', () => {
2066
container,
2067
);
2068
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
2065
- expect(Scheduler).toHaveYielded([
2069
+ assertLog([
2070
'ErrorBoundary constructor',
2071
'ErrorBoundary componentWillMount',
2072
'ErrorBoundary render success',
@@ -2082,7 +2086,7 @@ describe('ReactErrorBoundaries', () => {
2086
]);
2087
2088
ReactDOM.unmountComponentAtNode(container);
2085
- expect(Scheduler).toHaveYielded(['ErrorBoundary componentWillUnmount']);
2089
+ assertLog(['ErrorBoundary componentWillUnmount']);
2090
});
2091
2092
it('calls static getDerivedStateFromError for each error that is captured', () => {
@@ -2132,7 +2136,7 @@ describe('ReactErrorBoundaries', () => {
2136
expect(container.firstChild.textContent).toBe(
2137
'Caught an unmounting error: E2.' + 'Caught an updating error: E4.',
2138
);
2135
- expect(Scheduler).toHaveYielded([
2139
+ assertLog([
2140
// Begin update phase
2141
'OuterErrorBoundary componentWillReceiveProps',
2142
'OuterErrorBoundary componentWillUpdate',
@@ -2178,7 +2182,7 @@ describe('ReactErrorBoundaries', () => {
2182
]);
2183
2184
ReactDOM.unmountComponentAtNode(container);
2181
- expect(Scheduler).toHaveYielded([
2185
+ assertLog([
2186
'OuterErrorBoundary componentWillUnmount',
2187
'InnerUnmountBoundary componentWillUnmount',
2188
'InnerUpdateBoundary componentWillUnmount',
@@ -2230,7 +2234,7 @@ describe('ReactErrorBoundaries', () => {
2234
),
2235
).toThrow('Hello');
2236
expect(container.innerHTML).toBe('');
2233
- expect(Scheduler).toHaveYielded([
2237
+ assertLog([
2238
'NoopErrorBoundary constructor',
2239
'NoopErrorBoundary componentWillMount',
2240
'NoopErrorBoundary render',
@@ -2538,7 +2542,7 @@ describe('ReactErrorBoundaries', () => {
2542
2543
expect(container.firstChild.textContent).toBe('sibling');
2544
expect(container.lastChild.textContent).toBe('broken');
2541
- expect(Scheduler).toHaveYielded([
2545
+ assertLog([
2546
'OuterBoundary render success',
2547
'Component render sibling',
2548
'InnerBoundary render success',
@@ -2555,7 +2559,7 @@ describe('ReactErrorBoundaries', () => {
2559
// React should skip over the unmounting boundary and find the nearest still-mounted boundary.
2560
expect(container.firstChild.textContent).toBe('OuterFallback');
2561
expect(container.lastChild.textContent).toBe('OuterFallback');
2558
- expect(Scheduler).toHaveYielded([
2562
+ assertLog([
2563
'OuterBoundary render success',
2564
'Component render sibling',
2565
'BrokenComponentWillUnmount componentWillUnmount',
@@ -2622,7 +2626,7 @@ describe('ReactErrorBoundaries', () => {
2626
2627
expect(container.firstChild.textContent).toBe('sibling');
2628
expect(container.lastChild.textContent).toBe('ref');
2625
- expect(Scheduler).toHaveYielded([
2629
+ assertLog([
2630
'OuterBoundary render success',
2631
'Component render sibling',
2632
'InnerBoundary render success',
@@ -2640,7 +2644,7 @@ describe('ReactErrorBoundaries', () => {
2644
// React should skip over the unmounting boundary and find the nearest still-mounted boundary.
2645
expect(container.firstChild.textContent).toBe('OuterFallback');
2646
expect(container.lastChild.textContent).toBe('OuterFallback');
2643
- expect(Scheduler).toHaveYielded([
2647
+ assertLog([
2648
'OuterBoundary render success',
2649
'Component render sibling',
2650
'LocalBrokenCallbackRef ref false',
packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js
+8
-4
@@ -16,6 +16,7 @@ let ReactDOMClient;
16
let ReactDOMServer;
17
let ReactDOMServerBrowser;
18
let Scheduler;
19
+let waitForAll;
20
21
// These tests rely both on ReactDOMServer and ReactDOM.
22
// If a test only needs ReactDOMServer, put it in ReactServerRendering-test instead.
@@ -28,6 +29,9 @@ describe('ReactDOMServerHydration', () => {
29
ReactDOMServer = require('react-dom/server');
30
ReactDOMServerBrowser = require('react-dom/server.browser');
31
Scheduler = require('scheduler');
32
+
33
+ const InternalTestUtils = require('internal-test-utils');
34
+ waitForAll = InternalTestUtils.waitForAll;
35
});
36
37
it('should have the correct mounting behavior (new hydrate API)', () => {
@@ -604,7 +608,7 @@ describe('ReactDOMServerHydration', () => {
608
expect(customElement.obj).toBe(undefined);
609
});
610
607
- it('refers users to apis that support Suspense when something suspends', () => {
611
+ it('refers users to apis that support Suspense when something suspends', async () => {
612
const theInfinitePromise = new Promise(() => {});
613
function InfiniteSuspend() {
614
throw theInfinitePromise;
@@ -631,7 +635,7 @@ describe('ReactDOMServerHydration', () => {
635
},
636
});
637
634
- expect(Scheduler).toFlushAndYield([]);
638
+ await waitForAll([]);
639
expect(errors.length).toBe(1);
640
if (__DEV__) {
641
expect(errors[0]).toBe(
@@ -649,7 +653,7 @@ describe('ReactDOMServerHydration', () => {
653
}
654
});
655
652
- it('refers users to apis that support Suspense when something suspends (browser)', () => {
656
+ it('refers users to apis that support Suspense when something suspends (browser)', async () => {
657
const theInfinitePromise = new Promise(() => {});
658
function InfiniteSuspend() {
659
throw theInfinitePromise;
@@ -676,7 +680,7 @@ describe('ReactDOMServerHydration', () => {
680
},
681
});
682
679
- expect(Scheduler).toFlushAndYield([]);
683
+ await waitForAll([]);
684
expect(errors.length).toBe(1);
685
if (__DEV__) {
686
expect(errors[0]).toBe(
packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
+16
-11
@@ -14,6 +14,7 @@ let ReactTestUtils;
14
let Scheduler;
15
let act;
16
let container;
17
+let assertLog;
18
19
jest.useRealTimers();
20
@@ -122,6 +123,10 @@ function runActTests(label, render, unmount, rerender) {
123
ReactTestUtils = require('react-dom/test-utils');
124
Scheduler = require('scheduler');
125
act = ReactTestUtils.act;
126
+
127
+ const InternalTestUtils = require('internal-test-utils');
128
+ assertLog = InternalTestUtils.assertLog;
129
+
130
container = document.createElement('div');
131
document.body.appendChild(container);
132
});
@@ -145,7 +150,7 @@ function runActTests(label, render, unmount, rerender) {
150
render(<App />, container);
151
});
152
148
- expect(Scheduler).toHaveYielded([100]);
153
+ assertLog([100]);
154
});
155
156
// @gate __DEV__
@@ -165,7 +170,7 @@ function runActTests(label, render, unmount, rerender) {
170
act(() => {
171
render(<App />, container);
172
});
168
- expect(Scheduler).toHaveYielded([0]);
173
+ assertLog([0]);
174
const button = container.querySelector('#button');
175
function click() {
176
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
@@ -177,11 +182,11 @@ function runActTests(label, render, unmount, rerender) {
182
click();
183
});
184
// it consolidates the 3 updates, then fires the effect
180
- expect(Scheduler).toHaveYielded([3]);
185
+ assertLog([3]);
186
await act(async () => click());
182
- expect(Scheduler).toHaveYielded([4]);
187
+ assertLog([4]);
188
await act(async () => click());
184
- expect(Scheduler).toHaveYielded([5]);
189
+ assertLog([5]);
190
expect(button.innerHTML).toBe('5');
191
});
192
@@ -219,10 +224,10 @@ function runActTests(label, render, unmount, rerender) {
224
});
225
// the effect wouldn't have yielded yet because
226
// we're still inside an act() scope
222
- expect(Scheduler).toHaveYielded([]);
227
+ assertLog([]);
228
});
229
// but after exiting the last one, effects get flushed
225
- expect(Scheduler).toHaveYielded([0]);
230
+ assertLog([0]);
231
});
232
233
// @gate __DEV__
@@ -551,7 +556,7 @@ function runActTests(label, render, unmount, rerender) {
556
});
557
// exiting act() drains effects and microtasks
558
554
- expect(Scheduler).toHaveYielded([0, 1]);
559
+ assertLog([0, 1]);
560
expect(container.innerHTML).toBe('1');
561
});
562
@@ -576,7 +581,7 @@ function runActTests(label, render, unmount, rerender) {
581
render(<App />, container);
582
});
583
// all 5 ticks present and accounted for
579
- expect(Scheduler).toHaveYielded([0, 1, 2, 3, 4]);
584
+ assertLog([0, 1, 2, 3, 4]);
585
expect(container.innerHTML).toBe('5');
586
});
587
});
@@ -657,7 +662,7 @@ function runActTests(label, render, unmount, rerender) {
662
act(() => {
663
render(<App />, container);
664
});
660
- expect(Scheduler).toHaveYielded(['oh yes']);
665
+ assertLog(['oh yes']);
666
}
667
});
668
@@ -688,7 +693,7 @@ function runActTests(label, render, unmount, rerender) {
693
await act(async () => {
694
render(<App />, container);
695
});
691
- expect(Scheduler).toHaveYielded(['oh yes']);
696
+ assertLog(['oh yes']);
697
}
698
});
699
});
packages/react-dom/src/__tests__/ReactUpdates-test.js
+14
-6
@@ -15,6 +15,9 @@ let ReactDOMClient;
15
let ReactTestUtils;
16
let act;
17
let Scheduler;
18
+let waitForAll;
19
+let waitFor;
20
+let assertLog;
21
22
describe('ReactUpdates', () => {
23
beforeEach(() => {
@@ -25,6 +28,11 @@ describe('ReactUpdates', () => {
28
ReactTestUtils = require('react-dom/test-utils');
29
act = require('jest-react').act;
30
Scheduler = require('scheduler');
31
+
32
+ const InternalTestUtils = require('internal-test-utils');
33
+ waitForAll = InternalTestUtils.waitForAll;
34
+ waitFor = InternalTestUtils.waitFor;
35
+ assertLog = InternalTestUtils.assertLog;
36
});
37
38
// Note: This is based on a similar component we use in www. We can delete
@@ -1319,7 +1327,7 @@ describe('ReactUpdates', () => {
1327
});
1328
1329
// @gate www
1322
- it('delays sync updates inside hidden subtrees in Concurrent Mode', () => {
1330
+ it('delays sync updates inside hidden subtrees in Concurrent Mode', async () => {
1331
const container = document.createElement('div');
1332
1333
function Baz() {
@@ -1352,14 +1360,14 @@ describe('ReactUpdates', () => {
1360
1361
const root = ReactDOMClient.createRoot(container);
1362
let hiddenDiv;
1355
- act(() => {
1363
+ await act(async () => {
1364
root.render(<Foo />);
1357
- expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Baz', 'Foo#effect']);
1365
+ await waitFor(['Foo', 'Baz', 'Foo#effect']);
1366
hiddenDiv = container.firstChild.firstChild;
1367
expect(hiddenDiv.hidden).toBe(true);
1368
expect(hiddenDiv.innerHTML).toBe('');
1369
// Run offscreen update
1362
- expect(Scheduler).toFlushAndYield(['Bar']);
1370
+ await waitForAll(['Bar']);
1371
expect(hiddenDiv.hidden).toBe(true);
1372
expect(hiddenDiv.innerHTML).toBe('<p>bar 0</p>');
1373
});
@@ -1371,7 +1379,7 @@ describe('ReactUpdates', () => {
1379
expect(hiddenDiv.innerHTML).toBe('<p>bar 0</p>');
1380
1381
// Run offscreen update
1374
- expect(Scheduler).toFlushAndYield(['Bar']);
1382
+ await waitForAll(['Bar']);
1383
expect(hiddenDiv.innerHTML).toBe('<p>bar 1</p>');
1384
});
1385
@@ -1699,7 +1707,7 @@ describe('ReactUpdates', () => {
1707
ReactDOM.render(<Terminating />, container);
1708
});
1709
1702
- expect(Scheduler).toHaveYielded(['Done']);
1710
+ assertLog(['Done']);
1711
expect(container.textContent).toBe('1000');
1712
});
1713
}
packages/react-dom/src/__tests__/ReactWrongReturnPointer-test.js
+7
-7
@@ -16,6 +16,7 @@ let SuspenseList;
16
let getCacheForType;
17
let caches;
18
let seededCache;
19
+let assertLog;
20
21
beforeEach(() => {
22
React = require('react');
@@ -23,6 +24,9 @@ beforeEach(() => {
24
Scheduler = require('scheduler');
25
act = require('jest-react').act;
26
27
+ const InternalTestUtils = require('internal-test-utils');
28
+ assertLog = InternalTestUtils.assertLog;
29
+
30
Suspense = React.Suspense;
31
if (gate(flags => flags.enableSuspenseList)) {
32
SuspenseList = React.SuspenseList;
@@ -188,19 +192,15 @@ test('regression (#20932): return pointer is correct before entering deleted tre
192
await act(async () => {
193
root.render(<App />);
194
});
191
- expect(Scheduler).toHaveYielded([
192
- 'Suspend! [0]',
193
- 'Loading Async...',
194
- 'Loading Tail...',
195
- ]);
195
+ assertLog(['Suspend! [0]', 'Loading Async...', 'Loading Tail...']);
196
await act(async () => {
197
resolveText(0);
198
});
199
- expect(Scheduler).toHaveYielded([0, 'Tail']);
199
+ assertLog([0, 'Tail']);
200
await act(async () => {
201
setAsyncText(x => x + 1);
202
});
203
- expect(Scheduler).toHaveYielded([
203
+ assertLog([
204
'Suspend! [1]',
205
'Loading Async...',
206
'Suspend! [1]',
packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js
+14
-3
@@ -18,6 +18,8 @@ let ReactDOMClient;
18
let ReactDOMServer;
19
let Scheduler;
20
let act;
21
+let waitForAll;
22
+let waitFor;
23
24
function dispatchEvent(element, type) {
25
const event = document.createEvent('Event');
@@ -79,6 +81,11 @@ describe('DOMPluginEventSystem', () => {
81
Scheduler = require('scheduler');
82
ReactDOMServer = require('react-dom/server');
83
act = require('jest-react').act;
84
+
85
+ const InternalTestUtils = require('internal-test-utils');
86
+ waitForAll = InternalTestUtils.waitForAll;
87
+ waitFor = InternalTestUtils.waitFor;
88
+
89
container = document.createElement('div');
90
document.body.appendChild(container);
91
startNativeEventListenerClearDown();
@@ -1273,6 +1280,10 @@ describe('DOMPluginEventSystem', () => {
1280
Scheduler = require('scheduler');
1281
ReactDOMServer = require('react-dom/server');
1282
act = require('jest-react').act;
1283
+
1284
+ const InternalTestUtils = require('internal-test-utils');
1285
+ waitForAll = InternalTestUtils.waitForAll;
1286
+ waitFor = InternalTestUtils.waitFor;
1287
});
1288
1289
// @gate www
@@ -1955,7 +1966,7 @@ describe('DOMPluginEventSystem', () => {
1966
const root = ReactDOMClient.createRoot(container);
1967
root.render(<Test counter={0} />);
1968
1958
- expect(Scheduler).toFlushAndYield(['Test']);
1969
+ await waitForAll(['Test']);
1970
1971
// Click the button
1972
dispatchClickEvent(ref.current);
@@ -1969,7 +1980,7 @@ describe('DOMPluginEventSystem', () => {
1980
root.render(<Test counter={1} />);
1981
});
1982
// Yield before committing
1972
- expect(Scheduler).toFlushAndYieldThrough(['Test']);
1983
+ await waitFor(['Test']);
1984
1985
// Click the button again
1986
dispatchClickEvent(ref.current);
@@ -1979,7 +1990,7 @@ describe('DOMPluginEventSystem', () => {
1990
log.length = 0;
1991
1992
// Commit
1982
- expect(Scheduler).toFlushAndYield([]);
1993
+ await waitForAll([]);
1994
dispatchClickEvent(ref.current);
1995
expect(log).toEqual([{counter: 1}]);
1996
});
packages/react-dom/src/events/plugins/__tests__/ChangeEventPlugin-test.js
+28
-21
@@ -15,6 +15,8 @@ let ReactDOMClient;
15
let ReactFeatureFlags;
16
let Scheduler;
17
let act;
18
+let waitForAll;
19
+let assertLog;
20
21
const setUntrackedChecked = Object.getOwnPropertyDescriptor(
22
HTMLInputElement.prototype,
@@ -60,6 +62,11 @@ describe('ChangeEventPlugin', () => {
62
ReactDOMClient = require('react-dom/client');
63
act = require('jest-react').act;
64
Scheduler = require('scheduler');
65
+
66
+ const InternalTestUtils = require('internal-test-utils');
67
+ waitForAll = InternalTestUtils.waitForAll;
68
+ assertLog = InternalTestUtils.assertLog;
69
+
70
container = document.createElement('div');
71
document.body.appendChild(container);
72
});
@@ -497,7 +504,7 @@ describe('ChangeEventPlugin', () => {
504
});
505
506
describe('concurrent mode', () => {
500
- it('text input', () => {
507
+ it('text input', async () => {
508
const root = ReactDOMClient.createRoot(container);
509
let input;
510
@@ -522,10 +529,10 @@ describe('ChangeEventPlugin', () => {
529
// Initial mount. Test that this is async.
530
root.render(<ControlledInput />);
531
// Should not have flushed yet.
525
- expect(Scheduler).toHaveYielded([]);
532
+ assertLog([]);
533
expect(input).toBe(undefined);
534
// Flush callbacks.
528
- expect(Scheduler).toFlushAndYield(['render: initial']);
535
+ await waitForAll(['render: initial']);
536
expect(input.value).toBe('initial');
537
538
// Trigger a change event.
@@ -534,12 +541,12 @@ describe('ChangeEventPlugin', () => {
541
new Event('input', {bubbles: true, cancelable: true}),
542
);
543
// Change should synchronously flush
537
- expect(Scheduler).toHaveYielded(['render: changed']);
544
+ assertLog(['render: changed']);
545
// Value should be the controlled value, not the original one
546
expect(input.value).toBe('changed [!]');
547
});
548
542
- it('checkbox input', () => {
549
+ it('checkbox input', async () => {
550
const root = ReactDOMClient.createRoot(container);
551
let input;
552
@@ -567,10 +574,10 @@ describe('ChangeEventPlugin', () => {
574
// Initial mount. Test that this is async.
575
root.render(<ControlledInput reverse={false} />);
576
// Should not have flushed yet.
570
- expect(Scheduler).toHaveYielded([]);
577
+ assertLog([]);
578
expect(input).toBe(undefined);
579
// Flush callbacks.
573
- expect(Scheduler).toFlushAndYield(['render: false']);
580
+ await waitForAll(['render: false']);
581
expect(input.checked).toBe(false);
582
583
// Trigger a change event.
@@ -578,23 +585,23 @@ describe('ChangeEventPlugin', () => {
585
new MouseEvent('click', {bubbles: true, cancelable: true}),
586
);
587
// Change should synchronously flush
581
- expect(Scheduler).toHaveYielded(['render: true']);
588
+ assertLog(['render: true']);
589
expect(input.checked).toBe(true);
590
591
// Now let's make sure we're using the controlled value.
592
root.render(<ControlledInput reverse={true} />);
586
- expect(Scheduler).toFlushAndYield(['render: true']);
593
+ await waitForAll(['render: true']);
594
595
// Trigger another change event.
596
input.dispatchEvent(
597
new MouseEvent('click', {bubbles: true, cancelable: true}),
598
);
599
// Change should synchronously flush
593
- expect(Scheduler).toHaveYielded(['render: true']);
600
+ assertLog(['render: true']);
601
expect(input.checked).toBe(false);
602
});
603
597
- it('textarea', () => {
604
+ it('textarea', async () => {
605
const root = ReactDOMClient.createRoot(container);
606
let textarea;
607
@@ -619,10 +626,10 @@ describe('ChangeEventPlugin', () => {
626
// Initial mount. Test that this is async.
627
root.render(<ControlledTextarea />);
628
// Should not have flushed yet.
622
- expect(Scheduler).toHaveYielded([]);
629
+ assertLog([]);
630
expect(textarea).toBe(undefined);
631
// Flush callbacks.
625
- expect(Scheduler).toFlushAndYield(['render: initial']);
632
+ await waitForAll(['render: initial']);
633
expect(textarea.value).toBe('initial');
634
635
// Trigger a change event.
@@ -631,12 +638,12 @@ describe('ChangeEventPlugin', () => {
638
new Event('input', {bubbles: true, cancelable: true}),
639
);
640
// Change should synchronously flush
634
- expect(Scheduler).toHaveYielded(['render: changed']);
641
+ assertLog(['render: changed']);
642
// Value should be the controlled value, not the original one
643
expect(textarea.value).toBe('changed [!]');
644
});
645
639
- it('parent of input', () => {
646
+ it('parent of input', async () => {
647
const root = ReactDOMClient.createRoot(container);
648
let input;
649
@@ -665,10 +672,10 @@ describe('ChangeEventPlugin', () => {
672
// Initial mount. Test that this is async.
673
root.render(<ControlledInput />);
674
// Should not have flushed yet.
668
- expect(Scheduler).toHaveYielded([]);
675
+ assertLog([]);
676
expect(input).toBe(undefined);
677
// Flush callbacks.
671
- expect(Scheduler).toFlushAndYield(['render: initial']);
678
+ await waitForAll(['render: initial']);
679
expect(input.value).toBe('initial');
680
681
// Trigger a change event.
@@ -677,7 +684,7 @@ describe('ChangeEventPlugin', () => {
684
new Event('input', {bubbles: true, cancelable: true}),
685
);
686
// Change should synchronously flush
680
- expect(Scheduler).toHaveYielded(['render: changed']);
687
+ assertLog(['render: changed']);
688
// Value should be the controlled value, not the original one
689
expect(input.value).toBe('changed [!]');
690
});
@@ -711,10 +718,10 @@ describe('ChangeEventPlugin', () => {
718
// Initial mount. Test that this is async.
719
root.render(<ControlledInput />);
720
// Should not have flushed yet.
714
- expect(Scheduler).toHaveYielded([]);
721
+ assertLog([]);
722
expect(input).toBe(undefined);
723
// Flush callbacks.
717
- expect(Scheduler).toFlushAndYield(['render: initial']);
724
+ await waitForAll(['render: initial']);
725
expect(input.value).toBe('initial');
726
727
// Trigger a click event
@@ -724,7 +731,7 @@ describe('ChangeEventPlugin', () => {
731
732
// Flush microtask queue.
733
await null;
727
- expect(Scheduler).toHaveYielded(['render: ']);
734
+ assertLog(['render: ']);
735
expect(input.value).toBe('');
736
});
737