Skip to content

Commit 7af755e

Browse files
committed
Add test for erroring after having already postponed a previous sibling
This can trigger errors due to invalid injected scripts but doesn't actually fail anything else. It's a noop.
1 parent f1039be commit 7af755e

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

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

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7178,6 +7178,141 @@ describe('ReactDOMFizzServer', () => {
71787178
);
71797179
});
71807180

7181+
// @gate enablePostpone
7182+
it('can client render a boundary after having already postponed', async () => {
7183+
let prerendering = true;
7184+
let ssr = true;
7185+
7186+
function Postpone() {
7187+
if (prerendering) {
7188+
React.unstable_postpone();
7189+
}
7190+
return 'Hello';
7191+
}
7192+
7193+
function ServerError() {
7194+
if (ssr) {
7195+
throw new Error('server error');
7196+
}
7197+
return 'World';
7198+
}
7199+
7200+
function App() {
7201+
return (
7202+
<div>
7203+
<Suspense fallback="Loading1">
7204+
<Postpone />
7205+
<ServerError />
7206+
</Suspense>
7207+
<Suspense fallback="Loading2">
7208+
<Postpone />
7209+
</Suspense>
7210+
</div>
7211+
);
7212+
}
7213+
7214+
const prerenderErrors = [];
7215+
const prerendered = await ReactDOMFizzStatic.prerenderToNodeStream(
7216+
<App />,
7217+
{
7218+
onError(x) {
7219+
prerenderErrors.push(x.message);
7220+
},
7221+
},
7222+
);
7223+
expect(prerendered.postponed).not.toBe(null);
7224+
7225+
prerendering = false;
7226+
7227+
const ssrErrors = [];
7228+
7229+
const resumed = ReactDOMFizzServer.resumeToPipeableStream(
7230+
<App />,
7231+
JSON.parse(JSON.stringify(prerendered.postponed)),
7232+
{
7233+
onError(x) {
7234+
ssrErrors.push(x.message);
7235+
},
7236+
},
7237+
);
7238+
7239+
const windowErrors = [];
7240+
function globalError(e) {
7241+
windowErrors.push(e.message);
7242+
}
7243+
window.addEventListener('error', globalError);
7244+
7245+
// Create a separate stream so it doesn't close the writable. I.e. simple concat.
7246+
const preludeWritable = new Stream.PassThrough();
7247+
preludeWritable.setEncoding('utf8');
7248+
preludeWritable.on('data', chunk => {
7249+
writable.write(chunk);
7250+
});
7251+
7252+
await act(() => {
7253+
prerendered.prelude.pipe(preludeWritable);
7254+
});
7255+
7256+
expect(windowErrors).toEqual([]);
7257+
7258+
expect(getVisibleChildren(container)).toEqual(
7259+
<div>
7260+
{'Loading1'}
7261+
{'Loading2'}
7262+
</div>,
7263+
);
7264+
7265+
await act(() => {
7266+
resumed.pipe(writable);
7267+
});
7268+
7269+
expect(prerenderErrors).toEqual(['server error']);
7270+
7271+
// Since this errored, we shouldn't have to replay it.
7272+
expect(ssrErrors).toEqual([]);
7273+
7274+
expect(windowErrors).toEqual([]);
7275+
7276+
// Still loading...
7277+
expect(getVisibleChildren(container)).toEqual(
7278+
<div>
7279+
{'Loading1'}
7280+
{'Hello'}
7281+
</div>,
7282+
);
7283+
7284+
const recoverableErrors = [];
7285+
7286+
ssr = false;
7287+
7288+
await clientAct(() => {
7289+
ReactDOMClient.hydrateRoot(container, <App />, {
7290+
onRecoverableError(x) {
7291+
recoverableErrors.push(x.message);
7292+
},
7293+
});
7294+
});
7295+
7296+
expect(recoverableErrors).toEqual(
7297+
__DEV__
7298+
? ['server error']
7299+
: [
7300+
'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
7301+
],
7302+
);
7303+
expect(getVisibleChildren(container)).toEqual(
7304+
<div>
7305+
{'Hello'}
7306+
{'World'}
7307+
{'Hello'}
7308+
</div>,
7309+
);
7310+
7311+
expect(windowErrors).toEqual([]);
7312+
7313+
window.removeEventListener('error', globalError);
7314+
});
7315+
71817316
// @gate enablePostpone
71827317
it('can postpone in fallback', async () => {
71837318
let prerendering = true;

0 commit comments

Comments
 (0)