docs/coverage.md
This document explains how code coverage instrumentation works in Rocket.Chat's build and CI pipeline.
Coverage is collected during E2E test runs (API, UI, Livechat) to measure how much of the server-side code is exercised by tests. The instrumentation uses Istanbul-compatible tooling, which injects counters (__coverage__) into the compiled code at build time.
The coverage pipeline has three components:
rocketchat:coverage Meteor package collects __coverage__ data on process exitBuild (SWC + plugin) --> Run tests --> Process exit triggers report --> Merge & upload
Rocket.Chat uses Meteor's modern build stack with SWC as the transpiler. For coverage builds, the swc-plugin-coverage-instrument plugin is injected into .swcrc at build time.
This is configured in .github/actions/meteor-build/action.yml:
env:
BABEL_ENV: ${{ inputs.type }} # "production" or "coverage"
When BABEL_ENV=coverage, the build script:
rocketchat:coverage to .meteor/packagesswc-plugin-coverage-instrument into .swcrc via a node script:const swcrc = JSON.parse(fs.readFileSync('./apps/meteor/.swcrc', 'utf8'));
swcrc.jsc.experimental = swcrc.jsc.experimental || {};
swcrc.jsc.experimental.plugins = swcrc.jsc.experimental.plugins || [];
swcrc.jsc.experimental.plugins.push(['swc-plugin-coverage-instrument', {}]);
fs.writeFileSync('./apps/meteor/.swcrc', JSON.stringify(swcrc, null, 2) + '\n');
This approach ensures the same build pipeline (SWC) is used for both regular and coverage builds, avoiding behavioral differences between build modes.
Before the modern build stack, coverage was handled via babel-plugin-istanbul configured in .babelrc:
{
"env": {
"coverage": {
"plugins": [
["istanbul", { "exclude": ["**/*.spec.js", "**/*.test.js"] }]
]
}
}
}
This section is still present in .babelrc as a fallback for files that fall back to Babel compilation (e.g., SWC-incompatible code).
rocketchat:coverageThe rocketchat:coverage Meteor package (apps/meteor/packages/rocketchat-coverage/) is only added to the build during coverage runs. It:
process.on('exit') handlerglobalThis['__coverage__'] (populated by the instrumentation)istanbul-lib-coverage and istanbul-reportsConfiguration via environment variables:
| Variable | Description | Example |
|---|---|---|
COVERAGE_DIR | Output directory for reports | /tmp/coverage/api |
COVERAGE_FILE_NAME | Report filename | api-1.json |
COVERAGE_REPORTER | Istanbul reporter format | json, lcov |
Coverage is collected in the ci-test-e2e.yml workflow:
meteor-build action runs with type: coverage, producing a Docker image with instrumented codeCOVERAGE_DIR, COVERAGE_FILE_NAME, and COVERAGE_REPORTER are setnyc merge combines per-shard JSON reports into a single coverage fileTo build with coverage locally:
cd apps/meteor
# Inject the SWC coverage plugin into .swcrc
node -e "
const fs = require('fs');
const swcrc = JSON.parse(fs.readFileSync('.swcrc', 'utf8'));
swcrc.jsc.experimental = { plugins: [['swc-plugin-coverage-instrument', {}]] };
fs.writeFileSync('.swcrc', JSON.stringify(swcrc, null, 2));
"
# Add the coverage package
echo -e "rocketchat:coverage\n" >> .meteor/packages
# Set env vars and run
COVERAGE_DIR=/tmp/coverage COVERAGE_FILE_NAME=local.json COVERAGE_REPORTER=lcov yarn dev
Remember to restore .swcrc and .meteor/packages after testing.
| Package | Purpose |
|---|---|
swc-plugin-coverage-instrument | SWC plugin for Istanbul-compatible instrumentation |
istanbul-lib-coverage | Coverage map creation (used by rocketchat:coverage) |
istanbul-lib-report | Report context creation |
istanbul-reports | Report formatters (json, lcov, etc.) |