|
1 | 1 | import { expect } from '@playwright/test';
|
| 2 | +import type { Breadcrumb } from '@sentry/types'; |
2 | 3 |
|
3 | 4 | import { sentryTest } from '../../../utils/fixtures';
|
4 |
| -import { getCustomRecordingEvents, shouldSkipReplayTest, waitForReplayRequest } from '../../../utils/replayHelpers'; |
| 5 | +import type { PerformanceSpan } from '../../../utils/replayHelpers'; |
| 6 | +import { |
| 7 | + getCustomRecordingEvents, |
| 8 | + getReplayEventFromRequest, |
| 9 | + shouldSkipReplayTest, |
| 10 | + waitForReplayRequest, |
| 11 | +} from '../../../utils/replayHelpers'; |
5 | 12 |
|
6 | 13 | const COUNT = 250;
|
7 | 14 | const THROTTLE_LIMIT = 300;
|
@@ -49,86 +56,77 @@ sentryTest(
|
49 | 56 | await page.goto(url);
|
50 | 57 | await reqPromise0;
|
51 | 58 |
|
52 |
| - const reqPromise1 = waitForReplayRequest( |
53 |
| - page, |
54 |
| - (_event, res) => { |
55 |
| - const { performanceSpans } = getCustomRecordingEvents(res); |
56 |
| - |
57 |
| - return performanceSpans.some(span => span.op === 'resource.script'); |
58 |
| - }, |
59 |
| - 10_000, |
60 |
| - ); |
61 |
| - const reqPromise1Breadcrumbs = waitForReplayRequest( |
62 |
| - page, |
63 |
| - (_event, res) => { |
64 |
| - const { breadcrumbs } = getCustomRecordingEvents(res); |
65 |
| - |
66 |
| - return breadcrumbs.some(breadcrumb => breadcrumb.category === 'replay.throttled'); |
67 |
| - }, |
68 |
| - 10_000, |
69 |
| - ); |
| 59 | + let collectedSpans: PerformanceSpan[] = []; |
| 60 | + let collectedBreadcrumbs: Breadcrumb[] = []; |
| 61 | + |
| 62 | + page.on('response', response => { |
| 63 | + // We only capture sentry stuff |
| 64 | + if (!response.url().includes('https://dsn.ingest.sentry')) { |
| 65 | + return; |
| 66 | + } |
| 67 | + |
| 68 | + // If this is undefined, this is not a replay request |
| 69 | + if (!getReplayEventFromRequest(response.request())) { |
| 70 | + return; |
| 71 | + } |
| 72 | + |
| 73 | + const { performanceSpans, breadcrumbs } = getCustomRecordingEvents(response); |
| 74 | + |
| 75 | + collectedSpans.push( |
| 76 | + ...performanceSpans.filter(span => span.op === 'resource.script' || span.op === 'resource.fetch'), |
| 77 | + ); |
| 78 | + collectedBreadcrumbs.push(...breadcrumbs.filter(breadcrumb => breadcrumb.category === 'replay.throttled')); |
| 79 | + }); |
70 | 80 |
|
71 | 81 | await page.click('[data-network]');
|
72 | 82 | await page.click('[data-fetch]');
|
73 | 83 |
|
74 | 84 | await page.waitForFunction('window.__isLoaded()');
|
75 | 85 | await forceFlushReplay();
|
76 | 86 |
|
77 |
| - const { performanceSpans } = getCustomRecordingEvents(await reqPromise1); |
78 |
| - const { breadcrumbs } = getCustomRecordingEvents(await reqPromise1Breadcrumbs); |
| 87 | + await waitForFunction(() => collectedBreadcrumbs.length === 1, 10_000, 100); |
79 | 88 |
|
80 | 89 | // All assets have been _loaded_
|
81 | 90 | expect(scriptsLoaded).toBe(COUNT);
|
82 | 91 | expect(fetchLoaded).toBe(COUNT);
|
83 | 92 |
|
84 | 93 | // But only some have been captured by replay
|
85 |
| - // We check for <= THROTTLE_LIMIT, as there have been some captured before, which take up some of the throttle limit |
86 |
| - expect(performanceSpans.length).toBeLessThanOrEqual(THROTTLE_LIMIT); |
87 |
| - expect(performanceSpans.length).toBeGreaterThan(THROTTLE_LIMIT - 50); |
88 |
| - |
89 |
| - expect(breadcrumbs.filter(({ category }) => category === 'replay.throttled').length).toBe(1); |
| 94 | + // We give it some wiggle room to account for flakyness |
| 95 | + expect(collectedSpans.length).toBeLessThanOrEqual(THROTTLE_LIMIT); |
| 96 | + expect(collectedSpans.length).toBeGreaterThanOrEqual(THROTTLE_LIMIT - 50); |
| 97 | + expect(collectedBreadcrumbs.length).toBe(1); |
90 | 98 |
|
91 | 99 | // Now we wait for 6s (5s + some wiggle room), and make some requests again
|
92 |
| - await page.waitForTimeout(7_000); |
| 100 | + await page.waitForTimeout(6_000); |
93 | 101 | await forceFlushReplay();
|
94 | 102 |
|
95 |
| - const reqPromise2 = waitForReplayRequest( |
96 |
| - page, |
97 |
| - (_event, res) => { |
98 |
| - const { performanceSpans } = getCustomRecordingEvents(res); |
99 |
| - |
100 |
| - return performanceSpans.some(span => span.op === 'resource.script'); |
101 |
| - }, |
102 |
| - 10_000, |
103 |
| - ); |
104 |
| - const reqPromise2Breadcrumbs = waitForReplayRequest( |
105 |
| - page, |
106 |
| - (_event, res) => { |
107 |
| - const { breadcrumbs } = getCustomRecordingEvents(res); |
108 |
| - |
109 |
| - return breadcrumbs.some(breadcrumb => breadcrumb.category === 'replay.throttled'); |
110 |
| - }, |
111 |
| - 10_000, |
112 |
| - ); |
| 103 | + // Reset collectors |
| 104 | + collectedSpans = []; |
| 105 | + collectedBreadcrumbs = []; |
113 | 106 |
|
114 | 107 | await page.click('[data-network]');
|
115 | 108 | await page.click('[data-fetch]');
|
116 | 109 |
|
117 | 110 | await page.waitForFunction('window.__isLoaded(2)');
|
118 | 111 | await forceFlushReplay();
|
119 | 112 |
|
120 |
| - const { performanceSpans: performanceSpans2 } = getCustomRecordingEvents(await reqPromise2); |
121 |
| - const { breadcrumbs: breadcrumbs2 } = getCustomRecordingEvents(await reqPromise2Breadcrumbs); |
| 113 | + await waitForFunction(() => collectedBreadcrumbs.length === 1, 10_000, 100); |
122 | 114 |
|
123 | 115 | // All assets have been _loaded_
|
124 | 116 | expect(scriptsLoaded).toBe(COUNT * 2);
|
125 | 117 | expect(fetchLoaded).toBe(COUNT * 2);
|
126 | 118 |
|
127 | 119 | // But only some have been captured by replay
|
128 |
| - // We check for <= THROTTLE_LIMIT, as there have been some captured before, which take up some of the throttle limit |
129 |
| - expect(performanceSpans2.length).toBeLessThanOrEqual(THROTTLE_LIMIT); |
130 |
| - expect(performanceSpans2.length).toBeGreaterThan(THROTTLE_LIMIT - 50); |
131 |
| - |
132 |
| - expect(breadcrumbs2.filter(({ category }) => category === 'replay.throttled').length).toBe(1); |
| 120 | + // We give it some wiggle room to account for flakyness |
| 121 | + expect(collectedSpans.length).toBeLessThanOrEqual(THROTTLE_LIMIT); |
| 122 | + expect(collectedSpans.length).toBeGreaterThanOrEqual(THROTTLE_LIMIT - 50); |
| 123 | + expect(collectedBreadcrumbs.length).toBe(1); |
133 | 124 | },
|
134 | 125 | );
|
| 126 | + |
| 127 | +async function waitForFunction(cb: () => boolean, timeout = 2000, increment = 100) { |
| 128 | + while (timeout > 0 && !cb()) { |
| 129 | + await new Promise(resolve => setTimeout(resolve, increment)); |
| 130 | + await waitForFunction(cb, timeout - increment, increment); |
| 131 | + } |
| 132 | +} |
0 commit comments