Skip to content

Chore: [AEA-6542] - Migrate to vitest#4521

Merged
wildjames merged 23 commits intomasterfrom
aea-6452-vitest
Apr 14, 2026
Merged

Chore: [AEA-6542] - Migrate to vitest#4521
wildjames merged 23 commits intomasterfrom
aea-6452-vitest

Conversation

@wildjames
Copy link
Copy Markdown
Contributor

Summary

  • 🤖 Operational or Infrastructure Change

Details

We want to move from Jest to Vitest. This will touch a LOT of files, but largely should be minor changes. However, some parts of our test suites are jest-specific, and they're going to need to be refactored to be vitest-native.

Before merging, we need to verify that coverage is the same before and after this migration!

Copilot AI review requested due to automatic review settings April 1, 2026 13:19
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

This PR is linked to a ticket in an NHS Digital JIRA Project. Here's a handy link to the ticket:

AEA-6542

const directCrl = "http://crl.nhs.uk/int/1d/crlc3.crl"
const directArl = "http://crl.nhs.uk/int/1d/arlc3.crl"
const mockCrl = "https://example.com/ca.crl"
const validUrls = new RegExp(`(${ptlCrl}|${mockCrl}|${directCrl})`)

Check failure

Code scanning / CodeQL

Incomplete regular expression for hostnames

This string, which is used as a regular expression [here](1), has an unescaped '.' before 'nhs.uk/int/1d/arlc3', so it might match more hosts than expected.

Copilot Autofix

AI 17 days ago

In general, whenever you embed a literal hostname or full URL into a regular expression, you should escape regex metacharacters (especially .) so the regex matches the literal string rather than arbitrary characters. The simplest way in TypeScript is to either (a) manually escape the dots in the literal string (example\.com), or (b) run the literal string through a small helper like escapeRegExp before passing it to new RegExp. This avoids accidental over-broad matches.

For this file, the best fix without changing external behaviour is to keep the URL constants (ptlCrl, ptlArl, directCrl, directArl, mockCrl) as plain strings and only escape them at the point where they are interpolated into RegExp patterns. That means:

  • Introduce a local escapeRegExp helper in this test file that takes a string and returns it with all regex metacharacters escaped.
  • Use this helper when building validUrls and the ARL regex so that the underlying axios-mock-adapter patterns match the exact URLs and nothing more.

