11import { Scope } from '@sentry/browser' ;
2+ import { Event , Severity } from '@sentry/types' ;
23import { fireEvent , render , screen } from '@testing-library/react' ;
34import * as React from 'react' ;
5+ import { useState } from 'react' ;
46
57import { ErrorBoundary , ErrorBoundaryProps , UNKNOWN_COMPONENT , withErrorBoundary } from '../src/errorboundary' ;
68
7- const mockCaptureException = jest . fn ( ) ;
9+ const mockCaptureEvent = jest . fn ( ) ;
810const mockShowReportDialog = jest . fn ( ) ;
911const EVENT_ID = 'test-id-123' ;
1012
1113jest . mock ( '@sentry/browser' , ( ) => {
1214 const actual = jest . requireActual ( '@sentry/browser' ) ;
1315 return {
1416 ...actual ,
15- captureException : ( err : any , ctx : any ) => {
16- mockCaptureException ( err , ctx ) ;
17+ captureEvent : ( event : Event ) => {
18+ mockCaptureEvent ( event ) ;
1719 return EVENT_ID ;
1820 } ,
1921 showReportDialog : ( options : any ) => {
@@ -22,6 +24,15 @@ jest.mock('@sentry/browser', () => {
2224 } ;
2325} ) ;
2426
27+ function Boo ( { title } : { title : string } ) : JSX . Element {
28+ throw new Error ( title ) ;
29+ }
30+
31+ function Bam ( ) : JSX . Element {
32+ const [ title ] = useState ( 'boom' ) ;
33+ return < Boo title = { title } /> ;
34+ }
35+
2536const TestApp : React . FC < ErrorBoundaryProps > = ( { children, ...props } ) => {
2637 const [ isError , setError ] = React . useState ( false ) ;
2738 return (
@@ -45,10 +56,6 @@ const TestApp: React.FC<ErrorBoundaryProps> = ({ children, ...props }) => {
4556 ) ;
4657} ;
4758
48- function Bam ( ) : JSX . Element {
49- throw new Error ( 'boom' ) ;
50- }
51-
5259describe ( 'withErrorBoundary' , ( ) => {
5360 it ( 'sets displayName properly' , ( ) => {
5461 const TestComponent = ( ) => < h1 > Hello World</ h1 > ;
@@ -67,7 +74,7 @@ describe('ErrorBoundary', () => {
6774 jest . spyOn ( console , 'error' ) . mockImplementation ( ) ;
6875
6976 afterEach ( ( ) => {
70- mockCaptureException . mockClear ( ) ;
77+ mockCaptureEvent . mockClear ( ) ;
7178 mockShowReportDialog . mockClear ( ) ;
7279 } ) ;
7380
@@ -170,10 +177,15 @@ describe('ErrorBoundary', () => {
170177 expect ( container . innerHTML ) . toBe ( '<div>Fallback here</div>' ) ;
171178
172179 expect ( errorString ) . toBe ( 'Error: boom' ) ;
173- expect ( compStack ) . toBe ( `
174- in Bam (created by TestApp)
175- in ErrorBoundary (created by TestApp)
176- in TestApp` ) ;
180+ /*
181+ at Boo (/path/to/sentry-javascript/packages/react/test/errorboundary.test.tsx:23:20)
182+ at Bam (/path/to/sentry-javascript/packages/react/test/errorboundary.test.tsx:40:11)
183+ at ErrorBoundary (/path/to/sentry-javascript/packages/react/src/errorboundary.tsx:2026:39)
184+ at TestApp (/path/to/sentry-javascript/packages/react/test/errorboundary.test.tsx:22:23)
185+ */
186+ expect ( compStack ) . toMatch (
187+ / \s + ( a t B o o ) \( .* ?\) \s + ( a t B a m ) \( .* ?\) \s + ( a t E r r o r B o u n d a r y ) \( .* ?\) \s + ( a t T e s t A p p ) \( .* ?\) / g,
188+ ) ;
177189 expect ( eventIdString ) . toBe ( EVENT_ID ) ;
178190 } ) ;
179191 } ) ;
@@ -188,25 +200,60 @@ describe('ErrorBoundary', () => {
188200 ) ;
189201
190202 expect ( mockOnError ) . toHaveBeenCalledTimes ( 0 ) ;
191- expect ( mockCaptureException ) . toHaveBeenCalledTimes ( 0 ) ;
203+ expect ( mockCaptureEvent ) . toHaveBeenCalledTimes ( 0 ) ;
192204
193205 const btn = screen . getByTestId ( 'errorBtn' ) ;
194206 fireEvent . click ( btn ) ;
195207
196208 expect ( mockOnError ) . toHaveBeenCalledTimes ( 1 ) ;
197209 expect ( mockOnError ) . toHaveBeenCalledWith ( expect . any ( Error ) , expect . any ( String ) , expect . any ( String ) ) ;
198210
199- expect ( mockCaptureException ) . toHaveBeenCalledTimes ( 1 ) ;
200- expect ( mockCaptureException ) . toHaveBeenCalledWith ( expect . any ( Error ) , {
201- contexts : { react : { componentStack : expect . any ( String ) } } ,
202- } ) ;
211+ expect ( mockCaptureEvent ) . toHaveBeenCalledTimes ( 1 ) ;
212+
213+ // We do a detailed assert on the stacktrace as a regression test against future
214+ // react changes (that way we can update the docs if frames change in a major way).
215+ const event = mockCaptureEvent . mock . calls [ 0 ] [ 0 ] ;
216+ expect ( event . exception . values ) . toHaveLength ( 2 ) ;
217+ expect ( event . level ) . toBe ( Severity . Error ) ;
218+
219+ expect ( event . exception . values [ 0 ] . type ) . toEqual ( 'React ErrorBoundary Error' ) ;
220+ expect ( event . exception . values [ 0 ] . stacktrace . frames ) . toEqual ( [
221+ {
222+ colno : expect . any ( Number ) ,
223+ filename : expect . stringContaining ( 'errorboundary.test.tsx' ) ,
224+ function : 'TestApp' ,
225+ in_app : true ,
226+ lineno : expect . any ( Number ) ,
227+ } ,
228+ {
229+ colno : expect . any ( Number ) ,
230+ filename : expect . stringContaining ( 'errorboundary.tsx' ) ,
231+ function : 'ErrorBoundary' ,
232+ in_app : true ,
233+ lineno : expect . any ( Number ) ,
234+ } ,
235+ {
236+ colno : expect . any ( Number ) ,
237+ filename : expect . stringContaining ( 'errorboundary.test.tsx' ) ,
238+ function : 'Bam' ,
239+ in_app : true ,
240+ lineno : expect . any ( Number ) ,
241+ } ,
242+ {
243+ colno : expect . any ( Number ) ,
244+ filename : expect . stringContaining ( 'errorboundary.test.tsx' ) ,
245+ function : 'Boo' ,
246+ in_app : true ,
247+ lineno : expect . any ( Number ) ,
248+ } ,
249+ ] ) ;
203250 } ) ;
204251
205252 it ( 'calls `beforeCapture()` when an error occurs' , ( ) => {
206253 const mockBeforeCapture = jest . fn ( ) ;
207254
208255 const testBeforeCapture = ( ...args : any [ ] ) => {
209- expect ( mockCaptureException ) . toHaveBeenCalledTimes ( 0 ) ;
256+ expect ( mockCaptureEvent ) . toHaveBeenCalledTimes ( 0 ) ;
210257 mockBeforeCapture ( ...args ) ;
211258 } ;
212259
@@ -217,14 +264,14 @@ describe('ErrorBoundary', () => {
217264 ) ;
218265
219266 expect ( mockBeforeCapture ) . toHaveBeenCalledTimes ( 0 ) ;
220- expect ( mockCaptureException ) . toHaveBeenCalledTimes ( 0 ) ;
267+ expect ( mockCaptureEvent ) . toHaveBeenCalledTimes ( 0 ) ;
221268
222269 const btn = screen . getByTestId ( 'errorBtn' ) ;
223270 fireEvent . click ( btn ) ;
224271
225272 expect ( mockBeforeCapture ) . toHaveBeenCalledTimes ( 1 ) ;
226273 expect ( mockBeforeCapture ) . toHaveBeenLastCalledWith ( expect . any ( Scope ) , expect . any ( Error ) , expect . any ( String ) ) ;
227- expect ( mockCaptureException ) . toHaveBeenCalledTimes ( 1 ) ;
274+ expect ( mockCaptureEvent ) . toHaveBeenCalledTimes ( 1 ) ;
228275 } ) ;
229276
230277 it ( 'shows a Sentry Report Dialog with correct options' , ( ) => {
0 commit comments