Skip to content

Replace Karma and Mocha with Vitest - #1773

Draft
matthew-white wants to merge 4 commits into
masterfrom
vitest
Draft

Replace Karma and Mocha with Vitest#1773
matthew-white wants to merge 4 commits into
masterfrom
vitest

Conversation

@matthew-white

@matthew-white matthew-white commented Aug 15, 2026

Copy link
Copy Markdown
Member

A while ago, I worked on replacing Karma (and Mocha) with Vitest (getodk/central#1267). I never finished that work, but I wanted to share what I have so far.

State of the PR

I had these changes running locally at some point. However, that was well over a year ago. Importantly, it predated our new monorepo setup, which means that related configuration has changed since I last worked on it.

This PR is not expected to work at the moment. I'll add more details below as to why. Note that I didn't fully review the changes here. This PR is a work-in-progress.

I don't remember whether 100% of tests were passing back when I was working on this. It's very possible that they were. I seem to remember that most of them were at least. Locally, I don't see any changes to individual tests.

Overall approach

The overall approach I took was to use Vitest browser mode. That's because it seemed most similar to Karma.

At the time, Vitest browser mode was marked as experimental. I'm pretty sure there have been breaking changes to it since I last worked on it.

Required PRs

My original local commit was on a long branch (now quite outdated) that made a series of changes:

  • Introducing Vite for the first time
    • By now, we've been using Vite for a while for local development and the production build. The changes for Vite were extracted to a separate PR.
  • Replacing Should.js with Chai
    • IIRC this was needed for Vitest. Something related to the use of ES Modules in test/index.js and the fact that Should.js isn't an ES Module. But I also thought it was a good change by itself, so I extracted it to its own PR. That PR was merged a while ago.

I've rebased my local commit and also extracted other work that was on the same long branch. I've put that other work in separate PRs so that this core PR can be as slim as possible. The following PRs are required in order for the changes in this PR to work:

Follow-up work

I want this PR to be self-contained, i.e., to contain the minimal changes needed to move to Vitest. However, I had other changes on my local branch that can be considered in separate PRs. At least one of those PRs requires this PR to be merged first.

Package changes

I had local changes to package.json. However, that file has changed a lot in the repo since I last worked on this, so I decided not to include those changes in the PR. The packages I used back then are probably outdated by now anyway. But here's the list of packages I added locally:

"vitest": "^1.6.0",
"@vitest/browser": "^1.6.0",
"webdriverio": "^8.36.1",

I also removed packages related to Karma, Mocha, Vue CLI, and webpack. With this PR, none of those frameworks will be needed for Central Frontend anymore.

More details below

I'm going to leave comments below on particular files and lines in order to explain my approach and surface current open questions.

@changeset-bot

This comment was marked as resolved.

@matthew-white matthew-white left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Adding questions and comments

@matthew-white matthew-white Aug 15, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

My local commit also deleted karma.conf.js. One thing I'd want to do is review whether there have been any changes to karma.conf.js since the last time I worked on this. Maybe there's configuration in karma.conf.js that we'd want to preserve via parallel configuration in this file.

import { fileURLToPath } from 'node:url';
import { defineConfig, mergeConfig } from 'vitest/config';

import viteConfig from './vite.config';

@matthew-white matthew-white Aug 15, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

My work on this predated the monorepo setup, which is why this file expects vite.config.js to exist in this directory. Not sure whether the new vitest.config.js here will also be used by the Web Forms app. If so, it should probably go somewhere else (probably the apps/ directory or the root directory of the repository).

Comment thread apps/central/test/run.sh
trap 'rm -- ../../public/index.html "$output"' EXIT

NODE_ENV="test" karma start karma.conf.js | tee "$output"
vitest run "$@" | tee "$output"

@matthew-white matthew-white Aug 15, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think "$@" is there so that devs can specify a filter. See the related changes to CONTRIBUTING.md about filtering tests.

Comment thread apps/central/test/run.sh
Comment on lines -17 to -20
/WARN LOG:/ { ++warnings; print "WARNING: " $0 }
/stderr/ { ++warnings; print "WARNING: " $0 }
/ERROR LOG:/ { ++warnings; print "WARNING: " $0 }
/Module Warning/ { ++warnings; print "WARNING: " $0 }
/WARN \[web-server\]:/ { ++warnings; print "WARNING: " $0 }

@matthew-white matthew-white Aug 15, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I understand the removal of WARN [web-server]:, since I'm pretty sure that's from Karma. But I'm not sure why I replaced WARN LOG: with stderr. I'm also not sure what's going on with ERROR LOG:. Should that line be removed now, similar to WARN LOG:?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

In my local commit, I moved this file to test/setup/index.js. I didn't do so here because I wanted to make the diff clearer.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

When I was working on this, there was something really weird going on with hooks that I'm not sure I fully understood. IIRC individual test files had access to beforeAll, afterAll, beforeEach, and afterEach, but test/index.js didn't. There was some split in the environment available to test/index.js and the environment available to individual tests. I added these hook mechanisms here so that test/index.js could continue to define global hooks. That setup worked and was running for me locally, but it definitely felt like a workaround.

Comment on lines 15 to -18
window.should = should();
window.expect = expect;

@matthew-white matthew-white Aug 15, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm not sure why this PR removes expect from window, but keeps should. 🤔 But it's what I have in my local commit. Do we maybe import expect wherever we use it in tests (such that it doesn't need to be global)?

configurable: true
});

Error.stackTraceLimit = 40;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm not sure why this was added. It feels like something that should go in its own PR. Maybe we can just remove it if I don't remember why it was added. Or maybe there's no harm in adding it.

Comment on lines +11 to +12
`npm run test:debug` opens a browser window. This file styles the page to
improve its look for when `npm run test:debug` is run.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

npm run test:debug is added in #1772.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Maybe I could further reduce the size of this PR by moving these comments to #1772, along with the style changes that these comments mention that are further below in the file.

// given that we also specify them under `browser` above.
fileParallelism: false,
isolate: false,
sequence: { hooks: 'list' },

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I have a vague memory that this hooks configuration was an important step in getting things to run.

Comment thread apps/central/test/run.sh

@matthew-white matthew-white Aug 15, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

My local branch also made this change to package.json, which I don't really understand:

diff --git a/package.json b/package.json
index 715a4dec2..be759c7e7 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,8 @@
     "build:dev": "nf start build,nginx",
     "lint": "eslint --max-warnings 0 --cache --ext .js,.vue,.cjs src/ bin/ test/ *.js *.cjs",
     "lint:fix": "eslint --max-warnings 0 --fix --cache --ext .js,.vue,.cjs src/ bin/ test/ *.js *.cjs",
-    "test": "./test/run.sh"
+    "test": "vitest",
+    "test:run": "./test/run.sh"
   },
   "volta": {
     "node": "20.10.0"

Most everything still refers to npm run test, not npm run test:run, though my local branch does change CI to use test:run. But test/run.sh does some good things, both in CI and local development, e.g., it checks for Vue warnings and Sass warnings. I'm not sure why we would want to run vitest without running test/run.sh. Maybe it has something to do with filtering tests?

@matthew-white matthew-white Aug 16, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh maybe it's the difference between vitest and vitest run. Like does vitest by itself run continuously?

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