Concretely, in packages/coordinator/tests/services/verification/certificate-revocation.spec.ts:

  • Add a small escapeRegExp function near the top of the file (after the constants or just before the first use).
  • Change const validUrls = new RegExp(\(${ptlCrl}|${mockCrl}|${directCrl})`)to wrap each URL inescapeRegExp(...)`.
  • Change mock.onAny(new RegExp(\(${ptlArl}|${directArl})`))` similarly.
    No new imports are needed; the helper can be implemented with simple string replacement.
Suggested changeset 1
packages/coordinator/tests/services/verification/certificate-revocation.spec.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/coordinator/tests/services/verification/certificate-revocation.spec.ts b/packages/coordinator/tests/services/verification/certificate-revocation.spec.ts
--- a/packages/coordinator/tests/services/verification/certificate-revocation.spec.ts
+++ b/packages/coordinator/tests/services/verification/certificate-revocation.spec.ts
@@ -66,12 +66,15 @@
 const directCrl = "http://crl.nhs.uk/int/1d/crlc3.crl"
 const directArl = "http://crl.nhs.uk/int/1d/arlc3.crl"
 const mockCrl = "https://example.com/ca.crl"
-const validUrls = new RegExp(`(${ptlCrl}|${mockCrl}|${directCrl})`)
 
+const escapeRegExp = (value: string): string => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
+
+const validUrls = new RegExp(`(${escapeRegExp(ptlCrl)}|${escapeRegExp(mockCrl)}|${escapeRegExp(directCrl)})`)
+
 mock.onAny(validUrls).reply(200, TestCertificates.berRevocationList)
 
 // See packages/coordinator/tests/resources/certificates/static/README.md
-mock.onAny(new RegExp(`(${ptlArl}|${directArl})`)).reply(200, TestCertificates.staticCaCerts.caArl)
+mock.onAny(new RegExp(`(${escapeRegExp(ptlArl)}|${escapeRegExp(directArl)})`)).reply(200, TestCertificates.staticCaCerts.caArl)
 
 mock.onAny("https://egress.ptl.api.platform.nhs.uk:700/mock/crl404.crl").reply(404)
 
EOF
@@ -66,12 +66,15 @@
const directCrl = "http://crl.nhs.uk/int/1d/crlc3.crl"
const directArl = "http://crl.nhs.uk/int/1d/arlc3.crl"
const mockCrl = "https://example.com/ca.crl"
const validUrls = new RegExp(`(${ptlCrl}|${mockCrl}|${directCrl})`)

const escapeRegExp = (value: string): string => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")

const validUrls = new RegExp(`(${escapeRegExp(ptlCrl)}|${escapeRegExp(mockCrl)}|${escapeRegExp(directCrl)})`)

mock.onAny(validUrls).reply(200, TestCertificates.berRevocationList)

// See packages/coordinator/tests/resources/certificates/static/README.md
mock.onAny(new RegExp(`(${ptlArl}|${directArl})`)).reply(200, TestCertificates.staticCaCerts.caArl)
mock.onAny(new RegExp(`(${escapeRegExp(ptlArl)}|${escapeRegExp(directArl)})`)).reply(200, TestCertificates.staticCaCerts.caArl)

mock.onAny("https://egress.ptl.api.platform.nhs.uk:700/mock/crl404.crl").reply(404)

Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Migrates the repository’s test runner from Jest to Vitest across multiple packages, updating configs, scripts, and test suites to be Vitest-compatible while preserving existing test behavior and reporting/coverage outputs.

Changes:

  • Adds root Vitest config + workspace project configs and updates package-level test scripts to run Vitest with coverage and JUnit output.
  • Refactors Jest-specific tests/mocks/snapshots to Vitest equivalents (vi, Vitest snapshots, Vitest lifecycle APIs).
  • Removes Jest configuration files and Jest-only utilities across packages (and updates docs/editor config accordingly).

Reviewed changes

Copilot reviewed 133 out of 139 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
vitest.workspace.ts Adds workspace project definitions for package-level Vitest runs.
vitest.config.ts Adds shared Vitest configuration (reporters + coverage).
sonar-project.properties Updates coverage exclusions from Jest to Vitest config.
README.md Updates repo package descriptions (BDD suite wording).
packages/tool/site/server/package.json Switches server tests from Jest to Vitest run + JUnit output.
packages/tool/site/client/tests/vitest.setup.ts Adds client setup file placeholder for Vitest.
packages/tool/site/client/tests/pages/viewPrescriptionPage.spec.tsx Updates client test to be more async-safe under Vitest.
packages/tool/site/client/tests/pages/signPage.spec.tsx Migrates mocks/spies from Jest to Vitest for Sign page tests.
packages/tool/site/client/tests/pages/prescriptionSearchPage.spec.tsx Migrates mocks/spies from Jest to Vitest for Search page tests.
packages/tool/site/client/tests/pages/loginPage.spec.tsx Converts navigation mock to Vitest.
packages/tool/site/client/tests/pages/dispensePage.spec.tsx Updates async mocking and renders for Vitest.
packages/tool/site/client/tests/pages/claimPage.spec.tsx Updates async mocking and renders for Vitest.
packages/tool/site/client/tests/pages/snapshots/withdrawPage.spec.tsx.snap Converts Jest snapshot format to Vitest snapshot format.
packages/tool/site/client/tests/pages/snapshots/validatePage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/signPage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/sendPage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/returnPage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/releasePage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/myPrescriptionsPage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/logoutPage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/loginPage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/homePage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/doseToTextPage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/dispensePage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/configPage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/pages/snapshots/claimPage.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/selectField.spec.tsx Replaces jest.fn with vi.fn and formatting tweaks.
packages/tool/site/client/tests/components/release/releaseForm.spec.tsx Removes Jest globals import and swaps to vi.fn.
packages/tool/site/client/tests/components/release/snapshots/releaseForm.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/prescription-summary/snapshots/prescriptionSummaryViewEditable.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/prescription-summary/snapshots/prescriptionLevelDetail.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/prescription-summary/snapshots/practitionerRoleSummaryList.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/prescription-summary/snapshots/patientSummaryList.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/prescription-summary/snapshots/medicationSummary.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/pageHeader.spec.tsx Removes Jest globals import and normalizes JSX formatting.
packages/tool/site/client/tests/components/longRunningTask.spec.tsx Migrates mocks from Jest to Vitest.
packages/tool/site/client/tests/components/dispense/dispenseForm.spec.tsx Removes Jest globals import and swaps to vi.fn.
packages/tool/site/client/tests/components/dispense/createDispenseNotification.spec.ts Replaces Jest mocking/spying with Vitest equivalents.
packages/tool/site/client/tests/components/dispense/snapshots/dispenseForm.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/dispense/snapshots/createDispenseNotification.spec.ts.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/claim/claimForm.spec.tsx Migrates submit mocking to vi.fn and JSX formatting.
packages/tool/site/client/tests/components/claim/snapshots/productSummaryList.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/claim/snapshots/createDispenseClaim.spec.ts.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/claim/snapshots/claimForm.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/snapshots/sorter.spec.tsx.snap Updates snapshot names/format for Vitest.
packages/tool/site/client/tests/components/snapshots/selectField.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/snapshots/pageHeader.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/tests/components/snapshots/longRunningTask.spec.tsx.snap Converts Jest snapshot header to Vitest snapshot header.
packages/tool/site/client/package.json Updates test scripts to Vitest and removes Jest jsdom env dependency.
packages/tool/site/client/jest.config.js Removes Jest config (client package).
packages/tool/e2e-tests/seleniumEnvironment.ts Removes Jest custom environment (selenium).
packages/tool/e2e-tests/sandbox.test.ts Migrates global imports/hooks from Jest to Vitest.
packages/tool/e2e-tests/package.json Updates selenium e2e scripts to Vitest and removes jest-junit config block.
packages/tool/e2e-tests/live.test.ts Migrates global imports/hooks from Jest to Vitest.
packages/tool/e2e-tests/jest.setup.tsx Removes Jest retryTimes setup.
packages/tool/e2e-tests/jest.config.js Removes Jest config for selenium e2e tests.
packages/models/package.json Switches models tests from Jest to Vitest + coverage/JUnit.
packages/models/jest.config.js Removes Jest config for models package.
packages/models/examples/cases/task-release-case.ts Removes toJestCase helper now that tests consume case objects.
packages/models/examples/cases/task-case.ts Removes toJestCase helper now that tests consume case objects.
packages/models/examples/cases/process-case.ts Removes Jest-specific case tuple helpers.
packages/models/examples/cases/prepare-case.ts Removes toJestCase helper now that tests consume case objects.
packages/models/examples/cases/convert-case.ts Removes Jest-specific error tuple helper (keeps other helpers).
packages/models/examples/cases/claim-case.ts Removes toJestCase helper now that tests consume case objects.
packages/fhir-schema-generation/tsconfig.json Replaces Jest types with Vitest globals types.
packages/fhir-schema-generation/tests/vitest.setup.ts Adds Vitest setup (currently a Jest alias shim).
packages/fhir-schema-generation/tests/fetch-fhir.test.ts Refactors mocks/spies from Jest to Vitest.
packages/fhir-schema-generation/package.json Swaps Jest tooling for Vitest + coverage provider.
packages/fhir-schema-generation/jest.config.ts Removes Jest config for schema generation package.
packages/e2e-tests/vitest/setEnvVars.js Adds Vitest setup env var script for pact e2e package.
packages/e2e-tests/specs/sandbox/task.sucesses.spec.ts Refactors test.each inputs to consume case objects (Vitest).
packages/e2e-tests/specs/sandbox/process.successes.spec.ts Refactors test.each inputs to consume case objects (Vitest).
packages/e2e-tests/specs/sandbox/prepare.successes.spec.ts Refactors test.each inputs to consume case objects (Vitest).
packages/e2e-tests/specs/sandbox/prepare.failures.spec.ts Refactors test.each inputs to consume case objects (Vitest).
packages/e2e-tests/specs/sandbox/claim.successes.spec.ts Refactors test.each inputs to consume case objects (Vitest).
packages/e2e-tests/specs/live/task.successes.ts Refactors test.each inputs to consume case objects (Vitest).
packages/e2e-tests/specs/live/process.successes.ts Refactors test.each inputs to consume case objects (Vitest).
packages/e2e-tests/specs/live/process.failures.spec.ts Updates indexing logic to object-based cases for Vitest.
packages/e2e-tests/specs/live/prepare.successes.spec.ts Refactors test.each inputs to consume case objects (Vitest).
packages/e2e-tests/specs/live/claim.successes.ts Refactors test.each inputs to consume case objects (Vitest).
packages/e2e-tests/resources/test-resources.ts Stops mapping to Jest tuples; exports case objects directly.
packages/e2e-tests/README.md Updates docs to reference Vitest instead of Jest.
packages/e2e-tests/package.json Switches pact e2e scripts from Jest to Vitest (+ cache clear).
packages/e2e-tests/jest.config.js Removes Jest config for pact e2e package.
packages/coordinator/vitest/setEnvVars.js Adds Vitest env var injection script (replacing Jest setupFiles).
packages/coordinator/tsconfig.json Adds Vitest globals types for test compilation + formatting.
packages/coordinator/tsconfig-build.json Removes Jest types from build config.
packages/coordinator/tests/vitest.setup.ts Migrates custom matcher registration to Vitest expect.
packages/coordinator/tests/vitest.d.ts Adds Vitest typing augmentation for custom matcher.
packages/coordinator/tests/services/verification/signature-verification.spec.ts Migrates spy from Jest to Vitest.
packages/coordinator/tests/services/verification/certificate-revocation.spec.ts Converts mocks/spies/reset APIs from Jest to Vitest; adjusts URL assertions.
packages/coordinator/tests/services/validation/task-validator.spec.ts Migrates console spy from Jest to Vitest.
packages/coordinator/tests/services/validation/parameters-validator.spec.ts Migrates mock clearing from Jest to Vitest.
packages/coordinator/tests/services/validation/claim-validator.spec.ts Migrates logger spy from Jest to Vitest.
packages/coordinator/tests/services/validation/bundle-validator.spec.ts Migrates logger spy from Jest to Vitest.
packages/coordinator/tests/services/translation/response/spine-response-handler.spec.ts Replaces Jest mocks with vi.fn and formatting fixes.
packages/coordinator/tests/services/translation/response/release/release-response.spec.ts Converts module mocking to vi.mock + vi.importActual.
packages/coordinator/tests/services/translation/response/index.spec.ts Migrates logger spy from Jest to Vitest.
packages/coordinator/tests/services/translation/request/task.spec.ts Converts module mocking from Jest to Vitest.
packages/coordinator/tests/services/translation/request/signing.spec.ts Replaces Jest moment mocking with deterministic moment.now override.
packages/coordinator/tests/services/translation/request/prescribe/parent-prescription.spec.ts Replaces Jest moment mocking with deterministic moment.now override.
packages/coordinator/tests/services/translation/request/practitioner.spec.ts Replaces Jest moment mocking with deterministic moment.now override + formatting.
packages/coordinator/tests/services/translation/request/index.spec.ts Replaces Jest moment mocking with deterministic moment.now override + hoisted state.
packages/coordinator/tests/services/translation/request/dispense/release.spec.ts Converts module mocking from Jest to Vitest.
packages/coordinator/tests/services/translation/request/dispense/dispense-notification.spec.ts Converts multiple Jest mocks to vi.fn + moment.now override.
packages/coordinator/tests/services/translation/request/dispense/dispense-claim.spec.ts Converts mocks to Vitest and replaces moment mocking approach.
packages/coordinator/tests/services/translation/request/cancel/cancellation.spec.ts Replaces Jest moment mocking with deterministic moment.now override.
packages/coordinator/tests/services/translation/request/agent-person.spec.ts Converts module mocking from Jest to Vitest.
packages/coordinator/tests/services/translation/convert.successes.spec.ts Migrates fake timers/system time from Jest to Vitest.
packages/coordinator/tests/services/translation/convert.failures.spec.ts Updates test.each inputs to object-based cases.
packages/coordinator/tests/services/translation/common/dosage-instructions.spec.ts Replaces Jest logger mock creation with a manual vi.fn logger stub.
packages/coordinator/tests/services/communication/spine-communication.spec.ts Removes Jest import and migrates spies to Vitest.
packages/coordinator/tests/routes/util.spec.ts Migrates module mocking + logger stubs to Vitest; formatting fixes.
packages/coordinator/tests/resources/test-resources.ts Stops mapping error cases to Jest tuples.
packages/coordinator/tests/matchers/toContainObject.ts Updates custom matcher typings for Vitest.
packages/coordinator/tests/logging/helpers.ts Removes Jest-specific return type annotation.
packages/coordinator/tests/jest.d.ts Removes Jest matcher type augmentation.
packages/coordinator/tests/Factory/dispenseProposalReturn.spec.ts Migrates spies from Jest to Vitest.
packages/coordinator/tests/debug/conversions.spec.ts Migrates fake timers/system time from Jest to Vitest.
packages/coordinator/tests/app.test.ts Migrates module mocking helpers and spies to Vitest.
packages/coordinator/package.json Updates coordinator test scripts to Vitest; removes Jest-only dependency.
packages/coordinator/jest.config.js Removes Jest config for coordinator package.
packages/cdk/tests/cdk-nag.test.ts Switches test imports from Jest globals to Vitest.
packages/cdk/package.json Updates CDK package test script to Vitest + coverage/JUnit.
packages/cdk/jest.debug.confi.ts Removes Jest debug config file.
packages/cdk/jest.config.ts Removes Jest config for CDK package.
packages/bdd-tests/README.md Updates BDD docs title to remove Jest-Cucumber naming.
packages/bdd-tests/package.json Switches BDD scripts from Jest to cucumber-js directly.
packages/bdd-tests/jest.config.json Removes Jest config for BDD package.
packages/bdd-tests/.gitignore Removes Jest artifact ignore entry.
package.json Adds root Vitest runner and updates devDependencies accordingly.
eslint.config.mjs Adds Vitest globals to ESLint config.
cdk.json Updates exclusion patterns from Jest config to Vitest config/workspace.
.vscode/launch.json Renames debug config to Vitest and updates runtime args/version.
.vscode/extensions.json Replaces Jest extension recommendation with Vitest explorer.
.vscode/electronic-prescription-service-api.code-workspace Removes Jest workspace settings; recommends Vitest explorer.
.devcontainer/devcontainer.json Replaces Jest extension with Vitest explorer in devcontainer.
Comments suppressed due to low confidence (1)

packages/tool/e2e-tests/sandbox.test.ts:67

  • hasTestFailures is always defined as false in beforeEach and never set to true anymore (the old Jest custom environment that flipped it on failure was removed). As a result, failure screenshots will never be captured. In Vitest, use the afterEach hook context (e.g. afterEach(({ task }) => task.result?.state === 'fail') or similar) to detect failures instead of relying on a global flag.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/tool/site/client/tests/pages/signPage.spec.tsx
Comment thread packages/fhir-schema-generation/tests/vitest.setup.ts Outdated
Comment thread vitest.workspace.ts
Comment thread packages/models/examples/cases/convert-case.ts Outdated
Comment thread packages/tool/e2e-tests/live.test.ts
@wildjames
Copy link
Copy Markdown
Contributor Author

This will need to be force-merged since sonar is dumb

image

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 2 pipeline(s).

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 2 pipeline(s).

tstephen-nhs
tstephen-nhs previously approved these changes Apr 14, 2026
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 2 pipeline(s).

tstephen-nhs
tstephen-nhs previously approved these changes Apr 14, 2026
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 2 pipeline(s).

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 2 pipeline(s).

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 14, 2026

@wildjames wildjames merged commit 683a436 into master Apr 14, 2026
34 checks passed
@wildjames wildjames deleted the aea-6452-vitest branch April 14, 2026 14:53
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.

4 participants