diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemanticsDOM-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemanticsDOM-test.js index 67f0b9336ca2c..9f46b11d10aee 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemanticsDOM-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemanticsDOM-test.js @@ -15,6 +15,8 @@ let ReactDOMClient; let Scheduler; let act; let container; +let waitForAll; +let assertLog; describe('ReactSuspenseEffectsSemanticsDOM', () => { beforeEach(() => { @@ -26,6 +28,10 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => { Scheduler = require('scheduler'); act = require('jest-react').act; + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + assertLog = InternalTestUtils.assertLog; + container = document.createElement('div'); document.body.appendChild(container); }); @@ -139,23 +145,23 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => { act(() => { root.render(); }); - expect(Scheduler).toHaveYielded(['Loading...']); + assertLog(['Loading...']); await LazyChildA; - expect(Scheduler).toFlushAndYield(['A', 'Ref mount: A']); + await waitForAll(['A', 'Ref mount: A']); expect(container.innerHTML).toBe('A'); // Swap the position of A and B ReactDOM.flushSync(() => { root.render(); }); - expect(Scheduler).toHaveYielded(['Loading...', 'Ref unmount: A']); + assertLog(['Loading...', 'Ref unmount: A']); expect(container.innerHTML).toBe( 'ALoading...', ); await LazyChildB; - expect(Scheduler).toFlushAndYield(['B', 'Ref mount: B']); + await waitForAll(['B', 'Ref mount: B']); expect(container.innerHTML).toBe('B'); }); @@ -199,21 +205,21 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => { act(() => { root.render(); }); - expect(Scheduler).toHaveYielded(['Loading...']); + assertLog(['Loading...']); await LazyChildA; - expect(Scheduler).toFlushAndYield(['A', 'Did mount: A']); + await waitForAll(['A', 'Did mount: A']); expect(container.innerHTML).toBe('A'); // Swap the position of A and B ReactDOM.flushSync(() => { root.render(); }); - expect(Scheduler).toHaveYielded(['Loading...', 'Will unmount: A']); + assertLog(['Loading...', 'Will unmount: A']); expect(container.innerHTML).toBe('Loading...'); await LazyChildB; - expect(Scheduler).toFlushAndYield(['B', 'Did mount: B']); + await waitForAll(['B', 'Did mount: B']); expect(container.innerHTML).toBe('B'); }); @@ -251,24 +257,24 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => { act(() => { root.render(); }); - expect(Scheduler).toHaveYielded(['Loading...']); + assertLog(['Loading...']); await LazyChildA; - expect(Scheduler).toFlushAndYield(['A', 'Did mount: A']); + await waitForAll(['A', 'Did mount: A']); expect(container.innerHTML).toBe('A'); // Swap the position of A and B ReactDOM.flushSync(() => { root.render(); }); - expect(Scheduler).toHaveYielded(['Loading...', 'Will unmount: A']); + assertLog(['Loading...', 'Will unmount: A']); expect(container.innerHTML).toBe('Loading...'); // Destroy the whole tree, including the hidden A ReactDOM.flushSync(() => { root.render(

Hello

); }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(container.innerHTML).toBe('

Hello

'); }); @@ -318,17 +324,17 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => { act(() => { root.render(); }); - expect(Scheduler).toHaveYielded(['Loading...']); + assertLog(['Loading...']); await LazyChildA; - expect(Scheduler).toFlushAndYield(['A', 'Ref mount: A']); + await waitForAll(['A', 'Ref mount: A']); expect(container.innerHTML).toBe('A'); // Swap the position of A and B ReactDOM.flushSync(() => { root.render(); }); - expect(Scheduler).toHaveYielded(['Loading...', 'Ref unmount: A']); + assertLog(['Loading...', 'Ref unmount: A']); expect(container.innerHTML).toBe( 'ALoading...', ); @@ -337,7 +343,7 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => { ReactDOM.flushSync(() => { root.render(

Hello

); }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(container.innerHTML).toBe('

Hello

'); }); @@ -381,24 +387,24 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => { act(() => { root.render(); }); - expect(Scheduler).toHaveYielded(['Loading...']); + assertLog(['Loading...']); await LazyChildA; - expect(Scheduler).toFlushAndYield(['A', 'Did mount: A']); + await waitForAll(['A', 'Did mount: A']); expect(container.innerHTML).toBe('A'); // Swap the position of A and B ReactDOM.flushSync(() => { root.render(); }); - expect(Scheduler).toHaveYielded(['Loading...', 'Will unmount: A']); + assertLog(['Loading...', 'Will unmount: A']); expect(container.innerHTML).toBe('Loading...'); // Destroy the whole tree, including the hidden A ReactDOM.flushSync(() => { root.render(

Hello

); }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(container.innerHTML).toBe('

Hello

'); }); @@ -432,12 +438,12 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => { // Initial render ReactDOM.render(, container); - expect(Scheduler).toHaveYielded(['Child', 'Mount']); + assertLog(['Child', 'Mount']); // Update that suspends, causing the existing tree to switches it to // a fallback. ReactDOM.render(, container); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Child', 'Loading...', @@ -448,6 +454,6 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => { // Delete the tree and unmount the effect ReactDOM.render(null, container); - expect(Scheduler).toHaveYielded(['Unmount']); + assertLog(['Unmount']); }); }); diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseFallback-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseFallback-test.js index 41c27c4c922e2..5421862ddbf8e 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseFallback-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseFallback-test.js @@ -13,6 +13,7 @@ let Suspense; let getCacheForType; let caches; let seededCache; +let waitForAll; describe('ReactSuspenseFallback', () => { beforeEach(() => { @@ -25,6 +26,9 @@ describe('ReactSuspenseFallback', () => { getCacheForType = React.unstable_getCacheForType; caches = []; seededCache = null; + + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; }); function createTextCache() { @@ -128,26 +132,26 @@ describe('ReactSuspenseFallback', () => { } // @gate enableLegacyCache - it('suspends and shows fallback', () => { + it('suspends and shows fallback', async () => { ReactNoop.render( }> , ); - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']); + await waitForAll(['Suspend! [A]', 'Loading...']); expect(ReactNoop).toMatchRenderedOutput(); }); // @gate enableLegacyCache - it('suspends and shows null fallback', () => { + it('suspends and shows null fallback', async () => { ReactNoop.render( , ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend! [A]', // null ]); @@ -155,14 +159,14 @@ describe('ReactSuspenseFallback', () => { }); // @gate enableLegacyCache - it('suspends and shows undefined fallback', () => { + it('suspends and shows undefined fallback', async () => { ReactNoop.render( , ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend! [A]', // null ]); @@ -170,7 +174,7 @@ describe('ReactSuspenseFallback', () => { }); // @gate enableLegacyCache - it('suspends and shows inner fallback', () => { + it('suspends and shows inner fallback', async () => { ReactNoop.render( }> }> @@ -179,12 +183,12 @@ describe('ReactSuspenseFallback', () => { , ); - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']); + await waitForAll(['Suspend! [A]', 'Loading...']); expect(ReactNoop).toMatchRenderedOutput(); }); // @gate enableLegacyCache - it('suspends and shows inner undefined fallback', () => { + it('suspends and shows inner undefined fallback', async () => { ReactNoop.render( }> @@ -193,7 +197,7 @@ describe('ReactSuspenseFallback', () => { , ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend! [A]', // null ]); @@ -201,7 +205,7 @@ describe('ReactSuspenseFallback', () => { }); // @gate enableLegacyCache - it('suspends and shows inner null fallback', () => { + it('suspends and shows inner null fallback', async () => { ReactNoop.render( }> @@ -210,7 +214,7 @@ describe('ReactSuspenseFallback', () => { , ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend! [A]', // null ]); diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js index 701d23854993d..d1ef215063f04 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js @@ -5,6 +5,9 @@ let act; let Profiler; let Suspense; let SuspenseList; +let waitForAll; +let assertLog; +let waitFor; describe('ReactSuspenseList', () => { beforeEach(() => { @@ -19,6 +22,11 @@ describe('ReactSuspenseList', () => { if (gate(flags => flags.enableSuspenseList)) { SuspenseList = React.SuspenseList; } + + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + assertLog = InternalTestUtils.assertLog; + waitFor = InternalTestUtils.waitFor; }); function Text(props) { @@ -106,22 +114,22 @@ describe('ReactSuspenseList', () => { }); // @gate enableSuspenseList - it('warns if a single element is passed to a "forwards" list', () => { + it('warns if a single element is passed to a "forwards" list', async () => { function Foo({children}) { return {children}; } ReactNoop.render(); // No warning - Scheduler.unstable_flushAll(); + await waitForAll([]); ReactNoop.render({null}); // No warning - Scheduler.unstable_flushAll(); + await waitForAll([]); ReactNoop.render({false}); // No warning - Scheduler.unstable_flushAll(); + await waitForAll([]); ReactNoop.render( @@ -213,7 +221,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'Suspend! [B]', 'Loading B', @@ -231,7 +239,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); - expect(Scheduler).toFlushAndYield(['C']); + await waitForAll(['C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -243,7 +251,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B']); + await waitForAll(['B']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -280,13 +288,7 @@ describe('ReactSuspenseList', () => { ReactNoop.renderLegacySyncRoot(); - expect(Scheduler).toHaveYielded([ - 'A', - 'Suspend! [B]', - 'Loading B', - 'Suspend! [C]', - 'Loading C', - ]); + assertLog(['A', 'Suspend! [B]', 'Loading B', 'Suspend! [C]', 'Loading C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -300,7 +302,7 @@ describe('ReactSuspenseList', () => { C.resolve(); }); - expect(Scheduler).toHaveYielded(['C']); + assertLog(['C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -314,7 +316,7 @@ describe('ReactSuspenseList', () => { B.resolve(); }); - expect(Scheduler).toHaveYielded(['B']); + assertLog(['B']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -351,7 +353,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'Suspend! [B]', 'Loading B', @@ -372,7 +374,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'B', 'Suspend! [C]']); + await waitForAll(['A', 'B', 'Suspend! [C]']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -384,7 +386,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'B', 'C']); + await waitForAll(['A', 'B', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -425,7 +427,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'Suspend! [B]', 'Loading B', @@ -450,7 +452,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'B', 'Suspend! [C]']); + await waitForAll(['A', 'B', 'Suspend! [C]']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -466,7 +468,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'B', 'C']); + await waitForAll(['A', 'B', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -510,7 +512,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'B', 'Suspend! [C]', @@ -532,7 +534,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'B', 'C']); + await waitForAll(['A', 'B', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -572,7 +574,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'B', 'Suspend! [C]', @@ -592,7 +594,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'B', 'C']); + await waitForAll(['A', 'B', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -640,7 +642,7 @@ describe('ReactSuspenseList', () => { // Mount await A.resolve(); ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'Suspend! [B]', 'Loading B', @@ -654,7 +656,7 @@ describe('ReactSuspenseList', () => { , ); await B.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'B']); + await waitForAll(['A', 'B']); expect(ReactNoop).toMatchRenderedOutput( <> A @@ -665,7 +667,7 @@ describe('ReactSuspenseList', () => { // Update await C.resolve(); ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'C', 'Suspend! [D]', 'Loading D', @@ -679,7 +681,7 @@ describe('ReactSuspenseList', () => { , ); await D.resolve(); - expect(Scheduler).toFlushAndYield(['C', 'D']); + await waitForAll(['C', 'D']); expect(ReactNoop).toMatchRenderedOutput( <> C @@ -724,20 +726,20 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading']); + await waitForAll(['Suspend! [A]', 'Loading']); expect(ReactNoop).toMatchRenderedOutput(Loading); await A.resolve(); - expect(Scheduler).toFlushAndYield(['A']); + await waitForAll(['A']); expect(ReactNoop).toMatchRenderedOutput(A); // Let's do an update that should consult the avoided boundaries. ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'Suspend! [B]', 'Loading B', @@ -763,7 +765,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'Suspend! [C]']); + await waitForAll(['B', 'Suspend! [C]']); // Even though we could now show B, we're still waiting on C. expect(ReactNoop).toMatchRenderedOutput( @@ -776,7 +778,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'C']); + await waitForAll(['B', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -817,7 +819,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend! [A]', // null ]); @@ -826,14 +828,14 @@ describe('ReactSuspenseList', () => { await A.resolve(); - expect(Scheduler).toFlushAndYield(['A']); + await waitForAll(['A']); expect(ReactNoop).toMatchRenderedOutput(A); // Let's do an update that should consult the avoided boundaries. ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'Suspend! [B]', // null @@ -853,14 +855,14 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'Suspend! [C]']); + await waitForAll(['B', 'Suspend! [C]']); // Even though we could now show B, we're still waiting on C. expect(ReactNoop).toMatchRenderedOutput(A); await C.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'C']); + await waitForAll(['B', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -897,12 +899,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ - 'Suspend! [A]', - 'Loading A', - 'Loading B', - 'Loading C', - ]); + await waitForAll(['Suspend! [A]', 'Loading A', 'Loading B', 'Loading C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -914,7 +911,7 @@ describe('ReactSuspenseList', () => { await A.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'Suspend! [B]']); + await waitForAll(['A', 'Suspend! [B]']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -926,7 +923,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'C']); + await waitForAll(['B', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -963,12 +960,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ - 'Suspend! [C]', - 'Loading C', - 'Loading B', - 'Loading A', - ]); + await waitForAll(['Suspend! [C]', 'Loading C', 'Loading B', 'Loading A']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -980,7 +972,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); - expect(Scheduler).toFlushAndYield(['C', 'Suspend! [B]']); + await waitForAll(['C', 'Suspend! [B]']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -992,7 +984,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'A']); + await waitForAll(['B', 'A']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -1036,7 +1028,7 @@ describe('ReactSuspenseList', () => { />, ); - expect(Scheduler).toFlushAndYield(['B', 'D']); + await waitForAll(['B', 'D']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -1059,7 +1051,7 @@ describe('ReactSuspenseList', () => { />, ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend! [A]', 'Loading A', 'B', @@ -1087,7 +1079,7 @@ describe('ReactSuspenseList', () => { await A.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'Suspend! [C]']); + await waitForAll(['A', 'Suspend! [C]']); // Even though we could show A, it is still in a fallback state because // C is not yet resolved. We need to resolve everything in the head first. @@ -1104,7 +1096,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'C', 'Suspend! [E]']); + await waitForAll(['A', 'C', 'Suspend! [E]']); // We can now resolve the full head. expect(ReactNoop).toMatchRenderedOutput( @@ -1120,7 +1112,7 @@ describe('ReactSuspenseList', () => { await E.resolve(); - expect(Scheduler).toFlushAndYield(['E', 'Suspend! [F]']); + await waitForAll(['E', 'Suspend! [F]']); // In the tail we can resolve one-by-one. expect(ReactNoop).toMatchRenderedOutput( @@ -1147,7 +1139,7 @@ describe('ReactSuspenseList', () => { />, ); - expect(Scheduler).toFlushAndYield(['D', 'E', 'F']); + await waitForAll(['D', 'E', 'F']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -1203,7 +1195,7 @@ describe('ReactSuspenseList', () => { ]} />, ); - expect(Scheduler).toFlushAndYield(['F', 'E', 'D', 'C', 'B', 'A']); + await waitForAll(['F', 'E', 'D', 'C', 'B', 'A']); expect(ReactNoop).toMatchRenderedOutput( <> A @@ -1229,7 +1221,7 @@ describe('ReactSuspenseList', () => { />, ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend! [A]', 'Loading A', 'Suspend! [B]', @@ -1273,7 +1265,7 @@ describe('ReactSuspenseList', () => { await F.resolve(); - expect(Scheduler).toFlushAndYield(['Suspend! [D]', 'F']); + await waitForAll(['Suspend! [D]', 'F']); // Even though we could show F, it is still in a fallback state because // E is not yet resolved. We need to resolve everything in the head first. @@ -1294,7 +1286,7 @@ describe('ReactSuspenseList', () => { await D.resolve(); - expect(Scheduler).toFlushAndYield(['D', 'F', 'Suspend! [B]']); + await waitForAll(['D', 'F', 'Suspend! [B]']); // We can now resolve the full head. expect(ReactNoop).toMatchRenderedOutput( @@ -1312,7 +1304,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'Suspend! [A]']); + await waitForAll(['B', 'Suspend! [A]']); // In the tail we can resolve one-by-one. expect(ReactNoop).toMatchRenderedOutput( @@ -1329,7 +1321,7 @@ describe('ReactSuspenseList', () => { await A.resolve(); - expect(Scheduler).toFlushAndYield(['A']); + await waitForAll(['A']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -1366,12 +1358,12 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); }); - expect(Scheduler).toFlushAndYieldThrough(['A']); + await waitFor(['A']); Scheduler.unstable_advanceTime(200); jest.advanceTimersByTime(200); - expect(Scheduler).toFlushAndYieldThrough(['B']); + await waitFor(['B']); Scheduler.unstable_advanceTime(300); jest.advanceTimersByTime(300); @@ -1383,7 +1375,7 @@ describe('ReactSuspenseList', () => { // Time has now elapsed for so long that we're just going to give up // rendering the rest of the content. So that we can at least show // something. - expect(Scheduler).toFlushAndYieldThrough([ + await waitFor([ 'Loading C', 'C', // I'll flush through into the next render so that the first commits. ]); @@ -1397,7 +1389,7 @@ describe('ReactSuspenseList', () => { ); // Then we do a second pass to commit the last item. - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput( <> @@ -1432,13 +1424,13 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading A']); + await waitForAll(['Suspend! [A]', 'Loading A']); expect(ReactNoop).toMatchRenderedOutput(Loading A); await A.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'Suspend! [B]', 'Loading B']); + await waitForAll(['A', 'Suspend! [B]', 'Loading B']); // Incremental loading is suspended. jest.advanceTimersByTime(500); @@ -1452,7 +1444,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'Suspend! [C]', 'Loading C']); + await waitForAll(['B', 'Suspend! [C]', 'Loading C']); // Incremental loading is suspended. jest.advanceTimersByTime(500); @@ -1467,7 +1459,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); - expect(Scheduler).toFlushAndYield(['C']); + await waitForAll(['C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -1546,12 +1538,12 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); }); - expect(Scheduler).toFlushAndYieldThrough(['A']); + await waitFor(['A']); Scheduler.unstable_advanceTime(200); jest.advanceTimersByTime(200); - expect(Scheduler).toFlushAndYieldThrough(['B']); + await waitFor(['B']); Scheduler.unstable_advanceTime(300); jest.advanceTimersByTime(300); @@ -1563,7 +1555,7 @@ describe('ReactSuspenseList', () => { // Time has now elapsed for so long that we're just going to give up // rendering the rest of the content. So that we can at least show // something. - expect(Scheduler).toFlushAndYieldThrough([ + await waitFor([ 'Loading C', 'C', // I'll flush through into the next render so that the first commits. ]); @@ -1577,7 +1569,7 @@ describe('ReactSuspenseList', () => { ); // Then we do a second pass to commit the last two items. - expect(Scheduler).toFlushAndYield(['D']); + await waitForAll(['D']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -1622,7 +1614,7 @@ describe('ReactSuspenseList', () => { await A.resolve(); await D.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'D']); + await waitForAll(['A', 'D']); // First render commits A and D. expect(ReactNoop).toMatchRenderedOutput( @@ -1646,7 +1638,7 @@ describe('ReactSuspenseList', () => { />, ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'Suspend! [B]', 'Loading B', @@ -1673,7 +1665,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'Suspend! [C]']); + await waitForAll(['B', 'Suspend! [C]']); // Incremental loading is suspended. jest.advanceTimersByTime(500); @@ -1693,13 +1685,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); await E.resolve(); - expect(Scheduler).toFlushAndYield([ - 'B', - 'C', - 'E', - 'Suspend! [F]', - 'Loading F', - ]); + await waitForAll(['B', 'C', 'E', 'Suspend! [F]', 'Loading F']); jest.advanceTimersByTime(500); @@ -1716,7 +1702,7 @@ describe('ReactSuspenseList', () => { await F.resolve(); - expect(Scheduler).toFlushAndYield(['F']); + await waitForAll(['F']); jest.advanceTimersByTime(500); @@ -1765,7 +1751,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); await F.resolve(); - expect(Scheduler).toFlushAndYield(['F', 'C']); + await waitForAll(['F', 'C']); // First render commits C and F. expect(ReactNoop).toMatchRenderedOutput( @@ -1789,7 +1775,7 @@ describe('ReactSuspenseList', () => { />, ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'C', 'Suspend! [D]', 'Loading D', @@ -1816,7 +1802,7 @@ describe('ReactSuspenseList', () => { await D.resolve(); - expect(Scheduler).toFlushAndYield(['D', 'Suspend! [E]']); + await waitForAll(['D', 'Suspend! [E]']); // Incremental loading is suspended. jest.advanceTimersByTime(500); @@ -1841,13 +1827,7 @@ describe('ReactSuspenseList', () => { await D.resolve(); await E.resolve(); - expect(Scheduler).toFlushAndYield([ - 'D', - 'E', - 'B', - 'Suspend! [A]', - 'Loading A', - ]); + await waitForAll(['D', 'E', 'B', 'Suspend! [A]', 'Loading A']); jest.advanceTimersByTime(500); @@ -1864,7 +1844,7 @@ describe('ReactSuspenseList', () => { await A.resolve(); - expect(Scheduler).toFlushAndYield(['A']); + await waitForAll(['A']); jest.advanceTimersByTime(500); @@ -1916,7 +1896,7 @@ describe('ReactSuspenseList', () => { await A.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'D']); + await waitForAll(['A', 'D']); // First render commits A and D. expect(ReactNoop).toMatchRenderedOutput( @@ -1941,7 +1921,7 @@ describe('ReactSuspenseList', () => { />, ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'Suspend! [B]', 'Loading B', @@ -1976,7 +1956,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'Suspend! [C]']); + await waitForAll(['B', 'Suspend! [C]']); // Incremental loading is suspended. jest.advanceTimersByTime(500); @@ -1999,13 +1979,7 @@ describe('ReactSuspenseList', () => { await D.resolve(); await E.resolve(); - expect(Scheduler).toFlushAndYield([ - 'C', - 'D', - 'E', - 'Suspend! [F]', - 'Loading F', - ]); + await waitForAll(['C', 'D', 'E', 'Suspend! [F]', 'Loading F']); jest.advanceTimersByTime(500); @@ -2022,7 +1996,7 @@ describe('ReactSuspenseList', () => { await F.resolve(); - expect(Scheduler).toFlushAndYield(['F']); + await waitForAll(['F']); jest.advanceTimersByTime(500); @@ -2062,13 +2036,13 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading A']); + await waitForAll(['Suspend! [A]', 'Loading A']); expect(ReactNoop).toMatchRenderedOutput(null); await A.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'Suspend! [B]', 'Loading B']); + await waitForAll(['A', 'Suspend! [B]', 'Loading B']); // Incremental loading is suspended. jest.advanceTimersByTime(500); @@ -2077,7 +2051,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'Suspend! [C]', 'Loading C']); + await waitForAll(['B', 'Suspend! [C]', 'Loading C']); // Incremental loading is suspended. jest.advanceTimersByTime(500); @@ -2091,7 +2065,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); - expect(Scheduler).toFlushAndYield(['C']); + await waitForAll(['C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2129,7 +2103,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'Suspend! [B]', 'Loading B', @@ -2153,7 +2127,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'B', 'C', 'D']); + await waitForAll(['A', 'B', 'C', 'D']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2189,19 +2163,13 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ - 'A', - 'Suspend! [B]', - 'Loading B', - 'C', - 'Loading C', - ]); + await waitForAll(['A', 'Suspend! [B]', 'Loading B', 'C', 'Loading C']); expect(ReactNoop).toMatchRenderedOutput(Loading C); await B.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'B', 'C']); + await waitForAll(['A', 'B', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2238,7 +2206,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['A', 'C']); + await waitForAll(['A', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2251,14 +2219,7 @@ describe('ReactSuspenseList', () => { // so we're now effectively in "together" mode for the head. ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ - 'A', - 'Suspend! [B]', - 'Loading B', - 'C', - 'A', - 'C', - ]); + await waitForAll(['A', 'Suspend! [B]', 'Loading B', 'C', 'A', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2269,7 +2230,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B']); + await waitForAll(['B']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2303,7 +2264,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['A', 'B', '-']); + await waitForAll(['A', 'B', '-']); expect(ReactNoop).toMatchRenderedOutput(
@@ -2316,7 +2277,7 @@ describe('ReactSuspenseList', () => { // Update the row adjacent to the list act(() => updateAdjacent('C')); - expect(Scheduler).toHaveYielded(['C']); + assertLog(['C']); expect(ReactNoop).toMatchRenderedOutput(
@@ -2359,7 +2320,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['A', 'Sync B']); + await waitForAll(['A', 'Sync B']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2373,7 +2334,7 @@ describe('ReactSuspenseList', () => { // During an update we suspend on B. act(() => setAsyncB(true)); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Suspend! [B]', 'Loading B', // The second pass is the "force hide" pass @@ -2391,7 +2352,7 @@ describe('ReactSuspenseList', () => { // This should leave the tree intact. act(() => ReactNoop.render()); - expect(Scheduler).toHaveYielded(['A', 'Suspend! [B]', 'Loading B']); + assertLog(['A', 'Suspend! [B]', 'Loading B']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2402,7 +2363,7 @@ describe('ReactSuspenseList', () => { await AsyncB.resolve(); - expect(Scheduler).toFlushAndYield(['B']); + await waitForAll(['B']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2448,7 +2409,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['A', 'Sync B']); + await waitForAll(['A', 'Sync B']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2462,7 +2423,7 @@ describe('ReactSuspenseList', () => { // During an update we suspend on B. act(() => setAsyncB(true)); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Suspend! [B]', 'Loading B', // The second pass is the "force hide" pass @@ -2480,7 +2441,7 @@ describe('ReactSuspenseList', () => { // This should leave the tree intact. act(() => ReactNoop.render()); - expect(Scheduler).toHaveYielded(['A', 'Suspend! [B]', 'Loading B']); + assertLog(['A', 'Suspend! [B]', 'Loading B']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2491,7 +2452,7 @@ describe('ReactSuspenseList', () => { await AsyncB.resolve(); - expect(Scheduler).toFlushAndYield(['B']); + await waitForAll(['B']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2535,7 +2496,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput(null); @@ -2544,13 +2505,13 @@ describe('ReactSuspenseList', () => { updateLowPri(true); }); // Flush partially through. - expect(Scheduler).toFlushAndYieldThrough(['B', 'C']); + await waitFor(['B', 'C']); // Schedule another update at higher priority. ReactNoop.flushSync(() => updateHighPri(true)); // That will intercept the previous render. - expect(Scheduler).toHaveYielded([ + assertLog([ 'Suspend! [A]', 'Loading A', // Re-render at forced. @@ -2560,13 +2521,13 @@ describe('ReactSuspenseList', () => { expect(ReactNoop).toMatchRenderedOutput(Loading A); // Try again on low-pri. - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading A']); + await waitForAll(['Suspend! [A]', 'Loading A']); expect(ReactNoop).toMatchRenderedOutput(Loading A); }); await AsyncA.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'B', 'C', 'D']); + await waitForAll(['A', 'B', 'C', 'D']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2612,7 +2573,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'Suspend! [B]', 'Loading B', @@ -2631,7 +2592,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['A', 'B']); + await waitForAll(['A', 'B']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2681,15 +2642,10 @@ describe('ReactSuspenseList', () => { React.startTransition(() => { ReactNoop.render(); }); - expect(Scheduler).toFlushAndYieldThrough([ - 'App', - 'First Pass A', - 'Mount A', - 'A', - ]); + await waitFor(['App', 'First Pass A', 'Mount A', 'A']); expect(ReactNoop).toMatchRenderedOutput(A); - expect(Scheduler).toFlushAndYieldThrough(['First Pass B', 'Mount B', 'B']); + await waitFor(['First Pass B', 'Mount B', 'B']); expect(ReactNoop).toMatchRenderedOutput( <> A @@ -2697,7 +2653,7 @@ describe('ReactSuspenseList', () => { , ); - expect(Scheduler).toFlushAndYield(['C']); + await waitForAll(['C']); expect(ReactNoop).toMatchRenderedOutput( <> A @@ -2749,7 +2705,7 @@ describe('ReactSuspenseList', () => { React.startTransition(() => { ReactNoop.render(); }); - expect(Scheduler).toFlushAndYieldThrough([ + await waitFor([ 'App', 'First Pass A', 'Loading B', @@ -2765,7 +2721,7 @@ describe('ReactSuspenseList', () => { , ); - expect(Scheduler).toFlushAndYieldThrough(['First Pass B', 'Mount B', 'B']); + await waitFor(['First Pass B', 'Mount B', 'B']); expect(ReactNoop).toMatchRenderedOutput( <> A @@ -2774,7 +2730,7 @@ describe('ReactSuspenseList', () => { , ); - expect(Scheduler).toFlushAndYield(['C']); + await waitForAll(['C']); expect(ReactNoop).toMatchRenderedOutput( <> A @@ -2840,13 +2796,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ - 'App', - 'A', - 'B', - 'Suspend! [C]', - 'Fallback', - ]); + await waitForAll(['App', 'A', 'B', 'Suspend! [C]', 'Fallback']); expect(ReactNoop).toMatchRenderedOutput( <> A @@ -2868,7 +2818,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['App', 'A', 'B', 'C']); + await waitForAll(['App', 'A', 'B', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -2886,7 +2836,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'App', 'A', 'B', @@ -2928,7 +2878,7 @@ describe('ReactSuspenseList', () => { await C.resolve(); - expect(Scheduler).toFlushAndYield(['C', 'Suspend! [D]']); + await waitForAll(['C', 'Suspend! [D]']); expect(ReactNoop).toMatchRenderedOutput( <> A @@ -3003,12 +2953,7 @@ describe('ReactSuspenseList', () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ - 'Suspend! [A]', - 'Loading A', - 'Loading B', - 'Loading C', - ]); + await waitForAll(['Suspend! [A]', 'Loading A', 'Loading B', 'Loading C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -3020,7 +2965,7 @@ describe('ReactSuspenseList', () => { await A.resolve(); - expect(Scheduler).toFlushAndYield(['A', 'Suspend! [B]']); + await waitForAll(['A', 'Suspend! [B]']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -3032,7 +2977,7 @@ describe('ReactSuspenseList', () => { await B.resolve(); - expect(Scheduler).toFlushAndYield(['B', 'C']); + await waitForAll(['B', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> diff --git a/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js index 9ebf8c9170c6f..f8fe9d10cdd83 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js @@ -17,6 +17,8 @@ let ReactCache; let Suspense; let TextResource; let textResourceShouldFail; +let waitForAll; +let assertLog; describe('ReactSuspensePlaceholder', () => { beforeEach(() => { @@ -34,6 +36,10 @@ describe('ReactSuspensePlaceholder', () => { Profiler = React.Profiler; Suspense = React.Suspense; + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + assertLog = InternalTestUtils.assertLog; + TextResource = ReactCache.unstable_createResource( ([text, ms = 0]) => { let listeners = null; @@ -106,7 +112,7 @@ describe('ReactSuspensePlaceholder', () => { } } - it('times out children that are already hidden', () => { + it('times out children that are already hidden', async () => { class HiddenText extends React.PureComponent { render() { const text = this.props.text; @@ -132,13 +138,13 @@ describe('ReactSuspensePlaceholder', () => { // Initial mount ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['A', 'Suspend! [B]', 'C', 'Loading...']); + await waitForAll(['A', 'Suspend! [B]', 'C', 'Loading...']); expect(ReactNoop).toMatchRenderedOutput('Loading...'); jest.advanceTimersByTime(1000); - expect(Scheduler).toHaveYielded(['Promise resolved [B]']); + assertLog(['Promise resolved [B]']); - expect(Scheduler).toFlushAndYield(['A', 'B', 'C']); + await waitForAll(['A', 'B', 'C']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -150,11 +156,11 @@ describe('ReactSuspensePlaceholder', () => { // Update ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Suspend! [B2]', 'C', 'Loading...']); + await waitForAll(['Suspend! [B2]', 'C', 'Loading...']); // Time out the update jest.advanceTimersByTime(750); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput( <> @@ -166,8 +172,8 @@ describe('ReactSuspensePlaceholder', () => { // Resolve the promise jest.advanceTimersByTime(1000); - expect(Scheduler).toHaveYielded(['Promise resolved [B2]']); - expect(Scheduler).toFlushAndYield(['B2', 'C']); + assertLog(['Promise resolved [B2]']); + await waitForAll(['B2', 'C']); // Render the final update. A should still be hidden, because it was // given a `hidden` prop. @@ -194,39 +200,34 @@ describe('ReactSuspensePlaceholder', () => { // Initial mount ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['A', 'Suspend! [B]', 'C', 'Loading...']); + await waitForAll(['A', 'Suspend! [B]', 'C', 'Loading...']); expect(ReactNoop).not.toMatchRenderedOutput('ABC'); jest.advanceTimersByTime(1000); - expect(Scheduler).toHaveYielded(['Promise resolved [B]']); - expect(Scheduler).toFlushAndYield(['A', 'B', 'C']); + assertLog(['Promise resolved [B]']); + await waitForAll(['A', 'B', 'C']); expect(ReactNoop).toMatchRenderedOutput('ABC'); // Update ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ - 'A', - 'Suspend! [B2]', - 'C', - 'Loading...', - ]); + await waitForAll(['A', 'Suspend! [B2]', 'C', 'Loading...']); // Time out the update jest.advanceTimersByTime(750); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput('Loading...'); // Resolve the promise jest.advanceTimersByTime(1000); - expect(Scheduler).toHaveYielded(['Promise resolved [B2]']); - expect(Scheduler).toFlushAndYield(['A', 'B2', 'C']); + assertLog(['Promise resolved [B2]']); + await waitForAll(['A', 'B2', 'C']); // Render the final update. A should still be hidden, because it was // given a `hidden` prop. expect(ReactNoop).toMatchRenderedOutput('AB2C'); }); - it('preserves host context for text nodes', () => { + it('preserves host context for text nodes', async () => { function App(props) { return ( // uppercase is a special type that causes React Noop to render child @@ -244,32 +245,27 @@ describe('ReactSuspensePlaceholder', () => { // Initial mount ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['a', 'Suspend! [b]', 'c', 'Loading...']); + await waitForAll(['a', 'Suspend! [b]', 'c', 'Loading...']); expect(ReactNoop).toMatchRenderedOutput(LOADING...); jest.advanceTimersByTime(1000); - expect(Scheduler).toHaveYielded(['Promise resolved [b]']); - expect(Scheduler).toFlushAndYield(['a', 'b', 'c']); + assertLog(['Promise resolved [b]']); + await waitForAll(['a', 'b', 'c']); expect(ReactNoop).toMatchRenderedOutput(ABC); // Update ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ - 'a', - 'Suspend! [b2]', - 'c', - 'Loading...', - ]); + await waitForAll(['a', 'Suspend! [b2]', 'c', 'Loading...']); // Time out the update jest.advanceTimersByTime(750); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput(LOADING...); // Resolve the promise jest.advanceTimersByTime(1000); - expect(Scheduler).toHaveYielded(['Promise resolved [b2]']); - expect(Scheduler).toFlushAndYield(['a', 'b2', 'c']); + assertLog(['Promise resolved [b2]']); + await waitForAll(['a', 'b2', 'c']); // Render the final update. A should still be hidden, because it was // given a `hidden` prop. @@ -312,7 +308,7 @@ describe('ReactSuspensePlaceholder', () => { describe('when suspending during mount', () => { it('properly accounts for base durations when a suspended times out in a legacy tree', async () => { ReactNoop.renderLegacySyncRoot(); - expect(Scheduler).toHaveYielded([ + assertLog([ 'App', 'Suspending', 'Suspend! [Loaded]', @@ -330,11 +326,11 @@ describe('ReactSuspensePlaceholder', () => { jest.advanceTimersByTime(1000); - expect(Scheduler).toHaveYielded(['Promise resolved [Loaded]']); + assertLog(['Promise resolved [Loaded]']); ReactNoop.flushSync(); - expect(Scheduler).toHaveYielded(['Loaded']); + assertLog(['Loaded']); expect(ReactNoop).toMatchRenderedOutput('LoadedText'); expect(onRender).toHaveBeenCalledTimes(2); @@ -345,10 +341,10 @@ describe('ReactSuspensePlaceholder', () => { expect(onRender.mock.calls[1][3]).toBe(8); }); - it('properly accounts for base durations when a suspended times out in a concurrent tree', () => { + it('properly accounts for base durations when a suspended times out in a concurrent tree', async () => { ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'App', 'Suspending', 'Suspend! [Loaded]', @@ -368,8 +364,8 @@ describe('ReactSuspensePlaceholder', () => { // Resolve the pending promise. jest.advanceTimersByTime(1000); - expect(Scheduler).toHaveYielded(['Promise resolved [Loaded]']); - expect(Scheduler).toFlushAndYield(['Suspending', 'Loaded', 'Text']); + assertLog(['Promise resolved [Loaded]']); + await waitForAll(['Suspending', 'Loaded', 'Text']); expect(ReactNoop).toMatchRenderedOutput('LoadedText'); expect(onRender).toHaveBeenCalledTimes(2); @@ -385,7 +381,7 @@ describe('ReactSuspensePlaceholder', () => { ReactNoop.renderLegacySyncRoot( , ); - expect(Scheduler).toHaveYielded(['App', 'Text']); + assertLog(['App', 'Text']); expect(ReactNoop).toMatchRenderedOutput('Text'); expect(onRender).toHaveBeenCalledTimes(1); @@ -395,7 +391,7 @@ describe('ReactSuspensePlaceholder', () => { expect(onRender.mock.calls[0][3]).toBe(5); ReactNoop.render(); - expect(Scheduler).toHaveYielded([ + assertLog([ 'App', 'Suspending', 'Suspend! [Loaded]', @@ -415,7 +411,7 @@ describe('ReactSuspensePlaceholder', () => { ReactNoop.renderLegacySyncRoot( , ); - expect(Scheduler).toHaveYielded([ + assertLog([ 'App', 'Suspending', 'Suspend! [Loaded]', @@ -429,11 +425,11 @@ describe('ReactSuspensePlaceholder', () => { expect(onRender.mock.calls[1][3]).toBe(10); jest.advanceTimersByTime(1000); - expect(Scheduler).toHaveYielded(['Promise resolved [Loaded]']); + assertLog(['Promise resolved [Loaded]']); ReactNoop.flushSync(); - expect(Scheduler).toHaveYielded(['Loaded']); + assertLog(['Loaded']); expect(ReactNoop).toMatchRenderedOutput('LoadedNew'); expect(onRender).toHaveBeenCalledTimes(4); @@ -444,7 +440,7 @@ describe('ReactSuspensePlaceholder', () => { expect(onRender.mock.calls[3][3]).toBe(9); }); - it('properly accounts for base durations when a suspended times out in a concurrent tree', () => { + it('properly accounts for base durations when a suspended times out in a concurrent tree', async () => { ReactNoop.render( <> @@ -452,7 +448,7 @@ describe('ReactSuspensePlaceholder', () => { , ); - expect(Scheduler).toFlushAndYield(['App', 'Text']); + await waitForAll(['App', 'Text']); expect(ReactNoop).toMatchRenderedOutput('Text'); expect(onRender).toHaveBeenCalledTimes(1); @@ -467,7 +463,7 @@ describe('ReactSuspensePlaceholder', () => { , ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'App', 'Suspending', 'Suspend! [Loaded]', @@ -507,7 +503,7 @@ describe('ReactSuspensePlaceholder', () => { // from timers. Scheduler.unstable_advanceTime(100); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'App', 'Suspending', 'Suspend! [Loaded]', @@ -520,17 +516,8 @@ describe('ReactSuspensePlaceholder', () => { // Resolve the pending promise. jest.advanceTimersByTime(100); - expect(Scheduler).toHaveYielded([ - 'Promise resolved [Loaded]', - 'Promise resolved [Sibling]', - ]); - expect(Scheduler).toFlushAndYield([ - 'App', - 'Suspending', - 'Loaded', - 'New', - 'Sibling', - ]); + assertLog(['Promise resolved [Loaded]', 'Promise resolved [Sibling]']); + await waitForAll(['App', 'Suspending', 'Loaded', 'New', 'Sibling']); expect(onRender).toHaveBeenCalledTimes(3); // When the suspending data is resolved and our final UI is rendered, diff --git a/packages/react-reconciler/src/__tests__/ReactThenable-test.js b/packages/react-reconciler/src/__tests__/ReactThenable-test.js index cc51d48c669e3..c6804605c5809 100644 --- a/packages/react-reconciler/src/__tests__/ReactThenable-test.js +++ b/packages/react-reconciler/src/__tests__/ReactThenable-test.js @@ -12,6 +12,10 @@ let Suspense; let startTransition; let cache; let pendingTextRequests; +let waitFor; +let waitForPaint; +let assertLog; +let waitForAll; describe('ReactThenable', () => { beforeEach(() => { @@ -29,6 +33,12 @@ describe('ReactThenable', () => { startTransition = React.startTransition; cache = React.cache; + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + assertLog = InternalTestUtils.assertLog; + waitForPaint = InternalTestUtils.waitForPaint; + waitFor = InternalTestUtils.waitFor; + pendingTextRequests = new Map(); }); @@ -94,7 +104,7 @@ describe('ReactThenable', () => { }); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // React will yield when the async component suspends. 'Suspend!', 'Resolve in microtask', @@ -128,11 +138,7 @@ describe('ReactThenable', () => { root.render(); }); }); - expect(Scheduler).toHaveYielded([ - 'Suspend!', - 'Resolve in microtask', - 'Async', - ]); + assertLog(['Suspend!', 'Resolve in microtask', 'Async']); expect(root).toMatchRenderedOutput('Async'); }); @@ -172,7 +178,7 @@ describe('ReactThenable', () => { await act(async () => { root.render(); }); - expect(Scheduler).toHaveYielded(['Suspend!', 'Loading...']); + assertLog(['Suspend!', 'Loading...']); expect(root).toMatchRenderedOutput('Loading...'); }); @@ -201,7 +207,7 @@ describe('ReactThenable', () => { root.render(); }); }); - expect(Scheduler).toHaveYielded(['ABC']); + assertLog(['ABC']); expect(root).toMatchRenderedOutput('ABC'); }); @@ -229,7 +235,7 @@ describe('ReactThenable', () => { root.render(); }); }); - expect(Scheduler).toHaveYielded(['ABC']); + assertLog(['ABC']); expect(root).toMatchRenderedOutput('ABC'); }); @@ -275,7 +281,7 @@ describe('ReactThenable', () => { root.render(); }); }); - expect(Scheduler).toHaveYielded(['Oops!', 'Oops!']); + assertLog(['Oops!', 'Oops!']); }); // @gate enableUseHook @@ -308,7 +314,7 @@ describe('ReactThenable', () => { root.render(); }); }); - expect(Scheduler).toHaveYielded(['ABCD']); + assertLog(['ABCD']); expect(root).toMatchRenderedOutput('ABCD'); }); @@ -344,7 +350,7 @@ describe('ReactThenable', () => { root.render(); }); }); - expect(Scheduler).toHaveYielded(['CD', 'Loading...']); + assertLog(['CD', 'Loading...']); expect(root).toMatchRenderedOutput('Loading...'); }); @@ -397,7 +403,7 @@ describe('ReactThenable', () => { root.render(); }); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // First attempt. The uncached promise suspends. 'Suspend! [Async]', // Because the promise already fulfilled, we're able to unwrap the value @@ -427,7 +433,7 @@ describe('ReactThenable', () => { }); // @gate enableUseHook - test('basic use(context)', () => { + test('basic use(context)', async () => { const ContextA = React.createContext(''); const ContextB = React.createContext('B'); @@ -446,7 +452,7 @@ describe('ReactThenable', () => { const root = ReactNoop.createRoot(); root.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(root).toMatchRenderedOutput('AB'); }); @@ -482,7 +488,7 @@ describe('ReactThenable', () => { startTransition(() => { root.render(); }); - expect(Scheduler).toFlushUntilNextPaint([]); + await waitForPaint([]); expect(root).toMatchRenderedOutput(null); await resolve({default: }); @@ -492,7 +498,7 @@ describe('ReactThenable', () => { root.render(); }); - expect(Scheduler).toHaveYielded(['Hello ', 'world!']); + assertLog(['Hello ', 'world!']); expect(root).toMatchRenderedOutput(
Hello world!
); }); @@ -547,7 +553,7 @@ describe('ReactThenable', () => {
, ); }); - expect(Scheduler).toHaveYielded(['(empty)']); + assertLog(['(empty)']); expect(root).toMatchRenderedOutput('(empty)'); await act(async () => { @@ -559,13 +565,13 @@ describe('ReactThenable', () => { ); }); }); - expect(Scheduler).toHaveYielded(['Async text requested [Async]']); + assertLog(['Async text requested [Async]']); expect(root).toMatchRenderedOutput('(empty)'); await act(async () => { resolveTextRequests('Async'); }); - expect(Scheduler).toHaveYielded(['Async text requested [Async]', 'Async']); + assertLog(['Async text requested [Async]', 'Async']); expect(root).toMatchRenderedOutput('Async'); }); @@ -586,10 +592,7 @@ describe('ReactThenable', () => { }); }); // Even though the initial render was a transition, it shows a fallback. - expect(Scheduler).toHaveYielded([ - 'Async text requested [Async]', - 'Loading...', - ]); + assertLog(['Async text requested [Async]', 'Loading...']); expect(root).toMatchRenderedOutput('Loading...'); // Resolve the original data @@ -603,7 +606,7 @@ describe('ReactThenable', () => { // this test, how would the developer be able to imperatively flush it if it // wasn't initiated until the current `act` call? Can't think of a better // strategy at the moment. - expect(Scheduler).toHaveYielded(['Async text requested [Async]']); + assertLog(['Async text requested [Async]']); expect(root).toMatchRenderedOutput('Loading...'); // Flush the second request. @@ -611,7 +614,7 @@ describe('ReactThenable', () => { resolveTextRequests('Async'); }); // This time it finishes because it was during a retry. - expect(Scheduler).toHaveYielded(['Async text requested [Async]', 'Async']); + assertLog(['Async text requested [Async]', 'Async']); expect(root).toMatchRenderedOutput('Async'); }); @@ -635,9 +638,7 @@ describe('ReactThenable', () => { ); }); }); - expect(Scheduler).toHaveYielded([ - 'Async text requested [Will never resolve]', - ]); + assertLog(['Async text requested [Will never resolve]']); await act(async () => { root.render( @@ -646,7 +647,7 @@ describe('ReactThenable', () => { , ); }); - expect(Scheduler).toHaveYielded(['Something different']); + assertLog(['Something different']); }); // @gate enableUseHook @@ -669,9 +670,7 @@ describe('ReactThenable', () => { ); }); }); - expect(Scheduler).toHaveYielded([ - 'Async text requested [Will never resolve]', - ]); + assertLog(['Async text requested [Will never resolve]']); // Calling a hook should error because we're oustide of a component. expect(useState).toThrow( @@ -704,7 +703,7 @@ describe('ReactThenable', () => { ReactNoop.flushSync(() => { root.render(); }); - expect(Scheduler).toHaveYielded(['Hi']); + assertLog(['Hi']); expect(root).toMatchRenderedOutput('Hi'); }); @@ -745,26 +744,21 @@ describe('ReactThenable', () => { await act(() => { root.render(); }); - expect(Scheduler).toHaveYielded([ - 'childShouldSuspend: false, showChild: true', - 'Child', - ]); + assertLog(['childShouldSuspend: false, showChild: true', 'Child']); expect(root).toMatchRenderedOutput('Child'); - await act(() => { + await act(async () => { // Perform an update that causes the app to suspend startTransition(() => { setChildShouldSuspend(true); }); - expect(Scheduler).toFlushAndYieldThrough([ - 'childShouldSuspend: true, showChild: true', - ]); + await waitFor(['childShouldSuspend: true, showChild: true']); // While the update is in progress, schedule another update. startTransition(() => { setShowChild(false); }); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // Because the interleaved update is not higher priority than what we were // already working on, it won't interrupt. The first update will continue, // and will suspend. @@ -827,17 +821,14 @@ describe('ReactThenable', () => { }); }); // Suspends while we wait for the async service to respond. - expect(Scheduler).toHaveYielded([ - 'Compute uppercase: Hello', - 'Async text requested [HELLO!]', - ]); + assertLog(['Compute uppercase: Hello', 'Async text requested [HELLO!]']); expect(root).toMatchRenderedOutput(null); // The data is received. await act(async () => { resolveTextRequests('HELLO!'); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // We shouldn't run the uppercase computation again, because we can reuse // the computation from the previous attempt. // 'Compute uppercase: Hello', @@ -867,15 +858,12 @@ describe('ReactThenable', () => { root.render(); }); }); - expect(Scheduler).toHaveYielded(['Async text requested [apple]']); + assertLog(['Async text requested [apple]']); expect(root).toMatchRenderedOutput(null); await act(async () => { resolveTextRequests('apple'); }); - expect(Scheduler).toHaveYielded([ - 'Async text requested [apple]', - 'apple carrot', - ]); + assertLog(['Async text requested [apple]', 'apple carrot']); expect(root).toMatchRenderedOutput('apple carrot'); // Update the state variable after the use(). @@ -884,15 +872,12 @@ describe('ReactThenable', () => { _setVegetable('dill'); }); }); - expect(Scheduler).toHaveYielded(['Async text requested [apple]']); + assertLog(['Async text requested [apple]']); expect(root).toMatchRenderedOutput('apple carrot'); await act(async () => { resolveTextRequests('apple'); }); - expect(Scheduler).toHaveYielded([ - 'Async text requested [apple]', - 'apple dill', - ]); + assertLog(['Async text requested [apple]', 'apple dill']); expect(root).toMatchRenderedOutput('apple dill'); // Update the state variable before the use(). The second state is maintained. @@ -901,15 +886,12 @@ describe('ReactThenable', () => { _setFruit('banana'); }); }); - expect(Scheduler).toHaveYielded(['Async text requested [banana]']); + assertLog(['Async text requested [banana]']); expect(root).toMatchRenderedOutput('apple dill'); await act(async () => { resolveTextRequests('banana'); }); - expect(Scheduler).toHaveYielded([ - 'Async text requested [banana]', - 'banana dill', - ]); + assertLog(['Async text requested [banana]', 'banana dill']); expect(root).toMatchRenderedOutput('banana dill'); }); @@ -931,15 +913,12 @@ describe('ReactThenable', () => { root.render(); }); }); - expect(Scheduler).toHaveYielded(['Async text requested [aguacate]']); + assertLog(['Async text requested [aguacate]']); expect(root).toMatchRenderedOutput(null); await act(async () => { resolveTextRequests('aguacate'); }); - expect(Scheduler).toHaveYielded([ - 'Async text requested [aguacate]', - 'aguacate abogado', - ]); + assertLog(['Async text requested [aguacate]', 'aguacate abogado']); expect(root).toMatchRenderedOutput('aguacate abogado'); // Now update the state. @@ -948,15 +927,12 @@ describe('ReactThenable', () => { _setLawyer('avocat'); }); }); - expect(Scheduler).toHaveYielded(['Async text requested [aguacate]']); + assertLog(['Async text requested [aguacate]']); expect(root).toMatchRenderedOutput('aguacate abogado'); await act(async () => { resolveTextRequests('aguacate'); }); - expect(Scheduler).toHaveYielded([ - 'Async text requested [aguacate]', - 'aguacate avocat', - ]); + assertLog(['Async text requested [aguacate]', 'aguacate avocat']); expect(root).toMatchRenderedOutput('aguacate avocat'); }); @@ -977,13 +953,13 @@ describe('ReactThenable', () => { root.render(); }); }); - expect(Scheduler).toHaveYielded(['Async text requested [Hello]']); + assertLog(['Async text requested [Hello]']); expect(root).toMatchRenderedOutput(null); await act(async () => { resolveTextRequests('Hello'); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // We shouldn't request async text again, because the async function // was memoized // 'Async text requested [Hello]' @@ -1015,7 +991,7 @@ describe('ReactThenable', () => { , ); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Async text requested [A]', 'Async text requested [B]', 'Async text requested [C]', @@ -1028,19 +1004,19 @@ describe('ReactThenable', () => { await act(async () => { resolveTextRequests('A'); }); - expect(Scheduler).toHaveYielded(['A', '(Loading C...)', '(Loading B...)']); + assertLog(['A', '(Loading C...)', '(Loading B...)']); expect(root).toMatchRenderedOutput('A(Loading B...)'); await act(async () => { resolveTextRequests('B'); }); - expect(Scheduler).toHaveYielded(['B', '(Loading C...)']); + assertLog(['B', '(Loading C...)']); expect(root).toMatchRenderedOutput('AB(Loading C...)'); await act(async () => { resolveTextRequests('C'); }); - expect(Scheduler).toHaveYielded(['C']); + assertLog(['C']); expect(root).toMatchRenderedOutput('ABC'); }); @@ -1068,7 +1044,7 @@ describe('ReactThenable', () => { , ); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Async text requested [A]', 'Async text requested [B]', 'Async text requested [C]', @@ -1081,13 +1057,13 @@ describe('ReactThenable', () => { await act(async () => { resolveTextRequests('A'); }); - expect(Scheduler).toHaveYielded(['Async text requested [A]']); + assertLog(['Async text requested [A]']); expect(root).toMatchRenderedOutput('(Loading A...)'); await act(async () => { resolveTextRequests('A'); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // React suspends until A finishes loading. 'Async text requested [A]', 'A', @@ -1106,13 +1082,13 @@ describe('ReactThenable', () => { await act(async () => { resolveTextRequests('B'); }); - expect(Scheduler).toHaveYielded(['Async text requested [B]']); + assertLog(['Async text requested [B]']); expect(root).toMatchRenderedOutput('A(Loading B...)'); await act(async () => { resolveTextRequests('B'); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // React suspends until B finishes loading. 'Async text requested [B]', 'B', @@ -1126,13 +1102,13 @@ describe('ReactThenable', () => { await act(async () => { resolveTextRequests('C'); }); - expect(Scheduler).toHaveYielded(['Async text requested [C]']); + assertLog(['Async text requested [C]']); expect(root).toMatchRenderedOutput('AB(Loading C...)'); await act(async () => { resolveTextRequests('C'); }); - expect(Scheduler).toHaveYielded(['Async text requested [C]', 'C']); + assertLog(['Async text requested [C]', 'C']); expect(root).toMatchRenderedOutput('ABC'); }); @@ -1162,7 +1138,7 @@ describe('ReactThenable', () => { root.render(); }); }); - expect(Scheduler).toHaveYielded(['A1']); + assertLog(['A1']); expect(root).toMatchRenderedOutput('A1'); }); }); diff --git a/packages/react-reconciler/src/__tests__/ReactTopLevelFragment-test.js b/packages/react-reconciler/src/__tests__/ReactTopLevelFragment-test.js index bc015724f7958..38b0d0a52633a 100644 --- a/packages/react-reconciler/src/__tests__/ReactTopLevelFragment-test.js +++ b/packages/react-reconciler/src/__tests__/ReactTopLevelFragment-test.js @@ -12,7 +12,7 @@ let React; let ReactNoop; -let Scheduler; +let waitForAll; // This is a new feature in Fiber so I put it in its own test file. It could // probably move to one of the other test files once it is official. @@ -21,18 +21,20 @@ describe('ReactTopLevelFragment', function () { jest.resetModules(); React = require('react'); ReactNoop = require('react-noop-renderer'); - Scheduler = require('scheduler'); + + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; }); - it('should render a simple fragment at the top of a component', function () { + it('should render a simple fragment at the top of a component', async function () { function Fragment() { return [
Hello
,
World
]; } ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); }); - it('should preserve state when switching from a single child', function () { + it('should preserve state when switching from a single child', async function () { let instance = null; class Stateful extends React.Component { @@ -50,21 +52,21 @@ describe('ReactTopLevelFragment', function () { ); } ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); const instanceA = instance; expect(instanceA).not.toBe(null); ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); const instanceB = instance; expect(instanceB).toBe(instanceA); }); - it('should not preserve state when switching to a nested array', function () { + it('should not preserve state when switching to a nested array', async function () { let instance = null; class Stateful extends React.Component { @@ -82,21 +84,21 @@ describe('ReactTopLevelFragment', function () { ); } ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); const instanceA = instance; expect(instanceA).not.toBe(null); ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); const instanceB = instance; expect(instanceB).not.toBe(instanceA); }); - it('preserves state if an implicit key slot switches from/to null', function () { + it('preserves state if an implicit key slot switches from/to null', async function () { let instance = null; class Stateful extends React.Component { @@ -112,28 +114,28 @@ describe('ReactTopLevelFragment', function () { : [
Hello
, ]; } ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); const instanceA = instance; expect(instanceA).not.toBe(null); ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); const instanceB = instance; expect(instanceB).toBe(instanceA); ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); const instanceC = instance; expect(instanceC === instanceA).toBe(true); }); - it('should preserve state in a reorder', function () { + it('should preserve state in a reorder', async function () { let instance = null; class Stateful extends React.Component { @@ -149,14 +151,14 @@ describe('ReactTopLevelFragment', function () { : [[,
World
],
]; } ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); const instanceA = instance; expect(instanceA).not.toBe(null); ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); const instanceB = instance; diff --git a/packages/react-reconciler/src/__tests__/ReactTopLevelText-test.js b/packages/react-reconciler/src/__tests__/ReactTopLevelText-test.js index 878d3b6f51e1e..30a3fa093b4ab 100644 --- a/packages/react-reconciler/src/__tests__/ReactTopLevelText-test.js +++ b/packages/react-reconciler/src/__tests__/ReactTopLevelText-test.js @@ -12,7 +12,7 @@ let React; let ReactNoop; -let Scheduler; +let waitForAll; // This is a new feature in Fiber so I put it in its own test file. It could // probably move to one of the other test files once it is official. @@ -21,20 +21,22 @@ describe('ReactTopLevelText', () => { jest.resetModules(); React = require('react'); ReactNoop = require('react-noop-renderer'); - Scheduler = require('scheduler'); + + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; }); - it('should render a component returning strings directly from render', () => { + it('should render a component returning strings directly from render', async () => { const Text = ({value}) => value; ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput('foo'); }); - it('should render a component returning numbers directly from render', () => { + it('should render a component returning numbers directly from render', async () => { const Text = ({value}) => value; ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput('10'); }); }); diff --git a/packages/react-reconciler/src/__tests__/ReactTransition-test.js b/packages/react-reconciler/src/__tests__/ReactTransition-test.js index dd0b9e0d0fedc..a41670a785a6c 100644 --- a/packages/react-reconciler/src/__tests__/ReactTransition-test.js +++ b/packages/react-reconciler/src/__tests__/ReactTransition-test.js @@ -20,6 +20,10 @@ let useTransition; let startTransition; let act; let getCacheForType; +let waitForAll; +let waitFor; +let waitForPaint; +let assertLog; let caches; let seededCache; @@ -38,6 +42,12 @@ describe('ReactTransition', () => { getCacheForType = React.unstable_getCacheForType; act = require('jest-react').act; + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + waitFor = InternalTestUtils.waitFor; + waitForPaint = InternalTestUtils.waitForPaint; + assertLog = InternalTestUtils.assertLog; + caches = []; seededCache = null; }); @@ -178,13 +188,13 @@ describe('ReactTransition', () => { await act(async () => { root.render(); }); - expect(Scheduler).toHaveYielded(['(empty)']); + assertLog(['(empty)']); expect(root).toMatchRenderedOutput('(empty)'); await act(async () => { start(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Pending...', '(empty)', 'Suspend! [Async]', @@ -195,7 +205,7 @@ describe('ReactTransition', () => { await resolveText('Async'); }); - expect(Scheduler).toHaveYielded(['Async']); + assertLog(['Async']); expect(root).toMatchRenderedOutput('Async'); }); @@ -239,7 +249,7 @@ describe('ReactTransition', () => { seedNextTextCache('A content'); root.render(); }); - expect(Scheduler).toHaveYielded(['A label', 'A content']); + assertLog(['A label', 'A content']); expect(root).toMatchRenderedOutput( <> A label
A content
@@ -250,7 +260,7 @@ describe('ReactTransition', () => { await act(async () => { update('B'); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // Commit pending state 'B label (loading...)', 'A content', @@ -271,7 +281,7 @@ describe('ReactTransition', () => { await act(async () => { update('C'); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // Commit pending state 'C label (loading...)', 'A content', @@ -292,7 +302,7 @@ describe('ReactTransition', () => { await act(async () => { resolveText('B content'); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // Attempt to render C, but it suspends 'C label', 'Suspend! [C content]', @@ -308,7 +318,7 @@ describe('ReactTransition', () => { await act(async () => { resolveText('C content'); }); - expect(Scheduler).toHaveYielded(['C label', 'C content']); + assertLog(['C label', 'C content']); expect(root).toMatchRenderedOutput( <> C label
C content
@@ -364,7 +374,7 @@ describe('ReactTransition', () => { seedNextTextCache('A content'); root.render(); }); - expect(Scheduler).toHaveYielded(['A label', 'A content']); + assertLog(['A label', 'A content']); expect(root).toMatchRenderedOutput( <> A label
A content
@@ -375,7 +385,7 @@ describe('ReactTransition', () => { await act(async () => { update('B'); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // Commit pending state 'B label (loading...)', 'A content', @@ -396,7 +406,7 @@ describe('ReactTransition', () => { await act(async () => { update('C'); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // Commit pending state 'C label (loading...)', 'A content', @@ -417,7 +427,7 @@ describe('ReactTransition', () => { await act(async () => { resolveText('B content'); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // Attempt to render C, but it suspends 'C label', 'Suspend! [C content]', @@ -433,7 +443,7 @@ describe('ReactTransition', () => { await act(async () => { resolveText('C content'); }); - expect(Scheduler).toHaveYielded(['C label', 'C content']); + assertLog(['C label', 'C content']); expect(root).toMatchRenderedOutput( <> C label
C content
@@ -481,7 +491,7 @@ describe('ReactTransition', () => { await act(async () => { root.render(); }); - expect(Scheduler).toHaveYielded([]); + assertLog([]); expect(root).toMatchRenderedOutput(null); // Switch to A. @@ -490,7 +500,7 @@ describe('ReactTransition', () => { setShowA(true); }); }); - expect(Scheduler).toHaveYielded(['Suspend! [A]', 'Loading...']); + assertLog(['Suspend! [A]', 'Loading...']); expect(root).toMatchRenderedOutput(null); // Before A loads, switch to B. This should entangle A with B. @@ -500,7 +510,7 @@ describe('ReactTransition', () => { setShowB(true); }); }); - expect(Scheduler).toHaveYielded(['Suspend! [B]', 'Loading...']); + assertLog(['Suspend! [B]', 'Loading...']); expect(root).toMatchRenderedOutput(null); // Before A or B loads, switch to C. This should entangle C with B, and @@ -511,7 +521,7 @@ describe('ReactTransition', () => { setShowC(true); }); }); - expect(Scheduler).toHaveYielded(['Suspend! [C]', 'Loading...']); + assertLog(['Suspend! [C]', 'Loading...']); expect(root).toMatchRenderedOutput(null); // Now the data starts resolving out of order. @@ -523,7 +533,7 @@ describe('ReactTransition', () => { resolveText('B'); }); }); - expect(Scheduler).toHaveYielded(['Suspend! [C]', 'Loading...']); + assertLog(['Suspend! [C]', 'Loading...']); expect(root).toMatchRenderedOutput(null); // Now resolve A. Again, this will attempt to render C, since everything @@ -533,7 +543,7 @@ describe('ReactTransition', () => { resolveText('A'); }); }); - expect(Scheduler).toHaveYielded(['Suspend! [C]', 'Loading...']); + assertLog(['Suspend! [C]', 'Loading...']); expect(root).toMatchRenderedOutput(null); // Finally, resolve C. This time we can finish. @@ -542,7 +552,7 @@ describe('ReactTransition', () => { resolveText('C'); }); }); - expect(Scheduler).toHaveYielded(['C']); + assertLog(['C']); expect(root).toMatchRenderedOutput('C'); }, ); @@ -559,7 +569,7 @@ describe('ReactTransition', () => { , ); }); - expect(Scheduler).toHaveYielded(['Initial']); + assertLog(['Initial']); expect(root).toMatchRenderedOutput('Initial'); await act(async () => { @@ -577,7 +587,7 @@ describe('ReactTransition', () => { }); // Partially render it. - expect(Scheduler).toFlushAndYieldThrough([ + await waitFor([ // Once we the update suspends, we know it's a refresh transition, // because the Suspense boundary has already mounted. 'Suspend! [Async]', @@ -598,7 +608,7 @@ describe('ReactTransition', () => { // Because the first one is going to suspend regardless, we should // immediately switch to rendering the new transition. - expect(Scheduler).toHaveYielded(['Updated']); + assertLog(['Updated']); expect(root).toMatchRenderedOutput('Updated'); }); @@ -643,12 +653,7 @@ describe('ReactTransition', () => { await act(async () => { root.render(); - expect(Scheduler).toFlushAndYield([ - 'A', - 'shouldHideInParent: false', - 'B', - 'C', - ]); + await waitForAll(['A', 'shouldHideInParent: false', 'B', 'C']); expect(root).toMatchRenderedOutput('ABC'); // Schedule an update @@ -660,14 +665,14 @@ describe('ReactTransition', () => { // lane from the first one. At the time this was written, all transitions are worked on // simultaneously, unless a transition was already in progress when a // new one was scheduled. So, partially render the first transition. - expect(Scheduler).toFlushAndYieldThrough(['A']); + await waitFor(['A']); // Now schedule a second transition. We won't interrupt the first one. React.startTransition(() => { setShouldHideInParent(true); }); // Continue rendering the first transition. - expect(Scheduler).toFlushAndYieldThrough([ + await waitFor([ 'shouldHideInParent: false', 'Suspend! [Async]', 'Loading...', @@ -683,16 +688,13 @@ describe('ReactTransition', () => { // time we re-enter the work loop (we don't interrupt immediately, we // just wait for the next time slice), we should throw out the // suspended first transition and try the second one. - expect(Scheduler).toFlushUntilNextPaint([ - 'shouldHideInParent: true', - '(empty)', - ]); + await waitForPaint(['shouldHideInParent: true', '(empty)']); expect(root).toMatchRenderedOutput('A(empty)BC'); // Since the two transitions are not entangled, we then later go back // and finish retry the first transition. Not really relevant to this // test but I'll assert the result anyway. - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'shouldHideInParent: true', '(empty)', @@ -732,7 +734,7 @@ describe('ReactTransition', () => { await act(async () => { root.render(); }); - expect(Scheduler).toHaveYielded(['A0', 'B0', 'C0']); + assertLog(['A0', 'B0', 'C0']); expect(root).toMatchRenderedOutput('A0B0C0'); await act(async () => { @@ -741,14 +743,14 @@ describe('ReactTransition', () => { root.render(); }); // Flush past the root, but stop before the async component. - expect(Scheduler).toFlushAndYieldThrough(['A1']); + await waitFor(['A1']); // Schedule another transition on the root, which already completed. startTransition(() => { root.render(); }); // We'll keep working on the first update. - expect(Scheduler).toFlushAndYieldThrough([ + await waitFor([ // Now the async component suspends 'Suspend! [Async]', 'Loading...', @@ -762,7 +764,7 @@ describe('ReactTransition', () => { // TODO: This should work even if React does not yield to the main // thread. Should use same mechanism as selective hydration to interrupt // the render before the end of the current slice of work. - expect(Scheduler).toFlushAndYield(['A2', 'B2', 'C2']); + await waitForAll(['A2', 'B2', 'C2']); expect(root).toMatchRenderedOutput('A2B2C2'); }); @@ -798,11 +800,7 @@ describe('ReactTransition', () => { }); // Initial render. - expect(Scheduler).toHaveYielded([ - 'Transition pri: 0', - 'Normal pri: 0', - 'Commit', - ]); + assertLog(['Transition pri: 0', 'Normal pri: 0', 'Commit']); expect(root).toMatchRenderedOutput('Transition pri: 0, Normal pri: 0'); await act(async () => { @@ -810,7 +808,7 @@ describe('ReactTransition', () => { updateNormalPri(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // Normal update first. 'Transition pri: 0', 'Normal pri: 1', @@ -854,14 +852,14 @@ describe('ReactTransition', () => { }); // Initial render. - expect(Scheduler).toHaveYielded(['(empty)', 'Normal pri: 0', 'Commit']); + assertLog(['(empty)', 'Normal pri: 0', 'Commit']); expect(root).toMatchRenderedOutput('(empty), Normal pri: 0'); await act(async () => { updateTransitionPri(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // Suspend. 'Suspend! [Async]', 'Normal pri: 0', @@ -874,7 +872,7 @@ describe('ReactTransition', () => { updateNormalPri(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // Normal pri update. '(empty)', 'Normal pri: 1', @@ -914,17 +912,13 @@ describe('ReactTransition', () => { await act(async () => { root.render(); }); - expect(Scheduler).toHaveYielded([ - 'Transition pri: 0', - 'Normal pri: 0', - 'Commit', - ]); + assertLog(['Transition pri: 0', 'Normal pri: 0', 'Commit']); expect(root).toMatchRenderedOutput('Transition pri: 0, Normal pri: 0'); await act(async () => { updateTransitionPri(); - expect(Scheduler).toFlushAndYieldThrough([ + await waitFor([ // Start transition update. 'Transition pri: 1', ]); @@ -935,7 +929,7 @@ describe('ReactTransition', () => { }); if (gate(flags => flags.enableUnifiedSyncLane)) { - expect(Scheduler).toHaveYielded([ + assertLog([ 'Normal pri: 0', 'Commit', @@ -945,7 +939,7 @@ describe('ReactTransition', () => { 'Commit', ]); } else { - expect(Scheduler).toHaveYielded([ + assertLog([ // Finish transition update. 'Normal pri: 0', 'Commit', diff --git a/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js b/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js index 417ec6fb1ca3c..8ce36f55f9847 100644 --- a/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js +++ b/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js @@ -12,6 +12,8 @@ let React; let ReactNoop; let Scheduler; let act; +let waitForAll; +let assertLog; let getCacheForType; let useState; @@ -43,6 +45,10 @@ describe('ReactInteractionTracing', () => { act = require('jest-react').act; + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + assertLog = InternalTestUtils.assertLog; + useState = React.useState; startTransition = React.startTransition; Suspense = React.Suspense; @@ -233,7 +239,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); await act(async () => { startTransition(() => root.render()); @@ -242,12 +248,12 @@ describe('ReactInteractionTracing', () => { await advanceTimers(1000); // Doesn't call transition or marker code - expect(Scheduler).toFlushAndYield(['Page Two']); + await waitForAll(['Page Two']); startTransition(() => root.render(), { name: 'transition', }); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page One', 'onTransitionStart(transition, 2000)', 'onTransitionComplete(transition, 2000, 2000)', @@ -299,7 +305,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); await act(async () => { startTransition(() => navigateToPageTwo(), {name: 'page transition'}); @@ -307,7 +313,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', 'onTransitionStart(page transition, 1000)', 'onTransitionComplete(page transition, 1000, 2000)', @@ -358,7 +364,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One: hide']); + await waitForAll(['Page One: hide']); await act(async () => { startTransition( @@ -372,7 +378,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two: show', 'onTransitionStart(page transition, 1000)', 'onTransitionComplete(page transition, 1000, 2000)', @@ -431,7 +437,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); }); await act(async () => { @@ -440,7 +446,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Loading...', 'onTransitionStart(page transition, 1000)', @@ -451,7 +457,7 @@ describe('ReactInteractionTracing', () => { await advanceTimers(1000); await resolveText('Page Two'); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', 'onTransitionProgress(page transition, 1000, 3000, [])', 'onTransitionComplete(page transition, 1000, 3000)', @@ -526,13 +532,13 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); }); await act(async () => { startTransition(() => navigateToPageTwo(), {name: 'page transition'}); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Loading...', 'onTransitionStart(page transition, 1000)', @@ -542,14 +548,14 @@ describe('ReactInteractionTracing', () => { await resolveText('Page Two'); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', 'onTransitionProgress(page transition, 1000, 2000, [])', 'onTransitionComplete(page transition, 1000, 2000)', ]); startTransition(() => showTextFn(), {name: 'text transition'}); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Show Text]', 'Show Text Loading...', 'Page Two', @@ -560,7 +566,7 @@ describe('ReactInteractionTracing', () => { await resolveText('Show Text'); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Show Text', 'onTransitionProgress(text transition, 2000, 3000, [])', 'onTransitionComplete(text transition, 2000, 3000)', @@ -633,7 +639,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); }); await act(async () => { @@ -641,7 +647,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Loading...', 'onTransitionStart(page transition, 1000)', @@ -652,7 +658,7 @@ describe('ReactInteractionTracing', () => { await act(async () => { startTransition(() => showTextFn(), {name: 'show text'}); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Show Text]', 'Show Text Loading...', 'Suspend [Page Two]', @@ -667,7 +673,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', 'onTransitionProgress(page transition, 1000, 3000, [])', 'onTransitionComplete(page transition, 1000, 3000)', @@ -677,7 +683,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Show Text', 'onTransitionProgress(show text, 2000, 4000, [])', 'onTransitionComplete(show text, 2000, 4000)', @@ -750,7 +756,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); }); await act(async () => { @@ -758,7 +764,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Suspend [Show Text One]', 'Show Text One Loading...', @@ -773,7 +779,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', 'Suspend [Show Text One]', 'Show Text One Loading...', @@ -786,7 +792,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Show Text One', 'onTransitionProgress(page transition, 1000, 4000, [show text two])', ]); @@ -795,7 +801,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Show Text Two', 'onTransitionProgress(page transition, 1000, 5000, [])', 'onTransitionComplete(page transition, 1000, 5000)', @@ -881,7 +887,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); }); await act(async () => { @@ -890,7 +896,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Suspend [Show Text One]', 'Show Text One Loading...', @@ -906,7 +912,7 @@ describe('ReactInteractionTracing', () => { resolveText('Page Two'); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', 'Suspend [Show Text One]', 'Show Text One Loading...', @@ -920,7 +926,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', 'Suspend [Show Text One]', 'Show Text One Loading...', @@ -938,7 +944,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Show Text', 'onTransitionProgress(navigate, 1000, 5000, [show text one])', 'onTransitionProgress(show text one, 1000, 5000, [show text one])', @@ -948,7 +954,7 @@ describe('ReactInteractionTracing', () => { resolveText('Show Text Two'); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Show Text Two', 'onTransitionProgress(show text two, 3000, 6000, [])', 'onTransitionComplete(show text two, 3000, 6000)', @@ -959,7 +965,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Show Text One', 'onTransitionProgress(navigate, 1000, 7000, [])', 'onTransitionProgress(show text one, 1000, 7000, [])', @@ -1037,7 +1043,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); await act(async () => { startTransition(() => navigateToPageTwo(), {name: 'page transition'}); @@ -1045,7 +1051,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', 'onTransitionStart(page transition, 1000)', 'onMarkerComplete(page transition, marker two, 1000, 2000)', @@ -1124,7 +1130,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); }); await act(async () => { @@ -1133,7 +1139,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Suspend [Marker Text]', 'Loading...', @@ -1145,7 +1151,7 @@ describe('ReactInteractionTracing', () => { await advanceTimers(1000); await resolveText('Page Two'); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', 'Suspend [Marker Text]', 'Loading...', @@ -1157,7 +1163,7 @@ describe('ReactInteractionTracing', () => { await advanceTimers(1000); await resolveText('Marker Text'); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Marker Text', 'onMarkerProgress(page transition, async marker, 1000, 4000, [])', 'onMarkerComplete(page transition, async marker, 1000, 4000)', @@ -1244,7 +1250,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); }); await act(async () => { @@ -1253,7 +1259,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Outer Text]', 'Suspend [Inner Text One]', 'Inner One...', @@ -1267,12 +1273,12 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); await resolveText('Inner Text Two'); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); ReactNoop.expire(1000); await advanceTimers(1000); await resolveText('Outer Text'); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Outer Text', 'Suspend [Inner Text One]', 'Inner One...', @@ -1284,7 +1290,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); await resolveText('Inner Text One'); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Inner Text One', 'onMarkerProgress(page transition, outer marker, 1000, 5000, [])', 'onMarkerComplete(page transition, marker one, 1000, 5000)', @@ -1367,7 +1373,7 @@ describe('ReactInteractionTracing', () => { root.render(); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); startTransition( () => root.render(), @@ -1378,7 +1384,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Loading...', 'onTransitionStart(transition one, 1000)', @@ -1400,7 +1406,7 @@ describe('ReactInteractionTracing', () => { resolveText('Page Two'); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', 'onMarkerProgress(transition one, marker one, 1000, 4000, [])', 'onTransitionProgress(transition one, 1000, 4000, [])', @@ -1501,7 +1507,7 @@ describe('ReactInteractionTracing', () => { root.render(); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); startTransition( () => root.render(), @@ -1511,7 +1517,7 @@ describe('ReactInteractionTracing', () => { ); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Loading...', 'Suspend [Sibling Text]', @@ -1526,7 +1532,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Loading...', 'Suspend [Sibling Text]', @@ -1539,7 +1545,7 @@ describe('ReactInteractionTracing', () => { root.render(); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Loading...', 'Suspend [Sibling Text]', @@ -1550,12 +1556,12 @@ describe('ReactInteractionTracing', () => { resolveText('Page Two'); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page Two']); + await waitForAll(['Page Two']); resolveText('Sibling Text'); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Sibling Text', 'onMarkerProgress(transition one, parent, 1000, 6000, [])', 'onMarkerProgress(transition one, sibling, 1000, 6000, [])', @@ -1652,7 +1658,7 @@ describe('ReactInteractionTracing', () => { root.render(); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); startTransition( () => root.render(), @@ -1662,7 +1668,7 @@ describe('ReactInteractionTracing', () => { ); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page One]', 'Loading One...', 'Suspend [Page Two]', @@ -1677,7 +1683,7 @@ describe('ReactInteractionTracing', () => { root.render(); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Loading Two...', 'onMarkerProgress(transition, parent, 1000, 3000, [suspense two])', @@ -1688,7 +1694,7 @@ describe('ReactInteractionTracing', () => { await resolveText('Page Two'); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', // Marker progress will still get called after incomplete but not marker complete 'onMarkerProgress(transition, parent, 1000, 4000, [])', @@ -1793,7 +1799,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Page One']); + await waitForAll(['Page One']); startTransition( () => root.render(), @@ -1803,7 +1809,7 @@ describe('ReactInteractionTracing', () => { ); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page One]', 'Suspend [Child]', 'Loading Child...', @@ -1820,7 +1826,7 @@ describe('ReactInteractionTracing', () => { await resolveText('Page One'); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page One', 'Suspend [Child]', 'Loading Child...', @@ -1833,7 +1839,7 @@ describe('ReactInteractionTracing', () => { root.render(); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Page Two]', 'Loading Two...', // "suspense one" has unsuspended so shouldn't be included @@ -1848,7 +1854,7 @@ describe('ReactInteractionTracing', () => { await resolveText('Page Two'); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Page Two', 'onMarkerProgress(transition, parent, 1000, 5000, [])', 'onMarkerProgress(transition, two, 1000, 5000, [])', @@ -1933,7 +1939,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend [Child]', 'onTransitionStart(transition, 0)', 'onMarkerProgress(transition, parent, 0, 1000, [child])', @@ -1945,23 +1951,20 @@ describe('ReactInteractionTracing', () => { await advanceTimers(1000); // This appended child isn't part of the transition so we // don't call any callback - expect(Scheduler).toFlushAndYield([ - 'Suspend [Appended child]', - 'Suspend [Child]', - ]); + await waitForAll(['Suspend [Appended child]', 'Suspend [Child]']); // This deleted child isn't part of the transition so we // don't call any callbacks root.render(); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield(['Suspend [Child]']); + await waitForAll(['Suspend [Child]']); await resolveText('Child'); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Child', 'onMarkerProgress(transition, parent, 0, 4000, [])', 'onMarkerComplete(transition, parent, 0, 4000)', @@ -2056,7 +2059,7 @@ describe('ReactInteractionTracing', () => { await advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Suspend [Child]', 'onTransitionStart(transition one, 0)', 'onMarkerProgress(transition one, parent, 0, 1000, [child])', @@ -2075,7 +2078,7 @@ describe('ReactInteractionTracing', () => { await advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Suspend [Appended child]', 'Suspend [Child]', 'onTransitionStart(transition two, 1000)', @@ -2089,7 +2092,7 @@ describe('ReactInteractionTracing', () => { await advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Suspend [Child]', 'onMarkerProgress(transition two, appended child, 1000, 3000, [])', 'onMarkerIncomplete(transition two, appended child, 1000, [{endTime: 3000, name: appended child, type: suspense}])', @@ -2101,7 +2104,7 @@ describe('ReactInteractionTracing', () => { await advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Child', 'onMarkerProgress(transition one, parent, 0, 4000, [])', 'onMarkerComplete(transition one, parent, 0, 4000)', @@ -2161,7 +2164,7 @@ describe('ReactInteractionTracing', () => { ); ReactNoop.expire(1000); await advanceTimers(1000); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'one', 'onTransitionStart(transition one, 0)', 'onMarkerComplete(transition one, one, 0, 1000)', @@ -2175,10 +2178,10 @@ describe('ReactInteractionTracing', () => { ); ReactNoop.expire(1000); await advanceTimers(1000); - expect(() => { + await expect(async () => { // onMarkerComplete shouldn't be called for transitions with // new keys - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'two', 'onTransitionStart(transition two, 1000)', 'onTransitionComplete(transition two, 1000, 2000)', @@ -2195,7 +2198,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); await advanceTimers(1000); // This should not warn and onMarkerComplete should be called - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'three', 'onTransitionStart(transition three, 2000)', 'onMarkerComplete(transition three, three, 2000, 3000)', @@ -2247,7 +2250,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Suspend [Text]', 'Loading...', 'Suspend [Hidden Text]', @@ -2260,7 +2263,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Text', 'onMarkerComplete(transition, marker, 0, 2000)', 'onTransitionComplete(transition, 0, 2000)', @@ -2271,7 +2274,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); advanceTimers(1000); }); - expect(Scheduler).toHaveYielded(['Hidden Text']); + assertLog(['Hidden Text']); }); // @gate enableTransitionTracing @@ -2317,7 +2320,7 @@ describe('ReactInteractionTracing', () => { await advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Suspend [Page Two]', 'Loading...', 'onTransitionStart(page transition, 0)', @@ -2329,7 +2332,7 @@ describe('ReactInteractionTracing', () => { await advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Page Two', 'onTransitionProgress(page transition, 0, 2000, [])', 'onTransitionComplete(page transition, 0, 2000)', @@ -2387,7 +2390,7 @@ describe('ReactInteractionTracing', () => { advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Suspend [Text]', 'Loading...', 'Suspend [Text Two]', @@ -2404,7 +2407,7 @@ describe('ReactInteractionTracing', () => { ReactNoop.expire(1000); advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Text Two', 'onTransitionProgress(transition, 0, 2000, [])', 'onTransitionComplete(transition, 0, 2000)', @@ -2465,7 +2468,7 @@ describe('ReactInteractionTracing', () => { advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Suspend [Text one]', 'Loading one...', 'Suspend [Text two]', @@ -2482,7 +2485,7 @@ describe('ReactInteractionTracing', () => { advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Text one', 'onTransitionProgress(transition one, 0, 2000, []) /root one/', 'onTransitionComplete(transition one, 0, 2000) /root one/', @@ -2494,7 +2497,7 @@ describe('ReactInteractionTracing', () => { advanceTimers(1000); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Text two', 'onTransitionProgress(transition two, 0, 3000, []) /root two/', 'onTransitionComplete(transition two, 0, 3000) /root two/', diff --git a/packages/react-reconciler/src/__tests__/ReactUpdatePriority-test.js b/packages/react-reconciler/src/__tests__/ReactUpdatePriority-test.js index 0089053c96043..8194bcd24d633 100644 --- a/packages/react-reconciler/src/__tests__/ReactUpdatePriority-test.js +++ b/packages/react-reconciler/src/__tests__/ReactUpdatePriority-test.js @@ -6,6 +6,9 @@ let startTransition; let useState; let useEffect; let act; +let waitFor; +let waitForPaint; +let assertLog; describe('ReactUpdatePriority', () => { beforeEach(() => { @@ -20,6 +23,11 @@ describe('ReactUpdatePriority', () => { startTransition = React.startTransition; useState = React.useState; useEffect = React.useEffect; + + const InternalTestUtils = require('internal-test-utils'); + waitFor = InternalTestUtils.waitFor; + waitForPaint = InternalTestUtils.waitForPaint; + assertLog = InternalTestUtils.assertLog; }); function Text({text}) { @@ -43,9 +51,9 @@ describe('ReactUpdatePriority', () => { root.render(); }); // Should not have flushed the effect update yet - expect(Scheduler).toHaveYielded([1]); + assertLog([1]); }); - expect(Scheduler).toHaveYielded([2]); + assertLog([2]); }); test('setState inside passive effect triggered by idle update should have idle priority', async () => { @@ -68,13 +76,13 @@ describe('ReactUpdatePriority', () => { root.render(); }); // Should not have flushed the effect update yet - expect(Scheduler).toFlushUntilNextPaint(['Idle: 1, Default: 1']); + await waitForPaint(['Idle: 1, Default: 1']); // Schedule another update at default priority setDefaultState(2); // The default update flushes first, because - expect(Scheduler).toFlushUntilNextPaint([ + await waitForPaint([ // Idle update is scheduled 'Idle update', @@ -83,7 +91,7 @@ describe('ReactUpdatePriority', () => { ]); }); // Now the idle update has flushed - expect(Scheduler).toHaveYielded(['Idle: 2, Default: 2']); + assertLog(['Idle: 2, Default: 2']); }); test('continuous updates should interrupt transitions', async () => { @@ -111,19 +119,19 @@ describe('ReactUpdatePriority', () => { await act(async () => { root.render(); }); - expect(Scheduler).toHaveYielded(['A1', 'B1', 'C1']); + assertLog(['A1', 'B1', 'C1']); expect(root).toMatchRenderedOutput('A1B1C1'); await act(async () => { startTransition(() => { setCounter(2); }); - expect(Scheduler).toFlushAndYieldThrough(['A2']); + await waitFor(['A2']); ReactNoop.unstable_runWithPriority(ContinuousEventPriority, () => { setIsHidden(true); }); }); - expect(Scheduler).toHaveYielded([ + assertLog([ // Because the hide update has continuous priority, it should interrupt the // in-progress transition '(hidden)', diff --git a/packages/react-reconciler/src/__tests__/ReactUpdaters-test.internal.js b/packages/react-reconciler/src/__tests__/ReactUpdaters-test.internal.js index e19bf198a9be6..d60e637b0da02 100644 --- a/packages/react-reconciler/src/__tests__/ReactUpdaters-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactUpdaters-test.internal.js @@ -19,6 +19,9 @@ let allSchedulerTags; let allSchedulerTypes; let onCommitRootShouldYield; let act; +let waitFor; +let waitForAll; +let assertLog; describe('updaters', () => { beforeEach(() => { @@ -91,6 +94,11 @@ describe('updaters', () => { Scheduler = require('scheduler'); act = require('jest-react').act; + + const InternalTestUtils = require('internal-test-utils'); + waitFor = InternalTestUtils.waitFor; + waitForAll = InternalTestUtils.waitForAll; + assertLog = InternalTestUtils.assertLog; }); it('should report the (host) root as the scheduler for root-level render', async () => { @@ -210,10 +218,7 @@ describe('updaters', () => { const root = ReactDOMClient.createRoot(document.createElement('div')); await act(async () => { root.render(); - expect(Scheduler).toFlushAndYieldThrough([ - 'CascadingChild 0', - 'onCommitRoot', - ]); + await waitFor(['CascadingChild 0', 'onCommitRoot']); }); expect(triggerActiveCascade).not.toBeNull(); expect(triggerPassiveCascade).not.toBeNull(); @@ -221,7 +226,7 @@ describe('updaters', () => { await act(async () => { triggerActiveCascade(); - expect(Scheduler).toFlushAndYieldThrough([ + await waitFor([ 'CascadingChild 0', 'onCommitRoot', 'CascadingChild 1', @@ -236,7 +241,7 @@ describe('updaters', () => { await act(async () => { triggerPassiveCascade(); - expect(Scheduler).toFlushAndYieldThrough([ + await waitFor([ 'CascadingChild 1', 'onCommitRoot', 'CascadingChild 2', @@ -252,7 +257,7 @@ describe('updaters', () => { ]); // Verify no outstanding flushes - Scheduler.unstable_flushAll(); + await waitForAll([]); }); it('should cover suspense pings', async () => { @@ -291,7 +296,7 @@ describe('updaters', () => { await act(async () => { ReactDOM.render(, document.createElement('div')); - expect(Scheduler).toHaveYielded(['onCommitRoot']); + assertLog(['onCommitRoot']); }); expect(setShouldSuspend).not.toBeNull(); expect(allSchedulerTypes).toEqual([[null]]); @@ -299,7 +304,7 @@ describe('updaters', () => { await act(async () => { setShouldSuspend(true); }); - expect(Scheduler).toHaveYielded(['onCommitRoot']); + assertLog(['onCommitRoot']); expect(allSchedulerTypes).toEqual([[null], [Suspender]]); expect(resolver).not.toBeNull(); @@ -307,11 +312,11 @@ describe('updaters', () => { resolver('abc'); return promise; }); - expect(Scheduler).toHaveYielded(['onCommitRoot']); + assertLog(['onCommitRoot']); expect(allSchedulerTypes).toEqual([[null], [Suspender], [Suspender]]); // Verify no outstanding flushes - Scheduler.unstable_flushAll(); + await waitForAll([]); }); it('should cover error handling', async () => { @@ -354,7 +359,7 @@ describe('updaters', () => { await act(async () => { root.render(); }); - expect(Scheduler).toHaveYielded(['initial', 'onCommitRoot']); + assertLog(['initial', 'onCommitRoot']); expect(triggerError).not.toBeNull(); allSchedulerTypes.splice(0); @@ -363,11 +368,11 @@ describe('updaters', () => { await act(async () => { triggerError(); }); - expect(Scheduler).toHaveYielded(['onCommitRoot', 'error', 'onCommitRoot']); + assertLog(['onCommitRoot', 'error', 'onCommitRoot']); expect(allSchedulerTypes).toEqual([[Parent], [ErrorBoundary]]); // Verify no outstanding flushes - Scheduler.unstable_flushAll(); + await waitForAll([]); }); it('should distinguish between updaters in the case of interleaved work', async () => { @@ -409,7 +414,7 @@ describe('updaters', () => { ); // Render everything initially. - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'SyncPriorityUpdater 0', 'Yield HighPriority 0', 'LowPriorityUpdater 0', @@ -421,14 +426,14 @@ describe('updaters', () => { expect(allSchedulerTags).toEqual([[HostRoot]]); // Render a partial update, but don't finish. - act(() => { + await act(async () => { triggerLowPriorityUpdate(); - expect(Scheduler).toFlushAndYieldThrough(['LowPriorityUpdater 1']); + await waitFor(['LowPriorityUpdater 1']); expect(allSchedulerTags).toEqual([[HostRoot]]); // Interrupt with higher priority work. ReactDOM.flushSync(triggerSyncPriorityUpdate); - expect(Scheduler).toHaveYielded([ + assertLog([ 'SyncPriorityUpdater 1', 'Yield HighPriority 1', 'onCommitRoot', @@ -437,7 +442,7 @@ describe('updaters', () => { // Finish the initial partial update triggerLowPriorityUpdate(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'LowPriorityUpdater 2', 'Yield LowPriority 2', 'onCommitRoot', @@ -455,6 +460,6 @@ describe('updaters', () => { ]); // Verify no outstanding flushes - Scheduler.unstable_flushAll(); + await waitForAll([]); }); }); diff --git a/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js b/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js index aceb7d05d2d1e..8dbf746e8160c 100644 --- a/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js +++ b/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js @@ -13,6 +13,7 @@ let React; let ReactTestRenderer; let Scheduler; let act; +let assertLog; describe('StrictEffectsMode', () => { beforeEach(() => { @@ -21,6 +22,9 @@ describe('StrictEffectsMode', () => { ReactTestRenderer = require('react-test-renderer'); Scheduler = require('scheduler'); act = require('jest-react').act; + + const InternalTestUtils = require('internal-test-utils'); + assertLog = InternalTestUtils.assertLog; }); function supportsDoubleInvokeEffects() { @@ -51,10 +55,7 @@ describe('StrictEffectsMode', () => { ReactTestRenderer.create(); }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect mount', - 'useEffect mount', - ]); + assertLog(['useLayoutEffect mount', 'useEffect mount']); }); it('double invoking for effects works properly', () => { @@ -80,7 +81,7 @@ describe('StrictEffectsMode', () => { }); if (supportsDoubleInvokeEffects()) { - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect mount', 'useEffect mount', 'useLayoutEffect unmount', @@ -89,17 +90,14 @@ describe('StrictEffectsMode', () => { 'useEffect mount', ]); } else { - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect mount', - 'useEffect mount', - ]); + assertLog(['useLayoutEffect mount', 'useEffect mount']); } act(() => { renderer.update(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect unmount', 'useLayoutEffect mount', 'useEffect unmount', @@ -110,10 +108,7 @@ describe('StrictEffectsMode', () => { renderer.unmount(); }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect unmount', - 'useEffect unmount', - ]); + assertLog(['useLayoutEffect unmount', 'useEffect unmount']); }); it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => { @@ -139,7 +134,7 @@ describe('StrictEffectsMode', () => { }); if (supportsDoubleInvokeEffects()) { - expect(Scheduler).toHaveYielded([ + assertLog([ 'useEffect One mount', 'useEffect Two mount', 'useEffect One unmount', @@ -148,17 +143,14 @@ describe('StrictEffectsMode', () => { 'useEffect Two mount', ]); } else { - expect(Scheduler).toHaveYielded([ - 'useEffect One mount', - 'useEffect Two mount', - ]); + assertLog(['useEffect One mount', 'useEffect Two mount']); } act(() => { renderer.update(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useEffect One unmount', 'useEffect Two unmount', 'useEffect One mount', @@ -169,10 +161,7 @@ describe('StrictEffectsMode', () => { renderer.unmount(null); }); - expect(Scheduler).toHaveYielded([ - 'useEffect One unmount', - 'useEffect Two unmount', - ]); + assertLog(['useEffect One unmount', 'useEffect Two unmount']); }); it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => { @@ -200,7 +189,7 @@ describe('StrictEffectsMode', () => { }); if (supportsDoubleInvokeEffects()) { - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect One mount', 'useLayoutEffect Two mount', 'useLayoutEffect One unmount', @@ -209,17 +198,14 @@ describe('StrictEffectsMode', () => { 'useLayoutEffect Two mount', ]); } else { - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect One mount', - 'useLayoutEffect Two mount', - ]); + assertLog(['useLayoutEffect One mount', 'useLayoutEffect Two mount']); } act(() => { renderer.update(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect One unmount', 'useLayoutEffect Two unmount', 'useLayoutEffect One mount', @@ -230,10 +216,7 @@ describe('StrictEffectsMode', () => { renderer.unmount(); }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect One unmount', - 'useLayoutEffect Two unmount', - ]); + assertLog(['useLayoutEffect One unmount', 'useLayoutEffect Two unmount']); }); it('useEffect and useLayoutEffect is called twice when there is no unmount', () => { @@ -257,33 +240,27 @@ describe('StrictEffectsMode', () => { }); if (supportsDoubleInvokeEffects()) { - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect mount', 'useEffect mount', 'useLayoutEffect mount', 'useEffect mount', ]); } else { - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect mount', - 'useEffect mount', - ]); + assertLog(['useLayoutEffect mount', 'useEffect mount']); } act(() => { renderer.update(); }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect mount', - 'useEffect mount', - ]); + assertLog(['useLayoutEffect mount', 'useEffect mount']); act(() => { renderer.unmount(); }); - expect(Scheduler).toHaveYielded([]); + assertLog([]); }); it('passes the right context to class component lifecycles', () => { @@ -315,13 +292,13 @@ describe('StrictEffectsMode', () => { }); if (supportsDoubleInvokeEffects()) { - expect(Scheduler).toHaveYielded([ + assertLog([ 'componentDidMount', 'componentWillUnmount', 'componentDidMount', ]); } else { - expect(Scheduler).toHaveYielded(['componentDidMount']); + assertLog(['componentDidMount']); } }); @@ -352,26 +329,26 @@ describe('StrictEffectsMode', () => { }); if (supportsDoubleInvokeEffects()) { - expect(Scheduler).toHaveYielded([ + assertLog([ 'componentDidMount', 'componentWillUnmount', 'componentDidMount', ]); } else { - expect(Scheduler).toHaveYielded(['componentDidMount']); + assertLog(['componentDidMount']); } act(() => { renderer.update(); }); - expect(Scheduler).toHaveYielded(['componentDidUpdate']); + assertLog(['componentDidUpdate']); act(() => { renderer.unmount(); }); - expect(Scheduler).toHaveYielded(['componentWillUnmount']); + assertLog(['componentWillUnmount']); }); it('should not double invoke class lifecycles in legacy mode', () => { @@ -397,7 +374,7 @@ describe('StrictEffectsMode', () => { ReactTestRenderer.create(); }); - expect(Scheduler).toHaveYielded(['componentDidMount']); + assertLog(['componentDidMount']); }); it('double flushing passive effects only results in one double invoke', () => { @@ -427,7 +404,7 @@ describe('StrictEffectsMode', () => { }); if (supportsDoubleInvokeEffects()) { - expect(Scheduler).toHaveYielded([ + assertLog([ 'mount', 'useLayoutEffect mount', 'useEffect mount', @@ -442,7 +419,7 @@ describe('StrictEffectsMode', () => { 'useEffect mount', ]); } else { - expect(Scheduler).toHaveYielded([ + assertLog([ 'mount', 'useLayoutEffect mount', 'useEffect mount', @@ -492,7 +469,7 @@ describe('StrictEffectsMode', () => { }); if (supportsDoubleInvokeEffects()) { - expect(Scheduler).toHaveYielded([ + assertLog([ 'App useLayoutEffect mount', 'App useEffect mount', 'App useLayoutEffect unmount', @@ -501,10 +478,7 @@ describe('StrictEffectsMode', () => { 'App useEffect mount', ]); } else { - expect(Scheduler).toHaveYielded([ - 'App useLayoutEffect mount', - 'App useEffect mount', - ]); + assertLog(['App useLayoutEffect mount', 'App useEffect mount']); } act(() => { @@ -512,7 +486,7 @@ describe('StrictEffectsMode', () => { }); if (supportsDoubleInvokeEffects()) { - expect(Scheduler).toHaveYielded([ + assertLog([ 'App useLayoutEffect unmount', 'Child useLayoutEffect mount', 'App useLayoutEffect mount', @@ -525,7 +499,7 @@ describe('StrictEffectsMode', () => { 'Child useEffect mount', ]); } else { - expect(Scheduler).toHaveYielded([ + assertLog([ 'App useLayoutEffect unmount', 'Child useLayoutEffect mount', 'App useLayoutEffect mount', @@ -580,7 +554,7 @@ describe('StrictEffectsMode', () => { }); if (supportsDoubleInvokeEffects()) { - expect(Scheduler).toHaveYielded([ + assertLog([ 'componentDidMount', 'useLayoutEffect mount', 'useEffect mount', @@ -592,7 +566,7 @@ describe('StrictEffectsMode', () => { 'useEffect mount', ]); } else { - expect(Scheduler).toHaveYielded([ + assertLog([ 'componentDidMount', 'useLayoutEffect mount', 'useEffect mount', @@ -603,7 +577,7 @@ describe('StrictEffectsMode', () => { renderer.update(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect unmount', 'useLayoutEffect mount', 'useEffect unmount', @@ -614,7 +588,7 @@ describe('StrictEffectsMode', () => { renderer.unmount(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'componentWillUnmount', 'useLayoutEffect unmount', 'useEffect unmount', diff --git a/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js b/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js index 7a7603d3d9283..365adea441e38 100644 --- a/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js +++ b/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js @@ -13,6 +13,10 @@ let React; let ReactNoop; let Scheduler; let act; +let assertLog; +let waitFor; +let waitForAll; +let waitForPaint; describe('StrictEffectsMode defaults', () => { beforeEach(() => { @@ -23,6 +27,12 @@ describe('StrictEffectsMode defaults', () => { Scheduler = require('scheduler'); act = require('jest-react').act; + const InternalTestUtils = require('internal-test-utils'); + waitFor = InternalTestUtils.waitFor; + waitForAll = InternalTestUtils.waitForAll; + waitForPaint = InternalTestUtils.waitForPaint; + assertLog = InternalTestUtils.assertLog; + const ReactFeatureFlags = require('shared/ReactFeatureFlags'); ReactFeatureFlags.createRootStrictEffectsByDefault = __DEV__; }); @@ -46,10 +56,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.renderLegacySyncRoot(); }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect mount', - 'useEffect mount', - ]); + assertLog(['useLayoutEffect mount', 'useEffect mount']); }); it('should not double invoke class lifecycles in legacy mode', () => { @@ -75,11 +82,11 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.renderLegacySyncRoot(); }); - expect(Scheduler).toHaveYielded(['componentDidMount']); + assertLog(['componentDidMount']); }); if (__DEV__) { - it('should flush double-invoked effects within the same frame as layout effects if there are no passive effects', () => { + it('should flush double-invoked effects within the same frame as layout effects if there are no passive effects', async () => { function ComponentWithEffects({label}) { React.useLayoutEffect(() => { Scheduler.unstable_yieldValue(`useLayoutEffect mount "${label}"`); @@ -90,21 +97,21 @@ describe('StrictEffectsMode defaults', () => { return label; } - act(() => { + await act(async () => { ReactNoop.render( <> , ); - expect(Scheduler).toFlushUntilNextPaint([ + await waitForPaint([ 'useLayoutEffect mount "one"', 'useLayoutEffect unmount "one"', 'useLayoutEffect mount "one"', ]); }); - act(() => { + await act(async () => { ReactNoop.render( <> @@ -112,8 +119,8 @@ describe('StrictEffectsMode defaults', () => { , ); - expect(Scheduler).toHaveYielded([]); - expect(Scheduler).toFlushUntilNextPaint([ + assertLog([]); + await waitForPaint([ // Cleanup and re-run "one" (and "two") since there is no dependencies array. 'useLayoutEffect unmount "one"', 'useLayoutEffect mount "one"', @@ -128,7 +135,7 @@ describe('StrictEffectsMode defaults', () => { // This test also verifies that double-invoked effects flush synchronously // within the same frame as passive effects. - it('should double invoke effects only for newly mounted components', () => { + it('should double invoke effects only for newly mounted components', async () => { function ComponentWithEffects({label}) { React.useEffect(() => { Scheduler.unstable_yieldValue(`useEffect mount "${label}"`); @@ -145,14 +152,14 @@ describe('StrictEffectsMode defaults', () => { return label; } - act(() => { + await act(async () => { ReactNoop.render( <> , ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'useLayoutEffect mount "one"', 'useEffect mount "one"', 'useLayoutEffect unmount "one"', @@ -162,7 +169,7 @@ describe('StrictEffectsMode defaults', () => { ]); }); - act(() => { + await act(async () => { ReactNoop.render( <> @@ -170,13 +177,13 @@ describe('StrictEffectsMode defaults', () => { , ); - expect(Scheduler).toFlushAndYieldThrough([ + await waitFor([ // Cleanup and re-run "one" (and "two") since there is no dependencies array. 'useLayoutEffect unmount "one"', 'useLayoutEffect mount "one"', 'useLayoutEffect mount "two"', ]); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'useEffect unmount "one"', 'useEffect mount "one"', 'useEffect mount "two"', @@ -208,7 +215,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect mount', 'useEffect mount', 'useLayoutEffect unmount', @@ -221,7 +228,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect unmount', 'useLayoutEffect mount', 'useEffect unmount', @@ -232,10 +239,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(null); }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect unmount', - 'useEffect unmount', - ]); + assertLog(['useLayoutEffect unmount', 'useEffect unmount']); }); it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => { @@ -257,7 +261,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useEffect One mount', 'useEffect Two mount', 'useEffect One unmount', @@ -270,7 +274,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useEffect One unmount', 'useEffect Two unmount', 'useEffect One mount', @@ -281,10 +285,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(null); }); - expect(Scheduler).toHaveYielded([ - 'useEffect One unmount', - 'useEffect Two unmount', - ]); + assertLog(['useEffect One unmount', 'useEffect Two unmount']); }); it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => { @@ -308,7 +309,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect One mount', 'useLayoutEffect Two mount', 'useLayoutEffect One unmount', @@ -321,7 +322,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect One unmount', 'useLayoutEffect Two unmount', 'useLayoutEffect One mount', @@ -332,10 +333,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(null); }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect One unmount', - 'useLayoutEffect Two unmount', - ]); + assertLog(['useLayoutEffect One unmount', 'useLayoutEffect Two unmount']); }); it('useEffect and useLayoutEffect is called twice when there is no unmount', () => { @@ -355,7 +353,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect mount', 'useEffect mount', 'useLayoutEffect mount', @@ -366,16 +364,13 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect mount', - 'useEffect mount', - ]); + assertLog(['useLayoutEffect mount', 'useEffect mount']); act(() => { ReactNoop.render(null); }); - expect(Scheduler).toHaveYielded([]); + assertLog([]); }); //@gate useModernStrictMode @@ -430,7 +425,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'componentDidMount', 'componentWillUnmount', 'componentDidMount', @@ -460,7 +455,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'componentDidMount', 'componentWillUnmount', 'componentDidMount', @@ -470,13 +465,13 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded(['componentDidUpdate']); + assertLog(['componentDidUpdate']); act(() => { ReactNoop.render(null); }); - expect(Scheduler).toHaveYielded(['componentWillUnmount']); + assertLog(['componentWillUnmount']); }); it('double flushing passive effects only results in one double invoke', () => { @@ -503,7 +498,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'mount', 'useLayoutEffect mount', 'useEffect mount', @@ -555,7 +550,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'App useLayoutEffect mount', 'App useEffect mount', 'App useLayoutEffect unmount', @@ -568,7 +563,7 @@ describe('StrictEffectsMode defaults', () => { _setShowChild(true); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'App useLayoutEffect unmount', 'Child useLayoutEffect mount', 'App useLayoutEffect mount', @@ -622,7 +617,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'componentDidMount', 'useLayoutEffect mount', 'useEffect mount', @@ -638,7 +633,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'useLayoutEffect unmount', 'useLayoutEffect mount', 'useEffect unmount', @@ -649,7 +644,7 @@ describe('StrictEffectsMode defaults', () => { ReactNoop.render(null); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'componentWillUnmount', 'useLayoutEffect unmount', 'useEffect unmount', diff --git a/packages/react-reconciler/src/__tests__/useEffectEvent-test.js b/packages/react-reconciler/src/__tests__/useEffectEvent-test.js index c2f58292deb65..3a470a7a465cc 100644 --- a/packages/react-reconciler/src/__tests__/useEffectEvent-test.js +++ b/packages/react-reconciler/src/__tests__/useEffectEvent-test.js @@ -26,6 +26,8 @@ describe('useEffectEvent', () => { let useEffect; let useLayoutEffect; let useMemo; + let waitForAll; + let assertLog; beforeEach(() => { React = require('react'); @@ -40,6 +42,10 @@ describe('useEffectEvent', () => { useEffect = React.useEffect; useLayoutEffect = React.useLayoutEffect; useMemo = React.useMemo; + + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + assertLog = InternalTestUtils.assertLog; }); function Text(props) { @@ -48,7 +54,7 @@ describe('useEffectEvent', () => { } // @gate enableUseEffectEventHook - it('memoizes basic case correctly', () => { + it('memoizes basic case correctly', async () => { class IncrementButton extends React.PureComponent { increment = () => { this.props.onClick(); @@ -72,7 +78,7 @@ describe('useEffectEvent', () => { const button = React.createRef(null); ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Increment', 'Count: 0']); + await waitForAll(['Increment', 'Count: 0']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -81,7 +87,7 @@ describe('useEffectEvent', () => { ); act(button.current.increment); - expect(Scheduler).toHaveYielded(['Increment', 'Count: 1']); + assertLog(['Increment', 'Count: 1']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -90,7 +96,7 @@ describe('useEffectEvent', () => { ); act(button.current.increment); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Increment', // Event should use the updated callback function closed over the new value. 'Count: 2', @@ -104,7 +110,7 @@ describe('useEffectEvent', () => { // Increase the increment prop amount ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Increment', 'Count: 2']); + await waitForAll(['Increment', 'Count: 2']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -114,7 +120,7 @@ describe('useEffectEvent', () => { // Event uses the new prop act(button.current.increment); - expect(Scheduler).toHaveYielded(['Increment', 'Count: 12']); + assertLog(['Increment', 'Count: 12']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -124,7 +130,7 @@ describe('useEffectEvent', () => { }); // @gate enableUseEffectEventHook - it('can be defined more than once', () => { + it('can be defined more than once', async () => { class IncrementButton extends React.PureComponent { increment = () => { this.props.onClick(); @@ -158,7 +164,7 @@ describe('useEffectEvent', () => { const button = React.createRef(null); ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Increment', 'Count: 0']); + await waitForAll(['Increment', 'Count: 0']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -167,7 +173,7 @@ describe('useEffectEvent', () => { ); act(button.current.increment); - expect(Scheduler).toHaveYielded(['Increment', 'Count: 5']); + assertLog(['Increment', 'Count: 5']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -176,7 +182,7 @@ describe('useEffectEvent', () => { ); act(button.current.multiply); - expect(Scheduler).toHaveYielded(['Increment', 'Count: 25']); + assertLog(['Increment', 'Count: 25']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -186,7 +192,7 @@ describe('useEffectEvent', () => { }); // @gate enableUseEffectEventHook - it('does not preserve `this` in event functions', () => { + it('does not preserve `this` in event functions', async () => { class GreetButton extends React.PureComponent { greet = () => { this.props.onClick(); @@ -217,7 +223,7 @@ describe('useEffectEvent', () => { const button = React.createRef(null); ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Say hej', 'Greeting: Seb says hej']); + await waitForAll(['Say hej', 'Greeting: Seb says hej']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -226,10 +232,7 @@ describe('useEffectEvent', () => { ); act(button.current.greet); - expect(Scheduler).toHaveYielded([ - 'Say hej', - 'Greeting: undefined says hej', - ]); + assertLog(['Say hej', 'Greeting: undefined says hej']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -272,11 +275,11 @@ describe('useEffectEvent', () => { // If something throws, we try one more time synchronously in case the error was // caused by a data race. See recoverFromConcurrentError - expect(Scheduler).toHaveYielded(['Count: 0', 'Count: 0']); + assertLog(['Count: 0', 'Count: 0']); }); // @gate enableUseEffectEventHook - it("useLayoutEffect shouldn't re-fire when event handlers change", () => { + it("useLayoutEffect shouldn't re-fire when event handlers change", async () => { class IncrementButton extends React.PureComponent { increment = () => { this.props.onClick(); @@ -307,8 +310,8 @@ describe('useEffectEvent', () => { const button = React.createRef(null); ReactNoop.render(); - expect(Scheduler).toHaveYielded([]); - expect(Scheduler).toFlushAndYield([ + assertLog([]); + await waitForAll([ 'Increment', 'Count: 0', 'Effect: by 2', @@ -323,7 +326,7 @@ describe('useEffectEvent', () => { ); act(button.current.increment); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Increment', // Effect should not re-run because the dependency hasn't changed. 'Count: 3', @@ -336,7 +339,7 @@ describe('useEffectEvent', () => { ); act(button.current.increment); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Increment', // Event should use the updated callback function closed over the new value. 'Count: 4', @@ -350,7 +353,7 @@ describe('useEffectEvent', () => { // Increase the increment prop amount ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Increment', 'Count: 4', 'Effect: by 20', @@ -366,7 +369,7 @@ describe('useEffectEvent', () => { // Event uses the new prop act(button.current.increment); - expect(Scheduler).toHaveYielded(['Increment', 'Count: 34']); + assertLog(['Increment', 'Count: 34']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -376,7 +379,7 @@ describe('useEffectEvent', () => { }); // @gate enableUseEffectEventHook - it("useEffect shouldn't re-fire when event handlers change", () => { + it("useEffect shouldn't re-fire when event handlers change", async () => { class IncrementButton extends React.PureComponent { increment = () => { this.props.onClick(); @@ -407,7 +410,7 @@ describe('useEffectEvent', () => { const button = React.createRef(null); ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Increment', 'Count: 0', 'Effect: by 2', @@ -422,7 +425,7 @@ describe('useEffectEvent', () => { ); act(button.current.increment); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Increment', // Effect should not re-run because the dependency hasn't changed. 'Count: 3', @@ -435,7 +438,7 @@ describe('useEffectEvent', () => { ); act(button.current.increment); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Increment', // Event should use the updated callback function closed over the new value. 'Count: 4', @@ -449,7 +452,7 @@ describe('useEffectEvent', () => { // Increase the increment prop amount ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Increment', 'Count: 4', 'Effect: by 20', @@ -465,7 +468,7 @@ describe('useEffectEvent', () => { // Event uses the new prop act(button.current.increment); - expect(Scheduler).toHaveYielded(['Increment', 'Count: 34']); + assertLog(['Increment', 'Count: 34']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -475,7 +478,7 @@ describe('useEffectEvent', () => { }); // @gate enableUseEffectEventHook - it('is stable in a custom hook', () => { + it('is stable in a custom hook', async () => { class IncrementButton extends React.PureComponent { increment = () => { this.props.onClick(); @@ -512,7 +515,7 @@ describe('useEffectEvent', () => { const button = React.createRef(null); ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Increment', 'Count: 0', 'Effect: by 2', @@ -527,7 +530,7 @@ describe('useEffectEvent', () => { ); act(button.current.increment); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Increment', // Effect should not re-run because the dependency hasn't changed. 'Count: 3', @@ -540,7 +543,7 @@ describe('useEffectEvent', () => { ); act(button.current.increment); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Increment', // Event should use the updated callback function closed over the new value. 'Count: 4', @@ -554,7 +557,7 @@ describe('useEffectEvent', () => { // Increase the increment prop amount ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Increment', 'Count: 4', 'Effect: by 20', @@ -570,7 +573,7 @@ describe('useEffectEvent', () => { // Event uses the new prop act(button.current.increment); - expect(Scheduler).toHaveYielded(['Increment', 'Count: 34']); + assertLog(['Increment', 'Count: 34']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -580,7 +583,7 @@ describe('useEffectEvent', () => { }); // @gate enableUseEffectEventHook - it('is mutated before all other effects', () => { + it('is mutated before all other effects', async () => { function Counter({value}) { useInsertionEffect(() => { Scheduler.unstable_yieldValue('Effect value: ' + value); @@ -597,14 +600,14 @@ describe('useEffectEvent', () => { } ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Effect value: 1', 'Event value: 1']); + await waitForAll(['Effect value: 1', 'Event value: 1']); act(() => ReactNoop.render()); - expect(Scheduler).toHaveYielded(['Effect value: 2', 'Event value: 2']); + assertLog(['Effect value: 2', 'Event value: 2']); }); // @gate enableUseEffectEventHook - it("doesn't provide a stable identity", () => { + it("doesn't provide a stable identity", async () => { function Counter({shouldRender, value}) { const onClick = useEffectEvent(() => { Scheduler.unstable_yieldValue( @@ -627,16 +630,16 @@ describe('useEffectEvent', () => { } ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'onClick, shouldRender=true, value=0', 'onClick, shouldRender=true, value=0', ]); ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['onClick, shouldRender=true, value=1']); + await waitForAll(['onClick, shouldRender=true, value=1']); ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'onClick, shouldRender=false, value=2', 'onClick, shouldRender=false, value=2', ]); @@ -676,7 +679,7 @@ describe('useEffectEvent', () => { await act(async () => { root.render(); }); - expect(Scheduler).toHaveYielded(['Commit new event handler']); + assertLog(['Commit new event handler']); expect(root).toMatchRenderedOutput('Latest rendered value 1'); expect(committedEventHandler()).toBe('Value seen by useEffectEvent: 1'); @@ -686,14 +689,14 @@ describe('useEffectEvent', () => { }); // No new event handler should be committed, because it was omitted from // the dependency array. - expect(Scheduler).toHaveYielded([]); + assertLog([]); // But the event handler should still be able to see the latest value. expect(root).toMatchRenderedOutput('Latest rendered value 2'); expect(committedEventHandler()).toBe('Value seen by useEffectEvent: 2'); }); // @gate enableUseEffectEventHook - it('integration: implements docs chat room example', () => { + it('integration: implements docs chat room example', async () => { function createConnection() { let connectedCallback; let timeout; @@ -738,47 +741,47 @@ describe('useEffectEvent', () => { } act(() => ReactNoop.render()); - expect(Scheduler).toHaveYielded(['Welcome to the general room!']); + assertLog(['Welcome to the general room!']); expect(ReactNoop).toMatchRenderedOutput( , ); jest.advanceTimersByTime(100); Scheduler.unstable_advanceTime(100); - expect(Scheduler).toHaveYielded(['Connected! theme: light']); + assertLog(['Connected! theme: light']); // change roomId only act(() => ReactNoop.render()); - expect(Scheduler).toHaveYielded(['Welcome to the music room!']); + assertLog(['Welcome to the music room!']); expect(ReactNoop).toMatchRenderedOutput( , ); jest.advanceTimersByTime(100); Scheduler.unstable_advanceTime(100); // should trigger a reconnect - expect(Scheduler).toHaveYielded(['Connected! theme: light']); + assertLog(['Connected! theme: light']); // change theme only act(() => ReactNoop.render()); - expect(Scheduler).toHaveYielded(['Welcome to the music room!']); + assertLog(['Welcome to the music room!']); expect(ReactNoop).toMatchRenderedOutput( , ); jest.advanceTimersByTime(100); Scheduler.unstable_advanceTime(100); // should not trigger a reconnect - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); // change roomId only act(() => ReactNoop.render()); - expect(Scheduler).toHaveYielded(['Welcome to the travel room!']); + assertLog(['Welcome to the travel room!']); expect(ReactNoop).toMatchRenderedOutput( , ); jest.advanceTimersByTime(100); Scheduler.unstable_advanceTime(100); // should trigger a reconnect - expect(Scheduler).toHaveYielded(['Connected! theme: dark']); + assertLog(['Connected! theme: dark']); }); // @gate enableUseEffectEventHook @@ -837,12 +840,9 @@ describe('useEffectEvent', () => { , ), ); - expect(Scheduler).toHaveYielded([ - 'Add to cart', - 'url: /shop/1, numberOfItems: 0', - ]); + assertLog(['Add to cart', 'url: /shop/1, numberOfItems: 0']); act(button.current.addToCart); - expect(Scheduler).toHaveYielded(['Add to cart']); + assertLog(['Add to cart']); act(() => ReactNoop.render( @@ -851,9 +851,6 @@ describe('useEffectEvent', () => { , ), ); - expect(Scheduler).toHaveYielded([ - 'Add to cart', - 'url: /shop/2, numberOfItems: 1', - ]); + assertLog(['Add to cart', 'url: /shop/2, numberOfItems: 1']); }); });