diff --git a/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js b/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js index 296bfd5472464..303516013b6d0 100644 --- a/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js +++ b/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js @@ -18,6 +18,8 @@ let Scheduler; let Suspense; let TextResource; let textResourceShouldFail; +let waitForAll; +let assertLog; describe('ReactCache', () => { beforeEach(() => { @@ -33,6 +35,10 @@ describe('ReactCache', () => { ReactTestRenderer = require('react-test-renderer'); Scheduler = require('scheduler'); + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + assertLog = InternalTestUtils.assertLog; + TextResource = createResource( ([text, ms = 0]) => { let listeners = null; @@ -105,7 +111,7 @@ describe('ReactCache', () => { } } - it('throws a promise if the requested value is not in the cache', () => { + it('throws a promise if the requested value is not in the cache', async () => { function App() { return ( }> @@ -118,11 +124,11 @@ describe('ReactCache', () => { unstable_isConcurrent: true, }); - expect(Scheduler).toFlushAndYield(['Suspend! [Hi]', 'Loading...']); + await waitForAll(['Suspend! [Hi]', 'Loading...']); jest.advanceTimersByTime(100); - expect(Scheduler).toHaveYielded(['Promise resolved [Hi]']); - expect(Scheduler).toFlushAndYield(['Hi']); + assertLog(['Promise resolved [Hi]']); + await waitForAll(['Hi']); }); it('throws an error on the subsequent read if the promise is rejected', async () => { @@ -138,22 +144,22 @@ describe('ReactCache', () => { unstable_isConcurrent: true, }); - expect(Scheduler).toFlushAndYield(['Suspend! [Hi]', 'Loading...']); + await waitForAll(['Suspend! [Hi]', 'Loading...']); textResourceShouldFail = true; jest.advanceTimersByTime(100); - expect(Scheduler).toHaveYielded(['Promise rejected [Hi]']); + assertLog(['Promise rejected [Hi]']); expect(Scheduler).toFlushAndThrow('Failed to load: Hi'); - expect(Scheduler).toHaveYielded(['Error! [Hi]', 'Error! [Hi]']); + assertLog(['Error! [Hi]', 'Error! [Hi]']); // Should throw again on a subsequent read root.update(); expect(Scheduler).toFlushAndThrow('Failed to load: Hi'); - expect(Scheduler).toHaveYielded(['Error! [Hi]', 'Error! [Hi]']); + assertLog(['Error! [Hi]', 'Error! [Hi]']); }); - it('warns if non-primitive key is passed to a resource without a hash function', () => { + it('warns if non-primitive key is passed to a resource without a hash function', async () => { const BadTextResource = createResource(([text, ms = 0]) => { return new Promise((resolve, reject) => setTimeout(() => { @@ -177,8 +183,8 @@ describe('ReactCache', () => { ); if (__DEV__) { - expect(() => { - expect(Scheduler).toFlushAndYield(['App', 'Loading...']); + expect(async () => { + await waitForAll(['App', 'Loading...']); }).toErrorDev([ 'Invalid key type. Expected a string, number, symbol, or ' + 'boolean, but instead received: Hi,100\n\n' + @@ -186,7 +192,7 @@ describe('ReactCache', () => { 'function as the second argument to createResource().', ]); } else { - expect(Scheduler).toFlushAndYield(['App', 'Loading...']); + await waitForAll(['App', 'Loading...']); } }); @@ -204,19 +210,19 @@ describe('ReactCache', () => { unstable_isConcurrent: true, }, ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Suspend! [1]', 'Suspend! [2]', 'Suspend! [3]', 'Loading...', ]); jest.advanceTimersByTime(100); - expect(Scheduler).toHaveYielded([ + assertLog([ 'Promise resolved [1]', 'Promise resolved [2]', 'Promise resolved [3]', ]); - expect(Scheduler).toFlushAndYield([1, 2, 3]); + await waitForAll([1, 2, 3]); expect(root).toMatchRenderedOutput('123'); // Render 1, 4, 5 @@ -228,18 +234,10 @@ describe('ReactCache', () => { , ); - expect(Scheduler).toFlushAndYield([ - 1, - 'Suspend! [4]', - 'Suspend! [5]', - 'Loading...', - ]); + await waitForAll([1, 'Suspend! [4]', 'Suspend! [5]', 'Loading...']); jest.advanceTimersByTime(100); - expect(Scheduler).toHaveYielded([ - 'Promise resolved [4]', - 'Promise resolved [5]', - ]); - expect(Scheduler).toFlushAndYield([1, 4, 5]); + assertLog(['Promise resolved [4]', 'Promise resolved [5]']); + await waitForAll([1, 4, 5]); expect(root).toMatchRenderedOutput('145'); // We've now rendered values 1, 2, 3, 4, 5, over our limit of 3. The least @@ -253,7 +251,7 @@ describe('ReactCache', () => { , ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ // 1 is still cached 1, // 2 and 3 suspend because they were evicted from the cache @@ -262,11 +260,8 @@ describe('ReactCache', () => { 'Loading...', ]); jest.advanceTimersByTime(100); - expect(Scheduler).toHaveYielded([ - 'Promise resolved [2]', - 'Promise resolved [3]', - ]); - expect(Scheduler).toFlushAndYield([1, 2, 3]); + assertLog(['Promise resolved [2]', 'Promise resolved [3]']); + await waitForAll([1, 2, 3]); expect(root).toMatchRenderedOutput('123'); }); @@ -287,18 +282,15 @@ describe('ReactCache', () => { }, ); - expect(Scheduler).toFlushAndYield(['Loading...']); + await waitForAll(['Loading...']); jest.advanceTimersByTime(1000); - expect(Scheduler).toHaveYielded([ - 'Promise resolved [B]', - 'Promise resolved [A]', - ]); - expect(Scheduler).toFlushAndYield(['Result']); + assertLog(['Promise resolved [B]', 'Promise resolved [A]']); + await waitForAll(['Result']); expect(root).toMatchRenderedOutput('Result'); }); - it('if a thenable resolves multiple times, does not update the first cached value', () => { + it('if a thenable resolves multiple times, does not update the first cached value', async () => { let resolveThenable; const BadTextResource = createResource( ([text, ms = 0]) => { @@ -349,7 +341,7 @@ describe('ReactCache', () => { }, ); - expect(Scheduler).toFlushAndYield(['Suspend! [Hi]', 'Loading...']); + await waitForAll(['Suspend! [Hi]', 'Loading...']); resolveThenable('Hi'); // This thenable improperly resolves twice. We should not update the @@ -365,8 +357,8 @@ describe('ReactCache', () => { }, ); - expect(Scheduler).toHaveYielded([]); - expect(Scheduler).toFlushAndYield(['Hi']); + assertLog([]); + await waitForAll(['Hi']); expect(root).toMatchRenderedOutput('Hi'); }); diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index d7cec67f5bfa9..9495b476d6837 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -20,6 +20,7 @@ let ReactNoopFlightClient; let ErrorBoundary; let NoErrorExpected; let Scheduler; +let assertLog; describe('ReactFlight', () => { beforeEach(() => { @@ -33,6 +34,8 @@ describe('ReactFlight', () => { ReactNoopFlightClient = require('react-noop-renderer/flight-client'); act = require('jest-react').act; Scheduler = require('scheduler'); + const InternalTestUtils = require('internal-test-utils'); + assertLog = InternalTestUtils.assertLog; ErrorBoundary = class extends React.Component { state = {hasError: false, error: null}; @@ -808,13 +811,13 @@ describe('ReactFlight', () => { const ClientDoublerModuleRef = clientReference(ClientDoubler); const transport = ReactNoopFlightServer.render(); - expect(Scheduler).toHaveYielded([]); + assertLog([]); await act(async () => { ReactNoop.render(await ReactNoopFlightClient.read(transport)); }); - expect(Scheduler).toHaveYielded(['ClientDoubler']); + assertLog(['ClientDoubler']); expect(ReactNoop).toMatchRenderedOutput( <>
:S1:
@@ -997,7 +1000,7 @@ describe('ReactFlight', () => { const transport = ReactNoopFlightServer.render(); - expect(Scheduler).toHaveYielded(['suspended']); + assertLog(['suspended']); await act(async () => { resolve(); @@ -1005,7 +1008,7 @@ describe('ReactFlight', () => { jest.runAllImmediates(); }); - expect(Scheduler).toHaveYielded(['rendered']); + assertLog(['rendered']); await act(async () => { ServerContext._currentRenderer = null; @@ -1045,7 +1048,7 @@ describe('ReactFlight', () => { const transport = ReactNoopFlightServer.render(model); - expect(Scheduler).toHaveYielded([]); + assertLog([]); await act(async () => { ServerContext._currentRenderer = null; @@ -1054,7 +1057,7 @@ describe('ReactFlight', () => { ReactNoop.render(flightModel.foo); }); - expect(Scheduler).toHaveYielded(['ClientBar']); + assertLog(['ClientBar']); expect(ReactNoop).toMatchRenderedOutput(hi this is server); expect(() => { diff --git a/packages/react-debug-tools/src/__tests__/ReactDevToolsHooksIntegration-test.js b/packages/react-debug-tools/src/__tests__/ReactDevToolsHooksIntegration-test.js index 71b5657cabcce..4497c01e740a8 100644 --- a/packages/react-debug-tools/src/__tests__/ReactDevToolsHooksIntegration-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactDevToolsHooksIntegration-test.js @@ -19,6 +19,7 @@ describe('React hooks DevTools integration', () => { let overrideHookState; let scheduleUpdate; let setSuspenseHandler; + let waitForAll; global.IS_REACT_ACT_ENVIRONMENT = true; @@ -41,6 +42,9 @@ describe('React hooks DevTools integration', () => { ReactTestRenderer = require('react-test-renderer'); Scheduler = require('scheduler'); + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + act = ReactTestRenderer.act; }); @@ -256,7 +260,7 @@ describe('React hooks DevTools integration', () => { ), ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); // Ensure we timeout any suspense time. jest.advanceTimersByTime(1000); const fiber = renderer.root._currentFiber().child; diff --git a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js index 72f112cf5a5af..65e35c03d3b19 100644 --- a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js +++ b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js @@ -20,6 +20,10 @@ describe('Timeline profiler', () => { let store; let unmountFns; let utils; + let waitFor; + let waitForAll; + let waitForPaint; + let assertLog; beforeEach(() => { utils = require('./utils'); @@ -44,6 +48,12 @@ describe('Timeline profiler', () => { ReactDOMClient = require('react-dom/client'); Scheduler = require('scheduler'); + const InternalTestUtils = require('internal-test-utils'); + waitFor = InternalTestUtils.waitFor; + waitForAll = InternalTestUtils.waitForAll; + waitForPaint = InternalTestUtils.waitForPaint; + assertLog = InternalTestUtils.assertLog; + store = global.store; }); @@ -151,7 +161,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark concurrent render without suspends or state updates', () => { + it('should mark concurrent render without suspends or state updates', async () => { renderRootHelper(
); expect(clearedMarks).toMatchInlineSnapshot(` @@ -162,7 +172,7 @@ describe('Timeline profiler', () => { clearPendingMarks(); - expect(Scheduler).toFlushUntilNextPaint([]); + await waitForPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -196,8 +206,7 @@ describe('Timeline profiler', () => { renderRootHelper(); }); - // Do one step of work. - expect(Scheduler).toFlushAndYieldThrough(['Foo']); + await waitFor(['Foo']); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -291,7 +300,11 @@ describe('Timeline profiler', () => { }); it('should mark concurrent render with suspense that resolves', async () => { - const fakeSuspensePromise = Promise.resolve(true); + let resolveFakePromise; + const fakeSuspensePromise = new Promise( + resolve => (resolveFakePromise = resolve), + ); + function Example() { throw fakeSuspensePromise; } @@ -310,7 +323,7 @@ describe('Timeline profiler', () => { clearPendingMarks(); - expect(Scheduler).toFlushUntilNextPaint([]); + await waitForPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -333,7 +346,7 @@ describe('Timeline profiler', () => { clearPendingMarks(); - await fakeSuspensePromise; + await resolveFakePromise(); expect(clearedMarks).toMatchInlineSnapshot(` [ "--suspense-resolved-0-Example", @@ -342,7 +355,11 @@ describe('Timeline profiler', () => { }); it('should mark concurrent render with suspense that rejects', async () => { - const fakeSuspensePromise = Promise.reject(new Error('error')); + let rejectFakePromise; + const fakeSuspensePromise = new Promise( + (_, reject) => (rejectFakePromise = reject), + ); + function Example() { throw fakeSuspensePromise; } @@ -361,7 +378,7 @@ describe('Timeline profiler', () => { clearPendingMarks(); - expect(Scheduler).toFlushUntilNextPaint([]); + await waitForPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -384,7 +401,10 @@ describe('Timeline profiler', () => { clearPendingMarks(); - await expect(fakeSuspensePromise).rejects.toThrow(); + await expect(() => { + rejectFakePromise(new Error('error')); + return fakeSuspensePromise; + }).rejects.toThrow(); expect(clearedMarks).toMatchInlineSnapshot(` [ "--suspense-rejected-0-Example", @@ -392,7 +412,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark cascading class component state updates', () => { + it('should mark cascading class component state updates', async () => { class Example extends React.Component { state = {didMount: false}; componentDidMount() { @@ -413,7 +433,7 @@ describe('Timeline profiler', () => { clearPendingMarks(); - expect(Scheduler).toFlushUntilNextPaint([]); + await waitForPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -446,7 +466,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark cascading class component force updates', () => { + it('should mark cascading class component force updates', async () => { class Example extends React.Component { componentDidMount() { this.forceUpdate(); @@ -466,7 +486,7 @@ describe('Timeline profiler', () => { clearPendingMarks(); - expect(Scheduler).toFlushUntilNextPaint([]); + await waitForPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -499,7 +519,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark render phase state updates for class component', () => { + it('should mark render phase state updates for class component', async () => { class Example extends React.Component { state = {didRender: false}; render() { @@ -525,7 +545,7 @@ describe('Timeline profiler', () => { errorMessage = message; }); - expect(Scheduler).toFlushUntilNextPaint([]); + await waitForPaint([]); expect(console.error).toHaveBeenCalledTimes(1); expect(errorMessage).toContain( @@ -552,7 +572,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark render phase force updates for class component', () => { + it('should mark render phase force updates for class component', async () => { let forced = false; class Example extends React.Component { render() { @@ -579,7 +599,7 @@ describe('Timeline profiler', () => { errorMessage = message; }); - expect(Scheduler).toFlushUntilNextPaint([]); + await waitForPaint([]); expect(console.error).toHaveBeenCalledTimes(1); expect(errorMessage).toContain( @@ -606,7 +626,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark cascading layout updates', () => { + it('should mark cascading layout updates', async () => { function Example() { const [didMount, setDidMount] = React.useState(false); React.useLayoutEffect(() => { @@ -625,7 +645,7 @@ describe('Timeline profiler', () => { clearPendingMarks(); - expect(Scheduler).toFlushUntilNextPaint([]); + await waitForPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -660,7 +680,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark cascading passive updates', () => { + it('should mark cascading passive updates', async () => { function Example() { const [didMount, setDidMount] = React.useState(false); React.useEffect(() => { @@ -671,7 +691,7 @@ describe('Timeline profiler', () => { renderRootHelper(); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -709,7 +729,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark render phase updates', () => { + it('should mark render phase updates', async () => { function Example() { const [didRender, setDidRender] = React.useState(false); if (!didRender) { @@ -720,7 +740,7 @@ describe('Timeline profiler', () => { renderRootHelper(); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -840,7 +860,7 @@ describe('Timeline profiler', () => { clearPendingMarks(); - expect(Scheduler).toFlushUntilNextPaint([]); + await waitForPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -927,10 +947,7 @@ describe('Timeline profiler', () => { const unmount = renderRootHelper(); - expect(Scheduler).toFlushUntilNextPaint([ - 'layout 1 mount', - 'layout 2 mount', - ]); + await waitForPaint(['layout 1 mount', 'layout 2 mount']); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -957,7 +974,7 @@ describe('Timeline profiler', () => { clearPendingMarks(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'passive 1 mount', 'passive 2 mount', 'passive 3 mount', @@ -978,11 +995,11 @@ describe('Timeline profiler', () => { clearPendingMarks(); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); unmount(); - expect(Scheduler).toHaveYielded([ + assertLog([ 'layout 1 unmount', 'layout 2 unmount', 'passive 1 unmount', @@ -1063,7 +1080,7 @@ describe('Timeline profiler', () => { } renderRootHelper(); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); clearedMarks.splice(0); @@ -1102,7 +1119,7 @@ describe('Timeline profiler', () => { } renderRootHelper(); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); clearedMarks.splice(0); @@ -1110,7 +1127,7 @@ describe('Timeline profiler', () => { event.initEvent('mouseover', true, true); dispatchAndSetCurrentEvent(targetRef.current, event); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(clearedMarks).toMatchInlineSnapshot(` [ @@ -1324,11 +1341,11 @@ describe('Timeline profiler', () => { }); // Do one step of work. - expect(Scheduler).toFlushAndYieldThrough(['Foo']); + await waitFor(['Foo']); // Finish flushing so React commits; // Unless we do this, the ProfilerStore won't collect Profiling data. - expect(Scheduler).toFlushAndYield(['Bar']); + await waitForAll(['Bar']); // Since we yielded, the batch should report two separate "render" chunks. const batch = getBatchOfWork(0); @@ -1359,13 +1376,13 @@ describe('Timeline profiler', () => { , ); - expect(Scheduler).toHaveYielded(['suspended']); + assertLog(['suspended']); Scheduler.unstable_advanceTime(10); resolveFn(); await suspensePromise; - expect(Scheduler).toFlushAndYield(['resolved']); + await waitForAll(['resolved']); const timelineData = stopProfilingAndGetTimelineData(); @@ -1417,13 +1434,13 @@ describe('Timeline profiler', () => { , ); - expect(Scheduler).toHaveYielded(['suspended']); + assertLog(['suspended']); Scheduler.unstable_advanceTime(10); rejectFn(); await expect(suspensePromise).rejects.toThrow(); - expect(Scheduler).toHaveYielded(['rejected']); + assertLog(['rejected']); const timelineData = stopProfilingAndGetTimelineData(); @@ -1475,13 +1492,13 @@ describe('Timeline profiler', () => { , ); - expect(Scheduler).toFlushAndYield(['suspended']); + await waitForAll(['suspended']); Scheduler.unstable_advanceTime(10); resolveFn(); await suspensePromise; - expect(Scheduler).toFlushAndYield(['resolved']); + await waitForAll(['resolved']); const timelineData = stopProfilingAndGetTimelineData(); @@ -1533,13 +1550,13 @@ describe('Timeline profiler', () => { , ); - expect(Scheduler).toFlushAndYield(['suspended']); + await waitForAll(['suspended']); Scheduler.unstable_advanceTime(10); rejectFn(); await expect(suspensePromise).rejects.toThrow(); - expect(Scheduler).toFlushAndYield(['rejected']); + await waitForAll(['rejected']); const timelineData = stopProfilingAndGetTimelineData(); @@ -1566,7 +1583,7 @@ describe('Timeline profiler', () => { expect(timelineData.componentMeasures).toHaveLength(2); }); - it('should mark cascading class component state updates', () => { + it('should mark cascading class component state updates', async () => { class Example extends React.Component { state = {didMount: false}; componentDidMount() { @@ -1583,7 +1600,7 @@ describe('Timeline profiler', () => { renderRootHelper(); - expect(Scheduler).toFlushUntilNextPaint(['mount', 'update']); + await waitForPaint(['mount', 'update']); const timelineData = stopProfilingAndGetTimelineData(); expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); @@ -1626,7 +1643,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark cascading class component force updates', () => { + it('should mark cascading class component force updates', async () => { let forced = false; class Example extends React.Component { componentDidMount() { @@ -1642,7 +1659,7 @@ describe('Timeline profiler', () => { renderRootHelper(); - expect(Scheduler).toFlushUntilNextPaint(['mount', 'force update']); + await waitForPaint(['mount', 'force update']); const timelineData = stopProfilingAndGetTimelineData(); expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); @@ -1683,7 +1700,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark render phase state updates for class component', () => { + it('should mark render phase state updates for class component', async () => { class Example extends React.Component { state = {didRender: false}; render() { @@ -1705,7 +1722,7 @@ describe('Timeline profiler', () => { errorMessage = message; }); - expect(Scheduler).toFlushAndYield(['first render', 'second render']); + await waitForAll(['first render', 'second render']); expect(console.error).toHaveBeenCalledTimes(1); expect(errorMessage).toContain( @@ -1753,7 +1770,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark render phase force updates for class component', () => { + it('should mark render phase force updates for class component', async () => { let forced = false; class Example extends React.Component { render() { @@ -1774,7 +1791,7 @@ describe('Timeline profiler', () => { errorMessage = message; }); - expect(Scheduler).toFlushAndYield(['render', 'force update']); + await waitForAll(['render', 'force update']); expect(console.error).toHaveBeenCalledTimes(1); expect(errorMessage).toContain( @@ -1820,7 +1837,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark cascading layout updates', () => { + it('should mark cascading layout updates', async () => { function Example() { const [didMount, setDidMount] = React.useState(false); React.useLayoutEffect(() => { @@ -1834,7 +1851,7 @@ describe('Timeline profiler', () => { renderRootHelper(); - expect(Scheduler).toFlushAndYield(['mount', 'update']); + await waitForAll(['mount', 'update']); const timelineData = stopProfilingAndGetTimelineData(); expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); @@ -1884,7 +1901,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark cascading passive updates', () => { + it('should mark cascading passive updates', async () => { function Example() { const [didMount, setDidMount] = React.useState(false); React.useEffect(() => { @@ -1897,7 +1914,7 @@ describe('Timeline profiler', () => { } renderRootHelper(); - expect(Scheduler).toFlushAndYield(['mount', 'update']); + await waitForAll(['mount', 'update']); const timelineData = stopProfilingAndGetTimelineData(); expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); @@ -1947,7 +1964,7 @@ describe('Timeline profiler', () => { `); }); - it('should mark render phase updates', () => { + it('should mark render phase updates', async () => { function Example() { const [didRender, setDidRender] = React.useState(false); Scheduler.unstable_advanceTime(10); @@ -1959,7 +1976,7 @@ describe('Timeline profiler', () => { } renderRootHelper(); - expect(Scheduler).toFlushAndYield(['mount', 'update']); + await waitForAll(['mount', 'update']); const timelineData = stopProfilingAndGetTimelineData(); // Render phase updates should be retried as part of the same batch. @@ -2026,7 +2043,7 @@ describe('Timeline profiler', () => { , ); - expect(Scheduler).toHaveYielded([ + assertLog([ 'ErrorBoundary render', 'ExampleThatThrows', 'ExampleThatThrows', @@ -2122,7 +2139,7 @@ describe('Timeline profiler', () => { , ); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'ErrorBoundary render', 'ExampleThatThrows', 'ExampleThatThrows', @@ -2253,22 +2270,19 @@ describe('Timeline profiler', () => { const unmount = renderRootHelper(); - expect(Scheduler).toFlushUntilNextPaint([ - 'layout 1 mount', - 'layout 2 mount', - ]); + await waitForPaint(['layout 1 mount', 'layout 2 mount']); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'passive 1 mount', 'passive 2 mount', 'passive 3 mount', ]); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); unmount(); - expect(Scheduler).toHaveYielded([ + assertLog([ 'layout 1 unmount', 'layout 2 unmount', 'passive 1 unmount', @@ -2465,7 +2479,7 @@ describe('Timeline profiler', () => { renderRootHelper(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Render ComponentWithChildren', 'Render Child', 'Render Child', diff --git a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js index 5ed762afb86a1..a349f244ad8c7 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js @@ -15,6 +15,7 @@ let ReactDOM; let ReactDOMClient; let Scheduler; let act; +let waitForAll; const setUntrackedInputValue = Object.getOwnPropertyDescriptor( HTMLInputElement.prototype, @@ -33,6 +34,9 @@ describe('ReactDOMFiberAsync', () => { act = require('jest-react').act; Scheduler = require('scheduler'); + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + document.body.appendChild(container); }); @@ -592,7 +596,7 @@ describe('ReactDOMFiberAsync', () => { expect(containerC.textContent).toEqual('Finished'); }); - it('updates flush without yielding in the next event', () => { + it('updates flush without yielding in the next event', async () => { const root = ReactDOMClient.createRoot(container); function Text(props) { @@ -612,7 +616,7 @@ describe('ReactDOMFiberAsync', () => { expect(container.textContent).toEqual(''); // Everything should render immediately in the next event - expect(Scheduler).toFlushAndYield(['A', 'B', 'C']); + await waitForAll(['A', 'B', 'C']); expect(container.textContent).toEqual('ABC'); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index b80c35fd46534..77ad903f9d261 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -38,6 +38,10 @@ let buffer = ''; let hasErrored = false; let fatalError = undefined; let renderOptions; +let waitFor; +let waitForAll; +let assertLog; +let waitForPaint; function resetJSDOM(markup) { // Test Environment @@ -65,6 +69,12 @@ describe('ReactDOMFizzServer', () => { PropTypes = require('prop-types'); + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + waitFor = InternalTestUtils.waitFor; + waitForPaint = InternalTestUtils.waitForPaint; + assertLog = InternalTestUtils.assertLog; + if (gate(flags => flags.source)) { // The `with-selector` module composes the main `use-sync-external-store` // entrypoint. In the compiled artifacts, this is resolved to the `shim` @@ -562,7 +572,7 @@ describe('ReactDOMFizzServer', () => { expect(getVisibleChildren(container)).toEqual(
Loading...
); // Now we can client render it instead. - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expectErrors( errors, [ @@ -684,7 +694,7 @@ describe('ReactDOMFizzServer', () => { expect(getVisibleChildren(container)).toEqual(
Loading...
); // Now we can client render it instead. - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expectErrors( errors, @@ -843,7 +853,7 @@ describe('ReactDOMFizzServer', () => { expect(getVisibleChildren(container)).toEqual(
Loading...
); // Now we can client render it instead. - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expectErrors( errors, @@ -1161,7 +1171,7 @@ describe('ReactDOMFizzServer', () => { }); // We still can't render it on the client. - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expectErrors( errors, [ @@ -1883,7 +1893,7 @@ describe('ReactDOMFizzServer', () => { expect(getVisibleChildren(container)).toEqual(
Loading...
); // That will let us client render it instead. - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expectErrors( errors, [ @@ -2139,7 +2149,7 @@ describe('ReactDOMFizzServer', () => { ); pipe(writable); }); - expect(Scheduler).toHaveYielded(['server']); + assertLog(['server']); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { @@ -2149,9 +2159,9 @@ describe('ReactDOMFizzServer', () => { }, }); - expect(() => { + await expect(async () => { // The first paint switches to client rendering due to mismatch - expect(Scheduler).toFlushUntilNextPaint([ + await waitForPaint([ 'client', 'Log recoverable error: Hydration failed because the initial ' + 'UI does not match what was rendered on the server.', @@ -2224,7 +2234,7 @@ describe('ReactDOMFizzServer', () => { ); pipe(writable); }); - expect(Scheduler).toHaveYielded(['server']); + assertLog(['server']); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { @@ -2235,9 +2245,9 @@ describe('ReactDOMFizzServer', () => { }); // The first paint uses the client due to mismatch forcing client render - expect(() => { + await expect(async () => { // The first paint switches to client rendering due to mismatch - expect(Scheduler).toFlushUntilNextPaint([ + await waitForPaint([ 'client', 'Log recoverable error: Hydration failed because the initial ' + 'UI does not match what was rendered on the server.', @@ -2304,7 +2314,7 @@ describe('ReactDOMFizzServer', () => { const {pipe} = renderToPipeableStream(); pipe(writable); }); - expect(Scheduler).toHaveYielded(['Yay!']); + assertLog(['Yay!']); const span = container.getElementsByTagName('span')[0]; @@ -2319,8 +2329,8 @@ describe('ReactDOMFizzServer', () => { // An error logged but instead of surfacing it to the UI, we switched // to client rendering. - expect(() => { - expect(Scheduler).toFlushAndYield([ + await expect(async () => { + await waitForAll([ 'Yay!', 'Hydration error', 'There was an error while hydrating. Because the error happened ' + @@ -2394,7 +2404,7 @@ describe('ReactDOMFizzServer', () => { const {pipe} = renderToPipeableStream(); pipe(writable); }); - expect(Scheduler).toHaveYielded(['Yay!']); + assertLog(['Yay!']); const [span1, span2, span3] = container.getElementsByTagName('span'); @@ -2409,7 +2419,7 @@ describe('ReactDOMFizzServer', () => { // An error logged but instead of surfacing it to the UI, we switched // to client rendering. - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Yay!', 'Hydration error', 'There was an error while hydrating this Suspense boundary. Switched to client rendering.', @@ -2484,7 +2494,7 @@ describe('ReactDOMFizzServer', () => { const {pipe} = renderToPipeableStream(); pipe(writable); }); - expect(Scheduler).toHaveYielded(['Yay!']); + assertLog(['Yay!']); // Hydrate the tree. Child will throw during render. isClient = true; @@ -2497,7 +2507,7 @@ describe('ReactDOMFizzServer', () => { // Because we failed to recover from the error, onRecoverableError // shouldn't be called. - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual('Oops!'); expectErrors(errors, [], []); @@ -2541,7 +2551,7 @@ describe('ReactDOMFizzServer', () => { }); pipe(writable); }); - expect(Scheduler).toHaveYielded(['[s!] Oops.']); + assertLog(['[s!] Oops.']); // The server could not complete this boundary, so we'll retry on the client. const serverFallback = container.getElementsByTagName('p')[0]; @@ -2555,7 +2565,7 @@ describe('ReactDOMFizzServer', () => { }, }); // This should not report any errors yet. - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual(

Loading...

@@ -2573,7 +2583,7 @@ describe('ReactDOMFizzServer', () => { await act(async () => { resolveText('Yay!'); }); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Yay!', '[c!] The server could not finish this Suspense boundary, ' + 'likely due to an error during server rendering. ' + @@ -2626,7 +2636,7 @@ describe('ReactDOMFizzServer', () => { }); pipe(writable); }); - expect(Scheduler).toHaveYielded(['[s!] Oops.']); + assertLog(['[s!] Oops.']); // The server could not complete this boundary, so we'll retry on the client. const serverFallback = container.getElementsByTagName('p')[0]; @@ -2640,7 +2650,7 @@ describe('ReactDOMFizzServer', () => { }, }); // This should not report any errors yet. - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual(

Loading...

@@ -2667,7 +2677,7 @@ describe('ReactDOMFizzServer', () => { await act(async () => { resolveText('Yay!'); }); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Yay! (red)', '[c!] The server could not finish this Suspense boundary, ' + 'likely due to an error during server rendering. ' + @@ -2727,7 +2737,7 @@ describe('ReactDOMFizzServer', () => { ); pipe(writable); }); - expect(Scheduler).toHaveYielded(['[s!] Oops.']); + assertLog(['[s!] Oops.']); const serverFallback = container.getElementsByTagName('p')[0]; expect(serverFallback.innerHTML).toBe('Loading...'); @@ -2744,7 +2754,7 @@ describe('ReactDOMFizzServer', () => { }, ); // This should not report any errors yet. - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual(

Loading...

@@ -2763,7 +2773,7 @@ describe('ReactDOMFizzServer', () => { root.render(); Scheduler.unstable_flushAll(); jest.runAllTimers(); - expect(Scheduler).toHaveYielded([ + assertLog([ '[c!] The server could not finish this Suspense boundary, ' + 'likely due to an error during server rendering. ' + 'Switched to client rendering.', @@ -2781,7 +2791,7 @@ describe('ReactDOMFizzServer', () => { await act(async () => { resolveText('Yay!'); }); - expect(Scheduler).toFlushAndYield(['Yay!']); + await waitForAll(['Yay!']); expect(getVisibleChildren(container)).toEqual(
Yay! @@ -2849,7 +2859,7 @@ describe('ReactDOMFizzServer', () => { const {pipe} = renderToPipeableStream(); pipe(writable); }); - expect(Scheduler).toHaveYielded(['Yay!']); + assertLog(['Yay!']); const [span1, span2, span3] = container.getElementsByTagName('span'); @@ -2864,7 +2874,7 @@ describe('ReactDOMFizzServer', () => { // An error logged but instead of surfacing it to the UI, we switched // to client rendering. - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Hydration error', 'There was an error while hydrating this Suspense boundary. Switched ' + 'to client rendering.', @@ -2880,7 +2890,7 @@ describe('ReactDOMFizzServer', () => { await act(async () => { resolveText('Yay!'); }); - expect(Scheduler).toFlushAndYield(['Yay!']); + await waitForAll(['Yay!']); expect(getVisibleChildren(container)).toEqual(
@@ -2937,13 +2947,13 @@ describe('ReactDOMFizzServer', () => { }); // Partially render A, but yield before the render has finished - expect(Scheduler).toFlushAndYieldThrough(['Oops!', 'Oops!']); + await waitFor(['Oops!', 'Oops!']); // React will try rendering again synchronously. During the retry, A will // not throw. This simulates a concurrent data race that is fixed by // blocking the main thread. shouldThrow = false; - expect(Scheduler).toFlushAndYield([ + await waitForAll([ // Finish initial render attempt 'B', @@ -3001,7 +3011,7 @@ describe('ReactDOMFizzServer', () => { const {pipe} = renderToPipeableStream(); pipe(writable); }); - expect(Scheduler).toHaveYielded(['A', 'B']); + assertLog(['A', 'B']); // Hydrate the tree. Child will throw during hydration, but not when it // falls back to client rendering. @@ -3014,7 +3024,7 @@ describe('ReactDOMFizzServer', () => { }, }); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'A', 'B', @@ -3151,7 +3161,7 @@ describe('ReactDOMFizzServer', () => { }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expectErrors( errors, @@ -3232,7 +3242,7 @@ describe('ReactDOMFizzServer', () => { }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expectErrors( errors, @@ -3299,7 +3309,7 @@ describe('ReactDOMFizzServer', () => { }, }, ); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); }); describe('error escaping', () => { @@ -3442,7 +3452,7 @@ describe('ReactDOMFizzServer', () => { errors.push({error, errorInfo}); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); // If escaping were not done we would get a message that says "bad hash" expectErrors( @@ -3729,7 +3739,7 @@ describe('ReactDOMFizzServer', () => { // Now that the boundary resolves to it's children the hydration completes and discovers that there is a mismatch requiring // client-side rendering. await clientResolve(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual(

A

@@ -3808,8 +3818,8 @@ describe('ReactDOMFizzServer', () => { // Now that the boundary resolves to it's children the hydration completes and discovers that there is a mismatch requiring // client-side rendering. await clientResolve(); - expect(() => { - expect(Scheduler).toFlushAndYield([ + await expect(async () => { + await waitForAll([ 'Logged recoverable error: Text content does not match server-rendered HTML.', 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.', ]); @@ -3823,7 +3833,7 @@ describe('ReactDOMFizzServer', () => {
, ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); }); // @gate enableClientRenderFallbackOnTextMismatch @@ -3867,7 +3877,7 @@ describe('ReactDOMFizzServer', () => { ); }, }); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Logged recoverable error: Text content does not match server-rendered HTML.', 'Logged recoverable error: Text content does not match server-rendered HTML.', 'Logged recoverable error: Text content does not match server-rendered HTML.', @@ -3882,7 +3892,7 @@ describe('ReactDOMFizzServer', () => {
, ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); if (__DEV__) { expect(mockError.mock.calls.length).toBe(1); expect(mockError.mock.calls[0]).toEqual([ @@ -3958,7 +3968,7 @@ describe('ReactDOMFizzServer', () => { ); }, }); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'Logged recoverable error: uh oh', 'Logged recoverable error: Hydration failed because the initial UI does not match what was rendered on the server.', 'Logged recoverable error: Hydration failed because the initial UI does not match what was rendered on the server.', @@ -3973,7 +3983,7 @@ describe('ReactDOMFizzServer', () => {
, ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); }); // @gate __DEV__ @@ -4050,7 +4060,7 @@ describe('ReactDOMFizzServer', () => { ); }, }); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'throwing: first error', // this repeated first error is the invokeGuardedCallback throw 'throwing: first error', @@ -4081,7 +4091,7 @@ describe('ReactDOMFizzServer', () => {
, ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(mockError.mock.calls).toEqual([]); } finally { console.error = originalConsoleError; @@ -4181,7 +4191,7 @@ describe('ReactDOMFizzServer', () => { ); }, }); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'suspending', 'throwing: first error', // There is no repeated first error because we already suspended and no @@ -4202,7 +4212,7 @@ describe('ReactDOMFizzServer', () => { await unsuspend(); // Since our client components only throw on the very first render there are no // new throws in this pass - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(mockError.mock.calls).toEqual([]); } finally { @@ -4304,7 +4314,7 @@ describe('ReactDOMFizzServer', () => { ); }, }); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'throwing: first error', // duplicate because first error is re-done in invokeGuardedCallback 'throwing: first error', @@ -4331,7 +4341,7 @@ describe('ReactDOMFizzServer', () => { await unsuspend(); // Since our client components only throw on the very first render there are no // new throws in this pass - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(mockError.mock.calls).toEqual([]); } finally { console.error = originalConsoleError; @@ -4365,7 +4375,7 @@ describe('ReactDOMFizzServer', () => { }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
@@ -4374,7 +4384,7 @@ describe('ReactDOMFizzServer', () => { ); resolveText('A'); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
@@ -4411,7 +4421,7 @@ describe('ReactDOMFizzServer', () => { }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
@@ -4421,7 +4431,7 @@ describe('ReactDOMFizzServer', () => { ); resolve({default: () =>

lazy

}); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
@@ -4566,7 +4576,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
@@ -4629,7 +4639,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
@@ -4679,7 +4689,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
{['h', 'ello', 'w', 'orld']}
, @@ -4724,7 +4734,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
{['h', 'ello', 'w', 'orld']}
, @@ -4762,7 +4772,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
@@ -4800,7 +4810,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
@@ -4857,7 +4867,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
@@ -4883,7 +4893,7 @@ describe('ReactDOMFizzServer', () => { '
startfirststartfirst suspendedfirstendsecondstartsecond suspendedend
', ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
@@ -4939,7 +4949,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(
@@ -4985,7 +4995,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(hello); }); @@ -5009,7 +5019,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); expect(getVisibleChildren(container)).toEqual(hello); }); @@ -5044,7 +5054,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); if (gate(flags => flags.enableFloat)) { expect(errors).toEqual([]); // with float, the title doesn't render on the client or on the server @@ -5117,7 +5127,7 @@ describe('ReactDOMFizzServer', () => { errors.push(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(errors).toEqual([]); if (gate(flags => flags.enableFloat)) { // object titles are toStringed when float is on @@ -5171,7 +5181,7 @@ describe('ReactDOMFizzServer', () => { expect(getVisibleChildren(container)).toEqual('ABC'); ReactDOMClient.hydrateRoot(container, ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual('ABC'); }); @@ -5219,7 +5229,7 @@ describe('ReactDOMFizzServer', () => { ContextA._currentRenderer = null; ServerContext._currentRenderer = null; ReactDOMClient.hydrateRoot(container, ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual(['AB', 'C']); }); @@ -5273,7 +5283,7 @@ describe('ReactDOMFizzServer', () => { expect(getVisibleChildren(container)).toEqual('ABCD'); ReactDOMClient.hydrateRoot(container, ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual('ABCD'); }); @@ -5350,7 +5360,7 @@ describe('ReactDOMFizzServer', () => { reportedClientErrors.push(error); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual('Oops!'); expect(reportedClientErrors.length).toBe(1); if (__DEV__) { @@ -5383,7 +5393,7 @@ describe('ReactDOMFizzServer', () => { expect(getVisibleChildren(container)).toEqual('Hi'); ReactDOMClient.hydrateRoot(container, ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual('Hi'); }); @@ -5437,7 +5447,7 @@ describe('ReactDOMFizzServer', () => { expect(getVisibleChildren(container)).toEqual(); ReactDOMClient.hydrateRoot(container, ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual(); ref.current.dispatchEvent( @@ -5502,8 +5512,8 @@ describe('ReactDOMFizzServer', () => { errors.push(error); }, }); - expect(() => { - expect(Scheduler).toFlushAndYield([]); + await expect(async () => { + await waitForAll([]); }).toErrorDev( [ 'Expected server HTML to contain a matching in
', diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js index c25f18ca2a25a..eeeb9c6600d6e 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js @@ -22,6 +22,7 @@ let container; let buffer = ''; let hasErrored = false; let fatalError = undefined; +let waitForAll; describe('ReactDOMFizzServerHydrationWarning', () => { beforeEach(() => { @@ -33,6 +34,9 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ReactDOMFizzServer = require('react-dom/server'); Stream = require('stream'); + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + // Test Environment const jsdom = new JSDOM( '
', @@ -155,7 +159,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); // The text mismatch should be *silently* fixed. Even in production. expect(getVisibleChildren(container)).toEqual(
@@ -195,7 +199,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual(
@@ -236,8 +240,8 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(() => { - expect(Scheduler).toFlushAndYield([ + await expect(async () => { + await waitForAll([ 'Hydration failed because the initial UI does not match what was rendered on the server.', 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', ]); @@ -283,7 +287,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual(
{'Client'} @@ -319,8 +323,8 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(() => { - expect(Scheduler).toFlushAndYield([ + await expect(async () => { + await waitForAll([ 'Hydration failed because the initial UI does not match what was rendered on the server.', 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', ]); @@ -367,8 +371,8 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(() => { - expect(Scheduler).toFlushAndYield([ + await expect(async () => { + await waitForAll([ 'Hydration failed because the initial UI does not match what was rendered on the server.', 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', ]); @@ -418,8 +422,8 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(() => { - expect(Scheduler).toFlushAndYield([ + await expect(async () => { + await waitForAll([ 'Hydration failed because the initial UI does not match what was rendered on the server.', 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', ]); @@ -467,8 +471,8 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(() => { - expect(Scheduler).toFlushAndYield([ + await expect(async () => { + await waitForAll([ 'Hydration failed because the initial UI does not match what was rendered on the server.', 'Hydration failed because the initial UI does not match what was rendered on the server.', 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', @@ -521,7 +525,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual(
@@ -558,7 +562,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(getVisibleChildren(container)).toEqual(

Server HTML

@@ -591,8 +595,8 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(() => { - expect(Scheduler).toFlushAndYield([ + await expect(async () => { + await waitForAll([ 'Hydration failed because the initial UI does not match what was rendered on the server.', 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', ]); @@ -637,8 +641,8 @@ describe('ReactDOMFizzServerHydrationWarning', () => { Scheduler.unstable_yieldValue(error.message); }, }); - expect(() => { - expect(Scheduler).toFlushAndYield([ + await expect(async () => { + await waitForAll([ 'Hydration failed because the initial UI does not match what was rendered on the server.', 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', ]); diff --git a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js index 5be13e9cdac68..eebc2f078ead0 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js @@ -33,6 +33,8 @@ let buffer = ''; let hasErrored = false; let fatalError = undefined; let renderOptions; +let waitForAll; +let assertLog; function resetJSDOM(markup) { // Test Environment @@ -63,6 +65,10 @@ describe('ReactDOMFloat', () => { Stream = require('stream'); Suspense = React.Suspense; + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + assertLog = InternalTestUtils.assertLog; + textCache = new Map(); resetJSDOM('
'); @@ -259,10 +265,14 @@ describe('ReactDOMFloat', () => { root.render(children); return expect(() => { try { - expect(Scheduler).toFlushWithoutYielding(); + // TODO: Migrate this to waitForAll() + Scheduler.unstable_flushAll(); + assertLog([]); } catch (e) { try { - expect(Scheduler).toFlushWithoutYielding(); + // TODO: Migrate this to waitForAll() + Scheduler.unstable_flushAll(); + assertLog([]); } catch (f) {} } }); @@ -283,11 +293,11 @@ describe('ReactDOMFloat', () => { , ); try { - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); } catch (e) { // for DOMExceptions that happen when expecting this test to fail we need // to clear the scheduler first otherwise the expected failure will fail - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); throw e; } expect(getMeaningfulChildren(document)).toEqual( @@ -351,7 +361,7 @@ describe('ReactDOMFloat', () => { foo , ); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(getMeaningfulChildren(document)).toEqual( @@ -476,7 +486,7 @@ describe('ReactDOMFloat', () => {