|
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