Skip to content

Commit

Permalink
✨ [RUMF-783] collect view.first_input_time (#676)
Browse files Browse the repository at this point in the history
* 🐛 generate types in rum-core

* ⬆️  sync rumEvent.types

* ✨ collect view.first_input_time
  • Loading branch information
bcaudan authored Jan 8, 2021
1 parent 8a55636 commit 75f59f9
Show file tree
Hide file tree
Showing 8 changed files with 39 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { resetFirstHidden } from './trackFirstHidden'
import {
Timings,
trackFirstContentfulPaint,
trackFirstInputDelay,
trackFirstInputTimings,
trackLargestContentfulPaint,
trackNavigationTimings,
trackTimings,
Expand Down Expand Up @@ -71,6 +71,7 @@ describe('trackTimings', () => {
domInteractive: 234,
firstContentfulPaint: 123,
firstInputDelay: 100,
firstInputTime: 1000,
loadEvent: 567,
})
})
Expand Down Expand Up @@ -188,14 +189,16 @@ describe('largestContentfulPaint', () => {
})
})

describe('firstInputDelay', () => {
describe('firstInputTimings', () => {
let setupBuilder: TestSetupBuilder
let fidCallback: jasmine.Spy<(value: number) => void>
let fitCallback: jasmine.Spy<
({ firstInputDelay, firstInputTime }: { firstInputDelay: number; firstInputTime: number }) => void
>

beforeEach(() => {
fidCallback = jasmine.createSpy()
fitCallback = jasmine.createSpy()
setupBuilder = setup().beforeBuild(({ lifeCycle }) => {
return trackFirstInputDelay(lifeCycle, fidCallback)
return trackFirstInputTimings(lifeCycle, fitCallback)
})
resetFirstHidden()
})
Expand All @@ -206,12 +209,12 @@ describe('firstInputDelay', () => {
resetFirstHidden()
})

it('should provide the first input delay', () => {
it('should provide the first input timings', () => {
const { lifeCycle } = setupBuilder.build()

lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, FAKE_FIRST_INPUT_ENTRY)
expect(fidCallback).toHaveBeenCalledTimes(1)
expect(fidCallback).toHaveBeenCalledWith(100)
expect(fitCallback).toHaveBeenCalledTimes(1)
expect(fitCallback).toHaveBeenCalledWith({ firstInputDelay: 100, firstInputTime: 1000 })
})

it('should not be present if the page is hidden', () => {
Expand All @@ -220,6 +223,6 @@ describe('firstInputDelay', () => {

lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, FAKE_FIRST_INPUT_ENTRY)

expect(fidCallback).not.toHaveBeenCalled()
expect(fitCallback).not.toHaveBeenCalled()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface Timings {
loadEvent?: number
largestContentfulPaint?: number
firstInputDelay?: number
firstInputTime?: number
}

export function trackTimings(lifeCycle: LifeCycle, callback: (timings: Timings) => void) {
Expand All @@ -28,9 +29,10 @@ export function trackTimings(lifeCycle: LifeCycle, callback: (timings: Timings)
largestContentfulPaint,
})
})
const { stop: stopFIDTracking } = trackFirstInputDelay(lifeCycle, (firstInputDelay) => {
const { stop: stopFIDTracking } = trackFirstInputTimings(lifeCycle, ({ firstInputDelay, firstInputTime }) => {
setTimings({
firstInputDelay,
firstInputTime,
})
})

Expand Down Expand Up @@ -74,7 +76,7 @@ export function trackFirstContentfulPaint(lifeCycle: LifeCycle, callback: (fcp:
}

/**
* Track the largest contentful paint (LCP) occuring during the initial View. This can yield
* Track the largest contentful paint (LCP) occurring during the initial View. This can yield
* multiple values, only the most recent one should be used.
* Documentation: https://web.dev/lcp/
* Reference implementation: https://github.com/GoogleChrome/web-vitals/blob/master/src/getLCP.ts
Expand Down Expand Up @@ -121,17 +123,25 @@ export function trackLargestContentfulPaint(
}

/**
* Track the first input delay (FID) occuring during the initial View. This yields at most one
* value.
* Track the first input occurring during the initial View to return:
* - First Input Delay
* - First Input Time
* Callback is called at most one time.
* Documentation: https://web.dev/fid/
* Reference implementation: https://github.com/GoogleChrome/web-vitals/blob/master/src/getFID.ts
*/
export function trackFirstInputDelay(lifeCycle: LifeCycle, callback: (value: number) => void) {
export function trackFirstInputTimings(
lifeCycle: LifeCycle,
callback: ({ firstInputDelay, firstInputTime }: { firstInputDelay: number; firstInputTime: number }) => void
) {
const firstHidden = trackFirstHidden()

const { unsubscribe: stop } = lifeCycle.subscribe(LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, (entry) => {
if (entry.entryType === 'first-input' && entry.startTime < firstHidden.timeStamp) {
callback(entry.processingStart - entry.startTime)
callback({
firstInputDelay: entry.processingStart - entry.startTime,
firstInputTime: entry.startTime,
})
}
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ describe('viewCollection', () => {
domInteractive: 10,
firstContentfulPaint: 10,
firstInputDelay: 12,
firstInputTime: 10,
largestContentfulPaint: 10,
loadEvent: 10,
},
Expand All @@ -72,6 +73,7 @@ describe('viewCollection', () => {
},
firstContentfulPaint: 10 * 1e6,
firstInputDelay: 12 * 1e6,
firstInputTime: 10 * 1e6,
isActive: false,
largestContentfulPaint: 10 * 1e6,
loadEvent: 10 * 1e6,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ function processViewUpdate(view: View) {
},
firstContentfulPaint: msToNs(view.timings.firstContentfulPaint),
firstInputDelay: msToNs(view.timings.firstInputDelay),
firstInputTime: msToNs(view.timings.firstInputTime),
isActive: view.isActive,
largestContentfulPaint: msToNs(view.timings.largestContentfulPaint),
loadEvent: msToNs(view.timings.loadEvent),
Expand Down
3 changes: 2 additions & 1 deletion packages/rum-core/src/rawRumEvent.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Context, ContextValue, ErrorSource, ResourceType } from '@datadog/browser-core'
import { Context, ErrorSource, ResourceType } from '@datadog/browser-core'
import { ActionType } from './domain/rumEventsCollection/action/trackActions'
import { PerformanceResourceDetailsElement } from './domain/rumEventsCollection/resource/resourceUtils'
import { ViewLoadingType } from './domain/rumEventsCollection/view/trackViews'
Expand Down Expand Up @@ -58,6 +58,7 @@ export interface RawRumViewEvent {
loadingType: ViewLoadingType
firstContentfulPaint?: number
firstInputDelay?: number
firstInputTime?: number
cumulativeLayoutShift?: number
largestContentfulPaint?: number
domInteractive?: number
Expand Down
4 changes: 4 additions & 0 deletions packages/rum-core/src/rumEvent.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,10 @@ export type RumViewEvent = CommonProperties & {
* Duration in ns of the first input event delay
*/
readonly first_input_delay?: number
/**
* Duration in ns to the first input
*/
readonly first_input_time?: number
/**
* Total layout shift score that occured on the view
*/
Expand Down
2 changes: 1 addition & 1 deletion rum-events-format
2 changes: 1 addition & 1 deletion scripts/generate-schema-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const prettier = require('prettier')

const workingDirectory = path.join(__dirname, '../rum-events-format')
const schemaPath = path.join(workingDirectory, 'rum-events-format.json')
const compiledTypesPath = path.join(__dirname, '../packages/rum/src/rumEvent.types.ts')
const compiledTypesPath = path.join(__dirname, '../packages/rum-core/src/rumEvent.types.ts')
const prettierConfigPath = path.join(__dirname, '../.prettierrc.yml')

async function main() {
Expand Down

0 comments on commit 75f59f9

Please sign in to comment.