JavaScript Integration
@tracecov/core is a coverage engine for Node.js: give it your OpenAPI specification and the HTTP interactions your tests make, and it reports which operations, parameters, and JSON Schema keywords you exercised. Feed it interactions with record() from your HTTP client's interceptor or middleware.
Installation
npm install @tracecov/core
# or
yarn add @tracecov/core
# or
pnpm add @tracecov/core
Requirements:
- Node.js ≥ 18
- ESM project (
"type": "module"inpackage.json) or TypeScript with"moduleResolution": "nodenext"or"bundler"intsconfig.json - CommonJS projects: load it with a dynamic
import()inside an async function:
async function main() {
const { CoverageMap } = await import('@tracecov/core');
const coverage = CoverageMap.fromPath('openapi.json');
// ...
}
main();
@tracecov/core is a native Node.js addon and does not run in browser bundles.
Recording interactions
Load your spec, then record each request/response pair:
import { CoverageMap } from '@tracecov/core';
const coverage = CoverageMap.fromPath('openapi.json');
const url = 'https://api.example.com/users?limit=10';
const start = Date.now();
const res = await fetch(url);
coverage.record(
{ method: 'GET', url, headers: {} },
{
statusCode: res.status,
elapsed: (Date.now() - start) / 1000,
headers: Object.fromEntries(res.headers),
body: Buffer.from(await res.arrayBuffer()),
},
);
fromPath reads JSON or YAML; fromDict takes a parsed object; fromUrl fetches over HTTP (await it).
Call record() wherever you make requests — most clients let you do it once in an interceptor or middleware. Pass the request body as a Buffer in body (Buffer.from(JSON.stringify(payload))); a string throws. Pass the response body and headers to measure response coverage. Annotate a network failure with recordError(method, url, message). For Axios, @tracecov/axios wires this up for you.
Check that requests matched the spec:
console.log(coverage.statistic().operations);
console.log(coverage.unmatchedRequests());
{ seen: 1, total: 12 }
[]
If seen is 0 and unmatchedRequests() lists your requests with reason: 'no_matching_path', the API is served under a prefix the spec does not declare. Pass it as basePath:
const coverage = CoverageMap.fromPath('openapi.json', { basePath: '/api/v1' });
Automatic capture with Axios
@tracecov/axios attaches interceptors that record every request an Axios client makes — success, 4xx/5xx, and network failures — including request bodies, so you never call record() by hand:
npm install @tracecov/axios
import { CoverageMap } from '@tracecov/core';
import { instrumentAxios } from '@tracecov/axios';
import axios from 'axios';
const coverage = CoverageMap.fromPath('openapi.json');
const client = axios.create({ baseURL: 'https://api.example.com' });
instrumentAxios(client, coverage);
// Every call through `client` is recorded.
await client.get('/users', { params: { limit: 10 } });
coverage.saveHtmlReport({ outputFile: 'coverage.html' });
instrumentAxios records the URL Axios actually sends plus the request body and its media type. If recording ever throws it warns and continues; pass { onError } to handle it yourself.
Automatic capture with fetch
@tracecov/fetch wraps fetch so every call is recorded — no record() by hand. Patch the global fetch (returns a restore function), or wrap a specific one:
npm install @tracecov/fetch
import { CoverageMap } from '@tracecov/core';
import { instrumentFetch } from '@tracecov/fetch';
const coverage = CoverageMap.fromPath('openapi.json');
const restore = instrumentFetch(coverage); // patches globalThis.fetch
await fetch('https://api.example.com/users', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ name: 'a' }),
});
restore(); // undo the patch in afterAll
coverage.saveHtmlReport({ outputFile: 'coverage.html' });
Don't want to touch the global? wrapFetch(fetch, coverage) returns an instrumented fetch you call directly. Both accept the same { onError } option.
Express
@tracecov/express records coverage from the server side: one call captures every request the app handles — supertest, a real HTTP client, or a browser — so it works with the in-process test style that never touches a client you can instrument. Works on Express 4 and 5.
npm install @tracecov/express
import express from 'express';
import request from 'supertest';
import { CoverageMap } from '@tracecov/core';
import { instrumentExpress } from '@tracecov/express';
const app = express();
app.use(express.json());
app.get('/items', (req, res) => res.json([]));
const coverage = CoverageMap.fromPath('openapi.json');
instrumentExpress(app, coverage);
await request(app).get('/items?limit=10'); // recorded
coverage.saveHtmlReport({ outputFile: 'coverage.html' });
It hooks the app's request dispatch, so it captures whatever drives the app — supertest, fetch against a running server, or a browser — including request bodies. Pass { onError } to handle recording failures yourself.
Vitest
@tracecov/vitest merges the requests from every test file into one report. Feed it with @tracecov/fetch, @tracecov/axios, or record() by hand. Requires Vitest 3 or later.
npm install -D @tracecov/vitest @tracecov/fetch
Add the plugin to vitest.config.ts:
import { tracecov } from '@tracecov/vitest';
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [
tracecov({
schema: 'openapi.json',
setup: './tracecov.setup.ts',
report: { html: 'coverage.html' },
thresholds: { operations: 80 },
}),
],
});
Then create the setup file it points at — grab a coverage map and wire it to your client:
// tracecov.setup.ts
import { instrumentFetch } from '@tracecov/fetch';
import { collectCoverage } from '@tracecov/vitest/setup';
instrumentFetch(collectCoverage()); // or: instrumentAxios(client, collectCoverage())
Run your suite as usual. The configured reports are written when the run ends. A thresholds breach prints one line per metric and sets a non-zero exit code:
tracecov: Operations 50.0% / 80% required
report accepts html, json, and markdown output paths (plus title for HTML); thresholds takes any of operations, parameters, keywords, examples, responses, responseKeywords. schema and setup are resolved from the working directory, or from root if you set it in your Vitest config. collectCoverage() throws a clear error if the plugin isn't configured.
Jest
@tracecov/jest captures fetch calls in every Jest worker and merges them into one report. It needs no --experimental-vm-modules. Requires Jest 29 or later.
npm install -D @tracecov/jest
// jest.config.js
module.exports = {
setupFilesAfterEnv: ['./tracecov.setup.js'],
reporters: [
'default',
['@tracecov/jest/reporter', { schema: 'openapi.json', report: { html: 'coverage.html' } }],
],
};
// tracecov.setup.js
const { collectCoverage, instrumentFetch } = require('@tracecov/jest');
instrumentFetch(collectCoverage());
Same report/thresholds options and threshold output as the Vitest plugin.
Playwright
@tracecov/playwright instruments the request fixture and merges worker results into one report.
npm install -D @tracecov/playwright
See Playwright Integration for setup, worker merging and limitations.
Writing a report
coverage.saveHtmlReport({ outputFile: 'coverage.html' });
generateHtmlReport / generateJsonReport / generateMarkdownReport / generateTextReport return a string; saveHtmlReport / saveJsonReport / saveMarkdownReport write a file. See JSON Report Format for the machine-readable schema.
Enforcing thresholds
const violations = coverage.checkThresholds({ operations: 80, keywords: 70 });
for (const v of violations) {
console.error(`${v.dimension}: ${v.actual.toFixed(1)}% < ${v.minimum}%`);
}
Each violation reports the dimension, its actual percentage, and the minimum required. Exit non-zero when violations is non-empty to fail your CI.
Importing recorded traffic
@tracecov/core is the Community edition: recordFromHar, recordFromPostman, and recordFromVcr always throw, for example HAR import requires TraceCov Professional. See https://docs.tracecov.sh/editions/. Import HAR files, Postman collections, and VCR cassettes with the Professional CLI:
tracecov report openapi.json traffic.har
Full method-by-method documentation is in the API reference.