Skip to content

👷 Migrate unit tests from Karma/Jasmine to Vitest#4196

Draft
mormubis wants to merge 9 commits intomainfrom
adlrb/vitest
Draft

👷 Migrate unit tests from Karma/Jasmine to Vitest#4196
mormubis wants to merge 9 commits intomainfrom
adlrb/vitest

Conversation

@mormubis
Copy link
Copy Markdown
Contributor

@mormubis mormubis commented Feb 17, 2026

Motivation

Karma is deprecated. This migrates the full unit test suite to Vitest 4.x with browser mode (Playwright).

Changes

Most of the diff is mechanical spec file replacements (jasmine.createSpy()vi.fn(), etc.). The interesting parts:

mockClock had to be rewritten because Vitest's vi.useFakeTimers fakes performance by default, which breaks every perf-related test. The toFake list now explicitly excludes it.

allJsonSchemas was using webpack's require.context to load schema files. Replaced with Vite's import.meta.glob.

BrowserStack needed bs-local.com as server host because Safari replaces localhost with its own domain, breaking cookie access in vitest's iframe. Same issue we already have with ServiceWorker tests in E2E. Cookie tests are still skipped on Safari because this alone doesn't fully fix it.

The unit-bs CI job needs Playwright installed with system deps (--with-deps) and 4 CPUs to handle 5 concurrent remote browsers without timing out.

vite@8.0.8 is pinned in the developer extension because @vitejs/plugin-react can't resolve vite/internal in newer patch versions.

3784 unit tests pass, 18628 BrowserStack tests across 5 browsers.

Test instructions

yarn test:unit
yarn typecheck
yarn lint

Checklist

  • Tested locally
  • Tested on staging
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.
  • Updated documentation and/or relevant AGENTS.md file

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 17, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@cit-pr-commenter-54b7da
Copy link
Copy Markdown

cit-pr-commenter-54b7da Bot commented Feb 17, 2026

Bundles Sizes Evolution

📦 Bundle Name Base Size Local Size 𝚫 𝚫% Status
Rum 179.65 KiB 179.65 KiB 0 B 0.00%
Rum Profiler 6.17 KiB 6.17 KiB 0 B 0.00%
Rum Recorder 27.03 KiB 27.03 KiB 0 B 0.00%
Logs 56.78 KiB 56.78 KiB 0 B 0.00%
Rum Slim 135.50 KiB 135.50 KiB 0 B 0.00%
Worker 23.63 KiB 23.63 KiB 0 B 0.00%
🚀 CPU Performance
Action Name Base CPU Time (ms) Local CPU Time (ms) 𝚫%
RUM - add global context 0.0039 0.0046 +17.95%
RUM - add action 0.0131 0.0142 +8.40%
RUM - add error 0.0117 0.0142 +21.37%
RUM - add timing 0.0025 0.0032 +28.00%
RUM - start view 0.0119 0.0141 +18.49%
RUM - start/stop session replay recording 0.0006 0.0009 +50.00%
Logs - log message 0.0138 0.0151 +9.42%
🧠 Memory Performance
Action Name Base Memory Consumption Local Memory Consumption 𝚫
RUM - add global context 31.78 KiB 30.88 KiB -918 B
RUM - add action 56.94 KiB 56.51 KiB -440 B
RUM - add timing 32.72 KiB 32.67 KiB -58 B
RUM - add error 60.80 KiB 58.78 KiB -2.02 KiB
RUM - start/stop session replay recording 32.10 KiB 31.67 KiB -440 B
RUM - start view 484.29 KiB 481.56 KiB -2.73 KiB
Logs - log message 95.66 KiB 94.06 KiB -1.60 KiB

🔗 RealWorld

@mormubis mormubis force-pushed the adlrb/vitest branch 5 times, most recently from 319576e to 02e30e1 Compare February 20, 2026 10:19
@mormubis mormubis force-pushed the adlrb/vitest branch 18 times, most recently from 051c55a to 23d1a16 Compare April 30, 2026 21:16
Comment thread vitest.config.ts
},

test: {
browser: {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Browser mode with Playwright. Locally it runs headless Chromium. On BrowserStack, Playwright connects to remote browsers via WebSocket (wss://cdp.browserstack.com/playwright), so the old browser versions (Chrome 63, Edge 80, etc.) are real browsers hosted by BrowserStack, not local Playwright binaries. The aliases mirror tsconfig.base.json so vitest resolves packages the same way TypeScript does.

// AND so performance.timing.navigationStart remains accessible.
// When performance IS faked, @sinonjs/fake-timers replaces the object and our
// override silently fails (the fake performance.now() starts at 0).
vi.useFakeTimers({
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

performance is excluded from toFake. If we fake it, performance.now() returns fake time but the browser's internal performance observer still uses real time. Breaks every perf-related test.

mostRecent(): { args: Parameters<F>; returnValue: ReturnType<F> }
}

export function collectAsyncCalls<F extends (...args: any[]) => any>(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Vitest equivalent of the old Jasmine calls.all() pattern. Wraps a vi.fn() and resolves a promise once the expected number of calls is reached.

/// <reference types="vite/client" />
// Load all JSON schema files from the rum-events-format submodule.
// Uses Vite's import.meta.glob (replaces webpack's require.context).
const schemaModules = import.meta.glob('../../../rum-events-format/schemas/**/*.json', { eager: true })
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

require.contextimport.meta.glob. Same result, Vite API instead of webpack.

Comment thread vitest.bs.config.ts
browser: {
enabled: true,
provider: playwright(),
// Use bs-local.com instead of localhost so Safari on BrowserStack can access cookies.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Safari on BrowserStack replaces localhost with bs-local.com, which breaks cookie access in vitest's iframe. Same issue we already have with ServiceWorker tests in E2E.

}, DOM_MUTATION_OBSERVABLE_DURATION)
}
return () =>
new Promise<void>((resolve) => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Vitest doesn't support done() callbacks. Converted to new Promise<void>.

import { SESSION_STORE_KEY } from './sessionStoreStrategy'

// Safari on BrowserStack cannot access cookies because vitest runs tests in an iframe
// and BrowserStack replaces localhost with bs-local.com, triggering Safari's ITP restrictions.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Safari on BrowserStack still can't access cookies in the vitest iframe even with bs-local.com. Skipped on Safari only, runs fine on the other 4 browsers.

@@ -1,3 +1,4 @@
import { vi, beforeEach, describe, expect, it } from 'vitest'
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Typical spec migration. jasmine.createSpy()vi.fn(), spyOnvi.spyOn, etc. All 257 spec files follow this same pattern.

mormubis added 5 commits May 5, 2026 22:48
trackRuntimeError: Add preventDefault() listeners to suppress Vitest's
unhandled error reporting. Tests intentionally throw errors that are
handled by the SDK's own onerror instrumentation, but Vitest also
catches them via addEventListener. preventDefault() marks them as
handled without blocking window.onerror.

taskQueue: Snapshot requestSpy.mock.calls.length before iterating in
callAllActiveCallbacks. Vitest's mock.calls array grows live (unlike
Jasmine's calls.count() snapshot), so callbacks that trigger
scheduleNextRun caused an infinite loop.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant