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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ playwright-report/
# Claude Code local files
*.local.md
.claude/settings.local.json
**/__screenshots__/
7 changes: 6 additions & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ unit:
junit: test-report/unit/*.xml
script:
- yarn
- yarn playwright install chromium --with-deps
- yarn test:unit
after_script:
- node ./scripts/test/export-test-result.ts unit
Expand Down Expand Up @@ -285,14 +286,18 @@ unit-bs:
extends:
- .base-configuration
- .bs-allowed-branches
- .resource-allocation-4-cpus
interruptible: true
resource_group: browserstack
timeout: 35 minutes
artifacts:
when: always
reports:
junit: test-report/unit-bs/*.xml
script:
- yarn
- node scripts/test/ci-bs.ts test:unit
- yarn playwright install --with-deps
- FORCE_COLOR=1 node scripts/test/ci-bs.ts test:unit
after_script:
- node ./scripts/test/export-test-result.ts unit-bs

Expand Down
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ yarn build:apps
yarn test:unit

# Run specific test file
yarn test:unit --spec packages/core/src/path/to/feature.spec.ts
yarn vitest run packages/core/src/path/to/feature.spec.ts

# Run tests on a specific seed
yarn test:unit --seed 123
yarn vitest run --sequence.seed 123

# setup E2E tests (installs Playwright and builds test apps)
yarn test:e2e:init
Expand Down Expand Up @@ -66,7 +66,7 @@ test/
├── apps/ # Test apps for E2E and performance testing
├── e2e/ # Playwright E2E test scenarios
├── performance/ # Performance benchmarking tests
└── unit/ # Karma/Jasmine unit test configuration
└── unit/ # Vitest unit test configuration

scripts/ # Build, deploy, release automation
```
Expand All @@ -86,9 +86,9 @@ For deeper context, see:

### Unit Tests

- Test framework: Jasmine + Karma. Spec files co-located with implementation: `feature.ts` → `feature.spec.ts`
- Focus tests with `fit()` / `fdescribe()`, skip with `xit()` / `xdescribe()`
- Spec files co-located with implementation: `feature.ts` → `feature.spec.ts`
- Use `registerCleanupTask()` for cleanup, NOT `afterEach()`
- Test framework: Vitest (browser mode with Playwright)
- Mock values/functions: wrap with `mockable()` in source, use `replaceMockable()` or `replaceMockableWithSpy()` in tests (auto-cleanup)

### Naming Conventions
Expand Down
4 changes: 4 additions & 0 deletions LICENSE-3rdparty.csv
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ dev,@types/react-dom,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,@vitejs/plugin-vue,MIT,Copyright (c) 2019-present, Yuxi (Evan) You and Vite contributors
dev,@vitest/browser,MIT,Copyright (c) 2021-Present VoidZero Inc. and Vitest contributors
dev,@vitest/browser-playwright,MIT,Copyright (c) 2021-Present VoidZero Inc. and Vitest contributors
dev,@vitest/coverage-istanbul,MIT,Copyright (c) 2021-Present VoidZero Inc. and Vitest contributors
dev,@module-federation/enhanced,MIT, Copyright (c) 2020 ScriptedAlchemy LLC (Zack Jackson) Zhou Shaw (zhouxiao)
dev,@vue/test-utils,MIT,Copyright (c) 2021-present vuejs
dev,ajv,MIT,Copyright 2015-2017 Evgeny Poberezkin
Expand Down Expand Up @@ -87,6 +90,7 @@ dev,typescript,Apache-2.0,Copyright Microsoft Corporation
dev,typescript-eslint,MIT,Copyright (c) 2019 typescript-eslint and other contributors
dev,undici,MIT,Copyright (c) Matteo Collina and Undici contributors
dev,vite,MIT,Copyright (c) 2019-present, VoidZero Inc. and Vite contributors
dev,vitest,MIT,Copyright (c) 2021-Present VoidZero Inc. and Vitest contributors
dev,vue,MIT,Copyright (c) 2018-present, Yuxi (Evan) You
dev,nuxt,MIT,Copyright (c) 2016-present Nuxt Team
dev,vue-router,MIT,Copyright (c) 2019-present Eduardo San Martin Morote
Expand Down
3 changes: 2 additions & 1 deletion developer-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"@wxt-dev/module-react": "1.2.2",
"typescript": "6.0.3"
"typescript": "6.0.3",
"vite": "8.0.8"
},
"dependencies": {
"@datadog/browser-core": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { describe, expect, it } from 'vitest'
import type { RumActionEvent, RumResourceEvent } from '@datadog/browser-rum'
import { FacetRegistry } from '../../../hooks/useEvents'
import type { FacetValuesFilter } from '../../../hooks/useEvents'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { describe, expect, it } from 'vitest'
import type { TelemetryEvent } from '../../../../../../packages/core/src/domain/telemetry'
import type { LogsEvent } from '../../../../../../packages/logs/src/logsEvent.types'
import type { RumEvent } from '../../../../../../packages/rum-core/src/rumEvent.types'
Expand Down
5 changes: 3 additions & 2 deletions developer-extension/src/panel/flushEvents.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { vi, beforeEach, describe, expect, it, type Mock } from 'vitest'
import type { Configuration } from '@datadog/browser-core'
import { registerCleanupTask } from '../../../packages/core/test'
import type { PageMayExitEvent } from '../../../packages/core/src/browser/pageMayExitObservable'
import { createPageMayExitObservable } from '../../../packages/core/src/browser/pageMayExitObservable'
import { flushScript } from './flushEvents'

describe('flushEvents', () => {
let onExitSpy: jasmine.Spy<(event: PageMayExitEvent) => void>
let onExitSpy: Mock<(event: PageMayExitEvent) => void>
let configuration: Configuration

beforeEach(() => {
onExitSpy = jasmine.createSpy()
onExitSpy = vi.fn()
configuration = {} as Configuration
registerCleanupTask(createPageMayExitObservable(configuration).subscribe(onExitSpy).unsubscribe)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { describe, expect, it } from 'vitest'
import type { RumEvent } from '../../../../../packages/rum-core/src/rumEvent.types'
import type { LogsEvent } from '../../../../../packages/logs/src/logsEvent.types'
import { isSafari } from '../../../../../packages/core/src/tools/utils/browserDetection'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { beforeEach, describe, expect, it } from 'vitest'
import { isChromium } from '../../../../../packages/core/src/tools/utils/browserDetection'
import { getAllFields } from './facetRegistry'

describe('getAllFields', () => {
beforeEach(() => {
beforeEach((ctx) => {
if (!isChromium()) {
pending('Extension only supported in chromium')
ctx.skip()
return
}
})

Expand Down
16 changes: 12 additions & 4 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -469,17 +469,25 @@ export default tseslint.config(
},

{
files: ['**/webpack.*.{ts,mts}', 'eslint-local-rules/**/*.js'],
files: ['**/webpack.*.{ts,mts}', 'eslint-local-rules/**/*.js', 'vitest.config.ts', 'vitest.bs.config.ts'],
rules: {
// Webpack configuration files and eslint rules files are expected to use a default export.
// Webpack configuration files, eslint rules files, and vitest config are expected to use a default export.
'import/no-default-export': 'off',
},
},

