Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
2f11566
add s/s action
BeltranBulbarellaDD Dec 17, 2025
065a6fe
Add unit tests for startAction and stopAction
BeltranBulbarellaDD Dec 17, 2025
aec81c6
Linting fixes
BeltranBulbarellaDD Dec 17, 2025
270c0cb
cleanup
BeltranBulbarellaDD Dec 17, 2025
b0bc642
fix leaked listener
BeltranBulbarellaDD Dec 17, 2025
a06410a
Integrated trackEventCounts and integrate with startActionCollection.
BeltranBulbarellaDD Dec 30, 2025
2080477
Merge branch 'main' into beltran.bulbarella/start-stopAction
BeltranBulbarellaDD Dec 30, 2025
b4af4f6
merged rum-events-format
BeltranBulbarellaDD Dec 30, 2025
3e88bd7
Fix format
BeltranBulbarellaDD Dec 30, 2025
f129855
sync schemas
BeltranBulbarellaDD Dec 30, 2025
2259509
fix comma
BeltranBulbarellaDD Dec 30, 2025
446390e
Add telemetry type (remove after merge in R-E-F)
BeltranBulbarellaDD Dec 30, 2025
efe5ffc
Add loading_time to custom actions
BeltranBulbarellaDD Dec 31, 2025
2c95977
wait for fetch to complete in e2e test so to avoid flaky test
BeltranBulbarellaDD Dec 31, 2025
546fdde
revert test
BeltranBulbarellaDD Dec 31, 2025
d7a7e14
wait for requests to complete in e2e test so to avoid flaky test
BeltranBulbarellaDD Dec 31, 2025
e8725ba
Update activeCustomActions to track existing actions
BeltranBulbarellaDD Jan 5, 2026
9864c12
Clean up activeCustomActions on session renewal
BeltranBulbarellaDD Jan 5, 2026
a19c4d0
Stop active actions on session renewal, and reset the action context.
BeltranBulbarellaDD Jan 5, 2026
5b40d1e
Clean up active custom actions on stop()
BeltranBulbarellaDD Jan 5, 2026
b513153
Create trackAction to reuse in actionCollection and trackClickActions.
BeltranBulbarellaDD Jan 7, 2026
f13b695
Fix linter
BeltranBulbarellaDD Jan 7, 2026
969a859
Fix eslint config and linter
BeltranBulbarellaDD Jan 7, 2026
f8c506e
Prevent collision in getActionLookupKey
BeltranBulbarellaDD Jan 7, 2026
c3ddeeb
Remove unneded test, update getActionLookupKey test
BeltranBulbarellaDD Jan 7, 2026
e1f4512
Remove telemetry events
BeltranBulbarellaDD Jan 7, 2026
fda20bb
remove space
BeltranBulbarellaDD Jan 7, 2026
80850cf
Return an array of action IDs in findActionId
BeltranBulbarellaDD Jan 12, 2026
573cb0a
Merge branch 'main' into beltran.bulbarella/start-stopAction
BeltranBulbarellaDD Jan 13, 2026
ee43b93
run format
BeltranBulbarellaDD Jan 13, 2026
7946916
support start time for pre init action tracking
BeltranBulbarellaDD Jan 13, 2026
4227e82
Merge branch 'main' into beltran.bulbarella/start-stopAction
BeltranBulbarellaDD Jan 13, 2026
1b690e5
modify to test that the clocks are captured at call time
BeltranBulbarellaDD Jan 13, 2026
3e0bbd9
Remove actionKey, make experimental docs, pass clocks to actionCollec…
BeltranBulbarellaDD Jan 14, 2026
40e8331
Created trackCustomActions, revamp processAction, edit clickAction.
BeltranBulbarellaDD Jan 14, 2026
8e6763f
remove actionKey, run formatter.
BeltranBulbarellaDD Jan 14, 2026
feead72
Linting fixes
BeltranBulbarellaDD Jan 14, 2026
d6a11c0
Linting fixes.
BeltranBulbarellaDD Jan 14, 2026
02fa207
Re add actionKey, created TrackedActionMetadata.
BeltranBulbarellaDD Jan 14, 2026
c28599f
Fix typecheck
BeltranBulbarellaDD Jan 14, 2026
20019e3
Change getActionLookupKey
BeltranBulbarellaDD Jan 14, 2026
0dc0e6a
TEST REDUCE BUNDLE SIZE
BeltranBulbarellaDD Jan 14, 2026
53b70d9
SECOND TEST REDUCE BUNDLE SIZE
BeltranBulbarellaDD Jan 14, 2026
66ce586
Add back action types
BeltranBulbarellaDD Jan 14, 2026
c0a3fd5
Fix const names, removed InstantCustomAction, remove TrackedActionMet…
BeltranBulbarellaDD Jan 15, 2026
06d7565
Linter
BeltranBulbarellaDD Jan 15, 2026
d299a36
Merge branch 'main' into beltran.bulbarella/start-stopAction
BeltranBulbarellaDD Jan 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eslint.config.mjs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because the command yarn lint was linting also playwright files.

Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default tseslint.config(
'coverage',
'rum-events-format',
'.yarn',
'playwright-report',
'**/playwright-report',
'docs',
'developer-extension/.wxt',
'developer-extension/.output',
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/tools/experimentalFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export enum ExperimentalFeature {
FEATURE_OPERATION_VITAL = 'feature_operation_vital',
SHORT_SESSION_INVESTIGATION = 'short_session_investigation',
AVOID_FETCH_KEEPALIVE = 'avoid_fetch_keepalive',
START_STOP_ACTION = 'start_stop_action',
USE_CHANGE_RECORDS = 'use_change_records',
SOURCE_CODE_CONTEXT = 'source_code_context',
}
Expand Down
41 changes: 39 additions & 2 deletions packages/rum-core/src/boot/preStartRum.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
DefaultPrivacyLevel,
resetExperimentalFeatures,
resetFetchObservable,
ExperimentalFeature,
} from '@datadog/browser-core'
import type { Clock } from '@datadog/browser-core/test'
import {
Expand All @@ -18,13 +19,14 @@ import {
mockClock,
mockEventBridge,
mockSyntheticsWorkerValues,
mockExperimentalFeatures,
} from '@datadog/browser-core/test'
import type { HybridInitConfiguration, RumConfiguration, RumInitConfiguration } from '../domain/configuration'
import type { ViewOptions } from '../domain/view/trackViews'
import { ActionType, VitalType } from '../rawRumEvent.types'
import type { CustomAction } from '../domain/action/actionCollection'
import type { RumPlugin } from '../domain/plugins'
import { createCustomVitalsState } from '../domain/vital/vitalCollection'
import type { CustomAction } from '../domain/action/trackCustomActions'
import type { RumPublicApi, Strategy } from './rumPublicApi'
import type { StartRumResult } from './startRum'
import { createPreStartStrategy } from './preStartRum'
Expand Down Expand Up @@ -630,7 +632,7 @@ describe('preStartRum', () => {
const addActionSpy = jasmine.createSpy()
doStartRumSpy.and.returnValue({ addAction: addActionSpy } as unknown as StartRumResult)

const customAction: CustomAction = {
const customAction: Omit<CustomAction, 'id' | 'duration' | 'counts'> = {
name: 'foo',
type: ActionType.CUSTOM,
startClocks: clocksNow(),
Expand Down Expand Up @@ -753,6 +755,41 @@ describe('preStartRum', () => {
strategy.init(DEFAULT_INIT_CONFIGURATION, PUBLIC_API)
expect(addOperationStepVitalSpy).toHaveBeenCalledOnceWith('foo', 'start', undefined, undefined)
})

it('startAction / stopAction', () => {
mockExperimentalFeatures([ExperimentalFeature.START_STOP_ACTION])

const startActionSpy = jasmine.createSpy()
const stopActionSpy = jasmine.createSpy()
doStartRumSpy.and.returnValue({
startAction: startActionSpy,
stopAction: stopActionSpy,
} as unknown as StartRumResult)

strategy.startAction('user_login', { type: ActionType.CUSTOM })
strategy.stopAction('user_login')

strategy.init(DEFAULT_INIT_CONFIGURATION, PUBLIC_API)

expect(startActionSpy).toHaveBeenCalledWith(
'user_login',
jasmine.objectContaining({
type: ActionType.CUSTOM,
}),
jasmine.objectContaining({
relative: jasmine.any(Number),
timeStamp: jasmine.any(Number),
})
)
expect(stopActionSpy).toHaveBeenCalledWith(
'user_login',
undefined,
jasmine.objectContaining({
relative: jasmine.any(Number),
timeStamp: jasmine.any(Number),
})
)
})
})

describe('tracking consent', () => {
Expand Down
10 changes: 10 additions & 0 deletions packages/rum-core/src/boot/preStartRum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,16 @@ export function createPreStartStrategy(
bufferApiCalls.add((startRumResult) => startRumResult.addAction(action))
},

startAction(name, options) {
const startClocks = clocksNow()
bufferApiCalls.add((startRumResult) => startRumResult.startAction(name, options, startClocks))
},

stopAction(name, options) {
const stopClocks = clocksNow()
bufferApiCalls.add((startRumResult) => startRumResult.stopAction(name, options, stopClocks))
},

addError(providedError) {
bufferApiCalls.add((startRumResult) => startRumResult.addError(providedError))
},
Expand Down
68 changes: 68 additions & 0 deletions packages/rum-core/src/boot/rumPublicApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const noopStartRum = (): ReturnType<StartRum> => ({
hooks: {} as any,
telemetry: {} as any,
addOperationStepVital: () => undefined,
startAction: () => undefined,
stopAction: () => undefined,
})
const DEFAULT_INIT_CONFIGURATION = { applicationId: 'xxx', clientToken: 'xxx' }
const FAKE_WORKER = {} as DeflateWorker
Expand Down Expand Up @@ -756,6 +758,72 @@ describe('rum public api', () => {
})
})

describe('startAction / stopAction', () => {
it('should call startAction and stopAction on the strategy', () => {
const startActionSpy = jasmine.createSpy()
const stopActionSpy = jasmine.createSpy()
const rumPublicApi = makeRumPublicApi(
() => ({
...noopStartRum(),
startAction: startActionSpy,
stopAction: stopActionSpy,
}),
noopRecorderApi,
noopProfilerApi
)

rumPublicApi.init(DEFAULT_INIT_CONFIGURATION)
rumPublicApi.startAction('purchase', {
type: ActionType.CUSTOM,
context: { cart: 'abc' },
})
rumPublicApi.stopAction('purchase', {
context: { total: 100 },
})

expect(startActionSpy).toHaveBeenCalledWith(
'purchase',
jasmine.objectContaining({
type: ActionType.CUSTOM,
context: { cart: 'abc' },
})
)
expect(stopActionSpy).toHaveBeenCalledWith(
'purchase',
jasmine.objectContaining({
context: { total: 100 },
})
)
})

it('should sanitize startAction and stopAction inputs', () => {
const startActionSpy = jasmine.createSpy()
const rumPublicApi = makeRumPublicApi(
() => ({
...noopStartRum(),
startAction: startActionSpy,
}),
noopRecorderApi,
noopProfilerApi
)

rumPublicApi.init(DEFAULT_INIT_CONFIGURATION)
rumPublicApi.startAction('action_name', {
type: ActionType.CUSTOM,
context: { count: 123, nested: { foo: 'bar' } } as any,
actionKey: 'action_key',
})

expect(startActionSpy.calls.argsFor(0)[1]).toEqual(
jasmine.objectContaining({
type: ActionType.CUSTOM,
context: { count: 123, nested: { foo: 'bar' } },
actionKey: 'action_key',
})
)
})
})

describe('addDurationVital', () => {
it('should call addDurationVital on the startRum result', () => {
const addDurationVitalSpy = jasmine.createSpy()
Expand Down
39 changes: 39 additions & 0 deletions packages/rum-core/src/boot/rumPublicApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { callPluginsMethod } from '../domain/plugins'
import type { Hooks } from '../domain/hooks'
import type { SdkName } from '../domain/contexts/defaultContext'
import type { LongTaskContexts } from '../domain/longTask/longTaskCollection'
import type { ActionOptions } from '../domain/action/trackCustomActions'
import { createPreStartStrategy } from './preStartRum'
import type { StartRum, StartRumResult } from './startRum'

Expand Down Expand Up @@ -168,6 +169,24 @@ export interface RumPublicApi extends PublicApi {
*/
addAction: (name: string, context?: object) => void

/**
* [Experimental] start a custom action, stored in `@action`
*
* @category Data Collection
* @param name - Name of the action
* @param options - Options of the action
*/
startAction: (name: string, options?: ActionOptions) => void

/**
* [Experimental] stop a custom action, stored in `@action`
*
* @category Data Collection
* @param name - Name of the action
* @param options - Options of the action
*/
stopAction: (name: string, options?: ActionOptions) => void

/**
* Add a custom error, stored in `@error`.
*
Expand Down Expand Up @@ -523,6 +542,8 @@ export interface Strategy {
accountContext: ContextManager

addAction: StartRumResult['addAction']
startAction: StartRumResult['startAction']
stopAction: StartRumResult['stopAction']
addError: StartRumResult['addError']
addFeatureFlagEvaluation: StartRumResult['addFeatureFlagEvaluation']
startDurationVital: StartRumResult['startDurationVital']
Expand Down Expand Up @@ -653,6 +674,24 @@ export function makeRumPublicApi(
})
},

startAction: monitor((name, options) => {
// addTelemetryUsage({ feature: 'start-action' })
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be removed when DataDog/rum-events-format#335

strategy.startAction(sanitize(name)!, {
type: sanitize(options && options.type) as ActionType | undefined,
context: sanitize(options && options.context) as Context,
actionKey: options && options.actionKey,
})
}),

stopAction: monitor((name, options) => {
// addTelemetryUsage({ feature: 'stop-action' })
strategy.stopAction(sanitize(name)!, {
type: sanitize(options && options.type) as ActionType | undefined,
context: sanitize(options && options.context) as Context,
actionKey: options && options.actionKey,
})
}),

addError: (error, context) => {
const handlingStack = createHandlingStack('error')
callMonitored(() => {
Expand Down
2 changes: 2 additions & 0 deletions packages/rum-core/src/boot/startRum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ export function startRumEventCollection(

return {
addAction: actionCollection.addAction,
startAction: actionCollection.startAction,
stopAction: actionCollection.stopAction,
addEvent: eventCollection.addEvent,
addError,
addTiming,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { LifeCycle, LifeCycleEventType } from '../lifeCycle'
import type { AssembleHookParams, DefaultTelemetryEventAttributes, Hooks } from '../hooks'
import { createHooks } from '../hooks'
import type { RumMutationRecord } from '../../browser/domMutationObservable'
import type { ActionContexts } from './actionCollection'
import { LONG_TASK_START_TIME_CORRECTION, startActionCollection } from './actionCollection'
import { ActionNameSource } from './actionNameConstants'
import type { ActionContexts } from './trackAction'

describe('actionCollection', () => {
const lifeCycle = new LifeCycle()
Expand Down
Loading