Skip to content

Changed koenig kg-* packages from mocha to vitest - #29216

Merged
9larsons merged 3 commits into
mainfrom
slars/koenig-mocha-vitest-dc7b84
Jul 9, 2026
Merged

Changed koenig kg-* packages from mocha to vitest#29216
9larsons merged 3 commits into
mainfrom
slars/koenig-mocha-vitest-dc7b84

Conversation

@9larsons

@9larsons 9larsons commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Why

The rest of the monorepo standardised on vitest; mocha + c8 were the last holdout in koenig/ after the TryGhost/Koenig repo was merged in. This unifies the test tooling across the remaining koenig packages.

What

Migrates the 10 remaining mocha packages onto vitest: kg-card-factory, kg-clean-basic-html, kg-converters, kg-default-cards, kg-default-nodes, kg-default-transforms, kg-html-to-lexical, kg-lexical-html-renderer, kg-markdown-html-renderer, kg-utils. (koenig-lexical and kg-unsplash-selector were already on vitest; kg-simplemde has no tests.)

Per package:

  • New vitest.config.ts (globals: true, node env, v8 coverage).
  • test:unit now runs vitest run --coverage; mocha/@types/mocha/c8/tsx removed for vitest + @vitest/coverage-v8.
  • The redundant pretest build step is dropped — the Nx test:unit target already dependsOn: build.

Scope: runner only (assertions unchanged)

