@@ -18,6 +18,7 @@ import {
18
18
formatWithStyles ,
19
19
gt ,
20
20
gte ,
21
+ parseSourceFromComponentStack ,
21
22
} from 'react-devtools-shared/src/backend/utils' ;
22
23
import {
23
24
REACT_SUSPENSE_LIST_TYPE as SuspenseList ,
@@ -297,4 +298,93 @@ describe('utils', () => {
297
298
expect ( isPlainObject ( Object . create ( null ) ) ) . toBe ( true ) ;
298
299
} ) ;
299
300
} ) ;
301
+
302
+ describe ( 'parseSourceFromComponentStack' , ( ) => {
303
+ it ( 'should return null if passed empty string' , ( ) => {
304
+ expect ( parseSourceFromComponentStack ( '' ) ) . toEqual ( null ) ;
305
+ } ) ;
306
+
307
+ it ( 'should construct the source from the first frame if available' , ( ) => {
308
+ expect (
309
+ parseSourceFromComponentStack (
310
+ 'at l (https://react.dev/_next/static/chunks/main-78a3b4c2aa4e4850.js:1:10389)\n' +
311
+ 'at f (https://react.dev/_next/static/chunks/pages/%5B%5B...markdownPath%5D%5D-af2ed613aedf1d57.js:1:8519)\n' +
312
+ 'at r (https://react.dev/_next/static/chunks/pages/_app-dd0b77ea7bd5b246.js:1:498)\n' ,
313
+ ) ,
314
+ ) . toEqual ( {
315
+ sourceURL :
316
+ 'https://react.dev/_next/static/chunks/main-78a3b4c2aa4e4850.js' ,
317
+ line : 1 ,
318
+ column : 10389 ,
319
+ } ) ;
320
+ } ) ;
321
+
322
+ it ( 'should construct the source from highest available frame' , ( ) => {
323
+ expect (
324
+ parseSourceFromComponentStack (
325
+ ' at Q\n' +
326
+ ' at a\n' +
327
+ ' at m (https://react.dev/_next/static/chunks/848-122f91e9565d9ffa.js:5:9236)\n' +
328
+ ' at div\n' +
329
+ ' at div\n' +
330
+ ' at div\n' +
331
+ ' at nav\n' +
332
+ ' at div\n' +
333
+ ' at te (https://react.dev/_next/static/chunks/363-3c5f1b553b6be118.js:1:158857)\n' +
334
+ ' at tt (https://react.dev/_next/static/chunks/363-3c5f1b553b6be118.js:1:165520)\n' +
335
+ ' at f (https://react.dev/_next/static/chunks/pages/%5B%5B...markdownPath%5D%5D-af2ed613aedf1d57.js:1:8519)' ,
336
+ ) ,
337
+ ) . toEqual ( {
338
+ sourceURL :
339
+ 'https://react.dev/_next/static/chunks/848-122f91e9565d9ffa.js' ,
340
+ line : 5 ,
341
+ column : 9236 ,
342
+ } ) ;
343
+ } ) ;
344
+
345
+ it ( 'should construct the source from frame, which has only url specified' , ( ) => {
346
+ expect (
347
+ parseSourceFromComponentStack (
348
+ ' at Q\n' +
349
+ ' at a\n' +
350
+ ' at https://react.dev/_next/static/chunks/848-122f91e9565d9ffa.js:5:9236\n' ,
351
+ ) ,
352
+ ) . toEqual ( {
353
+ sourceURL :
354
+ 'https://react.dev/_next/static/chunks/848-122f91e9565d9ffa.js' ,
355
+ line : 5 ,
356
+ column : 9236 ,
357
+ } ) ;
358
+ } ) ;
359
+
360
+ it ( 'should parse sourceURL correctly if it includes parentheses' , ( ) => {
361
+ expect (
362
+ parseSourceFromComponentStack (
363
+ 'at HotReload (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js:307:11)\n' +
364
+ ' at Router (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/app-router.js:181:11)\n' +
365
+ ' at ErrorBoundaryHandler (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/error-boundary.js:114:9)' ,
366
+ ) ,
367
+ ) . toEqual ( {
368
+ sourceURL :
369
+ 'webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js' ,
370
+ line : 307 ,
371
+ column : 11 ,
372
+ } ) ;
373
+ } ) ;
374
+
375
+ it ( 'should support Firefox stack' , ( ) => {
376
+ expect (
377
+ parseSourceFromComponentStack (
378
+ 'tt@https://react.dev/_next/static/chunks/363-3c5f1b553b6be118.js:1:165558\n' +
379
+ 'f@https://react.dev/_next/static/chunks/pages/%5B%5B...markdownPath%5D%5D-af2ed613aedf1d57.js:1:8535\n' +
380
+ 'r@https://react.dev/_next/static/chunks/pages/_app-dd0b77ea7bd5b246.js:1:513' ,
381
+ ) ,
382
+ ) . toEqual ( {
383
+ sourceURL :
384
+ 'https://react.dev/_next/static/chunks/363-3c5f1b553b6be118.js' ,
385
+ line : 1 ,
386
+ column : 165558 ,
387
+ } ) ;
388
+ } ) ;
389
+ } ) ;
300
390
} ) ;
0 commit comments