From 0968722450329c4847d2569d05d897b83b37fe65 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 20 Apr 2023 11:41:13 +0200 Subject: [PATCH] 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 --- .../non_serializable_context/test.ts | 2 +- packages/utils/src/normalize.ts | 11 +++++++- packages/utils/test/normalize.test.ts | 26 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/browser-integration-tests/suites/public-api/setContext/non_serializable_context/test.ts b/packages/browser-integration-tests/suites/public-api/setContext/non_serializable_context/test.ts index 3c6d17dbdb03..9b270205f109 100644 --- a/packages/browser-integration-tests/suites/public-api/setContext/non_serializable_context/test.ts +++ b/packages/browser-integration-tests/suites/public-api/setContext/non_serializable_context/test.ts @@ -9,6 +9,6 @@ sentryTest('should normalize non-serializable context', async ({ getLocalTestPat const eventData = await getFirstSentryEnvelopeRequest(page, url); - expect(eventData.contexts?.non_serializable).toMatchObject({}); + expect(eventData.contexts?.non_serializable).toEqual('[HTMLElement: HTMLBodyElement]'); expect(eventData.message).toBe('non_serializable'); }); diff --git a/packages/utils/src/normalize.ts b/packages/utils/src/normalize.ts index 508442c2d14e..4b2dd611f8e2 100644 --- a/packages/utils/src/normalize.ts +++ b/packages/utils/src/normalize.ts @@ -170,6 +170,7 @@ function visit( // TODO remove this in v7 (this means the method will no longer be exported, under any name) export { visit as walk }; +/* eslint-disable complexity */ /** * Stringify the given value. Handles various known special values and types. * @@ -242,11 +243,19 @@ function stringifyValue( // them to strings means that instances of classes which haven't defined their `toStringTag` will just come out as // `"[object Object]"`. If we instead look at the constructor's name (which is the same as the name of the class), // we can make sure that only plain objects come out that way. - return `[object ${getConstructorName(value)}]`; + const objName = getConstructorName(value); + + // Handle HTML Elements + if (/^HTML(\w*)Element$/.test(objName)) { + return `[HTMLElement: ${objName}]`; + } + + return `[object ${objName}]`; } catch (err) { return `**non-serializable** (${err})`; } } +/* eslint-enable complexity */ function getConstructorName(value: unknown): string { const prototype: Prototype | null = Object.getPrototypeOf(value); diff --git a/packages/utils/test/normalize.test.ts b/packages/utils/test/normalize.test.ts index 94676c1449da..008bde5dfebe 100644 --- a/packages/utils/test/normalize.test.ts +++ b/packages/utils/test/normalize.test.ts @@ -263,6 +263,32 @@ describe('normalize()', () => { }); }); + describe('handles HTML elements', () => { + test('HTMLDivElement', () => { + expect( + normalize({ + div: document.createElement('div'), + div2: document.createElement('div'), + }), + ).toEqual({ + div: '[HTMLElement: HTMLDivElement]', + div2: '[HTMLElement: HTMLDivElement]', + }); + }); + + test('input elements', () => { + expect( + normalize({ + input: document.createElement('input'), + select: document.createElement('select'), + }), + ).toEqual({ + input: '[HTMLElement: HTMLInputElement]', + select: '[HTMLElement: HTMLSelectElement]', + }); + }); + }); + describe('calls toJSON if implemented', () => { test('primitive values', () => { const a = new Number(1) as any;