Skip to content

Commit

Permalink
feat(breadcrumbs): Send component names on UI breadcrumbs (#9946)
Browse files Browse the repository at this point in the history
One of the PRs scoped from
#9855

Sends component names on UI event breadcrumbs

---------

Co-authored-by: Abhijeet Prasad <aprasad@sentry.io>
  • Loading branch information
0Calories and AbhiPrasad committed Dec 21, 2023
1 parent 9a2570b commit f819d81
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
<body>
<button id="button1" type="button">Button 1</button>
<button id="button2" type="button">Button 2</button>
<button id="annotated-button" type="button" data-sentry-component="AnnotatedButton">Button 3</button>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,39 @@ sentryTest('captures Breadcrumb for clicks & debounces them for a second', async
},
]);
});

sentryTest(
'uses the annotated component name in the breadcrumb messages and adds it to the data object',
async ({ getLocalTestUrl, page }) => {
const url = await getLocalTestUrl({ testDir: __dirname });

await page.route('**/foo', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({
userNames: ['John', 'Jane'],
}),
headers: {
'Content-Type': 'application/json',
},
});
});

const promise = getFirstSentryEnvelopeRequest<Event>(page);

await page.goto(url);
await page.click('#annotated-button');
await page.evaluate('Sentry.captureException("test exception")');

const eventData = await promise;

expect(eventData.breadcrumbs).toEqual([
{
timestamp: expect.any(Number),
category: 'ui.click',
message: 'body > AnnotatedButton',
data: { 'ui.component_name': 'AnnotatedButton' },
},
]);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
<body>
<input id="input1" type="text" />
<input id="input2" type="text" />
<input id="annotated-input" data-sentry-component="AnnotatedInput" type="text" />
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,48 @@ sentryTest('captures Breadcrumb for events on inputs & debounced them', async ({
},
]);
});

sentryTest(
'includes the annotated component name within the breadcrumb message and data',
async ({ getLocalTestUrl, page }) => {
const url = await getLocalTestUrl({ testDir: __dirname });

await page.route('**/foo', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({
userNames: ['John', 'Jane'],
}),
headers: {
'Content-Type': 'application/json',
},
});
});

const promise = getFirstSentryEnvelopeRequest<Event>(page);

await page.goto(url);

await page.click('#annotated-input');
await page.type('#annotated-input', 'John', { delay: 1 });

await page.evaluate('Sentry.captureException("test exception")');
const eventData = await promise;
expect(eventData.exception?.values).toHaveLength(1);

expect(eventData.breadcrumbs).toEqual([
{
timestamp: expect.any(Number),
category: 'ui.click',
message: 'body > AnnotatedInput',
data: { 'ui.component_name': 'AnnotatedInput' },
},
{
timestamp: expect.any(Number),
category: 'ui.input',
message: 'body > AnnotatedInput',
data: { 'ui.component_name': 'AnnotatedInput' },
},
]);
},
);
35 changes: 21 additions & 14 deletions packages/browser/src/integrations/breadcrumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
IntegrationFn,
} from '@sentry/types';
import type {
Breadcrumb,
FetchBreadcrumbData,
FetchBreadcrumbHint,
XhrBreadcrumbData,
Expand All @@ -24,6 +25,7 @@ import {
addFetchInstrumentationHandler,
addHistoryInstrumentationHandler,
addXhrInstrumentationHandler,
getComponentName,
getEventDescription,
htmlTreeAsString,
logger,
Expand Down Expand Up @@ -133,6 +135,7 @@ function _getDomBreadcrumbHandler(
}

let target;
let componentName;
let keyAttrs = typeof dom === 'object' ? dom.serializeAttribute : undefined;

let maxStringLength =
Expand All @@ -152,9 +155,10 @@ function _getDomBreadcrumbHandler(
// Accessing event.target can throw (see getsentry/raven-js#838, #768)
try {
const event = handlerData.event as Event | Node;
target = _isEvent(event)
? htmlTreeAsString(event.target, { keyAttrs, maxStringLength })
: htmlTreeAsString(event, { keyAttrs, maxStringLength });
const element = _isEvent(event) ? event.target : event;

target = htmlTreeAsString(element, { keyAttrs, maxStringLength });
componentName = getComponentName(element);
} catch (e) {
target = '<unknown>';
}
Expand All @@ -163,17 +167,20 @@ function _getDomBreadcrumbHandler(
return;
}

addBreadcrumb(
{
category: `ui.${handlerData.name}`,
message: target,
},
{
event: handlerData.event,
name: handlerData.name,
global: handlerData.global,
},
);
const breadcrumb: Breadcrumb = {
category: `ui.${handlerData.name}`,
message: target,
};

if (componentName) {
breadcrumb.data = { 'ui.component_name': componentName };
}

addBreadcrumb(breadcrumb, {
event: handlerData.event,
name: handlerData.name,
global: handlerData.global,
});
};
}

Expand Down

0 comments on commit f819d81

Please sign in to comment.