From 0376fe35e149bc89012b47446134850500586760 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Fri, 19 Apr 2024 15:11:35 -0400 Subject: [PATCH 1/2] feat(replay): Add option to pass in custom record fn Bringing back https://github.com/getsentry/sentry-javascript/pull/8647 but it would be nice to test new versions of rrweb without having to bundle it with our replay SDK. --- .../src/coreHandlers/handleClick.ts | 11 ++++-- .../src/coreHandlers/handleDom.ts | 17 ++++----- .../src/coreHandlers/handleKeyboardEvent.ts | 9 +++-- .../src/coreHandlers/performanceObserver.ts | 2 +- packages/replay-internal/src/replay.ts | 25 +++++++++++-- packages/replay-internal/src/types/replay.ts | 4 ++ packages/replay-internal/src/types/rrweb.ts | 2 +- .../src/util/createPerformanceEntries.ts | 37 +++++++++++++------ .../src/util/handleRecordingEmit.ts | 2 +- 9 files changed, 75 insertions(+), 34 deletions(-) diff --git a/packages/replay-internal/src/coreHandlers/handleClick.ts b/packages/replay-internal/src/coreHandlers/handleClick.ts index dab5d04657fc..d1ccfb8805db 100644 --- a/packages/replay-internal/src/coreHandlers/handleClick.ts +++ b/packages/replay-internal/src/coreHandlers/handleClick.ts @@ -1,4 +1,5 @@ -import { IncrementalSource, MouseInteractions, record } from '@sentry-internal/rrweb'; +import { IncrementalSource, MouseInteractions } from '@sentry-internal/rrweb'; +import type { Mirror } from '@sentry-internal/rrweb-snapshot'; import type { Breadcrumb } from '@sentry/types'; import { WINDOW } from '../constants'; @@ -308,7 +309,11 @@ function nowInSeconds(): number { } /** Update the click detector based on a recording event of rrweb. */ -export function updateClickDetectorForRecordingEvent(clickDetector: ReplayClickDetector, event: RecordingEvent): void { +export function updateClickDetectorForRecordingEvent( + clickDetector: ReplayClickDetector, + event: RecordingEvent, + mirror: Mirror, +): void { try { // note: We only consider incremental snapshots here // This means that any full snapshot is ignored for mutation detection - the reason is that we simply cannot know if a mutation happened here. @@ -333,7 +338,7 @@ export function updateClickDetectorForRecordingEvent(clickDetector: ReplayClickD if (isIncrementalMouseInteraction(event)) { const { type, id } = event.data; - const node = record.mirror.getNode(id); + const node = mirror.getNode(id); if (node instanceof HTMLElement && type === MouseInteractions.Click) { clickDetector.registerClick(node); diff --git a/packages/replay-internal/src/coreHandlers/handleDom.ts b/packages/replay-internal/src/coreHandlers/handleDom.ts index a5c20810481b..98990c9dd0d4 100644 --- a/packages/replay-internal/src/coreHandlers/handleDom.ts +++ b/packages/replay-internal/src/coreHandlers/handleDom.ts @@ -1,5 +1,4 @@ -import { record } from '@sentry-internal/rrweb'; -import type { serializedElementNodeWithId, serializedNodeWithId } from '@sentry-internal/rrweb-snapshot'; +import type { Mirror, serializedElementNodeWithId, serializedNodeWithId } from '@sentry-internal/rrweb-snapshot'; import { NodeType } from '@sentry-internal/rrweb-snapshot'; import type { Breadcrumb, HandlerDataDom } from '@sentry/types'; import { htmlTreeAsString } from '@sentry/utils'; @@ -19,7 +18,7 @@ export const handleDomListener: (replay: ReplayContainer) => (handlerData: Handl return; } - const result = handleDom(handlerData); + const result = handleDom(handlerData, replay.getDomMirror()); if (!result) { return; @@ -50,10 +49,10 @@ export const handleDomListener: (replay: ReplayContainer) => (handlerData: Handl }; /** Get the base DOM breadcrumb. */ -export function getBaseDomBreadcrumb(target: Node | null, message: string): Breadcrumb { - const nodeId = record.mirror.getId(target); - const node = nodeId && record.mirror.getNode(nodeId); - const meta = node && record.mirror.getMeta(node); +export function getBaseDomBreadcrumb(target: Node | null, message: string, mirror: Mirror): Breadcrumb { + const nodeId = mirror.getId(target); + const node = nodeId && mirror.getNode(nodeId); + const meta = node && mirror.getMeta(node); const element = meta && isElement(meta) ? meta : null; return { @@ -80,12 +79,12 @@ export function getBaseDomBreadcrumb(target: Node | null, message: string): Brea * An event handler to react to DOM events. * Exported for tests. */ -export function handleDom(handlerData: HandlerDataDom): Breadcrumb | null { +export function handleDom(handlerData: HandlerDataDom, mirror: Mirror): Breadcrumb | null { const { target, message } = getDomTarget(handlerData); return createBreadcrumb({ category: `ui.${handlerData.name}`, - ...getBaseDomBreadcrumb(target, message), + ...getBaseDomBreadcrumb(target, message, mirror), }); } diff --git a/packages/replay-internal/src/coreHandlers/handleKeyboardEvent.ts b/packages/replay-internal/src/coreHandlers/handleKeyboardEvent.ts index 0f7560f39584..4679653d58b7 100644 --- a/packages/replay-internal/src/coreHandlers/handleKeyboardEvent.ts +++ b/packages/replay-internal/src/coreHandlers/handleKeyboardEvent.ts @@ -1,3 +1,4 @@ +import type { Mirror } from '@sentry-internal/rrweb-snapshot'; import type { Breadcrumb } from '@sentry/types'; import { htmlTreeAsString } from '@sentry/utils'; @@ -7,7 +8,7 @@ import { getBaseDomBreadcrumb } from './handleDom'; import { addBreadcrumbEvent } from './util/addBreadcrumbEvent'; /** Handle keyboard events & create breadcrumbs. */ -export function handleKeyboardEvent(replay: ReplayContainer, event: KeyboardEvent): void { +export function handleKeyboardEvent(replay: ReplayContainer, event: KeyboardEvent, mirror: Mirror): void { if (!replay.isEnabled()) { return; } @@ -17,7 +18,7 @@ export function handleKeyboardEvent(replay: ReplayContainer, event: KeyboardEven // session with a single "keydown" breadcrumb is created) replay.updateUserActivity(); - const breadcrumb = getKeyboardBreadcrumb(event); + const breadcrumb = getKeyboardBreadcrumb(event, mirror); if (!breadcrumb) { return; @@ -27,7 +28,7 @@ export function handleKeyboardEvent(replay: ReplayContainer, event: KeyboardEven } /** exported only for tests */ -export function getKeyboardBreadcrumb(event: KeyboardEvent): Breadcrumb | null { +export function getKeyboardBreadcrumb(event: KeyboardEvent, mirror: Mirror): Breadcrumb | null { const { metaKey, shiftKey, ctrlKey, altKey, key, target } = event; // never capture for input fields @@ -46,7 +47,7 @@ export function getKeyboardBreadcrumb(event: KeyboardEvent): Breadcrumb | null { } const message = htmlTreeAsString(target, { maxStringLength: 200 }) || ''; - const baseBreadcrumb = getBaseDomBreadcrumb(target as Node, message); + const baseBreadcrumb = getBaseDomBreadcrumb(target as Node, message, mirror); return createBreadcrumb({ category: 'ui.keyDown', diff --git a/packages/replay-internal/src/coreHandlers/performanceObserver.ts b/packages/replay-internal/src/coreHandlers/performanceObserver.ts index 45b843760e52..c26a358fc6ed 100644 --- a/packages/replay-internal/src/coreHandlers/performanceObserver.ts +++ b/packages/replay-internal/src/coreHandlers/performanceObserver.ts @@ -27,7 +27,7 @@ export function setupPerformanceObserver(replay: ReplayContainer): () => void { clearCallbacks.push( addLcpInstrumentationHandler(({ metric }) => { - replay.replayPerformanceEntries.push(getLargestContentfulPaint(metric)); + replay.replayPerformanceEntries.push(getLargestContentfulPaint(metric, replay.getDomMirror())); }), ); diff --git a/packages/replay-internal/src/replay.ts b/packages/replay-internal/src/replay.ts index 6588a9e4d79d..235c4a1e82e3 100644 --- a/packages/replay-internal/src/replay.ts +++ b/packages/replay-internal/src/replay.ts @@ -1,5 +1,6 @@ /* eslint-disable max-lines */ // TODO: We might want to split this file up import { EventType, record } from '@sentry-internal/rrweb'; +import type { Mirror } from '@sentry-internal/rrweb-snapshot'; import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, captureException, @@ -139,6 +140,13 @@ export class ReplayContainer implements ReplayContainerInterface { */ private _hasInitializedCoreListeners: boolean; + /** + * The `record` function to use, defaults to package's `record()`, but we can + * opt to pass in a different version, i.e. if we wanted to test a different + * version. + */ + private _recordFn: typeof record; + /** * Function to stop recording */ @@ -207,6 +215,8 @@ export class ReplayContainer implements ReplayContainerInterface { if (slowClickConfig) { this.clickDetector = new ClickDetector(this, slowClickConfig); } + + this._recordFn = options._experiments.recordFn || record; } /** Get the event context. */ @@ -214,6 +224,13 @@ export class ReplayContainer implements ReplayContainerInterface { return this._context; } + /** + * Returns rrweb's mirror + */ + public getDomMirror(): Mirror { + return this._recordFn.mirror; + } + /** If recording is currently enabled. */ public isEnabled(): boolean { return this._isEnabled; @@ -353,7 +370,7 @@ export class ReplayContainer implements ReplayContainerInterface { try { const canvasOptions = this._canvas; - this._stopRecording = record({ + this._stopRecording = this._recordFn({ ...this._recordingOptions, // When running in error sampling mode, we need to overwrite `checkoutEveryNms` // Without this, it would record forever, until an error happens, which we don't want @@ -926,7 +943,7 @@ export class ReplayContainer implements ReplayContainerInterface { /** Ensure page remains active when a key is pressed. */ private _handleKeyboardEvent: (event: KeyboardEvent) => void = (event: KeyboardEvent) => { - handleKeyboardEvent(this, event); + handleKeyboardEvent(this, event, this.getDomMirror()); }; /** @@ -1021,7 +1038,9 @@ export class ReplayContainer implements ReplayContainerInterface { * are included in the replay event before it is finished and sent to Sentry. */ private _addPerformanceEntries(): Promise> { - const performanceEntries = createPerformanceEntries(this.performanceEntries).concat(this.replayPerformanceEntries); + const performanceEntries = createPerformanceEntries(this.performanceEntries, this.getDomMirror()).concat( + this.replayPerformanceEntries, + ); this.performanceEntries = []; this.replayPerformanceEntries = []; diff --git a/packages/replay-internal/src/types/replay.ts b/packages/replay-internal/src/types/replay.ts index 7ebacad9e100..b78f68369489 100644 --- a/packages/replay-internal/src/types/replay.ts +++ b/packages/replay-internal/src/types/replay.ts @@ -1,3 +1,5 @@ +import type { record } from '@sentry-internal/rrweb'; +import type { Mirror } from '@sentry-internal/rrweb-snapshot'; import type { Breadcrumb, ErrorEvent, @@ -232,6 +234,7 @@ export interface ReplayPluginOptions extends ReplayNetworkOptions { _experiments: Partial<{ captureExceptions: boolean; traceInternals: boolean; + recordFn: typeof record; }>; } @@ -465,6 +468,7 @@ export interface ReplayContainer { isPaused(): boolean; isRecordingCanvas(): boolean; getContext(): InternalEventContext; + getDomMirror(): Mirror; initializeSampling(): void; start(): void; stop(options?: { reason?: string; forceflush?: boolean }): Promise; diff --git a/packages/replay-internal/src/types/rrweb.ts b/packages/replay-internal/src/types/rrweb.ts index a490a6e46c1b..7958cc6b0a30 100644 --- a/packages/replay-internal/src/types/rrweb.ts +++ b/packages/replay-internal/src/types/rrweb.ts @@ -31,7 +31,7 @@ export type ReplayEventWithTime = { /** * This is a partial copy of rrweb's recording options which only contains the properties - * we specifically us in the SDK. Users can specify additional properties, hence we add the + * we specifically use in the SDK. Users can specify additional properties, hence we add the * Record union type. */ export type RrwebRecordOptions = { diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index 20217d9e4083..eef081483e6f 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -1,4 +1,4 @@ -import { record } from '@sentry-internal/rrweb'; +import type { Mirror } from '@sentry-internal/rrweb-snapshot'; import { browserPerformanceTimeOrigin } from '@sentry/utils'; import { WINDOW } from '../constants'; @@ -16,7 +16,7 @@ import type { // Map entryType -> function to normalize data for event const ENTRY_TYPES: Record< string, - (entry: AllPerformanceEntry) => null | ReplayPerformanceEntry + (entry: AllPerformanceEntry, mirror: Mirror) => null | ReplayPerformanceEntry > = { // @ts-expect-error TODO: entry type does not fit the create* functions entry type resource: createResourceEntry, @@ -30,16 +30,22 @@ const ENTRY_TYPES: Record< */ export function createPerformanceEntries( entries: AllPerformanceEntry[], + mirror: Mirror, ): ReplayPerformanceEntry[] { - return entries.map(createPerformanceEntry).filter(Boolean) as ReplayPerformanceEntry[]; + return entries + .map(entry => createPerformanceEntry(entry, mirror)) + .filter(Boolean) as ReplayPerformanceEntry[]; } -function createPerformanceEntry(entry: AllPerformanceEntry): ReplayPerformanceEntry | null { +function createPerformanceEntry( + entry: AllPerformanceEntry, + mirror: Mirror, +): ReplayPerformanceEntry | null { if (!ENTRY_TYPES[entry.entryType]) { return null; } - return ENTRY_TYPES[entry.entryType](entry); + return ENTRY_TYPES[entry.entryType](entry, mirror); } function getAbsoluteTime(time: number): number { @@ -48,7 +54,7 @@ function getAbsoluteTime(time: number): number { return ((browserPerformanceTimeOrigin || WINDOW.performance.timeOrigin) + time) / 1000; } -function createPaintEntry(entry: PerformancePaintTiming): ReplayPerformanceEntry { +function createPaintEntry(entry: PerformancePaintTiming, _mirror: Mirror): ReplayPerformanceEntry { const { duration, entryType, name, startTime } = entry; const start = getAbsoluteTime(startTime); @@ -61,7 +67,10 @@ function createPaintEntry(entry: PerformancePaintTiming): ReplayPerformanceEntry }; } -function createNavigationEntry(entry: PerformanceNavigationTiming): ReplayPerformanceEntry | null { +function createNavigationEntry( + entry: PerformanceNavigationTiming, + _mirror: Mirror, +): ReplayPerformanceEntry | null { const { entryType, name, @@ -108,6 +117,7 @@ function createNavigationEntry(entry: PerformanceNavigationTiming): ReplayPerfor function createResourceEntry( entry: ExperimentalPerformanceResourceTiming, + _mirror: Mirror, ): ReplayPerformanceEntry | null { const { entryType, @@ -143,10 +153,13 @@ function createResourceEntry( /** * Add a LCP event to the replay based on an LCP metric. */ -export function getLargestContentfulPaint(metric: { - value: number; - entries: PerformanceEntry[]; -}): ReplayPerformanceEntry { +export function getLargestContentfulPaint( + metric: { + value: number; + entries: PerformanceEntry[]; + }, + mirror: Mirror, +): ReplayPerformanceEntry { const entries = metric.entries; const lastEntry = entries[entries.length - 1] as (PerformanceEntry & { element?: Element }) | undefined; const element = lastEntry ? lastEntry.element : undefined; @@ -163,7 +176,7 @@ export function getLargestContentfulPaint(metric: { data: { value, size: value, - nodeId: element ? record.mirror.getId(element) : undefined, + nodeId: element ? mirror.getId(element) : undefined, }, }; diff --git a/packages/replay-internal/src/util/handleRecordingEmit.ts b/packages/replay-internal/src/util/handleRecordingEmit.ts index eaec29be261a..cd7774fdea9e 100644 --- a/packages/replay-internal/src/util/handleRecordingEmit.ts +++ b/packages/replay-internal/src/util/handleRecordingEmit.ts @@ -32,7 +32,7 @@ export function getHandleRecordingEmit(replay: ReplayContainer): RecordingEmitCa hadFirstEvent = true; if (replay.clickDetector) { - updateClickDetectorForRecordingEvent(replay.clickDetector, event); + updateClickDetectorForRecordingEvent(replay.clickDetector, event, replay.getDomMirror()); } // The handler returns `true` if we do not want to trigger debounced flush, `false` if we want to debounce flush. From 230221199290c2e34600d03b32e32cdd0d810d62 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Fri, 19 Apr 2024 16:11:47 -0400 Subject: [PATCH 2/2] update tests --- .../beforeAddRecordingEvent.test.ts | 56 ++++++++++--------- .../test/integration/flush.test.ts | 56 ++++++++++--------- .../test/unit/coreHandlers/handleDom.test.ts | 13 +++-- .../coreHandlers/handleKeyboardEvent.test.ts | 17 +++--- .../unit/util/createPerformanceEntry.test.ts | 7 ++- 5 files changed, 83 insertions(+), 66 deletions(-) diff --git a/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts b/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts index a8331149838b..ce2605f47e41 100644 --- a/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts +++ b/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts @@ -1,4 +1,5 @@ import * as SentryBrowserUtils from '@sentry-internal/browser-utils'; +import { createMirror } from '@sentry-internal/rrweb-snapshot'; import * as SentryCore from '@sentry/core'; import type { Transport } from '@sentry/types'; @@ -146,32 +147,35 @@ describe('Integration | beforeAddRecordingEvent', () => { it('handles error in callback', async () => { createPerformanceSpans( replay, - createPerformanceEntries([ - { - name: 'https://sentry.io/foo.js', - entryType: 'resource', - startTime: 176.59999990463257, - duration: 5.600000023841858, - initiatorType: 'link', - nextHopProtocol: 'h2', - workerStart: 177.5, - redirectStart: 0, - redirectEnd: 0, - fetchStart: 177.69999992847443, - domainLookupStart: 177.69999992847443, - domainLookupEnd: 177.69999992847443, - connectStart: 177.69999992847443, - connectEnd: 177.69999992847443, - secureConnectionStart: 177.69999992847443, - requestStart: 177.5, - responseStart: 181, - responseEnd: 182.19999992847443, - transferSize: 0, - encodedBodySize: 0, - decodedBodySize: 0, - serverTiming: [], - } as unknown as PerformanceResourceTiming, - ]), + createPerformanceEntries( + [ + { + name: 'https://sentry.io/foo.js', + entryType: 'resource', + startTime: 176.59999990463257, + duration: 5.600000023841858, + initiatorType: 'link', + nextHopProtocol: 'h2', + workerStart: 177.5, + redirectStart: 0, + redirectEnd: 0, + fetchStart: 177.69999992847443, + domainLookupStart: 177.69999992847443, + domainLookupEnd: 177.69999992847443, + connectStart: 177.69999992847443, + connectEnd: 177.69999992847443, + secureConnectionStart: 177.69999992847443, + requestStart: 177.5, + responseStart: 181, + responseEnd: 182.19999992847443, + transferSize: 0, + encodedBodySize: 0, + decodedBodySize: 0, + serverTiming: [], + } as unknown as PerformanceResourceTiming, + ], + createMirror(), + ), ); jest.runAllTimers(); diff --git a/packages/replay-internal/test/integration/flush.test.ts b/packages/replay-internal/test/integration/flush.test.ts index ee4cf456fbf9..779e8825dfed 100644 --- a/packages/replay-internal/test/integration/flush.test.ts +++ b/packages/replay-internal/test/integration/flush.test.ts @@ -1,4 +1,5 @@ import * as SentryBrowserUtils from '@sentry-internal/browser-utils'; +import { createMirror } from '@sentry-internal/rrweb-snapshot'; import * as SentryUtils from '@sentry/utils'; import { DEFAULT_FLUSH_MIN_DELAY, MAX_REPLAY_DURATION, WINDOW } from '../../src/constants'; @@ -205,32 +206,35 @@ describe('Integration | flush', () => { Promise.all( createPerformanceSpans( replay, - createPerformanceEntries([ - { - name: 'https://sentry.io/foo.js', - entryType: 'resource', - startTime: 176.59999990463257, - duration: 5.600000023841858, - initiatorType: 'link', - nextHopProtocol: 'h2', - workerStart: 177.5, - redirectStart: 0, - redirectEnd: 0, - fetchStart: 177.69999992847443, - domainLookupStart: 177.69999992847443, - domainLookupEnd: 177.69999992847443, - connectStart: 177.69999992847443, - connectEnd: 177.69999992847443, - secureConnectionStart: 177.69999992847443, - requestStart: 177.5, - responseStart: 181, - responseEnd: 182.19999992847443, - transferSize: 0, - encodedBodySize: 0, - decodedBodySize: 0, - serverTiming: [], - } as unknown as PerformanceResourceTiming, - ]), + createPerformanceEntries( + [ + { + name: 'https://sentry.io/foo.js', + entryType: 'resource', + startTime: 176.59999990463257, + duration: 5.600000023841858, + initiatorType: 'link', + nextHopProtocol: 'h2', + workerStart: 177.5, + redirectStart: 0, + redirectEnd: 0, + fetchStart: 177.69999992847443, + domainLookupStart: 177.69999992847443, + domainLookupEnd: 177.69999992847443, + connectStart: 177.69999992847443, + connectEnd: 177.69999992847443, + secureConnectionStart: 177.69999992847443, + requestStart: 177.5, + responseStart: 181, + responseEnd: 182.19999992847443, + transferSize: 0, + encodedBodySize: 0, + decodedBodySize: 0, + serverTiming: [], + } as unknown as PerformanceResourceTiming, + ], + createMirror(), + ), ), ), ); diff --git a/packages/replay-internal/test/unit/coreHandlers/handleDom.test.ts b/packages/replay-internal/test/unit/coreHandlers/handleDom.test.ts index 29f39a6c0a42..ce891cc440bf 100644 --- a/packages/replay-internal/test/unit/coreHandlers/handleDom.test.ts +++ b/packages/replay-internal/test/unit/coreHandlers/handleDom.test.ts @@ -1,8 +1,11 @@ +import { createMirror } from '@sentry-internal/rrweb-snapshot'; import type { HandlerDataDom } from '@sentry/types'; import { handleDom } from '../../../src/coreHandlers/handleDom'; describe('Unit | coreHandlers | handleDom', () => { + const mirror = createMirror(); + test('it works with a basic click event on a div', () => { const parent = document.createElement('body'); const target = document.createElement('div'); @@ -15,7 +18,7 @@ describe('Unit | coreHandlers | handleDom', () => { target, }, }; - const actual = handleDom(handlerData); + const actual = handleDom(handlerData, mirror); expect(actual).toEqual({ category: 'ui.click', data: {}, @@ -37,7 +40,7 @@ describe('Unit | coreHandlers | handleDom', () => { target, }, }; - const actual = handleDom(handlerData); + const actual = handleDom(handlerData, mirror); expect(actual).toEqual({ category: 'ui.click', data: {}, @@ -62,7 +65,7 @@ describe('Unit | coreHandlers | handleDom', () => { target, }, }; - const actual = handleDom(handlerData); + const actual = handleDom(handlerData, mirror); expect(actual).toEqual({ category: 'ui.click', data: {}, @@ -87,7 +90,7 @@ describe('Unit | coreHandlers | handleDom', () => { target, }, }; - const actual = handleDom(handlerData); + const actual = handleDom(handlerData, mirror); expect(actual).toEqual({ category: 'ui.click', data: {}, @@ -118,7 +121,7 @@ describe('Unit | coreHandlers | handleDom', () => { target, }, }; - const actual = handleDom(handlerData); + const actual = handleDom(handlerData, mirror); expect(actual).toEqual({ category: 'ui.click', data: {}, diff --git a/packages/replay-internal/test/unit/coreHandlers/handleKeyboardEvent.test.ts b/packages/replay-internal/test/unit/coreHandlers/handleKeyboardEvent.test.ts index d08f1ef1a800..74a0a198c43d 100644 --- a/packages/replay-internal/test/unit/coreHandlers/handleKeyboardEvent.test.ts +++ b/packages/replay-internal/test/unit/coreHandlers/handleKeyboardEvent.test.ts @@ -1,16 +1,19 @@ +import { createMirror } from '@sentry-internal/rrweb-snapshot'; import { getKeyboardBreadcrumb } from '../../../src/coreHandlers/handleKeyboardEvent'; describe('Unit | coreHandlers | handleKeyboardEvent', () => { + const mirror = createMirror(); + describe('getKeyboardBreadcrumb', () => { it('returns null for event on input', function () { const event = makeKeyboardEvent({ tagName: 'input', key: 'Escape' }); - const actual = getKeyboardBreadcrumb(event); + const actual = getKeyboardBreadcrumb(event, mirror); expect(actual).toBeNull(); }); it('returns null for event on textarea', function () { const event = makeKeyboardEvent({ tagName: 'textarea', key: 'Escape' }); - const actual = getKeyboardBreadcrumb(event); + const actual = getKeyboardBreadcrumb(event, mirror); expect(actual).toBeNull(); }); @@ -24,13 +27,13 @@ describe('Unit | coreHandlers | handleKeyboardEvent', () => { }); const event = makeKeyboardEvent({ target, key: 'Escape' }); - const actual = getKeyboardBreadcrumb(event); + const actual = getKeyboardBreadcrumb(event, mirror); expect(actual).toBeNull(); }); it('returns breadcrumb for Escape event on body', function () { const event = makeKeyboardEvent({ tagName: 'body', key: 'Escape' }); - const actual = getKeyboardBreadcrumb(event); + const actual = getKeyboardBreadcrumb(event, mirror); expect(actual).toEqual({ category: 'ui.keyDown', data: { @@ -48,19 +51,19 @@ describe('Unit | coreHandlers | handleKeyboardEvent', () => { it.each(['a', '1', '!', '~', ']'])('returns null for %s key on body', key => { const event = makeKeyboardEvent({ tagName: 'body', key }); - const actual = getKeyboardBreadcrumb(event); + const actual = getKeyboardBreadcrumb(event, mirror); expect(actual).toEqual(null); }); it.each(['a', '1', '!', '~', ']'])('returns null for %s key + Shift on body', key => { const event = makeKeyboardEvent({ tagName: 'body', key, shiftKey: true }); - const actual = getKeyboardBreadcrumb(event); + const actual = getKeyboardBreadcrumb(event, mirror); expect(actual).toEqual(null); }); it.each(['a', '1', '!', '~', ']'])('returns breadcrumb for %s key + Ctrl on body', key => { const event = makeKeyboardEvent({ tagName: 'body', key, ctrlKey: true }); - const actual = getKeyboardBreadcrumb(event); + const actual = getKeyboardBreadcrumb(event, mirror); expect(actual).toEqual({ category: 'ui.keyDown', data: { diff --git a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts index 636eb3aded9d..a05513a7ecf2 100644 --- a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts +++ b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts @@ -5,11 +5,14 @@ jest.mock('@sentry/utils', () => ({ browserPerformanceTimeOrigin: Date.now(), })); +import { createMirror } from '@sentry-internal/rrweb-snapshot'; import { WINDOW } from '../../../src/constants'; import { createPerformanceEntries, getLargestContentfulPaint } from '../../../src/util/createPerformanceEntries'; import { PerformanceEntryNavigation } from '../../fixtures/performanceEntry/navigation'; describe('Unit | util | createPerformanceEntries', () => { + const mirror = createMirror(); + beforeEach(function () { if (!WINDOW.performance.getEntriesByType) { WINDOW.performance.getEntriesByType = jest.fn((type: string) => { @@ -54,7 +57,7 @@ describe('Unit | util | createPerformanceEntries', () => { } as const; // @ts-expect-error Needs a PerformanceEntry mock - expect(createPerformanceEntries([data])).toEqual([]); + expect(createPerformanceEntries([data], mirror)).toEqual([]); }); describe('getLargestContentfulPaint', () => { @@ -64,7 +67,7 @@ describe('Unit | util | createPerformanceEntries', () => { entries: [], }; - const event = getLargestContentfulPaint(metric); + const event = getLargestContentfulPaint(metric, mirror); expect(event).toEqual({ type: 'largest-contentful-paint',