# Migrate Jest to Vitest in an Electron App

Yes. An agent moves an Electron app's Jest suite to Vitest on its electron-vite config, ports the mocks and docblocks, and opens a pull request with tests green.

[Run this on Rush](https://cloud.getrush.ai/run/migrate-jest-to-vitest/electron/), in a Project that has your repository.

## What the agent does

The agent reads the test setup before it changes it, because in an Electron project the test runner is wired into the build. In an app shaped like electron-react-boilerplate/electron-react-boilerplate, `package.json` runs `jest` as `test`, builds with `electron-vite build` and starts with `electron-vite dev --watch`; `jest.config.js` sets `testEnvironment` to `jsdom`, maps `.svg?react` imports to `.erb/mocks/svgComponentMock.js`, image and font extensions to `.erb/mocks/fileMock.js` and stylesheets to `identity-obj-proxy`, adds `release/app/node_modules` and `src` to `moduleDirectories`, and transforms TypeScript with `ts-jest` resolved from `.erb/tooling`, a separate locked package that holds TypeScript 6 because the root project compiles with TypeScript 7, whose compiler API ts-jest cannot use. `setupFiles` points at `.erb/scripts/check-build-exists.ts`, which throws unless `release/app/dist/main/main.js` and `renderer.js` exist, and polyfills `TextEncoder` for JSDOM.

There are two tests, and they use Jest in different ways. `src/__tests__/App.test.tsx` renders `src/renderer/App.tsx` with `@testing-library/react` and `@testing-library/jest-dom` matchers; `src/__tests__/updates.test.ts` carries a `/** @jest-environment node */` docblock, calls `jest.mock` on `electron`, `electron-updater` and `electron-log`, uses `jest.resetModules` in `beforeEach` and `require` inside each test to get fresh module state, and asserts that `startAutoUpdates` in `src/main/updates.ts` contacts the updater once in a packaged app and never in development. The agent maps each of those onto Vitest: `vi.mock` with factories, `vi.resetModules`, a `// @vitest-environment node` comment in place of the docblock, and `@testing-library/jest-dom/vitest` for the matchers.

Then it writes `vitest.config.ts` from `electron.vite.config.ts`, not from scratch: the renderer section already has the `react()` and `svgr()` plugins, which make the `.svg?react` mock unnecessary, and the `release/app/package.json` dependencies it externalizes. The Vitest config sets `environment` to `jsdom`, `setupFiles` to a ported `check-build-exists` and the matcher import, `css` handling in place of `identity-obj-proxy`, an alias for asset files in place of `fileMock.js`, and `resolve.modules` equivalents for `release/app/node_modules`. It removes `jest`, `jest-environment-jsdom`, `@types/jest` and `identity-obj-proxy` from the root `devDependencies`, removes `ts-jest` and `eslint-plugin-jest` from `.erb/tooling/package.json` and runs `npm --prefix .erb/tooling install` there as its README asks, swaps `"types": ["node", "jest"]` in `tsconfig.json` for `vitest/globals` or explicit imports, and replaces the `eslint-plugin-jest` block in `eslint.config.js` with `eslint-plugin-vitest` rules for `src/**/__tests__/**`.

It runs the sequence `.github/workflows/test.yml` runs: `npm ci`, `npm run package` (which builds and so creates the files the setup script checks for), `npm run lint`, `npm exec tsc -- --noEmit` and `npm test`, now `vitest run`. On Linux it also runs `xvfb-run --auto-servernum npm run test:smoke`, the Electron startup check, to prove the main process bundle still starts. The pull request changes the configs, the two tests, both `package.json` files and their lockfiles, and nothing in `src/main` or `src/renderer` beyond test files.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Migrate this Electron project from Jest to Vitest. Read package.json, jest.config.js, electron.vite.config.ts, tsconfig.json, eslint.config.js, .erb/tooling/package.json and its README, .erb/scripts/check-build-exists.ts and both files under src/__tests__ first. Write vitest.config.ts that reuses the renderer plugins from electron.vite.config.ts, keeps the jsdom environment, the build-exists setup check, the module directories and the stylesheet and asset handling the Jest config had. Port App.test.tsx to @testing-library/jest-dom/vitest matchers and updates.test.ts to vi.mock, vi.resetModules and a vitest-environment node comment, keeping every assertion. Replace the test script with vitest run, remove jest, jest-environment-jsdom, @types/jest and identity-obj-proxy from the root, remove ts-jest and eslint-plugin-jest from .erb/tooling and reinstall it, update the tsconfig types and the ESLint test block. Run npm ci, npm run package, npm run lint, npm exec tsc -- --noEmit, npm test and, under xvfb-run, npm run test:smoke, and fix what fails. Open a pull request that lists each Jest feature and its Vitest replacement and quotes the vitest summary. Do not change application code under src/main or src/renderer.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request on a new branch. It adds `vitest.config.ts`, deletes `jest.config.js`, rewrites the two test files, edits `package.json`, `package-lock.json`, `.erb/tooling/package.json` and its lockfile, `tsconfig.json` and `eslint.config.js`, and leaves `src/main/` and `src/renderer/` untouched. The description is a table of Jest feature to Vitest replacement (`jest.mock` to `vi.mock`, the environment docblock to the environment comment, `moduleNameMapper` to aliases and plugins, `ts-jest` to Vite's own TypeScript handling) and quotes the `vitest run` summary.

The session read names the repository and the pull request URL, and the transcript keeps each command with its output: the package build, lint, type check, the Vitest run and the smoke test. A reviewer can see the first Vitest run if a mock behaved differently from its Jest version and what changed to fix it.

Before merging, a reviewer checks that the `updates.test.ts` assertions still cover the packaged and development branches of `startAutoUpdates`, that the build-exists check still runs before the suite, and that `.erb/tooling` no longer carries test tooling it does not need.

## Run it on every repository still on Jest

Start one run per repository from a terminal or a script with this body and the repository in `resources`, with a `request_id`, one UUID kept per repository, so a retry returns the same run. The pull request URL arrives on the session, or in the `run.completed` webhook when the key has a `webhook_url`.

The sandbox parks after the turn with its `node_modules` and its built `release/app/dist`, so a follow-up such as "also move the smoke test under Vitest" continues in the same clone without a second `npm ci`.

## Why run it on Rush?

This migration touches two package manifests, a tooling package pinned to an older TypeScript, the build config and the ESLint config, and the only way to know it worked is to run the full `test.yml` sequence including `npm run package` and an Electron startup under a virtual display. On Rush that runs in the sandbox with every command's output kept, so the pull request can quote the Vitest summary and the smoke test from a run a reviewer can read.

Several Electron apps built from the same boilerplate can be migrated at the same time, each in its own sandbox, and each pull request carries the proof for that repository rather than for a template.

## Frequently asked questions

### Does it keep the .erb/tooling package?

Yes. The README there explains it exists because ts-node, ts-jest and typescript-eslint need TypeScript 6 while the root compiles with TypeScript 7. The agent removes only `ts-jest` and `eslint-plugin-jest` from it and leaves `ts-node` and the ESLint parser, which `.erb/scripts/register.cjs` and `eslint.config.js` still load.

### What happens to the /** @jest-environment node */ docblock?

It becomes a `// @vitest-environment node` comment at the top of `updates.test.ts`, so that test keeps running in Node while `App.test.tsx` runs in jsdom from the config default.

### Will it change how the renderer is built?

No. `electron.vite.config.ts` stays as it is; `vitest.config.ts` imports the renderer plugins from it so the test build and the real build agree on React and SVG handling.

### Does it run the Electron smoke test?

Yes, under `xvfb-run` as the Linux job in `test.yml` does, because the migration edits `package.json` and the tooling package that `.erb/scripts/register.cjs` depends on, and a broken main bundle would show up there and nowhere else.
