@samitouri / QOS-React-2 / commits / e64a8f4035

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