or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

coverage-provider-browser.mdcoverage-provider-node.mdindex.mdv8-coverage-provider.md
tile.json

coverage-provider-node.mddocs/

Coverage Provider Module (Node.js)

The Node.js coverage provider module implements the CoverageProviderModule interface for Node.js environments. It manages the V8 profiler lifecycle using the Node.js Inspector API to collect coverage data without source code instrumentation.

Core Import

import coverageModule from '@vitest/coverage-v8';

Capabilities

Module Interface

The default export implements the CoverageProviderModule interface with lifecycle methods for coverage collection:

interface CoverageProviderModule {
  /**
   * Initialize V8 coverage collection using Node.js Inspector API
   * @param options - Configuration options
   * @param options.isolate - If false and coverage is already enabled, skip initialization
   */
  startCoverage(options: { isolate?: boolean }): Promise<void>;

  /**
   * Take a snapshot of current V8 coverage data
   * @param options - Configuration options
   * @param options.moduleExecutionInfo - Map of file paths to execution metadata including startOffset
   * @returns Object containing array of script coverage results with offset information
   */
  takeCoverage(options?: {
    moduleExecutionInfo?: Map<string, { startOffset: number }>;
  }): Promise<{ result: ScriptCoverageWithOffset[] }>;

  /**
   * Stop coverage collection and disconnect from V8 Inspector
   * @param options - Configuration options
   * @param options.isolate - If false, skip stopping (leave coverage enabled)
   */
  stopCoverage(options: { isolate?: boolean }): Promise<void>;

  /**
   * Factory method to get the V8CoverageProvider instance
   * @returns Promise resolving to V8CoverageProvider instance
   */
  getProvider(): Promise<V8CoverageProvider>;
}

Start Coverage

Initializes V8 coverage collection by connecting to the Node.js Inspector and enabling the profiler.

/**
 * Initialize V8 coverage collection using Node.js Inspector API.
 * Creates an inspector session, enables the Profiler, and starts precise coverage collection.
 *
 * @param options - Configuration options
 * @param options.isolate - If false and coverage is already enabled, skip initialization.
 *                          This allows multiple test files to share the same coverage session.
 * @returns Promise that resolves when coverage collection is started
 *
 * @example
 * // Start coverage for a single test file
 * await coverageModule.startCoverage({ isolate: true });
 *
 * @example
 * // Start coverage in shared mode (won't restart if already enabled)
 * await coverageModule.startCoverage({ isolate: false });
 */
function startCoverage(options: { isolate?: boolean }): Promise<void>;

Behavior:

  • Creates a new node:inspector/promises Session if not already created
  • Connects to the inspector
  • Enables the Profiler via Profiler.enable
  • Starts precise coverage with Profiler.startPreciseCoverage with options:
    • callCount: true - Collect execution counts
    • detailed: true - Collect detailed coverage information
  • If isolate is false and coverage is already enabled, does nothing (allows session reuse)

Inspector API Used:

  • inspector.Session from node:inspector/promises
  • Profiler.enable - Enables the profiler
  • Profiler.startPreciseCoverage - Starts detailed coverage collection

Take Coverage

Captures a snapshot of the current coverage data from V8 and performs initial filtering.

/**
 * Take a snapshot of current V8 coverage data.
 * Retrieves coverage from the V8 profiler and filters out irrelevant files
 * (non-file URLs, node_modules). Also adds startOffset information for module wrappers.
 *
 * @param options - Configuration options
 * @param options.moduleExecutionInfo - Map of normalized file paths to execution metadata.
 *                                       The startOffset from this map is added to each coverage entry
 *                                       to account for wrapper code added during module execution.
 * @returns Promise resolving to object containing array of script coverage results
 *
 * @example
 * const { result } = await coverageModule.takeCoverage({
 *   moduleExecutionInfo: new Map([
 *     ['/path/to/file.js', { startOffset: 0 }],
 *     ['/path/to/module.js', { startOffset: 42 }]
 *   ])
 * });
 *
 * console.log(result); // Array of ScriptCoverageWithOffset objects
 */
function takeCoverage(options?: {
  moduleExecutionInfo?: Map<string, { startOffset: number }>;
}): Promise<{ result: ScriptCoverageWithOffset[] }>;

