diff --git a/packages/rum/src/lifeCycle.ts b/packages/rum/src/lifeCycle.ts index 6e93a86469..c7a3370d6c 100644 --- a/packages/rum/src/lifeCycle.ts +++ b/packages/rum/src/lifeCycle.ts @@ -1,10 +1,12 @@ import { ErrorMessage, RequestCompleteEvent, RequestStartEvent } from '@datadog/browser-core' import { UserAction } from './userActionCollection' +import { View } from './viewCollection' export enum LifeCycleEventType { ERROR_COLLECTED, PERFORMANCE_ENTRY_COLLECTED, USER_ACTION_COLLECTED, + VIEW_COLLECTED, REQUEST_STARTED, REQUEST_COMPLETED, SESSION_WILL_RENEW, @@ -26,6 +28,7 @@ export class LifeCycle { notify(eventType: LifeCycleEventType.REQUEST_STARTED, data: RequestStartEvent): void notify(eventType: LifeCycleEventType.REQUEST_COMPLETED, data: RequestCompleteEvent): void notify(eventType: LifeCycleEventType.USER_ACTION_COLLECTED, data: UserAction): void + notify(eventType: LifeCycleEventType.VIEW_COLLECTED, data: View): void notify( eventType: | LifeCycleEventType.SESSION_WILL_RENEW @@ -52,6 +55,7 @@ export class LifeCycle { callback: (data: RequestCompleteEvent) => void ): Subscription subscribe(eventType: LifeCycleEventType.USER_ACTION_COLLECTED, callback: (data: UserAction) => void): Subscription + subscribe(eventType: LifeCycleEventType.VIEW_COLLECTED, callback: (data: View) => void): Subscription subscribe( eventType: | LifeCycleEventType.SESSION_WILL_RENEW diff --git a/packages/rum/src/rum.entry.ts b/packages/rum/src/rum.entry.ts index 6678bb5d0e..b3db07f59b 100644 --- a/packages/rum/src/rum.entry.ts +++ b/packages/rum/src/rum.entry.ts @@ -21,6 +21,7 @@ import { startPerformanceCollection } from './performanceCollection' import { startRum } from './rum' import { startRumSession } from './rumSession' import { startUserActionCollection, UserActionReference } from './userActionCollection' +import { startViewCollection } from './viewCollection' export interface RumUserConfiguration extends UserConfiguration { applicationId: string @@ -72,6 +73,7 @@ datadogRum.init = monitor((userConfiguration: RumUserConfiguration) => { const session = startRumSession(configuration, lifeCycle) const globalApi = startRum(rumUserConfiguration.applicationId, lifeCycle, configuration, session, internalMonitoring) + startViewCollection(location, lifeCycle) const [requestStartObservable, requestCompleteObservable] = startRequestCollection() startPerformanceCollection(lifeCycle, session) startDOMMutationCollection(lifeCycle) diff --git a/packages/rum/src/rum.ts b/packages/rum/src/rum.ts index b085cca30e..43365dfb55 100644 --- a/packages/rum/src/rum.ts +++ b/packages/rum/src/rum.ts @@ -32,7 +32,7 @@ import { import { InternalContext, RumGlobal } from './rum.entry' import { RumSession } from './rumSession' import { getUserActionReference, UserActionMeasures, UserActionReference, UserActionType } from './userActionCollection' -import { trackView, viewContext, ViewMeasures } from './viewCollection' +import { viewContext, ViewMeasures } from './viewCollection' export interface PerformancePaintTiming extends PerformanceEntry { entryType: 'paint' @@ -186,7 +186,7 @@ export function startRum( () => lifeCycle.notify(LifeCycleEventType.WILL_UNLOAD) ) - trackView(window.location, lifeCycle, batch.upsertRumEvent) + trackView(lifeCycle, batch.upsertRumEvent) trackErrors(lifeCycle, batch.addRumEvent) trackRequests(configuration, lifeCycle, session, batch.addRumEvent) trackPerformanceTiming(configuration, lifeCycle, batch.addRumEvent) @@ -248,6 +248,27 @@ function startRumBatch( } } +function trackView(lifeCycle: LifeCycle, upsertRumEvent: (event: RumViewEvent, key: string) => void) { + lifeCycle.subscribe(LifeCycleEventType.VIEW_COLLECTED, (view) => { + upsertRumEvent( + { + date: getTimestamp(view.startTime), + duration: msToNs(view.duration), + evt: { + category: RumEventCategory.VIEW, + }, + rum: { + documentVersion: view.documentVersion, + }, + view: { + measures: view.measures, + }, + }, + view.id + ) + }) +} + function trackErrors(lifeCycle: LifeCycle, addRumEvent: (event: RumErrorEvent) => void) { lifeCycle.subscribe(LifeCycleEventType.ERROR_COLLECTED, ({ message, startTime, context }: ErrorMessage) => { addRumEvent({ diff --git a/packages/rum/src/trackEventCounts.ts b/packages/rum/src/trackEventCounts.ts index ecf544cf46..dc0400ba32 100644 --- a/packages/rum/src/trackEventCounts.ts +++ b/packages/rum/src/trackEventCounts.ts @@ -48,13 +48,5 @@ export function trackEventCounts(lifeCycle: LifeCycle, callback: (eventCounts: E subscriptions.forEach((s) => s.unsubscribe()) }, eventCounts, - reset() { - const eventCountsMap = eventCounts as { [key: string]: number } - for (const key in eventCountsMap) { - if (Object.prototype.hasOwnProperty.call(eventCountsMap, key)) { - eventCountsMap[key] = 0 - } - } - }, } } diff --git a/packages/rum/src/viewCollection.ts b/packages/rum/src/viewCollection.ts index 6719198e4d..be64382b29 100644 --- a/packages/rum/src/viewCollection.ts +++ b/packages/rum/src/viewCollection.ts @@ -1,9 +1,18 @@ -import { DOM_EVENT, generateUUID, getTimestamp, monitor, msToNs, throttle } from '@datadog/browser-core' +import { DOM_EVENT, generateUUID, monitor, msToNs, throttle } from '@datadog/browser-core' import { LifeCycle, LifeCycleEventType } from './lifeCycle' -import { PerformancePaintTiming, RumEvent, RumEventCategory } from './rum' +import { PerformancePaintTiming } from './rum' import { trackEventCounts } from './trackEventCounts' +export interface View { + id: string + location: Location + measures: ViewMeasures + documentVersion: number + startTime: number + duration: number +} + export interface ViewMeasures { firstContentfulPaint?: number domInteractive?: number @@ -16,156 +25,141 @@ export interface ViewMeasures { userActionCount: number } -interface ViewContext { - id: string - location: Location -} +const THROTTLE_VIEW_UPDATE_PERIOD = 3000 -export let viewContext: ViewContext +export function startViewCollection(location: Location, lifeCycle: LifeCycle) { + let currentLocation = { ...location } + let endCurrentView = newView(lifeCycle, currentLocation, 0).end -const THROTTLE_VIEW_UPDATE_PERIOD = 3000 -let startOrigin: number -let documentVersion: number -let viewMeasures: ViewMeasures - -export function trackView( - location: Location, - lifeCycle: LifeCycle, - upsertRumEvent: (event: RumEvent, key: string) => void -) { - const scheduleViewUpdate = throttle(monitor(() => updateView(upsertRumEvent)), THROTTLE_VIEW_UPDATE_PERIOD, { - leading: false, + // Renew view on history changes + trackHistory(() => { + if (areDifferentViews(currentLocation, location)) { + currentLocation = { ...location } + endCurrentView() + endCurrentView = newView(lifeCycle, currentLocation).end + } }) - const { reset: resetEventCounts } = trackEventCounts(lifeCycle, (eventCounts) => { - viewMeasures = { ...viewMeasures, ...eventCounts } - scheduleViewUpdate() + // Renew view on session changes + lifeCycle.subscribe(LifeCycleEventType.SESSION_WILL_RENEW, () => { + endCurrentView() + }) + lifeCycle.subscribe(LifeCycleEventType.SESSION_RENEWED, () => { + endCurrentView = newView(lifeCycle, currentLocation).end }) - newView(location, resetEventCounts, upsertRumEvent) - trackHistory(location, resetEventCounts, upsertRumEvent) - trackTimings(lifeCycle, scheduleViewUpdate) - trackRenewSession(location, lifeCycle, resetEventCounts, upsertRumEvent) - lifeCycle.subscribe(LifeCycleEventType.WILL_UNLOAD, () => updateView(upsertRumEvent)) + // End the current view on page unload + lifeCycle.subscribe(LifeCycleEventType.WILL_UNLOAD, () => { + endCurrentView() + }) } -function newView( - location: Location, - resetEventCounts: () => void, - upsertRumEvent: (event: RumEvent, key: string) => void -) { - startOrigin = !viewContext ? 0 : performance.now() - viewContext = { - id: generateUUID(), - location: { ...location }, - } - documentVersion = 1 - viewMeasures = { +interface ViewContext { + id: string + location: Location +} + +export let viewContext: ViewContext + +function newView(lifeCycle: LifeCycle, location: Location, startOrigin: number = performance.now()) { + // Setup initial values + const id = generateUUID() + let measures: ViewMeasures = { errorCount: 0, longTaskCount: 0, resourceCount: 0, userActionCount: 0, } - resetEventCounts() - upsertViewEvent(upsertRumEvent) -} + let documentVersion = 0 -function updateView(upsertRumEvent: (event: RumEvent, key: string) => void) { - documentVersion += 1 - upsertViewEvent(upsertRumEvent) -} + viewContext = { id, location } -function upsertViewEvent(upsertRumEvent: (event: RumEvent, key: string) => void) { - upsertRumEvent( - { - date: getTimestamp(startOrigin), - duration: msToNs(performance.now() - startOrigin), - evt: { - category: RumEventCategory.VIEW, - }, - rum: { - documentVersion, - }, - view: { - measures: viewMeasures, - }, + // Update the view every time the measures are changing + const scheduleViewUpdate = throttle(monitor(updateView), THROTTLE_VIEW_UPDATE_PERIOD, { + leading: false, + }) + function updateMeasures(newMeasures: Partial) { + measures = { ...measures, ...newMeasures } + scheduleViewUpdate() + } + const { stop: stopTimingsTracking } = trackTimings(lifeCycle, updateMeasures) + const { stop: stopEventCountsTracking } = trackEventCounts(lifeCycle, updateMeasures) + + // Initial view update + updateView() + + function updateView() { + documentVersion += 1 + lifeCycle.notify(LifeCycleEventType.VIEW_COLLECTED, { + documentVersion, + id, + location, + measures, + duration: performance.now() - startOrigin, + startTime: startOrigin, + }) + } + + return { + end() { + stopTimingsTracking() + stopEventCountsTracking() + // Make a final view update + updateView() }, - viewContext.id - ) + } } -function trackHistory( - location: Location, - resetEventCounts: () => void, - upsertRumEvent: (event: RumEvent, key: string) => void -) { +function trackHistory(onChange: () => void) { const originalPushState = history.pushState history.pushState = monitor(function(this: History['pushState']) { originalPushState.apply(this, arguments as any) - onUrlChange(location, resetEventCounts, upsertRumEvent) + onChange() }) const originalReplaceState = history.replaceState history.replaceState = monitor(function(this: History['replaceState']) { originalReplaceState.apply(this, arguments as any) - onUrlChange(location, resetEventCounts, upsertRumEvent) + onChange() }) - window.addEventListener( - DOM_EVENT.POP_STATE, - monitor(() => { - onUrlChange(location, resetEventCounts, upsertRumEvent) - }) - ) -} - -function onUrlChange( - location: Location, - resetEventCounts: () => void, - upsertRumEvent: (event: RumEvent, key: string) => void -) { - if (areDifferentViews(viewContext.location, location)) { - updateView(upsertRumEvent) - newView(location, resetEventCounts, upsertRumEvent) - } + window.addEventListener(DOM_EVENT.POP_STATE, monitor(onChange)) } function areDifferentViews(previous: Location, current: Location) { return previous.pathname !== current.pathname } -function trackTimings(lifeCycle: LifeCycle, scheduleViewUpdate: () => void) { - lifeCycle.subscribe(LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, (entry) => { - if (entry.entryType === 'navigation') { - const navigationEntry = entry as PerformanceNavigationTiming - viewMeasures = { - ...viewMeasures, - domComplete: msToNs(navigationEntry.domComplete), - domContentLoaded: msToNs(navigationEntry.domContentLoadedEventEnd), - domInteractive: msToNs(navigationEntry.domInteractive), - loadEventEnd: msToNs(navigationEntry.loadEventEnd), - } - scheduleViewUpdate() - } else if (entry.entryType === 'paint' && entry.name === 'first-contentful-paint') { - const paintEntry = entry as PerformancePaintTiming - viewMeasures = { - ...viewMeasures, - firstContentfulPaint: msToNs(paintEntry.startTime), - } - scheduleViewUpdate() - } - }) +interface Timings { + domComplete?: number + domContentLoaded?: number + domInteractive?: number + loadEventEnd?: number + firstContentfulPaint?: number } -function trackRenewSession( - location: Location, - lifeCycle: LifeCycle, - resetEventCounts: () => void, - upsertRumEvent: (event: RumEvent, key: string) => void -) { - lifeCycle.subscribe(LifeCycleEventType.SESSION_WILL_RENEW, () => { - updateView(upsertRumEvent) - }) - - lifeCycle.subscribe(LifeCycleEventType.SESSION_RENEWED, () => { - newView(location, resetEventCounts, upsertRumEvent) - }) +function trackTimings(lifeCycle: LifeCycle, callback: (timings: Timings) => void) { + let timings: Timings = {} + const { unsubscribe: stopPerformanceTracking } = lifeCycle.subscribe( + LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, + (entry) => { + if (entry.entryType === 'navigation') { + const navigationEntry = entry as PerformanceNavigationTiming + timings = { + ...timings, + domComplete: msToNs(navigationEntry.domComplete), + domContentLoaded: msToNs(navigationEntry.domContentLoadedEventEnd), + domInteractive: msToNs(navigationEntry.domInteractive), + loadEventEnd: msToNs(navigationEntry.loadEventEnd), + } + callback(timings) + } else if (entry.entryType === 'paint' && entry.name === 'first-contentful-paint') { + const paintEntry = entry as PerformancePaintTiming + timings = { + ...timings, + firstContentfulPaint: msToNs(paintEntry.startTime), + } + callback(timings) + } + } + ) + return { stop: stopPerformanceTracking } } diff --git a/packages/rum/test/rum.spec.ts b/packages/rum/test/rum.spec.ts index 1190ab63d1..c9b23bf087 100644 --- a/packages/rum/test/rum.spec.ts +++ b/packages/rum/test/rum.spec.ts @@ -16,6 +16,7 @@ import { startPerformanceCollection } from '../src/performanceCollection' import { handleResourceEntry, RumEvent, RumResourceEvent, startRum } from '../src/rum' import { RumGlobal } from '../src/rum.entry' import { UserAction, UserActionType } from '../src/userActionCollection' +import { startViewCollection } from '../src/viewCollection' interface BrowserWindow extends Window { PerformanceObserver?: PerformanceObserver @@ -257,6 +258,7 @@ describe('rum session', () => { } const lifeCycle = new LifeCycle() startRum('appId', lifeCycle, configuration as Configuration, trackedWithResourcesSession, internalMonitoring) + startViewCollection(location, lifeCycle) startPerformanceCollection(lifeCycle, trackedWithResourcesSession) server.requests = [] @@ -276,6 +278,7 @@ describe('rum session', () => { } const lifeCycle = new LifeCycle() startRum('appId', lifeCycle, configuration as Configuration, trackedWithResourcesSession, internalMonitoring) + startViewCollection(location, lifeCycle) startPerformanceCollection(lifeCycle, trackedWithResourcesSession) server.requests = [] @@ -315,6 +318,7 @@ describe('rum session', () => { } const lifeCycle = new LifeCycle() startRum('appId', lifeCycle, configuration as Configuration, session, internalMonitoring) + startViewCollection(location, lifeCycle) startPerformanceCollection(lifeCycle, session) server.requests = [] @@ -339,6 +343,7 @@ describe('rum session', () => { } const lifeCycle = new LifeCycle() startRum('appId', lifeCycle, configuration as Configuration, session, internalMonitoring) + startViewCollection(location, lifeCycle) startPerformanceCollection(lifeCycle, session) server.requests = [] @@ -364,6 +369,7 @@ describe('rum session', () => { const lifeCycle = new LifeCycle() server.requests = [] startRum('appId', lifeCycle, configuration as Configuration, session, internalMonitoring) + startViewCollection(location, lifeCycle) interface ExpectedRequestBody { evt: { @@ -421,7 +427,9 @@ describe('rum init', () => { isTrackedWithResource: () => true, } - startRum('appId', new LifeCycle(), configuration as Configuration, session, internalMonitoring) + const lifeCycle = new LifeCycle() + startRum('appId', lifeCycle, configuration as Configuration, session, internalMonitoring) + startViewCollection(location, lifeCycle) expect(server.requests.length).toBeGreaterThan(0) }) @@ -447,6 +455,7 @@ describe('rum global context', () => { server = sinon.fakeServer.create() lifeCycle = new LifeCycle() RUM = startRum('appId', lifeCycle, configuration as Configuration, session, internalMonitoring) as RumApi + startViewCollection(location, lifeCycle) server.requests = [] }) @@ -494,6 +503,7 @@ describe('rum user action', () => { server = sinon.fakeServer.create() lifeCycle = new LifeCycle() RUM = startRum('appId', lifeCycle, configuration as Configuration, session, internalMonitoring) as RumApi + startViewCollection(location, lifeCycle) server.requests = [] }) diff --git a/packages/rum/test/viewCollection.spec.ts b/packages/rum/test/viewCollection.spec.ts index d3c2556467..20e83c9b43 100644 --- a/packages/rum/test/viewCollection.spec.ts +++ b/packages/rum/test/viewCollection.spec.ts @@ -1,17 +1,11 @@ import { getHash, getPathName, getSearch } from '@datadog/browser-core' import { LifeCycle, LifeCycleEventType } from '../src/lifeCycle' -import { PerformanceLongTaskTiming, PerformancePaintTiming, RumViewEvent } from '../src/rum' +import { PerformanceLongTaskTiming, PerformancePaintTiming } from '../src/rum' import { UserAction, UserActionType } from '../src/userActionCollection' -import { trackView, viewContext } from '../src/viewCollection' - -function setup({ - addRumEvent, - lifeCycle, -}: { - addRumEvent?: () => any - lifeCycle?: LifeCycle -} = {}) { +import { startViewCollection, View, viewContext } from '../src/viewCollection' + +function setup(lifeCycle: LifeCycle = new LifeCycle()) { spyOn(history, 'pushState').and.callFake((_: any, __: string, pathname: string) => { const url = `http://localhost${pathname}` fakeLocation.pathname = getPathName(url) @@ -19,7 +13,7 @@ function setup({ fakeLocation.hash = getHash(url) }) const fakeLocation: Partial = { pathname: '/foo' } - trackView(fakeLocation as Location, lifeCycle || new LifeCycle(), addRumEvent || (() => undefined)) + startViewCollection(fakeLocation as Location, lifeCycle) } describe('rum track url change', () => { @@ -54,17 +48,52 @@ describe('rum track url change', () => { }) }) +function spyOnViews() { + let addRumEvent: jasmine.Spy + let lifeCycle: LifeCycle + + function getViewEvent(index: number) { + return addRumEvent.calls.argsFor(index)[0] as View + } + + function getRumEventCount() { + return addRumEvent.calls.count() + } + + addRumEvent = jasmine.createSpy() + lifeCycle = new LifeCycle() + lifeCycle.subscribe(LifeCycleEventType.VIEW_COLLECTED, addRumEvent) + setup(lifeCycle) + + return { lifeCycle, getViewEvent, getRumEventCount } +} + describe('rum track renew session', () => { + let lifeCycle: LifeCycle + let getRumEventCount: () => number + let getViewEvent: (index: number) => View + beforeEach(() => { + ;({ lifeCycle, getRumEventCount, getViewEvent } = spyOnViews()) + }) + it('should update page view id on renew session', () => { - const lifeCycle = new LifeCycle() - setup({ - lifeCycle, - }) const initialView = viewContext.id lifeCycle.notify(LifeCycleEventType.SESSION_RENEWED) expect(viewContext.id).not.toEqual(initialView) }) + + it('should send a final view event when the session is renewed', () => { + expect(getRumEventCount()).toEqual(1) + + lifeCycle.notify(LifeCycleEventType.SESSION_WILL_RENEW) + expect(getViewEvent(0).id).toBe(getViewEvent(1).id) + expect(getRumEventCount()).toEqual(2) + + lifeCycle.notify(LifeCycleEventType.SESSION_RENEWED) + expect(getViewEvent(0).id).not.toBe(getViewEvent(2).id) + expect(getRumEventCount()).toEqual(3) + }) }) describe('rum view measures', () => { @@ -91,110 +120,85 @@ describe('rum view measures', () => { entryType: 'navigation', loadEventEnd: 567, } - let addRumEvent: jasmine.Spy - - function getViewEvent(index: number) { - return addRumEvent.calls.argsFor(index)[0] as RumViewEvent - } - - function getRumEventCount() { - return addRumEvent.calls.count() - } - + let lifeCycle: LifeCycle + let getRumEventCount: () => number + let getViewEvent: (index: number) => View beforeEach(() => { - addRumEvent = jasmine.createSpy() + ;({ lifeCycle, getRumEventCount, getViewEvent } = spyOnViews()) }) it('should track error count', () => { - const lifeCycle = new LifeCycle() - setup({ addRumEvent, lifeCycle }) - expect(getRumEventCount()).toEqual(1) - expect(getViewEvent(0).view.measures.errorCount).toEqual(0) + expect(getViewEvent(0).measures.errorCount).toEqual(0) lifeCycle.notify(LifeCycleEventType.ERROR_COLLECTED, {} as any) lifeCycle.notify(LifeCycleEventType.ERROR_COLLECTED, {} as any) history.pushState({}, '', '/bar') expect(getRumEventCount()).toEqual(3) - expect(getViewEvent(1).view.measures.errorCount).toEqual(2) - expect(getViewEvent(2).view.measures.errorCount).toEqual(0) + expect(getViewEvent(1).measures.errorCount).toEqual(2) + expect(getViewEvent(2).measures.errorCount).toEqual(0) }) it('should track long task count', () => { - const lifeCycle = new LifeCycle() - setup({ addRumEvent, lifeCycle }) - expect(getRumEventCount()).toEqual(1) - expect(getViewEvent(0).view.measures.longTaskCount).toEqual(0) + expect(getViewEvent(0).measures.longTaskCount).toEqual(0) lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, FAKE_LONG_TASK as PerformanceLongTaskTiming) history.pushState({}, '', '/bar') expect(getRumEventCount()).toEqual(3) - expect(getViewEvent(1).view.measures.longTaskCount).toEqual(1) - expect(getViewEvent(2).view.measures.longTaskCount).toEqual(0) + expect(getViewEvent(1).measures.longTaskCount).toEqual(1) + expect(getViewEvent(2).measures.longTaskCount).toEqual(0) }) it('should track resource count', () => { - const lifeCycle = new LifeCycle() - setup({ addRumEvent, lifeCycle }) - expect(getRumEventCount()).toEqual(1) - expect(getViewEvent(0).view.measures.resourceCount).toEqual(0) + expect(getViewEvent(0).measures.resourceCount).toEqual(0) lifeCycle.notify(LifeCycleEventType.RESOURCE_ADDED_TO_BATCH) history.pushState({}, '', '/bar') expect(getRumEventCount()).toEqual(3) - expect(getViewEvent(1).view.measures.resourceCount).toEqual(1) - expect(getViewEvent(2).view.measures.resourceCount).toEqual(0) + expect(getViewEvent(1).measures.resourceCount).toEqual(1) + expect(getViewEvent(2).measures.resourceCount).toEqual(0) }) it('should track user action count', () => { - const lifeCycle = new LifeCycle() - setup({ addRumEvent, lifeCycle }) - expect(getRumEventCount()).toEqual(1) - expect(getViewEvent(0).view.measures.userActionCount).toEqual(0) + expect(getViewEvent(0).measures.userActionCount).toEqual(0) lifeCycle.notify(LifeCycleEventType.USER_ACTION_COLLECTED, FAKE_USER_ACTION as UserAction) history.pushState({}, '', '/bar') expect(getRumEventCount()).toEqual(3) - expect(getViewEvent(1).view.measures.userActionCount).toEqual(1) - expect(getViewEvent(2).view.measures.userActionCount).toEqual(0) + expect(getViewEvent(1).measures.userActionCount).toEqual(1) + expect(getViewEvent(2).measures.userActionCount).toEqual(0) }) it('should reset event count when the view changes', () => { - const lifeCycle = new LifeCycle() - setup({ addRumEvent, lifeCycle }) - expect(getRumEventCount()).toEqual(1) - expect(getViewEvent(0).view.measures.resourceCount).toEqual(0) + expect(getViewEvent(0).measures.resourceCount).toEqual(0) lifeCycle.notify(LifeCycleEventType.RESOURCE_ADDED_TO_BATCH) history.pushState({}, '', '/bar') expect(getRumEventCount()).toEqual(3) - expect(getViewEvent(1).view.measures.resourceCount).toEqual(1) - expect(getViewEvent(2).view.measures.resourceCount).toEqual(0) + expect(getViewEvent(1).measures.resourceCount).toEqual(1) + expect(getViewEvent(2).measures.resourceCount).toEqual(0) lifeCycle.notify(LifeCycleEventType.RESOURCE_ADDED_TO_BATCH) lifeCycle.notify(LifeCycleEventType.RESOURCE_ADDED_TO_BATCH) history.pushState({}, '', '/baz') expect(getRumEventCount()).toEqual(5) - expect(getViewEvent(3).view.measures.resourceCount).toEqual(2) - expect(getViewEvent(4).view.measures.resourceCount).toEqual(0) + expect(getViewEvent(3).measures.resourceCount).toEqual(2) + expect(getViewEvent(4).measures.resourceCount).toEqual(0) }) it('should track performance timings', () => { - const lifeCycle = new LifeCycle() - setup({ addRumEvent, lifeCycle }) - expect(getRumEventCount()).toEqual(1) - expect(getViewEvent(0).view.measures).toEqual({ + expect(getViewEvent(0).measures).toEqual({ errorCount: 0, longTaskCount: 0, resourceCount: 0, @@ -209,7 +213,7 @@ describe('rum view measures', () => { history.pushState({}, '', '/bar') expect(getRumEventCount()).toEqual(3) - expect(getViewEvent(1).view.measures).toEqual({ + expect(getViewEvent(1).measures).toEqual({ domComplete: 456e6, domContentLoaded: 345e6, domInteractive: 234e6, @@ -220,7 +224,7 @@ describe('rum view measures', () => { resourceCount: 0, userActionCount: 0, }) - expect(getViewEvent(2).view.measures).toEqual({ + expect(getViewEvent(2).measures).toEqual({ errorCount: 0, longTaskCount: 0, resourceCount: 0,