{
files: ['test/e2e/**/*.ts', 'test/performance/**/*.ts'],
files: [
'test/e2e/**/*.ts',
'test/performance/**/*.ts',
SPEC_FILES,
'packages/*/test/**/*.ts',
'test/unit/**/*.ts',
'vitest.config.ts',
'vitest.bs.config.ts',
],
rules: {
// E2E codebase is importing @datadog/browser-* packages referenced by tsconfig.
// E2E, test utilities, and spec files import packages referenced by tsconfig or root devDependencies (vitest).
'import/no-extraneous-dependencies': 'off',
},
},
Expand Down
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@
"dev-server": "node scripts/dev-server/index.ts",
"release": "node ./scripts/release/prepare-release.ts --push",
"test": "yarn test:unit:watch",
"test:unit": "karma start ./test/unit/karma.local.conf.js",
"test:unit": "vitest run",
"test:script": "node --test --experimental-test-module-mocks './scripts/**/*.spec.*'",
"test:unit:watch": "yarn test:unit --no-single-run",
"test:unit:bs": "node --env-file-if-exists=.env ./scripts/test/bs-wrapper.ts karma start test/unit/karma.bs.conf.js",
"test:unit:watch": "vitest",
"test:unit:bs": "node ./scripts/test/bs-wrapper.ts vitest run --config vitest.bs.config.ts",
"test:e2e:init": "yarn build && yarn build:apps && yarn playwright install chromium --with-deps",
"test:e2e": "playwright test --config test/e2e/playwright.local.config.ts --project chromium",
"test:e2e:bs": "node --env-file-if-exists=.env ./scripts/test/bs-wrapper.ts playwright test --config test/e2e/playwright.bs.config.ts",
Expand Down Expand Up @@ -57,6 +57,9 @@
"@types/jasmine": "3.10.19",
"@types/node": "25.6.0",
"@types/node-forge": "1.3.14",
"@vitest/browser": "4.0.18",
"@vitest/browser-playwright": "4.0.18",
"@vitest/coverage-istanbul": "4.0.18",
"ajv": "8.20.0",
"browserstack-local": "1.5.13",
"busboy": "1.6.0",
Expand Down Expand Up @@ -97,6 +100,7 @@
"typescript": "6.0.3",
"typescript-eslint": "8.59.1",
"undici": "8.1.0",
"vitest": "4.0.18",
"webpack": "5.106.2",
"webpack-cli": "7.0.2",
"webpack-dev-middleware": "8.0.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { vi, describe, expect, it } from 'vitest'
import type { InitConfiguration } from '../domain/configuration'
import { display } from '../tools/display'
import { displayAlreadyInitializedError } from './displayAlreadyInitializedError'

