Share a Cypress test report

Cypress is excellent at telling you what failed. The Test Runner replays the DOM, the terminal prints a clean summary, the video is right there in cypress/videos/.

None of that survives contact with a teammate. Cypress ships no shareable report format, so the pipeline everyone converges on is cypress-mochawesome-reporter writing an HTML file into cypress/reports/ — a file that then sits inside a CI artifact zip, behind CI login, on a retention clock, with nowhere to discuss it. The last mile is always the same: someone screenshots a red spec into Slack.

The pattern

Store a scoped token — just reports:write — in your CI secret store. Create the report once, keep its id, and PATCH it on every run so there is one URL per suite, not one per run:

npx cypress run --reporter cypress-mochawesome-reporter

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 cypress/reports/html/index.html \
        --arg title "E2E — $GITHUB_SHA" '{title: $title, html: $html}')"

In GitHub Actions, with if: always() — the runs worth sharing are the red ones:

- name: Publish Cypress report to Comma
  if: always()
  env:
    COMMA_API_TOKEN: ${{ secrets.COMMA_API_TOKEN }}
    REPORT_ID: ${{ vars.COMMA_E2E_REPORT_ID }}
  run: |
    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 cypress/reports/html/index.html \
            --arg title "E2E — ${GITHUB_SHA:0:7}" '{title: $title, html: $html}')"

The same two lines work in GitLab CI, CircleCI, Jenkins or Buildkite — it's plain HTTPS. See publishing from CI for the general shape.

Running in parallel

Parallel Cypress runs produce one JSON per machine. Merge before you publish, or you'll publish a third of a test run:

npx mochawesome-merge cypress/reports/*.json > merged.json
npx marge merged.json --inline --reportDir cypress/reports/html

--inline (or inlineAssets: true in the reporter options) is the flag that matters here: it produces a single self-contained HTML file instead of an index.html plus an assets/ directory.

What renders, honestly

Comma stores report HTML verbatim and renders it inside an iframe with sandbox="allow-scripts" and no allow-same-origin. The report's own JavaScript runs — from an opaque origin, with no access to the app's DOM, cookies or storage — so:

  • Interactive — mochawesome's collapsible suites, the passed/failed/pending filters, expandable error stacks.
  • Faithful — the summary counts, spec names, durations, assertion diffs, stack traces, and any screenshot inlined as a data URI.
  • Needs its files — if you skipped --inline, upload the assets/ directory alongside the HTML; relative src/href references are rewritten to the uploaded copies.
  • Capped at 5 MB for the HTML body. A large suite with base64 screenshots inlined can exceed it — attach the screenshots as assets instead of inlining them.

Videos are the other half of the evidence. Attach cypress/videos/*.mp4 as assets so the recording of the failing run lives next to the thread about it.

Comments land on the failing spec

This is the part CI artifacts have never had.

A reviewer opens the link, expands checkout.cy.ts › applies a discount code, highlights the assertion diff, and pins a thread to it: "the coupon field got a new data-cy in #482 — selector needs updating." The thread stays anchored to that failure as revisions accumulate, so when the same spec flakes in three weeks the prior investigation is one click away instead of lost in Slack scrollback. That's the anchored comment model.

The loop closes on the automation side too: an agent in Claude Code or Cursor attached through Comma's MCP server reads the threads with list_comments, pushes a fix, and replies on the thread — same scoped token as the CI publish.

Housekeeping

  • One report per suite, one revision per run. PATCH, don't POST. Title revisions by commit so the history reads like a log.
  • Announce red runs. A webhook on revision.created posts the new revision to Slack or Discord — the Slack message goes back to being a notification, and the report is the artifact.
  • Rate limits are a non-issue. 60 requests/minute per token; one publish per build is nowhere near it.
  • Keep it team-visible. E2E results usually want team or private, not public-by-default — see the sharing model.

Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision history. Take the next red Cypress run you would have zipped, publish the report, and send the link instead.

Create your first report →

Related