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-junitshould 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 | 
| text-instead-badge | false | Use simple text instead of badge images for coverage display | 
| 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.jsonfiles 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.xmlfiles 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@mainThis 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
    text-instead-badge: false
    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.jsonOutput: 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 ReportOutput: 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-junitConfigure 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 ResultsOutput: 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.xmlOutput: 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 FilesOutput:
- 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/[email protected]
        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/[email protected]
  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 linksLink Removal Options:
- remove-links-to-files: true- Removes clickable links to files. Instead of- [example.js](link), shows plain- example.js
- remove-links-to-lines: true- Removes clickable links to line numbers. Instead of- [14-18](link), shows plain- 14-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 
With text-instead-badge: true:
Lines Statements Branches Functions 78.57% (33/42) 76.74% (33/43) 33.33% (2/6) 100% (0/0) 
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-commentis set tofalse
- Verify the action is running on supported events (pull_request,push,workflow_dispatch,workflow_run)
- For workflow_dispatchorworkflow_runevents, ensureissue-numberinput is provided
Problem: "Comment is too long (maximum is 65536 characters)"
Solutions:
- Use report-only-changed-files: true
- Set hide-summary: trueto show only badge
- Use remove-links-to-files: trueto remove clickable file links
- Use remove-links-to-lines: trueto 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-prefixif 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-numberinput 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 packageMIT © 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!