> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# coverage


[Added in v0.4.0](https://github.com/web-infra-dev/rstest/releases/tag/v0.4.0)

- **Type:**

```ts
type CoverageOptions = {
  enabled?: boolean;
  provider?: 'istanbul' | 'v8';
  include?: string[];
  changed?: boolean | string;
  exclude?: string[];
  reporters?: CoverageReporter[];
  reportsDirectory?: string;
  reportOnFailure?: boolean;
  clean?: boolean;
  allowExternal?: boolean;
  thresholds?: CoverageThresholds;
};

type CoverageReporter = string | [string, Record<string, unknown>] | ReportBase;
```

- **Default:** `undefined`

Collect code coverage and generate coverage reports.

```bash
$ npx rstest --coverage

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |
 index.ts |     100 |      100 |     100 |     100 |
----------|---------|----------|---------|---------|-------------------
```

## Options

### enabled

- **Type:** `boolean`
- **Default:** `false`
- **CLI:** `--coverage`, `--coverage=false`, `--no-coverage`

Enable or disable test coverage collection.


**CLI**

```bash
npx rstest --coverage
```


**rstest.config.ts**

```ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
  },
});
```


### provider

- **Type:** `'istanbul' | 'v8'`
- **Default:** `'istanbul'`
- **CLI:** `--coverage.provider <provider>`

The coverage provider to use. Rstest supports both [istanbul](https://istanbul.js.org/) and `v8`. Choose `istanbul` when you want SWC-based instrumentation performance and stable Istanbul semantics. Choose `v8` when coverage is mainly limited by memory pressure; it usually has lower runtime memory usage because it does not inject coverage counters into every executed module.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    provider: 'v8',
  },
});
```

#### Istanbul provider

[Istanbul](https://istanbul.js.org/) is a widely used JavaScript code coverage tool that collects coverage through instrumentation.

To enable istanbul coverage, install `@rstest/coverage-istanbul` first.


```sh [npm]
npm add @rstest/coverage-istanbul -D
```

```sh [yarn]
yarn add @rstest/coverage-istanbul -D
```

```sh [pnpm]
pnpm add @rstest/coverage-istanbul -D
```

```sh [bun]
bun add @rstest/coverage-istanbul -D
```

```sh [deno]
deno add npm:@rstest/coverage-istanbul -D
```

`@rstest/coverage-istanbul` is powered by [swc-plugin-coverage-instrument](https://github.com/kwonoj/swc-plugin-coverage-instrument). Rstest instruments source files through SWC during transform, so this provider has good transform-time performance. It can use more runtime memory because executed modules keep the injected coverage counters.

##### Excluding code

Use `coverage.exclude` when a whole file, generated file group, or injected script should not be instrumented:

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    provider: 'istanbul',
    exclude: ['**/src/injected-script.ts', '**/src/**/*.{worker,evaluate}.ts'],
  },
});
```

You can also pass the same patterns from the CLI for a one-off run:

```bash
npx rstest run --coverage --coverage.exclude "**/src/injected-script.ts" --coverage.exclude "**/src/**/*.{worker,evaluate}.ts"
```

Use Istanbul ignore comments only for small snippets that must stay in an otherwise covered file:

```ts title='src/injected-script.ts'
/* istanbul ignore file */

export function createInjectedScript() {
  return `globalThis.__APP_READY__ = true;`;
}
```

```ts
/* istanbul ignore next */
function browserEvaluateFn() {
  return window.location.href;
}

/* istanbul ignore if */
if (process.env.NODE_ENV === 'test') {
  setupTestOnlyState();
}

/* istanbul ignore else */
if (hasNativeFeature()) {
  useNativeFeature();
} else {
  useFallback();
}
```

If an ignored snippet still receives an injected `cov_*` call, use `coverage.exclude` for the source file instead.

##### Serialized functions and other realms

Istanbul injects `cov_*` coverage counter calls into instrumented files. Those counters are scoped to the transformed module that Rstest executes. If code from an instrumented file is serialized and executed somewhere else, the new scope or realm may not have the matching `cov_*` helper, and the test can fail with `ReferenceError: cov_... is not defined`.

This can happen when a function is serialized with `fn.toString()` or executed through APIs such as `page.evaluate`, `Worker`, `node:vm`, `eval`, or `new Function`.

To avoid the failure, prefer one of these approaches:

