Skip to content

Commit 0968722

Browse files
committed
fix(utils): Normalize HTML elements as string
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. IMHO it is more expected to see a string representation of this element instead? Either just the element class name (this PR), or alternatively
1 parent 6e5cb41 commit 0968722

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)