Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ [RUM-1039] Harmonize view tests #2430

Merged
merged 11 commits into from
Sep 20, 2023
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { RelativeTime, Duration } from '@datadog/browser-core'
import { addDuration } from '@datadog/browser-core'
import { addDuration, clocksNow } from '@datadog/browser-core'
import { ViewLoadingType } from '../../../rawRumEvent.types'
import type { TestSetupBuilder } from '../../../../test'
import { createPerformanceEntry, setup } from '../../../../test'
import { RumPerformanceEntryType } from '../../../browser/performanceCollection'
import { LifeCycleEventType } from '../../lifeCycle'
import { PAGE_ACTIVITY_END_DELAY, PAGE_ACTIVITY_VALIDATION_DELAY } from '../../waitPageActivityEnd'
import { THROTTLE_VIEW_UPDATE_PERIOD } from '../trackViews'
import type { ViewTest } from '../setupViewTest.specHelper'
import { setupViewTest } from '../setupViewTest.specHelper'
import { RumPerformanceEntryType } from '../../../browser/performanceCollection'
import { LifeCycleEventType } from '../../lifeCycle'
import { trackLoadingTime } from './trackLoadingTime'

const BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY = (PAGE_ACTIVITY_VALIDATION_DELAY * 0.8) as Duration

Expand All @@ -19,14 +19,25 @@ const LOAD_EVENT_AFTER_ACTIVITY_TIMING = (BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY

describe('trackLoadingTime', () => {
let setupBuilder: TestSetupBuilder
let viewTest: ViewTest
let loadingTimeCallback: jasmine.Spy<(loadingTime: Duration) => void>
let loadType: ViewLoadingType
let setLoadEvent: (loadEvent: Duration) => void

beforeEach(() => {
loadType = ViewLoadingType.ROUTE_CHANGE
loadingTimeCallback = jasmine.createSpy('loadingTimeCallback')
setupBuilder = setup()
.withFakeLocation('/foo')
.beforeBuild((buildContext) => {
viewTest = setupViewTest(buildContext)
return viewTest
.beforeBuild(({ lifeCycle, domMutationObservable, configuration }) => {
const loadingTimeTracking = trackLoadingTime(
lifeCycle,
domMutationObservable,
configuration,
loadType,
clocksNow(),
loadingTimeCallback
)
setLoadEvent = loadingTimeTracking.setLoadEvent
return loadingTimeTracking
})
.withFakeClock()
})
Expand All @@ -36,114 +47,85 @@ describe('trackLoadingTime', () => {
})

it('should have an undefined loading time if there is no activity on a route change', () => {
const { clock } = setupBuilder.build()
const { getViewUpdate, getViewUpdateCount, startView } = viewTest

startView()
clock.tick(THROTTLE_VIEW_UPDATE_PERIOD)
setupBuilder.build()

expect(getViewUpdateCount()).toEqual(3)
expect(getViewUpdate(2).commonViewMetrics.loadingTime).toBeUndefined()
expect(loadingTimeCallback).not.toHaveBeenCalled()
})

it('should have a loading time equal to the activity time if there is a unique activity on a route change', () => {
const { domMutationObservable, clock } = setupBuilder.build()
const { getViewUpdate, startView } = viewTest

startView()
clock.tick(BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY)
domMutationObservable.notify()
clock.tick(AFTER_PAGE_ACTIVITY_END_DELAY)
clock.tick(THROTTLE_VIEW_UPDATE_PERIOD)

expect(getViewUpdate(3).commonViewMetrics.loadingTime).toEqual(BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY)
expect(loadingTimeCallback).toHaveBeenCalledTimes(1)
expect(loadingTimeCallback).toHaveBeenCalledWith(BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY)
amortemousque marked this conversation as resolved.
Show resolved Hide resolved
})

it('should use loadEventEnd for initial view when having no activity', () => {
loadType = ViewLoadingType.INITIAL_LOAD
const { lifeCycle, clock } = setupBuilder.build()
const { getViewUpdate, getViewUpdateCount } = viewTest

expect(getViewUpdateCount()).toEqual(1)
const entry = createPerformanceEntry(RumPerformanceEntryType.NAVIGATION)
lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED, [entry])
amortemousque marked this conversation as resolved.
Show resolved Hide resolved
clock.tick(THROTTLE_VIEW_UPDATE_PERIOD)

expect(getViewUpdateCount()).toEqual(2)
expect(getViewUpdate(1).commonViewMetrics.loadingTime).toEqual(entry.loadEventEnd)
setLoadEvent(entry.loadEventEnd)
clock.tick(PAGE_ACTIVITY_END_DELAY)

expect(loadingTimeCallback).toHaveBeenCalledTimes(1)
expect(loadingTimeCallback).toHaveBeenCalledWith(entry.loadEventEnd)
})

it('should use loadEventEnd for initial view when load event is bigger than computed loading time', () => {
const { lifeCycle, domMutationObservable, clock } = setupBuilder.build()
const { getViewUpdate, getViewUpdateCount } = viewTest

expect(getViewUpdateCount()).toEqual(1)
loadType = ViewLoadingType.INITIAL_LOAD
const { domMutationObservable, clock } = setupBuilder.build()

clock.tick(BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY)

lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED, [
createPerformanceEntry(RumPerformanceEntryType.NAVIGATION, {
loadEventEnd: LOAD_EVENT_AFTER_ACTIVITY_TIMING,
}),
])

setLoadEvent(LOAD_EVENT_AFTER_ACTIVITY_TIMING)
domMutationObservable.notify()
clock.tick(AFTER_PAGE_ACTIVITY_END_DELAY)

clock.tick(THROTTLE_VIEW_UPDATE_PERIOD)

expect(getViewUpdateCount()).toEqual(2)
expect(getViewUpdate(1).commonViewMetrics.loadingTime).toEqual(LOAD_EVENT_AFTER_ACTIVITY_TIMING)
expect(loadingTimeCallback).toHaveBeenCalledTimes(1)
expect(loadingTimeCallback).toHaveBeenCalledWith(LOAD_EVENT_AFTER_ACTIVITY_TIMING)
})

it('should use computed loading time for initial view when load event is smaller than computed loading time', () => {
const { lifeCycle, domMutationObservable, clock } = setupBuilder.build()
const { getViewUpdate, getViewUpdateCount } = viewTest

expect(getViewUpdateCount()).toEqual(1)
loadType = ViewLoadingType.INITIAL_LOAD
const { domMutationObservable, clock } = setupBuilder.build()

clock.tick(BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY)
lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED, [
createPerformanceEntry(RumPerformanceEntryType.NAVIGATION, {
loadEventEnd: LOAD_EVENT_BEFORE_ACTIVITY_TIMING,
}),
])

setLoadEvent(LOAD_EVENT_BEFORE_ACTIVITY_TIMING)

domMutationObservable.notify()
clock.tick(AFTER_PAGE_ACTIVITY_END_DELAY)
clock.tick(THROTTLE_VIEW_UPDATE_PERIOD)

expect(getViewUpdateCount()).toEqual(2)
expect(getViewUpdate(1).commonViewMetrics.loadingTime).toEqual(BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY)
expect(loadingTimeCallback).toHaveBeenCalledTimes(1)
expect(loadingTimeCallback).toHaveBeenCalledWith(BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY)
})

it('should use computed loading time from time origin for initial view', () => {
loadType = ViewLoadingType.INITIAL_LOAD
const { domMutationObservable, clock } = setupBuilder.build()

// introduce a gap between time origin and tracking start
// ensure that `load event > activity delay` and `load event < activity delay + clock gap`
// to make the test fail if the clock gap is not correctly taken into account
const CLOCK_GAP = (LOAD_EVENT_AFTER_ACTIVITY_TIMING - BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY + 1) as Duration

setupBuilder.clock!.tick(CLOCK_GAP)

const { lifeCycle, domMutationObservable, clock } = setupBuilder.build()
const { getViewUpdate, getViewUpdateCount } = viewTest

expect(getViewUpdateCount()).toEqual(1)
clock.tick(CLOCK_GAP)
amortemousque marked this conversation as resolved.
Show resolved Hide resolved

clock.tick(BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY)

lifeCycle.notify(LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED, [
createPerformanceEntry(RumPerformanceEntryType.NAVIGATION, {
loadEventEnd: LOAD_EVENT_BEFORE_ACTIVITY_TIMING,
}),
])
setLoadEvent(LOAD_EVENT_AFTER_ACTIVITY_TIMING)

domMutationObservable.notify()
clock.tick(AFTER_PAGE_ACTIVITY_END_DELAY)

clock.tick(THROTTLE_VIEW_UPDATE_PERIOD)
amortemousque marked this conversation as resolved.
Show resolved Hide resolved

expect(getViewUpdateCount()).toEqual(2)
expect(getViewUpdate(1).commonViewMetrics.loadingTime).toEqual(
addDuration(BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY, CLOCK_GAP)
)
expect(loadingTimeCallback).toHaveBeenCalledTimes(1)
expect(loadingTimeCallback).toHaveBeenCalledWith(addDuration(BEFORE_PAGE_ACTIVITY_VALIDATION_DELAY, CLOCK_GAP))
})
})