Skip to content

Commit 61563f8

Browse files
ref: Rework DOM Breadcrumbs integration (#3208)
* ref: Rework DOM Breadcrumbs integration Co-authored-by: Katie Byers <[email protected]>
1 parent 6562472 commit 61563f8

File tree

3 files changed

+345
-140
lines changed

3 files changed

+345
-140
lines changed

packages/browser/src/integrations/breadcrumbs.ts

+1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ export class Breadcrumbs implements Integration {
184184
{
185185
event: handlerData.event,
186186
name: handlerData.name,
187+
global: handlerData.global,
187188
},
188189
);
189190
}

packages/browser/test/integration/suites/breadcrumbs.js

+152-2
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)