# Add Test Coverage Reporting to Nuxt CI

Yes. An agent runs your Nuxt unit tests with v8 coverage, adds a threshold and a coverage artifact to the GitHub Actions workflow, and opens a pull request.

[Run this on Rush](https://cloud.getrush.ai/run/add-coverage-to-ci/nuxt/), in a Project that has your repository.

## What the agent does

The agent starts from what the repository already has. In a Nuxt app shaped like nuxt/movies, `package.json` defines `test:unit` as `vitest`, `test:unit-coverage` as `vitest --coverage` and `test:unit-web` as `vitest --coverage --ui`, with `@vitest/coverage-v8` 4.1.11 pinned beside `vitest` 4.1.11 and `@nuxt/test-utils` 4.2.0 in `devDependencies`. `vitest.config.ts` uses `defineVitestConfig` from `@nuxt/test-utils/config`, names the project `unit`, includes `**/tests/unit/**/*.test.ts` plus `**/components/**/*.test.ts` and `**/composables/**/*.test.ts`, and already has a `coverage.include` of `**/components/**/*.vue` and `**/composables/**/*.ts`. The tests exist: 13 `*.nuxt.test.ts` files sit next to the components and composables under `app/`, such as `app/components/StarsRate.nuxt.test.ts`, which mounts the component with `@vue/test-utils` and asserts the `clip-path` of the filled stars image. What is missing is the number and the gate.

`.github/workflows/tests-unit.yml` checks out the code, sets up pnpm through `pnpm/setup` with caching, runs `pnpm run test:unit`, then `pnpm run lint` and `pnpm run typecheck`. No coverage is collected and nothing is uploaded, while the sibling `tests-e2e.yml` already uploads a `playwright-report` artifact with a 30-day retention. The agent runs `pnpm install` with the pnpm version `devEngines` pins, then `pnpm run test:unit-coverage` in the sandbox and reads the total line, branch and function percentages from the v8 summary for the included `.vue` and `.ts` files.

With those numbers it edits two files. In `vitest.config.ts` it adds `coverage.reporter` set to `text`, `lcov` and `json-summary` and a `coverage.thresholds` block with the measured figures rounded down, so the suite fails when coverage drops below where it is today rather than at an aspirational number nobody has reached. In `tests-unit.yml` it changes the test step to `pnpm run test:unit-coverage` and adds an `actions/upload-artifact` step for the `coverage/` directory, pinned by commit hash the way the workflow pins `actions/checkout` and `pnpm/setup`, with a retention matching the e2e workflow. The `coverage.include` list stays as it is, so `app/pages/` and `server/api/index.ts`, which have no unit tests, do not drag the number down.

It runs `pnpm run lint` and `pnpm run typecheck` as the workflow does, because `eslint.config.js` extends `@antfu/eslint-config` and will object to a stray comma in the Vitest config, and it adds `coverage/` to `.gitignore` if it is not there. The pull request carries the measured numbers in its description and the checks from the edited workflow run against it.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Add coverage reporting to this Nuxt project's CI. Read package.json, vitest.config.ts, .github/workflows/tests-unit.yml and .github/workflows/tests-e2e.yml first. Run pnpm install and pnpm run test:unit-coverage in the sandbox and record the line, branch, function and statement totals for the files coverage.include already names. In vitest.config.ts add coverage.reporter with text, lcov and json-summary, and coverage.thresholds set to the measured totals rounded down to whole numbers; do not change coverage.include. In tests-unit.yml switch the test step to pnpm run test:unit-coverage and add an upload-artifact step for the coverage directory, pinned by commit hash like the other actions, with the same retention the e2e workflow uses. Add coverage/ to .gitignore if missing. Run pnpm run lint and pnpm run typecheck. Open a pull request whose description quotes the measured totals and explains that the thresholds hold the current level. Do not write new tests and do not change any test file.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request on a new branch that changes `vitest.config.ts`, `.github/workflows/tests-unit.yml` and, if needed, `.gitignore`. The description quotes the totals the sandbox measured for the components and composables, states the thresholds that were set from them, and shows the `upload-artifact` step with its pinned hash. No test file changes and no dependency is added, because `@vitest/coverage-v8` is already in `package.json`.

The session read names the repository and the pull request URL; the transcript keeps the `test:unit-coverage` run with its coverage table, and the `lint` and `typecheck` runs. Once the pull request's own workflow runs, the `coverage` artifact on that run is the first one the project has.

Before merging, a reviewer compares the thresholds against the quoted totals, decides whether to round down by a point or two to leave room for refactors, and checks that `coverage.include` still excludes the pages, which would otherwise show as untested and fail the gate immediately.

## Run it on every new Nuxt repository

Start one run per repository from your own script with this body and `resources` pointing at the new repository, with a `request_id`, one UUID kept per repository, so a retry returns the same run. Read the pull request URL from the session when `status` leaves `running`, or from the `run.completed` webhook if the key has a `webhook_url`.

The same request also fits a scratch run from a terminal when someone notices a workflow with no coverage step: send it, keep working, and come back to the pull request. The sandbox parks after the turn, so a follow-up message such as "raise the branch threshold by five points and tell me which files fall short" continues in the same clone.

## Why run it on Rush?

Measuring coverage for a Nuxt app means a pnpm install at the pinned version, a `nuxt prepare` to generate the `.nuxt/` tsconfig references the project's `tsconfig.json` points at, and a Vitest run through `@nuxt/test-utils` that boots a Nuxt environment for every `.nuxt.test.ts` file. On Rush that happens in the run's own sandbox with the output kept, so the threshold in the pull request comes from a run anyone can read, not from a number typed from memory.

The result is a pull request with the measurement behind it and the checks from the edited workflow, and several repositories can get the same treatment at once, each in its own sandbox. When a threshold later fails, the coverage artifact on that workflow run says which file moved.

## Frequently asked questions

### Where does the threshold number come from?

From running `pnpm run test:unit-coverage` in the sandbox on your current code. The agent rounds each total down and writes it into `coverage.thresholds`, so the gate holds the level you have today; the pull request description quotes the raw numbers so you can lower them before merging.

### Will it write tests to raise coverage?

No. This run only measures and gates. Writing tests for the components under `app/components/media/` that have none is a separate request, and running it after this one gives the new tests a threshold to move.

### Does it need Codecov or another service?

No. The `lcov` and `json-summary` reporters write to `coverage/`, and the workflow uploads that directory as an artifact on each run. If you later add a service, its action reads the same `lcov.info`.

### Why keep the coverage.include list unchanged?

It already limits the measurement to components and composables, the parts the 13 unit tests exercise. Widening it to `app/pages/` would count server-rendered pages with no unit tests and fail the new threshold on the first run.
