Skip to content

feat(replay): Share performance instrumentation with tracing #9296

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 4 commits into from
Oct 24, 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,7 +1,6 @@
import * as glob from 'glob';
import * as path from 'path';
import * as childProcess from 'child_process';
import { promisify } from 'util';

async function run(): Promise<void> {
let testPaths: string[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { envelopeRequestParser } from '../../../utils/helpers';
import {
getDecompressedRecordingEvents,
getReplaySnapshot,
isCustomSnapshot,
isReplayEvent,
REPLAY_DEFAULT_FLUSH_MAX_DELAY,
shouldSkipReplayTest,
Expand Down Expand Up @@ -41,8 +42,8 @@ sentryTest(
// We only want to count replays here
if (event && isReplayEvent(event)) {
const events = getDecompressedRecordingEvents(route.request());
// this makes sure we ignore e.g. mouse move events which can otherwise lead to flakes
if (events.length > 0) {
// Make sure to not count mouse moves or performance spans
if (events.filter(event => !isCustomSnapshot(event) || event.data.tag !== 'performanceSpan').length > 0) {
called++;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
<meta charset="utf-8" />
</head>
<body>
<button id="button-add">Add items</button>
<button id="button-modify">Modify items</button>
<button id="button-remove">Remove items</button>
<button id="noop" type="button">Noop</button>
<button id="button-add" type="button">Add items</button>
<button id="button-modify" type="button">Modify items</button>
<button id="button-remove" type="button">Remove items</button>
<ul class="list"></ul>

<script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,47 @@ sentryTest(

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

const [res0] = await Promise.all([waitForReplayRequest(page, 0), page.goto(url)]);
// We have to click in order to ensure the LCP is generated, leading to consistent results
async function gotoPageAndClick() {
await page.goto(url);
await page.click('#noop');
}
const [res0] = await Promise.all([waitForReplayRequest(page, 0), gotoPageAndClick()]);
await forceFlushReplay();

const [res1] = await Promise.all([waitForReplayRequest(page), page.click('#button-add')]);
await forceFlushReplay();
const [res1] = await Promise.all([
waitForReplayRequest(page, (_event, res) => {
const parsed = getReplayRecordingContent(res);
return !!parsed.incrementalSnapshots.length || !!parsed.fullSnapshots.length;
}),
page.click('#button-add'),
forceFlushReplay(),
]);

const [res2] = await Promise.all([waitForReplayRequest(page), page.click('#button-modify')]);
await forceFlushReplay();
const [res2] = await Promise.all([
waitForReplayRequest(page, (_event, res) => {
const parsed = getReplayRecordingContent(res);
return !!parsed.incrementalSnapshots.length || !!parsed.fullSnapshots.length;
}),
page.click('#button-modify'),
forceFlushReplay(),
]);

const [res3] = await Promise.all([waitForReplayRequest(page), page.click('#button-remove')]);
await forceFlushReplay();
const [res3] = await Promise.all([
waitForReplayRequest(page, (_event, res) => {
const parsed = getReplayRecordingContent(res);
return !!parsed.incrementalSnapshots.length || !!parsed.fullSnapshots.length;
}),
page.click('#button-remove'),
forceFlushReplay(),
]);

const replayData0 = getReplayRecordingContent(res0);
const replayData1 = getReplayRecordingContent(res1);
const replayData2 = getReplayRecordingContent(res2);
const replayData3 = getReplayRecordingContent(res3);

expect(replayData0.fullSnapshots.length).toBe(1);
expect(replayData0.incrementalSnapshots.length).toBe(0);

expect(replayData1.fullSnapshots.length).toBe(0);
expect(replayData1.incrementalSnapshots.length).toBeGreaterThan(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
<meta charset="utf-8" />
</head>
<body>
<button id="button-add">Add items</button>
<button id="button-modify">Modify items</button>
<button id="button-remove">Remove items</button>
<button id="noop" type="button">Noop</button>
<button id="button-add" type="button">Add items</button>
<button id="button-modify" type="button">Modify items</button>
<button id="button-remove" type="button">Remove items</button>
<ul class="list"></ul>

<script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,26 @@ sentryTest(
});
});

const reqPromise0 = waitForReplayRequest(page, 0);

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

const [res0] = await Promise.all([reqPromise0, page.goto(url)]);
await forceFlushReplay();

const reqPromise1 = waitForReplayRequest(page);
// We have to click in order to ensure the LCP is generated, leading to consistent results
async function gotoPageAndClick() {
await page.goto(url);
await page.click('#noop');
}

const [res1] = await Promise.all([reqPromise1, page.click('#button-add')]);
const [res0] = await Promise.all([waitForReplayRequest(page, 0), gotoPageAndClick()]);
await forceFlushReplay();

const [res1] = await Promise.all([
waitForReplayRequest(page, (_event, res) => {
const parsed = getReplayRecordingContent(res);
return !!parsed.incrementalSnapshots.length || !!parsed.fullSnapshots.length;
}),
page.click('#button-add'),
forceFlushReplay(),
]);

// replay should be stopped due to mutation limit
let replay = await getReplaySnapshot(page);
expect(replay.session).toBe(undefined);
Expand All @@ -48,7 +56,6 @@ sentryTest(

const replayData0 = getReplayRecordingContent(res0);
expect(replayData0.fullSnapshots.length).toBe(1);
expect(replayData0.incrementalSnapshots.length).toBe(0);

// Breadcrumbs (click and mutation);
const replayData1 = getReplayRecordingContent(res1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
<body>
<div id="content"></div>
<img src="https://example.com/path/to/image.png" />
<button type="button">Test button</button>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ sentryTest('should capture a LCP vital with element details.', async ({ browserN
);

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

const eventData = await getFirstSentryEnvelopeRequest<Event>(page);
const [eventData] = await Promise.all([
getFirstSentryEnvelopeRequest<Event>(page),
page.goto(url),
page.click('button'),
]);

expect(eventData.measurements).toBeDefined();
expect(eventData.measurements?.lcp?.value).toBeDefined();
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-integration-tests/utils/replayHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ function isFullSnapshot(event: RecordingEvent): event is FullRecordingSnapshot {
return event.type === EventType.FullSnapshot;
}

function isCustomSnapshot(event: RecordingEvent): event is RecordingEvent & { data: CustomRecordingEvent } {
export function isCustomSnapshot(event: RecordingEvent): event is RecordingEvent & { data: CustomRecordingEvent } {
return event.type === EventType.Custom;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ test('Sends a Replay recording to Sentry', async ({ browser }) => {
return window.sentryReplayId;
});

// Keypress event ensures LCP is finished
await page.type('body', 'Y');

// Wait for replay to be sent

if (replayId === undefined) {
Expand Down Expand Up @@ -229,10 +232,7 @@ test('Sends a Replay recording to Sentry', async ({ browser }) => {
{ headers: { Authorization: `Bearer ${authToken}` } },
);

return {
status: response.status,
data: response.data,
};
return response.status === 200 ? response.data[0] : response.status;
} catch (e) {
if (e instanceof AxiosError && e.response) {
if (e.response.status !== 404) {
Expand All @@ -249,8 +249,5 @@ test('Sends a Replay recording to Sentry', async ({ browser }) => {
timeout: EVENT_POLLING_TIMEOUT,
},
)
.toEqual({
status: 200,
data: ReplayRecordingData,
});
.toEqual(ReplayRecordingData);
});
Loading