10
11
'use strict';
12
13
+function normalizeCodeLocInfo(str) {
14
+ return (
15
+ str &&
16
+ str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) {
17
+ return '\n in ' + name + (/\d/.test(m) ? ' (at **)' : '');
18
+ })
19
+ );
20
+}
21
+
22
const heldValues = [];
23
let finalizationCallback;
24
function FinalizationRegistryMock(callback) {
78
error,
79
};
80
}
81
+ componentDidCatch(error, errorInfo) {
82
+ expect(error).toBe(this.state.error);
83
+ if (this.props.expectedStack !== undefined) {
84
+ expect(normalizeCodeLocInfo(errorInfo.componentStack)).toBe(
85
+ this.props.expectedStack,
86
+ );
87
+ }
88
+ }
89
componentDidMount() {
90
expect(this.state.hasError).toBe(true);
91
expect(this.state.error).toBeTruthy();
917
});
918
});
919
920
+ it('should include server components in error boundary stacks in dev', async () => {
921
+ const ClientErrorBoundary = clientReference(ErrorBoundary);
922
+
923
+ function Throw({value}) {
924
+ throw value;
925
+ }
926
+
927
+ const expectedStack = __DEV__
928
+ ? // TODO: This should include Throw but it doesn't have a Fiber.
929
+ '\n in div' + '\n in ErrorBoundary (at **)' + '\n in App'
930
+ : '\n in div' + '\n in ErrorBoundary (at **)';
931
+
932
+ function App() {
933
+ return (
934
+ <ClientErrorBoundary
935
+ expectedMessage="This is a real Error."
936
+ expectedStack={expectedStack}>
937
+ <div>
938
+ <Throw value={new TypeError('This is a real Error.')} />
939
+ </div>
940
+ </ClientErrorBoundary>
941
+ );
942
+ }
943
+
944
+ const transport = ReactNoopFlightServer.render(<App />, {
945
+ onError(x) {
946
+ if (__DEV__) {
947
+ return 'a dev digest';
948
+ }
949
+ if (x instanceof Error) {
950
+ return `digest("${x.message}")`;
951
+ } else if (Array.isArray(x)) {
952
+ return `digest([])`;
953
+ } else if (typeof x === 'object' && x !== null) {
954
+ return `digest({})`;
955
+ }
956
+ return `digest(${String(x)})`;
957
+ },
958
+ });
959
+
960
+ await act(() => {
961
+ startTransition(() => {
962
+ ReactNoop.render(ReactNoopFlightClient.read(transport));
963
+ });
964
+ });
965
+ });
966
+
967
+ it('should include server components in warning stacks', async () => {
968
+ function Component() {
969
+ // Trigger key warning
970
+ return <div>{[<span />]}</div>;
971
+ }
972
+ const ClientComponent = clientReference(Component);
973
+
974
+ function Indirection({children}) {
975
+ return children;
976
+ }
977
+
978
+ function App() {
979
+ return (
980
+ <Indirection>
981
+ <ClientComponent />
982
+ </Indirection>
983
+ );
984
+ }
985
+
986
+ const transport = ReactNoopFlightServer.render(<App />);
987
+
988
+ await expect(async () => {
989
+ await act(() => {
990
+ startTransition(() => {
991
+ ReactNoop.render(ReactNoopFlightClient.read(transport));
992
+ });
993
+ });
994
+ }).toErrorDev(
995
+ 'Each child in a list should have a unique "key" prop.\n' +
996
+ '\n' +
997
+ 'Check the render method of `Component`. See https://reactjs.org/link/warning-keys for more information.\n' +
998
+ ' in span (at **)\n' +
999
+ ' in Component (at **)\n' +
1000
+ ' in Indirection (at **)\n' +
1001
+ ' in App (at **)',
1002
+ );
1003
+ });
1004
+
1005
it('should trigger the inner most error boundary inside a Client Component', async () => {
1006
function ServerComponent() {
1007
throw new Error('This was thrown in the Server Component.');