Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ yarn test:unit
# Run specific test file
yarn test:unit --spec packages/core/src/path/to/feature.spec.ts

# Run tests on a specific seed
yarn test:unit --seed 123

# setup E2E tests (installs Playwright and builds test apps)
yarn test:e2e:init

Expand Down
2 changes: 2 additions & 0 deletions LICENSE-3rdparty.csv
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dev,@types/node-forge,MIT,Copyright Microsoft Corporation
dev,@types/pako,MIT,Copyright Microsoft Corporation
dev,@types/react,MIT,Copyright Microsoft Corporation
dev,@types/react-dom,MIT,Copyright Microsoft Corporation
dev,@types/ws,MIT,Copyright Microsoft Corporation
dev,@wxt-dev/module-react,MIT,Copyright (c) 2023 Aaron
dev,@vitejs/plugin-react,MIT,Copyright (c) 2019-present Evan You & Vite Contributors
dev,ajv,MIT,Copyright 2015-2017 Evgeny Poberezkin
Expand Down Expand Up @@ -76,4 +77,5 @@ dev,vite,MIT,Copyright (c) 2019-present, VoidZero Inc. and Vite contributors
dev,webpack,MIT,Copyright JS Foundation and other contributors
dev,webpack-cli,MIT,Copyright JS Foundation and other contributors
dev,webpack-dev-middleware,MIT,Copyright JS Foundation and other contributors
dev,ws,MIT,Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com> Copyright (c) 2013 Arnout Kazemier and contributors Copyright (c) 2016 Luigi Pinca and contributors
dev,wxt,MIT,Copyright (c) 2023 Aaron
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export default tseslint.config(
'./test/e2e/tsconfig.json',
'./test/performance/tsconfig.json',
'./test/apps/**/tsconfig.json',
'./test/unit/vite-experiment/tsconfig.json',
],
sourceType: 'module',

Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"version": "scripts/cli version",
"test": "yarn test:unit:watch",
"test:unit": "karma start ./test/unit/karma.local.conf.js",
"test:unit:patou": "node ./test/unit/vite-experiment/main.ts --headless",
"test:script": "node --test --experimental-test-module-mocks './scripts/**/*.spec.*'",
"test:unit:watch": "yarn test:unit --no-single-run",
"test:unit:bs": "node ./scripts/test/bs-wrapper.ts karma start test/unit/karma.bs.conf.js",
Expand Down Expand Up @@ -56,6 +57,7 @@
"@types/jasmine": "3.10.19",
"@types/node": "25.0.10",
"@types/node-forge": "1.3.14",
"@types/ws": "8.18.1",
"ajv": "8.17.1",
"browserstack-local": "1.5.8",
"chrome-webstore-upload": "4.0.3",
Expand Down Expand Up @@ -97,9 +99,11 @@
"typescript": "5.9.3",
"typescript-eslint": "8.53.1",
"undici": "7.19.1",
"vite": "7.3.1",
"webpack": "5.104.1",
"webpack-cli": "6.0.1",
"webpack-dev-middleware": "7.4.5"
"webpack-dev-middleware": "7.4.5",
"ws": "8.19.0"
},
"resolutions": {
"puppeteer-core@npm:21.11.0/ws": "8.17.1"
Expand Down
6 changes: 1 addition & 5 deletions packages/core/src/browser/cookie.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { mockCookies } from '../../test'
import { getCurrentSite, resetGetCurrentSite } from './cookie'
import { getCurrentSite } from './cookie'

describe('cookie', () => {
describe('getCurrentSite', () => {
beforeEach(() => {
resetGetCurrentSite()
})

it('returns the eTLD+1 for example.com', () => {
mockCookies()
expect(getCurrentSite('example.com')).toBe('example.com')
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/browser/xhrObservable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,12 @@ function abortXhr({ target: xhr }: InstrumentedMethodCall<XMLHttpRequest, 'abort
context.isAborted = true
}
}

/**
* Reset the XHR observable global state. This is useful for testing to ensure clean state between tests.
*
* @internal
*/
export function resetXhrObservable() {
xhrObservable = undefined
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,28 +93,31 @@ describe('session in cookie strategy', () => {
{
initConfiguration: { clientToken: 'abc' },
cookieOptions: {},
cookieString: /^dd_cookie_test_[\w-]+=[^;]*;expires=[^;]+;path=\/;samesite=strict$/,
cookieString: /^dd_[\w_-]+=[^;]*;expires=[^;]+;path=\/;samesite=strict$/,
description: 'should set samesite to strict by default',
},
{
initConfiguration: { clientToken: 'abc', useSecureSessionCookie: true },
cookieOptions: { secure: true },
cookieString: /^dd_cookie_test_[\w-]+=[^;]*;expires=[^;]+;path=\/;samesite=strict;secure$/,
cookieString: /^dd_[\w_-]+=[^;]*;expires=[^;]+;path=\/;samesite=strict;secure$/,
description: 'should add secure attribute when defined',
},
{
initConfiguration: { clientToken: 'abc', trackSessionAcrossSubdomains: true },
cookieOptions: { domain: 'foo.bar' },
cookieString: new RegExp(
`^dd_cookie_test_[\\w-]+=[^;]*;expires=[^;]+;path=\\/;samesite=strict;domain=${getCurrentSite()}$`
`^dd_[\\w_-]+=[^;]*;expires=[^;]+;path=\\/;samesite=strict;domain=${getCurrentSite()}$`
),
description: 'should set cookie domain when tracking accross subdomains',
},
].forEach(({ description, initConfiguration, cookieString }) => {
it(description, () => {
const cookieSetSpy = spyOnProperty(document, 'cookie', 'set')
selectCookieStrategy(initConfiguration)
expect(cookieSetSpy.calls.argsFor(0)[0]).toMatch(cookieString)
expect(cookieSetSpy).toHaveBeenCalled()
for (const call of cookieSetSpy.calls.all()) {
expect(call.args[0]).toMatch(cookieString)
}
})
})
})
Expand Down
5 changes: 0 additions & 5 deletions packages/core/src/domain/telemetry/telemetry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import type { StackTrace } from '../../tools/stackTrace/computeStackTrace'
import { HookNames } from '../../tools/abstractHooks'
import {
addTelemetryError,
resetTelemetry,
scrubCustomerFrames,
formatError,
addTelemetryConfiguration,
Expand Down Expand Up @@ -71,10 +70,6 @@ function startAndSpyTelemetry(
}

describe('telemetry', () => {
afterEach(() => {
resetTelemetry()
})

it('collects "monitor" errors', async () => {
const { getTelemetryEvents } = startAndSpyTelemetry()
callMonitored(() => {
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/domain/telemetry/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { buildTags } from '../tags'
import { INTAKE_SITE_STAGING, INTAKE_SITE_US1_FED } from '../intakeSites'
import { BufferedObservable, Observable } from '../../tools/observable'
import { clocksNow } from '../../tools/utils/timeUtils'
import { displayIfDebugEnabled, startMonitorErrorCollection, resetMonitor } from '../../tools/monitor'
import { displayIfDebugEnabled, startMonitorErrorCollection } from '../../tools/monitor'
import { sendToExtension } from '../../tools/sendToExtension'
import { performDraw } from '../../tools/utils/numberUtils'
import { jsonStringify } from '../../tools/serialisation/jsonStringify'
Expand Down Expand Up @@ -247,7 +247,6 @@ function getRuntimeEnvInfo(): RuntimeEnvInfo {

export function resetTelemetry() {
telemetryObservable = undefined
resetMonitor()
}

/**
Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export {
startTelemetry,
addTelemetryDebug,
addTelemetryError,
resetTelemetry,
TelemetryService,
TelemetryMetrics,
addTelemetryConfiguration,
Expand Down Expand Up @@ -106,7 +105,7 @@ export type { CookieStore, WeakRef, WeakRefConstructor } from './browser/browser
export type { XhrCompleteContext, XhrStartContext } from './browser/xhrObservable'
export { initXhrObservable } from './browser/xhrObservable'
export type { FetchResolveContext, FetchStartContext, FetchContext } from './browser/fetchObservable'
export { initFetchObservable, resetFetchObservable, ResponseBodyAction } from './browser/fetchObservable'
export { initFetchObservable, ResponseBodyAction } from './browser/fetchObservable'
export { fetch } from './browser/fetch'
export type { PageMayExitEvent } from './browser/pageMayExitObservable'
export { createPageMayExitObservable, PageExitReason, isPageExitReason } from './browser/pageMayExitObservable'
Expand All @@ -115,7 +114,7 @@ export { requestIdleCallback } from './tools/requestIdleCallback'
export * from './tools/taskQueue'
export * from './tools/timer'
export type { ConsoleLog } from './domain/console/consoleObservable'
export { initConsoleObservable, resetConsoleObservable } from './domain/console/consoleObservable'
export { initConsoleObservable } from './domain/console/consoleObservable'
export type { BoundedBuffer } from './tools/boundedBuffer'
export { createBoundedBuffer } from './tools/boundedBuffer'
export { catchUserErrors } from './tools/catchUserErrors'
Expand Down
5 changes: 1 addition & 4 deletions packages/core/src/tools/monitor.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { display } from './display'
import { callMonitored, monitor, monitored, startMonitorErrorCollection, resetMonitor, setDebugMode } from './monitor'
import { callMonitored, monitor, monitored, startMonitorErrorCollection, setDebugMode } from './monitor'

describe('monitor', () => {
let onMonitorErrorCollectedSpy: jasmine.Spy<(error: unknown) => void>

beforeEach(() => {
onMonitorErrorCollectedSpy = jasmine.createSpy()
})
afterEach(() => {
resetMonitor()
})

describe('decorator', () => {
class Candidate {
Expand Down
7 changes: 2 additions & 5 deletions packages/core/src/tools/timer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { mockClock, mockZoneJs, registerCleanupTask } from '../../test'
import { mockClock, mockZoneJs } from '../../test'
import type { Clock, MockZoneJs } from '../../test'
import { resetMonitor, startMonitorErrorCollection } from './monitor'
import { startMonitorErrorCollection } from './monitor'
import { setTimeout, clearTimeout, setInterval, clearInterval } from './timer'
import { noop } from './utils/functionUtils'
;[
Expand All @@ -21,9 +21,6 @@ import { noop } from './utils/functionUtils'

beforeEach(() => {
clock = mockClock()
registerCleanupTask(() => {
resetMonitor()
})
zoneJs = mockZoneJs()
})

Expand Down
15 changes: 13 additions & 2 deletions packages/core/src/tools/valueHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface ValueHistory<Value> {
stop: () => void
}

let cleanupHistoriesInterval: TimeoutId | null = null
let cleanupHistoriesInterval: TimeoutId | undefined

const cleanupTasks: Set<() => void> = new Set()

Expand Down Expand Up @@ -143,9 +143,20 @@ export function createValueHistory<Value>({
cleanupTasks.delete(clearExpiredValues)
if (cleanupTasks.size === 0 && cleanupHistoriesInterval) {
clearInterval(cleanupHistoriesInterval)
cleanupHistoriesInterval = null
cleanupHistoriesInterval = undefined
}
}

return { add, find, closeActive, findAll, reset, stop }
}

/**
* Reset all global state. This is useful for testing to ensure clean state between tests.
*
* @internal
*/
export function resetValueHistoryGlobals() {
cleanupTasks.clear()
clearInterval(cleanupHistoriesInterval)
cleanupHistoriesInterval = undefined
}
3 changes: 0 additions & 3 deletions packages/core/test/emulate/mockTelemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { startMonitorErrorCollection } from '../../src/tools/monitor'
import {
addTelemetryError,
getTelemetryObservable,
resetTelemetry,
type RawTelemetryEvent,
type Telemetry,
} from '../../src/domain/telemetry'
Expand All @@ -16,7 +15,6 @@ export interface MockTelemetry {
}

export function startMockTelemetry() {
resetTelemetry()
const events: RawTelemetryEvent[] = []

const telemetryObservable = getTelemetryObservable()
Expand All @@ -29,7 +27,6 @@ export function startMockTelemetry() {

registerCleanupTask(() => {
subscription.unsubscribe()
resetTelemetry()
})

function getEvents() {
Expand Down
36 changes: 27 additions & 9 deletions packages/core/test/forEach.spec.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
import type { BuildEnvWindow } from './buildEnv'
import { resetValueHistoryGlobals } from '../src/tools/valueHistory'
import { resetFetchObservable } from '../src/browser/fetchObservable'
import { resetConsoleObservable } from '../src/domain/console/consoleObservable'
import { resetXhrObservable } from '../src/browser/xhrObservable'
import { resetGetCurrentSite } from '../src/browser/cookie'
import { resetReplayStats } from '../../rum/src/domain/replayStats'
import { resetInteractionCountPolyfill } from '../../rum-core/src/domain/view/viewMetrics/interactionCountPolyfill'
import { resetMonitor } from '../src/tools/monitor'
import { resetTelemetry } from '../src/domain/telemetry'
import { startLeakDetection } from './leakDetection'
import type { BuildEnvWindow } from './buildEnv'

beforeEach(() => {
;(window as unknown as BuildEnvWindow).__BUILD_ENV__SDK_VERSION__ = 'test'
// reset globals
;(window as any).DD_LOGS = {}
;(window as any).DD_RUM = {}
;(window as any).IS_REACT_ACT_ENVIRONMENT = true
// prevent 'Some of your tests did a full page reload!' issue
window.onbeforeunload = () => 'stop'
//window.onbeforeunload = () => 'stop'
startLeakDetection()
// Note: clearing cookies should be done in `beforeEach` rather than `afterEach`, because in some
// cases the test patches the `document.cookie` getter (ex: `spyOnProperty(document, 'cookie',
// 'get')`), which would prevent the `clearAllCookies` function from working properly.
})

afterEach(() => {
// reset globals
delete (window as any).DD_LOGS
delete (window as any).DD_RUM
clearAllCookies()
resetValueHistoryGlobals()
resetFetchObservable()
resetConsoleObservable()
resetXhrObservable()
resetGetCurrentSite()
resetReplayStats()
resetMonitor()
resetTelemetry()
resetInteractionCountPolyfill()
})

function clearAllCookies() {
document.cookie.split(';').forEach((c) => {
document.cookie = c.replace(/=.*/, `=;expires=${new Date().toUTCString()};path=/;samesite=strict`)
document.cookie = c.replace(/=.*/, `=;expires=${new Date(0).toUTCString()};path=/;samesite=strict`)
})
}
12 changes: 1 addition & 11 deletions packages/logs/src/boot/preStartLogs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@ import {
createFakeTelemetryObject,
} from '@datadog/browser-core/test'
import type { TimeStamp, TrackingConsentState } from '@datadog/browser-core'
import {
ONE_SECOND,
TrackingConsent,
createTrackingConsentState,
display,
resetFetchObservable,
} from '@datadog/browser-core'
import { ONE_SECOND, TrackingConsent, createTrackingConsentState, display } from '@datadog/browser-core'
import type { CommonContext } from '../rawLogsEvent.types'
import type { HybridInitConfiguration, LogsInitConfiguration } from '../domain/configuration'
import type { Logger } from '../domain/logger'
Expand All @@ -32,10 +26,6 @@ describe('preStartLogs', () => {
clock = mockClock()
})

afterEach(() => {
resetFetchObservable()
})

describe('configuration validation', () => {
let displaySpy: jasmine.Spy
let doStartLogsSpy: jasmine.Spy<DoStartLogs>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ErrorSource, resetFetchObservable } from '@datadog/browser-core'
import { ErrorSource } from '@datadog/browser-core'
import type { MockFetch, MockFetchManager } from '@datadog/browser-core/test'
import { SPEC_ENDPOINTS, mockFetch, registerCleanupTask } from '@datadog/browser-core/test'
import type { RawNetworkLogsEvent } from '../../rawLogsEvent.types'
Expand Down Expand Up @@ -34,7 +34,6 @@ describe('network error collection', () => {
const { stop } = startNetworkErrorCollection({ ...CONFIGURATION, forwardErrorsToLogs }, lifeCycle)
registerCleanupTask(() => {
stop()
resetFetchObservable()
})
fetch = window.fetch as MockFetch
}
Expand Down
5 changes: 0 additions & 5 deletions packages/rum-core/src/boot/preStartRum.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
TrackingConsent,
createTrackingConsentState,
DefaultPrivacyLevel,
resetFetchObservable,
ExperimentalFeature,
} from '@datadog/browser-core'
import type { Clock } from '@datadog/browser-core/test'
Expand Down Expand Up @@ -40,10 +39,6 @@ const FAKE_WORKER = {} as DeflateWorker
const PUBLIC_API = {} as RumPublicApi

describe('preStartRum', () => {
afterEach(() => {
resetFetchObservable()
})

describe('configuration validation', () => {
let strategy: Strategy
let doStartRumSpy: jasmine.Spy<DoStartRum>
Expand Down
Loading