From 1216f57dcf30845ce23e59bbb27bafff2086bad7 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Mon, 13 May 2024 09:57:55 -0400 Subject: [PATCH 1/2] fix(replay): Fix user activity not being updated in `start()` Closes #11983 --- packages/replay-internal/src/replay.ts | 7 ++ .../test/integration/start.test.ts | 68 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 packages/replay-internal/test/integration/start.test.ts diff --git a/packages/replay-internal/src/replay.ts b/packages/replay-internal/src/replay.ts index 6588a9e4d79d..16f7a6e7d884 100644 --- a/packages/replay-internal/src/replay.ts +++ b/packages/replay-internal/src/replay.ts @@ -28,6 +28,7 @@ import { clearSession } from './session/clearSession'; import { loadOrCreateSession } from './session/loadOrCreateSession'; import { saveSession } from './session/saveSession'; import { shouldRefreshSession } from './session/shouldRefreshSession'; + import type { AddEventResult, AddUpdateCallback, @@ -295,6 +296,12 @@ export class ReplayContainer implements ReplayContainerInterface { logInfoNextTick('[Replay] Starting replay in session mode', this._options._experiments.traceInternals); + // Required as user activity is initially set in + // constructor, so if `start()` is called after + // session idle expiration, a replay will not be + // created due to an idle timeout. + this._updateUserActivity(); + const session = loadOrCreateSession( { maxReplayDuration: this._options.maxReplayDuration, diff --git a/packages/replay-internal/test/integration/start.test.ts b/packages/replay-internal/test/integration/start.test.ts new file mode 100644 index 000000000000..e07719007ccc --- /dev/null +++ b/packages/replay-internal/test/integration/start.test.ts @@ -0,0 +1,68 @@ +import { vi } from 'vitest'; + +import { getClient } from '@sentry/core'; +import type { Transport } from '@sentry/types'; + +import { + DEFAULT_FLUSH_MIN_DELAY, + MAX_REPLAY_DURATION, + SESSION_IDLE_EXPIRE_DURATION, + WINDOW, +} from '../../src/constants'; +import type { ReplayContainer } from '../../src/replay'; +import { BASE_TIMESTAMP } from '../index'; +import type { RecordMock } from '../mocks/mockRrweb'; +import { resetSdkMock } from '../mocks/resetSdkMock'; +import type { DomHandler } from '../types'; +import { useFakeTimers } from '../utils/use-fake-timers'; +import type { Replay } from '../../src/integration'; + +useFakeTimers(); + +const prevLocation = WINDOW.location; + +describe('Integration | start', () => { + let replay: ReplayContainer; + let integration: Replay; + let domHandler: DomHandler; + let mockRecord: RecordMock; + + beforeEach(async () => { + ({ mockRecord, domHandler, replay, integration } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 0.0, + }, + })); + + const mockTransport = getClient()?.getTransport()?.send as vi.MockedFunction; + mockTransport?.mockClear(); + await vi.runAllTimersAsync(); + }); + + afterEach(async () => { + integration.stop(); + + await vi.runAllTimersAsync(); + vi.setSystemTime(new Date(BASE_TIMESTAMP)); + + Object.defineProperty(WINDOW, 'location', { + value: prevLocation, + writable: true, + }); + }); + + it('sends replay when calling `start()` after [SESSION_IDLE_EXPIRE_DURATION]ms', async () => { + await vi.advanceTimersByTimeAsync(SESSION_IDLE_EXPIRE_DURATION + 1); + + integration.start(); + + await vi.advanceTimersByTimeAsync(DEFAULT_FLUSH_MIN_DELAY); + + expect(replay).toHaveLastSentReplay({ + recordingPayloadHeader: { segment_id: 0 }, + }); + }); +}); From 1f91a37fd7c69339ff96a9dfbb9fde37892f23af Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Mon, 13 May 2024 10:03:13 -0400 Subject: [PATCH 2/2] lint --- .../test/integration/start.test.ts | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/packages/replay-internal/test/integration/start.test.ts b/packages/replay-internal/test/integration/start.test.ts index e07719007ccc..dff5df38b53d 100644 --- a/packages/replay-internal/test/integration/start.test.ts +++ b/packages/replay-internal/test/integration/start.test.ts @@ -3,32 +3,21 @@ import { vi } from 'vitest'; import { getClient } from '@sentry/core'; import type { Transport } from '@sentry/types'; -import { - DEFAULT_FLUSH_MIN_DELAY, - MAX_REPLAY_DURATION, - SESSION_IDLE_EXPIRE_DURATION, - WINDOW, -} from '../../src/constants'; +import { DEFAULT_FLUSH_MIN_DELAY, SESSION_IDLE_EXPIRE_DURATION } from '../../src/constants'; +import type { Replay } from '../../src/integration'; import type { ReplayContainer } from '../../src/replay'; import { BASE_TIMESTAMP } from '../index'; -import type { RecordMock } from '../mocks/mockRrweb'; import { resetSdkMock } from '../mocks/resetSdkMock'; -import type { DomHandler } from '../types'; import { useFakeTimers } from '../utils/use-fake-timers'; -import type { Replay } from '../../src/integration'; useFakeTimers(); -const prevLocation = WINDOW.location; - describe('Integration | start', () => { let replay: ReplayContainer; let integration: Replay; - let domHandler: DomHandler; - let mockRecord: RecordMock; beforeEach(async () => { - ({ mockRecord, domHandler, replay, integration } = await resetSdkMock({ + ({ replay, integration } = await resetSdkMock({ replayOptions: { stickySession: false, }, @@ -47,11 +36,6 @@ describe('Integration | start', () => { await vi.runAllTimersAsync(); vi.setSystemTime(new Date(BASE_TIMESTAMP)); - - Object.defineProperty(WINDOW, 'location', { - value: prevLocation, - writable: true, - }); }); it('sends replay when calling `start()` after [SESSION_IDLE_EXPIRE_DURATION]ms', async () => {