Skip to content

stevez/vitest-coverage-merge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vitest-coverage-merge

Merge Vitest coverage from unit tests (jsdom) and browser component tests.

📝 UPDATE (January 2025):

As of v0.2.0, normalization is now OFF by default. Use --normalize if you need to strip import statements and directives.

The Problem

When running Vitest with both jsdom (unit tests) and browser mode (component tests), the coverage reports have different statement counts:

Environment Import Handling
jsdom V8 doesn't count imports as statements
Real browser V8 counts imports as executable statements

This makes it impossible to accurately merge coverage without normalization.

The Solution

vitest-coverage-merge provides smart merging of coverage data. When you encounter statement count mismatches, you can use the --normalize flag to strip import statements and Next.js directives ('use client', 'use server') before merging.

Installation

npm install -D vitest-coverage-merge

Usage

CLI

# Merge unit and component coverage
npx vitest-coverage-merge coverage/unit coverage/component -o coverage/merged

# Merge multiple sources
npx vitest-coverage-merge coverage/unit coverage/component coverage/e2e -o coverage/all

# Merge with normalization (strips imports/directives)
npx vitest-coverage-merge coverage/unit coverage/component -o coverage/merged --normalize

Options

vitest-coverage-merge <dir1> <dir2> [dir3...] -o <output>

Arguments:
  <dir1> <dir2>    Coverage directories to merge (at least 2 required)
                   Each directory should contain coverage-final.json

Options:
  -o, --output     Output directory for merged coverage (required)
  --normalize      Strip import statements and directives before merging
  -h, --help       Show help
  -v, --version    Show version

Programmatic API

import { mergeCoverage, normalizeCoverage } from 'vitest-coverage-merge'

// Merge coverage directories
const result = await mergeCoverage({
  inputDirs: ['coverage/unit', 'coverage/component'],
  outputDir: 'coverage/merged',
  normalize: false, // default (set to true to strip imports/directives)
  reporters: ['json', 'lcov', 'html'], // default
})

console.log(result.statements.pct) // e.g., 85.5

Example Vitest Setup

vitest.config.ts (unit tests)

import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    environment: 'jsdom',
    include: ['src/**/*.test.{ts,tsx}'],
    exclude: ['src/**/*.browser.test.{ts,tsx}'],
    coverage: {
      enabled: true,
      provider: 'v8',
      reportsDirectory: './coverage/unit',
      reporter: ['json', 'lcov', 'html'],
    },
  },
})

vitest.component.config.ts (browser tests)

import { defineConfig } from 'vitest/config'
import { playwright } from '@vitest/browser-playwright'

export default defineConfig({
  test: {
    include: ['src/**/*.browser.test.{ts,tsx}'],
    coverage: {
      enabled: true,
      provider: 'v8',
      reportsDirectory: './coverage/component',
      reporter: ['json', 'lcov', 'html'],
    },
    browser: {
      enabled: true,
      provider: playwright(),
      instances: [{ browser: 'chromium' }],
    },
  },
})

package.json scripts

{
  "scripts": {
    "test": "npm run test:unit && npm run test:component",
    "test:unit": "vitest run",
    "test:component": "vitest run --config vitest.component.config.ts",
    "coverage:merge": "vitest-coverage-merge coverage/unit coverage/component -o coverage/merged"
  }
}

Output

The tool generates:

  • coverage-final.json - Istanbul coverage data
  • lcov.info - LCOV format for CI tools
  • index.html - HTML report (in lcov-report folder)

How It Works

  1. Load coverage-final.json from each input directory
  2. Normalize (optional, with --normalize flag) by stripping:
    • ESM import statements (import ... from '...')
    • React/Next.js directives ('use client', 'use server') - if present
  3. Smart merge using one of two strategies:
    • Default (no --normalize): "More items wins" - prefers source with more coverage items, giving you the union of all structures
    • With --normalize: "Fewer items wins" - prefers sources without directive statements (browser-style coverage)
  4. Merge execution counts using max strategy (takes highest count for each item)
  5. Generate reports (JSON, LCOV, HTML)

Note: This tool works with any ESM-based Vitest project (React, Vue, Svelte, vanilla JS/TS, etc.). The React/Next.js directive stripping only applies if those directives are present in your codebase - for non-React projects, it simply has no effect.

Why Not Use Vitest's Built-in Merge?

Vitest's --merge-reports is designed for sharded test runs, not for merging coverage from different environments (jsdom vs browser). It doesn't handle the statement count differences caused by how V8 treats imports differently in each environment.

Related Tools

License

MIT

About

Merge Vitest coverage from unit tests (jsdom) and browser component tests with automatic normalization

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors