/
test.ts
106 lines (85 loc) · 3.25 KB
/
test.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { expect } from '@playwright/test';
import type { Breadcrumb } from '@sentry/types';
import { sentryTest } from '../../../utils/fixtures';
import type { PerformanceSpan } from '../../../utils/replayHelpers';
import {
getCustomRecordingEvents,
getReplayEventFromRequest,
shouldSkipReplayTest,
waitForReplayRequest,
} from '../../../utils/replayHelpers';
const COUNT = 250;
const THROTTLE_LIMIT = 300;
sentryTest(
'throttles breadcrumbs when many requests are made at the same time',
async ({ getLocalTestUrl, page, forceFlushReplay, browserName }) => {
if (shouldSkipReplayTest() || browserName !== 'chromium') {
sentryTest.skip();
}
const reqPromise0 = waitForReplayRequest(page, 0);
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ id: 'test-id' }),
});
});
let scriptsLoaded = 0;
let fetchLoaded = 0;
await page.route('**/virtual-assets/script-**', route => {
scriptsLoaded++;
return route.fulfill({
status: 200,
contentType: 'text/javascript',
body: `const aha = ${'xx'.repeat(20_000)};`,
});
});
await page.route('**/virtual-assets/fetch-**', route => {
fetchLoaded++;
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ fetchResponse: 'aa'.repeat(20_000) }),
});
});
const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);
await reqPromise0;
const collectedSpans: PerformanceSpan[] = [];
const collectedBreadcrumbs: Breadcrumb[] = [];
page.on('response', response => {
// We only capture sentry stuff
if (!response.url().includes('https://dsn.ingest.sentry')) {
return;
}
// If this is undefined, this is not a replay request
if (!getReplayEventFromRequest(response.request())) {
return;
}
const { performanceSpans, breadcrumbs } = getCustomRecordingEvents(response);
collectedSpans.push(
...performanceSpans.filter(span => span.op === 'resource.script' || span.op === 'resource.fetch'),
);
collectedBreadcrumbs.push(...breadcrumbs.filter(breadcrumb => breadcrumb.category === 'replay.throttled'));
});
await page.click('[data-network]');
await page.click('[data-fetch]');
await page.waitForFunction('window.__isLoaded()');
await forceFlushReplay();
await waitForFunction(() => collectedBreadcrumbs.length === 1, 6_000, 100);
// All assets have been _loaded_
expect(scriptsLoaded).toBe(COUNT);
expect(fetchLoaded).toBe(COUNT);
// But only some have been captured by replay
// We give it some wiggle room to account for flakyness
expect(collectedSpans.length).toBeLessThanOrEqual(THROTTLE_LIMIT);
expect(collectedSpans.length).toBeGreaterThanOrEqual(THROTTLE_LIMIT - 50);
expect(collectedBreadcrumbs.length).toBe(1);
},
);
async function waitForFunction(cb: () => boolean, timeout = 2000, increment = 100) {
while (timeout > 0 && !cb()) {
await new Promise(resolve => setTimeout(resolve, increment));
await waitForFunction(cb, timeout - increment, increment);
}
}