This is intentionally a runner-only swap. Assertions stay on should/assert/sinon/chai, wired in via vitest setupFiles (the same should/sinon globals the mocha suite installed through each package's test/*/overrides.ts). Converting shouldexpect and sinonvi is a deliberate, separately-reviewable follow-up so the diff here stays reviewable and any assertion-semantics regressions land on a stable base.

Reviewer notes

  • Coverage gates preserved as vitest thresholds (old c8 --check-coverage = lines ≥ 90%; --100 = 100%). One deliberate exception: kg-default-transforms keeps its 100% gate on statements/functions/lines but leaves branches ungated — vitest's v8 provider counts two defensive $isTextNode branches in remove-at-link-nodes.ts that c8 scored as covered, so a 100% branch gate would fail a suite c8 passed. Documented in that config.
  • Mocha-isms fixed in test bodies (runner API, not assertions): beforebeforeAll; the mocha done callback → Promise (the repeated editorTest helper + direct done tests in kg-default-nodes); one empty describedescribe.todo.
  • Missing types added: @types/jsdom and @types/chai, so test:types passes. kg-default-nodes test-file type-checking is deliberately left src-only for now — its test files carry pre-existing @types/should typing gaps (.should.deepEqual, custom-assertion signatures) that the shouldexpect follow-up will clean up.

Verification

All 10 packages green locally: nx run-many -t test:unit (1,332 tests pass, 23 pre-existing pending), test:types, and lint (0 errors).

Shared config factory

The 10 per-package vitest.config.ts files were near-identical, so they now call a createKoenigVitestConfig factory in koenig/vitest.shared.ts — mirroring the createVitestConfig house pattern the React apps use (via admin-x-framework). The koenig libs are node/no-jsdom, so they get their own base rather than reusing the React one. Each package config is now a 3-6 line call passing only its setupFiles and coverage thresholds; no behaviour change.

no ref

- unifies the koenig test tooling with the rest of the monorepo, which
  standardised on vitest; mocha + c8 were the last holdout after the
  TryGhost/Koenig repo was merged into ghost/koenig
- migrates the 10 remaining mocha packages (kg-card-factory,
  kg-clean-basic-html, kg-converters, kg-default-cards, kg-default-nodes,
  kg-default-transforms, kg-html-to-lexical, kg-lexical-html-renderer,
  kg-markdown-html-renderer, kg-utils); koenig-lexical and
  kg-unsplash-selector were already on vitest
- runner-only swap: assertions stay on should/assert/sinon/chai for now,
  wired in via vitest setupFiles (the should/sinon globals the mocha suite
  installed through test/*/overrides.ts). Converting should->expect and
  sinon->vi is a deliberate, separately-reviewable follow-up
- each package gains a vitest.config.ts (globals, node env, v8 coverage);
  test:unit now runs `vitest run --coverage`; mocha/@types/mocha/c8/tsx
  dropped for vitest + @vitest/coverage-v8; the redundant pretest build is
  removed since the nx test:unit target already dependsOn build
- preserves the old `c8 --check-coverage` gates as vitest thresholds.
  kg-default-transforms keeps its 100% gate on statements/functions/lines
  but leaves branches ungated: vitest's v8 provider counts two defensive
  branches that c8 scored as covered, so a 100% branch gate would fail a
  suite c8 passed
- fixes mocha-isms vitest rejects (not assertion changes): before->beforeAll,
  the `done` callback -> Promise (the editorTest helper + direct done tests
  in kg-default-nodes), and an empty describe -> describe.todo
- adds the previously-missing @types/jsdom and @types/chai so the test
  type-checks pass; kg-default-nodes test-file type-checking stays src-only
  pending the should->expect follow-up that will clean up the should typings
@nx-cloud

nx-cloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit f873686

Command Status Duration Result
nx run @tryghost/admin-x-settings:test:acceptance ✅ Succeeded 11m 33s View ↗
nx run ghost:test:ci:integration ✅ Succeeded 2m 45s View ↗
nx run-many --target=build --projects=tag:publi... ✅ Succeeded 1s View ↗
nx run-many -t test:unit -p @tryghost/kg-card-f... ✅ Succeeded 6m 48s View ↗
nx run ghost:test:integration ✅ Succeeded 1m 40s View ↗
nx run @tryghost/admin:build ✅ Succeeded 3m 6s View ↗
nx run ghost-monorepo:lint:boundaries ✅ Succeeded 7s View ↗
nx run-many -t lint -p @tryghost/kg-card-factor... ✅ Succeeded 3m 23s View ↗
Additional runs (10) ✅ Succeeded ... View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-07-09 19:48:32 UTC

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1d6b81b5-151b-4b42-afab-7110b3392260

📥 Commits

Reviewing files that changed from the base of the PR and between 0071e99 and f873686.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • koenig/kg-clean-basic-html/package.json
  • koenig/kg-default-cards/package.json
  • koenig/kg-default-nodes/package.json
  • koenig/kg-default-nodes/test/vitest-globals.d.ts
  • koenig/kg-html-to-lexical/package.json
  • koenig/kg-lexical-html-renderer/package.json
  • pnpm-workspace.yaml
✅ Files skipped from review due to trivial changes (2)
  • koenig/kg-default-nodes/test/vitest-globals.d.ts
  • pnpm-workspace.yaml
🚧 Files skipped from review as they are similar to previous changes (5)
  • koenig/kg-default-cards/package.json
  • koenig/kg-default-nodes/package.json
  • koenig/kg-clean-basic-html/package.json
  • koenig/kg-lexical-html-renderer/package.json
  • koenig/kg-html-to-lexical/package.json

Walkthrough

This PR adds a shared Vitest configuration helper and updates multiple koenig packages to use it. Package test scripts and devDependencies were switched from Mocha/c8/tsx to Vitest-based type-checking and coverage runs, and test globals files now reference vitest/globals. In kg-default-nodes, the package config was updated and many tests were refactored from done callbacks to Promise-based helpers, with some setup hooks changed from before to beforeAll.

Possibly related PRs

  • TryGhost/Ghost#28778: Also updates Koenig test setup hooks from Mocha-style before(...) to Vitest-compatible beforeAll(...).

Suggested labels: migration

Suggested reviewers: EvanHahn, acburdine

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: migrating koenig kg-* packages from Mocha to Vitest.
Description check ✅ Passed The description directly matches the changeset and explains the Vitest migration and supporting updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch slars/koenig-mocha-vitest-dc7b84

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

no ref

- the 10 per-package vitest.config.ts files were near-identical; only the
  setup files and coverage thresholds varied
- adds koenig/vitest.shared.ts with a createKoenigVitestConfig factory,
  mirroring the createVitestConfig house pattern used by the React apps
  (admin-x-framework); the koenig libs are node/no-jsdom so they need their
  own base rather than reusing the React one
- each package config collapses to a 3-6 line call passing only its
  setupFiles and thresholds; no behaviour change, all suites still green

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
koenig/kg-default-nodes/test/nodes/at-link.test.ts (1)

16-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the duplicated editorTest helper to a shared utility.

The same editorTest function is copy-pasted across all node test files. A shared factory in test/test-utils/ that accepts the editor instance would eliminate this duplication:

// test/test-utils/editor-test.ts
import type {LexicalEditor} from 'lexical';

export const createEditorTest = (editor: LexicalEditor) =>
    (testFn: () => void) => () => new Promise<void>((resolve, reject) => {
        editor.update(() => {
            try {
                testFn();
                resolve();
            } catch (e) {
                reject(e);
            }
        });
    });

Each test file would then just call const editorTest = createEditorTest(editor); in its beforeAll hook. This is pre-existing duplication (not introduced by this PR), so defer if out of scope.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@koenig/kg-default-nodes/test/nodes/at-link.test.ts` around lines 16 - 25, The
duplicated editorTest helper should be centralized into a shared test utility
instead of being copy-pasted in each node test file. Move the logic into a
reusable factory such as createEditorTest under test/test-utils/ that accepts
the LexicalEditor instance, then update at-link.test.ts and the other node tests
to initialize editorTest from that shared helper in beforeAll. Keep the existing
behavior of wrapping editor.update and resolving/rejecting the Promise
unchanged.
koenig/kg-default-nodes/test/nodes/call-to-action.test.ts (1)

18-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider extracting the shared editorTest helper.

This identical Promise-wrapper editorTest helper is duplicated verbatim across the entire kg-default-nodes test suite (call-to-action, callout, codeblock, email-cta, email, image, tk, and more). Now that all these files share the same runner (Vitest), consolidating this into a single shared test-utils module would reduce duplication and centralize any future changes to the async test-completion mechanism.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@koenig/kg-default-nodes/test/nodes/call-to-action.test.ts` around lines 18 -
27, The Promise-wrapped editorTest helper is duplicated across multiple
kg-default-nodes tests, so consolidate it into a shared test-utils module.
Extract the common editor.update-based wrapper into one reusable helper and
update the call-to-action test (and the other suite files using the same
editorTest pattern) to import and use that shared helper. Keep the helper’s
behavior unchanged so Vitest async completion still works consistently
everywhere.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@koenig/kg-clean-basic-html/package.json`:
- Line 42: Update the `@types/jsdom` dependency in package.json to use the shared
catalog pin instead of an inline version. Add `@types/jsdom` to
pnpm-workspace.yaml and change this dependency entry to catalog: so the version
is centrally managed across the workspace.

In `@koenig/kg-default-cards/package.json`:
- Around line 44-55: The dev dependency list in package.json still pins
`@types/jsdom` inline while the shared workspace dependencies use catalog:
entries. Add `@types/jsdom` to the pnpm catalog in pnpm-workspace.yaml and update
the package.json entry in kg-default-cards to use catalog: consistently with the
other shared dev deps.

In `@koenig/kg-default-cards/vitest.config.ts`:
- Around line 1-18: The Vitest setup in defineConfig currently reports coverage
but no longer enforces the package’s minimum coverage gate. Update the coverage
config in vitest.config.ts by adding a coverage.thresholds block alongside the
existing reporter/include/all settings, so CI fails when coverage drops below
the expected floor. Keep the change scoped to the test.coverage object in the
default export.

In `@koenig/kg-default-nodes/vitest.config.ts`:
- Around line 5-10: The Vitest setup in vitest.config.ts enables globals, but
the package is missing typings for those global test APIs, so TypeScript and
IDEs won’t recognize describe/it/beforeEach in the test files. Add a
vitest-globals.d.ts for kg-default-nodes, or otherwise import the Vitest types
so the globals are declared for the test suite. Make sure the new typings are
included for test files since test:types only applies to src/**/*.

---

Nitpick comments:
In `@koenig/kg-default-nodes/test/nodes/at-link.test.ts`:
- Around line 16-25: The duplicated editorTest helper should be centralized into
a shared test utility instead of being copy-pasted in each node test file. Move
the logic into a reusable factory such as createEditorTest under
test/test-utils/ that accepts the LexicalEditor instance, then update
at-link.test.ts and the other node tests to initialize editorTest from that
shared helper in beforeAll. Keep the existing behavior of wrapping editor.update
and resolving/rejecting the Promise unchanged.

In `@koenig/kg-default-nodes/test/nodes/call-to-action.test.ts`:
- Around line 18-27: The Promise-wrapped editorTest helper is duplicated across
multiple kg-default-nodes tests, so consolidate it into a shared test-utils
module. Extract the common editor.update-based wrapper into one reusable helper
and update the call-to-action test (and the other suite files using the same
editorTest pattern) to import and use that shared helper. Keep the helper’s
behavior unchanged so Vitest async completion still works consistently
everywhere.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c5c5842-5733-4d16-9350-964d5acca7c4

📥 Commits

Reviewing files that changed from the base of the PR and between b966169 and 5461ad9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (58)
  • koenig/kg-card-factory/package.json
  • koenig/kg-card-factory/test/vitest-globals.d.ts
  • koenig/kg-card-factory/vitest.config.ts
  • koenig/kg-clean-basic-html/package.json
  • koenig/kg-clean-basic-html/test/clean-basic-html.test.ts
  • koenig/kg-clean-basic-html/test/vitest-globals.d.ts
  • koenig/kg-clean-basic-html/vitest.config.ts
  • koenig/kg-converters/package.json
  • koenig/kg-converters/test/vitest-globals.d.ts
  • koenig/kg-converters/vitest.config.ts
  • koenig/kg-default-cards/package.json
  • koenig/kg-default-cards/test/vitest-globals.d.ts
  • koenig/kg-default-cards/vitest.config.ts
  • koenig/kg-default-nodes/package.json
  • koenig/kg-default-nodes/test/generate-decorator-node.test.ts
  • koenig/kg-default-nodes/test/nodes/aside.test.ts
  • koenig/kg-default-nodes/test/nodes/at-link-search.test.ts
  • koenig/kg-default-nodes/test/nodes/at-link.test.ts
  • koenig/kg-default-nodes/test/nodes/audio.test.ts
  • koenig/kg-default-nodes/test/nodes/bookmark.test.ts
  • koenig/kg-default-nodes/test/nodes/button.test.ts
  • koenig/kg-default-nodes/test/nodes/call-to-action.test.ts
  • koenig/kg-default-nodes/test/nodes/callout.test.ts
  • koenig/kg-default-nodes/test/nodes/codeblock.test.ts
  • koenig/kg-default-nodes/test/nodes/email-cta.test.ts
  • koenig/kg-default-nodes/test/nodes/email.test.ts
  • koenig/kg-default-nodes/test/nodes/embed.test.ts
  • koenig/kg-default-nodes/test/nodes/file.test.ts
  • koenig/kg-default-nodes/test/nodes/gallery.test.ts
  • koenig/kg-default-nodes/test/nodes/header.test.ts
  • koenig/kg-default-nodes/test/nodes/horizontalrule.test.ts
  • koenig/kg-default-nodes/test/nodes/html.test.ts
  • koenig/kg-default-nodes/test/nodes/image.test.ts
  • koenig/kg-default-nodes/test/nodes/markdown.test.ts
  • koenig/kg-default-nodes/test/nodes/paywall.test.ts
  • koenig/kg-default-nodes/test/nodes/product.test.ts
  • koenig/kg-default-nodes/test/nodes/signup.test.ts
  • koenig/kg-default-nodes/test/nodes/tk.test.ts
  • koenig/kg-default-nodes/test/nodes/toggle.test.ts
  • koenig/kg-default-nodes/test/nodes/transistor.test.ts
  • koenig/kg-default-nodes/test/nodes/video.test.ts
  • koenig/kg-default-nodes/test/nodes/zwnj.test.ts
  • koenig/kg-default-nodes/test/serializers/linebreak.test.ts
  • koenig/kg-default-nodes/test/serializers/paragraph.test.ts
  • koenig/kg-default-nodes/test/utils/visibility.test.ts
  • koenig/kg-default-nodes/vitest.config.ts
  • koenig/kg-default-transforms/package.json
  • koenig/kg-default-transforms/vitest.config.ts
  • koenig/kg-html-to-lexical/package.json
  • koenig/kg-html-to-lexical/vitest.config.ts
  • koenig/kg-lexical-html-renderer/package.json
  • koenig/kg-lexical-html-renderer/vitest.config.ts
  • koenig/kg-markdown-html-renderer/package.json
  • koenig/kg-markdown-html-renderer/test/vitest-globals.d.ts
  • koenig/kg-markdown-html-renderer/vitest.config.ts
  • koenig/kg-utils/package.json
  • koenig/kg-utils/test/vitest-globals.d.ts
  • koenig/kg-utils/vitest.config.ts

Comment thread koenig/kg-clean-basic-html/package.json Outdated
Comment thread koenig/kg-default-cards/package.json Outdated
Comment thread koenig/kg-default-cards/vitest.config.ts Outdated
Comment thread koenig/kg-default-nodes/vitest.config.ts Outdated
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.04%. Comparing base (b23b4db) to head (f873686).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #29216   +/-   ##
=======================================
  Coverage   74.04%   74.04%           
=======================================
  Files        1570     1570           
  Lines      136624   136625    +1     
  Branches    16520    16523    +3     
=======================================
+ Hits       101159   101163    +4     
- Misses      34429    34456   +27     
+ Partials     1036     1006   -30     
Flag Coverage Δ
admin-tests 55.16% <ø> (-0.03%) ⬇️
e2e-tests 76.15% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…globals

no ref

- addresses CodeRabbit review on the vitest migration PR
- @types/jsdom was pinned inline (28.0.3) in 5 koenig packages; the catalog
  already centralises the other shared @types (node/react/jest/...), so this
  moves it there too and points the koenig consumers at catalog: to kill the
  duplication. ghost/core keeps its existing inline pin (same version, out of
  this PR's scope)
- re-adds koenig/kg-default-nodes/test/vitest-globals.d.ts so editors/tsc
  recognise the describe/it/beforeEach globals in its test files (lost when
  @types/mocha was removed). test:types stays src-only there: pulling the
  test files into the type-check resurfaces pre-existing @types/should gaps
  (.should.deepEqual etc.) that belong to the should->expect follow-up
@9larsons
9larsons enabled auto-merge (squash) July 9, 2026 19:44
@9larsons
9larsons merged commit f30b7b3 into main Jul 9, 2026
51 checks passed
@9larsons
9larsons deleted the slars/koenig-mocha-vitest-dc7b84 branch July 9, 2026 19:52
9larsons added a commit that referenced this pull request Jul 9, 2026
no ref

Follow-up to the mocha→vitest migration (#29216). After that landed, the koenig `kg-*` suites were running on vitest but still using `should`/`sinon`-style assertions, wired in through setup-file globals. This converts them to native vitest `expect` so the koenig tests match the rest of the monorepo's assertion style, and lets us delete the leftover scaffolding.
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