JavaScript and TypeScript coverage

Use Jest or Vitest to generate LCOV, then upload coverage/lcov.info from GitHub Actions.

Install dependencies

Jest and Vitest both emit LCOV. Use the package manager your project already uses.

npm install --save-dev jest
pnpm add -D vitest @vitest/coverage-v8
yarn add --dev vitest @vitest/coverage-v8

Generate coverage

Make sure your test script writes LCOV to coverage/lcov.info.

npm test -- --coverage --coverageReporters=lcov
pnpm vitest run --coverage --coverage.reporter=lcov
yarn vitest run --coverage --coverage.reporter=lcov

Jest GitHub Actions workflow

Add COVERAGE_UPLOAD_TOKEN as an organization or repository secret. The action uses the default upload endpoint unless you provide an optional endpoint override.

name: coverage

on:
  pull_request:
  push:

jobs:
  coverage:
    if: github.event_name == 'pull_request' || github.ref_name == github.event.repository.default_branch
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm test -- --coverage --coverageReporters=lcov
      - name: Upload coverage
        uses: 9to5/9to5-coverage-action@v1
        with:
          token: ${{ secrets.COVERAGE_UPLOAD_TOKEN }}
          path: coverage/lcov.info

Vitest GitHub Actions workflow

For npm projects, install Vitest and its matching coverage provider with npm install --save-dev vitest @vitest/coverage-v8 and commit the lockfile. LCOV must be requested explicitly. Run from the repository root; adapt the source include pattern to your project.

// vitest.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'lcov'],
      include: ['src/**/*.{js,jsx,ts,tsx}'],
    },
  },
})
name: coverage
on:
  pull_request:
  push:
jobs:
  coverage:
    if: github.event_name == 'pull_request' || github.ref_name == github.event.repository.default_branch
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npx vitest run --coverage
      - name: Upload coverage
        uses: 9to5/9to5-coverage-action@v1
        with:
          token: ${{ secrets.COVERAGE_UPLOAD_TOKEN }}
          path: coverage/lcov.info

Including source files explicitly keeps unimported files visible in coverage. See the Vitest coverage guide for provider compatibility.

Pull request behavior

9to5 coverage posts project and patch checks to the uploaded commit and comments when changed lines are uncovered.

Troubleshooting

If the upload says the file is missing, confirm the test command created coverage/lcov.info. If authentication fails, rotate the upload token and update the GitHub secret.