Skip to content

Commit 0ad66b7

Browse files
authored
Merge branch 'develop' into chdsbd/mark-error-handler-errors-unhandled
2 parents 2122252 + d400d69 commit 0ad66b7

File tree

25 files changed

+294
-100
lines changed

25 files changed

+294
-100
lines changed

packages/browser-integration-tests/suites/replay/bufferMode/test.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ sentryTest(
115115

116116
expect(event0).toEqual(
117117
getExpectedReplayEvent({
118-
contexts: { replay: { error_sample_rate: 0, session_sample_rate: 0 } },
119118
error_ids: [errorEventId!],
120119
replay_type: 'buffer',
121120
}),
@@ -150,7 +149,6 @@ sentryTest(
150149

151150
expect(event1).toEqual(
152151
getExpectedReplayEvent({
153-
contexts: { replay: { error_sample_rate: 0, session_sample_rate: 0 } },
154152
replay_type: 'buffer', // although we're in session mode, we still send 'buffer' as replay_type
155153
segment_id: 1,
156154
urls: [],
@@ -162,7 +160,6 @@ sentryTest(
162160

163161
expect(event2).toEqual(
164162
getExpectedReplayEvent({
165-
contexts: { replay: { error_sample_rate: 0, session_sample_rate: 0 } },
166163
replay_type: 'buffer', // although we're in session mode, we still send 'buffer' as replay_type
167164
segment_id: 2,
168165
urls: [],
@@ -266,7 +263,6 @@ sentryTest(
266263

267264
expect(event0).toEqual(
268265
getExpectedReplayEvent({
269-
contexts: { replay: { error_sample_rate: 0, session_sample_rate: 0 } },
270266
error_ids: [errorEventId!],
271267
replay_type: 'buffer',
272268
}),
@@ -372,7 +368,6 @@ sentryTest('[buffer-mode] can sample on each error event', async ({ getLocalTest
372368

373369
expect(event0).toEqual(
374370
getExpectedReplayEvent({
375-
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
376371
error_ids: errorEventIds,
377372
replay_type: 'buffer',
378373
}),

packages/browser-integration-tests/suites/replay/captureReplay/test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
6464
},
6565
},
6666
platform: 'javascript',
67-
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
6867
});
6968

7069
expect(replayEvent1).toBeDefined();
@@ -103,6 +102,5 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
103102
},
104103
},
105104
platform: 'javascript',
106-
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
107105
});
108106
});

packages/browser-integration-tests/suites/replay/captureReplayFromReplayPackage/test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ sentryTest('should capture replays (@sentry/replay export)', async ({ getLocalTe
6464
},
6565
},
6666
platform: 'javascript',
67-
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
6867
});
6968

7069
expect(replayEvent1).toBeDefined();
@@ -103,6 +102,5 @@ sentryTest('should capture replays (@sentry/replay export)', async ({ getLocalTe
103102
},
104103
},
105104
platform: 'javascript',
106-
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
107105
});
108106
});

packages/browser-integration-tests/suites/replay/customEvents/test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,60 @@ sentryTest(
174174
);
175175
},
176176
);
177+
178+
sentryTest(
179+
'replay recording should contain an "options" breadcrumb for Replay SDK configuration',
180+
async ({ forceFlushReplay, getLocalTestPath, page, browserName }) => {
181+
// TODO(replay): This is flakey on firefox and webkit where clicks are flakey
182+
if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) {
183+
sentryTest.skip();
184+
}
185+
186+
const reqPromise0 = waitForReplayRequest(page, 0);
187+
const reqPromise1 = waitForReplayRequest(page, 1);
188+
189+
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
190+
return route.fulfill({
191+
status: 200,
192+
contentType: 'application/json',
193+
body: JSON.stringify({ id: 'test-id' }),
194+
});
195+
});
196+
197+
const url = await getLocalTestPath({ testDir: __dirname });
198+
199+
await page.goto(url);
200+
await forceFlushReplay();
201+
202+
await page.click('#error');
203+
await forceFlushReplay();
204+
205+
const req0 = await reqPromise0;
206+
const content0 = getReplayRecordingContent(req0);
207+
208+
expect(content0.optionsEvents).toEqual([
209+
{
210+
tag: 'options',
211+
payload: {
212+
sessionSampleRate: 1,
213+
errorSampleRate: 0,
214+
useCompressionOption: false,
215+
blockAllMedia: false,
216+
maskAllText: true,
217+
maskAllInputs: true,
218+
useCompression: false,
219+
networkDetailHasUrls: false,
220+
networkCaptureBodies: true,
221+
networkRequestHasHeaders: true,
222+
networkResponseHasHeaders: true,
223+
},
224+
},
225+
]);
226+
227+
const req1 = await reqPromise1;
228+
const content1 = getReplayRecordingContent(req1);
229+
230+
// Should only be on first segment
231+
expect(content1.optionsEvents).toEqual([]);
232+
},
233+
);

packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ sentryTest(
8484

8585
expect(event0).toEqual(
8686
getExpectedReplayEvent({
87-
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
8887
error_ids: [errorEventId!],
8988
replay_type: 'buffer',
9089
}),
@@ -119,7 +118,6 @@ sentryTest(
119118

120119
expect(event1).toEqual(
121120
getExpectedReplayEvent({
122-
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
123121
replay_type: 'buffer', // although we're in session mode, we still send 'error' as replay_type
124122
segment_id: 1,
125123
urls: [],
@@ -134,7 +132,6 @@ sentryTest(
134132
// we continue recording everything
135133
expect(event2).toEqual(
136134
getExpectedReplayEvent({
137-
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
138135
replay_type: 'buffer',
139136
segment_id: 2,
140137
urls: [],

packages/browser-integration-tests/utils/replayEventTemplates.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ const DEFAULT_REPLAY_EVENT = {
3838
},
3939
},
4040
platform: 'javascript',
41-
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
4241
};
4342

4443
/**

packages/browser-integration-tests/utils/replayHelpers.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ type CustomRecordingContent = {
160160
type RecordingContent = {
161161
fullSnapshots: RecordingSnapshot[];
162162
incrementalSnapshots: RecordingSnapshot[];
163+
optionsEvents: CustomRecordingEvent[];
163164
} & CustomRecordingContent;
164165

165166
/**
@@ -207,6 +208,11 @@ export function getIncrementalRecordingSnapshots(resOrReq: Request | Response):
207208
return events.filter(isIncrementalSnapshot);
208209
}
209210

211+
function getOptionsEvents(replayRequest: Request): CustomRecordingEvent[] {
212+
const events = getDecompressedRecordingEvents(replayRequest);
213+
return getAllCustomRrwebRecordingEvents(events).filter(data => data.tag === 'options');
214+
}
215+
210216
function getDecompressedRecordingEvents(resOrReq: Request | Response): RecordingSnapshot[] {
211217
const replayRequest = getRequest(resOrReq);
212218
return (
@@ -227,8 +233,9 @@ export function getReplayRecordingContent(resOrReq: Request | Response): Recordi
227233
const fullSnapshots = getFullRecordingSnapshots(replayRequest);
228234
const incrementalSnapshots = getIncrementalRecordingSnapshots(replayRequest);
229235
const customEvents = getCustomRecordingEvents(replayRequest);
236+
const optionsEvents = getOptionsEvents(replayRequest);
230237

231-
return { fullSnapshots, incrementalSnapshots, ...customEvents };
238+
return { fullSnapshots, incrementalSnapshots, optionsEvents, ...customEvents };
232239
}
233240

234241
/**

packages/e2e-tests/test-applications/standard-frontend-react/tests/fixtures/ReplayRecordingData.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,26 @@ export const ReplayRecordingData = [
77
data: { href: expect.stringMatching(/http:\/\/localhost:\d+\//), width: 1280, height: 720 },
88
timestamp: expect.any(Number),
99
},
10+
{
11+
data: {
12+
payload: {
13+
blockAllMedia: true,
14+
errorSampleRate: 0,
15+
maskAllInputs: true,
16+
maskAllText: true,
17+
networkCaptureBodies: true,
18+
networkDetailHasUrls: false,
19+
networkRequestHasHeaders: true,
20+
networkResponseHasHeaders: true,
21+
sessionSampleRate: 1,
22+
useCompression: false,
23+
useCompressionOption: true,
24+
},
25+
tag: 'options',
26+
},
27+
timestamp: expect.any(Number),
28+
type: 5,
29+
},
1030
{
1131
type: 2,
1232
data: {

packages/replay/jest.setup.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,12 @@ const toHaveSameSession = function (received: jest.Mocked<ReplayContainer>, expe
5050
return {
5151
pass,
5252
message: () =>
53-
`${this.utils.matcherHint('toHaveSameSession', undefined, undefined, options)}\n\n` +
54-
`Expected: ${pass ? 'not ' : ''}${this.utils.printExpected(expected)}\n` +
55-
`Received: ${this.utils.printReceived(received.session)}`,
53+
`${this.utils.matcherHint(
54+
'toHaveSameSession',
55+
undefined,
56+
undefined,
57+
options,
58+
)}\n\n${this.utils.printDiffOrStringify(expected, received.session, 'Expected', 'Received')}`,
5659
};
5760
};
5861

@@ -138,11 +141,18 @@ const toHaveSentReplay = function (
138141

139142
let result: CheckCallForSentReplayResult;
140143

144+
const expectedKeysLength = expected ? ('sample' in expected ? Object.keys(expected.sample) : Object.keys(expected)).length : 0;
145+
141146
for (const currentCall of calls) {
142147
result = checkCallForSentReplay.call(this, currentCall[0], expected);
143148
if (result.pass) {
144149
break;
145150
}
151+
152+
// stop on the first call where any of the expected obj passes
153+
if (result.results.length < expectedKeysLength) {
154+
break;
155+
}
146156
}
147157

148158
// @ts-ignore use before assigned
@@ -161,10 +171,13 @@ const toHaveSentReplay = function (
161171
? 'Expected Replay to not have been sent, but a request was attempted'
162172
: 'Expected Replay to have been sent, but a request was not attempted'
163173
: `${this.utils.matcherHint('toHaveSentReplay', undefined, undefined, options)}\n\n${results
164-
.map(
165-
({ key, expectedVal, actualVal }: Result) =>
166-
`Expected (key: ${key}): ${pass ? 'not ' : ''}${this.utils.printExpected(expectedVal)}\n` +
167-
`Received (key: ${key}): ${this.utils.printReceived(actualVal)}`,
174+
.map(({ key, expectedVal, actualVal }: Result) =>
175+
this.utils.printDiffOrStringify(
176+
expectedVal,
177+
actualVal,
178+
`Expected (key: ${key})`,
179+
`Received (key: ${key})`,
180+
),
168181
)
169182
.join('\n')}`,
170183
};
@@ -197,10 +210,13 @@ const toHaveLastSentReplay = function (
197210
? 'Expected Replay to not have been sent, but a request was attempted'
198211
: 'Expected Replay to have last been sent, but a request was not attempted'
199212
: `${this.utils.matcherHint('toHaveSentReplay', undefined, undefined, options)}\n\n${results
200-
.map(
201-
({ key, expectedVal, actualVal }: Result) =>
202-
`Expected (key: ${key}): ${pass ? 'not ' : ''}${this.utils.printExpected(expectedVal)}\n` +
203-
`Received (key: ${key}): ${this.utils.printReceived(actualVal)}`,
213+
.map(({ key, expectedVal, actualVal }: Result) =>
214+
this.utils.printDiffOrStringify(
215+
expectedVal,
216+
actualVal,
217+
`Expected (key: ${key})`,
218+
`Received (key: ${key})`,
219+
),
204220
)
205221
.join('\n')}`,
206222
};

packages/replay/src/eventBuffer/EventBufferArray.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { AddEventResult, EventBuffer, RecordingEvent } from '../types';
1+
import type { AddEventResult, EventBuffer, EventBufferType, RecordingEvent } from '../types';
22
import { timestampToMs } from '../util/timestampToMs';
33

44
/**
@@ -18,6 +18,11 @@ export class EventBufferArray implements EventBuffer {
1818
return this.events.length > 0;
1919
}
2020

21+
/** @inheritdoc */
22+
public get type(): EventBufferType {
23+
return 'sync';
24+
}
25+
2126
/** @inheritdoc */
2227
public destroy(): void {
2328
this.events = [];

packages/replay/src/eventBuffer/EventBufferCompressionWorker.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ReplayRecordingData } from '@sentry/types';
22

3-
import type { AddEventResult, EventBuffer, RecordingEvent } from '../types';
3+
import type { AddEventResult, EventBuffer, EventBufferType, RecordingEvent } from '../types';
44
import { timestampToMs } from '../util/timestampToMs';
55
import { WorkerHandler } from './WorkerHandler';
66

@@ -22,6 +22,11 @@ export class EventBufferCompressionWorker implements EventBuffer {
2222
return !!this._earliestTimestamp;
2323
}
2424

25+
/** @inheritdoc */
26+
public get type(): EventBufferType {
27+
return 'worker';
28+
}
29+
2530
/**
2631
* Ensure the worker is ready (or not).
2732
* This will either resolve when the worker is ready, or reject if an error occured.

packages/replay/src/eventBuffer/EventBufferProxy.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ReplayRecordingData } from '@sentry/types';
22
import { logger } from '@sentry/utils';
33

4-
import type { AddEventResult, EventBuffer, RecordingEvent } from '../types';
4+
import type { AddEventResult, EventBuffer, EventBufferType, RecordingEvent } from '../types';
55
import { EventBufferArray } from './EventBufferArray';
66
import { EventBufferCompressionWorker } from './EventBufferCompressionWorker';
77

@@ -24,6 +24,11 @@ export class EventBufferProxy implements EventBuffer {
2424
this._ensureWorkerIsLoadedPromise = this._ensureWorkerIsLoaded();
2525
}
2626

27+
/** @inheritdoc */
28+
public get type(): EventBufferType {
29+
return this._used.type;
30+
}
31+
2732
/** @inheritDoc */
2833
public get hasEvents(): boolean {
2934
return this._used.hasEvents;

packages/replay/src/integration.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ export class Replay implements Integration {
123123
errorSampleRate,
124124
useCompression,
125125
blockAllMedia,
126+
maskAllInputs,
127+
maskAllText,
126128
networkDetailAllowUrls,
127129
networkCaptureBodies,
128130
networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),

packages/replay/src/types.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,16 @@ export interface ReplayPluginOptions extends ReplayNetworkOptions {
257257
*/
258258
blockAllMedia: boolean;
259259

260+
/**
261+
* Mask all inputs in recordings
262+
*/
263+
maskAllInputs: boolean;
264+
265+
/**
266+
* Mask all text in recordings
267+
*/
268+
maskAllText: boolean;
269+
260270
/**
261271
* _experiments allows users to enable experimental or internal features.
262272
* We don't consider such features as part of the public API and hence we don't guarantee semver for them.
@@ -435,12 +445,19 @@ export interface Session {
435445
shouldRefresh: boolean;
436446
}
437447

448+
export type EventBufferType = 'sync' | 'worker';
449+
438450
export interface EventBuffer {
439451
/**
440452
* If any events have been added to the buffer.
441453
*/
442454
readonly hasEvents: boolean;
443455

456+
/**
457+
* The buffer type
458+
*/
459+
readonly type: EventBufferType;
460+
444461
/**
445462
* Destroy the event buffer.
446463
*/

0 commit comments

Comments
 (0)