Skip to content

test: Unflake slow click test #13252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/flaky-test-detector.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand All @@ -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 }) => {
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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 }) => {
Expand All @@ -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',
});
});
Loading