From 32dcde4b11ccde98290fb0e476fa3c42dd852dec Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Mon, 29 Jan 2024 11:28:41 +0000 Subject: [PATCH] chore: add a test for snapshot loading (#20007) * chore: add a test for snapshot loading * fix test name --- .../player/sessionRecordingDataLogic.test.ts | 71 ++++++++++++++++--- .../player/sessionRecordingDataLogic.ts | 2 +- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts index e75910bb9166b..054f3ced2c725 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts @@ -13,7 +13,7 @@ import { resumeKeaLoadersErrors, silenceKeaLoadersErrors } from '~/initKea' import { useAvailableFeatures } from '~/mocks/features' import { useMocks } from '~/mocks/jest' import { initKeaTests } from '~/test/init' -import { AvailableFeature } from '~/types' +import { AvailableFeature, SessionRecordingSnapshotSource } from '~/types' import recordingEventsJson from '../__mocks__/recording_events_query' import recordingMetaJson from '../__mocks__/recording_meta.json' @@ -21,6 +21,21 @@ import { snapshotsAsJSONLines, sortedRecordingSnapshots } from '../__mocks__/rec const sortedRecordingSnapshotsJson = sortedRecordingSnapshots() +const BLOB_SOURCE: SessionRecordingSnapshotSource = { + source: 'blob', + start_timestamp: '2023-08-11T12:03:36.097000Z', + end_timestamp: '2023-08-11T12:04:52.268000Z', + blob_key: '1691755416097-1691755492268', + loaded: false, +} +const REALTIME_SOURCE: SessionRecordingSnapshotSource = { + source: 'realtime', + start_timestamp: '2024-01-28T21:19:49.217000Z', + end_timestamp: undefined, + blob_key: undefined, + loaded: false, +} + describe('sessionRecordingDataLogic', () => { let logic: ReturnType @@ -32,19 +47,20 @@ describe('sessionRecordingDataLogic', () => { // with no sources, returns sources... if (req.url.searchParams.get('source') === 'blob') { return res(ctx.text(snapshotsAsJSONLines())) + } else if (req.url.searchParams.get('source') === 'realtime') { + // ... since this is fake, we'll just return the same data + return res(ctx.text(snapshotsAsJSONLines())) } + // with no source requested should return sources + const sources = [BLOB_SOURCE] + if (req.params.id === 'has-real-time-too') { + sources.push(REALTIME_SOURCE) + } return [ 200, { - sources: [ - { - source: 'blob', - start_timestamp: '2023-08-11T12:03:36.097000Z', - end_timestamp: '2023-08-11T12:04:52.268000Z', - blob_key: '1691755416097-1691755492268', - }, - ], + sources, }, ] }, @@ -258,4 +274,41 @@ describe('sessionRecordingDataLogic', () => { expect(prepareRecordingSnapshots(snapshots)).toMatchSnapshot() }) }) + + describe('blob and realtime loading', () => { + beforeEach(async () => { + // load a different session + logic = sessionRecordingDataLogic({ sessionRecordingId: 'has-real-time-too' }) + logic.mount() + // Most of these tests assume the metadata is being loaded upfront which is the typical case + logic.actions.loadRecordingMeta() + }) + + it('loads each source, and on success reports recording viewed', async () => { + expect(logic.cache.realtimePollingInterval).toBeUndefined() + + await expectLogic(logic, () => { + logic.actions.loadRecordingSnapshots() + // loading the snapshots will trigger a loadRecordingSnapshotsSuccess + // that will have the blob source + // that triggers loadRecordingSnapshots + }).toDispatchActions([ + // the action we triggered + logic.actionCreators.loadRecordingSnapshots(), + 'loadRecordingSnapshotsSuccess', + // the response to that triggers loading of the first item which is the blob source + (action) => + action.type === logic.actionTypes.loadRecordingSnapshots && + action.payload.source?.source === 'blob', + 'loadRecordingSnapshotsSuccess', + // the response to that triggers loading of the second item which is the realtime source + (action) => + action.type === logic.actionTypes.loadRecordingSnapshots && + action.payload.source?.source === 'realtime', + 'loadRecordingSnapshotsSuccess', + // and then we report having viewed the recording + 'reportViewed', + ]) + }) + }) }) diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts index 9888a3e06713f..104b282ef08a6 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts @@ -282,7 +282,7 @@ export const sessionRecordingDataLogic = kea([ loadRecordingSnapshotsSuccess: () => { const { snapshots, sources } = values.sessionPlayerSnapshotData ?? {} if (snapshots && !snapshots.length && sources?.length === 1) { - // We got only a snapshot response for realtime, and it was empty + // We got only a single source to load, loaded it successfully, but it had no snapshots. posthog.capture('recording_snapshots_v2_empty_response', { source: sources[0], })