Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fizz] Support abort reasons #24680

Merged
merged 6 commits into from Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
180 changes: 179 additions & 1 deletion packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Expand Up @@ -1106,7 +1106,13 @@ describe('ReactDOMFizzServer', () => {
expect(Scheduler).toFlushAndYield([]);
expectErrors(
errors,
[['This Suspense boundary was aborted by the server.', expectedDigest]],
[
[
'The server did not finish this Suspense boundary: signal is aborted without reason',
expectedDigest,
componentStack(['h1', 'Suspense', 'div', 'App']),
],
],
[
[
'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
Expand Down Expand Up @@ -3057,6 +3063,178 @@ describe('ReactDOMFizzServer', () => {
);
});

// @gate experimental
it('Supports custom abort reasons with a string', async () => {
function App() {
return (
<div>
<p>
<Suspense fallback={'p'}>
<AsyncText text={'hello'} />
</Suspense>
</p>
<span>
<Suspense fallback={'span'}>
<AsyncText text={'world'} />
</Suspense>
</span>
</div>
);
}

let abort;
const loggedErrors = [];
await act(async () => {
const {
pipe,
abort: abortImpl,
} = ReactDOMFizzServer.renderToPipeableStream(<App />, {
onError(error) {
// In this test we contrive erroring with strings so we push the error whereas in most
// other tests we contrive erroring with Errors and push the message.
loggedErrors.push(error);
return 'a digest';
},
});
abort = abortImpl;
pipe(writable);
});

expect(loggedErrors).toEqual([]);
expect(getVisibleChildren(container)).toEqual(
<div>
<p>p</p>
<span>span</span>
</div>,
);

await act(() => {
abort('foobar');
});

expect(loggedErrors).toEqual(['foobar', 'foobar']);

const errors = [];
ReactDOMClient.hydrateRoot(container, <App />, {
onRecoverableError(error, errorInfo) {
errors.push({error, errorInfo});
},
});

expect(Scheduler).toFlushAndYield([]);

expectErrors(
errors,
[
[
'The server did not finish this Suspense boundary: foobar',
'a digest',
componentStack(['Suspense', 'p', 'div', 'App']),
],
[
'The server did not finish this Suspense boundary: foobar',
'a digest',
componentStack(['Suspense', 'span', 'div', 'App']),
],
],
[
[
'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
'a digest',
],
[
'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
'a digest',
],
],
);
});

// @gate experimental
it('Supports custom abort reasons with an Error', async () => {
function App() {
return (
<div>
<p>
<Suspense fallback={'p'}>
<AsyncText text={'hello'} />
</Suspense>
</p>
<span>
<Suspense fallback={'span'}>
<AsyncText text={'world'} />
</Suspense>
</span>
</div>
);
}

let abort;
const loggedErrors = [];
await act(async () => {
const {
pipe,
abort: abortImpl,
} = ReactDOMFizzServer.renderToPipeableStream(<App />, {
onError(error) {
loggedErrors.push(error.message);
return 'a digest';
},
});
abort = abortImpl;
pipe(writable);
});

expect(loggedErrors).toEqual([]);
expect(getVisibleChildren(container)).toEqual(
<div>
<p>p</p>
<span>span</span>
</div>,
);

await act(() => {
abort(new Error('uh oh'));
});

expect(loggedErrors).toEqual(['uh oh', 'uh oh']);

const errors = [];
ReactDOMClient.hydrateRoot(container, <App />, {
onRecoverableError(error, errorInfo) {
errors.push({error, errorInfo});
},
});

expect(Scheduler).toFlushAndYield([]);

expectErrors(
errors,
[
[
'The server did not finish this Suspense boundary: uh oh',
'a digest',
componentStack(['Suspense', 'p', 'div', 'App']),
],
[
'The server did not finish this Suspense boundary: uh oh',
'a digest',
componentStack(['Suspense', 'span', 'div', 'App']),
],
],
[
[
'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
'a digest',
],
[
'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.',
'a digest',
],
],
);
});

describe('error escaping', () => {
//@gate experimental
it('escapes error hash, message, and component stack values in directly flushed errors (html escaping)', async () => {
Expand Down