diff --git a/.github/workflows/flaky-test-detector.yml b/.github/workflows/flaky-test-detector.yml index 065fdbe1d747..44edf51fd45d 100644 --- a/.github/workflows/flaky-test-detector.yml +++ b/.github/workflows/flaky-test-detector.yml @@ -79,8 +79,17 @@ jobs: browser_integration: dev-packages/browser-integration-tests/suites/**/test.ts - name: Detect flaky tests + id: test run: yarn test:detect-flaky working-directory: dev-packages/browser-integration-tests env: CHANGED_TEST_PATHS: ${{ steps.changed.outputs.browser_integration_files }} TEST_RUN_COUNT: 'AUTO' + + - name: Artifacts upload + uses: actions/upload-artifact@v4 + if: failure() && steps.test.outcome == 'failure' + with: + name: playwright-test-results + path: test-results + retention-days: 5 diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/test.ts index 0b0b37fb1cf6..19e85e8b03db 100644 --- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/test.ts +++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestHeaders/test.ts @@ -8,9 +8,7 @@ import { shouldSkipReplayTest, } from '../../../../../utils/replayHelpers'; -// Skipping because this test is flaky -// https://github.com/getsentry/sentry-javascript/issues/11062 -sentryTest.skip('handles empty/missing request headers', async ({ getLocalTestPath, page, browserName }) => { +sentryTest('handles empty/missing request headers', async ({ getLocalTestPath, page, browserName }) => { if (shouldSkipReplayTest()) { sentryTest.skip(); } @@ -37,18 +35,21 @@ sentryTest.skip('handles empty/missing request headers', async ({ getLocalTestPa const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - /* eslint-disable */ - fetch('http://localhost:7654/foo', { - method: 'POST', - }).then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - /* eslint-enable */ - }); + const [, request, { replayRecordingSnapshots }] = await Promise.all([ + page.evaluate(() => { + /* eslint-disable */ + fetch('http://localhost:7654/foo', { + method: 'POST', + }).then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }), + requestPromise, + replayRequestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -65,7 +66,6 @@ sentryTest.skip('handles empty/missing request headers', async ({ getLocalTestPa }, }); - const { replayRecordingSnapshots } = await replayRequestPromise; expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.fetch')).toEqual([ { data: { @@ -110,25 +110,28 @@ sentryTest('captures request headers as POJO', async ({ getLocalTestPath, page, const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - /* eslint-disable */ - fetch('http://localhost:7654/foo', { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Cache: 'no-cache', - 'X-Custom-Header': 'foo', - 'X-Test-Header': 'test-value', - }, - }).then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - /* eslint-enable */ - }); + const [, request, { replayRecordingSnapshots }] = await Promise.all([ + page.evaluate(() => { + /* eslint-disable */ + fetch('http://localhost:7654/foo', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Cache: 'no-cache', + 'X-Custom-Header': 'foo', + 'X-Test-Header': 'test-value', + }, + }).then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }), + requestPromise, + replayRequestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -145,7 +148,6 @@ sentryTest('captures request headers as POJO', async ({ getLocalTestPath, page, }, }); - const { replayRecordingSnapshots } = await replayRequestPromise; expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.fetch')).toEqual([ { data: { @@ -195,25 +197,28 @@ sentryTest('captures request headers on Request', async ({ getLocalTestPath, pag const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - const request = new Request('http://localhost:7654/foo', { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Cache: 'no-cache', - 'X-Custom-Header': 'foo', - }, - }); - /* eslint-disable */ - fetch(request).then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - /* eslint-enable */ - }); + const [, request, { replayRecordingSnapshots }] = await Promise.all([ + page.evaluate(() => { + const request = new Request('http://localhost:7654/foo', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Cache: 'no-cache', + 'X-Custom-Header': 'foo', + }, + }); + /* eslint-disable */ + fetch(request).then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }), + requestPromise, + replayRequestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -230,7 +235,6 @@ sentryTest('captures request headers on Request', async ({ getLocalTestPath, pag }, }); - const { replayRecordingSnapshots } = await replayRequestPromise; expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.fetch')).toEqual([ { data: { @@ -280,25 +284,28 @@ sentryTest('captures request headers as Headers instance', async ({ getLocalTest await page.goto(url); - await page.evaluate(() => { - const headers = new Headers(); - headers.append('Accept', 'application/json'); - headers.append('Content-Type', 'application/json'); - headers.append('Cache', 'no-cache'); - headers.append('X-Custom-Header', 'foo'); + const [, request, { replayRecordingSnapshots }] = await Promise.all([ + page.evaluate(() => { + const headers = new Headers(); + headers.append('Accept', 'application/json'); + headers.append('Content-Type', 'application/json'); + headers.append('Cache', 'no-cache'); + headers.append('X-Custom-Header', 'foo'); - /* eslint-disable */ - fetch('http://localhost:7654/foo', { - method: 'POST', - headers, - }).then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - /* eslint-enable */ - }); + /* eslint-disable */ + fetch('http://localhost:7654/foo', { + method: 'POST', + headers, + }).then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }), + requestPromise, + replayRequestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -315,7 +322,6 @@ sentryTest('captures request headers as Headers instance', async ({ getLocalTest }, }); - const { replayRecordingSnapshots } = await replayRequestPromise; expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.fetch')).toEqual([ { data: { @@ -364,25 +370,28 @@ sentryTest('does not captures request headers if URL does not match', async ({ g const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - /* eslint-disable */ - fetch('http://localhost:7654/bar', { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Cache: 'no-cache', - 'X-Custom-Header': 'foo', - 'X-Test-Header': 'test-value', - }, - }).then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - /* eslint-enable */ - }); + const [, request, { replayRecordingSnapshots }] = await Promise.all([ + page.evaluate(() => { + /* eslint-disable */ + fetch('http://localhost:7654/bar', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Cache: 'no-cache', + 'X-Custom-Header': 'foo', + 'X-Test-Header': 'test-value', + }, + }).then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }), + requestPromise, + replayRequestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -399,7 +408,6 @@ sentryTest('does not captures request headers if URL does not match', async ({ g }, }); - const { replayRecordingSnapshots } = await replayRequestPromise; expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.fetch')).toEqual([ { data: { diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestSize/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestSize/test.ts index 52857d17479d..dafa896f2cbd 100644 --- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestSize/test.ts +++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureRequestSize/test.ts @@ -8,9 +8,7 @@ import { shouldSkipReplayTest, } from '../../../../../utils/replayHelpers'; -// Skipping because this test is flaky -// https://github.com/getsentry/sentry-javascript/issues/10395 -sentryTest.skip('captures request body size when body is sent', async ({ getLocalTestPath, page }) => { +sentryTest('captures request body size when body is sent', async ({ getLocalTestPath, page }) => { if (shouldSkipReplayTest()) { sentryTest.skip(); } @@ -37,19 +35,22 @@ sentryTest.skip('captures request body size when body is sent', async ({ getLoca const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - /* eslint-disable */ - fetch('http://localhost:7654/foo', { - method: 'POST', - body: '{"foo":"bar"}', - }).then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - /* eslint-enable */ - }); + const [, request, { replayRecordingSnapshots }] = await Promise.all([ + page.evaluate(() => { + /* eslint-disable */ + fetch('http://localhost:7654/foo', { + method: 'POST', + body: '{"foo":"bar"}', + }).then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }), + requestPromise, + replayRequestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -67,7 +68,6 @@ sentryTest.skip('captures request body size when body is sent', async ({ getLoca }, }); - const { replayRecordingSnapshots } = await replayRequestPromise; expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.fetch')).toEqual([ { data: { @@ -122,21 +122,24 @@ sentryTest('captures request size from non-text request body', async ({ getLocal const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - /* eslint-disable */ - const blob = new Blob(['Hello world!!'], { type: 'text/html' }); + const [, request, { replayRecordingSnapshots }] = await Promise.all([ + page.evaluate(() => { + /* eslint-disable */ + const blob = new Blob(['Hello world!!'], { type: 'text/html' }); - fetch('http://localhost:7654/foo', { - method: 'POST', - body: blob, - }).then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - /* eslint-enable */ - }); + fetch('http://localhost:7654/foo', { + method: 'POST', + body: blob, + }).then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }), + requestPromise, + replayRequestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -154,7 +157,6 @@ sentryTest('captures request size from non-text request body', async ({ getLocal }, }); - const { replayRecordingSnapshots } = await replayRequestPromise; expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.fetch')).toEqual([ { data: { diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/test.ts index 8f098627c120..8486cf541a18 100644 --- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/test.ts +++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseHeaders/test.ts @@ -37,14 +37,16 @@ sentryTest('handles empty headers', async ({ getLocalTestPath, page, browserName const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - fetch('http://localhost:7654/foo').then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - }); + const [, request] = await Promise.all([ + page.evaluate(() => { + fetch('http://localhost:7654/foo').then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + }), + requestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -111,14 +113,16 @@ sentryTest('captures response headers', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - fetch('http://localhost:7654/foo').then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - }); + const [, request] = await Promise.all([ + page.evaluate(() => { + fetch('http://localhost:7654/foo').then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + }), + requestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -191,14 +195,16 @@ sentryTest('does not capture response headers if URL does not match', async ({ g const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - fetch('http://localhost:7654/bar').then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - }); + const [, request] = await Promise.all([ + page.evaluate(() => { + fetch('http://localhost:7654/bar').then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + }), + requestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); diff --git a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseSize/test.ts b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseSize/test.ts index ad3aafe34562..63e3611d9d8f 100644 --- a/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseSize/test.ts +++ b/dev-packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/captureResponseSize/test.ts @@ -43,16 +43,18 @@ sentryTest('captures response size from Content-Length header if available', asy const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - /* eslint-disable */ - fetch('http://localhost:7654/foo').then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - /* eslint-enable */ - }); + const [, request] = await Promise.all([ + page.evaluate(() => { + /* eslint-disable */ + fetch('http://localhost:7654/foo').then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }), + requestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -133,16 +135,18 @@ sentryTest('captures response size without Content-Length header', async ({ getL const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - /* eslint-disable */ - fetch('http://localhost:7654/foo').then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - /* eslint-enable */ - }); + const [, request] = await Promise.all([ + page.evaluate(() => { + /* eslint-disable */ + fetch('http://localhost:7654/foo').then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }), + requestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -220,18 +224,21 @@ sentryTest('captures response size from non-text response body', async ({ getLoc const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.evaluate(() => { - /* eslint-disable */ - fetch('http://localhost:7654/foo', { - method: 'POST', - }).then(() => { - // @ts-expect-error Sentry is a global - Sentry.captureException('test error'); - }); - /* eslint-enable */ - }); + const [, request, { replayRecordingSnapshots }] = await Promise.all([ + page.evaluate(() => { + /* eslint-disable */ + fetch('http://localhost:7654/foo', { + method: 'POST', + }).then(() => { + // @ts-expect-error Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }), + requestPromise, + replayRequestPromise, + ]); - const request = await requestPromise; const eventData = envelopeRequestParser(request); expect(eventData.exception?.values).toHaveLength(1); @@ -248,7 +255,6 @@ sentryTest('captures response size from non-text response body', async ({ getLoc }, }); - const { replayRecordingSnapshots } = await replayRequestPromise; expect(getReplayPerformanceSpans(replayRecordingSnapshots).filter(span => span.op === 'resource.fetch')).toEqual([ { data: {