Skip to content

test: Fix flaky errorsInSession replay test #7309

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { expect } from '@playwright/test';
import { SDK_VERSION } from '@sentry/browser';
import type { ReplayEvent } from '@sentry/types';

import { sentryTest } from '../../../utils/fixtures';
import { envelopeRequestParser } from '../../../utils/helpers';
import { shouldSkipReplayTest, waitForReplayRequest } from '../../../utils/replayHelpers';
import { getReplayEvent, shouldSkipReplayTest, waitForReplayRequest } from '../../../utils/replayHelpers';

sentryTest('should capture replays', async ({ getLocalTestPath, page }) => {
if (shouldSkipReplayTest()) {
Expand All @@ -25,10 +23,10 @@ sentryTest('should capture replays', async ({ getLocalTestPath, page }) => {
const url = await getLocalTestPath({ testDir: __dirname });

await page.goto(url);
const replayEvent0 = envelopeRequestParser(await reqPromise0) as ReplayEvent;
const replayEvent0 = getReplayEvent(await reqPromise0);

await page.click('button');
const replayEvent1 = envelopeRequestParser(await reqPromise1) as ReplayEvent;
const replayEvent1 = getReplayEvent(await reqPromise1);

expect(replayEvent0).toBeDefined();
expect(replayEvent0).toEqual({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { expect } from '@playwright/test';
import { SDK_VERSION } from '@sentry/browser';
import type { ReplayEvent } from '@sentry/types';

import { sentryTest } from '../../../utils/fixtures';
import { envelopeRequestParser } from '../../../utils/helpers';
import { waitForReplayRequest } from '../../../utils/replayHelpers';
import { getReplayEvent, waitForReplayRequest } from '../../../utils/replayHelpers';

sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalTestPath, page }) => {
// For this test, we skip all bundle tests, as we're only interested in Replay being correctly
Expand All @@ -27,10 +25,10 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
const url = await getLocalTestPath({ testDir: __dirname });

await page.goto(url);
const replayEvent0 = envelopeRequestParser(await reqPromise0) as ReplayEvent;
const replayEvent0 = getReplayEvent(await reqPromise0);

await page.click('button');
const replayEvent1 = envelopeRequestParser(await reqPromise1) as ReplayEvent;
const replayEvent1 = getReplayEvent(await reqPromise1);

expect(replayEvent0).toBeDefined();
expect(replayEvent0).toEqual({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ sentryTest(
sentryTest.skip();
}

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

await page.route('https://dsn.ingest.sentry.io/**/*', route => {
Expand All @@ -89,7 +90,7 @@ sentryTest(
const url = await getLocalTestPath({ testDir: __dirname });

await page.goto(url);
await page.click('#go-background');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could probably sometimes lead to a second flush, which we didn't actually want/need. It will flush on page visit anyhow!

await reqPromise0;

await page.click('#drop');
await page.click('#go-background');
Expand Down
32 changes: 23 additions & 9 deletions packages/integration-tests/utils/replayHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { RecordingEvent, ReplayContainer } from '@sentry/replay/build/npm/types/types';
import type { Breadcrumb, Event, ReplayEvent } from '@sentry/types';
import pako from 'pako';
import type { Page, Request } from 'playwright';
import type { Page, Request, Response } from 'playwright';

import { envelopeRequestParser } from './helpers';

Expand Down Expand Up @@ -37,8 +37,10 @@ type SnapshotNode = {
* @param segmentId the segment_id of the replay event
* @returns
*/
export function waitForReplayRequest(page: Page, segmentId?: number): Promise<Request> {
return page.waitForRequest(req => {
export function waitForReplayRequest(page: Page, segmentId?: number): Promise<Response> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We actually wait on a response here now, as that may be a bit better/more expected timing.

return page.waitForResponse(res => {
const req = res.request();

const postData = req.postData();
if (!postData) {
return false;
Expand Down Expand Up @@ -78,7 +80,8 @@ export async function getReplaySnapshot(page: Page): Promise<ReplayContainer> {

export const REPLAY_DEFAULT_FLUSH_MAX_DELAY = 5_000;

export function getReplayEvent(replayRequest: Request): ReplayEvent {
export function getReplayEvent(resOrReq: Request | Response): ReplayEvent {
const replayRequest = getRequest(resOrReq);
const event = envelopeRequestParser(replayRequest);
if (!isReplayEvent(event)) {
throw new Error('Request is not a replay event');
Expand All @@ -103,7 +106,8 @@ type RecordingContent = {
* @param replayRequest
* @returns an object containing the replay breadcrumbs and performance spans
*/
export function getCustomRecordingEvents(replayRequest: Request): CustomRecordingContent {
export function getCustomRecordingEvents(resOrReq: Request | Response): CustomRecordingContent {
const replayRequest = getRequest(resOrReq);
const recordingEvents = getDecompressedRecordingEvents(replayRequest);

const breadcrumbs = getReplayBreadcrumbs(recordingEvents);
Expand All @@ -128,21 +132,25 @@ function getReplayPerformanceSpans(recordingEvents: RecordingEvent[]): Performan
.map(data => data.payload) as PerformanceSpan[];
}

export function getFullRecordingSnapshots(replayRequest: Request): RecordingSnapshot[] {
export function getFullRecordingSnapshots(resOrReq: Request | Response): RecordingSnapshot[] {
const replayRequest = getRequest(resOrReq);
const events = getDecompressedRecordingEvents(replayRequest) as RecordingEvent[];
return events.filter(event => event.type === 2).map(event => event.data as RecordingSnapshot);
}

function getIncrementalRecordingSnapshots(replayRequest: Request): RecordingSnapshot[] {
function getIncrementalRecordingSnapshots(resOrReq: Request | Response): RecordingSnapshot[] {
const replayRequest = getRequest(resOrReq);
const events = getDecompressedRecordingEvents(replayRequest) as RecordingEvent[];
return events.filter(event => event.type === 3).map(event => event.data as RecordingSnapshot);
}

function getDecompressedRecordingEvents(replayRequest: Request): RecordingEvent[] {
function getDecompressedRecordingEvents(resOrReq: Request | Response): RecordingEvent[] {
const replayRequest = getRequest(resOrReq);
return replayEnvelopeRequestParser(replayRequest, 5) as RecordingEvent[];
}

export function getReplayRecordingContent(replayRequest: Request): RecordingContent {
export function getReplayRecordingContent(resOrReq: Request | Response): RecordingContent {
const replayRequest = getRequest(resOrReq);
const fullSnapshots = getFullRecordingSnapshots(replayRequest);
const incrementalSnapshots = getIncrementalRecordingSnapshots(replayRequest);
const customEvents = getCustomRecordingEvents(replayRequest);
Expand Down Expand Up @@ -255,3 +263,9 @@ function normalizeNumberAttribute(num: number): string {

return `[${stepCount * step}-${(stepCount + 1) * step}]`;
}

/** Get a request from either a request or a response */
function getRequest(resOrReq: Request | Response): Request {
// @ts-ignore we check this
return typeof resOrReq.request === 'function' ? (resOrReq as Response).request() : (resOrReq as Request);
}