# Share a Karma or Angular Test Report — One Link From CI

Canonical: https://commareports.com/share-karma-report
Published: 2026-08-29

> ng test --code-coverage writes a coverage folder that dies with the CI runner. Publish it to a URL: annotated source at a stable link, threads on the uncovered branch, a revision per run.

# Share a Karma or Angular test report

`ng test --code-coverage` produces a genuinely useful artifact: your
source, line by line, with what the specs touched and what they didn't.
It answers the question a coverage percentage never does — _which_ branch
is untested, and does that branch matter?

Then it lands in `coverage/` on a CI runner, gets zipped as an artifact,
and is never opened by anyone.

## Generate it

```bash
ng test --watch=false --code-coverage
# → coverage/<project-name>/index.html
```

Under a bare Karma setup, `karma-coverage` does the same thing with the
same Istanbul output. For pass/fail results rather than coverage,
`karma-htmlfile-reporter` writes a **single self-contained file**, which
is the easier shape to publish.

The coverage output is a **directory**:

```
coverage/my-app/
├── index.html
├── src/app/…/foo.component.ts.html   # one page per source file
├── base.css  prettify.css
└── prettify.js  sorter.js  block-navigation.js
```

That's the detail that breaks the naive share: publish `index.html` alone
and you get an unstyled index whose every drill-down link is dead.

## Drop the folder in

Drag `coverage/my-app/` (or a zip of it) into
[Comma](https://commareports.com/):

- `index.html` becomes the **report body** — the page carrying the comment
  layer.
- The per-file pages, CSS and JS upload alongside it, and relative
  references are rewritten to the uploaded copies, so the drill-down from
  summary to directory to annotated source resolves.
- Scripts run inside a sandboxed iframe (`allow-scripts`, no
  `allow-same-origin`), so the sortable columns and the
  next/previous-uncovered-block navigation keep working.

## From CI

```bash
ng test --watch=false --browsers=ChromeHeadless --code-coverage || true

curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html coverage/my-app/index.html \
        --arg title "Coverage — $(git rev-parse --short HEAD)" \
        '{title: $title, html: $html}')"
```

The API is JSON-only, so the per-file pages go up through
`POST /api/v1/reports/$REPORT_ID/assets` as base64 — one call per file,
worth scripting once. See the [API reference](/docs/api).

The `|| true` matters: a failing spec fails the step, and the publish has
to survive it. Add `if: always()` in GitHub Actions, `after_script` in
GitLab, `post { always { … } }` in Jenkins. Use a
[scoped token](/docs/api-tokens) (`reports:write`) from CI secrets and
`PATCH` a saved report id, so one URL accumulates a revision per run
instead of a new orphan link every build. See
[publishing from CI](/docs/ci).

## What the URL changes

- **The PR links the coverage.** Reviewers click instead of rebuilding.
- **Feedback lands on the line.** Select the uncovered branch, leave a
  thread — "this is the error path in the interceptor" — and it stays
  anchored there across runs. See [commenting on HTML](/comment-on-html).
- **Revision diffs beat the percentage.** Two runs, one diff, which lines
  actually changed coverage rather than a number drifting by a third of a
  point.

## Who can see it

The coverage report **contains your source code**. Access is per report —
private, your team, anyone signed in at your domain, or anyone with the
link — and team or private is the right default for a private repo (domain-gating is
Enterprise). See
[sharing & access control](/docs/sharing).

## Limits

- **Entry HTML: 5 MB.** Assets: 25 MB per file, **250 MB and 500 files
  total**. A large Angular app can exceed 500 source pages — publish the
  feature module under review rather than the whole tree.
- **Scripts run, sandboxed** — no same-origin access.
- **60 requests/minute per token.**

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited
revision history.

**[Publish a coverage report →](https://commareports.com/)**

### Related

- [Share a coverage report](/share-coverage-report) · [Share an lcov report](/share-lcov-report)
- [Share a Jest or Vitest report](/share-jest-report) · [Share a Cypress report](/share-cypress-report)
- [Share a Storybook build](/share-storybook-static) — the other half of component review
- [Publish from CI](/ci) · [GitHub Actions HTML reports](/ci/github-actions-html-report)
