Skip to content

Commit d498ee9

Browse files
committed
Failing test case for deeply nested Suspense in Fallback
When a deep Suspense boundary is partially complete before a parent boundary resolves it can trigger a scenario that React thinks is a bug. This change implements a test case demonstrating this bug. It does not solve it
1 parent dddcae7 commit d498ee9

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed

packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10447,4 +10447,142 @@ describe('ReactDOMFizzServer', () => {
1044710447
</html>,
1044810448
);
1044910449
});
10450+
10451+
// @gate enablePostpone
10452+
it('aaaaaa', async () => {
10453+
jest.useRealTimers();
10454+
writable.on('data', console.log);
10455+
10456+
function Awaiter({promise, children}) {
10457+
const awaited = React.use(promise);
10458+
return children(awaited);
10459+
}
10460+
10461+
function createPromise() {
10462+
return new Promise(resolve => setTimeout(resolve));
10463+
}
10464+
10465+
const controller = new AbortController();
10466+
10467+
function App() {
10468+
setTimeout(() => {
10469+
controller.abort('done');
10470+
}, 0);
10471+
queueMicrotask(() => {
10472+
jest.runAllTimers();
10473+
});
10474+
10475+
const promise = createPromise();
10476+
10477+
return (
10478+
<div>
10479+
<Suspense
10480+
fallback={
10481+
<Suspense fallback="Loading...">
10482+
<Awaiter promise={promise}>
10483+
{() => [
10484+
<Suspense key={0} fallback="Loading more...">
10485+
<Awaiter promise={createPromise()}>{() => null}</Awaiter>
10486+
</Suspense>,
10487+
]}
10488+
</Awaiter>
10489+
</Suspense>
10490+
}>
10491+
<Awaiter promise={promise}>{() => <div>Success</div>}</Awaiter>
10492+
</Suspense>
10493+
</div>
10494+
);
10495+
}
10496+
10497+
const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(
10498+
<App />,
10499+
{signal: controller.signal},
10500+
);
10501+
console.log({prerendered});
10502+
expect(prerendered.postponed).not.toBe(null);
10503+
10504+
const resumed = await ReactDOMFizzServer.resumeToPipeableStream(
10505+
<App />,
10506+
JSON.parse(JSON.stringify(prerendered.postponed)),
10507+
);
10508+
10509+
console.log({resumed});
10510+
10511+
// Create a separate stream so it doesn't close the writable. I.e. simple concat.
10512+
const preludeWritable = new Stream.PassThrough();
10513+
preludeWritable.setEncoding('utf8');
10514+
preludeWritable.on('data', chunk => {
10515+
writable.write(chunk);
10516+
});
10517+
10518+
await act(() => {
10519+
prerendered.prelude.pipe(preludeWritable);
10520+
});
10521+
10522+
expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
10523+
10524+
await act(() => {
10525+
resumed.pipe(writable);
10526+
});
10527+
10528+
expect(getVisibleChildren(container)).toEqual(
10529+
<div>
10530+
<div>Success</div>
10531+
</div>,
10532+
);
10533+
10534+
await act(() => {});
10535+
await act(() => {});
10536+
await act(() => {});
10537+
await act(() => {});
10538+
await act(() => {});
10539+
});
10540+
10541+
fit('should not error when discarding deeply nested Suspense boundaries in a parent fallback partially complete before the parent boundary resolves', async () => {
10542+
let resolve1;
10543+
const promise1 = new Promise(r => (resolve1 = r));
10544+
let resolve2;
10545+
const promise2 = new Promise(r => (resolve2 = r));
10546+
const promise3 = new Promise(r => {});
10547+
10548+
function Use({children, promise}) {
10549+
React.use(promise);
10550+
return children;
10551+
}
10552+
function App2() {
10553+
return (
10554+
<div>
10555+
<Suspense
10556+
fallback={
10557+
<Suspense fallback="Loading...">
10558+
<Use promise={promise1}>
10559+
<Suspense fallback="Loading more...">
10560+
<Use promise={promise3}>
10561+
<div>deep fallback</div>
10562+
</Use>
10563+
</Suspense>
10564+
</Use>
10565+
</Suspense>
10566+
}>
10567+
<Use promise={promise2}>Success!</Use>
10568+
</Suspense>
10569+
</div>
10570+
);
10571+
}
10572+
10573+
writable.on('data', console.log);
10574+
await act(() => {
10575+
const {pipe} = renderToPipeableStream(<App2 />);
10576+
pipe(writable);
10577+
});
10578+
10579+
expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
10580+
10581+
await act(() => {
10582+
resolve1('resolved');
10583+
resolve2('resolved');
10584+
});
10585+
10586+
expect(getVisibleChildren(container)).toEqual(<div>Success!</div>);
10587+
});
1045010588
});

0 commit comments

Comments
 (0)