diff --git a/packages/react-dom/src/__tests__/ReactDOMImageLoad-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMImageLoad-test.internal.js
index 717d795d99884..304e1f5cb65bb 100644
--- a/packages/react-dom/src/__tests__/ReactDOMImageLoad-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactDOMImageLoad-test.internal.js
@@ -24,6 +24,10 @@ let images = [];
let onLoadSpy = null;
let actualLoadSpy = null;
+let waitForAll;
+let waitFor;
+let assertLog;
+
function PhaseMarkers({children}) {
Scheduler.unstable_yieldValue('render start');
React.useLayoutEffect(() => {
@@ -94,6 +98,11 @@ describe('ReactDOMImageLoad', () => {
ReactDOMClient = require('react-dom/client');
// Suspense = React.Suspense;
+ const InternalTestUtils = require('internal-test-utils');
+ waitForAll = InternalTestUtils.waitForAll;
+ waitFor = InternalTestUtils.waitFor;
+ assertLog = InternalTestUtils.assertLog;
+
onLoadSpy = jest.fn(reactEvent => {
const src = reactEvent.target.getAttribute('src');
Scheduler.unstable_yieldValue('onLoadSpy [' + src + ']');
@@ -206,26 +215,17 @@ describe('ReactDOMImageLoad', () => {
),
);
- expect(Scheduler).toFlushAndYieldThrough([
- 'render start',
- 'Img default',
- 'Yield',
- ]);
+ await waitFor(['render start', 'Img default', 'Yield']);
const img = last(images);
loadImage(img);
- expect(Scheduler).toHaveYielded([
+ assertLog([
'actualLoadSpy [default]',
// no onLoadSpy since we have not completed render
]);
- expect(Scheduler).toFlushAndYield([
- 'a',
- 'load triggered',
- 'last layout',
- 'last passive',
- ]);
+ await waitForAll(['a', 'load triggered', 'last layout', 'last passive']);
expect(img.__needsDispatch).toBe(true);
loadImage(img);
- expect(Scheduler).toHaveYielded([
+ assertLog([
'actualLoadSpy [default]', // the browser reloading of the image causes this to yield again
'onLoadSpy [default]',
]);
@@ -244,7 +244,7 @@ describe('ReactDOMImageLoad', () => {
),
);
- expect(Scheduler).toFlushAndYieldThrough([
+ await waitFor([
'render start',
'Img default',
'load triggered',
@@ -253,11 +253,8 @@ describe('ReactDOMImageLoad', () => {
Scheduler.unstable_requestPaint();
const img = last(images);
loadImage(img);
- expect(Scheduler).toHaveYielded([
- 'actualLoadSpy [default]',
- 'onLoadSpy [default]',
- ]);
- expect(Scheduler).toFlushAndYield(['last passive']);
+ assertLog(['actualLoadSpy [default]', 'onLoadSpy [default]']);
+ await waitForAll(['last passive']);
expect(img.__needsDispatch).toBe(false);
expect(onLoadSpy).toHaveBeenCalledTimes(1);
});
@@ -286,16 +283,12 @@ describe('ReactDOMImageLoad', () => {
React.startTransition(() => root.render());
- expect(Scheduler).toFlushAndYieldThrough([
- 'render start',
- 'Img a',
- 'Yield',
- ]);
+ await waitFor(['render start', 'Img a', 'Yield']);
const img = last(images);
loadImage(img);
- expect(Scheduler).toHaveYielded(['actualLoadSpy [a]']);
+ assertLog(['actualLoadSpy [a]']);
- expect(Scheduler).toFlushAndYieldThrough([
+ await waitFor([
'load triggered',
'last layout',
// the update in layout causes a passive effects flush before a sync render
@@ -309,7 +302,7 @@ describe('ReactDOMImageLoad', () => {
]);
expect(images.length).toBe(1);
loadImage(img);
- expect(Scheduler).toHaveYielded(['actualLoadSpy [b]', 'onLoadSpy [b]']);
+ assertLog(['actualLoadSpy [b]', 'onLoadSpy [b]']);
expect(onLoadSpy).toHaveBeenCalledTimes(1);
});
@@ -323,7 +316,7 @@ describe('ReactDOMImageLoad', () => {
,
);
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'render start',
'Img default',
'load triggered',
@@ -332,10 +325,7 @@ describe('ReactDOMImageLoad', () => {
]);
const img = last(images);
loadImage(img);
- expect(Scheduler).toHaveYielded([
- 'actualLoadSpy [default]',
- 'onLoadSpy [default]',
- ]);
+ assertLog(['actualLoadSpy [default]', 'onLoadSpy [default]']);
expect(onLoadSpy).toHaveBeenCalledTimes(1);
});
@@ -365,26 +355,17 @@ describe('ReactDOMImageLoad', () => {
),
);
- expect(Scheduler).toFlushAndYieldThrough([
- 'render start',
- 'Img default',
- 'Yield',
- ]);
+ await waitFor(['render start', 'Img default', 'Yield']);
const img = last(images);
loadImage(img);
- expect(Scheduler).toHaveYielded(['actualLoadSpy [default]']);
- expect(Scheduler).toFlushAndYield([
- 'a',
- 'load triggered',
- 'last layout',
- 'last passive',
- ]);
+ assertLog(['actualLoadSpy [default]']);
+ await waitForAll(['a', 'load triggered', 'last layout', 'last passive']);
expect(img.__needsDispatch).toBe(true);
loadImage(img);
// we expect the browser to load the image again but since we are no longer rendering
// the img there will be no onLoad called
- expect(Scheduler).toHaveYielded(['actualLoadSpy [default]']);
- expect(Scheduler).toFlushWithoutYielding();
+ assertLog(['actualLoadSpy [default]']);
+ await waitForAll([]);
expect(onLoadSpy).not.toHaveBeenCalled();
});
@@ -426,7 +407,7 @@ describe('ReactDOMImageLoad', () => {
),
);
- expect(Scheduler).toFlushAndYieldThrough([
+ await waitFor([
// initial render
'render start',
'Img default',
@@ -434,8 +415,8 @@ describe('ReactDOMImageLoad', () => {
]);
const img = last(images);
loadImage(img);
- expect(Scheduler).toHaveYielded(['actualLoadSpy [default]']);
- expect(Scheduler).toFlushAndYield([
+ assertLog(['actualLoadSpy [default]']);
+ await waitForAll([
'a',
'load triggered',
// img is present at first
@@ -450,8 +431,8 @@ describe('ReactDOMImageLoad', () => {
loadImage(img);
// we expect the browser to load the image again but since we are no longer rendering
// the img there will be no onLoad called
- expect(Scheduler).toHaveYielded(['actualLoadSpy [default]']);
- expect(Scheduler).toFlushWithoutYielding();
+ assertLog(['actualLoadSpy [default]']);
+ await waitForAll([]);
expect(onLoadSpy).not.toHaveBeenCalled();
});
@@ -548,22 +529,18 @@ describe('ReactDOMImageLoad', () => {
root.render();
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
React.startTransition(() => externalSetSrc('a'));
- expect(Scheduler).toFlushAndYieldThrough([
- 'YieldingWithImage',
- 'Img a',
- 'Yield',
- ]);
+ await waitFor(['YieldingWithImage', 'Img a', 'Yield']);
let img = last(images);
loadImage(img);
- expect(Scheduler).toHaveYielded(['actualLoadSpy [a]']);
+ assertLog(['actualLoadSpy [a]']);
ReactDOM.flushSync(() => externalSetSrcAlt('b'));
- expect(Scheduler).toHaveYielded([
+ assertLog([
'YieldingWithImage',
'Img b',
'Yield',
@@ -576,18 +553,12 @@ describe('ReactDOMImageLoad', () => {
expect(img.__needsDispatch).toBe(true);
loadImage(img);
- expect(Scheduler).toHaveYielded(['actualLoadSpy [b]', 'onLoadSpy [b]']);
+ assertLog(['actualLoadSpy [b]', 'onLoadSpy [b]']);
// why is there another update here?
- expect(Scheduler).toFlushAndYield([
- 'YieldingWithImage',
- 'Img b',
- 'Yield',
- 'b',
- 'Committed',
- ]);
+ await waitForAll(['YieldingWithImage', 'Img b', 'Yield', 'b', 'Committed']);
});
- it('preserves the src property / attribute when triggering a potential new load event', () => {
+ it('preserves the src property / attribute when triggering a potential new load event', async () => {
// this test covers a regression identified in https://github.com/mui/material-ui/pull/31263
// where the resetting of the src property caused the property to change from relative to fully qualified
@@ -612,17 +583,13 @@ describe('ReactDOMImageLoad', () => {
);
// render to yield to capture state of img src attribute and property before commit
- expect(Scheduler).toFlushAndYieldThrough([
- 'render start',
- 'Img default',
- 'Yield',
- ]);
+ await waitFor(['render start', 'Img default', 'Yield']);
const img = last(images);
const renderSrcProperty = img.src;
const renderSrcAttr = img.getAttribute('src');
// finish render and commit causing the src property to be rewritten
- expect(Scheduler).toFlushAndYield(['a', 'last layout', 'last passive']);
+ await waitForAll(['a', 'last layout', 'last passive']);
const commitSrcProperty = img.src;
const commitSrcAttr = img.getAttribute('src');
diff --git a/packages/react-dom/src/__tests__/ReactDOMNativeEventHeuristic-test.js b/packages/react-dom/src/__tests__/ReactDOMNativeEventHeuristic-test.js
index cfdeecf718f03..609eca887dde2 100644
--- a/packages/react-dom/src/__tests__/ReactDOMNativeEventHeuristic-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMNativeEventHeuristic-test.js
@@ -15,6 +15,8 @@ let ReactDOM;
let ReactDOMClient;
let Scheduler;
let act;
+let assertLog;
+let waitFor;
describe('ReactDOMNativeEventHeuristic-test', () => {
let container;
@@ -28,6 +30,10 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
Scheduler = require('scheduler');
act = require('jest-react').act;
+ const InternalTestUtils = require('internal-test-utils');
+ assertLog = InternalTestUtils.assertLog;
+ waitFor = InternalTestUtils.waitFor;
+
document.body.appendChild(container);
});
@@ -301,10 +307,10 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
dispatchAndSetCurrentEvent(target.current, mouseEnterEvent);
// Since mouse end is not discrete, should not have updated yet
- expect(Scheduler).toHaveYielded(['not hovered']);
+ assertLog(['not hovered']);
expect(container.textContent).toEqual('not hovered');
- expect(Scheduler).toFlushAndYieldThrough(['hovered']);
+ await waitFor(['hovered']);
expect(container.textContent).toEqual('hovered');
});
expect(container.textContent).toEqual('hovered');
@@ -381,7 +387,7 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
pressEvent.initEvent('click', true, true);
dispatchAndSetCurrentEvent(target, pressEvent);
- expect(Scheduler).toHaveYielded(['Count: 0 [after batchedUpdates]']);
+ assertLog(['Count: 0 [after batchedUpdates]']);
expect(container.textContent).toEqual('Count: 0');
// Intentionally not using `act` so we can observe in between the click
diff --git a/packages/react-dom/src/__tests__/ReactDOMNestedEvents-test.js b/packages/react-dom/src/__tests__/ReactDOMNestedEvents-test.js
index 6c632cd31ead5..ee6abf41da8d7 100644
--- a/packages/react-dom/src/__tests__/ReactDOMNestedEvents-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMNestedEvents-test.js
@@ -15,6 +15,7 @@ describe('ReactDOMNestedEvents', () => {
let Scheduler;
let act;
let useState;
+ let assertLog;
beforeEach(() => {
jest.resetModules();
@@ -23,6 +24,9 @@ describe('ReactDOMNestedEvents', () => {
Scheduler = require('scheduler');
act = require('jest-react').act;
useState = React.useState;
+
+ const InternalTestUtils = require('internal-test-utils');
+ assertLog = InternalTestUtils.assertLog;
});
test('nested event dispatches should not cause updates to flush', async () => {
@@ -67,9 +71,7 @@ describe('ReactDOMNestedEvents', () => {
await act(async () => {
buttonRef.current.click();
});
- expect(Scheduler).toHaveYielded([
- 'Value right after focus call: Clicked: false, Focused: false',
- ]);
+ assertLog(['Value right after focus call: Clicked: false, Focused: false']);
expect(buttonRef.current.innerHTML).toEqual('Clicked: true, Focused: true');
});
});
diff --git a/packages/react-dom/src/__tests__/ReactDOMRoot-test.js b/packages/react-dom/src/__tests__/ReactDOMRoot-test.js
index 9d51ba40c3796..6d1db3fdee419 100644
--- a/packages/react-dom/src/__tests__/ReactDOMRoot-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMRoot-test.js
@@ -16,6 +16,8 @@ let ReactDOMServer = require('react-dom/server');
let Scheduler = require('scheduler');
let act;
let useEffect;
+let assertLog;
+let waitFor;
describe('ReactDOMRoot', () => {
let container;
@@ -30,6 +32,10 @@ describe('ReactDOMRoot', () => {
Scheduler = require('scheduler');
act = require('jest-react').act;
useEffect = React.useEffect;
+
+ const InternalTestUtils = require('internal-test-utils');
+ assertLog = InternalTestUtils.assertLog;
+ waitFor = InternalTestUtils.waitFor;
});
it('renders children', () => {
@@ -255,7 +261,7 @@ describe('ReactDOMRoot', () => {
Scheduler.unstable_yieldValue('callback');
});
expect(container.textContent).toEqual('Hi');
- expect(Scheduler).toHaveYielded(['callback']);
+ assertLog(['callback']);
});
it('warns when unmounting with legacy API (no previous content)', () => {
@@ -401,10 +407,10 @@ describe('ReactDOMRoot', () => {
await act(async () => {
root.render();
- expect(Scheduler).toHaveYielded(['a']);
+ assertLog(['a']);
expect(container.textContent).toEqual('a');
- expect(Scheduler).toFlushAndYieldThrough(['b']);
+ await waitFor(['b']);
if (gate(flags => flags.allowConcurrentByDefault)) {
expect(container.textContent).toEqual('a');
} else {
diff --git a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
index 0862865f76004..7148c8163262e 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
@@ -21,6 +21,10 @@ let SuspenseList;
let Offscreen;
let act;
let IdleEventPriority;
+let waitForAll;
+let waitFor;
+let waitForPaint;
+let assertLog;
function normalizeCodeLocInfo(strOrErr) {
if (strOrErr && strOrErr.replace) {
@@ -113,6 +117,12 @@ describe('ReactDOMServerPartialHydration', () => {
SuspenseList = React.SuspenseList;
}
+ const InternalTestUtils = require('internal-test-utils');
+ waitForAll = InternalTestUtils.waitForAll;
+ assertLog = InternalTestUtils.assertLog;
+ waitForPaint = InternalTestUtils.waitForPaint;
+ waitFor = InternalTestUtils.waitFor;
+
IdleEventPriority = require('react-reconciler/constants').IdleEventPriority;
});
@@ -290,13 +300,7 @@ describe('ReactDOMServerPartialHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
const container = document.createElement('section');
container.innerHTML = finalHTML;
- expect(Scheduler).toHaveYielded([
- 'Hello',
- 'Component',
- 'Component',
- 'Component',
- 'Component',
- ]);
+ assertLog(['Hello', 'Component', 'Component', 'Component', 'Component']);
expect(container.innerHTML).toBe(
'Hello
Component
Component
Component
Component
',
@@ -310,7 +314,7 @@ describe('ReactDOMServerPartialHydration', () => {
Scheduler.unstable_yieldValue(error.message);
},
});
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'Suspend',
'Component',
'Component',
@@ -327,7 +331,7 @@ describe('ReactDOMServerPartialHydration', () => {
suspend = false;
resolve();
await promise;
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
// first pass, mismatches at end
'Hello',
'Component',
@@ -434,7 +438,7 @@ describe('ReactDOMServerPartialHydration', () => {
Scheduler.unstable_yieldValue(error.message);
},
});
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
expect(hydrated.length).toBe(0);
expect(deleted.length).toBe(0);
@@ -520,7 +524,7 @@ describe('ReactDOMServerPartialHydration', () => {
expect(container.innerHTML).toContain('A');
expect(container.innerHTML).not.toContain('B');
- expect(Scheduler).toHaveYielded([
+ assertLog([
'There was an error while hydrating this Suspense boundary. ' +
'Switched to client rendering.',
]);
@@ -640,7 +644,7 @@ describe('ReactDOMServerPartialHydration', () => {
});
});
}).toErrorDev('Did not expect server HTML to contain a in ');
- expect(Scheduler).toHaveYielded([
+ assertLog([
'Hydration failed because the initial UI does not match what was rendered on the server.',
'There was an error while hydrating this Suspense boundary. Switched to client rendering.',
]);
@@ -1389,7 +1393,7 @@ describe('ReactDOMServerPartialHydration', () => {
suspend = false;
const finalHTML = ReactDOMServer.renderToString(
);
- expect(Scheduler).toHaveYielded(['Child', 'Sibling']);
+ assertLog(['Child', 'Sibling']);
const container = document.createElement('div');
container.innerHTML = finalHTML;
@@ -1401,7 +1405,7 @@ describe('ReactDOMServerPartialHydration', () => {
await act(async () => {
suspend = true;
- expect(Scheduler).toFlushAndYieldThrough(['Child']);
+ await waitFor(['Child']);
// While we're part way through the hydration, we update the state.
// This will schedule an update on the children of the suspense boundary.
@@ -1410,7 +1414,7 @@ describe('ReactDOMServerPartialHydration', () => {
);
// This will throw it away and rerender.
- expect(Scheduler).toFlushAndYield(['Child', 'Sibling']);
+ await waitForAll(['Child', 'Sibling']);
expect(container.textContent).toBe('Hello');
@@ -1418,7 +1422,7 @@ describe('ReactDOMServerPartialHydration', () => {
resolve();
await promise;
});
- expect(Scheduler).toHaveYielded(['Child', 'Sibling']);
+ assertLog(['Child', 'Sibling']);
expect(container.textContent).toBe('Hello');
});
@@ -1635,7 +1639,7 @@ describe('ReactDOMServerPartialHydration', () => {
},
});
if (__DEV__) {
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'The server did not finish this Suspense boundary: The server used' +
' "renderToString" which does not support Suspense. If you intended' +
' for this Suspense boundary to render the fallback content on the' +
@@ -1644,7 +1648,7 @@ describe('ReactDOMServerPartialHydration', () => {
' please switch to "renderToPipeableStream" which supports Suspense on the server',
]);
} else {
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'The server could not finish this Suspense boundary, likely due to ' +
'an error during server rendering. Switched to client rendering.',
]);
@@ -1708,7 +1712,7 @@ describe('ReactDOMServerPartialHydration', () => {
},
});
if (__DEV__) {
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'The server did not finish this Suspense boundary: The server used' +
' "renderToString" which does not support Suspense. If you intended' +
' for this Suspense boundary to render the fallback content on the' +
@@ -1717,7 +1721,7 @@ describe('ReactDOMServerPartialHydration', () => {
' please switch to "renderToPipeableStream" which supports Suspense on the server',
]);
} else {
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'The server could not finish this Suspense boundary, likely due to ' +
'an error during server rendering. Switched to client rendering.',
]);
@@ -1786,7 +1790,7 @@ describe('ReactDOMServerPartialHydration', () => {
},
});
if (__DEV__) {
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'The server did not finish this Suspense boundary: The server used' +
' "renderToString" which does not support Suspense. If you intended' +
' for this Suspense boundary to render the fallback content on the' +
@@ -1795,7 +1799,7 @@ describe('ReactDOMServerPartialHydration', () => {
' please switch to "renderToPipeableStream" which supports Suspense on the server',
]);
} else {
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'The server could not finish this Suspense boundary, likely due to ' +
'an error during server rendering. Switched to client rendering.',
]);
@@ -2028,7 +2032,7 @@ describe('ReactDOMServerPartialHydration', () => {
suspend = false;
const html = ReactDOMServer.renderToString(
);
- expect(Scheduler).toHaveYielded(['Before', 'After']);
+ assertLog(['Before', 'After']);
const container = document.createElement('div');
container.innerHTML = html;
@@ -2044,7 +2048,7 @@ describe('ReactDOMServerPartialHydration', () => {
suspend = true;
await act(async () => {
- expect(Scheduler).toFlushAndYieldThrough(['Before', 'After']);
+ await waitFor(['Before', 'After']);
// This will cause us to skip the second row completely.
});
@@ -2108,7 +2112,7 @@ describe('ReactDOMServerPartialHydration', () => {
suspend = true;
if (__DEV__) {
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'The server did not finish this Suspense boundary: The server used' +
' "renderToString" which does not support Suspense. If you intended' +
' for this Suspense boundary to render the fallback content on the' +
@@ -2117,7 +2121,7 @@ describe('ReactDOMServerPartialHydration', () => {
' please switch to "renderToPipeableStream" which supports Suspense on the server',
]);
} else {
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'The server could not finish this Suspense boundary, likely due to ' +
'an error during server rendering. Switched to client rendering.',
]);
@@ -2182,7 +2186,7 @@ describe('ReactDOMServerPartialHydration', () => {
},
});
if (__DEV__) {
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'The server did not finish this Suspense boundary: The server used' +
' "renderToString" which does not support Suspense. If you intended' +
' for this Suspense boundary to render the fallback content on the' +
@@ -2191,7 +2195,7 @@ describe('ReactDOMServerPartialHydration', () => {
' please switch to "renderToPipeableStream" which supports Suspense on the server',
]);
} else {
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
'The server could not finish this Suspense boundary, likely due to ' +
'an error during server rendering. Switched to client rendering.',
]);
@@ -3009,7 +3013,7 @@ describe('ReactDOMServerPartialHydration', () => {
suspend = false;
const finalHTML = ReactDOMServer.renderToString(
);
- expect(Scheduler).toHaveYielded(['Child']);
+ assertLog(['Child']);
const container = document.createElement('div');
container.innerHTML = finalHTML;
@@ -3019,7 +3023,7 @@ describe('ReactDOMServerPartialHydration', () => {
container,
,
);
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
expect(ref.current).toBe(null);
expect(container.textContent).toBe('Hello');
@@ -3036,14 +3040,14 @@ describe('ReactDOMServerPartialHydration', () => {
// When we flush we expect the Normal pri render to take priority
// over hydration.
- expect(Scheduler).toFlushAndYieldThrough(['Sibling', 'Commit Sibling']);
+ await waitFor(['Sibling', 'Commit Sibling']);
// We shouldn't have hydrated the child yet.
expect(ref.current).toBe(null);
// But we did have a chance to update the content.
expect(container.textContent).toBe('HelloWorld');
- expect(Scheduler).toFlushAndYield(['Child']);
+ await waitForAll(['Child']);
// Now we're hydrated.
expect(ref.current).not.toBe(null);
@@ -3248,7 +3252,7 @@ describe('ReactDOMServerPartialHydration', () => {
}
const finalHTML = ReactDOMServer.renderToString(
);
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
const container = document.createElement('div');
container.innerHTML = finalHTML;
@@ -3267,7 +3271,7 @@ describe('ReactDOMServerPartialHydration', () => {
// The tree successfully hydrates
ReactDOMClient.hydrateRoot(container,
);
- expect(Scheduler).toFlushAndYield([]);
+ await waitForAll([]);
expect(ref.current).toBe(span);
});
@@ -3295,7 +3299,7 @@ describe('ReactDOMServerPartialHydration', () => {
// During server rendering, the Child component should not be evaluated,
// because it's inside a hidden tree.
const finalHTML = ReactDOMServer.renderToString(
);
- expect(Scheduler).toHaveYielded(['App']);
+ assertLog(['App']);
const container = document.createElement('div');
container.innerHTML = finalHTML;
@@ -3313,11 +3317,11 @@ describe('ReactDOMServerPartialHydration', () => {
// The visible span successfully hydrates
ReactDOMClient.hydrateRoot(container,
);
- expect(Scheduler).toFlushUntilNextPaint(['App']);
+ await waitForPaint(['App']);
expect(visibleRef.current).toBe(visibleSpan);
// Subsequently, the hidden child is prerendered on the client
- expect(Scheduler).toFlushUntilNextPaint(['HiddenChild']);
+ await waitForPaint(['HiddenChild']);
expect(container).toMatchInlineSnapshot(`
@@ -3434,7 +3438,7 @@ describe('ReactDOMServerPartialHydration', () => {
],
{withoutStack: 1},
);
- expect(Scheduler).toHaveYielded([
+ assertLog([
'Log recoverable error: Hydration failed because the initial UI does not match what was rendered on the server.',
// TODO: There were multiple mismatches in a single container. Should
// we attempt to de-dupe them?
@@ -3482,7 +3486,7 @@ describe('ReactDOMServerPartialHydration', () => {
],
{withoutStack: 1},
);
- expect(Scheduler).toHaveYielded([
+ assertLog([
'Text content does not match server-rendered HTML.',
'There was an error while hydrating. Because the error happened outside ' +
'of a Suspense boundary, the entire root will switch to client rendering.',
@@ -3527,7 +3531,7 @@ describe('ReactDOMServerPartialHydration', () => {
],
{withoutStack: 1},
);
- expect(Scheduler).toHaveYielded([
+ assertLog([
'Text content does not match server-rendered HTML.',
'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__/ReactDOMServerSelectiveHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
index cac14620e7971..bbcbc8c275164 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js
@@ -19,6 +19,10 @@ let ReactFeatureFlags;
let Scheduler;
let Suspense;
let act;
+let assertLog;
+let waitForAll;
+let waitFor;
+let waitForPaint;
let IdleEventPriority;
let ContinuousEventPriority;
@@ -137,6 +141,12 @@ describe('ReactDOMServerSelectiveHydration', () => {
Scheduler = require('scheduler');
Suspense = React.Suspense;
+ const InternalTestUtils = require('internal-test-utils');
+ assertLog = InternalTestUtils.assertLog;
+ waitForAll = InternalTestUtils.waitForAll;
+ waitFor = InternalTestUtils.waitFor;
+ waitForPaint = InternalTestUtils.waitForPaint;
+
IdleEventPriority = require('react-reconciler/constants').IdleEventPriority;
ContinuousEventPriority =
require('react-reconciler/constants').ContinuousEventPriority;
@@ -172,7 +182,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'B']);
+ assertLog(['App', 'A', 'B']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
@@ -185,7 +195,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// This should synchronously hydrate the root App and the second suspense
// boundary.
@@ -195,10 +205,10 @@ describe('ReactDOMServerSelectiveHydration', () => {
expect(result).toBe(false);
// We rendered App, B and then invoked the event without rendering A.
- expect(Scheduler).toHaveYielded(['App', 'B', 'Clicked B']);
+ assertLog(['App', 'B', 'Clicked B']);
// After continuing the scheduler, we finally hydrate A.
- expect(Scheduler).toFlushAndYield(['A']);
+ await waitForAll(['A']);
document.body.removeChild(container);
});
@@ -246,7 +256,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
+ assertLog(['App', 'A', 'B', 'C', 'D']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
@@ -263,14 +273,14 @@ describe('ReactDOMServerSelectiveHydration', () => {
ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// This click target cannot be hydrated yet because it's suspended.
await act(async () => {
const result = dispatchClickEvent(spanD);
expect(result).toBe(true);
});
- expect(Scheduler).toHaveYielded([
+ assertLog([
'App',
// Continuing rendering will render B next.
'B',
@@ -289,11 +299,11 @@ describe('ReactDOMServerSelectiveHydration', () => {
flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
)
) {
- expect(Scheduler).toHaveYielded(['D', 'A']);
+ assertLog(['D', 'A']);
} else {
// After the click, we should prioritize D and the Click first,
// and only after that render A and C.
- expect(Scheduler).toHaveYielded(['D', 'Clicked D', 'A']);
+ assertLog(['D', 'Clicked D', 'A']);
}
document.body.removeChild(container);
@@ -342,7 +352,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
+ assertLog(['App', 'A', 'B', 'C', 'D']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
@@ -361,7 +371,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// This click target cannot be hydrated yet because the first is Suspended.
dispatchClickEvent(spanA);
@@ -374,9 +384,9 @@ describe('ReactDOMServerSelectiveHydration', () => {
flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
)
) {
- expect(Scheduler).toHaveYielded(['App', 'C', 'Clicked C']);
+ assertLog(['App', 'C', 'Clicked C']);
} else {
- expect(Scheduler).toHaveYielded(['App']);
+ assertLog(['App']);
}
await act(async () => {
@@ -388,7 +398,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
if (
ReactFeatureFlags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay
) {
- expect(Scheduler).toHaveYielded([
+ assertLog([
'A',
'D',
// B should render last since it wasn't clicked.
@@ -397,7 +407,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
} else {
// We should prioritize hydrating A, C and D first since we clicked in
// them. Only after they're done will we hydrate B.
- expect(Scheduler).toHaveYielded([
+ assertLog([
'A',
'Clicked A',
'C',
@@ -447,7 +457,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'B']);
+ assertLog(['App', 'A', 'B']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
@@ -460,7 +470,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
const span = container.getElementsByTagName('span')[1];
@@ -471,10 +481,10 @@ describe('ReactDOMServerSelectiveHydration', () => {
target.virtualclick();
// We rendered App, B and then invoked the event without rendering A.
- expect(Scheduler).toHaveYielded(['App', 'B', 'Clicked B']);
+ assertLog(['App', 'B', 'Clicked B']);
// After continuing the scheduler, we finally hydrate A.
- expect(Scheduler).toFlushAndYield(['A']);
+ await waitForAll(['A']);
document.body.removeChild(container);
});
@@ -527,7 +537,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
+ assertLog(['App', 'A', 'B', 'C', 'D']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
@@ -545,14 +555,14 @@ describe('ReactDOMServerSelectiveHydration', () => {
ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// Continuing rendering will render B next.
await act(async () => {
const target = createEventTarget(spanD);
target.virtualclick();
});
- expect(Scheduler).toHaveYielded(['App', 'B', 'C']);
+ assertLog(['App', 'B', 'C']);
// After the click, we should prioritize D and the Click first,
// and only after that render A and C.
@@ -568,9 +578,9 @@ describe('ReactDOMServerSelectiveHydration', () => {
)
) {
// no replay
- expect(Scheduler).toHaveYielded(['D', 'A']);
+ assertLog(['D', 'A']);
} else {
- expect(Scheduler).toHaveYielded(['D', 'Clicked D', 'A']);
+ assertLog(['D', 'Clicked D', 'A']);
}
document.body.removeChild(container);
@@ -623,7 +633,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
+ assertLog(['App', 'A', 'B', 'C', 'D']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
@@ -643,7 +653,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// This click target cannot be hydrated yet because the first is Suspended.
createEventTarget(spanA).virtualclick();
@@ -653,9 +663,9 @@ describe('ReactDOMServerSelectiveHydration', () => {
if (
ReactFeatureFlags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay
) {
- expect(Scheduler).toHaveYielded(['App', 'C', 'Clicked C']);
+ assertLog(['App', 'C', 'Clicked C']);
} else {
- expect(Scheduler).toHaveYielded(['App']);
+ assertLog(['App']);
}
await act(async () => {
suspend = false;
@@ -666,7 +676,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
if (
ReactFeatureFlags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay
) {
- expect(Scheduler).toHaveYielded([
+ assertLog([
'A',
'D',
// B should render last since it wasn't clicked.
@@ -675,7 +685,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
} else {
// We should prioritize hydrating A, C and D first since we clicked in
// them. Only after they're done will we hydrate B.
- expect(Scheduler).toHaveYielded([
+ assertLog([
'A',
'Clicked A',
'C',
@@ -734,7 +744,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
);
}
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
+ assertLog(['App', 'A', 'B', 'C', 'D']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
document.body.appendChild(container);
@@ -752,14 +762,14 @@ describe('ReactDOMServerSelectiveHydration', () => {
ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// Click D
dispatchMouseHoverEvent(spanD, null);
dispatchClickEvent(spanD);
// Hover over B and then C.
dispatchMouseHoverEvent(spanB, spanD);
dispatchMouseHoverEvent(spanC, spanB);
- expect(Scheduler).toHaveYielded(['App']);
+ assertLog(['App']);
await act(async () => {
suspend = false;
resolve();
@@ -773,7 +783,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
) {
// We should prioritize hydrating D first because we clicked it.
// but event isnt replayed
- expect(Scheduler).toHaveYielded([
+ assertLog([
'D',
'B', // Ideally this should be later.
'C',
@@ -787,7 +797,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
// the same time since B was already scheduled.
// This is ok because it will at least not continue for nested
// boundary. See the next test below.
- expect(Scheduler).toHaveYielded([
+ assertLog([
'D',
'Clicked D',
'B', // Ideally this should be later.
@@ -883,7 +893,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
+ assertLog(['App', 'A', 'B', 'C', 'D']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
@@ -902,7 +912,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// Click D
dispatchMouseHoverEvent(spanD, null);
@@ -911,7 +921,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
dispatchMouseHoverEvent(spanB, spanD);
dispatchMouseHoverEvent(spanC, spanB);
- expect(Scheduler).toHaveYielded(['App']);
+ assertLog(['App']);
await act(async () => {
suspend = false;
@@ -927,7 +937,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
) {
// We should prioritize hydrating D first because we clicked it.
// but event isnt replayed
- expect(Scheduler).toHaveYielded([
+ assertLog([
'D',
'B', // Ideally this should be later.
'C',
@@ -948,7 +958,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
// the same time since B was already scheduled.
// This is ok because it will at least not continue for nested
// boundary. See the next test below.
- expect(Scheduler).toHaveYielded([
+ assertLog([
'D',
'Clicked D',
'B', // Ideally this should be later.
@@ -964,7 +974,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
// This test shows existing quirk where stopPropagation on mouseout
// prevents mouseEnter from firing
dispatchMouseHoverEvent(spanC, spanB);
- expect(Scheduler).toHaveYielded([
+ assertLog([
'Mouse Out Capture B',
// stopPropagation stops these
// 'Mouse Out B',
@@ -1120,7 +1130,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
expect(InnerScheduler).toHaveYielded(['Suspend Inner']);
}
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
});
afterEach(async () => {
document.body.innerHTML = '';
@@ -1143,13 +1153,13 @@ describe('ReactDOMServerSelectiveHydration', () => {
// Inner App renders because it is unblocked
expect(InnerScheduler).toHaveYielded(['Inner']);
// No event is replayed yet
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
dispatchMouseHoverEvent(innerDiv);
expect(OuterScheduler).toHaveYielded([]);
expect(InnerScheduler).toHaveYielded([]);
// No event is replayed yet
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
await act(async () => {
resolveOuter();
@@ -1166,7 +1176,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
// Outer hydrates and schedules Replay
expect(OuterScheduler).toHaveYielded(['Outer']);
// No event is replayed yet
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// fire scheduled Replay
await act(async () => {
@@ -1177,10 +1187,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
});
// First Inner Mouse Enter fires then Outer Mouse Enter
- expect(Scheduler).toHaveYielded([
- 'Inner Mouse Enter',
- 'Outer Mouse Enter',
- ]);
+ assertLog(['Inner Mouse Enter', 'Outer Mouse Enter']);
});
// @gate enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay
@@ -1209,14 +1216,14 @@ describe('ReactDOMServerSelectiveHydration', () => {
// Inner is still blocked so when Outer replays the event in capture phase
// inner ends up caling stopPropagation
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
expect(OuterScheduler).toHaveYielded([]);
expect(InnerScheduler).toHaveYielded(['Suspend Inner']);
dispatchMouseHoverEvent(innerDiv);
expect(OuterScheduler).toHaveYielded([]);
expect(InnerScheduler).toHaveYielded([]);
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
await act(async () => {
resolveInner();
@@ -1238,10 +1245,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
});
// First Inner Mouse Enter fires then Outer Mouse Enter
- expect(Scheduler).toHaveYielded([
- 'Inner Mouse Enter',
- 'Outer Mouse Enter',
- ]);
+ assertLog(['Inner Mouse Enter', 'Outer Mouse Enter']);
});
});
@@ -1280,7 +1284,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
}
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['Child']);
+ assertLog(['Child']);
const container = document.createElement('div');
@@ -1294,11 +1298,11 @@ describe('ReactDOMServerSelectiveHydration', () => {
dispatchMouseHoverEvent(childDiv);
// Not hydrated so event is saved for replay and stopPropagation is called
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
resolve();
Scheduler.unstable_flushNumberOfYields(1);
- expect(Scheduler).toHaveYielded(['Child']);
+ assertLog(['Child']);
Scheduler.unstable_scheduleCallback(
Scheduler.unstable_ImmediatePriority,
@@ -1316,7 +1320,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
// Even though the tree is remove the event is still dispatched with native event handler
// on the container firing.
- expect(Scheduler).toHaveYielded(['container2 mouse over']);
+ assertLog(['container2 mouse over']);
document.body.removeChild(container);
});
@@ -1366,7 +1370,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']);
+ assertLog(['App', 'A', 'B', 'C', 'D']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
@@ -1385,7 +1389,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// Hover over B and then C.
dispatchMouseHoverEvent(spanB, spanD);
@@ -1402,7 +1406,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
// Next it doesn't matter if we hydrate A or B first but as an
// implementation detail we're currently hydrating B first since
// we at one point hovered over it and we never deprioritized it.
- expect(Scheduler).toHaveYielded(['App', 'C', 'Hover C', 'A', 'B', 'D']);
+ assertLog(['App', 'C', 'Hover C', 'A', 'B', 'D']);
document.body.removeChild(container);
});
@@ -1432,7 +1436,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C']);
+ assertLog(['App', 'A', 'B', 'C']);
const container = document.createElement('div');
container.innerHTML = finalHTML;
@@ -1443,7 +1447,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const root = ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// Increase priority of B and then C.
root.unstable_scheduleHydration(spanB);
@@ -1451,7 +1455,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
// We should prioritize hydrating C first because the last added
// gets highest priority followed by the next added.
- expect(Scheduler).toFlushAndYield(['App', 'C', 'B', 'A']);
+ await waitForAll(['App', 'C', 'B', 'A']);
});
// @gate experimental || www
@@ -1485,7 +1489,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'a', 'B', 'b', 'C', 'c']);
+ assertLog(['App', 'A', 'a', 'B', 'b', 'C', 'c']);
const container = document.createElement('div');
container.innerHTML = finalHTML;
@@ -1497,10 +1501,10 @@ describe('ReactDOMServerSelectiveHydration', () => {
const spanB = container.getElementsByTagName('span')[2];
const spanC = container.getElementsByTagName('span')[4];
- act(() => {
+ await act(async () => {
const root = ReactDOMClient.hydrateRoot(container, );
// Hydrate the shell.
- expect(Scheduler).toFlushAndYieldThrough(['App', 'Commit']);
+ await waitFor(['App', 'Commit']);
// Render an update at Idle priority that needs to update A.
@@ -1510,7 +1514,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
// Start rendering. This will force the first boundary to hydrate
// by scheduling it at one higher pri than Idle.
- expect(Scheduler).toFlushAndYieldThrough([
+ await waitFor([
'App',
// Start hydrating A
@@ -1532,13 +1536,13 @@ describe('ReactDOMServerSelectiveHydration', () => {
// priority levels.
dispatchClickEvent(spanC);
- expect(Scheduler).toHaveYielded([
+ assertLog([
// Hydrate C first since we clicked it.
'C',
'c',
]);
- expect(Scheduler).toFlushAndYield([
+ await waitForAll([
// Finish hydration of A since we forced it to hydrate.
'A',
'a',
@@ -1612,7 +1616,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
'useLayoutEffect does nothing on the server',
]);
- expect(Scheduler).toHaveYielded(['App', 'A', 'B']);
+ assertLog(['App', 'A', 'B']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
@@ -1625,23 +1629,17 @@ describe('ReactDOMServerSelectiveHydration', () => {
ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
// This should synchronously hydrate the root App and the second suspense
// boundary.
dispatchClickEvent(span);
// We rendered App, B and then invoked the event without rendering A.
- expect(Scheduler).toHaveYielded([
- 'App',
- 'B',
- 'Capture Clicked B',
- 'Native Click B',
- 'Clicked B',
- ]);
+ assertLog(['App', 'B', 'Capture Clicked B', 'Native Click B', 'Clicked B']);
// After continuing the scheduler, we finally hydrate A.
- expect(Scheduler).toFlushAndYield(['A']);
+ await waitForAll(['A']);
document.body.removeChild(container);
});
@@ -1686,7 +1684,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
}
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'Child']);
+ assertLog(['App', 'Child']);
const container = document.createElement('div');
document.body.appendChild(container);
@@ -1696,12 +1694,12 @@ describe('ReactDOMServerSelectiveHydration', () => {
ReactDOMClient.hydrateRoot(container, );
// Nothing has been hydrated so far.
- expect(Scheduler).toHaveYielded([]);
+ assertLog([]);
const span = container.getElementsByTagName('span')[0];
dispatchClickEvent(span);
- expect(Scheduler).toHaveYielded(['App']);
+ assertLog(['App']);
dispatchClickEvent(span);
@@ -1740,7 +1738,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A']);
+ assertLog(['App', 'A']);
const container = document.createElement('div');
// We need this to be in the document since we'll dispatch events on it.
@@ -1756,12 +1754,12 @@ describe('ReactDOMServerSelectiveHydration', () => {
React.startTransition(() => {
ReactDOMClient.hydrateRoot(container, );
});
- expect(Scheduler).toFlushAndYieldThrough(['App']);
+ await waitFor(['App']);
// This should attempt to synchronously hydrate the root, then pause
// because it still suspended
const result = dispatchClickEvent(span);
- expect(Scheduler).toHaveYielded(['App']);
+ assertLog(['App']);
// The event should not have been cancelled because we didn't hydrate.
expect(result).toBe(true);
@@ -1778,9 +1776,9 @@ describe('ReactDOMServerSelectiveHydration', () => {
flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
)
) {
- expect(Scheduler).toHaveYielded(['App', 'A']);
+ assertLog(['App', 'A']);
} else {
- expect(Scheduler).toHaveYielded(['App', 'A', 'Clicked A']);
+ assertLog(['App', 'A', 'Clicked A']);
}
document.body.removeChild(container);
@@ -1804,20 +1802,20 @@ describe('ReactDOMServerSelectiveHydration', () => {
let spanRef;
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App A', 'Child A']);
+ assertLog(['App A', 'Child A']);
const container = document.createElement('div');
document.body.appendChild(container);
container.innerHTML = finalHTML;
const initialSpan = container.getElementsByTagName('span')[0];
const root = ReactDOMClient.hydrateRoot(container, );
- expect(Scheduler).toFlushUntilNextPaint(['App A']);
+ await waitForPaint(['App A']);
await act(async () => {
ReactDOM.flushSync(() => {
root.render();
});
});
- expect(Scheduler).toHaveYielded(['App B', 'Child A', 'App B', 'Child B']);
+ assertLog(['App B', 'Child A', 'App B', 'Child B']);
expect(initialSpan).toBe(spanRef);
});
@@ -1840,13 +1838,13 @@ describe('ReactDOMServerSelectiveHydration', () => {
let spanRef;
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App A', 'Child A']);
+ assertLog(['App A', 'Child A']);
const container = document.createElement('div');
document.body.appendChild(container);
container.innerHTML = finalHTML;
const initialSpan = container.getElementsByTagName('span')[0];
const root = ReactDOMClient.hydrateRoot(container, );
- expect(Scheduler).toFlushUntilNextPaint(['App A']);
+ await waitForPaint(['App A']);
await act(async () => {
TODO_scheduleContinuousSchedulerTask(() => {
@@ -1854,7 +1852,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
});
});
- expect(Scheduler).toHaveYielded(['App B', 'Child A', 'App B', 'Child B']);
+ assertLog(['App B', 'Child A', 'App B', 'Child B']);
expect(initialSpan).toBe(spanRef);
});
@@ -1876,17 +1874,17 @@ describe('ReactDOMServerSelectiveHydration', () => {
let spanRef;
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App A', 'Child A']);
+ assertLog(['App A', 'Child A']);
const container = document.createElement('div');
document.body.appendChild(container);
container.innerHTML = finalHTML;
const initialSpan = container.getElementsByTagName('span')[0];
const root = ReactDOMClient.hydrateRoot(container, );
- expect(Scheduler).toFlushUntilNextPaint(['App A']);
+ await waitForPaint(['App A']);
await act(async () => {
root.render();
});
- expect(Scheduler).toHaveYielded(['App B', 'Child A', 'App B', 'Child B']);
+ assertLog(['App B', 'Child A', 'App B', 'Child B']);
expect(initialSpan).toBe(spanRef);
});
@@ -1927,7 +1925,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
);
}
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'DefaultContext']);
+ assertLog(['App', 'A', 'DefaultContext']);
const container = document.createElement('div');
container.innerHTML = finalHTML;
document.body.appendChild(container);
@@ -1936,25 +1934,16 @@ describe('ReactDOMServerSelectiveHydration', () => {
await act(async () => {
const root = ReactDOMClient.hydrateRoot(container, );
- expect(Scheduler).toFlushAndYieldThrough([
- 'App',
- 'DefaultContext',
- 'Commit',
- ]);
+ await waitFor(['App', 'DefaultContext', 'Commit']);
TODO_scheduleIdleDOMSchedulerTask(() => {
root.render();
});
- expect(Scheduler).toFlushAndYieldThrough(['App', 'A']);
+ await waitFor(['App', 'A']);
dispatchClickEvent(spanA);
- expect(Scheduler).toHaveYielded(['A']);
- expect(Scheduler).toFlushAndYield([
- 'App',
- 'AA',
- 'DefaultContext',
- 'Commit',
- ]);
+ assertLog(['A']);
+ await waitForAll(['App', 'AA', 'DefaultContext', 'Commit']);
});
});
@@ -1994,29 +1983,18 @@ describe('ReactDOMServerSelectiveHydration', () => {
);
}
const finalHTML = ReactDOMServer.renderToString();
- expect(Scheduler).toHaveYielded(['App', 'A', 'DefaultContext']);
+ assertLog(['App', 'A', 'DefaultContext']);
const container = document.createElement('div');
container.innerHTML = finalHTML;
await act(async () => {
const root = ReactDOMClient.hydrateRoot(container, );
- expect(Scheduler).toFlushAndYieldThrough([
- 'App',
- 'DefaultContext',
- 'Commit',
- ]);
+ await waitFor(['App', 'DefaultContext', 'Commit']);
ReactDOM.flushSync(() => {
root.render();
});
- expect(Scheduler).toHaveYielded([
- 'App',
- 'A',
- 'App',
- 'AA',
- 'DefaultContext',
- 'Commit',
- ]);
+ assertLog(['App', 'A', 'App', 'AA', 'DefaultContext', 'Commit']);
});
});
});
diff --git a/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js b/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js
index 26d50204a619f..f8056fd6042e7 100644
--- a/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js
@@ -12,7 +12,6 @@
let JSDOM;
let Stream;
-let Scheduler;
let React;
let ReactDOM;
let ReactDOMClient;
@@ -23,18 +22,21 @@ let container;
let buffer = '';
let hasErrored = false;
let fatalError = undefined;
+let waitForAll;
describe('ReactDOM HostSingleton', () => {
beforeEach(() => {
jest.resetModules();
JSDOM = require('jsdom').JSDOM;
- Scheduler = require('scheduler');
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMFizzServer = require('react-dom/server');
Stream = require('stream');
+ const InternalTestUtils = require('internal-test-utils');
+ waitForAll = InternalTestUtils.waitForAll;
+
// Test Environment
const jsdom = new JSDOM(
'',
@@ -130,7 +132,7 @@ describe('ReactDOM HostSingleton', () => {
,
);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(getVisibleChildren(document)).toEqual(
@@ -150,8 +152,8 @@ describe('ReactDOM HostSingleton', () => {
,
);
- expect(() => {
- expect(Scheduler).toFlushWithoutYielding();
+ await expect(async () => {
+ await waitForAll([]);
}).toErrorDev(
'Warning: You are mounting a new head component when a previous one has not first unmounted. It is an error to render more than one head component at a time and attributes and children of these components will likely fail in unpredictable ways. Please only render a single instance of and if you need to mount a new one, ensure any previous ones have unmounted first',
);
@@ -175,7 +177,7 @@ describe('ReactDOM HostSingleton', () => {
,
);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(getVisibleChildren(document)).toEqual(
@@ -193,7 +195,7 @@ describe('ReactDOM HostSingleton', () => {
,
);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(getVisibleChildren(document)).toEqual(
@@ -280,7 +282,7 @@ describe('ReactDOM HostSingleton', () => {
,
);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(persistentElements).toEqual([
document.documentElement,
document.head,
@@ -320,7 +322,7 @@ describe('ReactDOM HostSingleton', () => {
,
);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(persistentElements).toEqual([
document.documentElement,
document.head,
@@ -359,7 +361,7 @@ describe('ReactDOM HostSingleton', () => {
,
);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(persistentElements).toEqual([
document.documentElement,
document.head,
@@ -395,7 +397,7 @@ describe('ReactDOM HostSingleton', () => {
,
);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(persistentElements).toEqual([
document.documentElement,
document.head,
@@ -422,7 +424,7 @@ describe('ReactDOM HostSingleton', () => {
// unmount the root
root.unmount();
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(persistentElements).toEqual([
document.documentElement,
document.head,
@@ -471,8 +473,8 @@ describe('ReactDOM HostSingleton', () => {
},
},
);
- expect(() => {
- expect(Scheduler).toFlushWithoutYielding();
+ await expect(async () => {
+ await waitForAll([]);
}).toErrorDev(
[
`Warning: Expected server HTML to contain a matching in .
@@ -555,7 +557,7 @@ describe('ReactDOM HostSingleton', () => {
},
);
expect(hydrationErrors).toEqual([]);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
expect(persistentElements).toEqual([
document.documentElement,
document.head,
@@ -627,7 +629,7 @@ describe('ReactDOM HostSingleton', () => {
,
);
- expect(Scheduler).toFlushWithoutYielding();
+ await waitForAll([]);
// We construct and insert some artificial stylesheets mimicing what a 3rd party script might do
// In the future we could hydrate with these already in the document but the rules are restrictive
@@ -683,7 +685,7 @@ describe('ReactDOM HostSingleton', () => {