Skip to main content

Unit and rendered tests

One Vitest run at the repository root tests everything written in TypeScript outside the contracts: the service layer, the cryptography and format code in src/lib, the Next.js API route handlers, the React pages and components, the SDK in packages/sdk, the scripts in scripts/ and the E2E harness's own helpers. It runs without a chain, a browser or a network: chain access goes through fakes, and pages render in jsdom.

Configuration​

vitest.config.ts:

export default defineConfig({
resolve: {
alias: { '@': path.resolve(__dirname, 'src') },
},
test: {
environment: 'node',
exclude: [...configDefaults.exclude, 'blockchain/**', 'interop/**', '.next/**', '.next-e2e/**', 'e2e/journeys/**'],
coverage: {
provider: 'v8',
include: ['src/services/**/*.ts', 'src/lib/**/*.ts'],
exclude: ['**/*.test.ts', '**/__fixtures__/**', 'src/services/testing/**'],
reporter: ['text', 'json-summary'],
thresholds: { lines: 80, statements: 80, functions: 80, branches: 80 },
},
},
});

What it means:

SettingEffect
alias '@'@/services/... resolves to src/services/..., as in the app
environment: 'node'Tests run in Node by default. A file that renders React opts into jsdom with a first-line comment
Default includeVitest's default pattern, so any *.test.ts, *.test.tsx (or .spec) file is picked up
excludeLeaves out the contract tests (blockchain/, run by Hardhat), the interop tests (interop/, run by node:test), Next.js build folders and the Playwright journeys. node_modules folders are excluded by the defaults
CoverageV8 coverage over src/services and src/lib only, with an 80% gate on all four measures

vitest.shims.d.ts holds a type reference to the @vitest/browser Playwright provider. Browser mode is not configured in vitest.config.ts; the rendered tests use jsdom.

Commands​

CommandWhat it does
npm run test:runOne run of every Vitest file (vitest run --passWithNoTests)
npm run test:coverageOne run with coverage and the 80% gate (what CI runs)
npm testWatch mode, for local work only
npx vitest run src/lib/eip712.test.tsOne file
npm run sdk:testOnly packages/sdk

Two environments​

EnvironmentHow a file chooses itUsed forCount
nodeDefaultServices, src/lib, route handlers, scripts, SDK, source scansthe rest
jsdom// @vitest-environment jsdom on the first lineRendered pages and components74 files

Rendered tests use @testing-library/react (render, screen, fireEvent, waitFor, cleanup) and the matchers from @testing-library/jest-dom/vitest. They mock next/navigation and wrap the component in ServicesProvider with fake services, so a page sees exactly the service answers the test chooses. A shortened example from src/components/layout/Header.test.tsx:

// @vitest-environment jsdom
import "@testing-library/jest-dom/vitest";
import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";

const route = vi.hoisted(() => ({ pathname: "/" }));
vi.mock("next/navigation", () => ({
useRouter: () => ({ push: vi.fn() }),
usePathname: () => route.pathname,
}));

Fakes for the chain​

Services take their contract access through a gateway interface (see Service layer), so tests hand them a fake instead of a provider.

FileExportsUse
src/services/testing/fake-gateway.tsfakeGateway(methods, options), asContract(methods), txResult(receipt)One fake contract behind every accessor of a ContractGateway; the writer runs an optional guard first (for example a wrong-network check). txResult returns a mock transaction whose wait() resolves to a receipt
src/services/testing/fake-credential-gateway.tsfakeCredentialGateway(...), FAKE_REGISTRY, FAKE_ISSUER_REGISTRY, FAKE_STATUS_REGISTRY, FAKE_RECORD_TYPE_REGISTRY, FAKE_HOLDER_REGISTRYThe same for the v0.3 registries, with fixed fake addresses

These files are excluded from coverage. Route handlers are tested by calling the exported handler with a constructed Request and injected stores and fetchers; for example src/app/api/upload/route.test.ts and src/lib/server/ops/handler.test.ts.

Shared vectors​

Where the browser and a contract must compute the same bytes, both test suites read one committed vector file, so a change on either side fails a test.

