A GitHub Action that adds Jest test coverage reports as comments to your pull requests, helping you track and improve test coverage with visual feedback.
- 📊 Visual Coverage Reports - Automatically comments on PRs with detailed coverage tables
- 🏷️ Coverage Badges - Dynamic badges showing coverage percentage with color coding
- 📈 Test Statistics - Shows passed, failed, skipped tests with execution time via JUnit XML
- 🔗 Direct File Links - Click to view uncovered lines directly in your repository
- 📁 Multiple Reports - Support for monorepo with multiple coverage reports
- 🎨 Customizable - Flexible titles, badges, and display options
- 📝 Multi-Format Support - Works with JSON summary, console output, and JUnit XML
- 🚀 Smart Updates - Updates existing comments instead of creating duplicates
Click to expand
Add this action to your workflow:
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
📖 Complete workflow example
name: Jest Coverage Comment
on:
pull_request:
branches:
- '*'
permissions:
contents: read
pull-requests: write
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npx jest --coverage --coverageReporters json-summary
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
📝 Core Inputs
Name | Required | Default | Description |
---|---|---|---|
github-token |
✓ | ${{github.token}} |
GitHub API Access Token |
issue-number |
Pull request number to comment on (required for workflow_dispatch/workflow_run events) | ||
coverage-summary-path |
./coverage/coverage-summary.json |
The location of the coverage-summary of Jest | |
junitxml-path |
The location of the junitxml path (npm package jest-junit should be installed) |
||
coverage-path |
The location of the coverage.txt (Jest console output) |
🎨 Display Options
Name | Default | Description |
---|---|---|
title |
Main title for the comment | |
summary-title |
Title for the coverage summary | |
badge-title |
Coverage |
Title for the badge icon |
junitxml-title |
Title for summary for junitxml | |
coverage-title |
Coverage Report |
Title for the coverage report |
hide-summary |
false |
Hide coverage summary report |
hide-comment |
false |
Hide the whole comment (use when you need only the output ) |
remove-links-to-files |
false |
Remove links to files (useful when summary-report is too big) |
remove-links-to-lines |
false |
Remove links to lines (useful when summary-report is too big) |
🔧 Advanced Options
Name | Default | Description |
---|---|---|
create-new-comment |
false |
When false, will update the same comment, otherwise will publish new comment on each run |
unique-id-for-comment |
When running in a matrix, pass the matrix value, so each comment will be updated its own comment | |
coverage-path-prefix |
Prefix for path when link to files in comment | |
report-only-changed-files |
false |
Show in report only changed files for this commit, and not all files |
multiple-files |
You can pass array of json-summary.json files and generate single comment with table of resultsSingle line should look like Title1, ./path/to/json-summary.json |
|
multiple-junitxml-files |
You can pass array of junit.xml files and generate single comment with table of resultsSingle line should look like Title1, ./path/to/junit.xml |
📤 Available Outputs
Name | Example | Description |
---|---|---|
coverage |
78 |
Percentage of the coverage, get from coverage-summary.json |
color |
yellow |
Color of the percentage. You can see the whole list of badge colors |
summaryHtml |
... |
Markdown table with summary. See the result examples |
tests |
9 |
Total number of tests, get from junitxml |
skipped |
0 |
Total number of skipped tests, get from junitxml |
failures |
0 |
Total number of tests with failures, get from junitxml |
errors |
0 |
Total number of tests with errors, get from junitxml |
time |
2.883 |
Seconds that took to run all the tests, get from junitxml |
lines |
71 |
Lines covered, get from Jest text report |
branches |
100 |
Branches covered, get from Jest text report |
functions |
28 |
Functions covered, get from Jest text report |
statements |
100 |
Statements covered, get from Jest text report |
Standard PR Comment
name: Jest Coverage Comment
on:
pull_request:
jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npx jest --coverage --coverageReporters json-summary
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
This will create a comment showing coverage percentage with a badge and summary table.
Example showing all available parameters
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
coverage-summary-path: ./coverage/coverage-summary.json
title: My Jest Coverage Comment
summary-title: My Summary Title
badge-title: Coverage
hide-comment: false
create-new-comment: false
hide-summary: false
remove-links-to-files: false
remove-links-to-lines: false
junitxml-title: My JUnit Title
junitxml-path: ./coverage/junit.xml
coverage-title: My Coverage Title
coverage-path: ./coverage.txt
coverage-path-prefix: src/
report-only-changed-files: false
Generate summary from JSON coverage report
Configure Jest to generate JSON summary:
// jest.config.js
module.exports = {
collectCoverage: true,
coverageReporters: ['json-summary', 'text', 'html'],
coverageDirectory: 'coverage',
}
Workflow:
- name: Run tests
run: npx jest --coverage
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
with:
coverage-summary-path: ./coverage/coverage-summary.json
Output: Badge with coverage percentage and summary table showing file-by-file coverage.
Generate report from Jest console output with file links
- name: Run tests
run: npx jest --coverage | tee ./coverage.txt && exit ${PIPESTATUS[0]}
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
with:
coverage-path: ./coverage.txt
coverage-title: Detailed Coverage Report
Output: Expandable section with detailed coverage report, including clickable links to files and specific uncovered lines.
Add test results with jest-junit package
Install jest-junit:
npm install --save-dev jest-junit
Configure Jest:
// jest.config.js
module.exports = {
reporters: [
'default',
['jest-junit', { outputDirectory: 'coverage', outputName: 'junit.xml' }],
],
}
Workflow:
- name: Run tests
run: npx jest --coverage
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
with:
junitxml-path: ./coverage/junit.xml
junitxml-title: Test Results
Output: Table showing tests count, skipped, failures, errors, and execution time.
Generate combined report for multiple packages
- name: Run tests for all packages
run: |
cd packages/frontend && npm test -- --coverage --coverageDirectory ../../coverage/frontend
cd packages/backend && npm test -- --coverage --coverageDirectory ../../coverage/backend
cd packages/shared && npm test -- --coverage --coverageDirectory ../../coverage/shared
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
with:
multiple-files: |
Frontend, ./coverage/frontend/coverage-summary.json
Backend, ./coverage/backend/coverage-summary.json
Shared Utils, ./coverage/shared/coverage-summary.json
multiple-junitxml-files: |
Frontend Tests, ./coverage/frontend/junit.xml
Backend Tests, ./coverage/backend/junit.xml
Shared Tests, ./coverage/shared/junit.xml
Output: Combined table showing coverage and test results for all packages.
Multiple Node.js versions with separate comments
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test -- --coverage
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
with:
unique-id-for-comment: node-${{ matrix.node-version }}
title: Coverage Report (Node.js ${{ matrix.node-version }})
Output: Separate coverage comments for each Node.js version, each updating independently.
Manual coverage reporting with issue number
name: Manual Coverage Report
on:
workflow_dispatch:
inputs:
pr_number:
description: 'Pull Request number'
required: true
type: string
jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npx jest --coverage --coverageReporters json-summary
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
with:
issue-number: ${{ github.event.inputs.pr_number }}
Usage: Manually trigger this workflow and provide a PR number to get coverage comments on that specific pull request.
Output: Coverage comment will be posted to the specified pull request, even when not triggered by the PR itself.
Show coverage only for files changed in the PR
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
with:
report-only-changed-files: true
title: Coverage for Changed Files
Output:
- In PR: Shows coverage only for files modified in the PR
- No changes: Shows message "report-only-changed-files is enabled. No files were changed in this commit :)"
Note: Only works with pull_request
and push
events.
Update coverage badge in README without commits
name: Update Coverage Badge
on:
push:
branches: [main]
jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test -- --coverage
- name: Jest Coverage Comment
id: coverage
uses: MishaKav/jest-coverage-comment@main
with:
hide-comment: true
- name: Dynamic Badges
if: github.ref == 'refs/heads/main'
uses: Schneegans/dynamic-badges-action@v1.7.0
with:
auth: ${{ secrets.GIST_SECRET }}
gistID: your-gist-id-here
filename: coverage.json
label: Coverage
message: ${{ steps.coverage.outputs.coverage }}%
color: ${{ steps.coverage.outputs.color }}
📊 Using Output Variables
- name: Jest Coverage Comment
id: coverage
uses: MishaKav/jest-coverage-comment@main
- name: Dynamic Badges
uses: Schneegans/dynamic-badges-action@v1.7.0
with:
auth: ${{ secrets.GIST_SECRET }}
gistID: your-gist-id
filename: coverage.json
label: Coverage
message: ${{ steps.coverage.outputs.coverage }}%
color: ${{ steps.coverage.outputs.color }}
- name: Fail if coverage too low
if: ${{ steps.coverage.outputs.coverage < 80 }}
run: |
echo "Coverage is below 80%!"
exit 1
⚡ Performance Optimization
For large coverage reports that might exceed GitHub's comment size limits:
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
with:
hide-summary: true # Show only badge and test results
report-only-changed-files: true # Only show changed files
remove-links-to-files: true # Remove clickable file links
remove-links-to-lines: true # Remove clickable line number links
Link Removal Options:
remove-links-to-files: true
- Removes clickable links to files. Instead of[example.js](link)
, shows plainexample.js
remove-links-to-lines: true
- Removes clickable links to line numbers. Instead of[14-18](link)
, shows plain14-18
These options significantly reduce comment size while preserving all coverage information.
Coverage badges automatically change color based on the percentage:
Coverage | Badge | Color |
---|---|---|
0-40% | Red | |
40-60% | Orange | |
60-80% | Yellow | |
80-90% | Green | |
90-100% | Bright Green |
View example outputs
Lines Statements Branches Functions 76.74% (33/43) 33.33% (2/6) 100% (0/0)
Tests Skipped Failures Errors Time 6 0 💤 0 ❌ 0 🔥 1.032s ⏱️ My Coverage Title (78%)
File % Stmts % Branch % Funcs % Lines Uncovered Line #s All files 76.74 100 33.33 78.57 src 75.67 100 40 75.67 controller.js 63.63 100 50 63.63 14–18 index.js 85.71 100 0 85.71 9 router.js 100 100 100 100 service.js 69.23 100 50 69.23 16–20 src/utils 83.33 100 0 100 config.js 100 100 100 100 utils.js 75 100 0 100
Common Issues and Solutions
Problem: The action runs successfully but no comment appears on the PR.
Solutions:
- Ensure proper permissions are set:
permissions: contents: read pull-requests: write
- Check if
hide-comment
is set tofalse
- Verify the action is running on supported events (
pull_request
,push
,workflow_dispatch
,workflow_run
) - For
workflow_dispatch
orworkflow_run
events, ensureissue-number
input is provided
Problem: "Comment is too long (maximum is 65536 characters)"
Solutions:
- Use
report-only-changed-files: true
- Set
hide-summary: true
to show only badge - Use
remove-links-to-files: true
to remove clickable file links - Use
remove-links-to-lines: true
to remove clickable line number links - Use
["text-summary", { "skipFull": true }]
in Jest coverage reporters to skip fully covered files
Problem: Jest not collecting coverage for your files
Solutions:
// Check collectCoverageFrom in jest.config.js
collectCoverageFrom: ['src/**/*.{js,ts}', '!src/**/*.test.{js,ts}']
Problem: "No such file or directory" errors
Solutions:
- Use absolute paths or paths relative to
$GITHUB_WORKSPACE
- Check that coverage files are generated before the action runs
- Verify Jest configuration generates required files
Problem: Links in the coverage report point to wrong files or 404
Solutions:
- Use
coverage-path-prefix
if your test paths differ from repository structure - Ensure the action runs on the correct commit SHA
Problem: Need to trigger coverage reporting on specific PRs manually
Solutions:
- Use
issue-number
input to specify the target PR:- name: Jest Coverage Comment uses: MishaKav/jest-coverage-comment@main with: issue-number: 123 # Replace with actual PR number
- For
workflow_dispatch
, add input parameter:on: workflow_dispatch: inputs: pr_number: description: 'Pull Request number' required: true
We welcome all contributions! Please feel free to submit pull requests or open issues for bugs, feature requests, or improvements.
# Clone the repository
git clone https://github.com/MishaKav/jest-coverage-comment.git
cd jest-coverage-comment
# Install dependencies
npm install
# Run tests
npm test
# Build the action
npm run build
# Package for distribution
npm run package
MIT © Misha Kav
For Python projects using pytest: Check out pytest-coverage-comment - a similar action for Python test coverage with pytest.
If you find this action helpful, please consider giving it a ⭐ on GitHub!