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 theassets/directory alongside the HTML; relativesrc/hrefreferences 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.createdposts 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
teamorprivate, 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.
Related
- Share a Playwright report — the same pattern, different runner
- Share an interactive HTML report — what runs inside the sandbox and what doesn't
- Publish from CI — the general pipeline pattern
- Comment on an HTML report — how anchored threads work