This GitHub Action reports vitest coverage results as a GitHub step-summary and as a comment on a pull request.
The action generates a high-level coverage summary for all coverage categories, as well as a detailed, file-based report. The report includes links to the files themselves and the uncovered lines for easy reference.
Want to contribute? Check out the Contributing Guidelines.
To use this action, you need to configure vitest
to create a coverage report with the following reporters:
json-summary
(required): This reporter generates a high-level summary of your overall coverage.json
(optional): If provided, this reporter generates file-specific coverage reports for each file in your project.
You can configure the reporters in your Vite configuration file (e.g., vite.config.js
) as follows:
import { defineConfig } from 'vite';
export default defineConfig({
test: {
coverage: {
// you can include other reporters, but 'json-summary' is required, json is recommended
reporter: ['text', 'json-summary', 'json'],
// If you want a coverage reports even if your tests are failing, include the reportOnFailure option
reportOnFailure: true,
}
}
});
Then execute npx vitest --coverage.enabled true
in a step before this action.
name: 'Test'
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
permissions:
# Required to checkout the code
contents: read
# Required to put a comment into the pull-request
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: 'Install Node'
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: 'Install Deps'
run: npm install
- name: 'Test'
run: npx vitest --coverage.enabled true
- name: 'Report Coverage'
# Set if: always() to also generate the report if tests are failing
# Only works if you set `reportOnFailure: true` in your vite config as specified above
if: always()
uses: davelosert/vitest-coverage-report-action@v2
Note
To enable comments on pull requests originating from forks, please refer to the configuration provided in the Working with Pull Requests from Forks section.
This action requires the pull-request: write
permission to add a comment to your pull request. If you're using the default GITHUB_TOKEN
, ensure that you include both pull-request: write
and contents: read
permissions in the job. The contents: read
permission is necessary for the actions/checkout
action to checkout the repository. This is particularly important for new repositories created after GitHub's announcement to change the default permissions to read-only
for all new GITHUB_TOKEN
s.
Option | Description | Default |
---|---|---|
working-directory |
The main path to search for coverage- and configuration files (adjusting this is especially useful in monorepos). | ./ |
json-summary-path |
The path to the json summary file. | ${working-directory}/coverage/coverage-summary.json |
json-final-path |
The path to the json final file. | ${working-directory}/coverage/coverage-final.json |
json-summary-compare-path |
The path to the json summary file to compare against. If given, will display a trend indicator and the difference in the summary. Respects the working-directory option. |
undefined |
vite-config-path |
The path to the vite config file. Will check the same paths as vite and vitest | Checks pattern ${working-directory}/vite[st].config.{t|mt|ct|j|mj|cj}s |
github-token |
A GitHub access token with permissions to write to issues (defaults to secrets.GITHUB_TOKEN ). |
${{ github.token }} |
file-coverage-mode |
Defines how file-based coverage is reported. Possible values are all , changes or none . |
changes |
file-coverage-root-path |
The root (or absolute) part of the path used within the json coverage reports to point to the covered files. You can change this if your reports were generated in a different context (e.g., a docker container) and the absolute paths don't match the current runner's workspace. Uses the runner's workspace path by default. | ${{ github.workspace }} |
name |
Give the report a custom name. This is useful if you want multiple reports for different test suites within the same PR. Needs to be unique. | '' |
pr-number |
The number of the PR to post a comment to. When using the push trigger, you can set this option to "auto" to make the action automaticaly search of a PR with a matching sha value and comment on it. |
If in the context of a PR, the number of that PR. If in the context of a triggered workflow, the PR of the triggering workflow. If no PR context is found, it defaults to undefined |
comment-on |
Specify where you want a comment to appear: "pr" for pull-request (if one can be found), "commit" for the commit in which context the action was run, or "none" for no comments. You can provide a comma-separated list of "pr" and "commit" to comment on both. | pr |
changes
- show Files coverage only for project files changed in that pull request (works only withpull_request
,pull_request_review
,pull_request_review_comment
actions)all
- show it grouped by changed and not changed files in that pull request (works only withpull_request
,pull_request_review
,pull_request_review_comment
actions)none
- do not show any File coverage details (only total Summary)
If your project includes multiple test suites and you want to consolidate their coverage reports into a single pull request comment, you must assign a unique name
to each action step that parses a summary report. For example:
## ...
- name: 'Report Frontend Coverage'
if: always() # Also generate the report if tests are failing
uses: davelosert/vitest-coverage-report-action@v2
with:
name: 'Frontend'
json-summary-path: './coverage/coverage-summary-frontend.json'
json-final-path: './coverage/coverage-final-frontend.json
- name: 'Report Backend Coverage'
if: always() # Also generate the report if tests are failing
uses: davelosert/vitest-coverage-report-action@v2
with:
name: 'Backend'
json-summary-path: './coverage/coverage-summary-backend.json'
json-final-path: './coverage/coverage-final-backend.json'
Warning
Currently, this action does not import the vite-configuration, but parses it as string to extract the coverage-thresholds by an regexp. In other words: All thresholds need to be directly defined in the config-file given to this action through the vite-config-path input. E.g., when using workspace to extend a parent-configuration, the thresholds can not be defined in the parent-config.
This action reads the coverage thresholds specified in the coverage
property of the Vite configuration file. It then uses these thresholds to determine the status of the generated report.
For instance, consider the following configuration:
import { defineConfig } from 'vite';
export default defineConfig({
test: {
coverage: {
thresholds: {
lines: 60,
branches: 60,
functions: 60,
statements: 60
}
}
}
});
With the above configuration, the report would appear as follows:
If no thresholds are defined, the status will display as '🔵'.
By using the json-summary-compare-path
option, the action will display both a trend indicator and the coverage difference in the summary. This feature is particularly useful for tracking changes between the main branch and a previous run.
The most straightforward method to obtain the comparison file within a pull request is to run the tests and generate the coverage for the target branch within a matrix job:
name: "Test"
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- branch: main
artifact: main
- branch: ${{ github.head_ref }}
artifact: pull-request
permissions:
# Required to checkout the code
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: ${{ matrix.branch }}
## Set repository to correctly checkout from forks
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: "Install Node"
uses: actions/setup-node@v4
with:
node-version: "20.x"
- name: "Install Deps"
run: npm install
- name: "Test"
run: npx vitest --coverage.enabled true
- name: "Upload Coverage"
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.artifact }}
path: coverage
report-coverage:
needs: test
runs-on: ubuntu-latest
steps:
- name: "Download Coverage Artifacts"
uses: actions/download-artifact@v4
with:
name: coverage-pull-request
path: coverage
- uses: actions/download-artifact@v4
with:
name: coverage-main
path: coverage-main
- name: "Report Coverage"
uses: davelosert/vitest-coverage-report-action@v2
with:
json-summary-compare-path: coverage-main/coverage-summary.json
If you're using a monorepo with Vitest Workspaces and running Vitest from your project's root, Vitest will disregard the coverage
property in individual project-level Vite configuration files. This is because some configuration options, such as coverage, apply to the entire workspace and are not allowed in a project config.
In such cases, you can create a Vite configuration file at the root of your project, alongside your vitest.workspace.js
file, to configure coverage for the entire workspace:
import { defineConfig } from 'vite';
export default defineConfig({
test: {
coverage: {
// you can include other reporters, but 'json-summary' is required, json is recommended
reporter: ['text', 'json-summary', 'json'],
}
}
});
Alternatively, you can supply coverage options directly to the CLI using dot notation:
npx vitest --coverage.enabled --coverage.provider=v8 --coverage.reporter=json-summary --coverage.reporter=json
Due to security considerations, GitHub Actions does not provide workflows originating from a fork with write access to your repository, even if such permissions are configured. Consequently, this action cannot comment on these pull requests using the above-documented configuration.
For more information on why this is the case, refer to the following article: Preventing Pwn-Requests.
However, you can circumvent this limitation by dividing your workflow into two separate workflows (see examples below):
-
Testing Workflow: This workflow runs tests in response to the
pull_request
trigger, within the context of the actual pull request, and uploads the coverage reports as artifacts. -
Reporting Workflow: This workflow is triggered upon the completion of the Testing Workflow using the
workflow_runs
event. It downloads and parses the coverage report, and posts a comment on the pull request.
Important
The Reporting Workflow must reside within your default branch (as specified in GitHub's workflow_run documentation)
This action will automatically detect:
- If it is being run within a
workflow_run
trigger - If the triggering workflow was a pull request
It will then automatically locate the appropriate pull request to comment on.
-
test.yml
name: "Test" on: pull_request: jobs: test: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@v4 - name: "Install Node" uses: actions/setup-node@v4 with: node-version: "20.x" - name: "Install Deps" run: npm install - name: "Test" run: npx vitest --coverage.enabled true - name: "Upload Coverage" uses: actions/upload-artifact@v4 with: name: coverage path: coverage
-
coverage.yml (has to be on the default branch)
name: Report Coverage on: workflow_run: workflows: ["Test"] types: - completed jobs: report: runs-on: ubuntu-latest permissions: pull-requests: write steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} - name: "Report Coverage" uses: davelosert/vitest-coverage-report-action@v2
Note
This configuration also works for pull requests originating from your own repository (not forks), so it can be used generally.
This approach has a few limitations:
- The Reporting Workflow is only triggered after the Testing Workflow completes. As a result, there will be a (most likely neglectable) delay before a comment appears on the pull request.
- To obtain the pull request number from a forked pull request, it's necessary to iterate over all pull requests in the repository using the Pulls REST API and match it by the
head_sha
. This is due to thegithub
context of the triggering workflow not containing the pull request information (see this discussion). While this is generally not an issue, it could cause delays if the repository is large and the pull request is significantly old. - Since the Reporting Workflow runs in the context of your repository's default branch, changes to your coverage threshold won't be reflected in the pull request comment. This can be mitigated by also uploading the Vite config as an artifact in the Testing Workflow.