Skip to content

Commit f819d81

Browse files
feat(breadcrumbs): Send component names on UI breadcrumbs (#9946)
One of the PRs scoped from #9855 Sends component names on UI event breadcrumbs --------- Co-authored-by: Abhijeet Prasad <[email protected]>
1 parent 9a2570b commit f819d81

File tree

5 files changed

+104
-14
lines changed

5 files changed

+104
-14
lines changed

packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/click/template.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
<body>
88
<button id="button1" type="button">Button 1</button>
99
<button id="button2" type="button">Button 2</button>
10+
<button id="annotated-button" type="button" data-sentry-component="AnnotatedButton">Button 3</button>
1011
</body>
1112
</html>

packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/click/test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,39 @@ sentryTest('captures Breadcrumb for clicks & debounces them for a second', async
5555
},
5656
]);
5757
});
58+
59+
sentryTest(
60+
'uses the annotated component name in the breadcrumb messages and adds it to the data object',
61+
async ({ getLocalTestUrl, page }) => {
62+
const url = await getLocalTestUrl({ testDir: __dirname });
63+
64+
await page.route('**/foo', route => {
65+
return route.fulfill({
66+
status: 200,
67+
body: JSON.stringify({
68+
userNames: ['John', 'Jane'],
69+
}),
70+
headers: {
71+
'Content-Type': 'application/json',
72+
},
73+
});
74+
});
75+
76+
const promise = getFirstSentryEnvelopeRequest<Event>(page);
77+
78+
await page.goto(url);
79+
await page.click('#annotated-button');
80+
await page.evaluate('Sentry.captureException("test exception")');
81+
82+
const eventData = await promise;
83+
84+
expect(eventData.breadcrumbs).toEqual([
85+
{
86+
timestamp: expect.any(Number),
87+
category: 'ui.click',
88+
message: 'body > AnnotatedButton',
89+
data: { 'ui.component_name': 'AnnotatedButton' },
90+
},
91+
]);
92+
},
93+
);

packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/textInput/template.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
<body>
88
<input id="input1" type="text" />
99
<input id="input2" type="text" />
10+
<input id="annotated-input" data-sentry-component="AnnotatedInput" type="text" />
1011
</body>
1112
</html>

packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/textInput/test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,48 @@ sentryTest('captures Breadcrumb for events on inputs & debounced them', async ({
6464
},
6565
]);
6666
});
67+
68+
sentryTest(
69+
'includes the annotated component name within the breadcrumb message and data',
70+
async ({ getLocalTestUrl, page }) => {
71+
const url = await getLocalTestUrl({ testDir: __dirname });
72+
73+
await page.route('**/foo', route => {
74+
return route.fulfill({
75+
status: 200,
76+
body: JSON.stringify({
77+
userNames: ['John', 'Jane'],
78+
}),
79+
headers: {
80+
'Content-Type': 'application/json',
81+
},
82+
});
83+
});
84+
85+
const promise = getFirstSentryEnvelopeRequest<Event>(page);
86+
87+
await page.goto(url);
88+
89+
await page.click('#annotated-input');
90+
await page.type('#annotated-input', 'John', { delay: 1 });
91+
92+
await page.evaluate('Sentry.captureException("test exception")');
93+
const eventData = await promise;
94+
expect(eventData.exception?.values).toHaveLength(1);
95+
96+
expect(eventData.breadcrumbs).toEqual([
97+
{
98+
timestamp: expect.any(Number),
99+
category: 'ui.click',
100+
message: 'body > AnnotatedInput',
101+
data: { 'ui.component_name': 'AnnotatedInput' },
102+
},
103+
{
104+
timestamp: expect.any(Number),
105+
category: 'ui.input',
106+
message: 'body > AnnotatedInput',
107+
data: { 'ui.component_name': 'AnnotatedInput' },
108+
},
109+
]);
110+
},
111+
);

packages/browser/src/integrations/breadcrumbs.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
IntegrationFn,
1313
} from '@sentry/types';
1414
import type {
15+
Breadcrumb,
1516
FetchBreadcrumbData,
1617
FetchBreadcrumbHint,
1718
XhrBreadcrumbData,
@@ -24,6 +25,7 @@ import {
2425
addFetchInstrumentationHandler,
2526
addHistoryInstrumentationHandler,
2627
addXhrInstrumentationHandler,
28+
getComponentName,
2729
getEventDescription,
2830
htmlTreeAsString,
2931
logger,
@@ -133,6 +135,7 @@ function _getDomBreadcrumbHandler(
133135
}
134136

135137
let target;
138+
let componentName;
136139
let keyAttrs = typeof dom === 'object' ? dom.serializeAttribute : undefined;
137140

138141
let maxStringLength =
@@ -152,9 +155,10 @@ function _getDomBreadcrumbHandler(
152155
// Accessing event.target can throw (see getsentry/raven-js#838, #768)
153156
try {
154157
const event = handlerData.event as Event | Node;
155-
target = _isEvent(event)
156-
? htmlTreeAsString(event.target, { keyAttrs, maxStringLength })
157-
: htmlTreeAsString(event, { keyAttrs, maxStringLength });
158+
const element = _isEvent(event) ? event.target : event;
159+
160+
target = htmlTreeAsString(element, { keyAttrs, maxStringLength });
161+
componentName = getComponentName(element);
158162
} catch (e) {
159163
target = '<unknown>';
160164
}
@@ -163,17 +167,20 @@ function _getDomBreadcrumbHandler(
163167
return;
164168
}
165169

166-
addBreadcrumb(
167-
{
168-
category: `ui.${handlerData.name}`,
169-
message: target,
170-
},
171-
{
172-
event: handlerData.event,
173-
name: handlerData.name,
174-
global: handlerData.global,
175-
},
176-
);
170+
const breadcrumb: Breadcrumb = {
171+
category: `ui.${handlerData.name}`,
172+
message: target,
173+
};
174+
175+
if (componentName) {
176+
breadcrumb.data = { 'ui.component_name': componentName };
177+
}
178+
179+
addBreadcrumb(breadcrumb, {
180+
event: handlerData.event,
181+
name: handlerData.name,
182+
global: handlerData.global,
183+
});
177184
};
178185
}
179186

0 commit comments

Comments
 (0)