Vector fileRead byPins
blockchain/test/fixtures/credential-vector.jsonsrc/lib/eip712.test.ts and contract tests such as blockchain/test/v03/CredentialIssuance.test.tsThe EIP-712 credential digest and id
blockchain/test/fixtures/commitment-vectors.jsonsrc/lib/commitments.test.ts and blockchain/test/v03/NoPlaintext.test.tsSalted subject and metadata commitments
blockchain/test/fixtures/invitation-vector.jsonsrc/lib/invitation.test.ts, src/lib/invitation-file.test.ts and blockchain/test/v03/HolderRegistry.test.tsThe enrolment invitation digest
blockchain/test/zk/fixtures/access-scope-vector.jsonsrc/lib/zk/scope.test.ts and blockchain/test/zk/AccessGate.test.tsThe access proof scope and message
vc/test-vectors/golden-vc.jsonsrc/lib/vc/golden.test.tsA complete verifiable credential
src/lib/disclosure/vectors.jsonsrc/lib/disclosure/vectors.test.tsSelective disclosure field tree
Frozen strings in src/lib/present/vectors.test.tsthe same fileVersion 1 compact codes (v1., d1., c1.) must keep decoding to the same facts
src/lib/doc-crypto/fixtures/rfc9180-x25519-aes256gcm.jsonsrc/lib/doc-crypto/wrap.test.tsPublished HPKE test vectors for key wrapping

Fixture builders live in __fixtures__ folders, for example src/lib/vc/__fixtures__/make-package.ts and make-badge-signer.ts.

Property tests​

Some format and cryptography tests use fast-check to generate many inputs, for example src/lib/doc-crypto/container.test.ts, payload.test.ts, encoding.test.ts, src/lib/vc/parse.test.ts, src/lib/import/csv.test.ts and adapters.test.ts. The contract invariant suite uses the same library; see Invariants and gas.

Source scans​

A few tests read source files instead of running them, to pin a rule about the code itself. Fourteen test files under src/ do this, including:

TestRule it pins
src/app/data-exposure.test.tsNo page except the record detail and add pages references ipfsHash; pages read records through the permission-aware call; the public verify pages look credentials up by id only
src/lib/zk/proof-boundary.test.tsThe proving package is imported only by the access proof module, which only the worker loads
src/lib/doc-crypto/boundary.test.tsWhich document crypto modules are safe for the server
src/app/fonts.test.tsFonts are self-hosted and not requested from a third-party host
src/compliance/dpia.test.tsThe data protection impact assessment draft exists and covers every required section
src/lib/vc/schema.test.ts, src/lib/vc/openbadges/schema.test.tsExported documents pass the vendored schemas

Where the tests are​

Tests sit next to the module they test (src/lib/eip712.ts and src/lib/eip712.test.ts). Counted on 2026-10-24:

FolderTest files
src/lib167
src/app74
src/services59
src/components19
packages/sdk6
e2e/support4
src/contracts2
src/compliance1
scripts (including scripts/lib)2
Total334

The last full run recorded on 2026-10-24 passed 3,369 tests in these 334 files.

Counting tests​

find . \( -path '*/node_modules' -o -path ./blockchain -o -path ./interop -o -path ./.next \
-o -path ./.next-e2e -o -path ./e2e/journeys -o -path ./.git \) -prune -o -type f \
\( -name "*.test.*" -o -name "*.spec.*" \) -print | wc -l
npm run test:run # prints the number of tests that ran

Conventions​

  • Test names read as sentences about behaviour: describe("the package stands on its own"), it("imports nothing from the app").
  • Prefer injecting a dependency over mocking a module; module mocks are kept for framework edges such as next/navigation.
  • Read failures get their own tests, so a failure is never shown as a negative answer: for example src/app/student/MyCredentialsPanel.test.tsx ("shows a retryable error when the history could not be read, not an empty list") and src/app/dashboard/IssuedCredentialsPanel.test.tsx ("says a failed load is not proof of an empty ledger and retries on request").
  • No .only, no skipped tests, no tests under docs/.

Source files​

  • vitest.config.ts, vitest.shims.d.ts
  • src/services/testing/fake-gateway.ts, src/services/testing/fake-credential-gateway.ts
  • src/lib/vc/__fixtures__/
  • blockchain/test/fixtures/, blockchain/test/zk/fixtures/access-scope-vector.json, vc/test-vectors/golden-vc.json

Tests​

The tests named on this page, for example src/components/layout/Header.test.tsx, src/lib/eip712.test.ts, src/lib/commitments.test.ts, src/app/data-exposure.test.ts and src/lib/present/vectors.test.ts.