Skip to content

Commit

Permalink
Merge branch 'develop' into chdsbd/mark-error-handler-errors-unhandled
Browse files Browse the repository at this point in the history
  • Loading branch information
chdsbd authored May 6, 2023
2 parents 2122252 + d400d69 commit 0ad66b7
Show file tree
Hide file tree
Showing 25 changed files with 294 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ sentryTest(

expect(event0).toEqual(
getExpectedReplayEvent({
contexts: { replay: { error_sample_rate: 0, session_sample_rate: 0 } },
error_ids: [errorEventId!],
replay_type: 'buffer',
}),
Expand Down Expand Up @@ -150,7 +149,6 @@ sentryTest(

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

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

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

expect(event0).toEqual(
getExpectedReplayEvent({
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
error_ids: errorEventIds,
replay_type: 'buffer',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
},
},
platform: 'javascript',
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
});

expect(replayEvent1).toBeDefined();
Expand Down Expand Up @@ -103,6 +102,5 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
},
},
platform: 'javascript',
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ sentryTest('should capture replays (@sentry/replay export)', async ({ getLocalTe
},
},
platform: 'javascript',
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
});

expect(replayEvent1).toBeDefined();
Expand Down Expand Up @@ -103,6 +102,5 @@ sentryTest('should capture replays (@sentry/replay export)', async ({ getLocalTe
},
},
platform: 'javascript',
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,60 @@ sentryTest(
);
},
);

sentryTest(
'replay recording should contain an "options" breadcrumb for Replay SDK configuration',
async ({ forceFlushReplay, getLocalTestPath, page, browserName }) => {
// TODO(replay): This is flakey on firefox and webkit where clicks are flakey
if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) {
sentryTest.skip();
}

const reqPromise0 = waitForReplayRequest(page, 0);
const reqPromise1 = waitForReplayRequest(page, 1);

await page.route('https://dsn.ingest.sentry.io/**/*', route => {
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ id: 'test-id' }),
});
});

const url = await getLocalTestPath({ testDir: __dirname });

await page.goto(url);
await forceFlushReplay();

await page.click('#error');
await forceFlushReplay();

const req0 = await reqPromise0;
const content0 = getReplayRecordingContent(req0);

expect(content0.optionsEvents).toEqual([
{
tag: 'options',
payload: {
sessionSampleRate: 1,
errorSampleRate: 0,
useCompressionOption: false,
blockAllMedia: false,
maskAllText: true,
maskAllInputs: true,
useCompression: false,
networkDetailHasUrls: false,
networkCaptureBodies: true,
networkRequestHasHeaders: true,
networkResponseHasHeaders: true,
},
},
]);

const req1 = await reqPromise1;
const content1 = getReplayRecordingContent(req1);

