diff --git a/packages/rum-core/src/browser/performanceCollection.ts b/packages/rum-core/src/browser/performanceCollection.ts index 74314326de..adc4a2ce5d 100644 --- a/packages/rum-core/src/browser/performanceCollection.ts +++ b/packages/rum-core/src/browser/performanceCollection.ts @@ -86,7 +86,9 @@ export interface RumFirstInputTiming { entryType: 'first-input' startTime: RelativeTime processingStart: RelativeTime + duration: Duration target?: Node + interactionId?: number } export interface RumPerformanceEventTiming { @@ -264,6 +266,7 @@ function retrieveFirstInputTiming(configuration: RumConfiguration, callback: (ti entryType: 'first-input', processingStart: relativeNow(), startTime: evt.timeStamp as RelativeTime, + duration: 0 as Duration, // arbitrary value to avoid nullable duration and simplify INP logic } if (evt.type === DOM_EVENT.POINTER_DOWN) { diff --git a/packages/rum-core/src/domain/rumEventsCollection/view/trackInitialViewTimings.spec.ts b/packages/rum-core/src/domain/rumEventsCollection/view/trackInitialViewTimings.spec.ts index f300f68772..f38d4d57f8 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/view/trackInitialViewTimings.spec.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/view/trackInitialViewTimings.spec.ts @@ -50,6 +50,7 @@ const FAKE_FIRST_INPUT_ENTRY: RumFirstInputTiming = { processingStart: 1100 as RelativeTime, startTime: 1000 as RelativeTime, target: document.createElement('button'), + duration: 0 as Duration, } describe('trackInitialViewTimings', () => { @@ -325,6 +326,7 @@ describe('firstInputTimings', () => { entryType: 'first-input' as const, processingStart: 900 as RelativeTime, startTime: 1000 as RelativeTime, + duration: 0 as Duration, }, ]) diff --git a/packages/rum-core/src/domain/rumEventsCollection/view/trackInteractionToNextPaint.spec.ts b/packages/rum-core/src/domain/rumEventsCollection/view/trackInteractionToNextPaint.spec.ts index 46afe230ec..6e8388b86b 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/view/trackInteractionToNextPaint.spec.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/view/trackInteractionToNextPaint.spec.ts @@ -1,4 +1,4 @@ -import type { Duration } from '@datadog/browser-core' +import type { Duration, RelativeTime } from '@datadog/browser-core' import { ExperimentalFeature, addExperimentalFeatures, @@ -7,7 +7,11 @@ import { } from '@datadog/browser-core' import type { TestSetupBuilder } from '../../../../test' import { setup } from '../../../../test' -import type { BrowserWindow, RumPerformanceEventTiming } from '../../../browser/performanceCollection' +import type { + BrowserWindow, + RumFirstInputTiming, + RumPerformanceEventTiming, +} from '../../../browser/performanceCollection' import { ViewLoadingType } from '../../../rawRumEvent.types' import type { LifeCycle } from '../../lifeCycle' import { LifeCycleEventType } from '../../lifeCycle' @@ -24,7 +28,11 @@ describe('trackInteractionToNextPaint', () => { function newInteraction( lifeCycle: LifeCycle, - { interactionId, duration = 40 as Duration, entryType = 'event' }: Partial + { + interactionId, + duration = 40 as Duration, + entryType = 'event', + }: Partial ) { if (interactionId) { interactionCountStub.incrementInteractionCount() @@ -32,6 +40,7 @@ describe('trackInteractionToNextPaint', () => { lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED, [ { entryType, + processingStart: relativeNow(), startTime: relativeNow(), duration, interactionId, @@ -92,6 +101,15 @@ describe('trackInteractionToNextPaint', () => { interactionCountStub.setInteractionCount(1 as Duration) // assumes an interaction happened but no PERFORMANCE_ENTRIES_COLLECTED have been triggered expect(getInteractionToNextPaint()).toEqual(0 as Duration) }) + + it('should take first-input entry into account', () => { + const { lifeCycle } = setupBuilder.build() + newInteraction(lifeCycle, { + interactionId: 1, + entryType: 'first-input', + }) + expect(getInteractionToNextPaint()).toEqual(40 as Duration) + }) }) describe('if feature flag disabled', () => { diff --git a/packages/rum-core/src/domain/rumEventsCollection/view/trackInteractionToNextPaint.ts b/packages/rum-core/src/domain/rumEventsCollection/view/trackInteractionToNextPaint.ts index b33b4c5ce2..8d935c1863 100644 --- a/packages/rum-core/src/domain/rumEventsCollection/view/trackInteractionToNextPaint.ts +++ b/packages/rum-core/src/domain/rumEventsCollection/view/trackInteractionToNextPaint.ts @@ -1,5 +1,6 @@ import { type Duration, noop, isExperimentalFeatureEnabled, ExperimentalFeature } from '@datadog/browser-core' -import { supportPerformanceTimingEvent, type RumPerformanceEventTiming } from '../../../browser/performanceCollection' +import { supportPerformanceTimingEvent } from '../../../browser/performanceCollection' +import type { RumFirstInputTiming, RumPerformanceEventTiming } from '../../../browser/performanceCollection' import { LifeCycleEventType, type LifeCycle } from '../../lifeCycle' import { ViewLoadingType } from '../../../rawRumEvent.types' import { getInteractionCount, initInteractionCountPolyfill } from './interactionCountPolyfill' @@ -30,7 +31,7 @@ export function trackInteractionToNextPaint(viewLoadingType: ViewLoadingType, li const { unsubscribe: stop } = lifeCycle.subscribe(LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED, (entries) => { for (const entry of entries) { - if (entry.entryType === 'event' && entry.interactionId) { + if ((entry.entryType === 'event' || entry.entryType === 'first-input') && entry.interactionId) { longestInteractions.process(entry) } } @@ -56,7 +57,7 @@ export function trackInteractionToNextPaint(viewLoadingType: ViewLoadingType, li } function trackLongestInteractions(getViewInteractionCount: () => number) { - const longestInteractions: RumPerformanceEventTiming[] = [] + const longestInteractions: Array = [] function sortAndTrimLongestInteractions() { longestInteractions.sort((a, b) => b.duration - a.duration).splice(MAX_INTERACTION_ENTRIES) @@ -68,7 +69,7 @@ function trackLongestInteractions(getViewInteractionCount: () => number) { * - if its duration is long enough, add the performance entry to the list of worst interactions * - if an entry with the same interaction id exists and its duration is lower than the new one, then replace it in the list of worst interactions */ - process(entry: RumPerformanceEventTiming) { + process(entry: RumPerformanceEventTiming | RumFirstInputTiming) { const interactionIndex = longestInteractions.findIndex( (interaction) => entry.interactionId === interaction.interactionId )