@@ -137,8 +137,9 @@ describe('ExceptionsManager', () => {
137
137
message +
138
138
'\n\n' +
139
139
'This error is located at:' +
140
- capturedErrorDefaults . componentStack ,
141
- // JS engine omitted here!
140
+ capturedErrorDefaults . componentStack +
141
+ ', js engine: ' +
142
+ jsEngine ,
142
143
) ;
143
144
} ) ;
144
145
@@ -293,7 +294,8 @@ describe('ExceptionsManager', () => {
293
294
) ;
294
295
expect ( exceptionData . isFatal ) . toBe ( false ) ;
295
296
expect ( mockError . mock . calls [ 0 ] ) . toHaveLength ( 1 ) ;
296
- expect ( mockError . mock . calls [ 0 ] [ 0 ] ) . toBe ( formattedMessage ) ;
297
+ expect ( mockError . mock . calls [ 0 ] [ 0 ] ) . toBeInstanceOf ( Error ) ;
298
+ expect ( mockError . mock . calls [ 0 ] [ 0 ] . toString ( ) ) . toBe ( formattedMessage ) ;
297
299
} ) ;
298
300
299
301
test ( 'logging a string' , ( ) => {
@@ -461,6 +463,23 @@ describe('ExceptionsManager', () => {
461
463
} ) ;
462
464
463
465
describe ( 'unstable_setExceptionDecorator' , ( ) => {
466
+ let mockError ;
467
+ beforeEach ( ( ) => {
468
+ // NOTE: We initialise a fresh mock every time using spyOn, above.
469
+ // We can't use `console._errorOriginal` for this, because that's a bound
470
+ // (=wrapped) version of the mock and Jest does not approve.
471
+ mockError = console . error ;
472
+ ExceptionsManager . installConsoleErrorReporter ( ) ;
473
+ } ) ;
474
+
475
+ afterEach ( ( ) => {
476
+ // There is no uninstallConsoleErrorReporter. Do this so the next install
477
+ // works.
478
+ console . error = console . _errorOriginal ;
479
+ delete console . _errorOriginal ;
480
+ delete console . reportErrorsAsExceptions ;
481
+ } ) ;
482
+
464
483
test ( 'modifying the exception data' , ( ) => {
465
484
const error = new Error ( 'Some error happened' ) ;
466
485
const decorator = jest . fn ( ) . mockImplementation ( data => ( {
@@ -509,7 +528,7 @@ describe('ExceptionsManager', () => {
509
528
expect ( nativeReportException ) . toHaveBeenCalled ( ) ;
510
529
} ) ;
511
530
512
- test ( 'prevents decorator recursion' , ( ) => {
531
+ test ( 'prevents decorator recursion from error handler ' , ( ) => {
513
532
const error = new Error ( 'Some error happened' ) ;
514
533
const decorator = jest . fn ( ) . mockImplementation ( data => {
515
534
console . error ( 'Logging an error within the decorator' ) ;
@@ -519,18 +538,88 @@ describe('ExceptionsManager', () => {
519
538
} ;
520
539
} ) ;
521
540
522
- ExceptionsManager . installConsoleErrorReporter ( ) ;
523
541
ExceptionsManager . unstable_setExceptionDecorator ( decorator ) ;
524
542
ExceptionsManager . handleException ( error , true ) ;
525
543
526
- expect ( decorator ) . toHaveBeenCalled ( ) ;
544
+ expect ( nativeReportException ) . toHaveBeenCalledTimes ( 1 ) ;
545
+ expect ( nativeReportException . mock . calls [ 0 ] [ 0 ] . message ) . toMatch (
546
+ / d e c o r a t e d : .* S o m e e r r o r h a p p e n e d / ,
547
+ ) ;
548
+ expect ( mockError ) . toHaveBeenCalledTimes ( 2 ) ;
549
+ expect ( mockError . mock . calls [ 0 ] [ 0 ] ) . toMatch (
550
+ / L o g g i n g a n e r r o r w i t h i n t h e d e c o r a t o r / ,
551
+ ) ;
552
+ expect ( mockError . mock . calls [ 1 ] [ 0 ] ) . toMatch (
553
+ / d e c o r a t e d : .* S o m e e r r o r h a p p e n e d / ,
554
+ ) ;
555
+ } ) ;
556
+
557
+ test ( 'prevents decorator recursion from console.error' , ( ) => {
558
+ const error = new Error ( 'Some error happened' ) ;
559
+ const decorator = jest . fn ( ) . mockImplementation ( data => {
560
+ console . error ( 'Logging an error within the decorator' ) ;
561
+ return {
562
+ ...data ,
563
+ message : 'decorated: ' + data . message ,
564
+ } ;
565
+ } ) ;
566
+
567
+ ExceptionsManager . unstable_setExceptionDecorator ( decorator ) ;
568
+ console . error ( error ) ;
569
+
527
570
expect ( nativeReportException ) . toHaveBeenCalledTimes ( 2 ) ;
528
571
expect ( nativeReportException . mock . calls [ 0 ] [ 0 ] . message ) . toMatch (
529
572
/ L o g g i n g a n e r r o r w i t h i n t h e d e c o r a t o r / ,
530
573
) ;
531
574
expect ( nativeReportException . mock . calls [ 1 ] [ 0 ] . message ) . toMatch (
532
575
/ d e c o r a t e d : .* S o m e e r r o r h a p p e n e d / ,
533
576
) ;
577
+ expect ( mockError ) . toHaveBeenCalledTimes ( 2 ) ;
578
+ // console.error calls are chained without exception pre-processing, so decorator doesn't apply
579
+ expect ( mockError . mock . calls [ 0 ] [ 0 ] . toString ( ) ) . toMatch (
580
+ / E r r o r : S o m e e r r o r h a p p e n e d / ,
581
+ ) ;
582
+ expect ( mockError . mock . calls [ 1 ] [ 0 ] ) . toMatch (
583
+ / L o g g i n g a n e r r o r w i t h i n t h e d e c o r a t o r / ,
584
+ ) ;
585
+ } ) ;
586
+
587
+ test ( 'can handle throwing decorators recursion when exception is thrown' , ( ) => {
588
+ const error = new Error ( 'Some error happened' ) ;
589
+ const decorator = jest . fn ( ) . mockImplementation ( data => {
590
+ throw new Error ( 'Throwing an error within the decorator' ) ;
591
+ } ) ;
592
+
593
+ ExceptionsManager . unstable_setExceptionDecorator ( decorator ) ;
594
+ ExceptionsManager . handleException ( error , true ) ;
595
+
596
+ expect ( nativeReportException ) . toHaveBeenCalledTimes ( 1 ) ;
597
+ // Exceptions in decorators are ignored and the decorator is not applied
598
+ expect ( nativeReportException . mock . calls [ 0 ] [ 0 ] . message ) . toMatch (
599
+ / E r r o r : S o m e e r r o r h a p p e n e d / ,
600
+ ) ;
601
+ expect ( mockError ) . toHaveBeenCalledTimes ( 1 ) ;
602
+ expect ( mockError . mock . calls [ 0 ] [ 0 ] ) . toMatch ( / E r r o r : S o m e e r r o r h a p p e n e d / ) ;
603
+ } ) ;
604
+
605
+ test ( 'can handle throwing decorators recursion when exception is logged' , ( ) => {
606
+ const error = new Error ( 'Some error happened' ) ;
607
+ const decorator = jest . fn ( ) . mockImplementation ( data => {
608
+ throw new Error ( 'Throwing an error within the decorator' ) ;
609
+ } ) ;
610
+
611
+ ExceptionsManager . unstable_setExceptionDecorator ( decorator ) ;
612
+ console . error ( error ) ;
613
+
614
+ expect ( nativeReportException ) . toHaveBeenCalledTimes ( 1 ) ;
615
+ // Exceptions in decorators are ignored and the decorator is not applied
616
+ expect ( nativeReportException . mock . calls [ 0 ] [ 0 ] . message ) . toMatch (
617
+ / E r r o r : S o m e e r r o r h a p p e n e d / ,
618
+ ) ;
619
+ expect ( mockError ) . toHaveBeenCalledTimes ( 1 ) ;
620
+ expect ( mockError . mock . calls [ 0 ] [ 0 ] . toString ( ) ) . toMatch (
621
+ / E r r o r : S o m e e r r o r h a p p e n e d / ,
622
+ ) ;
534
623
} ) ;
535
624
} ) ;
536
625
} ) ;
0 commit comments