81
});
82
}
83
84
+ function normalizeCodeLocInfo(str) {
85
+ return (
86
+ str &&
87
+ str.replace(
88
+ /^ +(?:at|in) ([\S]+)([^\n]*)(\n?)/gm,
89
+ function (m, name, location, eol) {
90
+ if (location.indexOf(__filename) === -1) {
91
+ // ignore frames from library code
92
+ return '';
93
+ }
94
+ return (
95
+ ' in ' +
96
+ name +
97
+ (/:\d+:\d+/.test(m)
98
+ ? ' ' +
99
+ location.replace(__dirname, '~').replace(/:\d+:\d+/, ':*:*')
100
+ : '') +
101
+ eol
102
+ );
103
+ },
104
+ )
105
+ );
106
+ }
107
+
108
function Text({text}) {
109
Scheduler.log(text);
110
return text;
2213
expect(root).toMatchRenderedOutput('Updated');
2214
},
2215
);
2216
+
2217
+ it('throws a descriptive error when a rejected promise without a reason property is passed to use()', async () => {
2218
+ const badThenable = Promise.resolve();
2219
+ // Simulate bad instrumentation: status is 'rejected' but the reason is not
2220
+ // in the `reason` property as intended.
2221
+ badThenable.status = 'rejected';
2222
+ badThenable.error = new Error('Something went wrong');
2223
+
2224
+ function Child() {
2225
+ return use(badThenable);
2226
+ }
2227
+
2228
+ function App({
2229
+ // Intentionally destrucutring a prop here so that our production error
2230
+ // stack trick is triggered at the beginning of the function
2231
+ prop,
2232
+ }) {
2233
+ return <Child />;
2234
+ }
2235
+
2236
+ const uncaughtErrors = [];
2237
+ const root = ReactNoop.createRoot({
2238
+ onUncaughtError(error, errorInfo) {
2239
+ uncaughtErrors.push({
2240
+ callStack: normalizeCodeLocInfo(error.stack),
2241
+ ownerStack: React.captureOwnerStack
2242
+ ? normalizeCodeLocInfo(React.captureOwnerStack())
2243
+ : null,
2244
+ componentStack: normalizeCodeLocInfo(errorInfo.componentStack),
2245
+ });
2246
+ },
2247
+ });
2248
+
2249
+ await act(() => {
2250
+ root.render(<App />);
2251
+ });
2252
+
2253
+ expect(uncaughtErrors).toEqual([
2254
+ {
2255
+ callStack:
2256
+ 'Error: A rejected Promise was passed to React without a `reason` property. ' +
2257
+ 'React threw a generic error from where the Promise was used to assist in identifying the problematic Promise. ' +
2258
+ "Make sure that instrumented Promises correctly set the `reason` property when setting `status` to `'rejected'`." +
2259
+ '\n in Child (~/ReactUse-test.js:*:*)' +
2260
+ '\n in Object.<anonymous> (~/ReactUse-test.js:*:*)',
2261
+ componentStack:
2262
+ '\n in Child (~/ReactUse-test.js:*:*)' +
2263
+ '\n in App (~/ReactUse-test.js:*:*)',
2264
+ ownerStack: __DEV__ ? '\n in App (~/ReactUse-test.js:*:*)' : null,
2265
+ },
2266
+ ]);
2267
+ });
2268
+
2269
+ it('throws a descriptive error when a rejected promise without a reason property is used as a child', async () => {
2270
+ const badThenable = Promise.resolve();
2271
+ // Simulate bad instrumentation: status is 'rejected' but the reason is not
2272
+ // in the `reason` property as intended.
2273
+ badThenable.status = 'rejected';
2274
+ badThenable.error = new Error('Something went wrong');
2275
+
2276
+ function Child() {
2277
+ return <div>{badThenable}</div>;
2278
+ }
2279
+
2280
+ function App({
2281
+ // Intentionally destrucutring a prop here so that our production error
2282
+ // stack trick is triggered at the beginning of the function
2283
+ prop,
2284
+ }) {
2285
+ return <Child />;
2286
+ }
2287
+
2288
+ const uncaughtErrors = [];
2289
+ const root = ReactNoop.createRoot({
2290
+ onUncaughtError(error, errorInfo) {
2291
+ uncaughtErrors.push({
2292
+ callStack: normalizeCodeLocInfo(error.stack),
2293
+ ownerStack: React.captureOwnerStack
2294
+ ? normalizeCodeLocInfo(React.captureOwnerStack())
2295
+ : null,
2296
+ componentStack: normalizeCodeLocInfo(errorInfo.componentStack),
2297
+ });
2298
+ },
2299
+ });
2300
+
2301
+ await act(() => {
2302
+ root.render(<App />);
2303
+ });
2304
+
2305
+ expect(uncaughtErrors).toEqual([
2306
+ {
2307
+ callStack:
2308
+ 'Error: A rejected Promise was passed to React without a `reason` property. ' +
2309
+ 'React threw a generic error from where the Promise was used to assist in identifying the problematic Promise. ' +
2310
+ "Make sure that instrumented Promises correctly set the `reason` property when setting `status` to `'rejected'`." +
2311
+ '\n in Object.<anonymous> (~/ReactUse-test.js:*:*)',
2312
+ componentStack: '\n in App (~/ReactUse-test.js:*:*)',
2313
+ ownerStack: __DEV__
2314
+ ? // TODO: Should start in Child since that's the owner.
2315
+ '\n in App (~/ReactUse-test.js:*:*)'
2316
+ : null,
2317
+ },
2318
+ ]);
2319
+ });
2320
});