Skip to content

Commit

Permalink
Improve error message when it's not a real message object
Browse files Browse the repository at this point in the history
Also deals with symbols.
  • Loading branch information
sebmarkbage committed Feb 14, 2024
1 parent dc31781 commit 4c64861
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 2 deletions.
111 changes: 110 additions & 1 deletion packages/react-client/src/__tests__/ReactFlight-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,16 @@ describe('ReactFlight', () => {
' builds to avoid leaking sensitive details. A digest property is included on this error instance which' +
' may provide additional details about the nature of the error.',
);
expect(this.state.error.digest).toContain(this.props.expectedMessage);
let expectedDigest = this.props.expectedMessage;
if (
expectedDigest.startsWith('Error: {') ||
expectedDigest.startsWith('Error: <')
) {
expectedDigest = '{}';
} else if (expectedDigest.startsWith('Error: [')) {
expectedDigest = '[]';
}
expect(this.state.error.digest).toContain(expectedDigest);
expect(this.state.error.stack).toBe(
'Error: ' + this.state.error.message,
);
Expand Down Expand Up @@ -776,6 +785,106 @@ describe('ReactFlight', () => {
});
});

it('should emit descriptions of errors in dev', async () => {
const ClientErrorBoundary = clientReference(ErrorBoundary);

function Throw({value}) {
throw value;
}

let testCases = (
<>
<ClientErrorBoundary expectedMessage="This is a real Error.">
<div>
<Throw value={new TypeError('This is a real Error.')} />
</div>
</ClientErrorBoundary>
<ClientErrorBoundary expectedMessage="Error: This is a string error.">
<div>
<Throw value="This is a string error." />
</div>
</ClientErrorBoundary>
<ClientErrorBoundary expectedMessage="Error: {message: ..., extra: ..., nested: ...}">
<div>
<Throw
value={{
message: 'This is a long message',
extra: 'properties',
nested: {more: 'prop'},
}}
/>
</div>
</ClientErrorBoundary>
<ClientErrorBoundary
expectedMessage={
'Error: {message: "Short", extra: ..., nested: ...}'
}>
<div>
<Throw
value={{
message: 'Short',
extra: 'properties',
nested: {more: 'prop'},
}}
/>
</div>
</ClientErrorBoundary>
<ClientErrorBoundary expectedMessage="Error: Symbol(hello)">
<div>
<Throw value={Symbol('hello')} />
</div>
</ClientErrorBoundary>
<ClientErrorBoundary expectedMessage="Error: 123">
<div>
<Throw value={123} />
</div>
</ClientErrorBoundary>
<ClientErrorBoundary expectedMessage="Error: undefined">
<div>
<Throw value={undefined} />
</div>
</ClientErrorBoundary>
<ClientErrorBoundary expectedMessage="Error: <div/>">
<div>
<Throw value={<div />} />
</div>
</ClientErrorBoundary>
<ClientErrorBoundary expectedMessage="Error: function Foo() {}">
<div>
<Throw value={function Foo() {}} />
</div>
</ClientErrorBoundary>
<ClientErrorBoundary expectedMessage={'Error: ["array"]'}>
<div>
<Throw value={['array']} />
</div>
</ClientErrorBoundary>
</>
);

const transport = ReactNoopFlightServer.render(testCases, {
onError(x) {
if (__DEV__) {
return 'a dev digest';
}
if (x instanceof Error) {
return `digest("${x.message}")`;
} else if (Array.isArray(x)) {
return `digest([])`;
} else if (typeof x === 'object' && x !== null) {
return `digest({})`;
}
return `digest(Error: ${String(x)})`;
},
});

await act(() => {
startTransition(() => {
ReactNoop.render(ReactNoopFlightClient.read(transport));
});
});
});

it('should trigger the inner most error boundary inside a Client Component', async () => {
function ServerComponent() {
throw new Error('This was thrown in the Server Component.');
Expand Down
5 changes: 4 additions & 1 deletion packages/react-server/src/ReactFlightServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1677,8 +1677,11 @@ function emitErrorChunk(
message = String(error.message);
// eslint-disable-next-line react-internal/safe-string-coercion
stack = String(error.stack);
} else if (typeof error === 'object' && error !== null) {
message = 'Error: ' + describeObjectForErrorMessage(error);
} else {
message = 'Error: ' + (error: any);
// eslint-disable-next-line react-internal/safe-string-coercion
message = 'Error: ' + String(error);
}
} catch (x) {
message = 'An error occurred but serializing the error message failed.';
Expand Down

0 comments on commit 4c64861

Please sign in to comment.