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.