Skip to content

Commit 8ae1ce4

Browse files
authored
test(replay): Streamline replay test imports (#6486)
1 parent 497a3c7 commit 8ae1ce4

File tree

8 files changed

+81
-85
lines changed

8 files changed

+81
-85
lines changed

packages/replay/test/mocks/mockSdk.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { ReplayConfiguration } from '../../src/types';
88
export interface MockSdkParams {
99
replayOptions?: ReplayConfiguration;
1010
sentryOptions?: BrowserOptions;
11+
autoStart?: boolean;
1112
}
1213

1314
class MockTransport implements Transport {
@@ -35,7 +36,7 @@ class MockTransport implements Transport {
3536
}
3637
}
3738

38-
export async function mockSdk({ replayOptions, sentryOptions }: MockSdkParams = {}): Promise<{
39+
export async function mockSdk({ replayOptions, sentryOptions, autoStart = true }: MockSdkParams = {}): Promise<{
3940
replay: ReplayContainer;
4041
integration: ReplayIntegration;
4142
}> {
@@ -75,7 +76,10 @@ export async function mockSdk({ replayOptions, sentryOptions }: MockSdkParams =
7576

7677
// Instead of `setupOnce`, which is tricky to test, we call this manually here
7778
replayIntegration['_setup']();
78-
replayIntegration.start();
79+
80+
if (autoStart) {
81+
replayIntegration.start();
82+
}
7983

8084
const replay = replayIntegration['_replay']!;
8185

packages/replay/test/mocks/resetSdkMock.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import { getCurrentHub } from '@sentry/core';
2-
31
import type { ReplayContainer } from '../../src/replay';
42
import { BASE_TIMESTAMP, RecordMock } from './../index';
5-
import type { DomHandler, MockTransportSend } from './../types';
3+
import type { DomHandler } from './../types';
64
import { mockSdk, MockSdkParams } from './mockSdk';
75

86
export async function resetSdkMock({ replayOptions, sentryOptions }: MockSdkParams): Promise<{
97
domHandler: DomHandler;
108
mockRecord: RecordMock;
11-
mockTransportSend: MockTransportSend;
129
replay: ReplayContainer;
1310
}> {
1411
let domHandler: DomHandler;
@@ -33,8 +30,6 @@ export async function resetSdkMock({ replayOptions, sentryOptions }: MockSdkPara
3330
sentryOptions,
3431
});
3532

36-
const mockTransportSend = getCurrentHub()?.getClient()?.getTransport()?.send as MockTransportSend;
37-
3833
// XXX: This is needed to ensure `domHandler` is set
3934
jest.runAllTimers();
4035
await new Promise(process.nextTick);
@@ -44,7 +39,6 @@ export async function resetSdkMock({ replayOptions, sentryOptions }: MockSdkPara
4439
// @ts-ignore use before assign
4540
domHandler,
4641
mockRecord,
47-
mockTransportSend,
4842
replay,
4943
};
5044
}

packages/replay/test/types.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
import { Transport } from '@sentry/types';
2-
3-
export type MockTransportSend = jest.MockedFunction<Transport['send']>;
41
export type DomHandler = (args: any) => any;

packages/replay/test/unit/coreHandlers/handleFetch.test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { handleFetch } from '../../../src/coreHandlers/handleFetch';
22
import { mockSdk } from './../../index';
33

4-
jest.unmock('@sentry/browser');
5-
64
beforeAll(function () {
75
mockSdk();
86
});

packages/replay/test/unit/createPerformanceEntry.test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { createPerformanceEntries } from '../../src/createPerformanceEntry';
22
import { mockSdk } from './../index';
33

4-
jest.unmock('@sentry/browser');
5-
64
beforeAll(function () {
75
mockSdk();
86
});

packages/replay/test/unit/index-errorSampleRate.test.ts

Lines changed: 68 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { captureException } from '@sentry/core';
1+
import { captureException, getCurrentHub } from '@sentry/core';
22

33
import { REPLAY_SESSION_KEY, VISIBILITY_CHANGE_TIMEOUT, WINDOW } from '../../src/constants';
44
import { addEvent } from '../../src/util/addEvent';
55
import { ReplayContainer } from './../../src/replay';
66
import { PerformanceEntryResource } from './../fixtures/performanceEntry/resource';
77
import { BASE_TIMESTAMP, RecordMock } from './../index';
88
import { resetSdkMock } from './../mocks/resetSdkMock';
9-
import { DomHandler, MockTransportSend } from './../types';
9+
import { DomHandler } from './../types';
1010
import { useFakeTimers } from './../utils/use-fake-timers';
1111

1212
useFakeTimers();
@@ -19,11 +19,11 @@ async function advanceTimers(time: number) {
1919
describe('Replay (errorSampleRate)', () => {
2020
let replay: ReplayContainer;
2121
let mockRecord: RecordMock;
22-
let mockTransportSend: MockTransportSend;
22+
2323
let domHandler: DomHandler;
2424

2525
beforeEach(async () => {
26-
({ mockRecord, mockTransportSend, domHandler, replay } = await resetSdkMock({
26+
({ mockRecord, domHandler, replay } = await resetSdkMock({
2727
replayOptions: {
2828
stickySession: true,
2929
},
@@ -86,9 +86,6 @@ describe('Replay (errorSampleRate)', () => {
8686
]),
8787
});
8888

89-
mockTransportSend.mockClear();
90-
expect(replay).not.toHaveSentReplay();
91-
9289
jest.runAllTimers();
9390
await new Promise(process.nextTick);
9491
jest.runAllTimers();
@@ -310,63 +307,6 @@ describe('Replay (errorSampleRate)', () => {
310307
});
311308
});
312309

313-
/**
314-
* This is testing a case that should only happen with error-only sessions.
315-
* Previously we had assumed that loading a session from session storage meant
316-
* that the session was not new. However, this is not the case with error-only
317-
* sampling since we can load a saved session that did not have an error (and
318-
* thus no replay was created).
319-
*/
320-
it('sends a replay after loading the session multiple times', async () => {
321-
// Pretend that a session is already saved before loading replay
322-
WINDOW.sessionStorage.setItem(
323-
REPLAY_SESSION_KEY,
324-
`{"segmentId":0,"id":"fd09adfc4117477abc8de643e5a5798a","sampled":"error","started":${BASE_TIMESTAMP},"lastActivity":${BASE_TIMESTAMP}}`,
325-
);
326-
({ mockRecord, mockTransportSend, replay } = await resetSdkMock({
327-
replayOptions: {
328-
stickySession: true,
329-
},
330-
}));
331-
replay.start();
332-
333-
jest.runAllTimers();
334-
335-
await new Promise(process.nextTick);
336-
const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 3 };
337-
mockRecord._emitter(TEST_EVENT);
338-
339-
expect(replay).not.toHaveSentReplay();
340-
341-
captureException(new Error('testing'));
342-
jest.runAllTimers();
343-
await new Promise(process.nextTick);
344-
345-
expect(replay).toHaveSentReplay({
346-
events: JSON.stringify([{ data: { isCheckout: true }, timestamp: BASE_TIMESTAMP, type: 2 }, TEST_EVENT]),
347-
});
348-
349-
mockTransportSend.mockClear();
350-
expect(replay).not.toHaveSentReplay();
351-
352-
jest.runAllTimers();
353-
await new Promise(process.nextTick);
354-
jest.runAllTimers();
355-
await new Promise(process.nextTick);
356-
357-
// New checkout when we call `startRecording` again after uploading segment
358-
// after an error occurs
359-
expect(replay).toHaveSentReplay({
360-
events: JSON.stringify([
361-
{
362-
data: { isCheckout: true },
363-
timestamp: BASE_TIMESTAMP + 10000 + 20,
364-
type: 2,
365-
},
366-
]),
367-
});
368-
});
369-
370310
it('has correct timestamps when error occurs much later than initial pageload/checkout', async () => {
371311
const ELAPSED = 60000;
372312
const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 3 };
@@ -415,3 +355,67 @@ describe('Replay (errorSampleRate)', () => {
415355
});
416356
});
417357
});
358+
359+
/**
360+
* This is testing a case that should only happen with error-only sessions.
361+
* Previously we had assumed that loading a session from session storage meant
362+
* that the session was not new. However, this is not the case with error-only
363+
* sampling since we can load a saved session that did not have an error (and
364+
* thus no replay was created).
365+
*/
366+
it('sends a replay after loading the session multiple times', async () => {
367+
// Pretend that a session is already saved before loading replay
368+
WINDOW.sessionStorage.setItem(
369+
REPLAY_SESSION_KEY,
370+
`{"segmentId":0,"id":"fd09adfc4117477abc8de643e5a5798a","sampled":"error","started":${BASE_TIMESTAMP},"lastActivity":${BASE_TIMESTAMP}}`,
371+
);
372+
const { mockRecord, replay } = await resetSdkMock({
373+
replayOptions: {
374+
stickySession: true,
375+
},
376+
autoStart: false,
377+
});
378+
379+
const fn = getCurrentHub()?.getClient()?.getTransport()?.send;
380+
const mockTransportSend = fn
381+
? (jest.spyOn(getCurrentHub().getClient()!.getTransport()!, 'send') as jest.MockedFunction<any>)
382+
: jest.fn();
383+
384+
replay.start();
385+
386+
jest.runAllTimers();
387+
388+
await new Promise(process.nextTick);
389+
const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 3 };
390+
mockRecord._emitter(TEST_EVENT);
391+
392+
expect(replay).not.toHaveSentReplay();
393+
394+
captureException(new Error('testing'));
395+
jest.runAllTimers();
396+
await new Promise(process.nextTick);
397+
398+
expect(replay).toHaveSentReplay({
399+
events: JSON.stringify([{ data: { isCheckout: true }, timestamp: BASE_TIMESTAMP, type: 2 }, TEST_EVENT]),
400+
});
401+
402+
mockTransportSend.mockClear();
403+
expect(replay).not.toHaveSentReplay();
404+
405+
jest.runAllTimers();
406+
await new Promise(process.nextTick);
407+
jest.runAllTimers();
408+
await new Promise(process.nextTick);
409+
410+
// New checkout when we call `startRecording` again after uploading segment
411+
// after an error occurs
412+
expect(replay).toHaveSentReplay({
413+
events: JSON.stringify([
414+
{
415+
data: { isCheckout: true },
416+
timestamp: BASE_TIMESTAMP + 10000 + 20,
417+
type: 2,
418+
},
419+
]),
420+
});
421+
});

packages/replay/test/unit/index-sampling.test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
jest.unmock('@sentry/browser');
2-
31
// mock functions need to be imported first
42
import { mockRrweb, mockSdk } from './../index';
53
import { useFakeTimers } from './../utils/use-fake-timers';

packages/replay/test/unit/index.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getCurrentHub } from '@sentry/core';
12
import { EventType } from 'rrweb';
23

34
import { MAX_SESSION_LIFE, REPLAY_SESSION_KEY, VISIBILITY_CHANGE_TIMEOUT, WINDOW } from '../../src/constants';
@@ -9,7 +10,7 @@ import { useFakeTimers } from '../utils/use-fake-timers';
910
import { PerformanceEntryResource } from './../fixtures/performanceEntry/resource';
1011
import { BASE_TIMESTAMP, RecordMock } from './../index';
1112
import { resetSdkMock } from './../mocks/resetSdkMock';
12-
import { DomHandler, MockTransportSend } from './../types';
13+
import { DomHandler } from './../types';
1314

1415
useFakeTimers();
1516

@@ -92,7 +93,7 @@ describe('Replay with custom mock', () => {
9293
describe('Replay', () => {
9394
let replay: ReplayContainer;
9495
let mockRecord: RecordMock;
95-
let mockTransportSend: MockTransportSend;
96+
let mockTransportSend: jest.SpyInstance<any>;
9697
let domHandler: DomHandler;
9798
const prevLocation = WINDOW.location;
9899

@@ -105,12 +106,14 @@ describe('Replay', () => {
105106
});
106107

107108
beforeEach(async () => {
108-
({ mockRecord, mockTransportSend, domHandler, replay } = await resetSdkMock({
109+
({ mockRecord, domHandler, replay } = await resetSdkMock({
109110
replayOptions: {
110111
stickySession: false,
111112
},
112113
}));
113114

115+
mockTransportSend = jest.spyOn(getCurrentHub().getClient()!.getTransport()!, 'send');
116+
114117
jest.spyOn(replay, 'flush');
115118
jest.spyOn(replay, 'runFlush');
116119
jest.spyOn(replay, 'sendReplayRequest');

0 commit comments

Comments
 (0)