From 0c2c6099bc6e2549bdf812b39db0e8522246016e Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Sun, 5 May 2024 12:05:02 -0400 Subject: [PATCH 01/11] feat(replay): Use `vitest` instead of jest [WIP] WIP, use vitest instead of jest. Some notes: * Using jsdom 24 to deal with `TextEncoder` issues * Use vi/jest `*Async` fake timer functions instead of `process.nextTick` * Our `useFakeTimers` module was setting an interval which was causing all sorts of flakes in tests with the updated fake timers library --- packages/replay-internal/package.json | 9 +- .../{jest.setup.ts => test.setup.ts} | 46 +-- .../test/integration/autoSaveSession.test.ts | 27 +- .../beforeAddRecordingEvent.test.ts | 33 +- .../coreHandlers/handleAfterSendEvent.test.ts | 55 +-- .../handleBeforeSendEvent.test.ts | 6 +- .../coreHandlers/handleGlobalEvent.test.ts | 2 +- .../test/integration/errorSampleRate.test.ts | 166 ++++----- .../test/integration/eventProcessors.test.ts | 18 +- .../test/integration/events.test.ts | 20 +- .../test/integration/flush.test.ts | 116 +++--- .../test/integration/getReplayId.test.ts | 2 +- .../integration/integrationSettings.test.ts | 17 +- .../test/integration/rateLimiting.test.ts | 12 +- .../test/integration/rrweb.test.ts | 10 +- .../test/integration/sampling.test.ts | 14 +- .../test/integration/sendReplayEvent.test.ts | 78 ++-- .../test/integration/session.test.ts | 66 ++-- .../integration/shouldFilterRequest.test.ts | 2 +- .../test/integration/stop.test.ts | 37 +- .../replay-internal/test/mocks/mockRrweb.ts | 65 ++-- .../replay-internal/test/mocks/mockSdk.ts | 5 +- .../test/mocks/resetSdkMock.ts | 16 +- .../unit/coreHandlers/handleClick.test.ts | 86 ++--- .../handleNetworkBreadcrumbs.test.ts | 6 +- .../util/addBreadcrumbEvent.test.ts | 4 +- .../coreHandlers/util/networkUtils.test.ts | 4 +- .../unit/coreHandlers/util/xhrUtils.test.ts | 8 +- .../EventBufferCompressionWorker.test.ts | 4 +- .../unit/eventBuffer/EventBufferProxy.test.ts | 4 +- .../test/unit/session/createSession.test.ts | 16 +- .../unit/session/loadOrCreateSession.test.ts | 16 +- .../test/unit/util/addEvent.test.ts | 13 +- .../unit/util/createPerformanceEntry.test.ts | 17 +- .../test/unit/util/debounce.test.ts | 96 ++--- .../test/unit/util/getPrivacyOptions.test.ts | 12 +- .../unit/util/handleRecordingEmit.test.ts | 12 +- .../test/unit/util/isSampled.test.ts | 2 +- .../test/unit/util/prepareReplayEvent.test.ts | 6 +- .../test/unit/util/throttle.test.ts | 32 +- .../test/utils/use-fake-timers.ts | 15 +- packages/replay-internal/vitest.config.ts | 16 + yarn.lock | 336 +++++++++++++++--- 43 files changed, 876 insertions(+), 651 deletions(-) rename packages/replay-internal/{jest.setup.ts => test.setup.ts} (86%) create mode 100644 packages/replay-internal/vitest.config.ts diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json index cd8ebe226254..e6ba49eb1d54 100644 --- a/packages/replay-internal/package.json +++ b/packages/replay-internal/package.json @@ -53,8 +53,8 @@ "clean": "rimraf build sentry-replay-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "jest", - "test:watch": "jest --watch", + "test": "vitest", + "test:watch": "vitest --watch", "yalc:publish": "ts-node ../../scripts/prepack.ts --bundles && yalc publish ./build/npm --push --sig" }, "repository": { @@ -73,7 +73,10 @@ "@sentry-internal/rrweb": "2.15.0", "@sentry-internal/rrweb-snapshot": "2.15.0", "fflate": "^0.8.1", - "jsdom-worker": "^0.2.1" + "jest-matcher-utils": "^29.0.0", + "jsdom": "^24.0.0", + "jsdom-worker": "^0.2.1", + "vitest": "^1.5.3" }, "dependencies": { "@sentry-internal/browser-utils": "8.0.0-beta.5", diff --git a/packages/replay-internal/jest.setup.ts b/packages/replay-internal/test.setup.ts similarity index 86% rename from packages/replay-internal/jest.setup.ts rename to packages/replay-internal/test.setup.ts index eaba8ee05179..5e0aa8ff49a5 100644 --- a/packages/replay-internal/jest.setup.ts +++ b/packages/replay-internal/test.setup.ts @@ -1,4 +1,7 @@ -import { TextEncoder } from 'util'; +import { vi } from 'vitest'; +import type { Assertion, AsymmetricMatchersContaining, Mocked, MockedFunction } from 'vitest'; +import { printDiffOrStringify } from 'jest-matcher-utils'; + /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { getClient } from '@sentry/core'; import type { ReplayRecordingData, Transport } from '@sentry/types'; @@ -6,12 +9,9 @@ import * as SentryUtils from '@sentry/utils'; import type { ReplayContainer, Session } from './src/types'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -(global as any).TextEncoder = TextEncoder; - -type MockTransport = jest.MockedFunction; +type MockTransport = MockedFunction; -jest.spyOn(SentryUtils, 'isBrowser').mockImplementation(() => true); +vi.spyOn(SentryUtils, 'isBrowser').mockImplementation(() => true); type EnvelopeHeader = { event_id: string; @@ -36,7 +36,7 @@ type SentReplayExpected = { }; // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const toHaveSameSession = function (received: jest.Mocked, expected: undefined | Session) { +const toHaveSameSession = function (received: Mocked, expected: undefined | Session) { const pass = this.equals(received.session?.id, expected?.id) as boolean; const options = { @@ -47,12 +47,12 @@ const toHaveSameSession = function (received: jest.Mocked, expe return { pass, message: () => - `${this.utils.matcherHint( - 'toHaveSameSession', - undefined, - undefined, - options, - )}\n\n${this.utils.printDiffOrStringify(expected, received.session, 'Expected', 'Received')}`, + `${this.utils.matcherHint('toHaveSameSession', undefined, undefined, options)}\n\n${printDiffOrStringify( + expected, + received.session, + 'Expected', + 'Received', + )}`, }; }; @@ -152,7 +152,7 @@ function getReplayCalls(calls: any[][][]): any[][][] { */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type const toHaveSentReplay = function ( - _received: jest.Mocked, + _received: Mocked, expected?: SentReplayExpected | { sample: SentReplayExpected; inverse: boolean }, ) { const { calls } = (getClient()?.getTransport()?.send as MockTransport).mock; @@ -194,12 +194,7 @@ const toHaveSentReplay = function ( : 'Expected Replay to have been sent, but a request was not attempted' : `${this.utils.matcherHint('toHaveSentReplay', undefined, undefined, options)}\n\n${results .map(({ key, expectedVal, actualVal }: Result) => - this.utils.printDiffOrStringify( - expectedVal, - actualVal, - `Expected (key: ${key})`, - `Received (key: ${key})`, - ), + printDiffOrStringify(expectedVal, actualVal, `Expected (key: ${key})`, `Received (key: ${key})`), ) .join('\n')}`, }; @@ -211,7 +206,7 @@ const toHaveSentReplay = function ( */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type const toHaveLastSentReplay = function ( - _received: jest.Mocked, + _received: Mocked, expected?: SentReplayExpected | { sample: SentReplayExpected; inverse: boolean }, ) { const { calls } = (getClient()?.getTransport()?.send as MockTransport).mock; @@ -235,12 +230,7 @@ const toHaveLastSentReplay = function ( : 'Expected Replay to have last been sent, but a request was not attempted' : `${this.utils.matcherHint('toHaveSentReplay', undefined, undefined, options)}\n\n${results .map(({ key, expectedVal, actualVal }: Result) => - this.utils.printDiffOrStringify( - expectedVal, - actualVal, - `Expected (key: ${key})`, - `Received (key: ${key})`, - ), + printDiffOrStringify(expectedVal, actualVal, `Expected (key: ${key})`, `Received (key: ${key})`), ) .join('\n')}`, }; @@ -254,7 +244,7 @@ expect.extend({ declare global { // eslint-disable-next-line @typescript-eslint/no-namespace - namespace jest { + namespace vi { interface AsymmetricMatchers { toHaveSentReplay(expected?: SentReplayExpected): void; toHaveLastSentReplay(expected?: SentReplayExpected): void; diff --git a/packages/replay-internal/test/integration/autoSaveSession.test.ts b/packages/replay-internal/test/integration/autoSaveSession.test.ts index 6fc0539c771d..93a4abf59709 100644 --- a/packages/replay-internal/test/integration/autoSaveSession.test.ts +++ b/packages/replay-internal/test/integration/autoSaveSession.test.ts @@ -1,29 +1,30 @@ +import { vi } from 'vitest'; + import { EventType } from '@sentry-internal/rrweb'; import type { RecordingEvent } from '../../src/types'; import { addEvent } from '../../src/util/addEvent'; import { resetSdkMock } from '../mocks/resetSdkMock'; import { useFakeTimers } from '../utils/use-fake-timers'; +import { saveSession } from '../../src/session/saveSession'; useFakeTimers(); +vi.mock('../../src/session/saveSession', () => { + return { + saveSession: vi.fn(), + }; +}); + describe('Integration | autoSaveSession', () => { afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test.each([ ['with stickySession=true', true, 1], ['with stickySession=false', false, 0], ])('%s', async (_: string, stickySession: boolean, addSummand: number) => { - const saveSessionSpy = jest.fn(); - - jest.mock('../../src/session/saveSession', () => { - return { - saveSession: saveSessionSpy, - }; - }); - const { replay } = await resetSdkMock({ replayOptions: { stickySession, @@ -31,11 +32,11 @@ describe('Integration | autoSaveSession', () => { }); // Initially called up to three times: once for start, then once for replay.updateSessionActivity & once for segmentId increase - expect(saveSessionSpy).toHaveBeenCalledTimes(addSummand * 3); + expect(saveSession).toHaveBeenCalledTimes(addSummand * 3); replay['_updateSessionActivity'](); - expect(saveSessionSpy).toHaveBeenCalledTimes(addSummand * 4); + expect(saveSession).toHaveBeenCalledTimes(addSummand * 4); // In order for runFlush to actually do something, we need to add an event const event = { @@ -48,8 +49,8 @@ describe('Integration | autoSaveSession', () => { addEvent(replay, event); - await replay['_runFlush'](); + await Promise.all([replay['_runFlush'](), vi.runAllTimersAsync()]); - expect(saveSessionSpy).toHaveBeenCalledTimes(addSummand * 5); + expect(saveSession).toHaveBeenCalledTimes(addSummand * 5); }); }); diff --git a/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts b/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts index a8331149838b..dc72c817a795 100644 --- a/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts +++ b/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts @@ -1,3 +1,6 @@ +import { vi } from 'vitest'; +import type { MockedFunction, MockInstance } from 'vitest'; + import * as SentryBrowserUtils from '@sentry-internal/browser-utils'; import * as SentryCore from '@sentry/core'; import type { Transport } from '@sentry/types'; @@ -14,24 +17,19 @@ import { useFakeTimers } from '../utils/use-fake-timers'; useFakeTimers(); -async function advanceTimers(time: number) { - jest.advanceTimersByTime(time); - await new Promise(process.nextTick); -} - -type MockTransportSend = jest.MockedFunction; +type MockTransportSend = MockedFunction; describe('Integration | beforeAddRecordingEvent', () => { let replay: ReplayContainer; let integration: Replay; let mockTransportSend: MockTransportSend; - let mockSendReplayRequest: jest.SpyInstance; + let mockSendReplayRequest: MockInstance; let domHandler: DomHandler; const { record: mockRecord } = mockRrweb(); beforeAll(async () => { - jest.setSystemTime(new Date(BASE_TIMESTAMP)); - jest.spyOn(SentryBrowserUtils, 'addClickKeypressInstrumentationHandler').mockImplementation(handler => { + vi.setSystemTime(new Date(BASE_TIMESTAMP)); + vi.spyOn(SentryBrowserUtils, 'addClickKeypressInstrumentationHandler').mockImplementation(handler => { domHandler = handler; }); @@ -69,14 +67,14 @@ describe('Integration | beforeAddRecordingEvent', () => { }, })); - mockSendReplayRequest = jest.spyOn(SendReplayRequest, 'sendReplayRequest'); + mockSendReplayRequest = vi.spyOn(SendReplayRequest, 'sendReplayRequest'); - jest.runAllTimers(); + vi.runAllTimers(); mockTransportSend = SentryCore.getClient()?.getTransport()?.send as MockTransportSend; }); beforeEach(() => { - jest.setSystemTime(new Date(BASE_TIMESTAMP)); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); mockRecord.takeFullSnapshot.mockClear(); mockTransportSend.mockClear(); @@ -90,9 +88,9 @@ describe('Integration | beforeAddRecordingEvent', () => { }); afterEach(async () => { - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); - jest.setSystemTime(new Date(BASE_TIMESTAMP)); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); clearSession(replay); }); @@ -106,7 +104,7 @@ describe('Integration | beforeAddRecordingEvent', () => { event: new Event('click'), }); - await advanceTimers(5000); + await vi.runAllTimersAsync(); expect(replay).toHaveLastSentReplay({ recordingPayloadHeader: { segment_id: 0 }, @@ -135,8 +133,7 @@ describe('Integration | beforeAddRecordingEvent', () => { integration.start(); - jest.runAllTimers(); - await new Promise(process.nextTick); + await vi.runAllTimersAsync(); expect(replay).toHaveLastSentReplay({ recordingPayloadHeader: { segment_id: 0 }, recordingData: JSON.stringify([{ data: { isCheckout: true }, timestamp: BASE_TIMESTAMP, type: 2 }]), @@ -174,7 +171,7 @@ describe('Integration | beforeAddRecordingEvent', () => { ]), ); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(replay).not.toHaveLastSentReplay(); diff --git a/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts b/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts index 03f4c236b81d..ed446a4f8c4b 100644 --- a/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts +++ b/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts @@ -1,3 +1,6 @@ +import { vi } from 'vitest'; +import type { MockInstance } from 'vitest'; + import { getClient } from '@sentry/core'; import type { ErrorEvent, Event } from '@sentry/types'; @@ -79,7 +82,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().traceIds)).toEqual(['tr2']); // Does not affect error session - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(Array.from(replay.getContext().errorIds)).toEqual([]); @@ -152,9 +155,11 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { }, })); - const mockSend = getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + const mockSend = getClient()!.getTransport()!.send as unknown as MockInstance; + expect(mockSend).toHaveBeenCalledTimes(0); const error1 = Error({ event_id: 'err1', tags: { replayId: 'replayid1' } }); + await vi.runOnlyPendingTimersAsync(); const handler = handleAfterSendEvent(replay); @@ -164,13 +169,13 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); - jest.runAllTimers(); - await new Promise(process.nextTick); - // Send twice, one for the error & one right after for the session conversion - expect(mockSend).toHaveBeenCalledTimes(1); - - jest.runAllTimers(); - await new Promise(process.nextTick); + // This is a bit flakey: handleAfterSendEvent calls + // `sendBufferedReplayOrFlush`, which flushes immediately but also + // calls `startRecording` which eventually triggers another flush after + // flush delay. I'm unable to get stable timer controls, so just + // testing the end result, which is that send gets called twice. + await vi.runOnlyPendingTimersAsync(); + await vi.runOnlyPendingTimersAsync(); expect(mockSend).toHaveBeenCalledTimes(2); // This is removed now, because it has been converted to a "session" session @@ -191,7 +196,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { }, })); - const mockSend = getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + const mockSend = getClient()!.getTransport()!.send as unknown as MockInstance; const error1 = Error({ event_id: 'err1' }); @@ -203,7 +208,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); // Send once for the regular session sending @@ -225,7 +230,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { }, })); - const mockSend = getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + const mockSend = getClient()!.getTransport()!.send as unknown as MockInstance; const profileEvent: Event = { type: 'profile' }; const replayEvent: Event = { type: 'replay_event' }; @@ -239,7 +244,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual([]); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(mockSend).toHaveBeenCalledTimes(0); @@ -260,7 +265,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { }, })); - const mockSend = getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + const mockSend = getClient()!.getTransport()!.send as unknown as MockInstance; const error1 = Error({ event_id: 'err1' }); @@ -272,7 +277,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual([]); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); // Remains in buffer mode & without flushing @@ -294,7 +299,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { }, })); - const mockSend = getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + const mockSend = getClient()!.getTransport()!.send as unknown as MockInstance; const error1: ErrorEvent = { event_id: 'err1', type: undefined }; @@ -306,7 +311,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); // Remains in buffer mode & without flushing @@ -328,7 +333,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { }, })); - const mockSend = getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + const mockSend = getClient()!.getTransport()!.send as unknown as MockInstance; const error1 = Error({ event_id: 'err1', message: UNABLE_TO_SEND_REPLAY }); @@ -340,7 +345,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); // Remains in buffer mode & without flushing @@ -362,7 +367,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { }, })); - const mockSend = getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + const mockSend = getClient()!.getTransport()!.send as unknown as MockInstance; const error1 = Error({ event_id: 'err1', tags: { replayId: 'replayid1' } }); @@ -372,7 +377,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { replay['_isEnabled'] = false; - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(mockSend).toHaveBeenCalledTimes(0); @@ -382,7 +387,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { const error1 = Error({ event_id: 'err1', tags: { replayId: 'replayid1' } }); const error2 = Error({ event_id: 'err2', tags: { replayId: 'replayid1' } }); - const beforeErrorSampling = jest.fn(event => event === error2); + const beforeErrorSampling = vi.fn(event => event === error2); ({ replay } = await resetSdkMock({ replayOptions: { @@ -395,7 +400,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { }, })); - const mockSend = getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + const mockSend = getClient()!.getTransport()!.send as unknown as MockInstance; const handler = handleAfterSendEvent(replay); @@ -403,7 +408,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { handler(error1, { statusCode: 200 }); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(beforeErrorSampling).toHaveBeenCalledTimes(1); @@ -415,7 +420,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { handler(error2, { statusCode: 200 }); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(beforeErrorSampling).toHaveBeenCalledTimes(2); diff --git a/packages/replay-internal/test/integration/coreHandlers/handleBeforeSendEvent.test.ts b/packages/replay-internal/test/integration/coreHandlers/handleBeforeSendEvent.test.ts index a1462b974711..8cd9fda46247 100644 --- a/packages/replay-internal/test/integration/coreHandlers/handleBeforeSendEvent.test.ts +++ b/packages/replay-internal/test/integration/coreHandlers/handleBeforeSendEvent.test.ts @@ -1,3 +1,5 @@ +import { vi } from 'vitest'; + import { handleBeforeSendEvent } from '../../../src/coreHandlers/handleBeforeSendEvent'; import type { ReplayContainer } from '../../../src/replay'; import { Error } from '../../fixtures/error'; @@ -24,7 +26,7 @@ describe('Integration | coreHandlers | handleBeforeSendEvent', () => { })); const handler = handleBeforeSendEvent(replay); - const addBreadcrumbSpy = jest.spyOn(replay, 'throttledAddEvent'); + const addBreadcrumbSpy = vi.spyOn(replay, 'throttledAddEvent'); const error = Error(); error.exception.values[0].value = @@ -58,7 +60,7 @@ describe('Integration | coreHandlers | handleBeforeSendEvent', () => { })); const handler = handleBeforeSendEvent(replay); - const addBreadcrumbSpy = jest.spyOn(replay, 'throttledAddEvent'); + const addBreadcrumbSpy = vi.spyOn(replay, 'throttledAddEvent'); const error = Error(); error.exception.values[0].value = 'https://reactjs.org/docs/error-decoder.html?invariant=423'; diff --git a/packages/replay-internal/test/integration/coreHandlers/handleGlobalEvent.test.ts b/packages/replay-internal/test/integration/coreHandlers/handleGlobalEvent.test.ts index 5200294808db..ebe43c25eb98 100644 --- a/packages/replay-internal/test/integration/coreHandlers/handleGlobalEvent.test.ts +++ b/packages/replay-internal/test/integration/coreHandlers/handleGlobalEvent.test.ts @@ -191,7 +191,7 @@ describe('Integration | coreHandlers | handleGlobalEvent', () => { expect(Array.from(replay.getContext().traceIds)).toEqual([]); expect(Array.from(replay.getContext().errorIds)).toEqual([]); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(Array.from(replay.getContext().errorIds)).toEqual([]); diff --git a/packages/replay-internal/test/integration/errorSampleRate.test.ts b/packages/replay-internal/test/integration/errorSampleRate.test.ts index 4f83f150548f..28f1d1192904 100644 --- a/packages/replay-internal/test/integration/errorSampleRate.test.ts +++ b/packages/replay-internal/test/integration/errorSampleRate.test.ts @@ -1,3 +1,4 @@ +import { vi } from 'vitest'; import { captureException, getClient } from '@sentry/core'; import { @@ -23,12 +24,7 @@ import { useFakeTimers } from '../utils/use-fake-timers'; useFakeTimers(); async function advanceTimers(time: number) { - jest.advanceTimersByTime(time); - await new Promise(process.nextTick); -} - -async function waitForBufferFlush() { - await new Promise(process.nextTick); + vi.advanceTimersByTime(time); await new Promise(process.nextTick); } @@ -72,13 +68,13 @@ describe('Integration | errorSampleRate', () => { name: 'click', event: new Event('click'), }); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(replay).not.toHaveLastSentReplay(); captureException(new Error('testing')); - await waitForBufferFlush(); + await vi.advanceTimersToNextTimerAsync(); expect(replay).toHaveLastSentReplay({ recordingPayloadHeader: { segment_id: 0 }, @@ -114,10 +110,10 @@ describe('Integration | errorSampleRate', () => { replayEventPayload: expect.objectContaining({ replay_type: 'buffer', }), - recordingData: JSON.stringify([{ data: { isCheckout: true }, timestamp: BASE_TIMESTAMP + 40, type: 2 }]), + recordingData: JSON.stringify([{ data: { isCheckout: true }, timestamp: BASE_TIMESTAMP, type: 2 }]), }); - jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); + vi.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); // Check that click will get captured domHandler({ @@ -131,11 +127,11 @@ describe('Integration | errorSampleRate', () => { recordingData: JSON.stringify([ { type: 5, - timestamp: BASE_TIMESTAMP + 10000 + 80, + timestamp: BASE_TIMESTAMP + 10000, data: { tag: 'breadcrumb', payload: { - timestamp: (BASE_TIMESTAMP + 10000 + 80) / 1000, + timestamp: (BASE_TIMESTAMP + 10000) / 1000, type: 'default', category: 'ui.click', message: '', @@ -160,13 +156,13 @@ describe('Integration | errorSampleRate', () => { name: 'click', event: new Event('click'), }); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(replay).not.toHaveLastSentReplay(); replay.sendBufferedReplayOrFlush({ continueRecording: false }); - await waitForBufferFlush(); + await vi.advanceTimersToNextTimerAsync(); expect(replay).toHaveSentReplay({ recordingPayloadHeader: { segment_id: 0 }, @@ -194,7 +190,7 @@ describe('Integration | errorSampleRate', () => { ]), }); - jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); + vi.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); // Check that click will not get captured domHandler({ name: 'click', @@ -245,14 +241,14 @@ describe('Integration | errorSampleRate', () => { name: 'click', event: new Event('click'), }); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(replay).not.toHaveLastSentReplay(); replay.sendBufferedReplayOrFlush({ continueRecording: true }); replay.sendBufferedReplayOrFlush({ continueRecording: true }); - await waitForBufferFlush(); + await vi.advanceTimersToNextTimerAsync(); expect(replay).toHaveSentReplay({ recordingPayloadHeader: { segment_id: 0 }, @@ -280,7 +276,7 @@ describe('Integration | errorSampleRate', () => { ]), }); - jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); + vi.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); // Check that click will not get captured domHandler({ name: 'click', @@ -309,7 +305,7 @@ describe('Integration | errorSampleRate', () => { replay['_initializeSessionForSampling'](); replay.setInitialState(); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); expect(replay).not.toHaveLastSentReplay(); @@ -323,11 +319,11 @@ describe('Integration | errorSampleRate', () => { }, }); - jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); + vi.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); document.dispatchEvent(new Event('visibilitychange')); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(replay).not.toHaveLastSentReplay(); @@ -342,13 +338,13 @@ describe('Integration | errorSampleRate', () => { }); document.dispatchEvent(new Event('visibilitychange')); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(replay).not.toHaveLastSentReplay(); // User comes back before `SESSION_IDLE_EXPIRE_DURATION` elapses - jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION - 100); + vi.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION - 100); Object.defineProperty(document, 'visibilityState', { configurable: true, get: function () { @@ -357,7 +353,7 @@ describe('Integration | errorSampleRate', () => { }); document.dispatchEvent(new Event('visibilitychange')); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); @@ -374,14 +370,14 @@ describe('Integration | errorSampleRate', () => { // Pretend 5 seconds have passed const ELAPSED = 5000; - jest.advanceTimersByTime(ELAPSED); + vi.advanceTimersByTime(ELAPSED); const TEST_EVENT = getTestEventCheckout({ timestamp: BASE_TIMESTAMP }); addEvent(replay, TEST_EVENT); document.dispatchEvent(new Event('visibilitychange')); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); @@ -397,7 +393,7 @@ describe('Integration | errorSampleRate', () => { expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(replay).not.toHaveLastSentReplay(); @@ -408,7 +404,7 @@ describe('Integration | errorSampleRate', () => { // Fire a new event every 4 seconds, 4 times [...Array(4)].forEach(() => { mockRecord._emitter(TEST_EVENT); - jest.advanceTimersByTime(4000); + vi.advanceTimersByTime(4000); }); // We are at time = +16seconds now (relative to BASE_TIMESTAMP) @@ -425,7 +421,7 @@ describe('Integration | errorSampleRate', () => { // Let's make sure it continues to work mockRecord._emitter(TEST_EVENT); await waitForFlush(); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(replay).not.toHaveLastSentReplay(); }); @@ -440,7 +436,7 @@ describe('Integration | errorSampleRate', () => { captureException(new Error('testing')); - await waitForBufferFlush(); + await vi.advanceTimersToNextTimerAsync(); expect(replay).toHaveLastSentReplay({ recordingPayloadHeader: { segment_id: 0 }, @@ -463,7 +459,7 @@ describe('Integration | errorSampleRate', () => { const sessionId = replay.getSessionId(); // Idle for given time - jest.advanceTimersByTime(waitTime + 1); + vi.advanceTimersByTime(waitTime + 1); await new Promise(process.nextTick); const TEST_EVENT = getTestEventIncremental({ @@ -472,7 +468,7 @@ describe('Integration | errorSampleRate', () => { }); mockRecord._emitter(TEST_EVENT); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); // We stop recording after 15 minutes of inactivity in error mode @@ -499,7 +495,7 @@ describe('Integration | errorSampleRate', () => { expect(replay).not.toHaveLastSentReplay(); // Idle for given time - jest.advanceTimersByTime(waitTime + 1); + vi.advanceTimersByTime(waitTime + 1); await new Promise(process.nextTick); const TEST_EVENT = getTestEventIncremental({ @@ -508,7 +504,7 @@ describe('Integration | errorSampleRate', () => { }); mockRecord._emitter(TEST_EVENT); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); // in production, this happens at a time interval, here we mock this @@ -536,7 +532,7 @@ describe('Integration | errorSampleRate', () => { // should still react to errors later on captureException(new Error('testing')); - await waitForBufferFlush(); + await vi.advanceTimersToNextTimerAsync(); expect(replay.session?.id).toBe(oldSessionId); @@ -560,7 +556,7 @@ describe('Integration | errorSampleRate', () => { expect(oldSessionId).toBeDefined(); // Idle for 15 minutes - jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); + vi.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); const TEST_EVENT = getTestEventIncremental({ data: { name: 'lost event' }, @@ -569,7 +565,7 @@ describe('Integration | errorSampleRate', () => { mockRecord._emitter(TEST_EVENT); expect(replay).not.toHaveLastSentReplay(); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); // We stop recording after SESSION_IDLE_EXPIRE_DURATION of inactivity in error mode @@ -582,7 +578,7 @@ describe('Integration | errorSampleRate', () => { captureException(new Error('testing')); await new Promise(process.nextTick); - jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); + vi.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); await new Promise(process.nextTick); expect(replay.session?.id).toBe(oldSessionId); @@ -617,16 +613,12 @@ describe('Integration | errorSampleRate', () => { expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); expect(replay).not.toHaveLastSentReplay(); - jest.runAllTimers(); - await new Promise(process.nextTick); - - jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); captureException(new Error('testing')); - await new Promise(process.nextTick); - jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); - await new Promise(process.nextTick); + vi.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersToNextTimerAsync(); expect(replay).toHaveSentReplay({ recordingData: JSON.stringify([ @@ -636,14 +628,10 @@ describe('Integration | errorSampleRate', () => { ]), replayEventPayload: expect.objectContaining({ replay_start_timestamp: BASE_TIMESTAMP / 1000, - // the exception happens roughly 10 seconds after BASE_TIMESTAMP - // (advance timers + waiting for flush after the checkout) and - // extra time is likely due to async of `addMemoryEntry()` - - timestamp: (BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY + DEFAULT_FLUSH_MIN_DELAY + 40) / 1000, + timestamp: (BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY + DEFAULT_FLUSH_MIN_DELAY) / 1000, error_ids: [expect.any(String)], trace_ids: [], - urls: ['http://localhost/'], + urls: ['http://localhost:3000/'], replay_id: expect.any(String), }), recordingPayloadHeader: { segment_id: 0 }, @@ -659,42 +647,43 @@ describe('Integration | errorSampleRate', () => { // add a mock performance event replay.performanceEntries.push(PerformanceEntryResource()); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); expect(replay).not.toHaveLastSentReplay(); - jest.advanceTimersByTime(ELAPSED); + vi.advanceTimersByTime(ELAPSED); // in production, this happens at a time interval // session started time should be updated to this current timestamp mockRecord.takeFullSnapshot(true); const optionsEvent = createOptionsEvent(replay); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(replay).not.toHaveLastSentReplay(); captureException(new Error('testing')); - await waitForBufferFlush(); + await vi.advanceTimersToNextTimerAsync(); + // await vi.advanceTimersToNextTimerAsync(); // This is still the timestamp from the full snapshot we took earlier - expect(replay.session?.started).toBe(BASE_TIMESTAMP + ELAPSED + TICK); + expect(replay.session?.started).toBe(BASE_TIMESTAMP + ELAPSED); // Does not capture mouse click expect(replay).toHaveSentReplay({ recordingPayloadHeader: { segment_id: 0 }, replayEventPayload: expect.objectContaining({ // Make sure the old performance event is thrown out - replay_start_timestamp: (BASE_TIMESTAMP + ELAPSED + TICK) / 1000, + replay_start_timestamp: (BASE_TIMESTAMP + ELAPSED) / 1000, }), recordingData: JSON.stringify([ { data: { isCheckout: true }, - timestamp: BASE_TIMESTAMP + ELAPSED + TICK, + timestamp: BASE_TIMESTAMP + ELAPSED, type: 2, }, optionsEvent, @@ -703,7 +692,7 @@ describe('Integration | errorSampleRate', () => { }); it('refreshes replay when user goes idle', async () => { - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); mockRecord._emitter(TEST_EVENT); @@ -711,12 +700,10 @@ describe('Integration | errorSampleRate', () => { expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); expect(replay).not.toHaveLastSentReplay(); - jest.runAllTimers(); - await new Promise(process.nextTick); - captureException(new Error('testing')); - await waitForBufferFlush(); + await vi.advanceTimersToNextTimerAsync(); + await vi.advanceTimersToNextTimerAsync(); expect(replay).toHaveLastSentReplay(); @@ -725,14 +712,14 @@ describe('Integration | errorSampleRate', () => { // Now wait after session expires - should stop recording mockRecord.takeFullSnapshot.mockClear(); - (getClient()!.getTransport()!.send as unknown as jest.SpyInstance).mockClear(); + (getClient()!.getTransport()!.send as unknown as MockInstance).mockClear(); expect(replay).not.toHaveLastSentReplay(); const sessionId = replay.getSessionId(); // Go idle - jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); + vi.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); await new Promise(process.nextTick); mockRecord._emitter(TEST_EVENT); @@ -747,9 +734,9 @@ describe('Integration | errorSampleRate', () => { expect(replay.getSessionId()).not.toBe(sessionId); }); - it('refreshes replay when session exceeds max length after latest captured error', async () => { + it.only('refreshes replay when session exceeds max length after latest captured error', async () => { const sessionId = replay.session?.id; - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); mockRecord._emitter(TEST_EVENT); @@ -757,10 +744,9 @@ describe('Integration | errorSampleRate', () => { expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); expect(replay).not.toHaveLastSentReplay(); - jest.runAllTimers(); - await new Promise(process.nextTick); + await vi.runAllTimersAsync(); - jest.advanceTimersByTime(2 * MAX_REPLAY_DURATION); + await vi.advanceTimersByTimeAsync(2 * MAX_REPLAY_DURATION); // in production, this happens at a time interval, here we mock this mockRecord.takeFullSnapshot(true); @@ -768,11 +754,11 @@ describe('Integration | errorSampleRate', () => { captureException(new Error('testing')); // Flush due to exception - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); await waitForFlush(); expect(replay.session?.id).toBe(sessionId); - expect(replay).toHaveLastSentReplay({ + expect(replay).toHaveSentReplay({ recordingPayloadHeader: { segment_id: 0 }, }); @@ -785,7 +771,7 @@ describe('Integration | errorSampleRate', () => { data: { isCheckout: true, }, - timestamp: BASE_TIMESTAMP + 2 * MAX_REPLAY_DURATION + DEFAULT_FLUSH_MIN_DELAY + 40, + timestamp: BASE_TIMESTAMP + 2 * MAX_REPLAY_DURATION + DEFAULT_FLUSH_MIN_DELAY, type: 2, }, ]), @@ -793,14 +779,12 @@ describe('Integration | errorSampleRate', () => { // Now wait after session expires - should stop recording mockRecord.takeFullSnapshot.mockClear(); - (getClient()!.getTransport()!.send as unknown as jest.SpyInstance).mockClear(); + (getClient()!.getTransport()!.send as unknown as MockInstance).mockClear(); - jest.advanceTimersByTime(MAX_REPLAY_DURATION); - await new Promise(process.nextTick); + await advanceTimers(MAX_REPLAY_DURATION); mockRecord._emitter(TEST_EVENT); - jest.runAllTimers(); - await new Promise(process.nextTick); + await vi.runAllTimersAsync(); expect(replay).not.toHaveLastSentReplay(); expect(mockRecord.takeFullSnapshot).toHaveBeenCalledTimes(0); @@ -812,7 +796,7 @@ describe('Integration | errorSampleRate', () => { captureException(new Error('testing')); await new Promise(process.nextTick); - jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); + vi.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); await new Promise(process.nextTick); expect(replay).toHaveLastSentReplay(); }); @@ -821,14 +805,14 @@ describe('Integration | errorSampleRate', () => { const stepDuration = 10_000; const steps = 5_000; - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); expect(replay).not.toHaveLastSentReplay(); let optionsEvent = createOptionsEvent(replay); for (let i = 1; i <= steps; i++) { - jest.advanceTimersByTime(stepDuration); + vi.advanceTimersByTime(stepDuration); optionsEvent = createOptionsEvent(replay); mockRecord._emitter({ data: { step: i }, timestamp: BASE_TIMESTAMP + stepDuration * i, type: 2 }, true); mockRecord._emitter({ data: { step: i }, timestamp: BASE_TIMESTAMP + stepDuration * i + 5, type: 3 }); @@ -842,7 +826,7 @@ describe('Integration | errorSampleRate', () => { // Now capture an error captureException(new Error('testing')); - await waitForBufferFlush(); + await vi.advanceTimersToNextTimerAsync(); expect(replay).toHaveLastSentReplay({ recordingData: JSON.stringify([ @@ -854,7 +838,7 @@ describe('Integration | errorSampleRate', () => { replay_start_timestamp: (BASE_TIMESTAMP + stepDuration * steps) / 1000, error_ids: [expect.any(String)], trace_ids: [], - urls: ['http://localhost/'], + urls: ['http://localhost:3000/'], replay_id: expect.any(String), }), recordingPayloadHeader: { segment_id: 0 }, @@ -890,7 +874,7 @@ describe('Integration | errorSampleRate', () => { // Waiting for max life should eventually refresh the session // We simulate a full checkout which would otherwise be done automatically for (let i = 0; i < MAX_REPLAY_DURATION / 60_000; i++) { - jest.advanceTimersByTime(60_000); + vi.advanceTimersByTime(60_000); await new Promise(process.nextTick); mockRecord.takeFullSnapshot(true); } @@ -917,7 +901,7 @@ describe('Integration | errorSampleRate', () => { }); integration['_initialize'](); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); @@ -928,7 +912,7 @@ describe('Integration | errorSampleRate', () => { // Waiting for max life should eventually stop recording // We simulate a full checkout which would otherwise be done automatically for (let i = 0; i < MAX_REPLAY_DURATION / 60_000; i++) { - jest.advanceTimersByTime(60_000); + vi.advanceTimersByTime(60_000); await new Promise(process.nextTick); mockRecord.takeFullSnapshot(true); } @@ -962,9 +946,6 @@ describe('Integration | errorSampleRate', () => { integration['_initialize'](); const optionsEvent = createOptionsEvent(replay); - jest.runAllTimers(); - - await new Promise(process.nextTick); const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); mockRecord._emitter(TEST_EVENT); @@ -973,7 +954,8 @@ describe('Integration | errorSampleRate', () => { captureException(new Error('testing')); // 2 ticks to send replay from an error - await waitForBufferFlush(); + await vi.advanceTimersToNextTimerAsync(); + await vi.advanceTimersToNextTimerAsync(); // Buffered events before error expect(replay).toHaveSentReplay({ @@ -986,13 +968,13 @@ describe('Integration | errorSampleRate', () => { }); // `startRecording()` after switching to session mode to continue recording - await waitForFlush(); + await vi.advanceTimersToNextTimerAsync(); // Latest checkout when we call `startRecording` again after uploading segment // after an error occurs (e.g. when we switch to session replay recording) expect(replay).toHaveLastSentReplay({ recordingPayloadHeader: { segment_id: 1 }, - recordingData: JSON.stringify([{ data: { isCheckout: true }, timestamp: BASE_TIMESTAMP + 40, type: 2 }]), + recordingData: JSON.stringify([{ data: { isCheckout: true }, timestamp: BASE_TIMESTAMP, type: 2 }]), }); }); }); diff --git a/packages/replay-internal/test/integration/eventProcessors.test.ts b/packages/replay-internal/test/integration/eventProcessors.test.ts index b683e1ac4279..58a0f488eb91 100644 --- a/packages/replay-internal/test/integration/eventProcessors.test.ts +++ b/packages/replay-internal/test/integration/eventProcessors.test.ts @@ -1,3 +1,5 @@ +import { vi } from 'vitest'; + import { getClient, getCurrentScope } from '@sentry/core'; import type { Event } from '@sentry/types'; @@ -15,7 +17,7 @@ describe('Integration | eventProcessors', () => { }); afterEach(() => { - jest.resetAllMocks(); + vi.resetAllMocks(); }); it('handles event processors properly', async () => { @@ -29,17 +31,17 @@ describe('Integration | eventProcessors', () => { const client = getClient()!; - jest.runAllTimers(); - const mockTransportSend = jest.spyOn(client.getTransport()!, 'send'); + await vi.runAllTimersAsync(); + const mockTransportSend = vi.spyOn(client.getTransport()!, 'send'); mockTransportSend.mockReset(); - const handler1 = jest.fn((event: Event): Event | null => { + const handler1 = vi.fn((event: Event): Event | null => { event.timestamp = MUTATED_TIMESTAMP; return event; }); - const handler2 = jest.fn((): Event | null => { + const handler2 = vi.fn((): Event | null => { return null; }); @@ -48,8 +50,7 @@ describe('Integration | eventProcessors', () => { const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); mockRecord._emitter(TEST_EVENT); - jest.runAllTimers(); - jest.advanceTimersByTime(1); + vi.runAllTimers(); await new Promise(process.nextTick); expect(mockTransportSend).toHaveBeenCalledTimes(1); @@ -59,8 +60,7 @@ describe('Integration | eventProcessors', () => { const TEST_EVENT2 = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); mockRecord._emitter(TEST_EVENT2); - jest.runAllTimers(); - jest.advanceTimersByTime(1); + vi.runAllTimers(); await new Promise(process.nextTick); expect(mockTransportSend).toHaveBeenCalledTimes(1); diff --git a/packages/replay-internal/test/integration/events.test.ts b/packages/replay-internal/test/integration/events.test.ts index 7a3d6e920a9e..c7670b70a0b6 100644 --- a/packages/replay-internal/test/integration/events.test.ts +++ b/packages/replay-internal/test/integration/events.test.ts @@ -14,19 +14,19 @@ import { useFakeTimers } from '../utils/use-fake-timers'; useFakeTimers(); async function advanceTimers(time: number) { - jest.advanceTimersByTime(time); + vi.advanceTimersByTime(time); await new Promise(process.nextTick); } describe('Integration | events', () => { let replay: ReplayContainer; let mockRecord: RecordMock; - let mockTransportSend: jest.SpyInstance; + let mockTransportSend: MockInstance; const prevLocation = WINDOW.location; beforeAll(async () => { - jest.setSystemTime(new Date(BASE_TIMESTAMP)); - jest.runAllTimers(); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); + vi.runAllTimers(); }); beforeEach(async () => { @@ -36,7 +36,7 @@ describe('Integration | events', () => { }, })); - mockTransportSend = jest.spyOn(getClient()!.getTransport()!, 'send'); + mockTransportSend = vi.spyOn(getClient()!.getTransport()!, 'send'); // Create a new session and clear mocks because a segment (from initial // checkout) will have already been uploaded by the time the tests run @@ -47,14 +47,14 @@ describe('Integration | events', () => { }); afterEach(async () => { - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); Object.defineProperty(WINDOW, 'location', { value: prevLocation, writable: true, }); clearSession(replay); - jest.clearAllMocks(); + vi.clearAllMocks(); mockRecord.takeFullSnapshot.mockClear(); replay.stop(); }); @@ -86,7 +86,7 @@ describe('Integration | events', () => { expect(replay).toHaveLastSentReplay({ replayEventPayload: expect.objectContaining({ replay_start_timestamp: BASE_TIMESTAMP / 1000, - urls: ['http://localhost/'], // this doesn't truly test if we are capturing the right URL as we don't change URLs, but good enough + urls: ['http://localhost:3000/'], // this doesn't truly test if we are capturing the right URL as we don't change URLs, but good enough }), }); }); @@ -129,7 +129,7 @@ describe('Integration | events', () => { expect(replay).toHaveLastSentReplay({ replayEventPayload: expect.objectContaining({ replay_start_timestamp: (BASE_TIMESTAMP - 10000) / 1000, - urls: ['http://localhost/'], // this doesn't truly test if we are capturing the right URL as we don't change URLs, but good enough + urls: ['http://localhost:3000/'], // this doesn't truly test if we are capturing the right URL as we don't change URLs, but good enough }), }); }); @@ -160,7 +160,7 @@ describe('Integration | events', () => { addEvent(replay, TEST_EVENT); // This event will trigger a flush WINDOW.dispatchEvent(new Event('blur')); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(mockTransportSend).toHaveBeenCalledTimes(1); diff --git a/packages/replay-internal/test/integration/flush.test.ts b/packages/replay-internal/test/integration/flush.test.ts index ee4cf456fbf9..b7c9b263c24a 100644 --- a/packages/replay-internal/test/integration/flush.test.ts +++ b/packages/replay-internal/test/integration/flush.test.ts @@ -1,3 +1,6 @@ +import { vi } from 'vitest'; +import type { MockedFunction, MockInstance } from 'vitest'; + import * as SentryBrowserUtils from '@sentry-internal/browser-utils'; import * as SentryUtils from '@sentry/utils'; @@ -16,17 +19,12 @@ import { useFakeTimers } from '../utils/use-fake-timers'; useFakeTimers(); -async function advanceTimers(time: number) { - jest.advanceTimersByTime(time); - await new Promise(process.nextTick); -} - -type MockSendReplay = jest.MockedFunction; -type MockAddPerformanceEntries = jest.MockedFunction; -type MockAddMemoryEntry = jest.SpyInstance; -type MockEventBufferFinish = jest.MockedFunction; -type MockFlush = jest.MockedFunction; -type MockRunFlush = jest.MockedFunction; +type MockSendReplay = MockedFunction; +type MockAddPerformanceEntries = MockedFunction; +type MockAddMemoryEntry = MockInstance; +type MockEventBufferFinish = MockedFunction; +type MockFlush = MockedFunction; +type MockRunFlush = MockedFunction; const prevLocation = WINDOW.location; const prevBrowserPerformanceTimeOrigin = SentryUtils.browserPerformanceTimeOrigin; @@ -45,44 +43,40 @@ describe('Integration | flush', () => { let mockAddPerformanceEntries: MockAddPerformanceEntries; beforeAll(async () => { - jest.spyOn(SentryBrowserUtils, 'addClickKeypressInstrumentationHandler').mockImplementation(handler => { + vi.spyOn(SentryBrowserUtils, 'addClickKeypressInstrumentationHandler').mockImplementation(handler => { domHandler = handler; }); ({ replay } = await mockSdk()); - mockSendReplay = jest.spyOn(SendReplay, 'sendReplay'); + mockSendReplay = vi.spyOn(SendReplay, 'sendReplay'); mockSendReplay.mockImplementation( - jest.fn(async () => { + vi.fn(async () => { return; }), ); // @ts-expect-error private API - mockFlush = jest.spyOn(replay, '_flush'); + mockFlush = vi.spyOn(replay, '_flush'); // @ts-expect-error private API - mockRunFlush = jest.spyOn(replay, '_runFlush'); + mockRunFlush = vi.spyOn(replay, '_runFlush'); // @ts-expect-error private API - mockAddPerformanceEntries = jest.spyOn(replay, '_addPerformanceEntries'); + mockAddPerformanceEntries = vi.spyOn(replay, '_addPerformanceEntries'); mockAddPerformanceEntries.mockImplementation(async () => { return []; }); - mockAddMemoryEntry = jest.spyOn(AddMemoryEntry, 'addMemoryEntry'); + mockAddMemoryEntry = vi.spyOn(AddMemoryEntry, 'addMemoryEntry'); }); - beforeEach(() => { - jest.runAllTimers(); - jest.setSystemTime(new Date(BASE_TIMESTAMP)); - mockSendReplay.mockClear(); + beforeEach(async () => { + await vi.runAllTimersAsync(); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); replay.eventBuffer?.destroy(); - mockAddPerformanceEntries.mockClear(); - mockFlush.mockClear(); - mockRunFlush.mockClear(); - mockAddMemoryEntry.mockClear(); + vi.clearAllMocks(); sessionStorage.clear(); clearSession(replay); @@ -90,7 +84,7 @@ describe('Integration | flush', () => { replay.setInitialState(); if (replay.eventBuffer) { - jest.spyOn(replay.eventBuffer, 'finish'); + vi.spyOn(replay.eventBuffer, 'finish'); } mockEventBufferFinish = replay.eventBuffer?.finish as MockEventBufferFinish; mockEventBufferFinish.mockClear(); @@ -102,9 +96,8 @@ describe('Integration | flush', () => { }); afterEach(async () => { - jest.runAllTimers(); - await new Promise(process.nextTick); - jest.setSystemTime(new Date(BASE_TIMESTAMP)); + await vi.runAllTimersAsync(); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); mockRecord.takeFullSnapshot.mockClear(); Object.defineProperty(WINDOW, 'location', { value: prevLocation, @@ -133,16 +126,12 @@ describe('Integration | flush', () => { expect(mockFlush).toHaveBeenCalledTimes(4); - jest.runAllTimers(); - await new Promise(process.nextTick); expect(mockRunFlush).toHaveBeenCalledTimes(1); - jest.runAllTimers(); - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); expect(mockRunFlush).toHaveBeenCalledTimes(2); - jest.runAllTimers(); - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); expect(mockRunFlush).toHaveBeenCalledTimes(2); }); @@ -164,18 +153,18 @@ describe('Integration | flush', () => { name: 'click', event: new Event('click'), }); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); // flush #2 @ t=5s - due to click expect(mockFlush).toHaveBeenCalledTimes(2); - await advanceTimers(1000); + await vi.advanceTimersByTimeAsync(1000); // flush #3 @ t=6s - due to blur WINDOW.dispatchEvent(new Event('blur')); expect(mockFlush).toHaveBeenCalledTimes(3); // NOTE: Blur also adds a breadcrumb which calls `addUpdate`, meaning it will // flush after `flushMinDelay`, but this gets cancelled by the blur - await advanceTimers(8000); + await vi.advanceTimersByTimeAsync(8000); expect(mockFlush).toHaveBeenCalledTimes(3); // flush #4 @ t=14s - due to blur @@ -183,7 +172,7 @@ describe('Integration | flush', () => { expect(mockFlush).toHaveBeenCalledTimes(4); expect(mockRunFlush).toHaveBeenCalledTimes(1); - await advanceTimers(6000); + await vi.advanceTimersByTimeAsync(6000); // t=20s // addPerformanceEntries is finished, `flushLock` promise is resolved, calls // debouncedFlush, which will call `flush` in 1 second @@ -236,7 +225,7 @@ describe('Integration | flush', () => { ); // flush #5 @ t=25s - debounced flush calls `flush` // 20s + `flushMinDelay` which is 5 seconds - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(mockFlush).toHaveBeenCalledTimes(5); expect(mockRunFlush).toHaveBeenCalledTimes(2); @@ -251,7 +240,7 @@ describe('Integration | flush', () => { }); // Make sure there's no other calls - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(mockSendReplay).toHaveBeenCalledTimes(2); }); @@ -267,11 +256,11 @@ describe('Integration | flush', () => { const TEST_EVENT = getTestEventCheckout({ timestamp: BASE_TIMESTAMP }); mockRecord._emitter(TEST_EVENT); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(mockFlush).toHaveBeenCalledTimes(1); // Make sure there's nothing queued up after - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(mockFlush).toHaveBeenCalledTimes(1); }); @@ -293,13 +282,13 @@ describe('Integration | flush', () => { const TEST_EVENT = getTestEventCheckout({ timestamp: BASE_TIMESTAMP }); mockRecord._emitter(TEST_EVENT); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(mockFlush).toHaveBeenCalledTimes(1); expect(mockSendReplay).toHaveBeenCalledTimes(0); // it should re-schedule the flush, so once the min. duration is reached it should automatically send it - await advanceTimers(100_000 - DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(100_000 - DEFAULT_FLUSH_MIN_DELAY); expect(mockFlush).toHaveBeenCalledTimes(20); expect(mockSendReplay).toHaveBeenCalledTimes(1); @@ -309,7 +298,7 @@ describe('Integration | flush', () => { it('does not flush if session is too long', async () => { replay.getOptions().maxReplayDuration = 100_000; - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); sessionStorage.clear(); clearSession(replay); @@ -322,7 +311,7 @@ describe('Integration | flush', () => { return true; }; - await advanceTimers(120_000); + await vi.advanceTimersByTimeAsync(120_000); // click happens first domHandler({ @@ -334,7 +323,7 @@ describe('Integration | flush', () => { const TEST_EVENT = getTestEventCheckout({ timestamp: BASE_TIMESTAMP }); mockRecord._emitter(TEST_EVENT); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(mockFlush).toHaveBeenCalledTimes(1); expect(mockSendReplay).toHaveBeenCalledTimes(0); @@ -351,7 +340,7 @@ describe('Integration | flush', () => { replay['_initializeSessionForSampling'](); replay.setInitialState(); await new Promise(process.nextTick); - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); // Clear the event buffer to simulate no checkout happened replay.eventBuffer!.clear(); @@ -363,7 +352,7 @@ describe('Integration | flush', () => { }); // no checkout! - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(mockFlush).toHaveBeenCalledTimes(1); expect(mockSendReplay).toHaveBeenCalledTimes(1); @@ -385,6 +374,21 @@ describe('Integration | flush', () => { }, }, }, + { + type: 5, + timestamp: BASE_TIMESTAMP, + data: { + tag: 'breadcrumb', + payload: { + timestamp: BASE_TIMESTAMP / 1000, + type: 'default', + category: 'console', + data: { logger: 'replay' }, + level: 'info', + message: '[Replay] Creating new session', + }, + }, + }, { type: 5, timestamp: BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY, @@ -408,14 +412,14 @@ describe('Integration | flush', () => { it('logs warning if adding event that is after maxReplayDuration', async () => { replay.getOptions()._experiments.traceInternals = true; - const spyLogger = jest.spyOn(SentryUtils.logger, 'info'); + const spyLogger = vi.spyOn(SentryUtils.logger, 'info'); sessionStorage.clear(); clearSession(replay); replay['_initializeSessionForSampling'](); replay.setInitialState(); await new Promise(process.nextTick); - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); replay.eventBuffer!.clear(); @@ -427,7 +431,7 @@ describe('Integration | flush', () => { mockRecord._emitter(TEST_EVENT); // no checkout! - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); // No flush is scheduled is aborted because event is after maxReplayDuration expect(mockFlush).toHaveBeenCalledTimes(0); @@ -457,7 +461,7 @@ describe('Integration | flush', () => { replay['_initializeSessionForSampling'](); replay.setInitialState(); await new Promise(process.nextTick); - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); replay.eventBuffer!.clear(); @@ -473,7 +477,7 @@ describe('Integration | flush', () => { const TEST_EVENT = getTestEventCheckout({ timestamp: BASE_TIMESTAMP + 100 }); mockRecord._emitter(TEST_EVENT); - await advanceTimers(160_000); + await vi.advanceTimersByTimeAsync(160_000); expect(mockFlush).toHaveBeenCalledTimes(1); expect(mockSendReplay).toHaveBeenCalledTimes(0); diff --git a/packages/replay-internal/test/integration/getReplayId.test.ts b/packages/replay-internal/test/integration/getReplayId.test.ts index 1080186974fc..7f24e5b1cbb7 100644 --- a/packages/replay-internal/test/integration/getReplayId.test.ts +++ b/packages/replay-internal/test/integration/getReplayId.test.ts @@ -5,7 +5,7 @@ useFakeTimers(); describe('Integration | getReplayId', () => { afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('works', async () => { diff --git a/packages/replay-internal/test/integration/integrationSettings.test.ts b/packages/replay-internal/test/integration/integrationSettings.test.ts index 3d7c180faf2f..b0ec35fc8a05 100644 --- a/packages/replay-internal/test/integration/integrationSettings.test.ts +++ b/packages/replay-internal/test/integration/integrationSettings.test.ts @@ -1,8 +1,11 @@ +import { vi } from 'vitest'; +import type { MockInstance } from 'vitest'; + import { mockSdk } from '../index'; describe('Integration | integrationSettings', () => { beforeEach(() => { - jest.resetModules(); + vi.resetModules(); }); describe('blockAllMedia', () => { @@ -14,10 +17,10 @@ describe('Integration | integrationSettings', () => { }); describe('replaysSessionSampleRate', () => { - let mockConsole: jest.SpyInstance; + let mockConsole: MockInstance; beforeEach(() => { - mockConsole = jest.spyOn(console, 'warn').mockImplementation(jest.fn()); + mockConsole = vi.spyOn(console, 'warn').mockImplementation(vi.fn()); }); afterEach(() => { @@ -53,10 +56,10 @@ describe('Integration | integrationSettings', () => { }); describe('replaysOnErrorSampleRate', () => { - let mockConsole: jest.SpyInstance; + let mockConsole: MockInstance; beforeEach(() => { - mockConsole = jest.spyOn(console, 'warn').mockImplementation(jest.fn()); + mockConsole = vi.spyOn(console, 'warn').mockImplementation(vi.fn()); }); afterEach(() => { @@ -92,10 +95,10 @@ describe('Integration | integrationSettings', () => { }); describe('all sample rates', () => { - let mockConsole: jest.SpyInstance; + let mockConsole: MockInstance; beforeEach(() => { - mockConsole = jest.spyOn(console, 'warn').mockImplementation(jest.fn()); + mockConsole = vi.spyOn(console, 'warn').mockImplementation(vi.fn()); }); afterEach(() => { diff --git a/packages/replay-internal/test/integration/rateLimiting.test.ts b/packages/replay-internal/test/integration/rateLimiting.test.ts index 70cba8f35eff..01e52aa641ef 100644 --- a/packages/replay-internal/test/integration/rateLimiting.test.ts +++ b/packages/replay-internal/test/integration/rateLimiting.test.ts @@ -10,18 +10,18 @@ import { useFakeTimers } from '../utils/use-fake-timers'; useFakeTimers(); async function advanceTimers(time: number) { - jest.advanceTimersByTime(time); + vi.advanceTimersByTime(time); await new Promise(process.nextTick); } -type MockTransportSend = jest.MockedFunction; +type MockTransportSend = vi.MockedFunction; describe('Integration | rate-limiting behaviour', () => { let replay: ReplayContainer; let mockTransportSend: MockTransportSend; beforeEach(async () => { - jest.setSystemTime(new Date(BASE_TIMESTAMP)); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); ({ replay } = await mockSdk({ autoStart: false, @@ -35,7 +35,7 @@ describe('Integration | rate-limiting behaviour', () => { afterEach(async () => { clearSession(replay); - jest.clearAllMocks(); + vi.clearAllMocks(); replay && replay.stop(); }); @@ -61,7 +61,7 @@ describe('Integration | rate-limiting behaviour', () => { } as TransportMakeRequestResponse, ], ])('handles %s responses by stopping the replay', async (_name, { statusCode, headers }) => { - const mockStop = jest.spyOn(replay, 'stop'); + const mockStop = vi.spyOn(replay, 'stop'); mockTransportSend.mockImplementationOnce(() => { return Promise.resolve({ statusCode, headers }); @@ -93,7 +93,7 @@ describe('Integration | rate-limiting behaviour', () => { } as TransportMakeRequestResponse, ], ])('handles %s responses by not stopping', async (_name, { statusCode, headers }) => { - const mockStop = jest.spyOn(replay, 'stop'); + const mockStop = vi.spyOn(replay, 'stop'); mockTransportSend.mockImplementationOnce(() => { return Promise.resolve({ statusCode, headers }); diff --git a/packages/replay-internal/test/integration/rrweb.test.ts b/packages/replay-internal/test/integration/rrweb.test.ts index 4423c45246ea..1b571b356244 100644 --- a/packages/replay-internal/test/integration/rrweb.test.ts +++ b/packages/replay-internal/test/integration/rrweb.test.ts @@ -5,7 +5,7 @@ useFakeTimers(); describe('Integration | rrweb', () => { afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('calls rrweb.record with custom options', async () => { @@ -16,19 +16,19 @@ describe('Integration | rrweb', () => { }, }); expect(mockRecord.mock.calls[0][0]).toMatchInlineSnapshot(` - Object { - "blockSelector": ".sentry-block,[data-sentry-block],base[href=\\"/\\"],img,image,svg,video,object,picture,embed,map,audio,link[rel=\\"icon\\"],link[rel=\\"apple-touch-icon\\"]", + { + "blockSelector": ".sentry-block,[data-sentry-block],base[href="/"],img,image,svg,video,object,picture,embed,map,audio,link[rel="icon"],link[rel="apple-touch-icon"]", "collectFonts": true, "emit": [Function], "errorHandler": [Function], - "ignoreSelector": ".sentry-test-ignore,.sentry-ignore,[data-sentry-ignore],input[type=\\"file\\"]", + "ignoreSelector": ".sentry-test-ignore,.sentry-ignore,[data-sentry-ignore],input[type="file"]", "inlineImages": false, "inlineStylesheet": true, "maskAllInputs": true, "maskAllText": true, "maskAttributeFn": [Function], "maskInputFn": undefined, - "maskInputOptions": Object { + "maskInputOptions": { "password": true, }, "maskTextFn": undefined, diff --git a/packages/replay-internal/test/integration/sampling.test.ts b/packages/replay-internal/test/integration/sampling.test.ts index b82bb9538b5e..433a010b76aa 100644 --- a/packages/replay-internal/test/integration/sampling.test.ts +++ b/packages/replay-internal/test/integration/sampling.test.ts @@ -5,7 +5,7 @@ useFakeTimers(); describe('Integration | sampling', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('does nothing if not sampled', async () => { @@ -20,8 +20,8 @@ describe('Integration | sampling', () => { }); // @ts-expect-error private API - const spyAddListeners = jest.spyOn(replay, '_addListeners'); - jest.runAllTimers(); + const spyAddListeners = vi.spyOn(replay, '_addListeners'); + vi.runAllTimers(); expect(replay.session).toBe(undefined); expect(replay.eventBuffer).toBeNull(); @@ -55,12 +55,12 @@ describe('Integration | sampling', () => { }); // @ts-expect-error private API - const spyAddListeners = jest.spyOn(replay, '_addListeners'); + const spyAddListeners = vi.spyOn(replay, '_addListeners'); // @ts-expect-error protected integration._initialize(); - jest.runAllTimers(); + vi.runAllTimers(); expect(replay.session?.id).toBeDefined(); expect(replay.eventBuffer).toBeDefined(); @@ -70,9 +70,9 @@ describe('Integration | sampling', () => { expect(replay.getContext()).toEqual({ errorIds: new Set(), initialTimestamp: expect.any(Number), - initialUrl: 'http://localhost/', + initialUrl: 'http://localhost:3000/', traceIds: new Set(), - urls: ['http://localhost/'], + urls: ['http://localhost:3000/'], }); expect(replay.recordingMode).toBe('buffer'); diff --git a/packages/replay-internal/test/integration/sendReplayEvent.test.ts b/packages/replay-internal/test/integration/sendReplayEvent.test.ts index 58cdecaf1c65..725a26ea50ef 100644 --- a/packages/replay-internal/test/integration/sendReplayEvent.test.ts +++ b/packages/replay-internal/test/integration/sendReplayEvent.test.ts @@ -1,3 +1,6 @@ +import { vi } from 'vitest'; +import type { MockedFunction, MockInstance } from 'vitest'; + import * as SentryBrowserUtils from '@sentry-internal/browser-utils'; import * as SentryCore from '@sentry/core'; import type { Transport } from '@sentry/types'; @@ -14,23 +17,18 @@ import { useFakeTimers } from '../utils/use-fake-timers'; useFakeTimers(); -async function advanceTimers(time: number) { - jest.advanceTimersByTime(time); - await new Promise(process.nextTick); -} - -type MockTransportSend = jest.MockedFunction; +type MockTransportSend = MockedFunction; describe('Integration | sendReplayEvent', () => { let replay: ReplayContainer; let mockTransportSend: MockTransportSend; - let mockSendReplayRequest: jest.SpyInstance; + let mockSendReplayRequest: MockInstance; let domHandler: DomHandler; const { record: mockRecord } = mockRrweb(); beforeAll(async () => { - jest.setSystemTime(new Date(BASE_TIMESTAMP)); - jest.spyOn(SentryBrowserUtils, 'addClickKeypressInstrumentationHandler').mockImplementation(handler => { + vi.setSystemTime(new Date(BASE_TIMESTAMP)); + vi.spyOn(SentryBrowserUtils, 'addClickKeypressInstrumentationHandler').mockImplementation(handler => { domHandler = handler; }); @@ -45,14 +43,14 @@ describe('Integration | sendReplayEvent', () => { }, })); - mockSendReplayRequest = jest.spyOn(SendReplayRequest, 'sendReplayRequest'); + mockSendReplayRequest = vi.spyOn(SendReplayRequest, 'sendReplayRequest'); - jest.runAllTimers(); + await vi.runAllTimersAsync(); mockTransportSend = SentryCore.getClient()?.getTransport()?.send as MockTransportSend; }); beforeEach(() => { - jest.setSystemTime(new Date(BASE_TIMESTAMP)); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); mockRecord.takeFullSnapshot.mockClear(); mockTransportSend.mockClear(); @@ -66,9 +64,9 @@ describe('Integration | sendReplayEvent', () => { }); afterEach(async () => { - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); - jest.setSystemTime(new Date(BASE_TIMESTAMP)); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); clearSession(replay); }); @@ -87,7 +85,7 @@ describe('Integration | sendReplayEvent', () => { // Pretend 5 seconds have passed const ELAPSED = 5000; - jest.advanceTimersByTime(ELAPSED); + await vi.advanceTimersByTimeAsync(ELAPSED); const TEST_EVENT = getTestEventCheckout({ timestamp: BASE_TIMESTAMP }); addEvent(replay, TEST_EVENT); @@ -121,7 +119,7 @@ describe('Integration | sendReplayEvent', () => { // Pretend 5 seconds have passed const ELAPSED = 5000; - jest.advanceTimersByTime(ELAPSED); + vi.advanceTimersByTime(ELAPSED); domHandler({ name: 'click', @@ -143,7 +141,7 @@ describe('Integration | sendReplayEvent', () => { // Pretend 5 seconds have passed const ELAPSED = 5000; - jest.advanceTimersByTime(ELAPSED); + vi.advanceTimersByTime(ELAPSED); domHandler({ name: 'input', @@ -158,7 +156,7 @@ describe('Integration | sendReplayEvent', () => { mockRecord._emitter(TEST_EVENT); // Pretend 5 seconds have passed const ELAPSED = 5000; - await advanceTimers(ELAPSED); + await vi.advanceTimersByTimeAsync(ELAPSED); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); @@ -177,7 +175,7 @@ describe('Integration | sendReplayEvent', () => { // Fire a new event every 4 seconds, 4 times for (let i = 0; i < 4; i++) { mockRecord._emitter(TEST_EVENT); - jest.advanceTimersByTime(4_000); + vi.advanceTimersByTime(4_000); } // We are at time = +16seconds now (relative to BASE_TIMESTAMP) @@ -191,7 +189,7 @@ describe('Integration | sendReplayEvent', () => { // There should also not be another attempt at an upload 5 seconds after the last replay event mockTransportSend.mockClear(); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(replay).not.toHaveLastSentReplay(); expect(replay.session?.lastActivity).toBe(BASE_TIMESTAMP); @@ -202,7 +200,7 @@ describe('Integration | sendReplayEvent', () => { // Let's make sure it continues to work mockTransportSend.mockClear(); mockRecord._emitter(TEST_EVENT); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(replay).toHaveLastSentReplay({ recordingData: JSON.stringify([TEST_EVENT]) }); }); @@ -216,7 +214,7 @@ describe('Integration | sendReplayEvent', () => { // Pretend 5 seconds have passed const ELAPSED = 5000; - jest.advanceTimersByTime(ELAPSED); + vi.advanceTimersByTime(ELAPSED); const TEST_EVENT = getTestEventCheckout({ timestamp: BASE_TIMESTAMP }); const hiddenBreadcrumb = { @@ -254,13 +252,13 @@ describe('Integration | sendReplayEvent', () => { }); // Pretend 5 seconds have passed const ELAPSED = 5000; - jest.advanceTimersByTime(ELAPSED); + vi.advanceTimersByTime(ELAPSED); const TEST_EVENT = getTestEventCheckout({ timestamp: BASE_TIMESTAMP }); addEvent(replay, TEST_EVENT); document.dispatchEvent(new Event('visibilitychange')); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); @@ -281,7 +279,7 @@ describe('Integration | sendReplayEvent', () => { // Pretend 5 seconds have passed const ELAPSED = 5000; - await advanceTimers(ELAPSED); + await vi.advanceTimersByTimeAsync(ELAPSED); expect(replay).toHaveLastSentReplay({ recordingData: JSON.stringify([ @@ -310,26 +308,26 @@ describe('Integration | sendReplayEvent', () => { const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); // Suppress console.errors - const mockConsole = jest.spyOn(console, 'error').mockImplementation(jest.fn()); + const mockConsole = vi.spyOn(console, 'error').mockImplementation(vi.fn()); // fail the first and second requests and pass the third one mockTransportSend.mockImplementationOnce(() => { throw new Error('Something bad happened'); }); mockRecord._emitter(TEST_EVENT); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); mockTransportSend.mockImplementationOnce(() => { throw new Error('Something bad happened'); }); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); // next tick should retry and succeed mockConsole.mockRestore(); - await advanceTimers(8000); - await advanceTimers(2000); + await vi.advanceTimersByTimeAsync(8000); + await vi.advanceTimersByTimeAsync(2000); expect(replay).toHaveLastSentReplay({ replayEventPayload: expect.objectContaining({ @@ -339,7 +337,7 @@ describe('Integration | sendReplayEvent', () => { // timestamp is set on first try, after 5s flush timestamp: (BASE_TIMESTAMP + 5000) / 1000, trace_ids: [], - urls: ['http://localhost/'], + urls: ['http://localhost:3000/'], }), recordingPayloadHeader: { segment_id: 0 }, recordingData: JSON.stringify([TEST_EVENT]), @@ -351,17 +349,17 @@ describe('Integration | sendReplayEvent', () => { expect(replay.session?.segmentId).toBe(1); // next tick should do nothing - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(replay).not.toHaveLastSentReplay(); }); it('fails to upload data and hits retry max and stops', async () => { const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); - const spyHandleException = jest.spyOn(SentryCore, 'captureException'); + const spyHandleException = vi.spyOn(SentryCore, 'captureException'); // Suppress console.errors - const mockConsole = jest.spyOn(console, 'error').mockImplementation(jest.fn()); + const mockConsole = vi.spyOn(console, 'error').mockImplementation(vi.fn()); expect(replay.session?.segmentId).toBe(0); @@ -371,24 +369,24 @@ describe('Integration | sendReplayEvent', () => { }); mockRecord._emitter(TEST_EVENT); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); expect(mockSendReplayRequest).toHaveBeenCalledTimes(1); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(mockSendReplayRequest).toHaveBeenCalledTimes(2); - await advanceTimers(10000); + await vi.advanceTimersByTimeAsync(10000); expect(mockSendReplayRequest).toHaveBeenCalledTimes(3); - await advanceTimers(30000); + await vi.advanceTimersByTimeAsync(30000); expect(mockSendReplayRequest).toHaveBeenCalledTimes(4); mockConsole.mockReset(); // Make sure it doesn't retry again - jest.runAllTimers(); + await vi.runAllTimersAsync(); expect(mockSendReplayRequest).toHaveBeenCalledTimes(4); // Retries = 3 (total tries = 4 including initial attempt) @@ -407,7 +405,7 @@ describe('Integration | sendReplayEvent', () => { // Events are ignored now, because we stopped mockRecord._emitter(TEST_EVENT); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(mockSendReplayRequest).toHaveBeenCalledTimes(4); }); diff --git a/packages/replay-internal/test/integration/session.test.ts b/packages/replay-internal/test/integration/session.test.ts index 0485fa78dd95..678450104877 100644 --- a/packages/replay-internal/test/integration/session.test.ts +++ b/packages/replay-internal/test/integration/session.test.ts @@ -1,3 +1,5 @@ +import { vi } from 'vitest'; + import { getClient } from '@sentry/core'; import type { Transport } from '@sentry/types'; @@ -24,11 +26,6 @@ import { useFakeTimers } from '../utils/use-fake-timers'; useFakeTimers(); -async function advanceTimers(time: number) { - jest.advanceTimersByTime(time); - await new Promise(process.nextTick); -} - const prevLocation = WINDOW.location; describe('Integration | session', () => { @@ -43,16 +40,16 @@ describe('Integration | session', () => { }, })); - const mockTransport = getClient()?.getTransport()?.send as jest.MockedFunction; + const mockTransport = getClient()?.getTransport()?.send as vi.MockedFunction; mockTransport?.mockClear(); + await vi.runAllTimersAsync(); }); afterEach(async () => { replay.stop(); - jest.runAllTimers(); - await new Promise(process.nextTick); - jest.setSystemTime(new Date(BASE_TIMESTAMP)); + await vi.runAllTimersAsync(); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); Object.defineProperty(WINDOW, 'location', { value: prevLocation, @@ -72,7 +69,7 @@ describe('Integration | session', () => { const initialSession = { ...replay.session } as Session; - jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); + vi.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); document.dispatchEvent(new Event('visibilitychange')); @@ -83,7 +80,7 @@ describe('Integration | session', () => { it('does not create a new session when document becomes focused after [SESSION_IDLE_EXPIRE_DURATION]ms', () => { const initialSession = { ...replay.session } as Session; - jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); + vi.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); WINDOW.dispatchEvent(new Event('focus')); @@ -105,7 +102,7 @@ describe('Integration | session', () => { expect(replay).toHaveSameSession(initialSession); // User comes back before `SESSION_IDLE_EXPIRE_DURATION` elapses - jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION - 1); + vi.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION - 1); Object.defineProperty(document, 'visibilityState', { configurable: true, get: function () { @@ -126,7 +123,7 @@ describe('Integration | session', () => { expect(initialSession?.id).toBeDefined(); expect(replay.getContext()).toEqual( expect.objectContaining({ - initialUrl: 'http://localhost/', + initialUrl: 'http://localhost:3000/', initialTimestamp: BASE_TIMESTAMP, }), ); @@ -137,7 +134,7 @@ describe('Integration | session', () => { }); const ELAPSED = SESSION_IDLE_EXPIRE_DURATION + 1; - jest.advanceTimersByTime(ELAPSED); + vi.advanceTimersByTime(ELAPSED); // Session has become in an idle state // @@ -203,10 +200,10 @@ describe('Integration | session', () => { // Replay does not send immediately because checkout was due to expired session expect(replay).not.toHaveLastSentReplay(); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); await new Promise(process.nextTick); - const newTimestamp = BASE_TIMESTAMP + ELAPSED + 20; + const newTimestamp = BASE_TIMESTAMP + ELAPSED; expect(replay).toHaveLastSentReplay({ recordingPayloadHeader: { segment_id: 0 }, @@ -236,7 +233,7 @@ describe('Integration | session', () => { expect(initialSession?.id).toBeDefined(); expect(replay.getContext()).toEqual( expect.objectContaining({ - initialUrl: 'http://localhost/', + initialUrl: 'http://localhost:3000/', initialTimestamp: BASE_TIMESTAMP, }), ); @@ -247,7 +244,7 @@ describe('Integration | session', () => { }); const ELAPSED = SESSION_IDLE_PAUSE_DURATION + 1; - jest.advanceTimersByTime(ELAPSED); + vi.advanceTimersByTime(ELAPSED); // Session has become in an idle state // @@ -305,7 +302,7 @@ describe('Integration | session', () => { // Replay does not send immediately expect(replay).not.toHaveLastSentReplay(); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(replay).toHaveLastSentReplay(); }); @@ -326,17 +323,15 @@ describe('Integration | session', () => { }); it('creates a new session if current session exceeds MAX_REPLAY_DURATION', async () => { - jest.clearAllMocks(); + vi.clearAllMocks(); const initialSession = { ...replay.session } as Session; expect(initialSession?.id).toBeDefined(); - expect(replay.getContext()).toEqual( - expect.objectContaining({ - initialUrl: 'http://localhost/', - initialTimestamp: BASE_TIMESTAMP, - }), - ); + expect(replay.getContext()).toMatchObject({ + initialUrl: 'http://localhost:3000/', + initialTimestamp: BASE_TIMESTAMP, + }); const url = 'http://dummy/'; Object.defineProperty(WINDOW, 'location', { @@ -345,7 +340,7 @@ describe('Integration | session', () => { // Advanced past MAX_REPLAY_DURATION const ELAPSED = MAX_REPLAY_DURATION + 1; - jest.advanceTimersByTime(ELAPSED); + await vi.advanceTimersByTimeAsync(ELAPSED); // Update activity so as to not consider session to be idling replay['_updateUserActivity'](); replay['_updateSessionActivity'](); @@ -360,10 +355,9 @@ describe('Integration | session', () => { const optionsEvent = createOptionsEvent(replay); const timestampAtRefresh = BASE_TIMESTAMP + ELAPSED; - jest.runAllTimers(); - await new Promise(process.nextTick); + await vi.runAllTimersAsync(); - expect(replay).not.toHaveSameSession(initialSession); + expect(replay.session.id).not.toBe(initialSession.id); expect(replay).not.toHaveLastSentReplay(); expect(replay['_stopRecording']).toBeDefined(); @@ -373,17 +367,15 @@ describe('Integration | session', () => { event: new Event('click'), }); - // 20 is for the process.nextTick - const newTimestamp = timestampAtRefresh + 20; + const newTimestamp = timestampAtRefresh; const NEW_TEST_EVENT = getTestEventIncremental({ data: { name: 'test' }, - timestamp: newTimestamp + DEFAULT_FLUSH_MIN_DELAY + 20, + timestamp: newTimestamp + DEFAULT_FLUSH_MIN_DELAY, }); mockRecord._emitter(NEW_TEST_EVENT); - jest.runAllTimers(); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); expect(replay).toHaveLastSentReplay({ recordingPayloadHeader: { segment_id: 0 }, @@ -431,7 +423,7 @@ describe('Integration | session', () => { // Pretend 5 seconds have passed const ELAPSED = 5000; - await advanceTimers(ELAPSED); + await vi.advanceTimersByTimeAsync(ELAPSED); const TEST_EVENT = getTestEventCheckout({ timestamp: BASE_TIMESTAMP }); @@ -445,7 +437,7 @@ describe('Integration | session', () => { addEvent(replay, TEST_EVENT); WINDOW.dispatchEvent(new Event('blur')); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(replay.session?.segmentId).toBe(2); expect(replay).toHaveLastSentReplay({ diff --git a/packages/replay-internal/test/integration/shouldFilterRequest.test.ts b/packages/replay-internal/test/integration/shouldFilterRequest.test.ts index 888b30e2c25f..207a8182581f 100644 --- a/packages/replay-internal/test/integration/shouldFilterRequest.test.ts +++ b/packages/replay-internal/test/integration/shouldFilterRequest.test.ts @@ -3,7 +3,7 @@ import { mockSdk } from '../index'; describe('Integration | shouldFilterRequest', () => { beforeEach(() => { - jest.resetModules(); + vi.resetModules(); }); it('should not filter requests from non-Sentry ingest URLs', async () => { diff --git a/packages/replay-internal/test/integration/stop.test.ts b/packages/replay-internal/test/integration/stop.test.ts index 3afbe3eeef1a..38af135611bb 100644 --- a/packages/replay-internal/test/integration/stop.test.ts +++ b/packages/replay-internal/test/integration/stop.test.ts @@ -1,3 +1,6 @@ +import { vi } from 'vitest'; +import type { MockedFunction, MockInstance } from 'vitest'; + import * as SentryBrowserUtils from '@sentry-internal/browser-utils'; import { WINDOW } from '../../src/constants'; @@ -13,7 +16,7 @@ import { useFakeTimers } from '../utils/use-fake-timers'; useFakeTimers(); -type MockRunFlush = jest.MockedFunction; +type MockRunFlush = MockedFunction; describe('Integration | stop', () => { let replay: ReplayContainer; @@ -22,31 +25,31 @@ describe('Integration | stop', () => { const { record: mockRecord } = mockRrweb(); - let mockAddDomInstrumentationHandler: jest.SpyInstance; + let mockAddDomInstrumentationHandler: MockInstance; let mockRunFlush: MockRunFlush; beforeAll(async () => { - jest.setSystemTime(new Date(BASE_TIMESTAMP)); - mockAddDomInstrumentationHandler = jest.spyOn(SentryBrowserUtils, 'addClickKeypressInstrumentationHandler'); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); + mockAddDomInstrumentationHandler = vi.spyOn(SentryBrowserUtils, 'addClickKeypressInstrumentationHandler'); ({ replay, integration } = await mockSdk()); // @ts-expect-error private API - mockRunFlush = jest.spyOn(replay, '_runFlush'); + mockRunFlush = vi.spyOn(replay, '_runFlush'); - jest.runAllTimers(); + vi.runAllTimers(); }); beforeEach(() => { - jest.setSystemTime(new Date(BASE_TIMESTAMP)); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); replay.eventBuffer?.destroy(); - jest.clearAllMocks(); + vi.clearAllMocks(); }); afterEach(async () => { - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); - jest.setSystemTime(new Date(BASE_TIMESTAMP)); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); sessionStorage.clear(); clearSession(replay); replay['_initializeSessionForSampling'](); @@ -71,8 +74,6 @@ describe('Integration | stop', () => { }, }); const ELAPSED = 5000; - // Not sure where the 20ms comes from tbh - const EXTRA_TICKS = 20; const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); const previousSessionId = replay.session?.id; @@ -80,7 +81,7 @@ describe('Integration | stop', () => { await integration.stop(); // Pretend 5 seconds have passed - jest.advanceTimersByTime(ELAPSED); + vi.advanceTimersByTime(ELAPSED); addEvent(replay, TEST_EVENT); WINDOW.dispatchEvent(new Event('blur')); @@ -99,9 +100,9 @@ describe('Integration | stop', () => { // will be different session expect(replay.session?.id).not.toEqual(previousSessionId); - jest.advanceTimersByTime(ELAPSED); + vi.advanceTimersByTime(ELAPSED); - const timestamp = +new Date(BASE_TIMESTAMP + ELAPSED + ELAPSED + EXTRA_TICKS) / 1000; + const timestamp = +new Date(BASE_TIMESTAMP + ELAPSED + ELAPSED) / 1000; const hiddenBreadcrumb = { type: 5, @@ -118,7 +119,7 @@ describe('Integration | stop', () => { addEvent(replay, TEST_EVENT); WINDOW.dispatchEvent(new Event('blur')); - jest.runAllTimers(); + vi.runAllTimers(); await new Promise(process.nextTick); expect(replay).toHaveLastSentReplay({ recordingPayloadHeader: { segment_id: 0 }, @@ -126,7 +127,7 @@ describe('Integration | stop', () => { // This event happens when we call `replay.start` { data: { isCheckout: true }, - timestamp: BASE_TIMESTAMP + ELAPSED + EXTRA_TICKS, + timestamp: BASE_TIMESTAMP + ELAPSED, type: 2, }, optionsEvent, @@ -137,7 +138,7 @@ describe('Integration | stop', () => { // Session's last activity is last updated when we call `setup()` and *NOT* // when tab is blurred - expect(replay.session?.lastActivity).toBe(BASE_TIMESTAMP + ELAPSED + 20); + expect(replay.session?.lastActivity).toBe(BASE_TIMESTAMP + ELAPSED); }); it('does not buffer new events after being stopped', async function () { diff --git a/packages/replay-internal/test/mocks/mockRrweb.ts b/packages/replay-internal/test/mocks/mockRrweb.ts index 30c5557298fc..bdffef9b7c32 100644 --- a/packages/replay-internal/test/mocks/mockRrweb.ts +++ b/packages/replay-internal/test/mocks/mockRrweb.ts @@ -1,10 +1,40 @@ -import type { record as rrwebRecord } from '@sentry-internal/rrweb'; +import { vi } from 'vitest'; +import type { Mock, MockedFunction } from 'vitest'; + +import { record } from '@sentry-internal/rrweb'; import type { RecordingEvent, ReplayEventWithTime } from '../../src/types'; import { ReplayEventTypeFullSnapshot, ReplayEventTypeIncrementalSnapshot } from '../../src/types'; +vi.mock('@sentry-internal/rrweb', async () => { + const mockRecordFn: Mock & Partial = vi.fn(({ emit }) => { + mockRecordFn._emitter = emit; + + emit(createCheckoutPayload()); + return function stop() { + mockRecordFn._emitter = vi.fn(); + }; + }); + mockRecordFn.takeFullSnapshot = vi.fn((isCheckout: boolean) => { + if (!mockRecordFn._emitter) { + return; + } + + mockRecordFn._emitter(createCheckoutPayload(isCheckout), isCheckout); + }); + + const ActualRrweb = await vi.importActual('@sentry-internal/rrweb'); + + mockRecordFn.mirror = ActualRrweb.record.mirror; + + return { + ...ActualRrweb, + record: mockRecordFn, + }; +}); + type RecordAdditionalProperties = { - takeFullSnapshot: jest.Mock; + takeFullSnapshot: Mock; // Below are not mocked addCustomEvent: () => void; @@ -15,7 +45,7 @@ type RecordAdditionalProperties = { _emitter: (event: RecordingEvent, ...args: any[]) => void; }; -export type RecordMock = jest.MockedFunction & RecordAdditionalProperties; +export type RecordMock = MockedFunction & RecordAdditionalProperties; function createCheckoutPayload(isCheckout: boolean = true): ReplayEventWithTime { return { @@ -26,34 +56,7 @@ function createCheckoutPayload(isCheckout: boolean = true): ReplayEventWithTime } export function mockRrweb(): { record: RecordMock } { - const mockRecordFn: jest.Mock & Partial = jest.fn(({ emit }) => { - mockRecordFn._emitter = emit; - - emit(createCheckoutPayload()); - return function stop() { - mockRecordFn._emitter = jest.fn(); - }; - }); - mockRecordFn.takeFullSnapshot = jest.fn((isCheckout: boolean) => { - if (!mockRecordFn._emitter) { - return; - } - - mockRecordFn._emitter(createCheckoutPayload(isCheckout), isCheckout); - }); - - jest.mock('@sentry-internal/rrweb', () => { - const ActualRrweb = jest.requireActual('@sentry-internal/rrweb'); - - mockRecordFn.mirror = ActualRrweb.record.mirror; - - return { - ...ActualRrweb, - record: mockRecordFn, - }; - }); - return { - record: mockRecordFn as RecordMock, + record: record as RecordMock, }; } diff --git a/packages/replay-internal/test/mocks/mockSdk.ts b/packages/replay-internal/test/mocks/mockSdk.ts index f57ef8c51d71..512e70950136 100644 --- a/packages/replay-internal/test/mocks/mockSdk.ts +++ b/packages/replay-internal/test/mocks/mockSdk.ts @@ -1,9 +1,11 @@ +import { vi } from 'vitest'; import type { Envelope, Transport, TransportMakeRequestResponse } from '@sentry/types'; import type { Replay as ReplayIntegration } from '../../src/integration'; import type { ReplayContainer } from '../../src/replay'; import type { ReplayConfiguration } from '../../src/types'; import type { TestClientOptions } from '../utils/TestClient'; +import { BASE_TIMESTAMP } from './../index'; import { getDefaultClientOptions, init } from '../utils/TestClient'; export interface MockSdkParams { @@ -16,7 +18,7 @@ class MockTransport implements Transport { send: (request: Envelope) => PromiseLike; constructor() { - this.send = jest.fn(async () => { + this.send = vi.fn(async () => { return { statusCode: 200, }; @@ -50,6 +52,7 @@ export async function mockSdk({ replayOptions, sentryOptions, autoStart = true } }> { const { Replay } = await import('../../src/integration'); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); // Scope this to the test, instead of the module let _initialized = false; class TestReplayIntegration extends Replay { diff --git a/packages/replay-internal/test/mocks/resetSdkMock.ts b/packages/replay-internal/test/mocks/resetSdkMock.ts index 47a1522a4c63..e5db1657ac12 100644 --- a/packages/replay-internal/test/mocks/resetSdkMock.ts +++ b/packages/replay-internal/test/mocks/resetSdkMock.ts @@ -1,3 +1,4 @@ +import { vi } from 'vitest'; import { resetInstrumentationHandlers } from '@sentry/utils'; import type { Replay as ReplayIntegration } from '../../src/integration'; @@ -16,15 +17,15 @@ export async function resetSdkMock({ replayOptions, sentryOptions, autoStart }: }> { let domHandler: DomHandler; - jest.setSystemTime(new Date(BASE_TIMESTAMP)); - jest.clearAllMocks(); - jest.resetModules(); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); + vi.clearAllMocks(); + vi.resetModules(); // Clear all handlers that have been registered resetInstrumentationHandlers(); const SentryBrowserUtils = await import('@sentry-internal/browser-utils'); - jest.spyOn(SentryBrowserUtils, 'addClickKeypressInstrumentationHandler').mockImplementation(handler => { + vi.spyOn(SentryBrowserUtils, 'addClickKeypressInstrumentationHandler').mockImplementation(handler => { domHandler = handler; }); const { mockRrweb } = await import('./mockRrweb'); @@ -37,9 +38,10 @@ export async function resetSdkMock({ replayOptions, sentryOptions, autoStart }: }); // XXX: This is needed to ensure `domHandler` is set - jest.runAllTimers(); - await new Promise(process.nextTick); - jest.setSystemTime(new Date(BASE_TIMESTAMP)); + await vi.runAllTimersAsync(); + // vi.runAllTimers(); + // await new Promise(process.nextTick); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); return { // @ts-expect-error use before assign diff --git a/packages/replay-internal/test/unit/coreHandlers/handleClick.test.ts b/packages/replay-internal/test/unit/coreHandlers/handleClick.test.ts index ae52d2076293..07ef281800c1 100644 --- a/packages/replay-internal/test/unit/coreHandlers/handleClick.test.ts +++ b/packages/replay-internal/test/unit/coreHandlers/handleClick.test.ts @@ -4,12 +4,12 @@ import { BASE_TIMESTAMP } from '../..'; import { ClickDetector, ignoreElement } from '../../../src/coreHandlers/handleClick'; import type { ReplayContainer } from '../../../src/types'; -jest.useFakeTimers(); +vi.useFakeTimers(); describe('Unit | coreHandlers | handleClick', () => { describe('ClickDetector', () => { beforeEach(() => { - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); }); test('it captures a single click', async () => { @@ -17,7 +17,7 @@ describe('Unit | coreHandlers | handleClick', () => { getCurrentRoute: () => 'test-route', } as ReplayContainer; - const mockAddBreadcrumbEvent = jest.fn(); + const mockAddBreadcrumbEvent = vi.fn(); const detector = new ClickDetector( replay, @@ -41,15 +41,15 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(1); expect(mockAddBreadcrumbEvent).toHaveBeenCalledWith(replay, { @@ -61,13 +61,13 @@ describe('Unit | coreHandlers | handleClick', () => { nodeId: 1, route: 'test-route', timeAfterClickMs: 3000, - url: 'http://localhost/', + url: 'http://localhost:3000/', }, message: undefined, timestamp: expect.any(Number), }); - jest.advanceTimersByTime(5_000); + vi.advanceTimersByTime(5_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(1); }); @@ -76,7 +76,7 @@ describe('Unit | coreHandlers | handleClick', () => { getCurrentRoute: () => 'test-route', } as ReplayContainer; - const mockAddBreadcrumbEvent = jest.fn(); + const mockAddBreadcrumbEvent = vi.fn(); const detector = new ClickDetector( replay, @@ -114,15 +114,15 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(1); expect(mockAddBreadcrumbEvent).toHaveBeenCalledWith(replay, { @@ -134,13 +134,13 @@ describe('Unit | coreHandlers | handleClick', () => { nodeId: 1, route: 'test-route', timeAfterClickMs: 3000, - url: 'http://localhost/', + url: 'http://localhost:3000/', }, message: undefined, timestamp: BASE_TIMESTAMP / 1000, }); - jest.advanceTimersByTime(2_000); + vi.advanceTimersByTime(2_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(2); expect(mockAddBreadcrumbEvent).toHaveBeenLastCalledWith(replay, { @@ -152,13 +152,13 @@ describe('Unit | coreHandlers | handleClick', () => { nodeId: 1, route: 'test-route', timeAfterClickMs: 3000, - url: 'http://localhost/', + url: 'http://localhost:3000/', }, message: undefined, timestamp: (BASE_TIMESTAMP + 1200) / 1000, }); - jest.advanceTimersByTime(5_000); + vi.advanceTimersByTime(5_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(2); }); @@ -167,7 +167,7 @@ describe('Unit | coreHandlers | handleClick', () => { getCurrentRoute: () => 'test-route', } as ReplayContainer; - const mockAddBreadcrumbEvent = jest.fn(); + const mockAddBreadcrumbEvent = vi.fn(); const detector = new ClickDetector( replay, @@ -207,11 +207,11 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(3_000); + vi.advanceTimersByTime(3_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(3); - jest.advanceTimersByTime(5_000); + vi.advanceTimersByTime(5_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(3); }); @@ -220,7 +220,7 @@ describe('Unit | coreHandlers | handleClick', () => { getCurrentRoute: () => 'test-route', } as ReplayContainer; - const mockAddBreadcrumbEvent = jest.fn(); + const mockAddBreadcrumbEvent = vi.fn(); const detector = new ClickDetector( replay, @@ -260,23 +260,23 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(3_000); + vi.advanceTimersByTime(3_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); }); describe('mutations', () => { let detector: ClickDetector; - let mockAddBreadcrumbEvent = jest.fn(); + let mockAddBreadcrumbEvent = vi.fn(); const replay = { getCurrentRoute: () => 'test-route', } as ReplayContainer; beforeEach(() => { - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); - mockAddBreadcrumbEvent = jest.fn(); + mockAddBreadcrumbEvent = vi.fn(); detector = new ClickDetector( replay, @@ -302,14 +302,14 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(500); + vi.advanceTimersByTime(500); // Pretend a mutation happend detector['_lastMutation'] = BASE_TIMESTAMP / 1000 + 0.5; expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(3_000); + vi.advanceTimersByTime(3_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); }); @@ -326,14 +326,14 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); // Pretend a mutation happend detector['_lastMutation'] = BASE_TIMESTAMP / 1000 + 2; expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(3_000); + vi.advanceTimersByTime(3_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(1); expect(mockAddBreadcrumbEvent).toHaveBeenCalledWith(replay, { @@ -345,13 +345,13 @@ describe('Unit | coreHandlers | handleClick', () => { nodeId: 1, route: 'test-route', timeAfterClickMs: 2000, - url: 'http://localhost/', + url: 'http://localhost:3000/', }, message: undefined, timestamp: expect.any(Number), }); - jest.advanceTimersByTime(5_000); + vi.advanceTimersByTime(5_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(1); }); @@ -367,14 +367,14 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); // Pretend a mutation happend detector['_lastMutation'] = BASE_TIMESTAMP / 1000 + 5; expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(5_000); + vi.advanceTimersByTime(5_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(1); expect(mockAddBreadcrumbEvent).toHaveBeenCalledWith(replay, { @@ -386,29 +386,29 @@ describe('Unit | coreHandlers | handleClick', () => { nodeId: 1, route: 'test-route', timeAfterClickMs: 3000, - url: 'http://localhost/', + url: 'http://localhost:3000/', }, message: undefined, timestamp: expect.any(Number), }); - jest.advanceTimersByTime(5_000); + vi.advanceTimersByTime(5_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(1); }); }); describe('scroll', () => { let detector: ClickDetector; - let mockAddBreadcrumbEvent = jest.fn(); + let mockAddBreadcrumbEvent = vi.fn(); const replay = { getCurrentRoute: () => 'test-route', } as ReplayContainer; beforeEach(() => { - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); - mockAddBreadcrumbEvent = jest.fn(); + mockAddBreadcrumbEvent = vi.fn(); detector = new ClickDetector( replay, @@ -434,14 +434,14 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(100); + vi.advanceTimersByTime(100); // Pretend a mutation happend detector['_lastScroll'] = BASE_TIMESTAMP / 1000 + 0.15; expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(3_000); + vi.advanceTimersByTime(3_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); }); @@ -458,14 +458,14 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(300); + vi.advanceTimersByTime(300); // Pretend a mutation happend detector['_lastScroll'] = BASE_TIMESTAMP / 1000 + 0.3; expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(3_000); + vi.advanceTimersByTime(3_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(1); expect(mockAddBreadcrumbEvent).toHaveBeenCalledWith(replay, { @@ -477,13 +477,13 @@ describe('Unit | coreHandlers | handleClick', () => { nodeId: 1, route: 'test-route', timeAfterClickMs: 3000, - url: 'http://localhost/', + url: 'http://localhost:3000/', }, message: undefined, timestamp: expect.any(Number), }); - jest.advanceTimersByTime(5_000); + vi.advanceTimersByTime(5_000); expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/replay-internal/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts b/packages/replay-internal/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts index b3522c0ceb6c..ef81feef3a8e 100644 --- a/packages/replay-internal/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts +++ b/packages/replay-internal/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts @@ -14,7 +14,7 @@ import type { EventBufferArray } from '../../../src/eventBuffer/EventBufferArray import type { ReplayContainer, ReplayNetworkOptions } from '../../../src/types'; import { setupReplayContainer } from '../../utils/setupReplayContainer'; -jest.useFakeTimers(); +vi.useFakeTimers(); async function waitForReplayEventBuffer() { // Need one Promise.resolve() per await in the util functions @@ -56,7 +56,7 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => { }; beforeEach(() => { - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); options = { replay: setupReplayContainer(), @@ -67,7 +67,7 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => { networkResponseHeaders: ['content-type', 'accept', 'x-custom-header'], }; - jest.runAllTimers(); + vi.runAllTimers(); }); it('ignores breadcrumb without data', async () => { diff --git a/packages/replay-internal/test/unit/coreHandlers/util/addBreadcrumbEvent.test.ts b/packages/replay-internal/test/unit/coreHandlers/util/addBreadcrumbEvent.test.ts index 45483ebab5b4..1979556b3dc9 100644 --- a/packages/replay-internal/test/unit/coreHandlers/util/addBreadcrumbEvent.test.ts +++ b/packages/replay-internal/test/unit/coreHandlers/util/addBreadcrumbEvent.test.ts @@ -3,11 +3,11 @@ import { addBreadcrumbEvent } from '../../../../src/coreHandlers/util/addBreadcr import type { EventBufferArray } from '../../../../src/eventBuffer/EventBufferArray'; import { setupReplayContainer } from '../../../utils/setupReplayContainer'; -jest.useFakeTimers(); +vi.useFakeTimers(); describe('Unit | coreHandlers | util | addBreadcrumbEvent', function () { beforeEach(function () { - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); }); it('handles circular references', async () => { diff --git a/packages/replay-internal/test/unit/coreHandlers/util/networkUtils.test.ts b/packages/replay-internal/test/unit/coreHandlers/util/networkUtils.test.ts index 8f240c8ea7a7..4a330081507a 100644 --- a/packages/replay-internal/test/unit/coreHandlers/util/networkUtils.test.ts +++ b/packages/replay-internal/test/unit/coreHandlers/util/networkUtils.test.ts @@ -1,3 +1,5 @@ +import { vi } from 'vitest'; + import { NETWORK_BODY_MAX_SIZE } from '../../../../src/constants'; import { buildNetworkRequestOrResponse, @@ -7,7 +9,7 @@ import { parseContentLengthHeader, } from '../../../../src/coreHandlers/util/networkUtils'; -jest.useFakeTimers(); +vi.useFakeTimers(); describe('Unit | coreHandlers | util | networkUtils', () => { describe('parseContentLengthHeader()', () => { diff --git a/packages/replay-internal/test/unit/coreHandlers/util/xhrUtils.test.ts b/packages/replay-internal/test/unit/coreHandlers/util/xhrUtils.test.ts index d39b473f317f..83086be6bf3c 100644 --- a/packages/replay-internal/test/unit/coreHandlers/util/xhrUtils.test.ts +++ b/packages/replay-internal/test/unit/coreHandlers/util/xhrUtils.test.ts @@ -8,12 +8,10 @@ describe('Unit | coreHandlers | util | xhrUtils', () => { }); it('works with a Document', () => { - const body = document.implementation.createHTMLDocument(); - const bodyEl = document.createElement('body'); - bodyEl.innerHTML = '
abc
'; - body.body = bodyEl; + const doc = document.implementation.createHTMLDocument(); + doc.body.innerHTML = '
abc
'; - const actual = _parseXhrResponse(body, ''); + const actual = _parseXhrResponse(doc, ''); expect(actual).toEqual(['
abc
']); }); diff --git a/packages/replay-internal/test/unit/eventBuffer/EventBufferCompressionWorker.test.ts b/packages/replay-internal/test/unit/eventBuffer/EventBufferCompressionWorker.test.ts index 6b314a0d1b24..5854a4fbaa6e 100644 --- a/packages/replay-internal/test/unit/eventBuffer/EventBufferCompressionWorker.test.ts +++ b/packages/replay-internal/test/unit/eventBuffer/EventBufferCompressionWorker.test.ts @@ -120,7 +120,7 @@ describe('Unit | eventBuffer | EventBufferCompressionWorker', () => { await buffer.addEvent(TEST_EVENT); // @ts-expect-error Mock this private so it triggers an error - jest.spyOn(buffer._compression._worker, 'postMessage').mockImplementationOnce(() => { + vi.spyOn(buffer._compression._worker, 'postMessage').mockImplementationOnce(() => { return Promise.reject('test worker error'); }); @@ -141,7 +141,7 @@ describe('Unit | eventBuffer | EventBufferCompressionWorker', () => { await buffer.addEvent({ data: { o: 2 }, timestamp: BASE_TIMESTAMP, type: 3 }); // @ts-expect-error Mock this private so it triggers an error - jest.spyOn(buffer._compression._worker, 'postMessage').mockImplementationOnce(() => { + vi.spyOn(buffer._compression._worker, 'postMessage').mockImplementationOnce(() => { return Promise.reject('test worker error'); }); diff --git a/packages/replay-internal/test/unit/eventBuffer/EventBufferProxy.test.ts b/packages/replay-internal/test/unit/eventBuffer/EventBufferProxy.test.ts index 101d061037dc..819813776c5d 100644 --- a/packages/replay-internal/test/unit/eventBuffer/EventBufferProxy.test.ts +++ b/packages/replay-internal/test/unit/eventBuffer/EventBufferProxy.test.ts @@ -9,11 +9,11 @@ import { createEventBuffer } from './../../../src/eventBuffer'; const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); describe('Unit | eventBuffer | EventBufferProxy', () => { - let consoleErrorSpy: jest.SpyInstance; + let consoleErrorSpy: MockInstance; beforeEach(() => { // Avoid logging errors to console - consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); }); afterEach(() => { diff --git a/packages/replay-internal/test/unit/session/createSession.test.ts b/packages/replay-internal/test/unit/session/createSession.test.ts index 7713e821e74c..f6e6850754a5 100644 --- a/packages/replay-internal/test/unit/session/createSession.test.ts +++ b/packages/replay-internal/test/unit/session/createSession.test.ts @@ -1,3 +1,5 @@ +import { vi } from 'vitest'; + import * as Sentry from '@sentry/core'; import type { Hub } from '@sentry/types'; @@ -5,23 +7,23 @@ import { WINDOW } from '../../../src/constants'; import { createSession } from '../../../src/session/createSession'; import { saveSession } from '../../../src/session/saveSession'; -jest.mock('./../../../src/session/saveSession'); +vi.mock('./../../../src/session/saveSession'); -jest.mock('@sentry/utils', () => { +vi.mock('@sentry/utils', async () => { return { - ...(jest.requireActual('@sentry/utils') as { string: unknown }), - uuid4: jest.fn(() => 'test_session_id'), + ...((await vi.importActual('@sentry/utils')) as { string: unknown }), + uuid4: vi.fn(() => 'test_session_id'), }; }); -type CaptureEventMockType = jest.MockedFunction; +type CaptureEventMockType = vi.MockedFunction; describe('Unit | session | createSession', () => { - const captureEventMock: CaptureEventMockType = jest.fn(); + const captureEventMock: CaptureEventMockType = vi.fn(); beforeAll(() => { WINDOW.sessionStorage.clear(); - jest.spyOn(Sentry, 'getCurrentHub').mockImplementation(() => { + vi.spyOn(Sentry, 'getCurrentHub').mockImplementation(() => { return { captureEvent: captureEventMock, // eslint-disable-next-line deprecation/deprecation diff --git a/packages/replay-internal/test/unit/session/loadOrCreateSession.test.ts b/packages/replay-internal/test/unit/session/loadOrCreateSession.test.ts index bac9ee397984..51f4793c7768 100644 --- a/packages/replay-internal/test/unit/session/loadOrCreateSession.test.ts +++ b/packages/replay-internal/test/unit/session/loadOrCreateSession.test.ts @@ -1,3 +1,5 @@ +import { vi } from 'vitest'; + import { MAX_REPLAY_DURATION, SESSION_IDLE_EXPIRE_DURATION, WINDOW } from '../../../src/constants'; import { makeSession } from '../../../src/session/Session'; import * as CreateSession from '../../../src/session/createSession'; @@ -6,10 +8,10 @@ import { loadOrCreateSession } from '../../../src/session/loadOrCreateSession'; import { saveSession } from '../../../src/session/saveSession'; import type { SessionOptions } from '../../../src/types'; -jest.mock('@sentry/utils', () => { +vi.mock('@sentry/utils', async () => { return { - ...(jest.requireActual('@sentry/utils') as { string: unknown }), - uuid4: jest.fn(() => 'test_session_uuid'), + ...((await vi.importActual('@sentry/utils')) as { string: unknown }), + uuid4: vi.fn(() => 'test_session_uuid'), }; }); @@ -42,15 +44,15 @@ function createMockSession(when: number = Date.now(), id = 'test_session_id') { describe('Unit | session | loadOrCreateSession', () => { beforeAll(() => { - jest.spyOn(CreateSession, 'createSession'); - jest.spyOn(FetchSession, 'fetchSession'); + vi.spyOn(CreateSession, 'createSession'); + vi.spyOn(FetchSession, 'fetchSession'); WINDOW.sessionStorage.clear(); }); afterEach(() => { WINDOW.sessionStorage.clear(); - (CreateSession.createSession as jest.MockedFunction).mockClear(); - (FetchSession.fetchSession as jest.MockedFunction).mockClear(); + (CreateSession.createSession as vi.MockedFunction).mockClear(); + (FetchSession.fetchSession as vi.MockedFunction).mockClear(); }); describe('stickySession: false', () => { diff --git a/packages/replay-internal/test/unit/util/addEvent.test.ts b/packages/replay-internal/test/unit/util/addEvent.test.ts index 8c1d9dea8175..748c2a87b684 100644 --- a/packages/replay-internal/test/unit/util/addEvent.test.ts +++ b/packages/replay-internal/test/unit/util/addEvent.test.ts @@ -1,5 +1,7 @@ import 'jsdom-worker'; +import { vi } from 'vitest'; + import { BASE_TIMESTAMP } from '../..'; import { MAX_REPLAY_DURATION, REPLAY_MAX_EVENT_BUFFER_SIZE, SESSION_IDLE_PAUSE_DURATION } from '../../../src/constants'; import type { EventBufferProxy } from '../../../src/eventBuffer/EventBufferProxy'; @@ -12,7 +14,7 @@ useFakeTimers(); describe('Unit | util | addEvent', () => { it('stops when encountering a compression error', async function () { - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); const replay = setupReplayContainer({ options: { @@ -20,10 +22,11 @@ describe('Unit | util | addEvent', () => { }, }); + await vi.runAllTimersAsync(); await (replay.eventBuffer as EventBufferProxy).ensureWorkerIsLoaded(); // @ts-expect-error Mock this private so it triggers an error - jest.spyOn(replay.eventBuffer._compression._worker, 'postMessage').mockImplementationOnce(() => { + vi.spyOn(replay.eventBuffer._compression._worker, 'postMessage').mockImplementationOnce(() => { return Promise.reject('test worker error'); }); @@ -33,7 +36,7 @@ describe('Unit | util | addEvent', () => { }); it('stops when exceeding buffer size limit', async function () { - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); const replay = setupReplayContainer({ options: { @@ -41,6 +44,8 @@ describe('Unit | util | addEvent', () => { }, }); + await vi.runAllTimersAsync(); + const largeEvent = getTestEventIncremental({ data: { a: 'a'.repeat(REPLAY_MAX_EVENT_BUFFER_SIZE / 3) }, timestamp: BASE_TIMESTAMP, @@ -60,7 +65,7 @@ describe('Unit | util | addEvent', () => { describe('shouldAddEvent', () => { beforeEach(() => { - jest.setSystemTime(BASE_TIMESTAMP); + vi.setSystemTime(BASE_TIMESTAMP); }); it('returns true by default', () => { diff --git a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts index 636eb3aded9d..fd086eea6fa0 100644 --- a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts +++ b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts @@ -1,8 +1,11 @@ -jest.useFakeTimers().setSystemTime(new Date('2023-01-01')); +import { vi } from 'vitest'; -jest.mock('@sentry/utils', () => ({ - ...jest.requireActual('@sentry/utils'), - browserPerformanceTimeOrigin: Date.now(), +vi.useFakeTimers(); +vi.setSystemTime(new Date('2023-01-01')); + +vi.mock('@sentry/utils', async () => ({ + ...(await vi.importActual('@sentry/utils')), + browserPerformanceTimeOrigin: new Date('2023-01-01').getTime(), })); import { WINDOW } from '../../../src/constants'; @@ -12,7 +15,7 @@ import { PerformanceEntryNavigation } from '../../fixtures/performanceEntry/navi describe('Unit | util | createPerformanceEntries', () => { beforeEach(function () { if (!WINDOW.performance.getEntriesByType) { - WINDOW.performance.getEntriesByType = jest.fn((type: string) => { + WINDOW.performance.getEntriesByType = vi.fn((type: string) => { if (type === 'navigation') { return [PerformanceEntryNavigation()]; } @@ -22,8 +25,8 @@ describe('Unit | util | createPerformanceEntries', () => { }); afterAll(function () { - jest.clearAllMocks(); - jest.useRealTimers(); + vi.clearAllMocks(); + vi.useRealTimers(); }); it('ignores sdks own requests', function () { diff --git a/packages/replay-internal/test/unit/util/debounce.test.ts b/packages/replay-internal/test/unit/util/debounce.test.ts index d52f9d456b49..59eb9e2a84fa 100644 --- a/packages/replay-internal/test/unit/util/debounce.test.ts +++ b/packages/replay-internal/test/unit/util/debounce.test.ts @@ -1,77 +1,77 @@ import { debounce } from '../../../src/util/debounce'; describe('Unit | util | debounce', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); it('delay the execution of the passed callback function by the passed minDelay', () => { - const callback = jest.fn(); + const callback = vi.fn(); const debouncedCallback = debounce(callback, 100); debouncedCallback(); expect(callback).not.toHaveBeenCalled(); - jest.advanceTimersByTime(99); + vi.advanceTimersByTime(99); expect(callback).not.toHaveBeenCalled(); - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); expect(callback).toHaveBeenCalled(); }); it('should invoke the callback at latest by maxWait, if the option is specified', () => { - const callback = jest.fn(); + const callback = vi.fn(); const debouncedCallback = debounce(callback, 100, { maxWait: 150 }); debouncedCallback(); expect(callback).not.toHaveBeenCalled(); - jest.advanceTimersByTime(98); + vi.advanceTimersByTime(98); expect(callback).not.toHaveBeenCalled(); debouncedCallback(); - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); expect(callback).not.toHaveBeenCalled(); - jest.advanceTimersByTime(49); + vi.advanceTimersByTime(49); // at this time, the callback shouldn't be invoked and with a new call, it should be devounced further. debouncedCallback(); expect(callback).not.toHaveBeenCalled(); // But because the maxWait is reached, the callback should nevertheless be invoked. - jest.advanceTimersByTime(10); + vi.advanceTimersByTime(10); expect(callback).toHaveBeenCalled(); }); it('should not invoke the callback as long as it is debounced and no maxWait option is specified', () => { - const callback = jest.fn(); + const callback = vi.fn(); const debouncedCallback = debounce(callback, 100); debouncedCallback(); expect(callback).not.toHaveBeenCalled(); - jest.advanceTimersByTime(99); + vi.advanceTimersByTime(99); expect(callback).not.toHaveBeenCalled(); debouncedCallback(); - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); expect(callback).not.toHaveBeenCalled(); - jest.advanceTimersByTime(98); + vi.advanceTimersByTime(98); debouncedCallback(); expect(callback).not.toHaveBeenCalled(); - jest.advanceTimersByTime(99); + vi.advanceTimersByTime(99); expect(callback).not.toHaveBeenCalled(); debouncedCallback(); - jest.advanceTimersByTime(100); + vi.advanceTimersByTime(100); expect(callback).toHaveBeenCalled(); }); it('should invoke the callback as soon as callback.flush() is called', () => { - const callback = jest.fn(); + const callback = vi.fn(); const debouncedCallback = debounce(callback, 100, { maxWait: 200 }); debouncedCallback(); expect(callback).not.toHaveBeenCalled(); - jest.advanceTimersByTime(10); + vi.advanceTimersByTime(10); expect(callback).not.toHaveBeenCalled(); debouncedCallback.flush(); @@ -79,26 +79,26 @@ describe('Unit | util | debounce', () => { }); it('should not invoke the callback, if callback.cancel() is called', () => { - const callback = jest.fn(); + const callback = vi.fn(); const debouncedCallback = debounce(callback, 100, { maxWait: 200 }); debouncedCallback(); expect(callback).not.toHaveBeenCalled(); - jest.advanceTimersByTime(99); + vi.advanceTimersByTime(99); expect(callback).not.toHaveBeenCalled(); // If the callback is canceled, it should not be invoked after the minwait debouncedCallback.cancel(); - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); expect(callback).not.toHaveBeenCalled(); // And it should also not be invoked after the maxWait - jest.advanceTimersByTime(500); + vi.advanceTimersByTime(500); expect(callback).not.toHaveBeenCalled(); }); it("should return the callback's return value when calling callback.flush()", () => { - const callback = jest.fn().mockReturnValue('foo'); + const callback = vi.fn().mockReturnValue('foo'); const debouncedCallback = debounce(callback, 100); debouncedCallback(); @@ -108,7 +108,7 @@ describe('Unit | util | debounce', () => { }); it('should return the callbacks return value on subsequent calls of the debounced function', () => { - const callback = jest.fn().mockReturnValue('foo'); + const callback = vi.fn().mockReturnValue('foo'); const debouncedCallback = debounce(callback, 100); const returnValue1 = debouncedCallback(); @@ -116,7 +116,7 @@ describe('Unit | util | debounce', () => { expect(callback).not.toHaveBeenCalled(); // now we expect the callback to have been invoked - jest.advanceTimersByTime(200); + vi.advanceTimersByTime(200); expect(callback).toHaveBeenCalledTimes(1); // calling the debounced function now should return the return value of the callback execution @@ -125,13 +125,13 @@ describe('Unit | util | debounce', () => { expect(callback).toHaveBeenCalledTimes(1); // and the callback should also be invoked again - jest.advanceTimersByTime(200); + vi.advanceTimersByTime(200); expect(callback).toHaveBeenCalledTimes(2); }); it('should handle return values of consecutive invocations without maxWait', () => { let i = 0; - const callback = jest.fn().mockImplementation(() => { + const callback = vi.fn().mockImplementation(() => { return `foo-${++i}`; }); const debouncedCallback = debounce(callback, 100); @@ -141,7 +141,7 @@ describe('Unit | util | debounce', () => { expect(callback).not.toHaveBeenCalled(); // now we expect the callback to have been invoked - jest.advanceTimersByTime(200); + vi.advanceTimersByTime(200); expect(callback).toHaveBeenCalledTimes(1); // calling the debounced function now should return the return value of the callback execution @@ -149,13 +149,13 @@ describe('Unit | util | debounce', () => { expect(returnValue1).toBe('foo-1'); expect(callback).toHaveBeenCalledTimes(1); - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); const returnValue2 = debouncedCallback(); expect(returnValue2).toBe('foo-1'); expect(callback).toHaveBeenCalledTimes(1); // and the callback should also be invoked again - jest.advanceTimersByTime(200); + vi.advanceTimersByTime(200); const returnValue3 = debouncedCallback(); expect(returnValue3).toBe('foo-2'); expect(callback).toHaveBeenCalledTimes(2); @@ -163,7 +163,7 @@ describe('Unit | util | debounce', () => { it('should handle return values of consecutive invocations with maxWait', () => { let i = 0; - const callback = jest.fn().mockImplementation(() => { + const callback = vi.fn().mockImplementation(() => { return `foo-${++i}`; }); const debouncedCallback = debounce(callback, 150, { maxWait: 200 }); @@ -173,26 +173,26 @@ describe('Unit | util | debounce', () => { expect(callback).not.toHaveBeenCalled(); // now we expect the callback to have been invoked - jest.advanceTimersByTime(149); + vi.advanceTimersByTime(149); const returnValue1 = debouncedCallback(); expect(returnValue1).toBe(undefined); expect(callback).not.toHaveBeenCalled(); // calling the debounced function now should return the return value of the callback execution // as it was executed because of maxWait - jest.advanceTimersByTime(51); + vi.advanceTimersByTime(51); const returnValue2 = debouncedCallback(); expect(returnValue2).toBe('foo-1'); expect(callback).toHaveBeenCalledTimes(1); // at this point (100ms after the last debounce call), nothing should have happened - jest.advanceTimersByTime(100); + vi.advanceTimersByTime(100); const returnValue3 = debouncedCallback(); expect(returnValue3).toBe('foo-1'); expect(callback).toHaveBeenCalledTimes(1); // and the callback should now have been invoked again - jest.advanceTimersByTime(150); + vi.advanceTimersByTime(150); const returnValue4 = debouncedCallback(); expect(returnValue4).toBe('foo-2'); expect(callback).toHaveBeenCalledTimes(2); @@ -200,7 +200,7 @@ describe('Unit | util | debounce', () => { it('should handle return values of consecutive invocations after a cancellation', () => { let i = 0; - const callback = jest.fn().mockImplementation(() => { + const callback = vi.fn().mockImplementation(() => { return `foo-${++i}`; }); const debouncedCallback = debounce(callback, 150, { maxWait: 200 }); @@ -210,7 +210,7 @@ describe('Unit | util | debounce', () => { expect(callback).not.toHaveBeenCalled(); // now we expect the callback to have been invoked - jest.advanceTimersByTime(149); + vi.advanceTimersByTime(149); const returnValue1 = debouncedCallback(); expect(returnValue1).toBe(undefined); expect(callback).not.toHaveBeenCalled(); @@ -218,20 +218,20 @@ describe('Unit | util | debounce', () => { debouncedCallback.cancel(); // calling the debounced function now still return undefined because we cancelled the invocation - jest.advanceTimersByTime(51); + vi.advanceTimersByTime(51); const returnValue2 = debouncedCallback(); expect(returnValue2).toBe(undefined); expect(callback).not.toHaveBeenCalled(); // and the callback should also be invoked again - jest.advanceTimersByTime(150); + vi.advanceTimersByTime(150); const returnValue3 = debouncedCallback(); expect(returnValue3).toBe('foo-1'); expect(callback).toHaveBeenCalledTimes(1); }); it('should handle the return value of calling flush after cancelling', () => { - const callback = jest.fn().mockReturnValue('foo'); + const callback = vi.fn().mockReturnValue('foo'); const debouncedCallback = debounce(callback, 100); debouncedCallback(); @@ -242,30 +242,30 @@ describe('Unit | util | debounce', () => { }); it('should handle equal wait and maxWait values and only invoke func once', () => { - const callback = jest.fn().mockReturnValue('foo'); + const callback = vi.fn().mockReturnValue('foo'); const debouncedCallback = debounce(callback, 100, { maxWait: 100 }); debouncedCallback(); - jest.advanceTimersByTime(25); + vi.advanceTimersByTime(25); debouncedCallback(); - jest.advanceTimersByTime(25); + vi.advanceTimersByTime(25); debouncedCallback(); - jest.advanceTimersByTime(25); + vi.advanceTimersByTime(25); debouncedCallback(); - jest.advanceTimersByTime(25); + vi.advanceTimersByTime(25); expect(callback).toHaveBeenCalledTimes(1); const retval = debouncedCallback(); expect(retval).toBe('foo'); - jest.advanceTimersByTime(25); + vi.advanceTimersByTime(25); debouncedCallback(); - jest.advanceTimersByTime(25); + vi.advanceTimersByTime(25); debouncedCallback(); - jest.advanceTimersByTime(25); + vi.advanceTimersByTime(25); debouncedCallback(); - jest.advanceTimersByTime(25); + vi.advanceTimersByTime(25); expect(callback).toHaveBeenCalledTimes(2); }); diff --git a/packages/replay-internal/test/unit/util/getPrivacyOptions.test.ts b/packages/replay-internal/test/unit/util/getPrivacyOptions.test.ts index e6d707ef879c..536ee7d0df21 100644 --- a/packages/replay-internal/test/unit/util/getPrivacyOptions.test.ts +++ b/packages/replay-internal/test/unit/util/getPrivacyOptions.test.ts @@ -1,11 +1,13 @@ +import { afterEach, beforeEach, describe, vi } from 'vitest'; + import { getPrivacyOptions } from '../../../src/util/getPrivacyOptions'; describe('Unit | util | getPrivacyOptions', () => { beforeEach(() => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); + vi.spyOn(console, 'warn').mockImplementation(() => {}); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('has correct default options', () => { @@ -18,9 +20,9 @@ describe('Unit | util | getPrivacyOptions', () => { ignore: ['.custom-ignore'], }), ).toMatchInlineSnapshot(` - Object { - "blockSelector": ".custom-block,.sentry-block,[data-sentry-block],base[href=\\"/\\"]", - "ignoreSelector": ".custom-ignore,.sentry-ignore,[data-sentry-ignore],input[type=\\"file\\"]", + { + "blockSelector": ".custom-block,.sentry-block,[data-sentry-block],base[href="/"]", + "ignoreSelector": ".custom-ignore,.sentry-ignore,[data-sentry-ignore],input[type="file"]", "maskTextSelector": ".custom-mask,.sentry-mask,[data-sentry-mask]", "unblockSelector": ".custom-unblock", "unmaskTextSelector": ".custom-unmask", diff --git a/packages/replay-internal/test/unit/util/handleRecordingEmit.test.ts b/packages/replay-internal/test/unit/util/handleRecordingEmit.test.ts index 8e22c886a5eb..fb7eacfa032e 100644 --- a/packages/replay-internal/test/unit/util/handleRecordingEmit.test.ts +++ b/packages/replay-internal/test/unit/util/handleRecordingEmit.test.ts @@ -12,11 +12,11 @@ useFakeTimers(); let optionsEvent: ReplayOptionFrameEvent; describe('Unit | util | handleRecordingEmit', () => { - let addEventMock: jest.SpyInstance; + let addEventMock: MockInstance; beforeEach(function () { - jest.setSystemTime(BASE_TIMESTAMP); - addEventMock = jest.spyOn(SentryAddEvent, 'addEventSync').mockImplementation(() => { + vi.setSystemTime(BASE_TIMESTAMP); + addEventMock = vi.spyOn(SentryAddEvent, 'addEventSync').mockImplementation(() => { return true; }); }); @@ -45,14 +45,12 @@ describe('Unit | util | handleRecordingEmit', () => { }; handler(event); - await new Promise(process.nextTick); expect(addEventMock).toBeCalledTimes(2); expect(addEventMock).toHaveBeenNthCalledWith(1, replay, event, true); expect(addEventMock).toHaveBeenLastCalledWith(replay, optionsEvent, false); handler(event); - await new Promise(process.nextTick); expect(addEventMock).toBeCalledTimes(3); expect(addEventMock).toHaveBeenLastCalledWith(replay, event, false); @@ -78,7 +76,6 @@ describe('Unit | util | handleRecordingEmit', () => { }; handler(event, true); - await new Promise(process.nextTick); // Called twice, once for event and once for settings on checkout only expect(addEventMock).toBeCalledTimes(2); @@ -86,10 +83,9 @@ describe('Unit | util | handleRecordingEmit', () => { expect(addEventMock).toHaveBeenLastCalledWith(replay, optionsEvent, false); handler(event, true); - await new Promise(process.nextTick); expect(addEventMock).toBeCalledTimes(4); expect(addEventMock).toHaveBeenNthCalledWith(3, replay, event, true); - expect(addEventMock).toHaveBeenLastCalledWith(replay, { ...optionsEvent, timestamp: BASE_TIMESTAMP + 20 }, false); + expect(addEventMock).toHaveBeenLastCalledWith(replay, { ...optionsEvent, timestamp: BASE_TIMESTAMP }, false); }); }); diff --git a/packages/replay-internal/test/unit/util/isSampled.test.ts b/packages/replay-internal/test/unit/util/isSampled.test.ts index 97a824cb0e9d..6dfd163c45c7 100644 --- a/packages/replay-internal/test/unit/util/isSampled.test.ts +++ b/packages/replay-internal/test/unit/util/isSampled.test.ts @@ -14,7 +14,7 @@ const cases: [number, number, boolean][] = [ ]; describe('Unit | util | isSampled', () => { - const mockRandom = jest.spyOn(Math, 'random'); + const mockRandom = vi.spyOn(Math, 'random'); test.each(cases)( 'given sample rate of %p and RNG returns %p, result should be %p', diff --git a/packages/replay-internal/test/unit/util/prepareReplayEvent.test.ts b/packages/replay-internal/test/unit/util/prepareReplayEvent.test.ts index ac4ae503d10e..78c2c457a02d 100644 --- a/packages/replay-internal/test/unit/util/prepareReplayEvent.test.ts +++ b/packages/replay-internal/test/unit/util/prepareReplayEvent.test.ts @@ -1,3 +1,5 @@ +import { vi } from 'vitest'; + import { getClient, getCurrentScope, setCurrentClient } from '@sentry/core'; import type { ReplayEvent } from '@sentry/types'; @@ -11,7 +13,7 @@ describe('Unit | util | prepareReplayEvent', () => { setCurrentClient(client); client.init(); - jest.spyOn(client, 'getSdkMetadata').mockImplementation(() => { + vi.spyOn(client, 'getSdkMetadata').mockImplementation(() => { return { sdk: { name: 'sentry.javascript.testSdk', @@ -22,7 +24,7 @@ describe('Unit | util | prepareReplayEvent', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('works', async () => { diff --git a/packages/replay-internal/test/unit/util/throttle.test.ts b/packages/replay-internal/test/unit/util/throttle.test.ts index a242bde73398..797558dea904 100644 --- a/packages/replay-internal/test/unit/util/throttle.test.ts +++ b/packages/replay-internal/test/unit/util/throttle.test.ts @@ -1,14 +1,14 @@ import { BASE_TIMESTAMP } from '../..'; import { SKIPPED, THROTTLED, throttle } from '../../../src/util/throttle'; -jest.useFakeTimers(); +vi.useFakeTimers(); describe('Unit | util | throttle', () => { it('executes when not hitting the limit', () => { const now = BASE_TIMESTAMP; - jest.setSystemTime(now); + vi.setSystemTime(now); - const callback = jest.fn(); + const callback = vi.fn(); const fn = throttle(callback, 100, 60); fn(); @@ -17,20 +17,20 @@ describe('Unit | util | throttle', () => { expect(callback).toHaveBeenCalledTimes(3); - jest.advanceTimersByTime(59_000); + vi.advanceTimersByTime(59_000); fn(); fn(); expect(callback).toHaveBeenCalledTimes(5); - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); fn(); expect(callback).toHaveBeenCalledTimes(6); - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); fn(); @@ -39,8 +39,8 @@ describe('Unit | util | throttle', () => { it('stops executing when hitting the limit', () => { const now = BASE_TIMESTAMP; - jest.setSystemTime(now); - const callback = jest.fn(); + vi.setSystemTime(now); + const callback = vi.fn(); const fn = throttle(callback, 5, 60); fn(); @@ -49,14 +49,14 @@ describe('Unit | util | throttle', () => { expect(callback).toHaveBeenCalledTimes(3); - jest.advanceTimersByTime(59_000); + vi.advanceTimersByTime(59_000); fn(); fn(); expect(callback).toHaveBeenCalledTimes(5); - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); fn(); fn(); @@ -66,7 +66,7 @@ describe('Unit | util | throttle', () => { expect(callback).toHaveBeenCalledTimes(5); // Now, the first three will "expire", so we can add three more - jest.advanceTimersByTime(1_000); + vi.advanceTimersByTime(1_000); fn(); fn(); @@ -78,9 +78,9 @@ describe('Unit | util | throttle', () => { it('has correct return value', () => { const now = BASE_TIMESTAMP; - jest.setSystemTime(now); + vi.setSystemTime(now); - const callback = jest.fn(() => 'foo'); + const callback = vi.fn(() => 'foo'); const fn = throttle(callback, 5, 60); expect(fn()).toBe('foo'); @@ -93,7 +93,7 @@ describe('Unit | util | throttle', () => { expect(fn()).toBe(SKIPPED); // After 61s, should be reset - jest.advanceTimersByTime(61_000); + vi.advanceTimersByTime(61_000); expect(fn()).toBe('foo'); expect(fn()).toBe('foo'); @@ -107,10 +107,10 @@ describe('Unit | util | throttle', () => { it('passes args correctly', () => { const now = BASE_TIMESTAMP; - jest.setSystemTime(now); + vi.setSystemTime(now); const originalFunction = (a: number, b: number) => a + b; - const callback = jest.fn(originalFunction); + const callback = vi.fn(originalFunction); const fn = throttle(callback, 5, 60); expect(fn(1, 2)).toBe(3); diff --git a/packages/replay-internal/test/utils/use-fake-timers.ts b/packages/replay-internal/test/utils/use-fake-timers.ts index ef7c92dc8101..57fda295cb59 100644 --- a/packages/replay-internal/test/utils/use-fake-timers.ts +++ b/packages/replay-internal/test/utils/use-fake-timers.ts @@ -1,14 +1,5 @@ -export function useFakeTimers(): void { - const _setInterval = setInterval; - const _clearInterval = clearInterval; - jest.useFakeTimers(); - - let interval: any; - beforeAll(function () { - interval = _setInterval(() => jest.advanceTimersByTime(20), 20); - }); +import { vi } from 'vitest'; - afterAll(function () { - _clearInterval(interval); - }); +export function useFakeTimers(): void { + vi.useFakeTimers(); } diff --git a/packages/replay-internal/vitest.config.ts b/packages/replay-internal/vitest.config.ts new file mode 100644 index 000000000000..9b4a2451c0c4 --- /dev/null +++ b/packages/replay-internal/vitest.config.ts @@ -0,0 +1,16 @@ +import type { UserConfig } from 'vitest'; +import { defineConfig } from 'vitest/config'; + +import baseConfig from '../../vite/vite.config'; + +export default defineConfig({ + ...baseConfig, + test: { + ...(baseConfig as UserConfig & { test: any }).test, + coverage: {}, + globals: true, + setupFiles: ['./test.setup.ts'], + reporters: ['default'], + environment: 'jsdom', + }, +}); diff --git a/yarn.lock b/yarn.lock index 1c764d790299..a1a39a471547 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8382,17 +8382,8 @@ dependencies: "@types/unist" "*" -"@types/history-4@npm:@types/history@4.7.8": - version "4.7.8" - resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" - integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== - -"@types/history-5@npm:@types/history@4.7.8": - version "4.7.8" - resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" - integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== - -"@types/history@*": +"@types/history-4@npm:@types/history@4.7.8", "@types/history-5@npm:@types/history@4.7.8", "@types/history@*": + name "@types/history-4" version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== @@ -8756,15 +8747,7 @@ "@types/history" "^3" "@types/react" "*" -"@types/react-router-4@npm:@types/react-router@5.1.14": - version "5.1.14" - resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.14.tgz#e0442f4eb4c446541ad7435d44a97f8fe6df40da" - integrity sha512-LAJpqYUaCTMT2anZheoidiIymt8MuX286zoVFPM3DVb23aQBH0mAkFvzpd4LKqiolV8bBtZWT5Qp7hClCNDENw== - dependencies: - "@types/history" "*" - "@types/react" "*" - -"@types/react-router-5@npm:@types/react-router@5.1.14": +"@types/react-router-4@npm:@types/react-router@5.1.14", "@types/react-router-5@npm:@types/react-router@5.1.14": version "5.1.14" resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.14.tgz#e0442f4eb4c446541ad7435d44a97f8fe6df40da" integrity sha512-LAJpqYUaCTMT2anZheoidiIymt8MuX286zoVFPM3DVb23aQBH0mAkFvzpd4LKqiolV8bBtZWT5Qp7hClCNDENw== @@ -9211,6 +9194,15 @@ "@vitest/utils" "1.4.0" chai "^4.3.10" +"@vitest/expect@1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.5.3.tgz#34198e2123fb5be68f606729114aadbd071d77dc" + integrity sha512-y+waPz31pOFr3rD7vWTbwiLe5+MgsMm40jTZbQE8p8/qXyBX3CQsIXRx9XK12IbY7q/t5a5aM/ckt33b4PxK2g== + dependencies: + "@vitest/spy" "1.5.3" + "@vitest/utils" "1.5.3" + chai "^4.3.10" + "@vitest/runner@0.29.8": version "0.29.8" resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.29.8.tgz#ede8a7be8a074ea1180bc1d1595bd879ed15971c" @@ -9229,6 +9221,15 @@ p-limit "^5.0.0" pathe "^1.1.1" +"@vitest/runner@1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.5.3.tgz#226a726ca0bf11c1f287fa867547bdfca072b1e6" + integrity sha512-7PlfuReN8692IKQIdCxwir1AOaP5THfNkp0Uc4BKr2na+9lALNit7ub9l3/R7MP8aV61+mHKRGiqEKRIwu6iiQ== + dependencies: + "@vitest/utils" "1.5.3" + p-limit "^5.0.0" + pathe "^1.1.1" + "@vitest/snapshot@1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.4.0.tgz#2945b3fb53767a3f4f421919e93edfef2935b8bd" @@ -9238,6 +9239,15 @@ pathe "^1.1.1" pretty-format "^29.7.0" +"@vitest/snapshot@1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.5.3.tgz#ffdd917daebf4415c7abad6993bafd5f4ee14aaf" + integrity sha512-K3mvIsjyKYBhNIDujMD2gfQEzddLe51nNOAf45yKRt/QFJcUIeTQd2trRvv6M6oCBHNVnZwFWbQ4yj96ibiDsA== + dependencies: + magic-string "^0.30.5" + pathe "^1.1.1" + pretty-format "^29.7.0" + "@vitest/spy@0.29.8": version "0.29.8" resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.29.8.tgz#2e0c3b30e04d317b2197e3356234448aa432e131" @@ -9252,6 +9262,13 @@ dependencies: tinyspy "^2.2.0" +"@vitest/spy@1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.5.3.tgz#a81dfa87e4af3fe2c5d6f84cc81be31580b05e2c" + integrity sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg== + dependencies: + tinyspy "^2.2.0" + "@vitest/utils@0.29.8": version "0.29.8" resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.29.8.tgz#423da85fd0c6633f3ab496cf7d2fc0119b850df8" @@ -9272,6 +9289,16 @@ loupe "^2.3.7" pretty-format "^29.7.0" +"@vitest/utils@1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.5.3.tgz#216068c28db577480ca77e0b2094f0b1fa29bbcd" + integrity sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA== + dependencies: + diff-sequences "^29.6.3" + estree-walker "^3.0.3" + loupe "^2.3.7" + pretty-format "^29.7.0" + "@vue/compiler-core@3.2.45": version "3.2.45" resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz#d9311207d96f6ebd5f4660be129fb99f01ddb41b" @@ -10013,6 +10040,13 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" +agent-base@^7.0.2, agent-base@^7.1.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + agentkeepalive@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" @@ -13632,6 +13666,13 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +cssstyle@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.0.1.tgz#ef29c598a1e90125c870525490ea4f354db0660a" + integrity sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ== + dependencies: + rrweb-cssom "^0.6.0" + csstype@^2.6.8: version "2.6.21" resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" @@ -13685,6 +13726,14 @@ data-urls@^3.0.1: whatwg-mimetype "^3.0.0" whatwg-url "^10.0.0" +data-urls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" + integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== + dependencies: + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + date-fns@^2.29.2: version "2.29.3" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" @@ -13746,6 +13795,11 @@ decimal.js@^10.2.1, decimal.js@^10.3.1: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== +decimal.js@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + decode-named-character-reference@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" @@ -15229,7 +15283,7 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@^4.4.0: +entities@^4.4.0, entities@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== @@ -17885,6 +17939,15 @@ handlebars@^4.0.4, handlebars@^4.3.1, handlebars@^4.7.3, handlebars@^4.7.6, hand optionalDependencies: uglify-js "^3.1.4" +happy-dom@^14.7.1: + version "14.7.1" + resolved "https://registry.yarnpkg.com/happy-dom/-/happy-dom-14.7.1.tgz#4356617f5fcb722c26ccd4c5c8a235cead675592" + integrity sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg== + dependencies: + entities "^4.5.0" + webidl-conversions "^7.0.0" + whatwg-mimetype "^3.0.0" + hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -18395,6 +18458,13 @@ html-encoding-sniffer@^3.0.0: dependencies: whatwg-encoding "^2.0.0" +html-encoding-sniffer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" + integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== + dependencies: + whatwg-encoding "^3.1.1" + html-entities@^2.3.2: version "2.5.2" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" @@ -18518,6 +18588,14 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" +http-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + http-proxy-middleware@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" @@ -18569,6 +18647,14 @@ https-proxy-agent@^4.0.0: agent-base "5" debug "4" +https-proxy-agent@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" + integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== + dependencies: + agent-base "^7.0.2" + debug "4" + https@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https/-/https-1.0.0.tgz#3c37c7ae1a8eeb966904a2ad1e975a194b7ed3a4" @@ -20281,6 +20367,33 @@ jsdom@^19.0.0: ws "^8.2.3" xml-name-validator "^4.0.0" +jsdom@^24.0.0: + version "24.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-24.0.0.tgz#e2dc04e4c79da368481659818ee2b0cd7c39007c" + integrity sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A== + dependencies: + cssstyle "^4.0.1" + data-urls "^5.0.0" + decimal.js "^10.4.3" + form-data "^4.0.0" + html-encoding-sniffer "^4.0.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.7" + parse5 "^7.1.2" + rrweb-cssom "^0.6.0" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.3" + w3c-xmlserializer "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^3.1.1" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + ws "^8.16.0" + xml-name-validator "^5.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -23506,6 +23619,11 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== +nwsapi@^2.2.7: + version "2.2.9" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.9.tgz#7f3303218372db2e9f27c27766bcfc59ae7e61c6" + integrity sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg== + nx@16.4.1, "nx@>=16.1.3 < 17": version "16.4.1" resolved "https://registry.yarnpkg.com/nx/-/nx-16.4.1.tgz#3a8a58f78d6fbf2264639d401278ee16140d0b11" @@ -24266,7 +24384,7 @@ parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parse5@^7.0.0: +parse5@^7.0.0, parse5@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== @@ -25572,6 +25690,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + pupa@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" @@ -25613,6 +25736,11 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -25793,7 +25921,8 @@ react-is@^18.0.0: dependencies: "@remix-run/router" "1.0.2" -"react-router-6@npm:react-router@6.3.0": +"react-router-6@npm:react-router@6.3.0", react-router@6.3.0: + name react-router-6 version "6.3.0" resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557" integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ== @@ -25808,13 +25937,6 @@ react-router-dom@^6.2.2: history "^5.2.0" react-router "6.3.0" -react-router@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557" - integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ== - dependencies: - history "^5.2.0" - react@^18.0.0: version "18.0.0" resolved "https://registry.yarnpkg.com/react/-/react-18.0.0.tgz#b468736d1f4a5891f38585ba8e8fb29f91c3cb96" @@ -26802,6 +26924,11 @@ rollup@^4.2.0: "@rollup/rollup-win32-x64-msvc" "4.9.1" fsevents "~2.3.2" +rrweb-cssom@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" + integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== + rsvp@^3.0.14, rsvp@^3.0.17, rsvp@^3.0.18, rsvp@^3.0.21, rsvp@^3.0.6, rsvp@^3.1.0: version "3.6.2" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a" @@ -27002,6 +27129,13 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + scheduler@^0.21.0: version "0.21.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820" @@ -28050,7 +28184,8 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + name string-width-cjs version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -28076,15 +28211,6 @@ string-width@^2.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -28180,14 +28306,7 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -28873,6 +28992,11 @@ tinypool@^0.8.2: resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.3.tgz#e17d0a5315a7d425f875b05f7af653c225492d39" integrity sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw== +tinypool@^0.8.3: + version "0.8.4" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.4.tgz#e217fe1270d941b39e98c625dcecebb1408c9aa8" + integrity sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ== + tinyspy@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-1.1.1.tgz#0cb91d5157892af38cb2d217f5c7e8507a5bf092" @@ -29001,6 +29125,16 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" +tough-cookie@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + tr46@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" @@ -29022,6 +29156,13 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" +tr46@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec" + integrity sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g== + dependencies: + punycode "^2.3.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -29633,6 +29774,11 @@ universalify@^0.1.0, universalify@^0.1.2: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" @@ -29743,6 +29889,14 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" @@ -30030,6 +30184,17 @@ vite-node@1.4.0: picocolors "^1.0.0" vite "^5.0.0" +vite-node@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.5.3.tgz#498f4eb6f4e37ff95f66ffb9c905708a75f84b2e" + integrity sha512-axFo00qiCpU/JLd8N1gu9iEYL3xTbMbMrbe5nDp9GL0nb6gurIdZLkkFogZXWnE8Oyy5kfSLwNVIcVsnhE7lgQ== + dependencies: + cac "^6.7.14" + debug "^4.3.4" + pathe "^1.1.1" + picocolors "^1.0.0" + vite "^5.0.0" + vite@4.5.3, "vite@^3.0.0 || ^4.0.0": version "4.5.3" resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.3.tgz#d88a4529ea58bae97294c7e2e6f0eab39a50fb1a" @@ -30140,6 +30305,32 @@ vitest@^1.4.0: vite-node "1.4.0" why-is-node-running "^2.2.2" +vitest@^1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.5.3.tgz#48880013373af16fa4c60a07dd3409fe19397a16" + integrity sha512-2oM7nLXylw3mQlW6GXnRriw+7YvZFk/YNV8AxIC3Z3MfFbuziLGWP9GPxxu/7nRlXhqyxBikpamr+lEEj1sUEw== + dependencies: + "@vitest/expect" "1.5.3" + "@vitest/runner" "1.5.3" + "@vitest/snapshot" "1.5.3" + "@vitest/spy" "1.5.3" + "@vitest/utils" "1.5.3" + acorn-walk "^8.3.2" + chai "^4.3.10" + debug "^4.3.4" + execa "^8.0.1" + local-pkg "^0.5.0" + magic-string "^0.30.5" + pathe "^1.1.1" + picocolors "^1.0.0" + std-env "^3.5.0" + strip-literal "^2.0.0" + tinybench "^2.5.1" + tinypool "^0.8.3" + vite "^5.0.0" + vite-node "1.5.3" + why-is-node-running "^2.2.2" + vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -30177,6 +30368,13 @@ w3c-xmlserializer@^3.0.0: dependencies: xml-name-validator "^4.0.0" +w3c-xmlserializer@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== + dependencies: + xml-name-validator "^5.0.0" + walk-sync@^0.2.5: version "0.2.7" resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.2.7.tgz#b49be4ee6867657aeb736978b56a29d10fa39969" @@ -30592,6 +30790,13 @@ whatwg-encoding@^2.0.0: dependencies: iconv-lite "0.6.3" +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" + whatwg-fetch@>=0.10.0: version "3.6.2" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" @@ -30607,6 +30812,11 @@ whatwg-mimetype@^3.0.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + whatwg-url@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da" @@ -30615,6 +30825,14 @@ whatwg-url@^10.0.0: tr46 "^3.0.0" webidl-conversions "^7.0.0" +whatwg-url@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.0.0.tgz#00baaa7fd198744910c4b1ef68378f2200e4ceb6" + integrity sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw== + dependencies: + tr46 "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -30818,7 +31036,8 @@ workerpool@^6.4.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.4.0.tgz#f8d5cfb45fde32fa3b7af72ad617c3369567a462" integrity sha512-i3KR1mQMNwY2wx20ozq2EjISGtQWDIfV56We+yGJ5yDs8jTwQiLLaqHlkBHITlCuJnYlVRmXegxFxZg7gqI++A== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + name wrap-ansi-cjs version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -30836,15 +31055,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -30917,6 +31127,11 @@ ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== +ws@^8.16.0: + version "8.17.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea" + integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== + ws@^8.2.3: version "8.5.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" @@ -30947,6 +31162,11 @@ xml-name-validator@^4.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== +xml-name-validator@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" From a33ce012571aa2d58429b545f75183b271477ab4 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Sun, 5 May 2024 12:32:04 -0400 Subject: [PATCH 02/11] revert hack --- .../coreHandlers/handleAfterSendEvent.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts b/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts index ed446a4f8c4b..5b60c803c170 100644 --- a/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts +++ b/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts @@ -169,12 +169,12 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); - // This is a bit flakey: handleAfterSendEvent calls - // `sendBufferedReplayOrFlush`, which flushes immediately but also - // calls `startRecording` which eventually triggers another flush after - // flush delay. I'm unable to get stable timer controls, so just - // testing the end result, which is that send gets called twice. + // handleAfterSendEvent calls `sendBufferedReplayOrFlush`, which + // flushes immediately but also calls `startRecording` which eventually + // triggers another flush after flush delay. await vi.runOnlyPendingTimersAsync(); + expect(mockSend).toHaveBeenCalledTimes(1); + await vi.runOnlyPendingTimersAsync(); expect(mockSend).toHaveBeenCalledTimes(2); From 091bf94a71c1243b5b10297fa3782f746d9e5d48 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Sun, 5 May 2024 12:32:17 -0400 Subject: [PATCH 03/11] async timers --- .../test/integration/beforeAddRecordingEvent.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts b/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts index dc72c817a795..a29d142ac374 100644 --- a/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts +++ b/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts @@ -171,8 +171,7 @@ describe('Integration | beforeAddRecordingEvent', () => { ]), ); - vi.runAllTimers(); - await new Promise(process.nextTick); + await vi.runAllTimersAsync(); expect(replay).not.toHaveLastSentReplay(); expect(replay.isEnabled()).toBe(true); From ac4de92588e7dc5115760fb6273dbe4c97530fd3 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Sun, 5 May 2024 12:34:52 -0400 Subject: [PATCH 04/11] remove nextTick --- .../coreHandlers/handleAfterSendEvent.test.ts | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts b/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts index 5b60c803c170..afab6ac6030a 100644 --- a/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts +++ b/packages/replay-internal/test/integration/coreHandlers/handleAfterSendEvent.test.ts @@ -82,8 +82,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().traceIds)).toEqual(['tr2']); // Does not affect error session - vi.runAllTimers(); - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); expect(Array.from(replay.getContext().errorIds)).toEqual([]); expect(Array.from(replay.getContext().traceIds)).toEqual(['tr2']); @@ -208,8 +207,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); - vi.runAllTimers(); - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); // Send once for the regular session sending expect(mockSend).toHaveBeenCalledTimes(1); @@ -244,8 +242,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual([]); - vi.runAllTimers(); - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); expect(mockSend).toHaveBeenCalledTimes(0); expect(Array.from(replay.getContext().errorIds)).toEqual([]); @@ -277,8 +274,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual([]); - vi.runAllTimers(); - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); // Remains in buffer mode & without flushing expect(mockSend).toHaveBeenCalledTimes(0); @@ -311,8 +307,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); - vi.runAllTimers(); - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); // Remains in buffer mode & without flushing expect(mockSend).toHaveBeenCalledTimes(0); @@ -345,8 +340,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); - vi.runAllTimers(); - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); // Remains in buffer mode & without flushing expect(mockSend).toHaveBeenCalledTimes(0); @@ -377,8 +371,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { replay['_isEnabled'] = false; - vi.runAllTimers(); - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); expect(mockSend).toHaveBeenCalledTimes(0); }); @@ -408,8 +401,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { handler(error1, { statusCode: 200 }); - vi.runAllTimers(); - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); expect(beforeErrorSampling).toHaveBeenCalledTimes(1); @@ -420,8 +412,7 @@ describe('Integration | coreHandlers | handleAfterSendEvent', () => { handler(error2, { statusCode: 200 }); - vi.runAllTimers(); - await new Promise(process.nextTick); + await vi.advanceTimersToNextTimerAsync(); expect(beforeErrorSampling).toHaveBeenCalledTimes(2); From c0d4cfa8e1c2e1b213db8c107a34fb8fc05dfe3e Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Sun, 5 May 2024 12:51:44 -0400 Subject: [PATCH 05/11] remove extra/unneeded changes --- packages/replay-internal/test/integration/session.test.ts | 2 +- packages/replay-internal/test/mocks/mockSdk.ts | 2 -- packages/replay-internal/test/mocks/resetSdkMock.ts | 2 -- .../test/unit/coreHandlers/util/xhrUtils.test.ts | 4 +++- .../test/unit/util/handleRecordingEmit.test.ts | 3 +++ 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/replay-internal/test/integration/session.test.ts b/packages/replay-internal/test/integration/session.test.ts index 678450104877..fb1327296ad2 100644 --- a/packages/replay-internal/test/integration/session.test.ts +++ b/packages/replay-internal/test/integration/session.test.ts @@ -357,7 +357,7 @@ describe('Integration | session', () => { await vi.runAllTimersAsync(); - expect(replay.session.id).not.toBe(initialSession.id); + expect(replay).not.toHaveSameSession(initialSession); expect(replay).not.toHaveLastSentReplay(); expect(replay['_stopRecording']).toBeDefined(); diff --git a/packages/replay-internal/test/mocks/mockSdk.ts b/packages/replay-internal/test/mocks/mockSdk.ts index 512e70950136..c899d7ab77a6 100644 --- a/packages/replay-internal/test/mocks/mockSdk.ts +++ b/packages/replay-internal/test/mocks/mockSdk.ts @@ -5,7 +5,6 @@ import type { Replay as ReplayIntegration } from '../../src/integration'; import type { ReplayContainer } from '../../src/replay'; import type { ReplayConfiguration } from '../../src/types'; import type { TestClientOptions } from '../utils/TestClient'; -import { BASE_TIMESTAMP } from './../index'; import { getDefaultClientOptions, init } from '../utils/TestClient'; export interface MockSdkParams { @@ -52,7 +51,6 @@ export async function mockSdk({ replayOptions, sentryOptions, autoStart = true } }> { const { Replay } = await import('../../src/integration'); - vi.setSystemTime(new Date(BASE_TIMESTAMP)); // Scope this to the test, instead of the module let _initialized = false; class TestReplayIntegration extends Replay { diff --git a/packages/replay-internal/test/mocks/resetSdkMock.ts b/packages/replay-internal/test/mocks/resetSdkMock.ts index e5db1657ac12..5bacb433e0f3 100644 --- a/packages/replay-internal/test/mocks/resetSdkMock.ts +++ b/packages/replay-internal/test/mocks/resetSdkMock.ts @@ -39,8 +39,6 @@ export async function resetSdkMock({ replayOptions, sentryOptions, autoStart }: // XXX: This is needed to ensure `domHandler` is set await vi.runAllTimersAsync(); - // vi.runAllTimers(); - // await new Promise(process.nextTick); vi.setSystemTime(new Date(BASE_TIMESTAMP)); return { diff --git a/packages/replay-internal/test/unit/coreHandlers/util/xhrUtils.test.ts b/packages/replay-internal/test/unit/coreHandlers/util/xhrUtils.test.ts index 83086be6bf3c..6c805e6d2238 100644 --- a/packages/replay-internal/test/unit/coreHandlers/util/xhrUtils.test.ts +++ b/packages/replay-internal/test/unit/coreHandlers/util/xhrUtils.test.ts @@ -9,7 +9,9 @@ describe('Unit | coreHandlers | util | xhrUtils', () => { it('works with a Document', () => { const doc = document.implementation.createHTMLDocument(); - doc.body.innerHTML = '
abc
'; + const bodyEl = document.createElement('body'); + bodyEl.innerHTML = '
abc
'; + doc.body = bodyEl; const actual = _parseXhrResponse(doc, ''); expect(actual).toEqual(['
abc
']); diff --git a/packages/replay-internal/test/unit/util/handleRecordingEmit.test.ts b/packages/replay-internal/test/unit/util/handleRecordingEmit.test.ts index fb7eacfa032e..975762565e17 100644 --- a/packages/replay-internal/test/unit/util/handleRecordingEmit.test.ts +++ b/packages/replay-internal/test/unit/util/handleRecordingEmit.test.ts @@ -1,3 +1,6 @@ +import { vi } from 'vitest'; +import type { MockInstance } from 'vitest'; + import { EventType } from '@sentry-internal/rrweb'; import { BASE_TIMESTAMP } from '../..'; From 83162b6665b97a6504574480cfc8e80d04c7e21d Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Sun, 5 May 2024 12:54:17 -0400 Subject: [PATCH 06/11] remove only --- .../replay-internal/test/integration/errorSampleRate.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/replay-internal/test/integration/errorSampleRate.test.ts b/packages/replay-internal/test/integration/errorSampleRate.test.ts index 28f1d1192904..38eb5472c46d 100644 --- a/packages/replay-internal/test/integration/errorSampleRate.test.ts +++ b/packages/replay-internal/test/integration/errorSampleRate.test.ts @@ -734,7 +734,7 @@ describe('Integration | errorSampleRate', () => { expect(replay.getSessionId()).not.toBe(sessionId); }); - it.only('refreshes replay when session exceeds max length after latest captured error', async () => { + it('refreshes replay when session exceeds max length after latest captured error', async () => { const sessionId = replay.session?.id; vi.setSystemTime(BASE_TIMESTAMP); @@ -827,6 +827,7 @@ describe('Integration | errorSampleRate', () => { // Now capture an error captureException(new Error('testing')); await vi.advanceTimersToNextTimerAsync(); + await vi.advanceTimersToNextTimerAsync(); expect(replay).toHaveLastSentReplay({ recordingData: JSON.stringify([ From ceca10e8e372c9b24fe1d3827df2beb63a46d6b3 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Sun, 5 May 2024 12:57:44 -0400 Subject: [PATCH 07/11] lint --- packages/replay-internal/test.setup.ts | 26 ++++++++----------- .../test/integration/errorSampleRate.test.ts | 1 - .../test/integration/flush.test.ts | 7 +---- .../replay-internal/test/utils/TestClient.ts | 2 -- packages/replay-internal/tsconfig.json | 2 +- 5 files changed, 13 insertions(+), 25 deletions(-) diff --git a/packages/replay-internal/test.setup.ts b/packages/replay-internal/test.setup.ts index 5e0aa8ff49a5..5b7e16b97481 100644 --- a/packages/replay-internal/test.setup.ts +++ b/packages/replay-internal/test.setup.ts @@ -1,5 +1,5 @@ import { vi } from 'vitest'; -import type { Assertion, AsymmetricMatchersContaining, Mocked, MockedFunction } from 'vitest'; +import type { Mocked, MockedFunction } from 'vitest'; import { printDiffOrStringify } from 'jest-matcher-utils'; /* eslint-disable @typescript-eslint/no-unsafe-member-access */ @@ -101,6 +101,7 @@ function checkCallForSentReplay( : (expected as SentReplayExpected); if (isObjectContaining) { + // eslint-disable-next-line no-console console.warn('`expect.objectContaining` is unnecessary when using the `toHaveSentReplay` matcher'); } @@ -242,18 +243,13 @@ expect.extend({ toHaveLastSentReplay, }); -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace vi { - interface AsymmetricMatchers { - toHaveSentReplay(expected?: SentReplayExpected): void; - toHaveLastSentReplay(expected?: SentReplayExpected): void; - toHaveSameSession(expected: undefined | Session): void; - } - interface Matchers { - toHaveSentReplay(expected?: SentReplayExpected): R; - toHaveLastSentReplay(expected?: SentReplayExpected): R; - toHaveSameSession(expected: undefined | Session): R; - } - } +interface CustomMatchers { + toHaveSentReplay(expected?: SentReplayExpected): R; + toHaveLastSentReplay(expected?: SentReplayExpected): R; + toHaveSameSession(expected: undefined | Session): R; +} + +declare module 'vitest' { + type Assertion = CustomMatchers; + type AsymmetricMatchersContaining = CustomMatchers; } diff --git a/packages/replay-internal/test/integration/errorSampleRate.test.ts b/packages/replay-internal/test/integration/errorSampleRate.test.ts index 38eb5472c46d..d5f6d02e31bb 100644 --- a/packages/replay-internal/test/integration/errorSampleRate.test.ts +++ b/packages/replay-internal/test/integration/errorSampleRate.test.ts @@ -640,7 +640,6 @@ describe('Integration | errorSampleRate', () => { it('has correct timestamps when error occurs much later than initial pageload/checkout', async () => { const ELAPSED = BUFFER_CHECKOUT_TIME; - const TICK = 20; const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); mockRecord._emitter(TEST_EVENT); diff --git a/packages/replay-internal/test/integration/flush.test.ts b/packages/replay-internal/test/integration/flush.test.ts index b7c9b263c24a..bb00da732df3 100644 --- a/packages/replay-internal/test/integration/flush.test.ts +++ b/packages/replay-internal/test/integration/flush.test.ts @@ -1,5 +1,5 @@ import { vi } from 'vitest'; -import type { MockedFunction, MockInstance } from 'vitest'; +import type { MockedFunction } from 'vitest'; import * as SentryBrowserUtils from '@sentry-internal/browser-utils'; import * as SentryUtils from '@sentry/utils'; @@ -8,7 +8,6 @@ import { DEFAULT_FLUSH_MIN_DELAY, MAX_REPLAY_DURATION, WINDOW } from '../../src/ import type { ReplayContainer } from '../../src/replay'; import { clearSession } from '../../src/session/clearSession'; import type { EventBuffer } from '../../src/types'; -import * as AddMemoryEntry from '../../src/util/addMemoryEntry'; import { createPerformanceEntries } from '../../src/util/createPerformanceEntries'; import { createPerformanceSpans } from '../../src/util/createPerformanceSpans'; import * as SendReplay from '../../src/util/sendReplay'; @@ -21,7 +20,6 @@ useFakeTimers(); type MockSendReplay = MockedFunction; type MockAddPerformanceEntries = MockedFunction; -type MockAddMemoryEntry = MockInstance; type MockEventBufferFinish = MockedFunction; type MockFlush = MockedFunction; type MockRunFlush = MockedFunction; @@ -39,7 +37,6 @@ describe('Integration | flush', () => { let mockFlush: MockFlush; let mockRunFlush: MockRunFlush; let mockEventBufferFinish: MockEventBufferFinish; - let mockAddMemoryEntry: MockAddMemoryEntry; let mockAddPerformanceEntries: MockAddPerformanceEntries; beforeAll(async () => { @@ -68,8 +65,6 @@ describe('Integration | flush', () => { mockAddPerformanceEntries.mockImplementation(async () => { return []; }); - - mockAddMemoryEntry = vi.spyOn(AddMemoryEntry, 'addMemoryEntry'); }); beforeEach(async () => { diff --git a/packages/replay-internal/test/utils/TestClient.ts b/packages/replay-internal/test/utils/TestClient.ts index 26a14f2a9795..f95eb5309bcb 100644 --- a/packages/replay-internal/test/utils/TestClient.ts +++ b/packages/replay-internal/test/utils/TestClient.ts @@ -20,10 +20,8 @@ export class TestClient extends BaseClient { exception: { values: [ { - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ type: exception.name, value: exception.message, - /* eslint-enable @typescript-eslint/no-unsafe-member-access */ }, ], }, diff --git a/packages/replay-internal/tsconfig.json b/packages/replay-internal/tsconfig.json index f8f54556da93..c0374f57b7a8 100644 --- a/packages/replay-internal/tsconfig.json +++ b/packages/replay-internal/tsconfig.json @@ -3,5 +3,5 @@ "compilerOptions": { "module": "esnext" }, - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts", "./test.setup.ts", "./vitest.config.ts"] } From cde0779f9ad23bec778c44edf9bfc7e00e13f7a1 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Sun, 5 May 2024 13:08:30 -0400 Subject: [PATCH 08/11] fix test config files --- packages/replay-internal/.eslintrc.js | 2 +- packages/replay-internal/jest.config.ts | 17 ----------------- packages/replay-internal/tsconfig.json | 2 +- packages/replay-internal/tsconfig.test.json | 2 +- 4 files changed, 3 insertions(+), 20 deletions(-) delete mode 100644 packages/replay-internal/jest.config.ts diff --git a/packages/replay-internal/.eslintrc.js b/packages/replay-internal/.eslintrc.js index 6af926975df0..dc39a10b354b 100644 --- a/packages/replay-internal/.eslintrc.js +++ b/packages/replay-internal/.eslintrc.js @@ -10,7 +10,7 @@ module.exports = { files: ['src/**/*.ts'], }, { - files: ['jest.setup.ts', 'jest.config.ts'], + files: ['test.setup.ts', 'vitest.config.ts'], parserOptions: { project: ['tsconfig.test.json'], }, diff --git a/packages/replay-internal/jest.config.ts b/packages/replay-internal/jest.config.ts deleted file mode 100644 index 90a3cf471f8d..000000000000 --- a/packages/replay-internal/jest.config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Config } from '@jest/types'; -import { jsWithTs as jsWithTsPreset } from 'ts-jest/presets'; - -export default async (): Promise => { - return { - ...jsWithTsPreset, - globals: { - 'ts-jest': { - tsconfig: '/tsconfig.test.json', - }, - __DEBUG_BUILD__: true, - }, - setupFilesAfterEnv: ['./jest.setup.ts'], - testEnvironment: 'jsdom', - testMatch: ['/test/**/*(*.)@(spec|test).ts'], - }; -}; diff --git a/packages/replay-internal/tsconfig.json b/packages/replay-internal/tsconfig.json index c0374f57b7a8..f8f54556da93 100644 --- a/packages/replay-internal/tsconfig.json +++ b/packages/replay-internal/tsconfig.json @@ -3,5 +3,5 @@ "compilerOptions": { "module": "esnext" }, - "include": ["src/**/*.ts", "./test.setup.ts", "./vitest.config.ts"] + "include": ["src/**/*.ts"] } diff --git a/packages/replay-internal/tsconfig.test.json b/packages/replay-internal/tsconfig.test.json index ad87caa06c48..1e96fa6b8945 100644 --- a/packages/replay-internal/tsconfig.test.json +++ b/packages/replay-internal/tsconfig.test.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*.ts", "jest.config.ts", "jest.setup.ts"], + "include": ["test/**/*.ts", "vitest.config.ts", "test.setup.ts"], "compilerOptions": { "types": ["node", "jest"], From 1864c679fffaa43c7782968de9dd04cb5afd3559 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Sun, 5 May 2024 14:21:32 -0400 Subject: [PATCH 09/11] jsdom 21.1.2 --- yarn.lock | 195 +++++++++++++++++++++++++----------------------------- 1 file changed, 89 insertions(+), 106 deletions(-) diff --git a/yarn.lock b/yarn.lock index a1a39a471547..116e83f73d2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9950,6 +9950,14 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" +acorn-globals@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" + integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== + dependencies: + acorn "^8.1.0" + acorn-walk "^8.0.2" + acorn-import-assertions@^1.7.6: version "1.8.0" resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" @@ -9970,7 +9978,7 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.0.0, acorn-walk@^8.2.0, acorn-walk@^8.3.2: +acorn-walk@^8.0.0, acorn-walk@^8.0.2, acorn-walk@^8.2.0, acorn-walk@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== @@ -9995,7 +10003,7 @@ acorn@^7.1.1, acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.4, acorn@^8.11.3: +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.11.3: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -10040,13 +10048,6 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" -agent-base@^7.0.2, agent-base@^7.1.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" - integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== - dependencies: - debug "^4.3.4" - agentkeepalive@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" @@ -13666,10 +13667,10 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -cssstyle@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.0.1.tgz#ef29c598a1e90125c870525490ea4f354db0660a" - integrity sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ== +cssstyle@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" + integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg== dependencies: rrweb-cssom "^0.6.0" @@ -13726,13 +13727,14 @@ data-urls@^3.0.1: whatwg-mimetype "^3.0.0" whatwg-url "^10.0.0" -data-urls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" - integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== +data-urls@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" + integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g== dependencies: - whatwg-mimetype "^4.0.0" - whatwg-url "^14.0.0" + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.0" date-fns@^2.29.2: version "2.29.3" @@ -15283,7 +15285,7 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@^4.4.0, entities@^4.5.0: +entities@^4.4.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== @@ -17939,15 +17941,6 @@ handlebars@^4.0.4, handlebars@^4.3.1, handlebars@^4.7.3, handlebars@^4.7.6, hand optionalDependencies: uglify-js "^3.1.4" -happy-dom@^14.7.1: - version "14.7.1" - resolved "https://registry.yarnpkg.com/happy-dom/-/happy-dom-14.7.1.tgz#4356617f5fcb722c26ccd4c5c8a235cead675592" - integrity sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg== - dependencies: - entities "^4.5.0" - webidl-conversions "^7.0.0" - whatwg-mimetype "^3.0.0" - hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -18458,13 +18451,6 @@ html-encoding-sniffer@^3.0.0: dependencies: whatwg-encoding "^2.0.0" -html-encoding-sniffer@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" - integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== - dependencies: - whatwg-encoding "^3.1.1" - html-entities@^2.3.2: version "2.5.2" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" @@ -18588,14 +18574,6 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" -http-proxy-agent@^7.0.0: - version "7.0.2" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" - integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== - dependencies: - agent-base "^7.1.0" - debug "^4.3.4" - http-proxy-middleware@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" @@ -18631,7 +18609,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0: +https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -18647,14 +18625,6 @@ https-proxy-agent@^4.0.0: agent-base "5" debug "4" -https-proxy-agent@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" - integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== - dependencies: - agent-base "^7.0.2" - debug "4" - https@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https/-/https-1.0.0.tgz#3c37c7ae1a8eeb966904a2ad1e975a194b7ed3a4" @@ -19850,6 +19820,16 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-docblock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" @@ -19903,6 +19883,11 @@ jest-get-type@^29.4.3: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + jest-haste-map@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" @@ -19964,6 +19949,16 @@ jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-matcher-utils@^29.0.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-message-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" @@ -20367,32 +20362,37 @@ jsdom@^19.0.0: ws "^8.2.3" xml-name-validator "^4.0.0" -jsdom@^24.0.0: - version "24.0.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-24.0.0.tgz#e2dc04e4c79da368481659818ee2b0cd7c39007c" - integrity sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A== +jsdom@^21.1.2: + version "21.1.2" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-21.1.2.tgz#6433f751b8718248d646af1cdf6662dc8a1ca7f9" + integrity sha512-sCpFmK2jv+1sjff4u7fzft+pUh2KSUbUrEHYHyfSIbGTIcmnjyp83qg6qLwdJ/I3LpTXx33ACxeRL7Lsyc6lGQ== dependencies: - cssstyle "^4.0.1" - data-urls "^5.0.0" + abab "^2.0.6" + acorn "^8.8.2" + acorn-globals "^7.0.0" + cssstyle "^3.0.0" + data-urls "^4.0.0" decimal.js "^10.4.3" + domexception "^4.0.0" + escodegen "^2.0.0" form-data "^4.0.0" - html-encoding-sniffer "^4.0.0" - http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.2" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.7" + nwsapi "^2.2.4" parse5 "^7.1.2" rrweb-cssom "^0.6.0" saxes "^6.0.0" symbol-tree "^3.2.4" - tough-cookie "^4.1.3" - w3c-xmlserializer "^5.0.0" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" webidl-conversions "^7.0.0" - whatwg-encoding "^3.1.1" - whatwg-mimetype "^4.0.0" - whatwg-url "^14.0.0" - ws "^8.16.0" - xml-name-validator "^5.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.1" + ws "^8.13.0" + xml-name-validator "^4.0.0" jsesc@^2.5.1: version "2.5.2" @@ -23619,7 +23619,7 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== -nwsapi@^2.2.7: +nwsapi@^2.2.4: version "2.2.9" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.9.tgz#7f3303218372db2e9f27c27766bcfc59ae7e61c6" integrity sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg== @@ -25690,7 +25690,7 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -punycode@^2.3.1: +punycode@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== @@ -29125,7 +29125,7 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" -tough-cookie@^4.1.3: +tough-cookie@^4.1.2: version "4.1.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== @@ -29156,12 +29156,12 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" -tr46@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec" - integrity sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g== +tr46@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" + integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw== dependencies: - punycode "^2.3.1" + punycode "^2.3.0" tr46@~0.0.3: version "0.0.3" @@ -30368,12 +30368,12 @@ w3c-xmlserializer@^3.0.0: dependencies: xml-name-validator "^4.0.0" -w3c-xmlserializer@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" - integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== dependencies: - xml-name-validator "^5.0.0" + xml-name-validator "^4.0.0" walk-sync@^0.2.5: version "0.2.7" @@ -30790,13 +30790,6 @@ whatwg-encoding@^2.0.0: dependencies: iconv-lite "0.6.3" -whatwg-encoding@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" - integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== - dependencies: - iconv-lite "0.6.3" - whatwg-fetch@>=0.10.0: version "3.6.2" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" @@ -30812,11 +30805,6 @@ whatwg-mimetype@^3.0.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== -whatwg-mimetype@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" - integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== - whatwg-url@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da" @@ -30825,12 +30813,12 @@ whatwg-url@^10.0.0: tr46 "^3.0.0" webidl-conversions "^7.0.0" -whatwg-url@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.0.0.tgz#00baaa7fd198744910c4b1ef68378f2200e4ceb6" - integrity sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw== +whatwg-url@^12.0.0, whatwg-url@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" + integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ== dependencies: - tr46 "^5.0.0" + tr46 "^4.1.1" webidl-conversions "^7.0.0" whatwg-url@^5.0.0: @@ -31127,7 +31115,7 @@ ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== -ws@^8.16.0: +ws@^8.13.0: version "8.17.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea" integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== @@ -31162,11 +31150,6 @@ xml-name-validator@^4.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== -xml-name-validator@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" - integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== - xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" From 2b2ea3db6d4bfafaa1d5ac4e82efded829c02be8 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Sun, 5 May 2024 14:21:37 -0400 Subject: [PATCH 10/11] lint --- packages/replay-internal/package.json | 6 ++++-- packages/replay-internal/test.setup.ts | 2 +- .../test/integration/autoSaveSession.test.ts | 2 +- .../test/integration/beforeAddRecordingEvent.test.ts | 2 +- .../test/integration/errorSampleRate.test.ts | 2 +- .../test/integration/sendReplayEvent.test.ts | 2 +- packages/replay-internal/test/integration/stop.test.ts | 2 +- packages/replay-internal/test/mocks/mockSdk.ts | 2 +- packages/replay-internal/test/mocks/resetSdkMock.ts | 2 +- 9 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json index e6ba49eb1d54..4a1b4ecdd707 100644 --- a/packages/replay-internal/package.json +++ b/packages/replay-internal/package.json @@ -51,7 +51,9 @@ "build:tarball": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", "circularDepCheck": "madge --circular src/index.ts", "clean": "rimraf build sentry-replay-*.tgz", - "fix": "eslint . --format stylish --fix", + "fix": "run-s fix:biome fix:eslint", + "fix:eslint": "eslint . --format stylish --fix", + "fix:biome": "biome check --apply .", "lint": "eslint . --format stylish", "test": "vitest", "test:watch": "vitest --watch", @@ -74,7 +76,7 @@ "@sentry-internal/rrweb-snapshot": "2.15.0", "fflate": "^0.8.1", "jest-matcher-utils": "^29.0.0", - "jsdom": "^24.0.0", + "jsdom": "^21.1.2", "jsdom-worker": "^0.2.1", "vitest": "^1.5.3" }, diff --git a/packages/replay-internal/test.setup.ts b/packages/replay-internal/test.setup.ts index 5b7e16b97481..05a762e60d50 100644 --- a/packages/replay-internal/test.setup.ts +++ b/packages/replay-internal/test.setup.ts @@ -1,6 +1,6 @@ +import { printDiffOrStringify } from 'jest-matcher-utils'; import { vi } from 'vitest'; import type { Mocked, MockedFunction } from 'vitest'; -import { printDiffOrStringify } from 'jest-matcher-utils'; /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { getClient } from '@sentry/core'; diff --git a/packages/replay-internal/test/integration/autoSaveSession.test.ts b/packages/replay-internal/test/integration/autoSaveSession.test.ts index 93a4abf59709..7fb3951756ae 100644 --- a/packages/replay-internal/test/integration/autoSaveSession.test.ts +++ b/packages/replay-internal/test/integration/autoSaveSession.test.ts @@ -2,11 +2,11 @@ import { vi } from 'vitest'; import { EventType } from '@sentry-internal/rrweb'; +import { saveSession } from '../../src/session/saveSession'; import type { RecordingEvent } from '../../src/types'; import { addEvent } from '../../src/util/addEvent'; import { resetSdkMock } from '../mocks/resetSdkMock'; import { useFakeTimers } from '../utils/use-fake-timers'; -import { saveSession } from '../../src/session/saveSession'; useFakeTimers(); diff --git a/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts b/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts index a29d142ac374..7f8495375864 100644 --- a/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts +++ b/packages/replay-internal/test/integration/beforeAddRecordingEvent.test.ts @@ -1,5 +1,5 @@ import { vi } from 'vitest'; -import type { MockedFunction, MockInstance } from 'vitest'; +import type { MockInstance, MockedFunction } from 'vitest'; import * as SentryBrowserUtils from '@sentry-internal/browser-utils'; import * as SentryCore from '@sentry/core'; diff --git a/packages/replay-internal/test/integration/errorSampleRate.test.ts b/packages/replay-internal/test/integration/errorSampleRate.test.ts index d5f6d02e31bb..b3b605f28c12 100644 --- a/packages/replay-internal/test/integration/errorSampleRate.test.ts +++ b/packages/replay-internal/test/integration/errorSampleRate.test.ts @@ -1,5 +1,5 @@ -import { vi } from 'vitest'; import { captureException, getClient } from '@sentry/core'; +import { vi } from 'vitest'; import { BUFFER_CHECKOUT_TIME, diff --git a/packages/replay-internal/test/integration/sendReplayEvent.test.ts b/packages/replay-internal/test/integration/sendReplayEvent.test.ts index 725a26ea50ef..8e99f72ff517 100644 --- a/packages/replay-internal/test/integration/sendReplayEvent.test.ts +++ b/packages/replay-internal/test/integration/sendReplayEvent.test.ts @@ -1,5 +1,5 @@ import { vi } from 'vitest'; -import type { MockedFunction, MockInstance } from 'vitest'; +import type { MockInstance, MockedFunction } from 'vitest'; import * as SentryBrowserUtils from '@sentry-internal/browser-utils'; import * as SentryCore from '@sentry/core'; diff --git a/packages/replay-internal/test/integration/stop.test.ts b/packages/replay-internal/test/integration/stop.test.ts index 38af135611bb..b4d256f3f9f0 100644 --- a/packages/replay-internal/test/integration/stop.test.ts +++ b/packages/replay-internal/test/integration/stop.test.ts @@ -1,5 +1,5 @@ import { vi } from 'vitest'; -import type { MockedFunction, MockInstance } from 'vitest'; +import type { MockInstance, MockedFunction } from 'vitest'; import * as SentryBrowserUtils from '@sentry-internal/browser-utils'; diff --git a/packages/replay-internal/test/mocks/mockSdk.ts b/packages/replay-internal/test/mocks/mockSdk.ts index c899d7ab77a6..d5c9a088b779 100644 --- a/packages/replay-internal/test/mocks/mockSdk.ts +++ b/packages/replay-internal/test/mocks/mockSdk.ts @@ -1,5 +1,5 @@ -import { vi } from 'vitest'; import type { Envelope, Transport, TransportMakeRequestResponse } from '@sentry/types'; +import { vi } from 'vitest'; import type { Replay as ReplayIntegration } from '../../src/integration'; import type { ReplayContainer } from '../../src/replay'; diff --git a/packages/replay-internal/test/mocks/resetSdkMock.ts b/packages/replay-internal/test/mocks/resetSdkMock.ts index 5bacb433e0f3..ff2006b0604e 100644 --- a/packages/replay-internal/test/mocks/resetSdkMock.ts +++ b/packages/replay-internal/test/mocks/resetSdkMock.ts @@ -1,5 +1,5 @@ -import { vi } from 'vitest'; import { resetInstrumentationHandlers } from '@sentry/utils'; +import { vi } from 'vitest'; import type { Replay as ReplayIntegration } from '../../src/integration'; import type { ReplayContainer } from '../../src/replay'; From a42012e7e8f2002948db63c123516681e7d48e47 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Mon, 6 May 2024 12:14:56 -0400 Subject: [PATCH 11/11] use deps from root package.json --- packages/replay-internal/package.json | 4 +- yarn.lock | 81 --------------------------- 2 files changed, 1 insertion(+), 84 deletions(-) diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json index 5f70af1068bf..e10f2927990f 100644 --- a/packages/replay-internal/package.json +++ b/packages/replay-internal/package.json @@ -76,9 +76,7 @@ "@sentry-internal/rrweb-snapshot": "2.15.0", "fflate": "^0.8.1", "jest-matcher-utils": "^29.0.0", - "jsdom": "^21.1.2", - "jsdom-worker": "^0.2.1", - "vitest": "^1.5.3" + "jsdom-worker": "^0.2.1" }, "dependencies": { "@sentry-internal/browser-utils": "8.0.0-beta.6", diff --git a/yarn.lock b/yarn.lock index c53e32a49f0b..299357635fca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9113,15 +9113,6 @@ strip-literal "^2.0.0" test-exclude "^6.0.0" -"@vitest/expect@1.5.3": - version "1.5.3" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.5.3.tgz#34198e2123fb5be68f606729114aadbd071d77dc" - integrity sha512-y+waPz31pOFr3rD7vWTbwiLe5+MgsMm40jTZbQE8p8/qXyBX3CQsIXRx9XK12IbY7q/t5a5aM/ckt33b4PxK2g== - dependencies: - "@vitest/spy" "1.5.3" - "@vitest/utils" "1.5.3" - chai "^4.3.10" - "@vitest/expect@1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.6.0.tgz#0b3ba0914f738508464983f4d811bc122b51fb30" @@ -9131,15 +9122,6 @@ "@vitest/utils" "1.6.0" chai "^4.3.10" -"@vitest/runner@1.5.3": - version "1.5.3" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.5.3.tgz#226a726ca0bf11c1f287fa867547bdfca072b1e6" - integrity sha512-7PlfuReN8692IKQIdCxwir1AOaP5THfNkp0Uc4BKr2na+9lALNit7ub9l3/R7MP8aV61+mHKRGiqEKRIwu6iiQ== - dependencies: - "@vitest/utils" "1.5.3" - p-limit "^5.0.0" - pathe "^1.1.1" - "@vitest/runner@1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.6.0.tgz#a6de49a96cb33b0e3ba0d9064a3e8d6ce2f08825" @@ -9149,15 +9131,6 @@ p-limit "^5.0.0" pathe "^1.1.1" -"@vitest/snapshot@1.5.3": - version "1.5.3" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.5.3.tgz#ffdd917daebf4415c7abad6993bafd5f4ee14aaf" - integrity sha512-K3mvIsjyKYBhNIDujMD2gfQEzddLe51nNOAf45yKRt/QFJcUIeTQd2trRvv6M6oCBHNVnZwFWbQ4yj96ibiDsA== - dependencies: - magic-string "^0.30.5" - pathe "^1.1.1" - pretty-format "^29.7.0" - "@vitest/snapshot@1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.6.0.tgz#deb7e4498a5299c1198136f56e6e0f692e6af470" @@ -9167,13 +9140,6 @@ pathe "^1.1.1" pretty-format "^29.7.0" -"@vitest/spy@1.5.3": - version "1.5.3" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.5.3.tgz#a81dfa87e4af3fe2c5d6f84cc81be31580b05e2c" - integrity sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg== - dependencies: - tinyspy "^2.2.0" - "@vitest/spy@1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.6.0.tgz#362cbd42ccdb03f1613798fde99799649516906d" @@ -9181,16 +9147,6 @@ dependencies: tinyspy "^2.2.0" -"@vitest/utils@1.5.3": - version "1.5.3" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.5.3.tgz#216068c28db577480ca77e0b2094f0b1fa29bbcd" - integrity sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA== - dependencies: - diff-sequences "^29.6.3" - estree-walker "^3.0.3" - loupe "^2.3.7" - pretty-format "^29.7.0" - "@vitest/utils@1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.6.0.tgz#5c5675ca7d6f546a7b4337de9ae882e6c57896a1" @@ -29954,17 +29910,6 @@ vfile@^6.0.0: unist-util-stringify-position "^4.0.0" vfile-message "^4.0.0" -vite-node@1.5.3: - version "1.5.3" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.5.3.tgz#498f4eb6f4e37ff95f66ffb9c905708a75f84b2e" - integrity sha512-axFo00qiCpU/JLd8N1gu9iEYL3xTbMbMrbe5nDp9GL0nb6gurIdZLkkFogZXWnE8Oyy5kfSLwNVIcVsnhE7lgQ== - dependencies: - cac "^6.7.14" - debug "^4.3.4" - pathe "^1.1.1" - picocolors "^1.0.0" - vite "^5.0.0" - vite-node@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.6.0.tgz#2c7e61129bfecc759478fa592754fd9704aaba7f" @@ -30030,32 +29975,6 @@ vitefu@^0.2.4: resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.4.tgz#212dc1a9d0254afe65e579351bed4e25d81e0b35" integrity sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g== -vitest@^1.5.3: - version "1.5.3" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.5.3.tgz#48880013373af16fa4c60a07dd3409fe19397a16" - integrity sha512-2oM7nLXylw3mQlW6GXnRriw+7YvZFk/YNV8AxIC3Z3MfFbuziLGWP9GPxxu/7nRlXhqyxBikpamr+lEEj1sUEw== - dependencies: - "@vitest/expect" "1.5.3" - "@vitest/runner" "1.5.3" - "@vitest/snapshot" "1.5.3" - "@vitest/spy" "1.5.3" - "@vitest/utils" "1.5.3" - acorn-walk "^8.3.2" - chai "^4.3.10" - debug "^4.3.4" - execa "^8.0.1" - local-pkg "^0.5.0" - magic-string "^0.30.5" - pathe "^1.1.1" - picocolors "^1.0.0" - std-env "^3.5.0" - strip-literal "^2.0.0" - tinybench "^2.5.1" - tinypool "^0.8.3" - vite "^5.0.0" - vite-node "1.5.3" - why-is-node-running "^2.2.2" - vitest@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.6.0.tgz#9d5ad4752a3c451be919e412c597126cffb9892f"