diff --git a/.github/workflows/flaky-test-detector.yml b/.github/workflows/flaky-test-detector.yml index d2238ce58e71..5d0d5af5d247 100644 --- a/.github/workflows/flaky-test-detector.yml +++ b/.github/workflows/flaky-test-detector.yml @@ -68,10 +68,10 @@ jobs: CHANGED_TEST_PATHS: ${{ steps.changed.outputs.browser_integration_files }} TEST_RUN_COUNT: 'AUTO' - - name: Artifacts upload + - name: Upload Playwright Traces uses: actions/upload-artifact@v4 if: failure() && steps.test.outcome == 'failure' with: name: playwright-test-results - path: test-results + path: dev-packages/browser-integration-tests/test-results retention-days: 5 diff --git a/dev-packages/browser-integration-tests/suites/replay/slowClick/mutation/test.ts b/dev-packages/browser-integration-tests/suites/replay/slowClick/mutation/test.ts index ba3dd43ac3d3..aafdced81505 100644 --- a/dev-packages/browser-integration-tests/suites/replay/slowClick/mutation/test.ts +++ b/dev-packages/browser-integration-tests/suites/replay/slowClick/mutation/test.ts @@ -18,49 +18,51 @@ sentryTest('mutation after threshold results in slow click', async ({ forceFlush const url = await getLocalTestUrl({ testDir: __dirname }); - await Promise.all([waitForReplayRequest(page, 0), page.goto(url)]); - await forceFlushReplay(); + const replayRequestPromise = waitForReplayRequest(page, 0); + + const segmentReqWithSlowClickBreadcrumbPromise = waitForReplayRequest(page, (event, res) => { + const { breadcrumbs } = getCustomRecordingEvents(res); + + return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.slowClickDetected'); + }); - const [req1] = await Promise.all([ - waitForReplayRequest(page, (event, res) => { - const { breadcrumbs } = getCustomRecordingEvents(res); + await page.goto(url); + await replayRequestPromise; - return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.slowClickDetected'); - }), + await forceFlushReplay(); + + await page.locator('#mutationButton').click(); - page.locator('#mutationButton').click(), - ]); + const segmentReqWithSlowClick = await segmentReqWithSlowClickBreadcrumbPromise; - const { breadcrumbs } = getCustomRecordingEvents(req1); + const { breadcrumbs } = getCustomRecordingEvents(segmentReqWithSlowClick); const slowClickBreadcrumbs = breadcrumbs.filter(breadcrumb => breadcrumb.category === 'ui.slowClickDetected'); - expect(slowClickBreadcrumbs).toEqual([ - { - category: 'ui.slowClickDetected', - type: 'default', - data: { - endReason: 'mutation', - clickCount: 1, - node: { - attributes: { - id: 'mutationButton', - }, - id: expect.any(Number), - tagName: 'button', - textContent: '******* ********', + expect(slowClickBreadcrumbs).toContainEqual({ + category: 'ui.slowClickDetected', + type: 'default', + data: { + endReason: 'mutation', + clickCount: 1, + node: { + attributes: { + id: 'mutationButton', }, - nodeId: expect.any(Number), - timeAfterClickMs: expect.any(Number), - url: 'http://sentry-test.io/index.html', + id: expect.any(Number), + tagName: 'button', + textContent: '******* ********', }, - message: 'body > button#mutationButton', - timestamp: expect.any(Number), + nodeId: expect.any(Number), + timeAfterClickMs: expect.any(Number), + url: 'http://sentry-test.io/index.html', }, - ]); + message: 'body > button#mutationButton', + timestamp: expect.any(Number), + }); expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeGreaterThan(3000); - expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeLessThan(3500); + expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeLessThan(3501); }); sentryTest('multiple clicks are counted', async ({ getLocalTestUrl, page }) => { @@ -78,49 +80,50 @@ sentryTest('multiple clicks are counted', async ({ getLocalTestUrl, page }) => { const url = await getLocalTestUrl({ testDir: __dirname }); - await Promise.all([waitForReplayRequest(page, 0), page.goto(url)]); + const replayRequestPromise = waitForReplayRequest(page, 0); + const segmentReqWithSlowClickBreadcrumbPromise = waitForReplayRequest(page, (event, res) => { + const { breadcrumbs } = getCustomRecordingEvents(res); - const [req1] = await Promise.all([ - waitForReplayRequest(page, (event, res) => { - const { breadcrumbs } = getCustomRecordingEvents(res); + return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.slowClickDetected'); + }); - return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.slowClickDetected'); - }), - page.locator('#mutationButton').click({ clickCount: 4 }), - ]); + await page.goto(url); + await replayRequestPromise; - const { breadcrumbs } = getCustomRecordingEvents(req1); + await page.locator('#mutationButton').click({ clickCount: 4 }); + + const segmentReqWithSlowClick = await segmentReqWithSlowClickBreadcrumbPromise; + + const { breadcrumbs } = getCustomRecordingEvents(segmentReqWithSlowClick); const slowClickBreadcrumbs = breadcrumbs.filter(breadcrumb => breadcrumb.category === 'ui.slowClickDetected'); const multiClickBreadcrumbs = breadcrumbs.filter(breadcrumb => breadcrumb.category === 'ui.multiClick'); - expect(slowClickBreadcrumbs).toEqual([ - { - category: 'ui.slowClickDetected', - type: 'default', - data: { - endReason: 'mutation', - clickCount: 4, - node: { - attributes: { - id: 'mutationButton', - }, - id: expect.any(Number), - tagName: 'button', - textContent: '******* ********', + expect(slowClickBreadcrumbs).toContainEqual({ + category: 'ui.slowClickDetected', + type: 'default', + data: { + endReason: expect.stringMatching(/^(mutation|timeout)$/), + clickCount: 4, + node: { + attributes: { + id: 'mutationButton', }, - nodeId: expect.any(Number), - timeAfterClickMs: expect.any(Number), - url: 'http://sentry-test.io/index.html', + id: expect.any(Number), + tagName: 'button', + textContent: '******* ********', }, - message: 'body > button#mutationButton', - timestamp: expect.any(Number), + nodeId: expect.any(Number), + timeAfterClickMs: expect.any(Number), + url: 'http://sentry-test.io/index.html', }, - ]); + message: 'body > button#mutationButton', + timestamp: expect.any(Number), + }); expect(multiClickBreadcrumbs.length).toEqual(0); expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeGreaterThan(3000); - expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeLessThan(3500); + expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeLessThan(3501); }); sentryTest('immediate mutation does not trigger slow click', async ({ forceFlushReplay, getLocalTestUrl, page }) => { @@ -138,7 +141,15 @@ sentryTest('immediate mutation does not trigger slow click', async ({ forceFlush const url = await getLocalTestUrl({ testDir: __dirname }); - await Promise.all([waitForReplayRequest(page, 0), page.goto(url)]); + const replayRequestPromise = waitForReplayRequest(page, 0); + const segmentReqWithClickBreadcrumbPromise = waitForReplayRequest(page, (_event, res) => { + const { breadcrumbs } = getCustomRecordingEvents(res); + + return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.click'); + }); + + await page.goto(url); + await replayRequestPromise; await forceFlushReplay(); let slowClickCount = 0; @@ -150,36 +161,29 @@ sentryTest('immediate mutation does not trigger slow click', async ({ forceFlush slowClickCount += slowClicks.length; }); - const [req1] = await Promise.all([ - waitForReplayRequest(page, (_event, res) => { - const { breadcrumbs } = getCustomRecordingEvents(res); - - return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.click'); - }), - page.locator('#mutationButtonImmediately').click(), - ]); - - const { breadcrumbs } = getCustomRecordingEvents(req1); - - expect(breadcrumbs).toEqual([ - { - category: 'ui.click', - data: { - node: { - attributes: { - id: 'mutationButtonImmediately', - }, - id: expect.any(Number), - tagName: 'button', - textContent: '******* ******** ***********', + await page.locator('#mutationButtonImmediately').click(); + + const segmentReqWithSlowClick = await segmentReqWithClickBreadcrumbPromise; + + const { breadcrumbs } = getCustomRecordingEvents(segmentReqWithSlowClick); + + expect(breadcrumbs).toContainEqual({ + category: 'ui.click', + data: { + node: { + attributes: { + id: 'mutationButtonImmediately', }, - nodeId: expect.any(Number), + id: expect.any(Number), + tagName: 'button', + textContent: '******* ******** ***********', }, - message: 'body > button#mutationButtonImmediately', - timestamp: expect.any(Number), - type: 'default', + nodeId: expect.any(Number), }, - ]); + message: 'body > button#mutationButtonImmediately', + timestamp: expect.any(Number), + type: 'default', + }); // Ensure we wait for timeout, to make sure no slow click is created // Waiting for 3500 + 1s rounding room @@ -204,39 +208,41 @@ sentryTest('inline click handler does not trigger slow click', async ({ forceFlu const url = await getLocalTestUrl({ testDir: __dirname }); - await Promise.all([waitForReplayRequest(page, 0), page.goto(url)]); + const replayRequestPromise = waitForReplayRequest(page, 0); + const segmentReqWithClickBreadcrumbPromise = waitForReplayRequest(page, (event, res) => { + const { breadcrumbs } = getCustomRecordingEvents(res); + + return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.click'); + }); + + await page.goto(url); + await replayRequestPromise; + await forceFlushReplay(); - const [req1] = await Promise.all([ - waitForReplayRequest(page, (event, res) => { - const { breadcrumbs } = getCustomRecordingEvents(res); - - return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.click'); - }), - page.locator('#mutationButtonInline').click(), - ]); - - const { breadcrumbs } = getCustomRecordingEvents(req1); - - expect(breadcrumbs).toEqual([ - { - category: 'ui.click', - data: { - node: { - attributes: { - id: 'mutationButtonInline', - }, - id: expect.any(Number), - tagName: 'button', - textContent: '******* ******** ***********', + await page.locator('#mutationButtonInline').click(); + + const segmentReqWithClick = await segmentReqWithClickBreadcrumbPromise; + + const { breadcrumbs } = getCustomRecordingEvents(segmentReqWithClick); + + expect(breadcrumbs).toContainEqual({ + category: 'ui.click', + data: { + node: { + attributes: { + id: 'mutationButtonInline', }, - nodeId: expect.any(Number), + id: expect.any(Number), + tagName: 'button', + textContent: '******* ******** ***********', }, - message: 'body > button#mutationButtonInline', - timestamp: expect.any(Number), - type: 'default', + nodeId: expect.any(Number), }, - ]); + message: 'body > button#mutationButtonInline', + timestamp: expect.any(Number), + type: 'default', + }); }); sentryTest('mouseDown events are considered', async ({ getLocalTestUrl, page }) => { @@ -254,36 +260,36 @@ sentryTest('mouseDown events are considered', async ({ getLocalTestUrl, page }) const url = await getLocalTestUrl({ testDir: __dirname }); - await Promise.all([waitForReplayRequest(page, 0), page.goto(url)]); - - const [req1] = await Promise.all([ - waitForReplayRequest(page, (event, res) => { - const { breadcrumbs } = getCustomRecordingEvents(res); - - return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.click'); - }), - page.locator('#mouseDownButton').click(), - ]); - - const { breadcrumbs } = getCustomRecordingEvents(req1); - - expect(breadcrumbs).toEqual([ - { - category: 'ui.click', - data: { - node: { - attributes: { - id: 'mouseDownButton', - }, - id: expect.any(Number), - tagName: 'button', - textContent: '******* ******** ** ***** ****', + const replayRequestPromise = waitForReplayRequest(page, 0); + const segmentReqWithClickBreadcrumbPromise = waitForReplayRequest(page, (event, res) => { + const { breadcrumbs } = getCustomRecordingEvents(res); + + return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.click'); + }); + + await page.goto(url); + await replayRequestPromise; + + await page.locator('#mouseDownButton').click(); + const segmentReqWithClick = await segmentReqWithClickBreadcrumbPromise; + + const { breadcrumbs } = getCustomRecordingEvents(segmentReqWithClick); + + expect(breadcrumbs).toContainEqual({ + category: 'ui.click', + data: { + node: { + attributes: { + id: 'mouseDownButton', }, - nodeId: expect.any(Number), + id: expect.any(Number), + tagName: 'button', + textContent: '******* ******** ** ***** ****', }, - message: 'body > button#mouseDownButton', - timestamp: expect.any(Number), - type: 'default', + nodeId: expect.any(Number), }, - ]); + message: 'body > button#mouseDownButton', + timestamp: expect.any(Number), + type: 'default', + }); });