Behavior:

  • On StackBlitz provider, returns empty array (StackBlitz doesn't support V8 coverage)
  • Calls Profiler.takePreciseCoverage to get coverage snapshot from V8
  • Filters results to include only:
    • URLs starting with file:// protocol
    • Files not in node_modules/
  • Adds startOffset to each entry from moduleExecutionInfo map (defaults to 0 if not found)
  • The startOffset accounts for wrapper code that module systems add around module code

Returns: Object with shape:

{
  result: ScriptCoverageWithOffset[] // Array of coverage entries
}

Filtering Logic:

  1. URL must start with file:// (excludes eval, data URLs, etc.)
  2. URL must not contain /node_modules/ (excludes dependencies)

Stop Coverage

Stops coverage collection and cleans up the Inspector session.

/**
 * Stop coverage collection and disconnect from V8 Inspector.
 * Stops the profiler and disconnects the inspector session.
 *
 * @param options - Configuration options
 * @param options.isolate - If false, skip stopping. This allows the coverage session
 *                          to remain active for subsequent test files.
 * @returns Promise that resolves when coverage collection is stopped
 *
 * @example
 * // Stop coverage and clean up
 * await coverageModule.stopCoverage({ isolate: true });
 *
 * @example
 * // Keep coverage session active (no-op)
 * await coverageModule.stopCoverage({ isolate: false });
 */
function stopCoverage(options: { isolate?: boolean }): Promise<void>;

Behavior:

  • If isolate is false, does nothing (allows session to continue for next test)
  • Otherwise:
    • Stops precise coverage via Profiler.stopPreciseCoverage
    • Disables the profiler via Profiler.disable
    • Disconnects the inspector session

Inspector API Used:

  • Profiler.stopPreciseCoverage - Stops coverage collection
  • Profiler.disable - Disables the profiler
  • session.disconnect() - Disconnects the inspector

Get Provider

Factory method that returns an instance of the V8CoverageProvider class.

/**
 * Factory method to get the V8CoverageProvider instance.
 * The provider is dynamically imported to prevent bundling.
 *
 * @returns Promise resolving to new V8CoverageProvider instance
 *
 * @example
 * const provider = await coverageModule.getProvider();
 * provider.initialize(vitestContext);
 * const coverageMap = await provider.generateCoverage({ allTestsRun: true });
 */
function getProvider(): Promise<V8CoverageProvider>;

Behavior:

  • Dynamically imports ./provider.js to avoid bundling the provider
  • Creates and returns a new instance of V8CoverageProvider
  • The provider can then be initialized with Vitest context

Types

ScriptCoverageWithOffset

Extended script coverage type that includes offset information for module wrapper code:

interface ScriptCoverageWithOffset extends Profiler.ScriptCoverage {
  /**
   * Offset for wrapper code added during module execution.
   * Module systems (CommonJS, ESM) often wrap module code with additional code.
   * This offset adjusts coverage ranges to account for that wrapper code.
   */
  startOffset: number;
}

Profiler.ScriptCoverage

V8 script coverage data from Node.js Inspector API:

interface ScriptCoverage {
  /** Unique script identifier assigned by V8 */
  scriptId: string;

  /** URL of the script (e.g., "file:///path/to/file.js") */
  url: string;

  /** Coverage entries for functions in the script */
  functions: FunctionCoverage[];
}

interface FunctionCoverage {
  /** Name of the function (empty string for anonymous functions) */
  functionName: string;

  /** Coverage ranges within the function showing which parts were executed */
  ranges: Range[];

  /**
   * Indicates whether this is block-level coverage (true) or function-level only (false).
   * When true, ranges contain detailed execution counts for code blocks.
   */
  isBlockCoverage: boolean;
}

interface Range {
  /** Start offset in the script source code (in characters) */
  startOffset: number;

  /** End offset in the script source code (in characters) */
  endOffset: number;

  /** Number of times this range was executed */
  count: number;
}

Usage Example

Complete usage example showing the lifecycle:

import coverageModule from '@vitest/coverage-v8';

// 1. Start coverage collection
await coverageModule.startCoverage({ isolate: true });

// 2. Run your test code
// ... test execution happens here ...

// 3. Take coverage snapshot after tests
const { result } = await coverageModule.takeCoverage({
  moduleExecutionInfo: new Map([
    ['/path/to/file.js', { startOffset: 0 }]
  ])
});

console.log(`Collected coverage for ${result.length} files`);

// 4. Stop coverage collection
await coverageModule.stopCoverage({ isolate: true });

// 5. Get provider for processing coverage data
const provider = await coverageModule.getProvider();

Implementation Details

Inspector Session Management

The module maintains a single inspector session:

  • Session is created on first startCoverage call
  • Session is reused across multiple takeCoverage calls
  • Session is only disconnected when stopCoverage is called with isolate: true

Isolate Parameter

The isolate parameter controls session lifecycle:

  • isolate: true - Each test file gets its own coverage session (start/stop for each file)
  • isolate: false - All test files share one coverage session (cumulative coverage)

Vitest uses isolate: false by default to accumulate coverage across all test files.

StackBlitz Handling

On StackBlitz platform, V8 coverage is not supported:

  • takeCoverage returns empty results
  • No coverage data is collected
  • Reports will be empty

URL Filtering

Coverage is filtered to include only relevant files:

  • Must use file:// protocol (excludes internal scripts, eval code)
  • Must not be in node_modules/ (excludes dependency code)

This filtering reduces the amount of data sent over RPC and improves performance.

Platform Requirements

  • Node.js: Requires Node.js with Inspector API support (Node.js 8+)
  • V8 Engine: Must be running on V8 JavaScript engine
  • Not Supported: Bun, non-V8 environments
  • StackBlitz: Coverage collection not available, returns empty results