Skip to content

Commit 1a01e03

Browse files
committed
Define reportError in the Jest preset so React does not mask errors on Node 26
1 parent 024b474 commit 1a01e03

2 files changed

Lines changed: 42 additions & 0 deletions

File tree

‎packages/jest-preset/jest/__tests__/setup-test.js‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
* @format
99
*/
1010

11+
import * as React from 'react';
12+
1113
const NativeExceptionsManager = jest.requireMock(
1214
'react-native/Libraries/Core/NativeExceptionsManager',
1315
).default;
@@ -54,3 +56,28 @@ test('native component mocks work with a partial react-native mock', () => {
5456
}).not.toThrow();
5557
});
5658
});
59+
60+
test('reportError is defined so React does not mistake this for a DOM', () => {
61+
// `window` is aliased to `global`, so React only avoids constructing and
62+
// dispatching a DOM `ErrorEvent` (which this environment cannot do) when
63+
// `reportError` is available.
64+
// $FlowFixMe[prop-missing] - `reportError` is not in Flow's global defs.
65+
expect(typeof global.reportError).toBe('function');
66+
});
67+
68+
test('an error thrown in a transition is reported unchanged', () => {
69+
const error = new Error('Original error');
70+
const handler = jest.fn();
71+
72+
process.on('uncaughtException', handler);
73+
74+
try {
75+
React.startTransition(() => {
76+
throw error;
77+
});
78+
} finally {
79+
process.off('uncaughtException', handler);
80+
}
81+
82+
expect(handler).toHaveBeenCalledWith(error);
83+
});

‎packages/jest-preset/jest/setup.js‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,21 @@ Object.defineProperties(global, {
5555
value: jest.requireActual<unknown>('regenerator-runtime/runtime'),
5656
writable: true,
5757
},
58+
// `window` is aliased to `global` below, so React's `reportGlobalError`
59+
// treats this environment as a DOM as soon as Node exposes an `ErrorEvent`
60+
// global (Node >= 26 does), and then calls `window.dispatchEvent`, which
61+
// does not exist here. The resulting `TypeError` replaces whatever error
62+
// React was trying to report. Defining `reportError` keeps React on a code
63+
// path that works: like `window.reportError` on the web, it reports the
64+
// error as if it were uncaught, which in Node means `uncaughtException`.
65+
reportError: {
66+
configurable: true,
67+
enumerable: true,
68+
value(error: unknown): void {
69+
process.emit('uncaughtException', error);
70+
},
71+
writable: true,
72+
},
5873
requestAnimationFrame: {
5974
configurable: true,
6075
enumerable: true,

0 commit comments

Comments
 (0)