describe('displayAlreadyInitializedError', () => {
it('should display an error', () => {
const displayErrorSpy = spyOn(display, 'error')
const displayErrorSpy = vi.spyOn(display, 'error')
displayAlreadyInitializedError('DD_RUM', {} as InitConfiguration)
expect(displayErrorSpy).toHaveBeenCalledTimes(1)
expect(displayErrorSpy).toHaveBeenCalledWith('DD_RUM is already initialized.')
})

it('should not display an error if the "silentMultipleInit" option is used', () => {
const displayErrorSpy = spyOn(display, 'error')
const displayErrorSpy = vi.spyOn(display, 'error')
displayAlreadyInitializedError('DD_RUM', { silentMultipleInit: true } as InitConfiguration)
expect(displayErrorSpy).not.toHaveBeenCalled()
})
Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/boot/init.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { vi, describe, expect, it } from 'vitest'
import { display } from '../tools/display'
import { defineGlobal } from './init'

Expand All @@ -17,8 +18,8 @@ describe('defineGlobal', () => {
})

it('run the queued callbacks on the old value', () => {
const fn1 = jasmine.createSpy()
const fn2 = jasmine.createSpy()
const fn1 = vi.fn()
const fn2 = vi.fn()
const myGlobal: any = {
foo: {
q: [fn1, fn2],
Expand All @@ -42,7 +43,7 @@ describe('defineGlobal', () => {
q: [onReady],
},
}
const displaySpy = spyOn(display, 'error')
const displaySpy = vi.spyOn(display, 'error')

defineGlobal(myGlobal, 'foo', {})
expect(displaySpy).toHaveBeenCalledWith('onReady callback threw an error:', myError)
Expand Down
27 changes: 14 additions & 13 deletions packages/core/src/browser/addEventListener.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { vi, beforeEach, describe, expect, it } from 'vitest'
import type { Configuration } from '../domain/configuration'
import { createNewEvent, mockZoneJs, registerCleanupTask } from '../../test'
import type { MockZoneJs } from '../../test'
Expand All @@ -16,7 +17,7 @@ describe('addEventListener', () => {
})

it('uses the original addEventListener method instead of the method patched by Zone.js', () => {
const zoneJsPatchedAddEventListener = jasmine.createSpy()
const zoneJsPatchedAddEventListener = vi.fn()
const eventTarget = document.createElement('div')
zoneJs.replaceProperty(eventTarget, 'addEventListener', zoneJsPatchedAddEventListener)

Expand All @@ -25,7 +26,7 @@ describe('addEventListener', () => {
})

it('uses the original removeEventListener method instead of the method patched by Zone.js', () => {
const zoneJsPatchedRemoveEventListener = jasmine.createSpy()
const zoneJsPatchedRemoveEventListener = vi.fn()
const eventTarget = document.createElement('div')
zoneJs.replaceProperty(eventTarget, 'removeEventListener', zoneJsPatchedRemoveEventListener)

Expand All @@ -41,17 +42,17 @@ describe('addEventListener', () => {
// eslint-disable-next-line @typescript-eslint/unbound-method
const originalRemoveEventListener = EventTarget.prototype.removeEventListener

EventTarget.prototype.addEventListener = jasmine.createSpy()
EventTarget.prototype.removeEventListener = jasmine.createSpy()
EventTarget.prototype.addEventListener = vi.fn()
EventTarget.prototype.removeEventListener = vi.fn()

registerCleanupTask(() => {
EventTarget.prototype.addEventListener = originalAddEventListener
EventTarget.prototype.removeEventListener = originalRemoveEventListener
})

const htmlDivElement = document.createElement('div')
htmlDivElement.addEventListener = jasmine.createSpy()
htmlDivElement.removeEventListener = jasmine.createSpy()
htmlDivElement.addEventListener = vi.fn()
htmlDivElement.removeEventListener = vi.fn()

const { stop } = addEventListener({ allowUntrustedEvents: false }, htmlDivElement, DOM_EVENT.CLICK, noop)

Expand All @@ -71,11 +72,11 @@ describe('addEventListener', () => {
})

it('Use the addEventListener method when the eventTarget is not an instance of EventTarget', () => {
const listener = jasmine.createSpy()
const listener = vi.fn()

const customEventTarget = {
addEventListener: jasmine.createSpy(),
removeEventListener: jasmine.createSpy(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
} as unknown as HTMLElement

const { stop } = addEventListener({ allowUntrustedEvents: false }, customEventTarget, 'change', listener)
Expand All @@ -93,7 +94,7 @@ describe('addEventListener', () => {
})

it('should be ignored if __ddIsTrusted is absent', () => {
const listener = jasmine.createSpy()
const listener = vi.fn()
const eventTarget = document.createElement('div')
addEventListener(configuration, eventTarget, DOM_EVENT.CLICK, listener)

Expand All @@ -103,7 +104,7 @@ describe('addEventListener', () => {
})

it('should be ignored if __ddIsTrusted is false', () => {
const listener = jasmine.createSpy()
const listener = vi.fn()
const eventTarget = document.createElement('div')
addEventListener(configuration, eventTarget, DOM_EVENT.CLICK, listener)

Expand All @@ -113,7 +114,7 @@ describe('addEventListener', () => {
})

it('should not be ignored if __ddIsTrusted is true', () => {
const listener = jasmine.createSpy()
const listener = vi.fn()
const eventTarget = document.createElement('div')
addEventListener(configuration, eventTarget, DOM_EVENT.CLICK, listener)

Expand All @@ -124,7 +125,7 @@ describe('addEventListener', () => {
})

it('should not be ignored if allowUntrustedEvents is true', () => {
const listener = jasmine.createSpy()
const listener = vi.fn()
const eventTarget = document.createElement('div')
configuration = { allowUntrustedEvents: true } as Configuration

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/browser/cookie.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { describe, expect, it } from 'vitest'
import { mockCookies } from '../../test'
import { getCurrentSite } from './cookie'

Expand Down
13 changes: 7 additions & 6 deletions packages/core/src/browser/fetch.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { vi, beforeEach, describe, expect, it } from 'vitest'
import { type MockZoneJs, mockZoneJs } from '../../test'
import { fetch } from './fetch'

Expand All @@ -9,8 +10,8 @@ describe('fetch', () => {
})

it('does not use the Zone.js function', async () => {
const nativeFetchSpy = spyOn(window, 'fetch')
const zoneJsFetchSpy = jasmine.createSpy('zoneJsFetch')
const nativeFetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue(new Response())
const zoneJsFetchSpy = vi.fn()

zoneJs.replaceProperty(window, 'fetch', zoneJsFetchSpy)

Expand All @@ -21,8 +22,8 @@ describe('fetch', () => {
})

it('calls the native fetch function with correct arguments', async () => {
const nativeFetchSpy = spyOn(window, 'fetch')
const zoneJsFetchSpy = jasmine.createSpy('zoneJsFetch')
const nativeFetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue(new Response())
const zoneJsFetchSpy = vi.fn()

zoneJs.replaceProperty(window, 'fetch', zoneJsFetchSpy)

Expand All @@ -33,8 +34,8 @@ describe('fetch', () => {

it('returns the response from native fetch', async () => {
const mockResponse = new Response('test response', { status: 200 })
spyOn(window, 'fetch').and.returnValue(Promise.resolve(mockResponse))
const zoneJsFetchSpy = jasmine.createSpy('zoneJsFetch').and.returnValue(Promise.resolve(new Response()))
vi.spyOn(window, 'fetch').mockReturnValue(Promise.resolve(mockResponse))
const zoneJsFetchSpy = vi.fn().mockReturnValue(Promise.resolve(new Response()))

zoneJs.replaceProperty(window, 'fetch', zoneJsFetchSpy)

Expand Down
Loading
Loading