Skip to content

Commit cd7a513

Browse files
authored
fix(utils): Normalize HTML elements as string (#7916)
Currently, we do not special-case html elements in normalization. This means they are still normalized as objects, leading to potentially deeply nested stuff, and to problems with e.g. replay.
1 parent 6e5cb41 commit cd7a513

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

packages/browser-integration-tests/suites/public-api/setContext/non_serializable_context/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ sentryTest('should normalize non-serializable context', async ({ getLocalTestPat
99

1010
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
1111

12-
expect(eventData.contexts?.non_serializable).toMatchObject({});
12+
expect(eventData.contexts?.non_serializable).toEqual('[HTMLElement: HTMLBodyElement]');
1313
expect(eventData.message).toBe('non_serializable');
1414
});

packages/utils/src/normalize.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ function visit(
170170
// TODO remove this in v7 (this means the method will no longer be exported, under any name)
171171
export { visit as walk };
172172

173+
/* eslint-disable complexity */
173174
/**
174175
* Stringify the given value. Handles various known special values and types.
175176
*
@@ -242,11 +243,19 @@ function stringifyValue(
242243
// them to strings means that instances of classes which haven't defined their `toStringTag` will just come out as
243244
// `"[object Object]"`. If we instead look at the constructor's name (which is the same as the name of the class),
244245
// we can make sure that only plain objects come out that way.
245-
return `[object ${getConstructorName(value)}]`;
246+
const objName = getConstructorName(value);
247+
248+
// Handle HTML Elements
249+
if (/^HTML(\w*)Element$/.test(objName)) {
250+
return `[HTMLElement: ${objName}]`;
251+
}
252+
253+
return `[object ${objName}]`;
246254
} catch (err) {
247255
return `**non-serializable** (${err})`;
248256
}
249257
}
258+
/* eslint-enable complexity */
250259

251260
function getConstructorName(value: unknown): string {
252261
const prototype: Prototype | null = Object.getPrototypeOf(value);

packages/utils/test/normalize.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,32 @@ describe('normalize()', () => {
263263
});
264264
});
265265

266+
describe('handles HTML elements', () => {
267+
test('HTMLDivElement', () => {
268+
expect(
269+
normalize({
270+
div: document.createElement('div'),
271+
div2: document.createElement('div'),
272+
}),
273+
).toEqual({
274+
div: '[HTMLElement: HTMLDivElement]',
275+
div2: '[HTMLElement: HTMLDivElement]',
276+
});
277+
});
278+
279+
test('input elements', () => {
280+
expect(
281+
normalize({
282+
input: document.createElement('input'),
283+
select: document.createElement('select'),
284+
}),
285+
).toEqual({
286+
input: '[HTMLElement: HTMLInputElement]',
287+
select: '[HTMLElement: HTMLSelectElement]',
288+
});
289+
});
290+
});
291+
266292
describe('calls toJSON if implemented', () => {
267293
test('primitive values', () => {
268294
const a = new Number(1) as any;

0 commit comments

Comments
 (0)