diff --git a/dev-packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts b/dev-packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts index 898a8aeeae5f..b7de95d47434 100644 --- a/dev-packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts +++ b/dev-packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts @@ -118,8 +118,9 @@ sentryTest('captures multiple multi clicks', async ({ getLocalTestUrl, page, for await page.locator('#mutationButtonImmediately').click({ clickCount: 4 }); await forceFlushReplay(); - // Ensure we waited at least 1s, which is the threshold to create a new ui.click breadcrumb - await new Promise(resolve => setTimeout(resolve, 1001)); + // The browser's 1s breadcrumb debounce timer must have fired before the next click creates a new ui.click + // breadcrumb, so leave some slack for timer delays on slow CI machines. + await new Promise(resolve => setTimeout(resolve, 1500)); await page.locator('#mutationButtonImmediately').click({ clickCount: 2 }); await forceFlushReplay(); diff --git a/packages/replay-internal/src/coreHandlers/handleClick.ts b/packages/replay-internal/src/coreHandlers/handleClick.ts index f4576c3a7ca7..263b53ed42dd 100644 --- a/packages/replay-internal/src/coreHandlers/handleClick.ts +++ b/packages/replay-internal/src/coreHandlers/handleClick.ts @@ -183,10 +183,10 @@ export class ClickDetector implements ReplayClickDetector { const now = nowInSeconds(); this._clicks.forEach(click => { - if (!click.mutationAfter && this._lastMutation) { + if (click.mutationAfter === undefined && this._lastMutation) { click.mutationAfter = click.timestamp <= this._lastMutation ? this._lastMutation - click.timestamp : undefined; } - if (!click.scrollAfter && this._lastScroll) { + if (click.scrollAfter === undefined && this._lastScroll) { click.scrollAfter = click.timestamp <= this._lastScroll ? this._lastScroll - click.timestamp : undefined; } @@ -215,8 +215,8 @@ export class ClickDetector implements ReplayClickDetector { /** Generate matching breadcrumb(s) for the click. */ private _generateBreadcrumbs(click: Click): void { const replay = this._replay; - const hadScroll = click.scrollAfter && click.scrollAfter <= this._scrollTimeout; - const hadMutation = click.mutationAfter && click.mutationAfter <= this._threshold; + const hadScroll = click.scrollAfter !== undefined && click.scrollAfter <= this._scrollTimeout; + const hadMutation = click.mutationAfter !== undefined && click.mutationAfter <= this._threshold; const isSlowClick = !hadScroll && !hadMutation; const { clickCount, clickBreadcrumb } = click; diff --git a/packages/replay-internal/test/unit/coreHandlers/handleClick.test.ts b/packages/replay-internal/test/unit/coreHandlers/handleClick.test.ts index 66beddf6ab19..445ce6993dca 100644 --- a/packages/replay-internal/test/unit/coreHandlers/handleClick.test.ts +++ b/packages/replay-internal/test/unit/coreHandlers/handleClick.test.ts @@ -321,6 +321,23 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); }); + test('it does not consider clicks with mutation in the same millisecond as slow click', async () => { + const breadcrumb: Breadcrumb = { + timestamp: BASE_TIMESTAMP / 1000, + data: { + nodeId: 1, + }, + }; + const node = document.createElement('button'); + detector.handleClick(breadcrumb, node); + + detector.registerMutation(BASE_TIMESTAMP); + + vi.advanceTimersByTime(3_500); + + expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); + }); + test('it considers clicks with mutation after threshold as slow click', async () => { const breadcrumb: Breadcrumb = { timestamp: BASE_TIMESTAMP / 1000, @@ -453,6 +470,23 @@ describe('Unit | coreHandlers | handleClick', () => { expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); }); + test('it does not consider clicks with scroll in the same millisecond as slow click', async () => { + const breadcrumb: Breadcrumb = { + timestamp: BASE_TIMESTAMP / 1000, + data: { + nodeId: 1, + }, + }; + const node = document.createElement('button'); + detector.handleClick(breadcrumb, node); + + detector.registerScroll(BASE_TIMESTAMP); + + vi.advanceTimersByTime(3_500); + + expect(mockAddBreadcrumbEvent).toHaveBeenCalledTimes(0); + }); + test('it considers clicks with scroll after threshold as slow click', async () => { const breadcrumb: Breadcrumb = { timestamp: BASE_TIMESTAMP / 1000,