@@ -12,6 +12,9 @@ const PUBLIC_DSN = 'https://username@domain/123';
1212// eslint-disable-next-line no-var
1313declare var global : any ;
1414
15+ const backendEventFromException = jest . spyOn ( TestBackend . prototype , 'eventFromException' ) ;
16+ const clientProcess = jest . spyOn ( TestClient . prototype as any , '_process' ) ;
17+
1518jest . mock ( '@sentry/utils' , ( ) => {
1619 const original = jest . requireActual ( '@sentry/utils' ) ;
1720 return {
@@ -57,7 +60,7 @@ describe('BaseClient', () => {
5760 } ) ;
5861
5962 afterEach ( ( ) => {
60- jest . restoreAllMocks ( ) ;
63+ jest . clearAllMocks ( ) ;
6164 } ) ;
6265
6366 describe ( 'constructor() / getDsn()' , ( ) => {
@@ -249,6 +252,30 @@ describe('BaseClient', () => {
249252 } ) ,
250253 ) ;
251254 } ) ;
255+
256+ test . each ( [
257+ [ '`Error` instance' , new Error ( 'Will I get caught twice?' ) ] ,
258+ [ 'plain object' , { 'Will I' : 'get caught twice?' } ] ,
259+ [ 'primitive wrapper' , new String ( 'Will I get caught twice?' ) ] ,
260+ // Primitives aren't tested directly here because they need to be wrapped with `objectify` *before* being passed
261+ // to `captureException` (which is how we'd end up with a primitive wrapper as tested above) in order for the
262+ // already-seen check to work . Any primitive which is passed without being wrapped will be captured each time it
263+ // is encountered, so this test doesn't apply.
264+ ] ) ( "doesn't capture the same exception twice - %s" , ( _name : string , thrown : any ) => {
265+ const client = new TestClient ( { dsn : PUBLIC_DSN } ) ;
266+
267+ expect ( thrown . __sentry_captured__ ) . toBeUndefined ( ) ;
268+
269+ client . captureException ( thrown ) ;
270+
271+ expect ( thrown . __sentry_captured__ ) . toBe ( true ) ;
272+ expect ( backendEventFromException ) . toHaveBeenCalledTimes ( 1 ) ;
273+
274+ client . captureException ( thrown ) ;
275+
276+ // `captureException` should bail right away this second time around and not get as far as calling this again
277+ expect ( backendEventFromException ) . toHaveBeenCalledTimes ( 1 ) ;
278+ } ) ;
252279 } ) ;
253280
254281 describe ( 'captureMessage' , ( ) => {
@@ -325,6 +352,35 @@ describe('BaseClient', () => {
325352 expect ( TestBackend . instance ! . event ) . toBeUndefined ( ) ;
326353 } ) ;
327354
355+ test . each ( [
356+ [ '`Error` instance' , new Error ( 'Will I get caught twice?' ) ] ,
357+ [ 'plain object' , { 'Will I' : 'get caught twice?' } ] ,
358+ [ 'primitive wrapper' , new String ( 'Will I get caught twice?' ) ] ,
359+ // Primitives aren't tested directly here because they need to be wrapped with `objectify` *before* being passed
360+ // to `captureEvent` (which is how we'd end up with a primitive wrapper as tested above) in order for the
361+ // already-seen check to work . Any primitive which is passed without being wrapped will be captured each time it
362+ // is encountered, so this test doesn't apply.
363+ ] ) ( "doesn't capture an event wrapping the same exception twice - %s" , ( _name : string , thrown : any ) => {
364+ // Note: this is the same test as in `describe(captureException)`, except with the exception already wrapped in a
365+ // hint and accompanying an event. Duplicated here because some methods skip `captureException` and go straight to
366+ // `captureEvent`.
367+ const client = new TestClient ( { dsn : PUBLIC_DSN } ) ;
368+ const event = { exception : { values : [ { type : 'Error' , message : 'Will I get caught twice?' } ] } } ;
369+ const hint = { originalException : thrown } ;
370+
371+ expect ( thrown . __sentry_captured__ ) . toBeUndefined ( ) ;
372+
373+ client . captureEvent ( event , hint ) ;
374+
375+ expect ( thrown . __sentry_captured__ ) . toBe ( true ) ;
376+ expect ( clientProcess ) . toHaveBeenCalledTimes ( 1 ) ;
377+
378+ client . captureEvent ( event , hint ) ;
379+
380+ // `captureEvent` should bail right away this second time around and not get as far as calling this again
381+ expect ( clientProcess ) . toHaveBeenCalledTimes ( 1 ) ;
382+ } ) ;
383+
328384 test ( 'sends an event' , ( ) => {
329385 expect . assertions ( 2 ) ;
330386 const client = new TestClient ( { dsn : PUBLIC_DSN } ) ;
@@ -798,7 +854,7 @@ describe('BaseClient', () => {
798854 expect ( TestBackend . instance ! . event ) . toBeUndefined ( ) ;
799855 } ) ;
800856
801- test ( 'calls beforeSend gets an access to a hint as a second argument' , ( ) => {
857+ test ( 'beforeSend gets access to a hint as a second argument' , ( ) => {
802858 expect . assertions ( 2 ) ;
803859 const beforeSend = jest . fn ( ( event , hint ) => ( { ...event , data : hint . data } ) ) ;
804860 const client = new TestClient ( { dsn : PUBLIC_DSN , beforeSend } ) ;
0 commit comments