// Should only be on first segment
expect(content1.optionsEvents).toEqual([]);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ sentryTest(

expect(event0).toEqual(
getExpectedReplayEvent({
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
error_ids: [errorEventId!],
replay_type: 'buffer',
}),
Expand Down Expand Up @@ -119,7 +118,6 @@ sentryTest(

expect(event1).toEqual(
getExpectedReplayEvent({
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
replay_type: 'buffer', // although we're in session mode, we still send 'error' as replay_type
segment_id: 1,
urls: [],
Expand All @@ -134,7 +132,6 @@ sentryTest(
// we continue recording everything
expect(event2).toEqual(
getExpectedReplayEvent({
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
replay_type: 'buffer',
segment_id: 2,
urls: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ const DEFAULT_REPLAY_EVENT = {
},
},
platform: 'javascript',
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
};

/**
Expand Down
9 changes: 8 additions & 1 deletion packages/browser-integration-tests/utils/replayHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ type CustomRecordingContent = {
type RecordingContent = {
fullSnapshots: RecordingSnapshot[];
incrementalSnapshots: RecordingSnapshot[];
optionsEvents: CustomRecordingEvent[];
} & CustomRecordingContent;

/**
Expand Down Expand Up @@ -207,6 +208,11 @@ export function getIncrementalRecordingSnapshots(resOrReq: Request | Response):
return events.filter(isIncrementalSnapshot);
}

function getOptionsEvents(replayRequest: Request): CustomRecordingEvent[] {
const events = getDecompressedRecordingEvents(replayRequest);
return getAllCustomRrwebRecordingEvents(events).filter(data => data.tag === 'options');
}

function getDecompressedRecordingEvents(resOrReq: Request | Response): RecordingSnapshot[] {
const replayRequest = getRequest(resOrReq);
return (
Expand All @@ -227,8 +233,9 @@ export function getReplayRecordingContent(resOrReq: Request | Response): Recordi
const fullSnapshots = getFullRecordingSnapshots(replayRequest);
const incrementalSnapshots = getIncrementalRecordingSnapshots(replayRequest);
const customEvents = getCustomRecordingEvents(replayRequest);
const optionsEvents = getOptionsEvents(replayRequest);

return { fullSnapshots, incrementalSnapshots, ...customEvents };
return { fullSnapshots, incrementalSnapshots, optionsEvents, ...customEvents };
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ export const ReplayRecordingData = [
data: { href: expect.stringMatching(/http:\/\/localhost:\d+\//), width: 1280, height: 720 },
timestamp: expect.any(Number),
},
{
data: {
payload: {
blockAllMedia: true,
errorSampleRate: 0,
maskAllInputs: true,
maskAllText: true,
networkCaptureBodies: true,
networkDetailHasUrls: false,
networkRequestHasHeaders: true,
networkResponseHasHeaders: true,
sessionSampleRate: 1,
useCompression: false,
useCompressionOption: true,
},
tag: 'options',
},
timestamp: expect.any(Number),
type: 5,
},
{
type: 2,
data: {
Expand Down
38 changes: 27 additions & 11 deletions packages/replay/jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,12 @@ const toHaveSameSession = function (received: jest.Mocked<ReplayContainer>, expe
return {
pass,
message: () =>
`${this.utils.matcherHint('toHaveSameSession', undefined, undefined, options)}\n\n` +
`Expected: ${pass ? 'not ' : ''}${this.utils.printExpected(expected)}\n` +
`Received: ${this.utils.printReceived(received.session)}`,
`${this.utils.matcherHint(
'toHaveSameSession',
undefined,
undefined,
options,
)}\n\n${this.utils.printDiffOrStringify(expected, received.session, 'Expected', 'Received')}`,
};
};

Expand Down Expand Up @@ -138,11 +141,18 @@ const toHaveSentReplay = function (

let result: CheckCallForSentReplayResult;

const expectedKeysLength = expected ? ('sample' in expected ? Object.keys(expected.sample) : Object.keys(expected)).length : 0;

for (const currentCall of calls) {
result = checkCallForSentReplay.call(this, currentCall[0], expected);
if (result.pass) {
break;
}

// stop on the first call where any of the expected obj passes
if (result.results.length < expectedKeysLength) {
break;
}
}

// @ts-ignore use before assigned
Expand All @@ -161,10 +171,13 @@ const toHaveSentReplay = function (
? 'Expected Replay to not have been sent, but a request was attempted'
: '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) =>
`Expected (key: ${key}): ${pass ? 'not ' : ''}${this.utils.printExpected(expectedVal)}\n` +
`Received (key: ${key}): ${this.utils.printReceived(actualVal)}`,
.map(({ key, expectedVal, actualVal }: Result) =>
this.utils.printDiffOrStringify(
expectedVal,
actualVal,
`Expected (key: ${key})`,
`Received (key: ${key})`,
),
)
.join('\n')}`,
};
Expand Down Expand Up @@ -197,10 +210,13 @@ const toHaveLastSentReplay = function (
? 'Expected Replay to not have been sent, but a request was attempted'
: '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) =>
`Expected (key: ${key}): ${pass ? 'not ' : ''}${this.utils.printExpected(expectedVal)}\n` +
`Received (key: ${key}): ${this.utils.printReceived(actualVal)}`,
.map(({ key, expectedVal, actualVal }: Result) =>
this.utils.printDiffOrStringify(
expectedVal,
actualVal,
`Expected (key: ${key})`,
`Received (key: ${key})`,
),
)
.join('\n')}`,
};
Expand Down
7 changes: 6 additions & 1 deletion packages/replay/src/eventBuffer/EventBufferArray.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AddEventResult, EventBuffer, RecordingEvent } from '../types';
import type { AddEventResult, EventBuffer, EventBufferType, RecordingEvent } from '../types';
import { timestampToMs } from '../util/timestampToMs';

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

/** @inheritdoc */
public get type(): EventBufferType {
return 'sync';
}

/** @inheritdoc */
public destroy(): void {
this.events = [];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ReplayRecordingData } from '@sentry/types';

import type { AddEventResult, EventBuffer, RecordingEvent } from '../types';
import type { AddEventResult, EventBuffer, EventBufferType, RecordingEvent } from '../types';
import { timestampToMs } from '../util/timestampToMs';
import { WorkerHandler } from './WorkerHandler';

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

/** @inheritdoc */
public get type(): EventBufferType {
return 'worker';
}

/**
* Ensure the worker is ready (or not).
* This will either resolve when the worker is ready, or reject if an error occured.
Expand Down
7 changes: 6 additions & 1 deletion packages/replay/src/eventBuffer/EventBufferProxy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ReplayRecordingData } from '@sentry/types';
import { logger } from '@sentry/utils';

import type { AddEventResult, EventBuffer, RecordingEvent } from '../types';
import type { AddEventResult, EventBuffer, EventBufferType, RecordingEvent } from '../types';
import { EventBufferArray } from './EventBufferArray';
import { EventBufferCompressionWorker } from './EventBufferCompressionWorker';

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

/** @inheritdoc */
public get type(): EventBufferType {
return this._used.type;
}

/** @inheritDoc */
public get hasEvents(): boolean {
return this._used.hasEvents;
Expand Down
2 changes: 2 additions & 0 deletions packages/replay/src/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ export class Replay implements Integration {
errorSampleRate,
useCompression,
blockAllMedia,
maskAllInputs,
maskAllText,
networkDetailAllowUrls,
networkCaptureBodies,
networkRequestHeaders: _getMergedNetworkHeaders(networkRequestHeaders),
Expand Down
17 changes: 17 additions & 0 deletions packages/replay/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,16 @@ export interface ReplayPluginOptions extends ReplayNetworkOptions {
*/
blockAllMedia: boolean;

/**
* Mask all inputs in recordings
*/
maskAllInputs: boolean;

/**
* Mask all text in recordings
*/
maskAllText: boolean;

/**
* _experiments allows users to enable experimental or internal features.
* We don't consider such features as part of the public API and hence we don't guarantee semver for them.
Expand Down Expand Up @@ -435,12 +445,19 @@ export interface Session {
shouldRefresh: boolean;
}

export type EventBufferType = 'sync' | 'worker';

export interface EventBuffer {
/**
* If any events have been added to the buffer.
*/
readonly hasEvents: boolean;

/**
* The buffer type
*/
readonly type: EventBufferType;

/**
* Destroy the event buffer.
*/
Expand Down
Loading

0 comments on commit 0ad66b7

Please sign in to comment.