- exclude the source file with `coverage.exclude`;
- switch to `coverage.provider: 'v8'` for Node or headless, non-watch Chromium Browser Mode tests;
- avoid serializing Istanbul-instrumented functions.

In headless, non-watch Chromium Browser Mode runs, the `v8` provider avoids injected counters as well. Firefox and WebKit Browser Mode tests should use `coverage.exclude` or avoid serializing instrumented functions.

#### V8 provider


[Added in v0.10.2](https://github.com/web-infra-dev/rstest/releases/tag/v0.10.2)

The `v8` provider collects precise coverage from the Node.js inspector or Chromium's native coverage API, then remaps it to Istanbul format. Compared with Istanbul instrumentation, V8 coverage usually has lower runtime memory usage because executed modules do not need extra coverage counters. It is a better fit for large test suites where coverage memory pressure is the bottleneck.

To enable V8 coverage, install [@rstest/coverage-v8](https://github.com/web-infra-dev/rstest/tree/main/packages/coverage-v8) first.


```sh [npm]
npm add @rstest/coverage-v8 -D
```

```sh [yarn]
yarn add @rstest/coverage-v8 -D
```

```sh [pnpm]
pnpm add @rstest/coverage-v8 -D
```

```sh [bun]
bun add @rstest/coverage-v8 -D
```

```sh [deno]
deno add npm:@rstest/coverage-v8 -D
```

Because the underlying coverage data comes from Node.js / Chromium V8 itself, remapped branch coverage and uncovered line details can vary slightly across engine versions.

##### Ignore hints

The `v8` provider honors ignore hints when converting V8 coverage to Istanbul format. The hint prefix can be any of the following forms:

```ts
/* istanbul ignore next */
/* c8 ignore next */
/* v8 ignore next */
/* node:coverage ignore next */
```

Prefer using one prefix consistently in a project. The supported hint forms are:

```ts
/* v8 ignore next */
const value = optionalExpensivePath();

/* v8 ignore if */
if (process.env.NODE_ENV === 'test') {
  setupTestOnlyState();
}

/* v8 ignore else */
if (hasNativeFeature()) {
  useNativeFeature();
} else {
  useFallback();
}

/* v8 ignore start */
const generatedLookup = createGeneratedLookup();
/* v8 ignore stop */
```

`ignore file` is also recognized, but it applies to the generated coverage entry before source-map remapping. In normal Rstest output, one generated chunk can contain multiple source files, so prefer `coverage.exclude` when a source file or generated file group should be excluded.

The `v8` provider works in `node`, `jsdom`, and `happy-dom` test environments. Stable Browser Mode support is limited to headless, non-watch Chromium runs with the Playwright provider. Chromium coverage is collected from executed scripts with HTTP(S) identities in the page renderer and remapped through source maps, without instrumenting application code. Query-bearing script URLs and both classic and module scripts are supported.

Headed and watch Browser Mode coverage is experimental. Reports may include execution from the container or inactive runner frames, and watch rebuilds may not preserve exact script/source-map pairing. Use the default `istanbul` provider when those modes require stable coverage semantics. Firefox and WebKit do not expose the required native coverage API and must also use `istanbul`.

Native browser V8 coverage does not collect scripts identified only by `blob:`, `data:`, another non-HTTP source URL, or no URL, including anonymous `eval` and `new Function` calls. It also does not collect code that runs exclusively in dedicated `Worker` or `SharedWorker` targets. Use `istanbul` when these scripts must contribute coverage.

### include

- **Type:** `string[]`
- **Default:** `undefined`
- **CLI:** `--coverage.include <pattern>`

A list of glob patterns that should be included for coverage collection. Repeat the CLI flag to specify multiple patterns.

By default, rstest will only collect coverage for files that are tested. If you want to include untested files in the coverage report, you can use the `include` option to specify the files or patterns to include.

Note that you should use standard glob syntax here. For a single extension, write `src/**/*.ts` instead of `src/**/*.{ts}`. Single-value braces are treated literally by the underlying glob libraries, so `src/**/*.{ts}` will not match `.ts` files.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    include: ['src/**/*.{js,jsx,ts,tsx}'],
  },
});
```

### changed


[Added in v0.10.0](https://github.com/web-infra-dev/rstest/releases/tag/v0.10.0)

- **Type:** `boolean | string`
- **Default:** `undefined`
- **CLI:** `--coverage.changed`, `--coverage.changed=<commit>`

Collect coverage only for files changed in the current Git repository. Pass a commit or branch to collect coverage for files changed since that ref.

This option is available in both config and CLI:

- Use `coverage.changed` in `rstest.config.ts` when you want a persistent default, for example in CI.
- Use `--coverage.changed` for one-off local or CI runs.

`coverage.changed` only limits the coverage report scope. It does not change which tests are executed.

That means these two commands do different things:

```bash
npx rstest run --changed
npx rstest run --coverage.changed
```

- `--changed` changes the test selection and runs tests related to changed files.
- `--coverage.changed` keeps the normal test selection, but only reports coverage for changed source files.

#### Interaction with `--changed`

- When [`--changed`](/guide/basic/cli.md#run-changed-tests) is used and `coverage.changed` is not configured, coverage reports inherit the changed files collected by `--changed`.
- If `--changed` hits [`forceRerunTriggers`](/config/test/force-rerun-triggers.md), Rstest reruns the full test suite. Coverage stays full in that case unless `coverage.changed` is explicitly enabled.
- If you want to use `--changed` for test selection but still keep full coverage reports, set `coverage.changed` to `false` in config.

Common setups:

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    changed: 'origin/main',
  },
});
```

