Skip to content

Commit

Permalink
✨ [RUMF-1214] implement view.frustration.count
Browse files Browse the repository at this point in the history
  • Loading branch information
BenoitZugmeyer committed Apr 29, 2022
1 parent 8d4a0f1 commit 50b1f90
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { RumEvent } from '../../../rumEvent.types'
import type { TestSetupBuilder, ViewTest } from '../../../../test/specHelper'
import { setup, setupViewTest } from '../../../../test/specHelper'
import type { RumPerformanceNavigationTiming } from '../../../browser/performanceCollection'
import { RumEventType } from '../../../rawRumEvent.types'
import { FrustrationType, RumEventType } from '../../../rawRumEvent.types'
import type { LifeCycle } from '../../lifeCycle'
import { LifeCycleEventType } from '../../lifeCycle'
import { PAGE_ACTIVITY_END_DELAY, PAGE_ACTIVITY_VALIDATION_DELAY } from '../../waitIdlePage'
Expand Down Expand Up @@ -226,14 +226,40 @@ describe('rum track view metrics', () => {
expect(getViewUpdateCount()).toEqual(1)
expect(getViewUpdate(0).eventCounts.actionCount).toEqual(0)

lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, { type: RumEventType.ACTION } as RumEvent & Context)
lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, {
type: RumEventType.ACTION,
action: { type: 'custom' },
} as RumEvent & Context)
startView()

expect(getViewUpdateCount()).toEqual(3)
expect(getViewUpdate(1).eventCounts.actionCount).toEqual(1)
expect(getViewUpdate(2).eventCounts.actionCount).toEqual(0)
})

it('should track frustration count', () => {
const { lifeCycle } = setupBuilder.build()
const { getViewUpdate, getViewUpdateCount, startView } = viewTest

expect(getViewUpdateCount()).toEqual(1)
expect(getViewUpdate(0).eventCounts.frustrationCount).toEqual(0)

lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, {
type: RumEventType.ACTION,
action: {
type: 'click',
frustration: {
type: [FrustrationType.DEAD, FrustrationType.ERROR],
},
},
} as RumEvent & Context)
startView()

expect(getViewUpdateCount()).toEqual(3)
expect(getViewUpdate(1).eventCounts.frustrationCount).toEqual(2)
expect(getViewUpdate(2).eventCounts.frustrationCount).toEqual(0)
})

