diff --git a/packages/replay/.eslintrc.js b/packages/replay/.eslintrc.js index c30426ad113d..1bc8b64b1464 100644 --- a/packages/replay/.eslintrc.js +++ b/packages/replay/.eslintrc.js @@ -39,8 +39,6 @@ module.exports = { rules: { // TODO (high-prio): Go through console logs and figure out which ones should be replaced with the SDK logger 'no-console': 'off', - // TODO (high-pio): Re-enable this after migration - 'no-restricted-globals': 'off', // TODO (high-prio): Re-enable this after migration '@typescript-eslint/explicit-member-accessibility': 'off', // TODO (high-prio): Remove this exception from naming convention after migration diff --git a/packages/replay/config/rollup.config.core.ts b/packages/replay/config/rollup.config.core.ts index a8f7fc1e7d09..8a652b16f739 100644 --- a/packages/replay/config/rollup.config.core.ts +++ b/packages/replay/config/rollup.config.core.ts @@ -19,7 +19,7 @@ const config = defineConfig({ format: 'esm', }, ], - external: [...Object.keys(pkg.dependencies || {})], + external: [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})], plugins: [ typescript({ tsconfig: IS_PRODUCTION ? './config/tsconfig.core.json' : './tsconfig.json', diff --git a/packages/replay/package.json b/packages/replay/package.json index 73934a34d3a7..2f8b3dee5189 100644 --- a/packages/replay/package.json +++ b/packages/replay/package.json @@ -60,6 +60,9 @@ "lodash.debounce": "^4.0.8", "rrweb": "^1.1.3" }, + "peerDependencies": { + "@sentry/browser": "7.22.0" + }, "engines": { "node": ">=12" }, diff --git a/packages/replay/src/createPerformanceEntry.ts b/packages/replay/src/createPerformanceEntry.ts index 090bdf51e2e0..ed92889ad47c 100644 --- a/packages/replay/src/createPerformanceEntry.ts +++ b/packages/replay/src/createPerformanceEntry.ts @@ -1,3 +1,4 @@ +import { WINDOW } from '@sentry/browser'; import { browserPerformanceTimeOrigin } from '@sentry/utils'; import { record } from 'rrweb'; @@ -60,7 +61,7 @@ function createPerformanceEntry(entry: AllPerformanceEntry): ReplayPerformanceEn function getAbsoluteTime(time: number): number { // browserPerformanceTimeOrigin can be undefined if `performance` or // `performance.now` doesn't exist, but this is already checked by this integration - return ((browserPerformanceTimeOrigin || window.performance.timeOrigin) + time) / 1000; + return ((browserPerformanceTimeOrigin || WINDOW.performance.timeOrigin) + time) / 1000; } // TODO: type definition! diff --git a/packages/replay/src/eventBuffer.ts b/packages/replay/src/eventBuffer.ts index 988c7de0e726..90550ed33021 100644 --- a/packages/replay/src/eventBuffer.ts +++ b/packages/replay/src/eventBuffer.ts @@ -12,6 +12,7 @@ interface CreateEventBufferParams { } export function createEventBuffer({ useCompression }: CreateEventBufferParams): IEventBuffer { + // eslint-disable-next-line no-restricted-globals if (useCompression && window.Worker) { const workerBlob = new Blob([workerString]); const workerUrl = URL.createObjectURL(workerBlob); diff --git a/packages/replay/src/index.ts b/packages/replay/src/index.ts index 7e72b7eb7294..a0327fd0aa1f 100644 --- a/packages/replay/src/index.ts +++ b/packages/replay/src/index.ts @@ -1,4 +1,5 @@ /* eslint-disable max-lines */ // TODO: We might want to split this file up +import { WINDOW } from '@sentry/browser'; import { addGlobalEventProcessor, getCurrentHub, Scope, setContext } from '@sentry/core'; import { Breadcrumb, Client, Event, Integration } from '@sentry/types'; import { addInstrumentationHandler, createEnvelope, logger } from '@sentry/utils'; @@ -232,7 +233,7 @@ export class Replay implements Integration { return; } // XXX: See method comments above - window.setTimeout(() => this.start()); + setTimeout(() => this.start()); } /** @@ -396,8 +397,8 @@ export class Replay implements Integration { * first flush. */ setInitialState(): void { - const urlPath = `${window.location.pathname}${window.location.hash}${window.location.search}`; - const url = `${window.location.origin}${urlPath}`; + const urlPath = `${WINDOW.location.pathname}${WINDOW.location.hash}${WINDOW.location.search}`; + const url = `${WINDOW.location.origin}${urlPath}`; this.performanceEvents = []; @@ -414,9 +415,9 @@ export class Replay implements Integration { */ addListeners(): void { try { - document.addEventListener('visibilitychange', this.handleVisibilityChange); - window.addEventListener('blur', this.handleWindowBlur); - window.addEventListener('focus', this.handleWindowFocus); + WINDOW.document.addEventListener('visibilitychange', this.handleVisibilityChange); + WINDOW.addEventListener('blur', this.handleWindowBlur); + WINDOW.addEventListener('focus', this.handleWindowFocus); // There is no way to remove these listeners, so ensure they are only added once if (!this.hasInitializedCoreListeners) { @@ -440,7 +441,7 @@ export class Replay implements Integration { } // PerformanceObserver // - if (!('PerformanceObserver' in window)) { + if (!('PerformanceObserver' in WINDOW)) { return; } @@ -475,10 +476,10 @@ export class Replay implements Integration { */ removeListeners(): void { try { - document.removeEventListener('visibilitychange', this.handleVisibilityChange); + WINDOW.document.removeEventListener('visibilitychange', this.handleVisibilityChange); - window.removeEventListener('blur', this.handleWindowBlur); - window.removeEventListener('focus', this.handleWindowFocus); + WINDOW.removeEventListener('blur', this.handleWindowBlur); + WINDOW.removeEventListener('focus', this.handleWindowFocus); if (this.performanceObserver) { this.performanceObserver.disconnect(); @@ -665,7 +666,7 @@ export class Replay implements Integration { * page will also trigger a change to a hidden state. */ handleVisibilityChange: () => void = () => { - if (document.visibilityState === 'visible') { + if (WINDOW.document.visibilityState === 'visible') { this.doChangeToForegroundTasks(); } else { this.doChangeToBackgroundTasks(); @@ -980,13 +981,13 @@ export class Replay implements Integration { addMemoryEntry(): Promise | undefined { // window.performance.memory is a non-standard API and doesn't work on all browsers // so we check before creating the event. - if (!('memory' in window.performance)) { + if (!('memory' in WINDOW.performance)) { return; } return this.createPerformanceSpans([ // @ts-ignore memory doesn't exist on type Performance as the API is non-standard (we check that it exists above) - createMemoryEntry(window.performance.memory), + createMemoryEntry(WINDOW.performance.memory), ]); } diff --git a/packages/replay/src/session/deleteSession.ts b/packages/replay/src/session/deleteSession.ts index 56841f9d48d1..0476ffe10b54 100644 --- a/packages/replay/src/session/deleteSession.ts +++ b/packages/replay/src/session/deleteSession.ts @@ -1,17 +1,19 @@ +import { WINDOW } from '@sentry/browser'; + import { REPLAY_SESSION_KEY } from './constants'; /** * Deletes a session from storage */ export function deleteSession(): void { - const hasSessionStorage = 'sessionStorage' in window; + const hasSessionStorage = 'sessionStorage' in WINDOW; if (!hasSessionStorage) { return; } try { - window.sessionStorage.removeItem(REPLAY_SESSION_KEY); + WINDOW.sessionStorage.removeItem(REPLAY_SESSION_KEY); } catch { // Ignore potential SecurityError exceptions } diff --git a/packages/replay/src/session/fetchSession.ts b/packages/replay/src/session/fetchSession.ts index b7e14b309ee5..201218a0f38d 100644 --- a/packages/replay/src/session/fetchSession.ts +++ b/packages/replay/src/session/fetchSession.ts @@ -1,3 +1,5 @@ +import { WINDOW } from '@sentry/browser'; + import { SampleRates } from '../types'; import { REPLAY_SESSION_KEY } from './constants'; import { Session } from './Session'; @@ -6,7 +8,7 @@ import { Session } from './Session'; * Fetches a session from storage */ export function fetchSession({ sessionSampleRate, errorSampleRate }: SampleRates): Session | null { - const hasSessionStorage = 'sessionStorage' in window; + const hasSessionStorage = 'sessionStorage' in WINDOW; if (!hasSessionStorage) { return null; @@ -14,7 +16,7 @@ export function fetchSession({ sessionSampleRate, errorSampleRate }: SampleRates try { // This can throw if cookies are disabled - const sessionStringFromStorage = window.sessionStorage.getItem(REPLAY_SESSION_KEY); + const sessionStringFromStorage = WINDOW.sessionStorage.getItem(REPLAY_SESSION_KEY); if (!sessionStringFromStorage) { return null; diff --git a/packages/replay/src/session/saveSession.ts b/packages/replay/src/session/saveSession.ts index 3728ae4f7a94..518ba2d8bdd6 100644 --- a/packages/replay/src/session/saveSession.ts +++ b/packages/replay/src/session/saveSession.ts @@ -1,14 +1,16 @@ +import { WINDOW } from '@sentry/browser'; + import { REPLAY_SESSION_KEY } from './constants'; import { Session } from './Session'; export function saveSession(session: Session): void { - const hasSessionStorage = 'sessionStorage' in window; + const hasSessionStorage = 'sessionStorage' in WINDOW; if (!hasSessionStorage) { return; } try { - window.sessionStorage.setItem(REPLAY_SESSION_KEY, JSON.stringify(session)); + WINDOW.sessionStorage.setItem(REPLAY_SESSION_KEY, JSON.stringify(session)); } catch { // Ignore potential SecurityError exceptions } diff --git a/packages/replay/src/util/isBrowser.ts b/packages/replay/src/util/isBrowser.ts index 5093781bc4dd..fb87bc3c7e55 100644 --- a/packages/replay/src/util/isBrowser.ts +++ b/packages/replay/src/util/isBrowser.ts @@ -1,5 +1,6 @@ import { isNodeEnv } from '@sentry/utils'; export function isBrowser(): boolean { + // eslint-disable-next-line no-restricted-globals return typeof window !== 'undefined' && !isNodeEnv(); } diff --git a/packages/replay/src/util/isInternal.ts b/packages/replay/src/util/isInternal.ts index e6e920e4f058..3ea5decae3e5 100644 --- a/packages/replay/src/util/isInternal.ts +++ b/packages/replay/src/util/isInternal.ts @@ -1,3 +1,5 @@ +import { WINDOW } from '@sentry/browser'; + import { isBrowser } from './isBrowser'; /** @@ -5,5 +7,5 @@ import { isBrowser } from './isBrowser'; * (e.g. on https://sentry.io ) */ export function isInternal(): boolean { - return isBrowser() && ['sentry.io', 'dev.getsentry.net'].includes(window.location.host); + return isBrowser() && ['sentry.io', 'dev.getsentry.net'].includes(WINDOW.location.host); } diff --git a/packages/replay/test/unit/flush.test.ts b/packages/replay/test/unit/flush.test.ts index 57135ffa7994..c6b0de4e744d 100644 --- a/packages/replay/test/unit/flush.test.ts +++ b/packages/replay/test/unit/flush.test.ts @@ -1,3 +1,4 @@ +import { WINDOW } from '@sentry/browser'; import * as SentryUtils from '@sentry/utils'; import { BASE_TIMESTAMP, mockRrweb, mockSdk } from '@test'; @@ -20,7 +21,7 @@ type MockEventBufferFinish = jest.MockedFunction; type MockRunFlush = jest.MockedFunction; -const prevLocation = window.location; +const prevLocation = WINDOW.location; let domHandler: (args: any) => any; const { record: mockRecord } = mockRrweb(); @@ -91,9 +92,7 @@ afterEach(async () => { replay.clearSession(); replay.loadSession({ expiry: SESSION_IDLE_DURATION }); mockRecord.takeFullSnapshot.mockClear(); - // @ts-ignore: The operand of a 'delete' operator must be optional.ts(2790) - delete window.location; - Object.defineProperty(window, 'location', { + Object.defineProperty(WINDOW, 'location', { value: prevLocation, writable: true, }); @@ -109,10 +108,10 @@ it('flushes twice after multiple flush() calls)', async () => { // the following blur events will all call a debounced flush function, which // should end up queueing a second flush - window.dispatchEvent(new Event('blur')); - window.dispatchEvent(new Event('blur')); - window.dispatchEvent(new Event('blur')); - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); expect(replay.flush).toHaveBeenCalledTimes(4); @@ -138,7 +137,7 @@ it('long first flush enqueues following events', async () => { expect(mockAddPerformanceEntries).not.toHaveBeenCalled(); // flush #1 @ t=0s - due to blur - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); expect(replay.flush).toHaveBeenCalledTimes(1); expect(replay.runFlush).toHaveBeenCalledTimes(1); @@ -152,7 +151,7 @@ it('long first flush enqueues following events', async () => { await advanceTimers(1000); // flush #3 @ t=6s - due to blur - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); expect(replay.flush).toHaveBeenCalledTimes(3); // NOTE: Blur also adds a breadcrumb which calls `addUpdate`, meaning it will @@ -161,7 +160,7 @@ it('long first flush enqueues following events', async () => { expect(replay.flush).toHaveBeenCalledTimes(3); // flush #4 @ t=14s - due to blur - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); expect(replay.flush).toHaveBeenCalledTimes(4); expect(replay.runFlush).toHaveBeenCalledTimes(1); diff --git a/packages/replay/test/unit/index-errorSampleRate.test.ts b/packages/replay/test/unit/index-errorSampleRate.test.ts index 9367e0d7f782..0149ee73b022 100644 --- a/packages/replay/test/unit/index-errorSampleRate.test.ts +++ b/packages/replay/test/unit/index-errorSampleRate.test.ts @@ -1,6 +1,6 @@ jest.unmock('@sentry/browser'); -import { captureException } from '@sentry/browser'; +import { captureException, WINDOW } from '@sentry/browser'; import { BASE_TIMESTAMP, RecordMock } from '@test'; import { PerformanceEntryResource } from '@test/fixtures/performanceEntry/resource'; import { resetSdkMock } from '@test/mocks'; @@ -317,7 +317,7 @@ describe('Replay (errorSampleRate)', () => { */ it('sends a replay after loading the session multiple times', async () => { // Pretend that a session is already saved before loading replay - window.sessionStorage.setItem( + WINDOW.sessionStorage.setItem( REPLAY_SESSION_KEY, `{"segmentId":0,"id":"fd09adfc4117477abc8de643e5a5798a","sampled":"error","started":${BASE_TIMESTAMP},"lastActivity":${BASE_TIMESTAMP}}`, ); diff --git a/packages/replay/test/unit/index.test.ts b/packages/replay/test/unit/index.test.ts index ec15575b7df5..dcec60a95b63 100644 --- a/packages/replay/test/unit/index.test.ts +++ b/packages/replay/test/unit/index.test.ts @@ -1,6 +1,7 @@ jest.mock('./../../src/util/isInternal', () => ({ isInternal: jest.fn(() => true), })); +import { WINDOW } from '@sentry/browser'; import { BASE_TIMESTAMP, RecordMock } from '@test'; import { PerformanceEntryResource } from '@test/fixtures/performanceEntry/resource'; import { resetSdkMock } from '@test/mocks'; @@ -23,7 +24,7 @@ describe('Replay', () => { let mockTransportSend: MockTransportSend; let domHandler: DomHandler; let spyCaptureException: jest.MockedFunction; - const prevLocation = window.location; + const prevLocation = WINDOW.location; type MockSendReplayRequest = jest.MockedFunction; let mockSendReplayRequest: MockSendReplayRequest; @@ -56,9 +57,7 @@ describe('Replay', () => { afterEach(async () => { jest.runAllTimers(); await new Promise(process.nextTick); - // @ts-ignore: The operand of a 'delete' operator must be optional.ts(2790) - delete window.location; - Object.defineProperty(window, 'location', { + Object.defineProperty(WINDOW, 'location', { value: prevLocation, writable: true, }); @@ -97,7 +96,7 @@ describe('Replay', () => { it('clears session', () => { replay.clearSession(); - expect(window.sessionStorage.getItem(REPLAY_SESSION_KEY)).toBe(null); + expect(WINDOW.sessionStorage.getItem(REPLAY_SESSION_KEY)).toBe(null); expect(replay.session).toBe(undefined); }); @@ -133,7 +132,7 @@ describe('Replay', () => { jest.advanceTimersByTime(VISIBILITY_CHANGE_TIMEOUT + 1); - window.dispatchEvent(new Event('focus')); + WINDOW.dispatchEvent(new Event('focus')); expect(mockRecord.takeFullSnapshot).toHaveBeenLastCalledWith(true); @@ -169,7 +168,7 @@ describe('Replay', () => { expect(replay).toHaveSameSession(initialSession); }); - it('uploads a replay event when window is blurred', async () => { + it('uploads a replay event when WINDOW is blurred', async () => { Object.defineProperty(document, 'visibilityState', { configurable: true, get: function () { @@ -196,7 +195,7 @@ describe('Replay', () => { }; replay.addEvent(TEST_EVENT); - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); await new Promise(process.nextTick); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); expect(replay).toHaveSentReplay({ @@ -303,7 +302,7 @@ describe('Replay', () => { ); const url = 'http://dummy/'; - Object.defineProperty(window, 'location', { + Object.defineProperty(WINDOW, 'location', { value: new URL(url), }); @@ -391,7 +390,7 @@ describe('Replay', () => { ); const url = 'http://dummy/'; - Object.defineProperty(window, 'location', { + Object.defineProperty(WINDOW, 'location', { value: new URL(url), }); @@ -419,7 +418,7 @@ describe('Replay', () => { return true; }); - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); await advanceTimers(5000); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); @@ -639,7 +638,7 @@ describe('Replay', () => { const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 2 }; replay.addEvent(TEST_EVENT); - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); await new Promise(process.nextTick); expect(replay).toHaveSentReplay({ recordingPayloadHeader: { segment_id: 0 }, @@ -647,7 +646,7 @@ describe('Replay', () => { expect(replay.session?.segmentId).toBe(1); replay.addEvent(TEST_EVENT); - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); jest.runAllTimers(); await new Promise(process.nextTick); expect(replay.session?.segmentId).toBe(2); @@ -679,7 +678,7 @@ describe('Replay', () => { }; replay.addEvent(TEST_EVENT); - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); await new Promise(process.nextTick); expect(replay).toHaveSentReplay({ @@ -773,7 +772,7 @@ describe('Replay', () => { type: 5, }); - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); await new Promise(process.nextTick); expect(replay).toHaveSentReplay({ replayEventPayload: expect.objectContaining({ @@ -816,7 +815,7 @@ describe('Replay', () => { replay.addEvent(TEST_EVENT); // This event will trigger a flush - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); jest.runAllTimers(); await new Promise(process.nextTick); diff --git a/packages/replay/test/unit/session/Session.test.ts b/packages/replay/test/unit/session/Session.test.ts index 046d0d87ba8c..27e4a67d92e3 100644 --- a/packages/replay/test/unit/session/Session.test.ts +++ b/packages/replay/test/unit/session/Session.test.ts @@ -1,7 +1,10 @@ jest.mock('./../../../src/session/saveSession'); jest.mock('@sentry/browser', () => { + const originalModule = jest.requireActual('@sentry/browser'); + return { + ...originalModule, getCurrentHub: jest.fn(() => { return { captureEvent: jest.fn(), @@ -11,24 +14,25 @@ jest.mock('@sentry/browser', () => { }; }); +jest.mock('@sentry/utils', () => { + const originalModule = jest.requireActual('@sentry/utils'); + + return { + ...originalModule, + uuid4: jest.fn(() => 'test_session_id'), + }; +}); + import * as Sentry from '@sentry/browser'; +import { WINDOW } from '@sentry/browser'; import { saveSession } from '../../../src/session/saveSession'; import { Session } from '../../../src/session/Session'; type CaptureEventMockType = jest.MockedFunction; -jest.mock('@sentry/browser'); - -jest.mock('@sentry/utils', () => { - return { - ...(jest.requireActual('@sentry/utils') as { string: unknown }), - uuid4: jest.fn(() => 'test_session_id'), - }; -}); - beforeEach(() => { - window.sessionStorage.clear(); + WINDOW.sessionStorage.clear(); }); afterEach(() => { diff --git a/packages/replay/test/unit/session/createSession.test.ts b/packages/replay/test/unit/session/createSession.test.ts index ed281f5bc75a..c30e0b097dfd 100644 --- a/packages/replay/test/unit/session/createSession.test.ts +++ b/packages/replay/test/unit/session/createSession.test.ts @@ -1,3 +1,4 @@ +import { WINDOW } from '@sentry/browser'; import * as Sentry from '@sentry/core'; import { createSession } from '../../../src/session/createSession'; @@ -17,7 +18,7 @@ type CaptureEventMockType = jest.MockedFunction; const captureEventMock: CaptureEventMockType = jest.fn(); beforeAll(() => { - window.sessionStorage.clear(); + WINDOW.sessionStorage.clear(); jest.spyOn(Sentry, 'getCurrentHub'); (Sentry.getCurrentHub as jest.Mock).mockImplementation(() => ({ captureEvent: captureEventMock, diff --git a/packages/replay/test/unit/session/deleteSession.test.ts b/packages/replay/test/unit/session/deleteSession.test.ts index 61aed26de53e..750f542ba3e8 100644 --- a/packages/replay/test/unit/session/deleteSession.test.ts +++ b/packages/replay/test/unit/session/deleteSession.test.ts @@ -1,7 +1,9 @@ +import { WINDOW } from '@sentry/browser'; + import { REPLAY_SESSION_KEY } from '../../../src/session/constants'; import { deleteSession } from '../../../src/session/deleteSession'; -const storageEngine = window.sessionStorage; +const storageEngine = WINDOW.sessionStorage; it('deletes a session', function () { storageEngine.setItem( diff --git a/packages/replay/test/unit/session/fetchSession.test.ts b/packages/replay/test/unit/session/fetchSession.test.ts index 54889ae114d4..171605ce5da8 100644 --- a/packages/replay/test/unit/session/fetchSession.test.ts +++ b/packages/replay/test/unit/session/fetchSession.test.ts @@ -1,18 +1,20 @@ +import { WINDOW } from '@sentry/browser'; + import { REPLAY_SESSION_KEY } from '../../../src/session/constants'; import { fetchSession } from '../../../src/session/fetchSession'; -const oldSessionStorage = window.sessionStorage; +const oldSessionStorage = WINDOW.sessionStorage; beforeAll(() => { - window.sessionStorage.clear(); + WINDOW.sessionStorage.clear(); }); afterEach(() => { - Object.defineProperty(window, 'sessionStorage', { + Object.defineProperty(WINDOW, 'sessionStorage', { writable: true, value: oldSessionStorage, }); - window.sessionStorage.clear(); + WINDOW.sessionStorage.clear(); }); const SAMPLE_RATES = { @@ -21,7 +23,7 @@ const SAMPLE_RATES = { }; it('fetches a valid and sampled session', function () { - window.sessionStorage.setItem( + WINDOW.sessionStorage.setItem( REPLAY_SESSION_KEY, '{"id":"fd09adfc4117477abc8de643e5a5798a","sampled": true,"started":1648827162630,"lastActivity":1648827162658}', ); @@ -40,13 +42,13 @@ it('fetches a session that does not exist', function () { }); it('fetches an invalid session', function () { - window.sessionStorage.setItem(REPLAY_SESSION_KEY, '{"id":"fd09adfc4117477abc8de643e5a5798a",'); + WINDOW.sessionStorage.setItem(REPLAY_SESSION_KEY, '{"id":"fd09adfc4117477abc8de643e5a5798a",'); expect(fetchSession(SAMPLE_RATES)).toBe(null); }); it('safely attempts to fetch session when Session Storage is disabled', function () { - Object.defineProperty(window, 'sessionStorage', { + Object.defineProperty(WINDOW, 'sessionStorage', { writable: true, value: { getItem: () => { diff --git a/packages/replay/test/unit/session/getSession.test.ts b/packages/replay/test/unit/session/getSession.test.ts index 7f39184c3719..752c19030c0f 100644 --- a/packages/replay/test/unit/session/getSession.test.ts +++ b/packages/replay/test/unit/session/getSession.test.ts @@ -1,3 +1,5 @@ +import { WINDOW } from '@sentry/browser'; + import * as CreateSession from '../../../src/session/createSession'; import * as FetchSession from '../../../src/session/fetchSession'; import { getSession } from '../../../src/session/getSession'; @@ -32,11 +34,11 @@ function createMockSession(when: number = new Date().getTime()) { beforeAll(() => { jest.spyOn(CreateSession, 'createSession'); jest.spyOn(FetchSession, 'fetchSession'); - window.sessionStorage.clear(); + WINDOW.sessionStorage.clear(); }); afterEach(() => { - window.sessionStorage.clear(); + WINDOW.sessionStorage.clear(); (CreateSession.createSession as jest.MockedFunction).mockClear(); (FetchSession.fetchSession as jest.MockedFunction).mockClear(); }); diff --git a/packages/replay/test/unit/session/saveSession.test.ts b/packages/replay/test/unit/session/saveSession.test.ts index 65f3ed270db8..5001261a9fcd 100644 --- a/packages/replay/test/unit/session/saveSession.test.ts +++ b/packages/replay/test/unit/session/saveSession.test.ts @@ -1,13 +1,15 @@ +import { WINDOW } from '@sentry/browser'; + import { REPLAY_SESSION_KEY } from '../../../src/session/constants'; import { saveSession } from '../../../src/session/saveSession'; import { Session } from '../../../src/session/Session'; beforeAll(() => { - window.sessionStorage.clear(); + WINDOW.sessionStorage.clear(); }); afterEach(() => { - window.sessionStorage.clear(); + WINDOW.sessionStorage.clear(); }); it('saves a valid session', function () { @@ -23,5 +25,5 @@ it('saves a valid session', function () { ); saveSession(session); - expect(window.sessionStorage.getItem(REPLAY_SESSION_KEY)).toEqual(JSON.stringify(session)); + expect(WINDOW.sessionStorage.getItem(REPLAY_SESSION_KEY)).toEqual(JSON.stringify(session)); }); diff --git a/packages/replay/test/unit/stop.test.ts b/packages/replay/test/unit/stop.test.ts index ade2c159d96c..414cf9452dbb 100644 --- a/packages/replay/test/unit/stop.test.ts +++ b/packages/replay/test/unit/stop.test.ts @@ -1,3 +1,4 @@ +import { WINDOW } from '@sentry/browser'; import * as SentryUtils from '@sentry/utils'; // mock functions need to be imported first import { BASE_TIMESTAMP, mockRrweb, mockSdk } from '@test'; @@ -10,7 +11,7 @@ useFakeTimers(); describe('Replay - stop', () => { let replay: Replay; - const prevLocation = window.location; + const prevLocation = WINDOW.location; type MockAddInstrumentationHandler = jest.MockedFunction; const { record: mockRecord } = mockRrweb(); @@ -43,9 +44,7 @@ describe('Replay - stop', () => { replay.loadSession({ expiry: SESSION_IDLE_DURATION }); mockRecord.takeFullSnapshot.mockClear(); mockAddInstrumentationHandler.mockClear(); - // @ts-ignore: The operand of a 'delete' operator must be optional.ts(2790) - delete window.location; - Object.defineProperty(window, 'location', { + Object.defineProperty(WINDOW, 'location', { value: prevLocation, writable: true, }); @@ -74,7 +73,7 @@ describe('Replay - stop', () => { jest.advanceTimersByTime(ELAPSED); replay.addEvent(TEST_EVENT); - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); await new Promise(process.nextTick); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); expect(replay).not.toHaveSentReplay(); @@ -104,7 +103,7 @@ describe('Replay - stop', () => { }; replay.addEvent(TEST_EVENT); - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); jest.runAllTimers(); await new Promise(process.nextTick); expect(replay).toHaveSentReplay({ @@ -125,7 +124,7 @@ describe('Replay - stop', () => { }); it('does not buffer events when stopped', async function () { - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); expect(replay.eventBuffer?.length).toBe(1); // stop replays @@ -133,7 +132,7 @@ describe('Replay - stop', () => { expect(replay.eventBuffer?.length).toBe(undefined); - window.dispatchEvent(new Event('blur')); + WINDOW.dispatchEvent(new Event('blur')); await new Promise(process.nextTick); expect(replay.eventBuffer?.length).toBe(undefined);