The example above still runs the normal test suite, but the coverage report only includes files changed since `origin/main`.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    changed: 'HEAD~1',
  },
});
```

```bash
npx rstest run --changed --coverage
npx rstest run --coverage.changed
npx rstest run --coverage.changed=HEAD~1
npx rstest run --coverage.changed=origin/main
```

- `npx rstest run --changed --coverage` runs changed-related tests and, by default, also limits coverage to the same changed file set.
- `npx rstest run --coverage.changed=HEAD~1` runs the normal test suite but only reports coverage for files changed since `HEAD~1`.
- `npx rstest run --coverage.changed=origin/main` is useful when your branch is based on `main`: it still runs the normal test suite, but the coverage report only includes files changed compared with `origin/main`.

### exclude

- **Type:** `string[]`
- **CLI:** `--coverage.exclude <pattern>`
- **Default:**

```ts
[
  '**/node_modules/**',
  '**/__tests__/**',
  '**/__mocks__/**',
  '**/*.d.ts',
  '**/*.{test,spec}.[jt]s',
  '**/*.{test,spec}.[cm][jt]s',
  '**/*.{test,spec}.[jt]sx',
  '**/*.{test,spec}.[cm][jt]sx',
];
```

A glob pattern array to exclude files from test coverage collection. Repeat the CLI flag to specify multiple patterns.

Any patterns specified here will be merged with default values.

Rstest does not exclude directories named `test` by default. This avoids accidentally dropping source files from packages or workspaces whose path contains a `test` segment, such as `packages/test/src/index.ts`. To exclude those directories from coverage, add an explicit pattern such as `test/**` or `**/test/**`.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    exclude: ['**/node_modules/**', '**/dist/**'],
  },
});
```

### reporters

- **Type:** `CoverageReporter[]`
- **Default:** `['text', 'html', 'clover', 'json']`
- **CLI:** `--coverage.reporters <reporter>`

The reporters to use for coverage collection. Rstest uses the standard [Istanbul reporter API](https://istanbul.js.org/docs/advanced/alternative-reporters/) instead of a Rstest-specific coverage reporter API.

Each reporter can be either a string (the reporter name), a tuple with the reporter name and its options, or an Istanbul-compatible reporter object with an `execute(context)` method.

The CLI supports reporter names only. Repeat the flag to specify multiple reporters:

```bash
npx rstest run --coverage.reporters text --coverage.reporters=json
```

- See [Istanbul Reporters](https://istanbul.js.org/docs/advanced/alternative-reporters/) for available reporters.
- See [@types/istanbul-reports](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/istanbul-reports/index.d.ts) for details about reporter specific options.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    reporters: [
      'html',
      ['text', { skipFull: true }],
      ['json', { file: 'coverage-final.json' }],
    ],
  },
});
```

#### Custom coverage reporters

Custom coverage reporters should follow the Istanbul reporter contract. A reporter package or file is loaded by Rstest's coverage provider (using [`istanbul-reports`](https://github.com/istanbuljs/istanbuljs/tree/main/packages/istanbul-reports) when possible), constructed with the options from `coverage.reporters`, and then called with an Istanbul report context.

For example, an ESM reporter can export a class with an `execute` method. The `context` parameter is Istanbul's `Context` type from `istanbul-lib-report`:

```js title='custom-coverage-reporter.mjs'
export default class CustomCoverageReporter {
  constructor(options = {}) {
    this.options = options;
  }

  /** @param {import('istanbul-lib-report').Context} context */
  execute(context) {
    // Use the standard Istanbul report context.
    const summary = context.getTree('flat').getRoot().getCoverageSummary();
    console.log('Coverage summary:', summary.toJSON());
  }
}
```

Then reference the reporter by package name or file path in `rstest.config.ts`:

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    reporters: [
      'text',
      ['./custom-coverage-reporter.mjs', { outputFile: 'summary.json' }],
    ],
  },
});
```

