diff --git a/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
index 4440f900294a..ab68bdc7817c 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
@@ -1,10 +1,12 @@
let React;
-let ReactTestRenderer;
+let ReactDOMClient;
+let ReactDOM;
let ReactFeatureFlags;
let Scheduler;
let Suspense;
let act;
let textCache;
+let container;
let assertLog;
let waitForPaint;
@@ -18,9 +20,11 @@ describe('ReactSuspense', () => {
ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
React = require('react');
- ReactTestRenderer = require('react-test-renderer');
+ ReactDOM = require('react-dom');
+ ReactDOMClient = require('react-dom/client');
act = require('internal-test-utils').act;
Scheduler = require('scheduler');
+ container = document.createElement('div');
Suspense = React.Suspense;
@@ -115,18 +119,19 @@ describe('ReactSuspense', () => {
);
}
+ const root = ReactDOMClient.createRoot(container);
// Render an empty shell
- const root = ReactTestRenderer.create(, {
- isConcurrent: true,
+ await act(() => {
+ root.render();
});
-
- await waitForAll(['Foo']);
- expect(root).toMatchRenderedOutput(null);
+ assertLog(['Foo']);
+ const renderedEl = container;
+ expect(renderedEl.innerText).toBeUndefined();
// Navigate the shell to now render the child content.
// This should suspend.
React.startTransition(() => {
- root.update();
+ root.render();
});
await waitForAll([
@@ -136,19 +141,20 @@ describe('ReactSuspense', () => {
'Suspend! [A]',
'Loading...',
]);
- expect(root).toMatchRenderedOutput(null);
+ expect(container.textContent).toEqual('');
await waitForAll([]);
- expect(root).toMatchRenderedOutput(null);
+ expect(container.textContent).toEqual('');
- await resolveText('A');
+ resolveText('A');
await waitForAll(['Foo', 'Bar', 'A', 'B']);
- expect(root).toMatchRenderedOutput('AB');
+ expect(container.textContent).toEqual('AB');
});
it('suspends siblings and later recovers each independently', async () => {
+ const root = ReactDOMClient.createRoot(container);
// Render two sibling Suspense components
- const root = ReactTestRenderer.create(
+ root.render(
<>
}>
@@ -157,9 +163,6 @@ describe('ReactSuspense', () => {
>,
- {
- isConcurrent: true,
- },
);
await waitForAll([
@@ -168,19 +171,19 @@ describe('ReactSuspense', () => {
'Suspend! [B]',
'Loading B...',
]);
- expect(root).toMatchRenderedOutput('Loading A...Loading B...');
+ expect(container.innerHTML).toEqual('Loading A...Loading B...');
// Resolve first Suspense's promise and switch back to the normal view. The
// second Suspense should still show the placeholder
await act(() => resolveText('A'));
assertLog(['A']);
- expect(root).toMatchRenderedOutput('ALoading B...');
+ expect(container.textContent).toEqual('ALoading B...');
// Resolve the second Suspense's promise resolves and switche back to the
// normal view
await act(() => resolveText('B'));
assertLog(['B']);
- expect(root).toMatchRenderedOutput('AB');
+ expect(container.textContent).toEqual('AB');
});
it('interrupts current render if promise resolves before current render phase', async () => {
@@ -210,22 +213,22 @@ describe('ReactSuspense', () => {
Scheduler.log('Async');
return 'Async';
}
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render(
+ <>
+ } />
+
+ >,
+ );
+ });
- const root = ReactTestRenderer.create(
- <>
- } />
-
- >,
- {
- isConcurrent: true,
- },
- );
- await waitForAll(['Initial']);
- expect(root).toMatchRenderedOutput('Initial');
+ assertLog(['Initial']);
+ expect(container.textContent).toEqual('Initial');
// The update will suspend.
React.startTransition(() => {
- root.update(
+ root.render(
<>
}>
@@ -242,7 +245,7 @@ describe('ReactSuspense', () => {
// The promise resolves before the current render phase has completed
resolveThenable();
assertLog([]);
- expect(root).toMatchRenderedOutput('Initial');
+ expect(container.textContent).toEqual('Initial');
// Start over from the root, instead of continuing.
await waitForAll([
@@ -251,7 +254,7 @@ describe('ReactSuspense', () => {
'After Suspense',
'Sibling',
]);
- expect(root).toMatchRenderedOutput('AsyncAfter SuspenseSibling');
+ expect(container.textContent).toEqual('AsyncAfter SuspenseSibling');
});
it('throttles fallback committing globally', async () => {
@@ -267,12 +270,13 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create(, {
- isConcurrent: true,
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render();
});
- await waitForAll(['Foo', 'Suspend! [A]', 'Loading...']);
- expect(root).toMatchRenderedOutput('Loading...');
+ assertLog(['Foo', 'Suspend! [A]', 'Loading...']);
+ expect(container.textContent).toEqual('Loading...');
await resolveText('A');
await waitForAll(['A', 'Suspend! [B]', 'Loading more...']);
@@ -280,13 +284,13 @@ describe('ReactSuspense', () => {
// By this point, we have enough info to show "A" and "Loading more..."
// However, we've just shown the outer fallback. So we'll delay
// showing the inner fallback hoping that B will resolve soon enough.
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
await act(() => resolveText('B'));
// By this point, B has resolved.
// The contents of both should pop in together.
assertLog(['A', 'B']);
- expect(root).toMatchRenderedOutput('AB');
+ expect(container.textContent).toEqual('AB');
});
it('does not throttle fallback committing for too long', async () => {
@@ -302,12 +306,12 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create(, {
- isConcurrent: true,
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render();
});
-
- await waitForAll(['Foo', 'Suspend! [A]', 'Loading...']);
- expect(root).toMatchRenderedOutput('Loading...');
+ assertLog(['Foo', 'Suspend! [A]', 'Loading...']);
+ expect(container.textContent).toEqual('Loading...');
await resolveText('A');
await waitForAll(['A', 'Suspend! [B]', 'Loading more...']);
@@ -315,16 +319,16 @@ describe('ReactSuspense', () => {
// By this point, we have enough info to show "A" and "Loading more..."
// However, we've just shown the outer fallback. So we'll delay
// showing the inner fallback hoping that B will resolve soon enough.
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
// But if we wait a bit longer, eventually we'll give up and show a
// fallback. The exact value here isn't important. It's a JND ("Just
// Noticeable Difference").
jest.advanceTimersByTime(500);
- expect(root).toMatchRenderedOutput('ALoading more...');
+ expect(container.textContent).toEqual('ALoading more...');
await act(() => resolveText('B'));
assertLog(['B']);
- expect(root).toMatchRenderedOutput('AB');
+ expect(container.textContent).toEqual('AB');
});
// @gate forceConcurrentByDefaultForTesting
@@ -333,10 +337,12 @@ describe('ReactSuspense', () => {
"delay and we've already skipped over a lower priority update in " +
'a parent',
async () => {
+ const root = ReactDOMClient.createRoot(container);
+
function interrupt() {
// React has a heuristic to batch all updates that occur within the same
// event. This is a trick to circumvent that heuristic.
- ReactTestRenderer.create('whatever');
+ ReactDOM.render('whatever', document.createElement('div'));
}
function App({shouldSuspend, step}) {
@@ -352,16 +358,12 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create(null, {
- isConcurrent: true,
- });
-
- root.update();
+ root.render();
await waitForAll(['A0', 'B0', 'C0']);
- expect(root).toMatchRenderedOutput('A0B0C0');
+ expect(container.textContent).toEqual('A0B0C0');
// This update will suspend.
- root.update();
+ root.render();
// Do a bit of work
await waitFor(['A1']);
@@ -369,7 +371,7 @@ describe('ReactSuspense', () => {
// Schedule another update. This will have lower priority because it's
// a transition.
React.startTransition(() => {
- root.update();
+ root.render();
});
// Interrupt to trigger a restart.
@@ -384,7 +386,7 @@ describe('ReactSuspense', () => {
]);
// Should not have committed loading state
- expect(root).toMatchRenderedOutput('A0B0C0');
+ expect(container.textContent).toEqual('A0B0C0');
// After suspending, should abort the first update and switch to the
// second update. So, C1 should not appear in the log.
@@ -393,11 +395,11 @@ describe('ReactSuspense', () => {
// the render before the end of the current slice of work.
await waitForAll(['A2', 'B2', 'C2']);
- expect(root).toMatchRenderedOutput('A2B2C2');
+ expect(container.textContent).toEqual('A2B2C2');
},
);
- it('mounts a lazy class component in non-concurrent mode', async () => {
+ it('mounts a lazy class component in non-concurrent mode (legacy)', async () => {
class Class extends React.Component {
componentDidMount() {
Scheduler.log('Did mount: ' + this.props.label);
@@ -416,19 +418,20 @@ describe('ReactSuspense', () => {
const LazyClass = React.lazy(() => fakeImport(Class));
- const root = ReactTestRenderer.create(
+ ReactDOM.render(
}>
,
+ container,
);
assertLog(['Loading...']);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
await LazyClass;
await waitForPaint(['Hi', 'Did mount: Hi']);
- expect(root).toMatchRenderedOutput('Hi');
+ expect(container.textContent).toEqual('Hi');
});
it('updates memoized child of suspense component when context updates (simple memo)', async () => {
@@ -455,21 +458,22 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create(, {
- isConcurrent: true,
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render();
});
- await waitForAll(['Suspend! [default]', 'Loading...']);
+ assertLog(['Suspend! [default]', 'Loading...']);
await act(() => resolveText('default'));
assertLog(['default']);
- expect(root).toMatchRenderedOutput('default');
+ expect(container.textContent).toEqual('default');
await act(() => setValue('new value'));
assertLog(['Suspend! [new value]', 'Loading...']);
await act(() => resolveText('new value'));
assertLog(['new value']);
- expect(root).toMatchRenderedOutput('new value');
+ expect(container.textContent).toEqual('new value');
});
it('updates memoized child of suspense component when context updates (manual memo)', async () => {
@@ -501,21 +505,22 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create(, {
- isConcurrent: true,
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render();
});
- await waitForAll(['Suspend! [default]', 'Loading...']);
+ assertLog(['Suspend! [default]', 'Loading...']);
await act(() => resolveText('default'));
assertLog(['default']);
- expect(root).toMatchRenderedOutput('default');
+ expect(container.textContent).toEqual('default');
await act(() => setValue('new value'));
assertLog(['Suspend! [new value]', 'Loading...']);
await act(() => resolveText('new value'));
assertLog(['new value']);
- expect(root).toMatchRenderedOutput('new value');
+ expect(container.textContent).toEqual('new value');
});
it('updates memoized child of suspense component when context updates (function)', async () => {
@@ -538,28 +543,28 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create(
-
- }>
-
-
- ,
- {
- isConcurrent: true,
- },
- );
- await waitForAll(['Suspend! [default]', 'Loading...']);
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render(
+
+ }>
+
+
+ ,
+ );
+ });
+ assertLog(['Suspend! [default]', 'Loading...']);
await act(() => resolveText('default'));
assertLog(['default']);
- expect(root).toMatchRenderedOutput('default');
+ expect(container.textContent).toEqual('default');
await act(() => setValue('new value'));
assertLog(['Suspend! [new value]', 'Loading...']);
await act(() => resolveText('new value'));
assertLog(['new value']);
- expect(root).toMatchRenderedOutput('new value');
+ expect(container.textContent).toEqual('new value');
});
it('updates memoized child of suspense component when context updates (forwardRef)', async () => {
@@ -582,28 +587,28 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create(
-
- }>
-
-
- ,
- {
- isConcurrent: true,
- },
- );
- await waitForAll(['Suspend! [default]', 'Loading...']);
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render(
+
+ }>
+
+
+ ,
+ );
+ });
+ assertLog(['Suspend! [default]', 'Loading...']);
await act(() => resolveText('default'));
assertLog(['default']);
- expect(root).toMatchRenderedOutput('default');
+ expect(container.textContent).toEqual('default');
await act(() => setValue('new value'));
assertLog(['Suspend! [new value]', 'Loading...']);
await act(() => resolveText('new value'));
assertLog(['new value']);
- expect(root).toMatchRenderedOutput('new value');
+ expect(container.textContent).toEqual('new value');
});
it('re-fires layout effects when re-showing Suspense', async () => {
@@ -630,12 +635,13 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create(, {
- isConcurrent: true,
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render();
});
- await waitForAll(['Child 1', 'create layout']);
- expect(root).toMatchRenderedOutput('Child 1');
+ assertLog(['Child 1', 'create layout']);
+ expect(container.textContent).toEqual('Child 1');
await act(() => {
_setShow(true);
@@ -649,10 +655,36 @@ describe('ReactSuspense', () => {
await act(() => resolveText('Child 2'));
assertLog(['Child 1', 'Child 2', 'create layout']);
- expect(root).toMatchRenderedOutput(['Child 1', 'Child 2'].join(''));
+ expect(container.textContent).toEqual(['Child 1', 'Child 2'].join(''));
});
- describe('outside concurrent mode', () => {
+ it('does not get stuck with fallback in concurrent mode for a large delay', async () => {
+ function App(props) {
+ return (
+ }>
+
+
+
+ );
+ }
+
+ const root = ReactDOMClient.createRoot(container);
+ await act(() => {
+ root.render();
+ });
+
+ assertLog(['Suspend! [Child 1]', 'Loading...']);
+ await resolveText('Child 1');
+ await waitForAll(['Child 1', 'Suspend! [Child 2]']);
+
+ jest.advanceTimersByTime(6000);
+
+ await act(() => resolveText('Child 2'));
+ assertLog(['Child 1', 'Child 2']);
+ expect(container.textContent).toEqual(['Child 1', 'Child 2'].join(''));
+ });
+
+ describe('outside concurrent mode (legacy)', () => {
it('a mounted class component can suspend without losing state', async () => {
class TextWithLifecycle extends React.Component {
componentDidMount() {
@@ -698,8 +730,7 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create();
-
+ ReactDOM.render(, container);
assertLog([
'A',
'Suspend! [B:1]',
@@ -712,24 +743,24 @@ describe('ReactSuspense', () => {
'Mount [C]',
'Mount [Loading...]',
]);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
await resolveText('B:1');
- await waitForPaint([
+ assertLog([
'B:1',
'Unmount [Loading...]',
// Should be a mount, not an update
'Mount [B:1]',
]);
- expect(root).toMatchRenderedOutput('AB:1C');
+ expect(container.textContent).toEqual('AB:1C');
instance.setState({step: 2});
assertLog(['Suspend! [B:2]', 'Loading...', 'Mount [Loading...]']);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
await resolveText('B:2');
- await waitForPaint(['B:2', 'Unmount [Loading...]', 'Update [B:2]']);
- expect(root).toMatchRenderedOutput('AB:2C');
+ assertLog(['B:2', 'Unmount [Loading...]', 'Update [B:2]']);
+ expect(container.textContent).toEqual('AB:2C');
});
it('bails out on timed-out primary children even if they receive an update', async () => {
@@ -751,25 +782,25 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create();
+ ReactDOM.render(, container);
assertLog(['Stateful: 1', 'Suspend! [A]', 'Loading...']);
await resolveText('A');
- await waitForPaint(['A']);
- expect(root).toMatchRenderedOutput('Stateful: 1A');
+ assertLog(['A']);
+ expect(container.textContent).toEqual('Stateful: 1A');
- root.update();
+ ReactDOM.render(, container);
assertLog(['Stateful: 1', 'Suspend! [B]', 'Loading...']);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
instance.setState({step: 2});
assertLog(['Stateful: 2', 'Suspend! [B]']);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
await resolveText('B');
- await waitForPaint(['B']);
- expect(root).toMatchRenderedOutput('Stateful: 2B');
+ assertLog(['B']);
+ expect(container.textContent).toEqual('Stateful: 2B');
});
it('when updating a timed-out tree, always retries the suspended component', async () => {
@@ -799,17 +830,17 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create();
+ ReactDOM.render(, container);
assertLog(['Stateful: 1', 'Suspend! [A]', 'Loading...']);
await resolveText('A');
- await waitForPaint(['A']);
- expect(root).toMatchRenderedOutput('Stateful: 1A');
+ assertLog(['A']);
+ expect(container.textContent).toEqual('Stateful: 1A');
- root.update();
+ ReactDOM.render(, container);
assertLog(['Stateful: 1', 'Suspend! [B]', 'Loading...']);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
instance.setState({step: 2});
assertLog([
@@ -820,14 +851,14 @@ describe('ReactSuspense', () => {
// pending work, so it was improperly treated as complete.
'Suspend! [B]',
]);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
await resolveText('B');
- await waitForPaint(['B']);
- expect(root).toMatchRenderedOutput('Stateful: 2B');
+ assertLog(['B']);
+ expect(container.textContent).toEqual('Stateful: 2B');
});
- it('suspends in a class that has componentWillUnmount and is then deleted', () => {
+ it('suspends in a class that has componentWillUnmount and is then deleted', async () => {
class AsyncTextWithUnmount extends React.Component {
componentWillUnmount() {
Scheduler.log('will unmount');
@@ -845,12 +876,12 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create();
+ ReactDOM.render(, container);
assertLog(['Suspend! [A]', 'Loading...']);
- root.update();
+ ReactDOM.render(, container);
// Should not fire componentWillUnmount
assertLog(['B']);
- expect(root).toMatchRenderedOutput('B');
+ expect(container.textContent).toEqual('B');
});
it('suspends in a component that also contains useEffect', async () => {
@@ -874,10 +905,10 @@ describe('ReactSuspense', () => {
);
}
- ReactTestRenderer.create();
+ ReactDOM.render(, container);
assertLog(['Suspend! [A]', 'Loading...']);
await resolveText('A');
- await waitForPaint(['A', 'Did commit: A']);
+ assertLog(['A', 'Did commit: A']);
});
it('retries when an update is scheduled on a timed out tree', async () => {
@@ -898,35 +929,33 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create(, {
- isConcurrent: true,
- });
+ ReactDOM.render(, container);
// Initial render
- await waitForAll(['Suspend! [Step: 1]', 'Loading...']);
+ assertLog(['Suspend! [Step: 1]', 'Loading...']);
await act(() => resolveText('Step: 1'));
assertLog(['Step: 1']);
- expect(root).toMatchRenderedOutput('Step: 1');
+ expect(container.textContent).toEqual('Step: 1');
// Update that suspends
await act(() => {
instance.setState({step: 2});
});
assertLog(['Suspend! [Step: 2]', 'Loading...']);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
// Update while still suspended
instance.setState({step: 3});
- await waitForAll(['Suspend! [Step: 3]']);
- expect(root).toMatchRenderedOutput('Loading...');
+ assertLog(['Suspend! [Step: 3]']);
+ expect(container.textContent).toEqual('Loading...');
await act(() => {
resolveText('Step: 2');
resolveText('Step: 3');
});
assertLog(['Step: 3']);
- expect(root).toMatchRenderedOutput('Step: 3');
+ expect(container.textContent).toEqual('Step: 3');
});
it('does not remount the fallback while suspended children resolve in legacy mode', async () => {
@@ -950,7 +979,7 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create();
+ ReactDOM.render(, container);
// Initial render
assertLog([
@@ -962,48 +991,19 @@ describe('ReactSuspense', () => {
await waitForAll([]);
await resolveText('Child 1');
- await waitForPaint([
- 'Child 1',
- 'Suspend! [Child 2]',
- 'Suspend! [Child 3]',
- ]);
+ assertLog(['Child 1', 'Suspend! [Child 2]', 'Suspend! [Child 3]']);
await resolveText('Child 2');
- await waitForPaint(['Child 2', 'Suspend! [Child 3]']);
+ assertLog(['Child 2', 'Suspend! [Child 3]']);
await resolveText('Child 3');
- await waitForPaint(['Child 3']);
- expect(root).toMatchRenderedOutput(
+ assertLog(['Child 3']);
+ expect(container.textContent).toEqual(
['Child 1', 'Child 2', 'Child 3'].join(''),
);
expect(mounts).toBe(1);
});
- it('does not get stuck with fallback in concurrent mode for a large delay', async () => {
- function App(props) {
- return (
- }>
-
-
-
- );
- }
-
- const root = ReactTestRenderer.create(, {
- isConcurrent: true,
- });
-
- await waitForAll(['Suspend! [Child 1]', 'Loading...']);
- await resolveText('Child 1');
- await waitForAll(['Child 1', 'Suspend! [Child 2]']);
-
- jest.advanceTimersByTime(6000);
-
- await act(() => resolveText('Child 2'));
- assertLog(['Child 1', 'Child 2']);
- expect(root).toMatchRenderedOutput(['Child 1', 'Child 2'].join(''));
- });
-
it('reuses effects, including deletions, from the suspended tree', async () => {
const {useState} = React;
@@ -1020,35 +1020,35 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create();
+ ReactDOM.render(, container);
assertLog(['Suspend! [Tab: 0]', ' + sibling', 'Loading...']);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
await resolveText('Tab: 0');
- await waitForPaint(['Tab: 0']);
- expect(root).toMatchRenderedOutput('Tab: 0 + sibling');
+ assertLog(['Tab: 0']);
+ expect(container.textContent).toEqual('Tab: 0 + sibling');
await act(() => setTab(1));
assertLog(['Suspend! [Tab: 1]', ' + sibling', 'Loading...']);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
await resolveText('Tab: 1');
- await waitForPaint(['Tab: 1']);
- expect(root).toMatchRenderedOutput('Tab: 1 + sibling');
+ assertLog(['Tab: 1']);
+ expect(container.textContent).toEqual('Tab: 1 + sibling');
await act(() => setTab(2));
assertLog(['Suspend! [Tab: 2]', ' + sibling', 'Loading...']);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
await resolveText('Tab: 2');
- await waitForPaint(['Tab: 2']);
- expect(root).toMatchRenderedOutput('Tab: 2 + sibling');
+ assertLog(['Tab: 2']);
+ expect(container.textContent).toEqual('Tab: 2 + sibling');
});
it('does not warn if a mounted component is pinged', async () => {
const {useState} = React;
- const root = ReactTestRenderer.create(null);
+ ReactDOM.render(null, container);
let setStep;
function UpdatingText({text, ms}) {
@@ -1058,25 +1058,22 @@ describe('ReactSuspense', () => {
return ;
}
- root.update(
+ ReactDOM.render(
}>
,
+ container,
);
assertLog(['Suspend! [A:0]', 'Loading...']);
await resolveText('A:0');
- await waitForPaint(['A:0']);
- expect(root).toMatchRenderedOutput('A:0');
+ assertLog(['A:0']);
+ expect(container.textContent).toEqual('A:0');
await act(() => setStep(1));
assertLog(['Suspend! [A:1]', 'Loading...']);
- expect(root).toMatchRenderedOutput('Loading...');
-
- await act(() => {
- root.update(null);
- });
+ expect(container.textContent).toEqual('Loading...');
});
it('memoizes promise listeners per thread ID to prevent redundant renders', async () => {
@@ -1090,14 +1087,14 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create(null);
+ ReactDOM.render(null, container);
- root.update();
+ ReactDOM.render(, container);
assertLog(['Suspend! [A]', 'Suspend! [B]', 'Suspend! [C]', 'Loading...']);
await resolveText('A');
- await waitForPaint([
+ assertLog([
'A',
// The promises for B and C have now been thrown twice
'Suspend! [B]',
@@ -1105,7 +1102,7 @@ describe('ReactSuspense', () => {
]);
await resolveText('B');
- await waitForPaint([
+ assertLog([
// Even though the promise for B was thrown twice, we should only
// re-render once.
'B',
@@ -1114,7 +1111,7 @@ describe('ReactSuspense', () => {
]);
await resolveText('C');
- await waitForPaint([
+ assertLog([
// Even though the promise for C was thrown three times, we should only
// re-render once.
'C',
@@ -1153,11 +1150,9 @@ describe('ReactSuspense', () => {
}
}
- const root = ReactTestRenderer.create(null);
+ ReactDOM.render(null, container);
- await act(() => {
- root.update();
- });
+ ReactDOM.render(, container);
});
it('updates memoized child of suspense component when context updates (simple memo)', async () => {
@@ -1184,19 +1179,19 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create();
+ ReactDOM.render(, container);
assertLog(['Suspend! [default]', 'Loading...']);
await resolveText('default');
- await waitForPaint(['default']);
- expect(root).toMatchRenderedOutput('default');
+ assertLog(['default']);
+ expect(container.textContent).toEqual('default');
await act(() => setValue('new value'));
assertLog(['Suspend! [new value]', 'Loading...']);
await resolveText('new value');
- await waitForPaint(['new value']);
- expect(root).toMatchRenderedOutput('new value');
+ assertLog(['new value']);
+ expect(container.textContent).toEqual('new value');
});
it('updates memoized child of suspense component when context updates (manual memo)', async () => {
@@ -1228,19 +1223,19 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create();
+ ReactDOM.render(, container);
assertLog(['Suspend! [default]', 'Loading...']);
await resolveText('default');
- await waitForPaint(['default']);
- expect(root).toMatchRenderedOutput('default');
+ assertLog(['default']);
+ expect(container.textContent).toEqual('default');
await act(() => setValue('new value'));
assertLog(['Suspend! [new value]', 'Loading...']);
await resolveText('new value');
- await waitForPaint(['new value']);
- expect(root).toMatchRenderedOutput('new value');
+ assertLog(['new value']);
+ expect(container.textContent).toEqual('new value');
});
it('updates memoized child of suspense component when context updates (function)', async () => {
@@ -1265,25 +1260,26 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create(
+ ReactDOM.render(
}>
,
+ container,
);
assertLog(['Suspend! [default]', 'Loading...']);
await resolveText('default');
- await waitForPaint(['default']);
- expect(root).toMatchRenderedOutput('default');
+ assertLog(['default']);
+ expect(container.textContent).toEqual('default');
await act(() => setValue('new value'));
assertLog(['Suspend! [new value]', 'Loading...']);
await resolveText('new value');
- await waitForPaint(['new value']);
- expect(root).toMatchRenderedOutput('new value');
+ assertLog(['new value']);
+ expect(container.textContent).toEqual('new value');
});
it('updates memoized child of suspense component when context updates (forwardRef)', async () => {
@@ -1310,19 +1306,19 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create();
+ ReactDOM.render(, container);
assertLog(['Suspend! [default]', 'Loading...']);
await resolveText('default');
- await waitForPaint(['default']);
- expect(root).toMatchRenderedOutput('default');
+ assertLog(['default']);
+ expect(container.textContent).toEqual('default');
await act(() => setValue('new value'));
assertLog(['Suspend! [new value]', 'Loading...']);
await resolveText('new value');
- await waitForPaint(['new value']);
- expect(root).toMatchRenderedOutput('new value');
+ assertLog(['new value']);
+ expect(container.textContent).toEqual('new value');
});
it('updates context consumer within child of suspended suspense component when context updates', async () => {
@@ -1364,17 +1360,17 @@ describe('ReactSuspense', () => {
);
}
- const root = ReactTestRenderer.create();
+ ReactDOM.render(, container);
assertLog(['Received context value [default]', 'default']);
- expect(root).toMatchRenderedOutput('default');
+ expect(container.textContent).toEqual('default');
await act(() => setValue('new value'));
assertLog(['Received context value [new value]', 'Loading...']);
- expect(root).toMatchRenderedOutput('Loading...');
+ expect(container.textContent).toEqual('Loading...');
await act(() => setValue('default'));
assertLog(['Received context value [default]', 'default']);
- expect(root).toMatchRenderedOutput('default');
+ expect(container.textContent).toEqual('default');
});
});
});