@@ -446,15 +446,14 @@ describe("breadcrumbs", function() {
446446 } ) ;
447447 } ) ;
448448
449- it ( "should bail out if accessing the `type` and ` target` properties of an event throw an exception" , function ( ) {
449+ it ( "should bail out if accessing the `target` property of an event throws an exception" , function ( ) {
450450 // see: https://github.com/getsentry/sentry-javascript/issues/768
451451 return runInSandbox ( sandbox , function ( ) {
452452 // click <input/>
453453 var click = new MouseEvent ( "click" ) ;
454454 function kaboom ( ) {
455455 throw new Error ( "lol" ) ;
456456 }
457- Object . defineProperty ( click , "type" , { get : kaboom } ) ;
458457 Object . defineProperty ( click , "target" , { get : kaboom } ) ;
459458
460459 var input = document . querySelector ( ".a" ) ; // leaf node
@@ -500,6 +499,121 @@ describe("breadcrumbs", function() {
500499 } ) ;
501500 } ) ;
502501
502+ it ( "should correctly capture multiple consecutive breadcrumbs if they are of different type" , function ( ) {
503+ return runInSandbox ( sandbox , function ( ) {
504+ var input = document . getElementsByTagName ( "input" ) [ 0 ] ;
505+
506+ var clickHandler = function ( ) { } ;
507+ input . addEventListener ( "click" , clickHandler ) ;
508+ var keypressHandler = function ( ) { } ;
509+ input . addEventListener ( "keypress" , keypressHandler ) ;
510+
511+ input . dispatchEvent ( new MouseEvent ( "click" ) ) ;
512+ input . dispatchEvent ( new KeyboardEvent ( "keypress" ) ) ;
513+
514+ Sentry . captureMessage ( "test" ) ;
515+ } ) . then ( function ( summary ) {
516+ if ( IS_LOADER ) {
517+ // The async loader doesn't wrap event listeners, but we should receive the event without breadcrumbs
518+ assert . lengthOf ( summary . events , 1 ) ;
519+ } else {
520+ // Breadcrumb should be captured by the global event listeners, not a specific one
521+ assert . equal ( summary . breadcrumbs . length , 2 ) ;
522+ assert . equal ( summary . breadcrumbs [ 0 ] . category , "ui.click" ) ;
523+ assert . equal (
524+ summary . breadcrumbs [ 0 ] . message ,
525+ 'body > form#foo-form > input[name="foo"]'
526+ ) ;
527+ assert . equal ( summary . breadcrumbs [ 1 ] . category , "ui.input" ) ;
528+ assert . equal (
529+ summary . breadcrumbs [ 0 ] . message ,
530+ 'body > form#foo-form > input[name="foo"]'
531+ ) ;
532+ assert . equal ( summary . breadcrumbHints [ 0 ] . global , false ) ;
533+ assert . equal ( summary . breadcrumbHints [ 1 ] . global , false ) ;
534+ assert . isUndefined ( summary . events [ 0 ] . exception ) ;
535+ }
536+ } ) ;
537+ } ) ;
538+
539+ it ( "should debounce multiple consecutive identical breadcrumbs but allow for switching to a different type" , function ( ) {
540+ return runInSandbox ( sandbox , function ( ) {
541+ var input = document . getElementsByTagName ( "input" ) [ 0 ] ;
542+
543+ var clickHandler = function ( ) { } ;
544+ input . addEventListener ( "click" , clickHandler ) ;
545+ var keypressHandler = function ( ) { } ;
546+ input . addEventListener ( "keypress" , keypressHandler ) ;
547+
548+ input . dispatchEvent ( new MouseEvent ( "click" ) ) ;
549+ input . dispatchEvent ( new MouseEvent ( "click" ) ) ;
550+ input . dispatchEvent ( new MouseEvent ( "click" ) ) ;
551+ input . dispatchEvent ( new KeyboardEvent ( "keypress" ) ) ;
552+ input . dispatchEvent ( new KeyboardEvent ( "keypress" ) ) ;
553+ input . dispatchEvent ( new KeyboardEvent ( "keypress" ) ) ;
554+
555+ Sentry . captureMessage ( "test" ) ;
556+ } ) . then ( function ( summary ) {
557+ if ( IS_LOADER ) {
558+ // The async loader doesn't wrap event listeners, but we should receive the event without breadcrumbs
559+ assert . lengthOf ( summary . events , 1 ) ;
560+ } else {
561+ // Breadcrumb should be captured by the global event listeners, not a specific one
562+ assert . equal ( summary . breadcrumbs . length , 2 ) ;
563+ assert . equal ( summary . breadcrumbs [ 0 ] . category , "ui.click" ) ;
564+ assert . equal (
565+ summary . breadcrumbs [ 0 ] . message ,
566+ 'body > form#foo-form > input[name="foo"]'
567+ ) ;
568+ assert . equal ( summary . breadcrumbs [ 1 ] . category , "ui.input" ) ;
569+ assert . equal (
570+ summary . breadcrumbs [ 0 ] . message ,
571+ 'body > form#foo-form > input[name="foo"]'
572+ ) ;
573+ assert . equal ( summary . breadcrumbHints [ 0 ] . global , false ) ;
574+ assert . equal ( summary . breadcrumbHints [ 1 ] . global , false ) ;
575+ assert . isUndefined ( summary . events [ 0 ] . exception ) ;
576+ }
577+ } ) ;
578+ } ) ;
579+
580+ it ( "should debounce multiple consecutive identical breadcrumbs but allow for switching to a different target" , function ( ) {
581+ return runInSandbox ( sandbox , function ( ) {
582+ var input = document . querySelector ( "#foo-form input" ) ;
583+ var div = document . querySelector ( "#foo-form div" ) ;
584+
585+ var clickHandler = function ( ) { } ;
586+ input . addEventListener ( "click" , clickHandler ) ;
587+ div . addEventListener ( "click" , clickHandler ) ;
588+
589+ input . dispatchEvent ( new MouseEvent ( "click" ) ) ;
590+ div . dispatchEvent ( new MouseEvent ( "click" ) ) ;
591+
592+ Sentry . captureMessage ( "test" ) ;
593+ } ) . then ( function ( summary ) {
594+ if ( IS_LOADER ) {
595+ // The async loader doesn't wrap event listeners, but we should receive the event without breadcrumbs
596+ assert . lengthOf ( summary . events , 1 ) ;
597+ } else {
598+ // Breadcrumb should be captured by the global event listeners, not a specific one
599+ assert . equal ( summary . breadcrumbs . length , 2 ) ;
600+ assert . equal ( summary . breadcrumbs [ 0 ] . category , "ui.click" ) ;
601+ assert . equal (
602+ summary . breadcrumbs [ 0 ] . message ,
603+ 'body > form#foo-form > input[name="foo"]'
604+ ) ;
605+ assert . equal ( summary . breadcrumbs [ 1 ] . category , "ui.click" ) ;
606+ assert . equal (
607+ summary . breadcrumbs [ 1 ] . message ,
608+ "body > form#foo-form > div.contenteditable"
609+ ) ;
610+ assert . equal ( summary . breadcrumbHints [ 0 ] . global , false ) ;
611+ assert . equal ( summary . breadcrumbHints [ 1 ] . global , false ) ;
612+ assert . isUndefined ( summary . events [ 0 ] . exception ) ;
613+ }
614+ } ) ;
615+ } ) ;
616+
503617 it (
504618 optional (
505619 "should flush keypress breadcrumbs when an error is thrown" ,
@@ -659,6 +773,42 @@ describe("breadcrumbs", function() {
659773 } ) ;
660774 } ) ;
661775
776+ it ( "should remove breadcrumb instrumentation when all event listeners are detached" , function ( ) {
777+ return runInSandbox ( sandbox , function ( ) {
778+ var input = document . getElementsByTagName ( "input" ) [ 0 ] ;
779+
780+ var clickHandler = function ( ) { } ;
781+ var otherClickHandler = function ( ) { } ;
782+ input . addEventListener ( "click" , clickHandler ) ;
783+ input . addEventListener ( "click" , otherClickHandler ) ;
784+ input . removeEventListener ( "click" , clickHandler ) ;
785+ input . removeEventListener ( "click" , otherClickHandler ) ;
786+
787+ var keypressHandler = function ( ) { } ;
788+ var otherKeypressHandler = function ( ) { } ;
789+ input . addEventListener ( "keypress" , keypressHandler ) ;
790+ input . addEventListener ( "keypress" , otherKeypressHandler ) ;
791+ input . removeEventListener ( "keypress" , keypressHandler ) ;
792+ input . removeEventListener ( "keypress" , otherKeypressHandler ) ;
793+
794+ input . dispatchEvent ( new MouseEvent ( "click" ) ) ;
795+ input . dispatchEvent ( new KeyboardEvent ( "keypress" ) ) ;
796+
797+ Sentry . captureMessage ( "test" ) ;
798+ } ) . then ( function ( summary ) {
799+ if ( IS_LOADER ) {
800+ // The async loader doesn't wrap event listeners, but we should receive the event without breadcrumbs
801+ assert . lengthOf ( summary . events , 1 ) ;
802+ } else {
803+ // Breadcrumb should be captured by the global event listeners, not a specific one
804+ assert . equal ( summary . breadcrumbs . length , 2 ) ;
805+ assert . equal ( summary . breadcrumbHints [ 0 ] . global , true ) ;
806+ assert . equal ( summary . breadcrumbHints [ 1 ] . global , true ) ;
807+ assert . isUndefined ( summary . events [ 0 ] . exception ) ;
808+ }
809+ } ) ;
810+ } ) ;
811+
662812 it (
663813 optional (
664814 "should record history.[pushState|replaceState] changes as navigation breadcrumbs" ,
0 commit comments