You can also pass an Istanbul-compatible reporter object directly when the reporter is created in config:

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    reporters: [
      {
        execute(context) {
          const summary = context
            .getTree('flat')
            .getRoot()
            .getCoverageSummary();
          console.log(summary.toJSON());
        },
      },
    ],
  },
});
```

### reportsDirectory

- **Type:** `string`
- **Default:** `'./coverage'`
- **CLI:** `--coverage.reportsDirectory <dir>`

The directory to store coverage reports.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    reportsDirectory: './coverage-reports',
  },
});
```

### reportOnFailure

- **Type:** `boolean`
- **Default:** `false`
- **CLI:** `--coverage.reportOnFailure`

Whether to report coverage and check thresholds when tests fail.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';
export default defineConfig({
  coverage: {
    enabled: true,
    reportOnFailure: true,
  },
});
```

### allowExternal


[Added in v0.9.3](https://github.com/web-infra-dev/rstest/releases/tag/v0.9.3)

- **Type:** `boolean`
- **Default:** `false`
- **CLI:** `--coverage.allowExternal`

Whether to collect coverage for source files outside the project root directory. This is useful in monorepo setups where tests import modules from sibling packages.

By default, rstest excludes files outside the project root from coverage reports, which aligns with the behavior of Jest and Vitest.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    allowExternal: true,
  },
});
```

### clean

- **Type:** `boolean`
- **Default:** `true`
- **CLI:** `--coverage.clean`, `--coverage.clean=false`

Whether to clean the coverage directory before running tests.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    clean: true,
  },
});
```

### thresholds

- **Type:**

```ts
type CoverageThreshold = {
  statements?: number;
  functions?: number;
  branches?: number;
  lines?: number;
};

type CoverageThresholds = CoverageThreshold & {
  /** check thresholds for matched files */
  [glob: string]: CoverageThreshold & {
    perFile?: boolean;
  };
};
```

- **Default:** `undefined`

Coverage thresholds for enforcing minimum coverage requirements. You can set thresholds for statements, functions, branches, and lines.

Thresholds specified as a positive number are taken to be the minimum percentage required. Thresholds specified as a negative number represent the maximum number of uncovered entities allowed.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    thresholds: {
      statements: 80,
      functions: 80,
      branches: 80,
      lines: -10,
    },
  },
});
```

When the code coverage is below the specified thresholds, the test will fail and output an error message like below:

```bash
Error: Coverage for statements 75% does not meet global threshold 80%
Error: Coverage for functions 75% does not meet global threshold 80%
Error: Coverage for branches 75% does not meet global threshold 80%
Error: Uncovered lines 20 exceeds maximum global threshold allowed 10
```

#### glob pattern

If globs are specified, thresholds will be checked for each matched file pattern. If the file specified by path is not found, an error is returned.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    thresholds: {
      // Thresholds for matching glob pattern
      'src/**': {
        statements: 100,
      },
      'node/**/*.js': {
        statements: 90,
      },
      // Thresholds for all files
      statements: 80,
    },
  },
});
```

Following the above configuration, rstest will fail if:

- The total code coverage of all files in `src/**` is below 100%.
- The total code coverage of all files in `node/**/*.js` is below 90%.
- The global code coverage is below 80% for statements.

#### check threshold for per file

Rstest also supports checking thresholds for each matched file by setting `perFile` to `true`.

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    thresholds: {
      'src/**': {
        statements: 90,
        perFile: true,
      },
    },
  },
});
```