it('should reset event count when the view changes', () => {
const { lifeCycle, changeLocation } = setupBuilder.build()
const { getViewUpdate, getViewUpdateCount, startView } = viewTest
Expand Down Expand Up @@ -267,6 +293,7 @@ describe('rum track view metrics', () => {
longTaskCount: 0,
resourceCount: 0,
actionCount: 0,
frustrationCount: 0,
})

lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, { type: RumEventType.RESOURCE } as RumEvent & Context)
Expand All @@ -281,6 +308,7 @@ describe('rum track view metrics', () => {
longTaskCount: 0,
resourceCount: 1,
actionCount: 0,
frustrationCount: 0,
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function trackViewMetrics(
longTaskCount: 0,
resourceCount: 0,
actionCount: 0,
frustrationCount: 0,
},
}
const { stop: stopEventCountsTracking } = trackEventCounts(lifeCycle, (newEventCounts) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const VIEW: ViewEvent = {
longTaskCount: 10,
resourceCount: 10,
actionCount: 10,
frustrationCount: 10,
},
id: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
name: undefined,
Expand Down Expand Up @@ -89,6 +90,9 @@ describe('viewCollection', () => {
action: {
count: 10,
},
frustration: {
count: 10,
},
cumulative_layout_shift: 1,
custom_timings: {
bar: (20 * 1e6) as ServerDuration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ function processViewUpdate(
action: {
count: view.eventCounts.actionCount,
},
frustration: {
count: view.eventCounts.frustrationCount,
},
cumulative_layout_shift: view.cumulativeLayoutShift,
dom_complete: toServerDuration(view.timings.domComplete),
dom_content_loaded: toServerDuration(view.timings.domContentLoaded),
Expand Down
38 changes: 26 additions & 12 deletions packages/rum-core/src/domain/trackEventCounts.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Context } from '@datadog/browser-core'
import { objectValues } from '@datadog/browser-core'
import type { RumEvent } from '../rumEvent.types'
import { RumEventType } from '../rawRumEvent.types'
import { FrustrationType, RumEventType } from '../rawRumEvent.types'
import { LifeCycle, LifeCycleEventType } from './lifeCycle'
import type { EventCounts } from './trackEventCounts'
import { trackEventCounts } from './trackEventCounts'
Expand All @@ -13,58 +13,72 @@ describe('trackEventCounts', () => {
lifeCycle = new LifeCycle()
})

function notifyCollectedRawRumEvent(type: RumEventType) {
lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, { type } as RumEvent & Context)
function notifyCollectedRawRumEvent(partialEvent: Partial<RumEvent>) {
lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, partialEvent as RumEvent & Context)
}

it('tracks errors', () => {
const { eventCounts } = trackEventCounts(lifeCycle)
notifyCollectedRawRumEvent(RumEventType.ERROR)
notifyCollectedRawRumEvent({ type: RumEventType.ERROR })
expect(eventCounts.errorCount).toBe(1)
})

it('tracks long tasks', () => {
const { eventCounts } = trackEventCounts(lifeCycle)
notifyCollectedRawRumEvent(RumEventType.LONG_TASK)
notifyCollectedRawRumEvent({ type: RumEventType.LONG_TASK })
expect(eventCounts.longTaskCount).toBe(1)
})

it("doesn't track views", () => {
const { eventCounts } = trackEventCounts(lifeCycle)
notifyCollectedRawRumEvent(RumEventType.VIEW)
notifyCollectedRawRumEvent({ type: RumEventType.VIEW })
expect(objectValues(eventCounts as unknown as { [key: string]: number }).every((value) => value === 0)).toBe(true)
})

it('tracks actions', () => {
const { eventCounts } = trackEventCounts(lifeCycle)
notifyCollectedRawRumEvent(RumEventType.ACTION)
notifyCollectedRawRumEvent({ type: RumEventType.ACTION, action: { type: 'custom' } })
expect(eventCounts.actionCount).toBe(1)
})

it('tracks resources', () => {
const { eventCounts } = trackEventCounts(lifeCycle)
notifyCollectedRawRumEvent(RumEventType.RESOURCE)
notifyCollectedRawRumEvent({ type: RumEventType.RESOURCE })
expect(eventCounts.resourceCount).toBe(1)
})

it('tracks frustration counts', () => {
const { eventCounts } = trackEventCounts(lifeCycle)
notifyCollectedRawRumEvent({
type: RumEventType.ACTION,
action: {
type: 'click',
frustration: {
type: [FrustrationType.ERROR, FrustrationType.DEAD],
},
},
})
expect(eventCounts.frustrationCount).toBe(2)
})

it('stops tracking when stop is called', () => {
const { eventCounts, stop } = trackEventCounts(lifeCycle)
notifyCollectedRawRumEvent(RumEventType.RESOURCE)
notifyCollectedRawRumEvent({ type: RumEventType.RESOURCE })
expect(eventCounts.resourceCount).toBe(1)
stop()
notifyCollectedRawRumEvent(RumEventType.RESOURCE)
notifyCollectedRawRumEvent({ type: RumEventType.RESOURCE })
expect(eventCounts.resourceCount).toBe(1)
})

it('invokes a potential callback when a count is increased', () => {
const spy = jasmine.createSpy<(eventCounts: EventCounts) => void>()
trackEventCounts(lifeCycle, spy)

notifyCollectedRawRumEvent(RumEventType.RESOURCE)
notifyCollectedRawRumEvent({ type: RumEventType.RESOURCE })
expect(spy).toHaveBeenCalledTimes(1)
expect(spy.calls.mostRecent().args[0].resourceCount).toBe(1)

notifyCollectedRawRumEvent(RumEventType.RESOURCE)
notifyCollectedRawRumEvent({ type: RumEventType.RESOURCE })
expect(spy).toHaveBeenCalledTimes(2)
expect(spy.calls.mostRecent().args[0].resourceCount).toBe(2)
})
Expand Down
9 changes: 7 additions & 2 deletions packages/rum-core/src/domain/trackEventCounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface EventCounts {
actionCount: number
longTaskCount: number
resourceCount: number
frustrationCount: number
}

export function trackEventCounts(lifeCycle: LifeCycle, callback: (eventCounts: EventCounts) => void = noop) {
Expand All @@ -16,16 +17,20 @@ export function trackEventCounts(lifeCycle: LifeCycle, callback: (eventCounts: E
longTaskCount: 0,
resourceCount: 0,
actionCount: 0,
frustrationCount: 0,
}

const subscription = lifeCycle.subscribe(LifeCycleEventType.RUM_EVENT_COLLECTED, ({ type }): void => {
switch (type) {
const subscription = lifeCycle.subscribe(LifeCycleEventType.RUM_EVENT_COLLECTED, (event): void => {
switch (event.type) {
case RumEventType.ERROR:
eventCounts.errorCount += 1
callback(eventCounts)
break
case RumEventType.ACTION:
eventCounts.actionCount += 1
if (event.action.frustration) {
eventCounts.frustrationCount += event.action.frustration.type.length
}
callback(eventCounts)
break
case RumEventType.LONG_TASK:
Expand Down
1 change: 1 addition & 0 deletions packages/rum-core/src/rawRumEvent.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export interface RawRumViewEvent {
action: Count
long_task: Count
resource: Count
frustration: Count
in_foreground_periods?: InForegroundPeriod[]
}
session: {
Expand Down
1 change: 1 addition & 0 deletions packages/rum-core/test/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export function createRawRumEvent(type: RumEventType, overrides?: Context): RawR
view: {
id: generateUUID(),
action: { count: 0 },
frustration: { count: 0 },
error: { count: 0 },
is_active: true,
loading_type: ViewLoadingType.INITIAL_LOAD,
Expand Down
8 changes: 8 additions & 0 deletions test/e2e/scenario/rum/actions.scenario.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ describe('action collection', () => {
expect(actionEvents.length).toBe(1)
expect(actionEvents[0].action.frustration!.type).toEqual(['error'])
expect(actionEvents[0].action.error!.count).toBe(1)

expect(serverEvents.rumViews[0].view.frustration!.count).toBe(1)

await withBrowserLogs((browserLogs) => {
expect(browserLogs.length).toEqual(1)
})
Expand All @@ -131,6 +134,8 @@ describe('action collection', () => {

expect(actionEvents.length).toBe(1)
expect(actionEvents[0].action.frustration!.type).toEqual(['dead'])

expect(serverEvents.rumViews[0].view.frustration!.count).toBe(1)
})

createTest('collect multiple frustrations in one action')
Expand All @@ -154,6 +159,9 @@ describe('action collection', () => {

expect(actionEvents.length).toBe(1)
expect(actionEvents[0].action.frustration!.type).toEqual(jasmine.arrayWithExactContents(['error', 'dead']))

expect(serverEvents.rumViews[0].view.frustration!.count).toBe(2)

await withBrowserLogs((browserLogs) => {
expect(browserLogs.length).toEqual(1)
})
Expand Down

0 comments on commit 50b1f90

Please sign in to comment.