Skip to content

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

Draft
mormubis wants to merge 11 commits into
mainfrom
adlrb/vitest
Draft

👷 Migrate unit tests from Karma/Jasmine to Vitest#4196
mormubis wants to merge 11 commits into
mainfrom
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 173.61 KiB 173.61 KiB 0 B 0.00%
Rum Profiler 8.07 KiB 8.07 KiB 0 B 0.00%
Rum Recorder 21.23 KiB 21.23 KiB 0 B 0.00%
Logs 56.96 KiB 56.96 KiB 0 B 0.00%
Rum Slim 131.28 KiB 131.28 KiB 0 B 0.00%
Worker 22.99 KiB 22.99 KiB 0 B 0.00%

🔗 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.

@mormubis mormubis force-pushed the adlrb/vitest branch 9 times, most recently from da29bfd to d0856e5 Compare May 25, 2026 15:49
mormubis added 9 commits May 27, 2026 15:09
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.
@mormubis mormubis force-pushed the adlrb/vitest branch 8 times, most recently from b2f494c to e2aff48 Compare May 29, 2026 14:06
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