Skip to content

Commit a161623

Browse files
yurishkuroclaude
andauthored
[adr/007] Migrate packages/plexus tests from Jest to Vitest (#3690)
- Replace jest/babel-jest/jest-environment-jsdom/@types/jest and all Babel preset deps with vitest + @vitest/coverage-v8 + jsdom - Add vitest.config.ts with jsdom environment and globals:true - Add test/vitest-setup.ts: sets global.jest = vi alias so test files using jest.fn(), jest.clearAllMocks() etc. work without modification - Rename *.test.js → *.test.jsx (JSX requires explicit extension in Vite) - Replace jest.mock() → vi.mock() in all test files (hoisting requires the literal vi.mock() pattern; cannot be aliased at runtime) - Replace jest.requireActual() → async vi.importActual() (1 occurrence) - Mock factories returning default exports: return Comp → return { default: Comp } - Mock factories for ZoomManager: wrap in { default: ... } - Fix SvgEdgesLayer.test.jsx: replace require() with ES imports for mocked modules (require() unavailable in ESM test code) - Fix demo/src/index.test.tsx: vi.mock(), vi.fn(), and regular function for LayoutManager constructor mock (arrow fns can't be newed) - Delete test/babel-transform.js, generic-file-transform.js, jest-per-test-setup.js - Update tsconfig.json: "types": ["jest"] → ["vitest/globals"] Result: 8 test files, 91 tests — matches Jest baseline exactly. ## AI Usage in this PR (choose one) See [AI Usage Policy](https://github.com/jaegertracing/jaeger/blob/main/CONTRIBUTING_GUIDELINES.md#ai-usage-policy). - [ ] **None**: No AI tools were used in creating this PR - [ ] **Light**: AI provided minor assistance (formatting, simple suggestions) - [ ] **Moderate**: AI helped with code generation or debugging specific parts - [x] **Heavy**: AI generated most or all of the code changes --------- Signed-off-by: Yuri Shkuro <github@ysh.us> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a92be1f commit a161623

17 files changed

+579
-315
lines changed

docs/adr/0007-vite-plus-migration.md

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# ADR 0007: Migrate to Vite+ (Full Vite Toolchain)
22

33
**Status**: Partially Implemented
4-
**Last Updated**: 2026-04-01
4+
**Last Updated**: 2026-04-02
55

66
---
77

@@ -17,13 +17,13 @@ Prettier, and Jest with a single dependency that has built-in TypeScript support
1717

1818
## Context & Problem
1919

20-
### Current State (after PRs A, B, C1, C2, D, E)
20+
### Current State (after PRs A, B, C1, C2, D, E, F-plexus)
2121

2222
| Concern | `packages/jaeger-ui` | `packages/plexus` |
2323
| ---------------- | ---------------------------------- | ---------------------------------- |
2424
| Dev server | Vite 8 | n/a |
2525
| Production build | Vite 8 (Rolldown engine) | ✅ dropped |
26-
| Testing | Jest 30 + `babel-jest` | Jest 30 + `babel-jest` |
26+
| Testing | Jest 30 + `babel-jest` | ✅ Vitest 4 |
2727
| TypeScript | `tsc` (type-check only, 1 config) | `tsc` (noEmit, source only) |
2828
| Linting | ✅ Oxlint (via `vp lint`) | ✅ Oxlint (via `vp lint`) |
2929
| Formatting | ✅ Oxfmt (via `vp fmt`) | ✅ Oxfmt (via `vp fmt`) |
@@ -76,8 +76,7 @@ Migrate the monorepo to Vite+ in phases:
7676
4.**Replace Prettier with Oxfmt**`prettier` removed; `oxfmt --migrate=prettier` migrated the config.
7777
5.**Upgrade TypeScript** — upgraded to 6.0.2; `moduleResolution` switched to `"bundler"`.
7878
6.**Consolidate jaeger-ui tsconfigs**`tsconfig.lint.json` deleted; `tsconfig.json` is the single config.
79-
7. **Replace Jest + Babel with Vitest** — deferred; significant migration effort, but the correct
80-
long-term direction.
79+
7. 🔶 **Replace Jest + Babel with Vitest** — plexus done ([#3690](https://github.com/jaegertracing/jaeger-ui/pull/3690)); jaeger-ui pending.
8180

8281
---
8382

@@ -254,7 +253,7 @@ Upgraded TypeScript from 5.9.3 to 6.0.2. Two fixes required:
254253
`"exports"` field in `package.json`. `"bundler"` accurately reflects how Vite resolves modules.
255254
- `packages/plexus/tsconfig.json`: added `"types": ["jest"]` because TypeScript 6.0 tightened
256255
`@types` auto-discovery under `moduleResolution: "bundler"` — Jest globals were no longer found
257-
automatically.
256+
automatically. (Later changed to `"types": ["vitest/globals"]` in PR F when plexus migrated to Vitest.)
258257

259258
---
260259

@@ -304,15 +303,54 @@ both of which are unaffected by switching the lint/test tooling.
304303

305304
### 8. Both Packages — Replace Jest + Babel with Vitest (PR F)
306305

307-
**Deferred.** The migration is the correct long-term direction but carries significant effort:
308-
309-
- `jest.fn()``vi.fn()` across the test suite
310-
- `@testing-library/jest-dom` import path changes
311-
- `importMetaTransform` Babel plugin in `test/babel-transform.js` is eliminated (Vitest runs native ESM)
312-
- Snapshot files may need regeneration
313-
- See Unknowns 3–6 for the investigation plan
314-
315-
Both packages continue to use Jest 30 + `babel-jest` until PR F is ready.
306+
**Partially done.** `packages/plexus` migrated to Vitest in [#3690](https://github.com/jaegertracing/jaeger-ui/pull/3690).
307+
`packages/jaeger-ui` remains on Jest + Babel and is the next step.
308+
309+
#### plexus migration (✅ complete in [#3690](https://github.com/jaegertracing/jaeger-ui/pull/3690))
310+
311+
- Added `vitest`, `@vitest/coverage-v8`, `jsdom` to devDependencies; removed `jest`, `babel-jest`,
312+
`jest-environment-jsdom`, all `@babel/*` packages, `@types/jest`.
313+
- Added `packages/plexus/vitest.config.ts` with `environment: 'jsdom'`, `globals: true`,
314+
`moduleNameMapper` for CSS, and coverage settings.
315+
- Replaced `packages/plexus/test/jest-per-test-setup.js` with `test/vitest-setup.ts`:
316+
- Imports `@testing-library/jest-dom/vitest` for matcher compatibility.
317+
- Sets `(global as any).jest = vi` so existing `jest.fn()` / `jest.clearAllMocks()` calls in test
318+
files continue to work **without rewriting them**.
319+
- `jest.mock(` calls (4 test files + demo) renamed to `vi.mock(` — the one API that cannot be aliased
320+
because Vitest's transform hoists `vi.mock(` patterns statically before imports, whereas `jest.mock(`
321+
is left in place and never hoisted.
322+
- Six test files renamed `.test.js``.test.jsx` so Vite/esbuild parses their JSX syntax correctly.
323+
- Mock factory functions updated for Vitest ESM:
324+
- All default-exported modules must be returned as `{ default: MockComponent }` inside factory bodies.
325+
- `jest.requireActual()` replaced with `async vi.importActual()` (async in Vitest).
326+
- Constructor mocks passed to `vi.fn()` must use regular `function` expressions, not arrow functions
327+
(Vitest 4.x rejects arrow functions called with `new`).
328+
- `vi.fn()` (not the `jest` alias) must be used inside `vi.mock()` factory bodies — the alias is not
329+
available in hoisted factory scope.
330+
- Deleted `test/babel-transform.js` and `test/generic-file-transform.js` (no longer needed).
331+
332+
#### Lessons learned (applicable to jaeger-ui migration)
333+
334+
| Situation | Vitest behaviour | Action required |
335+
|-----------|-----------------|-----------------|
336+
| `jest.fn()`, `jest.clearAllMocks()`, etc. | Not available by default | Alias: `global.jest = vi` in setup file |
337+
| `jest.mock()` / `jest.unmock()` | Not hoisted by transform | Rename to `vi.mock()` / `vi.unmock()` |
338+
| `jest.requireActual(mod)` | Synchronous, CommonJS | Replace with `await vi.importActual(mod)` (async factory) |
339+
| Default-export mocks | Factory can return component directly | Must return `{ default: MockComponent }` |
340+
| Constructor mocks via `mockImplementation` | Arrow functions fail with `new` | Use regular `function() { return {...}; }` |
341+
| `vi.fn()` inside `vi.mock()` factory | `jest` alias not in scope | Use `vi.fn()` explicitly |
342+
| JSX in `.js` test files | Vite/esbuild skips JSX transform for `.js` | Rename to `.jsx` |
343+
| `require()` inside test scope | Unavailable in ESM | Use top-level ES `import` |
344+
345+
#### jaeger-ui migration (pending)
346+
347+
The same strategy applies but at much larger scale (226 test files, ~2600 tests). Additional concerns:
348+
349+
- `packages/jaeger-ui/test/jest-per-test-setup.js` mocks browser APIs (`ResizeObserver`, `matchMedia`,
350+
`requestAnimationFrame`) with `jest.fn()` — covered by the `global.jest = vi` alias.
351+
- `importMetaTransform` Babel plugin in `test/babel-transform.js` is eliminated (Vitest runs native ESM).
352+
- Snapshot files may need regeneration — see Unknown 3.
353+
- `transformIgnorePatterns` for ESM packages (`redux-actions`, `d3-*`) become unnecessary — see Unknown 5.
316354

317355
---
318356

@@ -341,7 +379,7 @@ transpilation, so the tsconfig value has no effect on builds or the dev server.
341379

342380
---
343381

344-
### Unknown 2: Oxlint rule coverage
382+
### Unknown 2: Oxlint rule coverage
345383

346384
**Risk**: Oxlint may not have direct equivalents for all currently active ESLint rules.
347385

@@ -382,6 +420,9 @@ transpilation, so the tsconfig value has no effect on builds or the dev server.
382420

383421
### Unknown 3: Vitest snapshot compatibility
384422

423+
**Status**: Not yet validated for `jaeger-ui`. Plexus has no snapshot tests, so this unknown remains
424+
open for the `jaeger-ui` migration.
425+
385426
**Risk**: Vitest's snapshot serializer may produce subtly different output for component snapshots compared
386427
to Jest's `jest-snapshot`. Existing `.snap` files may need to be regenerated even if functionally equivalent.
387428

@@ -397,16 +438,23 @@ semantic regressions. After update, all snapshot tests pass.
397438

398439
### Unknown 4: Vitest jsdom API coverage for existing test setup
399440

441+
**Status**: Partially resolved via plexus migration. The `global.jest = vi` alias strategy works —
442+
confirmed in plexus where `jest.fn()` / `jest.clearAllMocks()` calls continue to work without renaming.
443+
`@testing-library/jest-dom/vitest` import path is confirmed correct. Remaining jaeger-ui-specific
444+
concerns (browser API mocks, `process.env.TZ`, globalSetup isolation) still need validation.
445+
400446
**Risk**: The `packages/jaeger-ui/test/jest-per-test-setup.js` mocks several browser APIs (`ResizeObserver`,
401-
`MessageChannel`, `matchMedia`, `requestAnimationFrame`) using `jest.fn()`. Under Vitest these need to be
402-
replaced with `vi.fn()`. The `@testing-library/jest-dom` matchers need the Vitest-compatible import path.
447+
`MessageChannel`, `matchMedia`, `requestAnimationFrame`) using `jest.fn()`. Under Vitest these will be
448+
covered by the `global.jest = vi` alias. The `@testing-library/jest-dom` matchers need the
449+
Vitest-compatible import path (`@testing-library/jest-dom/vitest`).
403450

404451
Additionally, the `globalSetup` mechanism differs: Jest's `globalSetup` runs in the Node context before
405452
workers; Vitest's `globalSetup` does the same but the process isolation model differs.
406453

407454
**Experiment**:
408-
1. Port the setup file to `vi.*` APIs and run a representative subset of tests (e.g., Ant Design component
409-
tests, Redux-connected components) to confirm that mocks behave identically.
455+
1. Port the setup file to use `global.jest = vi` alias (not a full `vi.*` rewrite) and run a representative
456+
subset of tests (e.g., Ant Design component tests, Redux-connected components) to confirm that mocks
457+
behave identically.
410458
2. Verify `process.env.TZ = 'UTC'` propagates correctly under Vitest's globalSetup.
411459

412460
**Success criteria**: All previously-passing tests pass under Vitest without mock-related failures.
@@ -415,6 +463,10 @@ workers; Vitest's `globalSetup` does the same but the process isolation model di
415463

416464
### Unknown 5: `transformIgnorePatterns` equivalents in Vitest
417465

466+
**Status**: Confirmed non-issue for plexus (no ESM-only `node_modules` deps). Jaeger-ui still needs
467+
validation for `redux-actions`, `d3-zoom`, `d3-selection`, but the expectation remains that Vitest's
468+
native ESM pipeline makes `transformIgnorePatterns` unnecessary.
469+
418470
**Risk**: Jest required explicit `transformIgnorePatterns` to process ESM-only packages in `node_modules`
419471
(`redux-actions`, `d3-zoom`, `d3-selection`, `@jaegertracing/plexus`). Vitest's Vite transform pipeline
420472
handles ESM natively, so these patterns should be unnecessary. However, some packages may have non-standard
@@ -432,6 +484,8 @@ Babel workaround for `redux-actions` is confirmed unnecessary.
432484

433485
### Unknown 6: `@vitejs/plugin-legacy` interaction with Vitest
434486

487+
**Status**: Not yet validated. Applies only to `jaeger-ui`.
488+
435489
**Risk**: `packages/jaeger-ui/vite.config.mts` currently uses `@vitejs/plugin-legacy` to emit a legacy
436490
browser bundle. Vitest ignores `transformIndexHtml` hooks so the plugin should be harmless, but this needs
437491
confirming.
@@ -476,15 +530,15 @@ confirm no errors or unexpected HTML injection.
476530
| ✅ C2 | Replace Prettier with Oxfmt ([#3686](https://github.com/jaegertracing/jaeger-ui/pull/3686)) | None | Done |
477531
| ✅ D | Upgrade TypeScript to 6.0.2 ([#3688](https://github.com/jaegertracing/jaeger-ui/pull/3688)) | None | Done |
478532
| ✅ E | Consolidate `jaeger-ui` tsconfigs; remove `main` from plexus package.json ([#3689](https://github.com/jaegertracing/jaeger-ui/pull/3689)) | Unknown 1 | Done |
479-
| F | Migrate Jest → Vitest in both packages; remove Babel test deps | Unknowns 3, 4, 5, 6 | Deferred |
533+
| 🔶 F | Migrate Jest → Vitest in both packages; remove Babel test deps ([#3690](https://github.com/jaegertracing/jaeger-ui/pull/3690) plexus ✅, jaeger-ui pending) | Unknowns 3, 4, 5, 6 | Partial |
480534
| G | Update CLAUDE.md, README, CI workflows | None | After F |
481535

482536
### Investigation strategy
483537

484538
- **Unknown 2** (Oxlint rules): ✅ Resolved — parallel run completed; rule coverage confirmed; ESLint removed.
485539
- **Unknown 1** (tsconfig): ✅ Resolved — tsconfig files merged; `tsc-lint` and Vite build both pass.
486-
- **Unknowns 3–6** (Vitest): Validate together in a single throwaway branch by porting the test setup for
487-
one package and running the full suite.
540+
- **Unknowns 3–6** (Vitest): Partially validated via plexus migration (PR F / #3690). Unknowns 4 and 5
541+
confirmed non-issues for plexus. Unknowns 3 and 6 remain open for jaeger-ui (snapshots and plugin-legacy).
488542

489543
---
490544

0 commit comments

Comments
 (0)