From 10d0cee112fba179701beb374d5b69223315194c Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Fri, 10 Jul 2020 13:27:27 +0200 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20keep=20context=20history=20(beh?= =?UTF-8?q?ind=20flag)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum/src/parentContexts.ts | 96 ++++++++++--- packages/rum/src/rum.ts | 7 +- packages/rum/test/parentContexts.spec.ts | 172 ++++++++++++++++++++++- packages/rum/test/specHelper.ts | 6 +- test/static/bundle-e2e-page.html | 2 +- 5 files changed, 259 insertions(+), 24 deletions(-) diff --git a/packages/rum/src/parentContexts.ts b/packages/rum/src/parentContexts.ts index daed161fc5..5d7bae974a 100644 --- a/packages/rum/src/parentContexts.ts +++ b/packages/rum/src/parentContexts.ts @@ -1,6 +1,7 @@ import { Context } from '@datadog/browser-core' import { LifeCycle, LifeCycleEventType } from './lifeCycle' import { RumSession } from './rumSession' +import { AutoUserAction } from './userActionCollection' export interface ViewContext extends Context { sessionId: string | undefined @@ -16,31 +17,59 @@ export interface ActionContext extends Context { } } -interface InternalContext { +export interface ParentContexts { + findAction: (startTime?: number) => ActionContext | undefined + findView: (startTime?: number) => ViewContext | undefined +} + +interface CurrentContext { id: string startTime: number } -export interface ParentContexts { - findAction: (startTime?: number) => ActionContext | undefined - findView: (startTime?: number) => ViewContext | undefined +interface PreviousContext { + startTime: number + endTime: number + context: T } -export function startParentContexts(location: Location, lifeCycle: LifeCycle, session: RumSession): ParentContexts { - let currentView: InternalContext | undefined - let currentAction: InternalContext | undefined +export function startParentContexts( + location: Location, + lifeCycle: LifeCycle, + session: RumSession, + withContextHistory: boolean +): ParentContexts { + let currentView: CurrentContext | undefined + let currentAction: CurrentContext | undefined let currentSessionId: string | undefined - lifeCycle.subscribe(LifeCycleEventType.VIEW_CREATED, (internalContext) => { - currentView = internalContext + const previousViews: Array> = [] + const previousActions: Array> = [] + + lifeCycle.subscribe(LifeCycleEventType.VIEW_CREATED, (currentContext) => { + if (currentView && withContextHistory) { + previousViews.unshift({ + context: buildCurrentViewContext(), + endTime: currentContext.startTime, + startTime: currentView.startTime, + }) + } + currentView = currentContext currentSessionId = session.getId() }) - lifeCycle.subscribe(LifeCycleEventType.AUTO_ACTION_CREATED, (internalContext) => { - currentAction = internalContext + lifeCycle.subscribe(LifeCycleEventType.AUTO_ACTION_CREATED, (currentContext) => { + currentAction = currentContext }) - lifeCycle.subscribe(LifeCycleEventType.AUTO_ACTION_COMPLETED, () => { + lifeCycle.subscribe(LifeCycleEventType.AUTO_ACTION_COMPLETED, (userAction: AutoUserAction) => { + if (currentAction && withContextHistory) { + previousActions.unshift({ + context: buildCurrentActionContext(), + endTime: currentAction.startTime + userAction.duration, + startTime: currentAction.startTime, + }) + } currentAction = undefined }) @@ -48,13 +77,46 @@ export function startParentContexts(location: Location, lifeCycle: LifeCycle, se currentAction = undefined }) + function buildCurrentViewContext() { + return { sessionId: currentSessionId, view: { id: currentView!.id, url: location.href } } + } + + function buildCurrentActionContext() { + return { userAction: { id: currentAction!.id } } + } + + function findContext( + buildContext: () => T, + previousContexts: Array>, + currentContext?: CurrentContext, + startTime?: number + ) { + if (!startTime) { + return currentContext ? buildContext() : undefined + } + if (currentContext && startTime >= currentContext.startTime) { + return buildContext() + } + if (!withContextHistory) { + return undefined + } + for (const previousContext of previousContexts) { + if (startTime > previousContext.endTime) { + break + } + if (startTime >= previousContext.startTime) { + return previousContext.context + } + } + return undefined + } + return { findAction: (startTime) => { - if (!currentAction || (startTime !== undefined && startTime < currentAction.startTime)) { - return undefined - } - return { userAction: { id: currentAction.id } } + return findContext(buildCurrentActionContext, previousActions, currentAction, startTime) + }, + findView: (startTime) => { + return findContext(buildCurrentViewContext, previousViews, currentView, startTime) }, - findView: () => currentView && { sessionId: currentSessionId, view: { id: currentView.id, url: location.href } }, } } diff --git a/packages/rum/src/rum.ts b/packages/rum/src/rum.ts index 31fb80923f..3412915bea 100644 --- a/packages/rum/src/rum.ts +++ b/packages/rum/src/rum.ts @@ -170,7 +170,12 @@ export function startRum( ): Omit { let globalContext: Context = {} - const parentContexts = startParentContexts(window.location, lifeCycle, session) + const parentContexts = startParentContexts( + window.location, + lifeCycle, + session, + configuration.isEnabled('context-history') + ) internalMonitoring.setExternalContextProvider(() => { return deepMerge( diff --git a/packages/rum/test/parentContexts.spec.ts b/packages/rum/test/parentContexts.spec.ts index 2c93d13d8f..18de52ca19 100644 --- a/packages/rum/test/parentContexts.spec.ts +++ b/packages/rum/test/parentContexts.spec.ts @@ -1,7 +1,13 @@ import { LifeCycleEventType } from '../src/lifeCycle' +import { AutoUserAction } from '../src/userActionCollection' import { setup, TestSetupBuilder } from './specHelper' -describe('parentContexts', () => { +function stubActionWithDuration(duration: number): AutoUserAction { + const action: Partial = { duration } + return action as AutoUserAction +} + +describe('parentContexts (only current)', () => { const FAKE_ID = 'fake' const startTime = 10 @@ -24,7 +30,7 @@ describe('parentContexts', () => { isTracked: () => true, isTrackedWithResource: () => true, }) - .withParentContexts() + .withParentContexts(false) }) describe('findView', () => { @@ -121,3 +127,165 @@ describe('parentContexts', () => { }) }) }) + +describe('parentContexts (with context history)', () => { + const FAKE_ID = 'fake' + const startTime = 10 + + let fakeUrl: string + let sessionId: string + let setupBuilder: TestSetupBuilder + + beforeEach(() => { + fakeUrl = 'fake-url' + sessionId = 'fake-session' + const fakeLocation = { + get href() { + return fakeUrl + }, + } + setupBuilder = setup() + .withFakeLocation(fakeLocation) + .withSession({ + getId: () => sessionId, + isTracked: () => true, + isTrackedWithResource: () => true, + }) + .withParentContexts(true) + }) + + describe('findView', () => { + it('should return undefined when there is no current view and no startTime', () => { + const { parentContexts } = setupBuilder.build() + + expect(parentContexts.findView()).toBeUndefined() + }) + + it('should return the current view context when there is no start time', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime, id: FAKE_ID }) + + expect(parentContexts.findView()).toBeDefined() + expect(parentContexts.findView()!.view.id).toEqual(FAKE_ID) + }) + + it('should return the view context corresponding to startTime', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime: 10, id: 'view 1' }) + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime: 20, id: 'view 2' }) + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime: 30, id: 'view 3' }) + + expect(parentContexts.findView(15)!.view.id).toEqual('view 1') + expect(parentContexts.findView(20)!.view.id).toEqual('view 2') + expect(parentContexts.findView(40)!.view.id).toEqual('view 3') + }) + + it('should return undefined when no view context corresponding to startTime', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime: 10, id: 'view 1' }) + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime: 20, id: 'view 2' }) + + expect(parentContexts.findView(5)).not.toBeDefined() + }) + + it('should replace the current view context on VIEW_CREATED', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime, id: FAKE_ID }) + const newViewId = 'fake 2' + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime, id: newViewId }) + + expect(parentContexts.findView()!.view.id).toEqual(newViewId) + }) + + it('should return the current url with the current view', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime, id: FAKE_ID }) + expect(parentContexts.findView()!.view.url).toBe('fake-url') + + fakeUrl = 'other-url' + + expect(parentContexts.findView()!.view.url).toBe('other-url') + }) + + it('should update session id only on VIEW_CREATED', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime, id: FAKE_ID }) + expect(parentContexts.findView()!.sessionId).toBe('fake-session') + + sessionId = 'other-session' + expect(parentContexts.findView()!.sessionId).toBe('fake-session') + + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime, id: 'fake 2' }) + expect(parentContexts.findView()!.sessionId).toBe('other-session') + }) + }) + + describe('findAction', () => { + it('should return undefined when there is no current action and no startTime', () => { + const { parentContexts } = setupBuilder.build() + + expect(parentContexts.findAction()).toBeUndefined() + }) + + it('should return the current action context when no startTime', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime, id: FAKE_ID }) + + expect(parentContexts.findAction()).toBeDefined() + expect(parentContexts.findAction()!.userAction.id).toBe(FAKE_ID) + }) + + it('should return the action context corresponding to startTime', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime: 10, id: 'action 1' }) + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_COMPLETED, stubActionWithDuration(10)) + + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime: 30, id: 'action 2' }) + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_COMPLETED, stubActionWithDuration(10)) + + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime: 50, id: 'action 3' }) + + expect(parentContexts.findAction(15)!.userAction.id).toBe('action 1') + expect(parentContexts.findAction(20)!.userAction.id).toBe('action 1') + expect(parentContexts.findAction(30)!.userAction.id).toBe('action 2') + expect(parentContexts.findAction(55)!.userAction.id).toBe('action 3') + }) + + it('should return undefined if no action context corresponding to startTime', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime: 10, id: 'action 1' }) + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_DISCARDED) + + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime: 20, id: 'action 2' }) + + expect(parentContexts.findAction(10)).toBeUndefined() + }) + + it('should clear the current action on ACTION_DISCARDED', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime, id: FAKE_ID }) + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_DISCARDED) + + expect(parentContexts.findAction()).toBeUndefined() + }) + + it('should clear the current action on ACTION_COMPLETED', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime, id: FAKE_ID }) + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_COMPLETED, stubActionWithDuration(10)) + + expect(parentContexts.findAction()).toBeUndefined() + }) + }) +}) diff --git a/packages/rum/test/specHelper.ts b/packages/rum/test/specHelper.ts index fec70a7389..ca848ff970 100644 --- a/packages/rum/test/specHelper.ts +++ b/packages/rum/test/specHelper.ts @@ -39,7 +39,7 @@ export interface TestSetupBuilder { withViewCollection: () => TestSetupBuilder withUserActionCollection: () => TestSetupBuilder withPerformanceCollection: () => TestSetupBuilder - withParentContexts: () => TestSetupBuilder + withParentContexts: (withContextHistory: boolean) => TestSetupBuilder withFakeClock: () => TestSetupBuilder withFakeServer: () => TestSetupBuilder withPerformanceObserverStubBuilder: () => TestSetupBuilder @@ -117,9 +117,9 @@ export function setup(): TestSetupBuilder { buildTasks.push(() => startPerformanceCollection(lifeCycle, session)) return setupBuilder }, - withParentContexts() { + withParentContexts(withContextHistory: boolean) { buildTasks.push(() => { - parentContexts = startParentContexts(fakeLocation as Location, lifeCycle, session) + parentContexts = startParentContexts(fakeLocation as Location, lifeCycle, session, withContextHistory) }) return setupBuilder }, diff --git a/test/static/bundle-e2e-page.html b/test/static/bundle-e2e-page.html index 08daf8c91a..6b4d42c2f4 100644 --- a/test/static/bundle-e2e-page.html +++ b/test/static/bundle-e2e-page.html @@ -29,7 +29,7 @@ internalMonitoringEndpoint: `${intakeOrigin}/monitoring?${specIdParam}`, logsEndpoint: `${intakeOrigin}/logs?${specIdParam}`, rumEndpoint: `${intakeOrigin}/rum?${specIdParam}`, - enableExperimentalFeatures: [], + enableExperimentalFeatures: ['context-history'], trackInteractions: true, }) From 8da470c0a0985eba162d52a156881d1cdfe23361 Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Fri, 10 Jul 2020 14:50:02 +0200 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20cleanup=20context=20history?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/index.ts | 1 + packages/rum/src/parentContexts.ts | 46 +++++++++++++++---- packages/rum/test/parentContexts.spec.ts | 57 ++++++++++++++++++++++++ packages/rum/test/specHelper.ts | 3 ++ 4 files changed, 99 insertions(+), 8 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 253b782578..ad0efbe690 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -22,6 +22,7 @@ export { } from './requestCollection' export { startSessionManagement, + SESSION_TIME_OUT_DELAY, // Exposed for tests SESSION_COOKIE_NAME, stopSessionManagement, diff --git a/packages/rum/src/parentContexts.ts b/packages/rum/src/parentContexts.ts index 5d7bae974a..ece9ab7662 100644 --- a/packages/rum/src/parentContexts.ts +++ b/packages/rum/src/parentContexts.ts @@ -1,8 +1,12 @@ -import { Context } from '@datadog/browser-core' +import { Context, monitor, ONE_MINUTE, SESSION_TIME_OUT_DELAY } from '@datadog/browser-core' import { LifeCycle, LifeCycleEventType } from './lifeCycle' import { RumSession } from './rumSession' import { AutoUserAction } from './userActionCollection' +export const VIEW_CONTEXT_TIME_OUT_DELAY = SESSION_TIME_OUT_DELAY +export const ACTION_CONTEXT_TIME_OUT_DELAY = 5 * ONE_MINUTE // arbitrary +export const CLEAR_OLD_CONTEXTS_INTERVAL = ONE_MINUTE + export interface ViewContext extends Context { sessionId: string | undefined view: { @@ -17,11 +21,6 @@ export interface ActionContext extends Context { } } -export interface ParentContexts { - findAction: (startTime?: number) => ActionContext | undefined - findView: (startTime?: number) => ViewContext | undefined -} - interface CurrentContext { id: string startTime: number @@ -33,6 +32,12 @@ interface PreviousContext { context: T } +export interface ParentContexts { + findAction: (startTime?: number) => ActionContext | undefined + findView: (startTime?: number) => ViewContext | undefined + stop: () => void +} + export function startParentContexts( location: Location, lifeCycle: LifeCycle, @@ -43,8 +48,8 @@ export function startParentContexts( let currentAction: CurrentContext | undefined let currentSessionId: string | undefined - const previousViews: Array> = [] - const previousActions: Array> = [] + let previousViews: Array> = [] + let previousActions: Array> = [] lifeCycle.subscribe(LifeCycleEventType.VIEW_CREATED, (currentContext) => { if (currentView && withContextHistory) { @@ -77,6 +82,28 @@ export function startParentContexts( currentAction = undefined }) + lifeCycle.subscribe(LifeCycleEventType.SESSION_RENEWED, () => { + previousViews = [] + previousActions = [] + currentView = undefined + currentAction = undefined + }) + + const clearOldContextsInterval = window.setInterval( + monitor(() => { + clearOldContexts(previousViews, VIEW_CONTEXT_TIME_OUT_DELAY) + clearOldContexts(previousActions, ACTION_CONTEXT_TIME_OUT_DELAY) + }), + CLEAR_OLD_CONTEXTS_INTERVAL + ) + + function clearOldContexts(previousContexts: Array>, timeOutDelay: number) { + const oldTimeThreshold = performance.now() - timeOutDelay + while (previousContexts.length > 0 && previousContexts[previousContexts.length - 1].startTime < oldTimeThreshold) { + previousContexts.pop() + } + } + function buildCurrentViewContext() { return { sessionId: currentSessionId, view: { id: currentView!.id, url: location.href } } } @@ -118,5 +145,8 @@ export function startParentContexts( findView: (startTime) => { return findContext(buildCurrentViewContext, previousViews, currentView, startTime) }, + stop: () => { + window.clearInterval(clearOldContextsInterval) + }, } } diff --git a/packages/rum/test/parentContexts.spec.ts b/packages/rum/test/parentContexts.spec.ts index 18de52ca19..85e6b8d9aa 100644 --- a/packages/rum/test/parentContexts.spec.ts +++ b/packages/rum/test/parentContexts.spec.ts @@ -1,4 +1,9 @@ import { LifeCycleEventType } from '../src/lifeCycle' +import { + ACTION_CONTEXT_TIME_OUT_DELAY, + CLEAR_OLD_CONTEXTS_INTERVAL, + VIEW_CONTEXT_TIME_OUT_DELAY, +} from '../src/parentContexts' import { AutoUserAction } from '../src/userActionCollection' import { setup, TestSetupBuilder } from './specHelper' @@ -154,6 +159,10 @@ describe('parentContexts (with context history)', () => { .withParentContexts(true) }) + afterEach(() => { + setupBuilder.cleanup() + }) + describe('findView', () => { it('should return undefined when there is no current view and no startTime', () => { const { parentContexts } = setupBuilder.build() @@ -288,4 +297,52 @@ describe('parentContexts (with context history)', () => { expect(parentContexts.findAction()).toBeUndefined() }) }) + + describe('history contexts', () => { + it('should be cleared on SESSION_RENEWED', () => { + const { lifeCycle, parentContexts } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime: 10, id: 'view 1' }) + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime: 20, id: 'view 2' }) + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime: 10, id: 'action 1' }) + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_COMPLETED, stubActionWithDuration(10)) + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime: 20, id: 'action 2' }) + + expect(parentContexts.findView(15)).toBeDefined() + expect(parentContexts.findAction(15)).toBeDefined() + expect(parentContexts.findView(25)).toBeDefined() + expect(parentContexts.findAction(25)).toBeDefined() + + lifeCycle.notify(LifeCycleEventType.SESSION_RENEWED) + + expect(parentContexts.findView(15)).toBeUndefined() + expect(parentContexts.findAction(15)).toBeUndefined() + expect(parentContexts.findView(25)).toBeUndefined() + expect(parentContexts.findAction(25)).toBeUndefined() + }) + + it('should be cleared when too old', () => { + const { lifeCycle, parentContexts, clock } = setupBuilder.withFakeClock().build() + + const originalTime = performance.now() + const targetTime = originalTime + 5 + + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime: originalTime, id: 'view 1' }) + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime: originalTime, id: 'action 1' }) + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_COMPLETED, stubActionWithDuration(10)) + lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, { startTime: originalTime + 10, id: 'view 2' }) + + clock.tick(10) + expect(parentContexts.findView(targetTime)).toBeDefined() + expect(parentContexts.findAction(targetTime)).toBeDefined() + + clock.tick(ACTION_CONTEXT_TIME_OUT_DELAY + CLEAR_OLD_CONTEXTS_INTERVAL) + expect(parentContexts.findView(targetTime)).toBeDefined() + expect(parentContexts.findAction(targetTime)).toBeUndefined() + + clock.tick(VIEW_CONTEXT_TIME_OUT_DELAY + CLEAR_OLD_CONTEXTS_INTERVAL) + expect(parentContexts.findView(targetTime)).toBeUndefined() + expect(parentContexts.findAction(targetTime)).toBeUndefined() + }) + }) }) diff --git a/packages/rum/test/specHelper.ts b/packages/rum/test/specHelper.ts index ca848ff970..a068dfee91 100644 --- a/packages/rum/test/specHelper.ts +++ b/packages/rum/test/specHelper.ts @@ -120,6 +120,9 @@ export function setup(): TestSetupBuilder { withParentContexts(withContextHistory: boolean) { buildTasks.push(() => { parentContexts = startParentContexts(fakeLocation as Location, lifeCycle, session, withContextHistory) + cleanupTasks.push(() => { + parentContexts.stop() + }) }) return setupBuilder }, From 13098db1f1c6eec149973ad69c486ea43c11d1d3 Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Fri, 10 Jul 2020 15:20:50 +0200 Subject: [PATCH 3/4] add extra tests on internal context --- packages/rum/src/rum.entry.ts | 2 +- packages/rum/test/rum.spec.ts | 52 ++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/packages/rum/src/rum.entry.ts b/packages/rum/src/rum.entry.ts index 035648e864..70629444d0 100644 --- a/packages/rum/src/rum.entry.ts +++ b/packages/rum/src/rum.entry.ts @@ -51,7 +51,7 @@ const STUBBED_RUM = { addUserAction(name: string, context: Context) { makeStub('addUserAction') }, - getInternalContext(): InternalContext | undefined { + getInternalContext(startTime?: number): InternalContext | undefined { makeStub('getInternalContext') return undefined }, diff --git a/packages/rum/test/rum.spec.ts b/packages/rum/test/rum.spec.ts index 81788da8b3..d16c8651ce 100644 --- a/packages/rum/test/rum.spec.ts +++ b/packages/rum/test/rum.spec.ts @@ -10,7 +10,7 @@ import sinon from 'sinon' import { LifeCycle, LifeCycleEventType } from '../src/lifeCycle' import { handleResourceEntry, RawRumEvent, RumEvent, RumResourceEvent } from '../src/rum' -import { CustomUserAction, UserActionType } from '../src/userActionCollection' +import { AutoUserAction, CustomUserAction, UserActionType } from '../src/userActionCollection' import { SESSION_KEEP_ALIVE_INTERVAL, THROTTLE_VIEW_UPDATE_PERIOD } from '../src/viewCollection' import { setup, TestSetupBuilder } from './specHelper' @@ -583,3 +583,53 @@ describe('rum context', () => { expect(subsequentRequests[0].date).toEqual(initialViewDate) }) }) + +describe('rum internal context', () => { + let setupBuilder: TestSetupBuilder + + beforeEach(() => { + setupBuilder = setup().withRum() + }) + + afterEach(() => { + setupBuilder.cleanup() + }) + + it('should return current internal context', () => { + const { rumApi, lifeCycle } = setupBuilder.build() + + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime: 10, id: 'fake' }) + + expect(rumApi.getInternalContext()).toEqual({ + application_id: 'appId', + session_id: '1234', + user_action: { + id: 'fake', + }, + view: { + id: jasmine.any(String), + url: window.location.href, + }, + }) + }) + + it('should return internal context corresponding to startTime', () => { + const { rumApi, lifeCycle } = setupBuilder.build() + + const stubUserAction: Partial = { duration: 10 } + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, { startTime: 10, id: 'fake' }) + lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_COMPLETED, stubUserAction as AutoUserAction) + + expect(rumApi.getInternalContext(15)).toEqual({ + application_id: 'appId', + session_id: '1234', + user_action: { + id: 'fake', + }, + view: { + id: jasmine.any(String), + url: window.location.href, + }, + }) + }) +}) From 4d712d9cb8f4e04c6ab47e28fd382129b78e024f Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Fri, 10 Jul 2020 15:22:27 +0200 Subject: [PATCH 4/4] remove unneeded UserAction type --- packages/rum/src/userActionCollection.ts | 2 -- packages/rum/test/userActionCollection.spec.ts | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/rum/src/userActionCollection.ts b/packages/rum/src/userActionCollection.ts index a1d09cfdae..9c59b0df5d 100644 --- a/packages/rum/src/userActionCollection.ts +++ b/packages/rum/src/userActionCollection.ts @@ -32,8 +32,6 @@ export interface AutoUserAction { measures: UserActionMeasures } -export type UserAction = CustomUserAction | AutoUserAction - export function startUserActionCollection(lifeCycle: LifeCycle) { const userAction = startUserActionManagement(lifeCycle) diff --git a/packages/rum/test/userActionCollection.spec.ts b/packages/rum/test/userActionCollection.spec.ts index 3c093ae25a..3788ffc46d 100644 --- a/packages/rum/test/userActionCollection.spec.ts +++ b/packages/rum/test/userActionCollection.spec.ts @@ -1,7 +1,7 @@ import { DOM_EVENT, ErrorMessage } from '@datadog/browser-core' import { LifeCycle, LifeCycleEventType } from '../src/lifeCycle' import { PAGE_ACTIVITY_MAX_DURATION, PAGE_ACTIVITY_VALIDATION_DELAY } from '../src/trackPageActivities' -import { AutoUserAction, UserAction, UserActionType } from '../src/userActionCollection' +import { AutoUserAction, UserActionType } from '../src/userActionCollection' import { setup, TestSetupBuilder } from './specHelper' // Used to wait some time after the creation of a user action @@ -126,7 +126,7 @@ describe('startUserActionCollection', () => { describe('newUserAction', () => { let setupBuilder: TestSetupBuilder - const { events, pushEvent } = eventsCollector() + const { events, pushEvent } = eventsCollector() function newClick(name: string) { const button = document.createElement('button')