Skip to content

feat(core): add runReport to onTestRunEnd payload#1288

Open
fi3ework wants to merge 4 commits into
mainfrom
feat-run-report
Open

feat(core): add runReport to onTestRunEnd payload#1288
fi3ework wants to merge 4 commits into
mainfrom
feat-run-report

Conversation

@fi3ework
Copy link
Copy Markdown
Member

@fi3ework fi3ework commented May 20, 2026

Summary

Adds a precomputed runReport field to the Reporter.onTestRunEnd payload, so reporters and the CLI exit-code logic share one run-level summary instead of each filtering the raw results / testResults arrays. Matches the contract style used by Jest (AggregatedResult) and Vitest (reason + unhandledErrors).

What this gives custom reporters

  • New runReport: RunReport field carrying status ('pass' | 'fail'), pre-aggregated counts, the flattened failures list, the serialized unhandledErrors, and duration / snapshot.
  • New public type exports from @rstest/core: RunReport, FailureItem, FormattedError.
  • All previously-documented onTestRunEnd payload fields (results, testResults, duration, snapshotSummary, unhandledErrors, filterRerunTestPaths) are preserved — custom reporters that destructure only those keep compiling and behaving the same.

What bug this prevents

Before this PR each reporter computed "did this run pass?" itself, and they had drifted: the markdown reporter ignored passWithNoTests in the no-tests case while the JSON reporter honored it, so the same run could report pass and fail in different outputs. With buildRunReport as the single source of truth this class of drift is no longer possible — adding a new reporter (or a new no-tests / unhandled-error edge case) only needs to be expressed once.

Behavior changes a reviewer should know about

  • runReport: RunReport is required on the payload type (matches Jest's AggregatedResult and Vitest's reason, which are also required). TS structural typing means existing implements Reporter classes that don't destructure it keep compiling; the only break is hand-typing Reporter['onTestRunEnd'] and manually calling it without the field — an extremely uncommon pattern.
  • Watch-mode + no-tests now correctly leaves process.exitCode alone (it would have been set to 1 by the new predicate otherwise, contradicting reportNoTestFiles's long-standing watch behavior).
  • runReport.unhandledErrors preserves the full FormattedError shape (diff/expected/actual/fullStack), so reporters that print assertion diffs for run-level errors keep working.

Compatibility

  • No existing built-in reporter changes output for any input it previously handled correctly.
  • Custom reporters using Reporter typing: no changes needed.
  • New docs in website/docs/{en,zh}/api/javascript-api/{reporter,types}.mdx.

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).

Reporters and the runner previously each re-derived run-level pass/fail
status and counts by filtering raw arrays. This caused subtle drift —
e.g. the markdown reporter did not honor `passWithNoTests` when no tests
were discovered, while the JSON reporter did.

The `Reporter.onTestRunEnd` payload now carries a precomputed `runReport`
with `status`, `counts`, `failures`, and serialized `unhandledErrors`.
Built-in reporters (default, dot, md, json, junit, github-actions, blob,
mergeReports) read from `runReport` directly, and `runTests` derives the
process exit code from `runReport.status` so all consumers stay aligned.

This matches the contract used by Jest (`AggregatedResult`) and Vitest
(`reason: 'passed' | 'interrupted' | 'failed'` + `unhandledErrors`), and
exposes `RunReport` / `FailureItem` as public types so custom reporters
can consume the same data.

The previously documented payload fields (`results`, `testResults`,
`duration`, `snapshotSummary`, `unhandledErrors`, etc.) are preserved.
Custom reporters that destructure only those fields continue to work
without changes.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying rstest with  Cloudflare Pages  Cloudflare Pages

Latest commit: 529596f
Status:🚫  Build failed.

View logs

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 529596faed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/core/src/reporter/index.ts
Comment thread packages/core/src/core/runTests.ts
Comment thread packages/core/src/core/runTests.ts Outdated
Three issues caught by Codex review on the previous commit:

1. Browser hostController called `onTestRunEnd` without `runReport`, so
   built-in reporters that destructure it (md/json/junit/githubActions/
   blob/default/dot) crashed in browser-only runs. Browser package now
   imports `buildRunReport` from `@rstest/core/browser` and constructs
   `runReport` at both the early-error path and the main `notifyTestRunEnd`
   path. This also unblocks `packages/browser` build, which is what was
   failing CI.

2. Watch-mode "no tests discovered" was setting `process.exitCode = 1`
   because `buildRunReport` marked the run as `fail`. `reportNoTestFiles`
   intentionally does not set an exit code in watch mode, so the runner
   and browser host now pass `passWithNoTests: isWatchMode || config.passWithNoTests`
   when building the report.

3. `runReport.failures` previously applied `filterRerunTestPaths`, which
   was derived from node-only `currentEntries`. In unified node+browser
   runs that filter dropped browser failures from `runReport.failures`,
   so reporters consuming it (notably GitHub Actions annotations) lost
   browser failure visibility. The filter is now removed from
   `buildRunReport` / `collectFailures` and applied in
   `printSummaryErrorLogs` instead, which is the only place the watch
   narrowing actually matters.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 20, 2026

Rsdoctor Bundle Diff Analysis

Found 12 projects in monorepo, 2 projects with changes.

📊 Quick Summary
Project Total Size Change
adapter-rsbuild 3.7 KB 0
adapter-rslib 24.7 KB 0
adapter-rspack 7.8 KB 0
browser 2.0 MB +826.0 B (0.0%)
browser-react 3.7 KB 0
browser-ui 810.6 KB 0
coverage-istanbul 9.6 KB 0
core/browser 970.1 KB 0
core/loaders 869.0 B 0
core/main 1.7 MB +1.2 KB (0.1%)
vscode/extension 26.9 MB 0
vscode/worker 14.4 KB 0
📋 Detailed Reports (Click to expand)

📁 browser

Path: packages/browser/.rsdoctor/rsdoctor-data.json

📌 Baseline Commit: 7938535556 | PR: #1286

Metric Current Baseline Change
📊 Total Size 2.0 MB 2.0 MB +826.0 B (0.0%)
📄 JavaScript 2.0 MB 2.0 MB +826.0 B (0.0%)
🎨 CSS 11.8 KB 11.8 KB 0
🌐 HTML 765.0 B 765.0 B 0
📁 Other Assets 96.0 B 96.0 B 0

📦 Download Diff Report: browser Bundle Diff

📁 core/main

Path: packages/core/.rsdoctor/main/rsdoctor-data.json

📌 Baseline Commit: 7938535556 | PR: #1286

Metric Current Baseline Change
📊 Total Size 1.7 MB 1.7 MB +1.2 KB (0.1%)
📄 JavaScript 1.6 MB 1.6 MB +1.2 KB (0.1%)
🎨 CSS 0 B 0 B 0
🌐 HTML 0 B 0 B 0
📁 Other Assets 81.6 KB 81.6 KB 0

📦 Download Diff Report: core/main Bundle Diff

Generated by Rsdoctor GitHub Action

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7ab99687e3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/core/src/reporter/md.ts Outdated
Comment thread packages/core/src/reporter/runReport.ts
Comment thread packages/core/src/types/reporter.ts
… rerun

Two issues caught by Codex review on commit 7ab9968:

1. `MdReporter.onTestRunEnd` consumed unfiltered `runReport.failures`,
   so in watch mode the markdown report kept rendering stale failures
   from previous reruns. MdReporter now destructures the existing
   `filterRerunTestPaths` payload field and runs `filterFailuresByPaths`
   to narrow the list, matching the behavior already wired through
   `printSummaryErrorLogs` for DefaultReporter / DotReporter.

2. `RunReport.unhandledErrors` was flattened to `{message, stack, name}`,
   silently dropping `diff` / `expected` / `actual` / `fullStack` from
   `FormattedError`. `printError` and reporter output relied on those
   fields for assertion diffs and `DEBUG=rstest` hints. The type is now
   `FormattedError[]`, and `buildRunReport` preserves the extra fields.
   `FormattedError` is added to the public `@rstest/core` type exports.

The (Error | FormattedError)[] union initially proposed for
`BuildRunReportInput.unhandledErrors` was collapsed to `FormattedError[]`
because `Error` is structurally a subtype (only `message` is required on
`FormattedError`), eliminating an `as Partial<FormattedError>` cast.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 632db13f8b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/core/src/core/runTests.ts Outdated
Comment thread packages/core/src/core/mergeReports.ts
- runTests: feed buildRunReport the local per-run results/testResults
  arrays instead of context.reporterResults (which dedupes by testPath
  and would drop status from multi-project same-path runs).
- mergeReports: when reconstructing Error from blob.unhandledErrors,
  also copy the FormattedError fields (diff/expected/actual/fullStack)
  serialized by BlobReporter so sharded runs keep assertion detail.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d442bdbefd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +762 to +763
results,
testResults,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Build runReport from the same data passed to reporters

runReport is computed from the per-rerun results/testResults arrays here, but notifyReportersOnTestRunEnd still sends context.reporterResults to onTestRunEnd; in watch reruns these two datasets diverge. That creates contradictory payloads (for example JSON/JUnit can report status: pass and zero failures from runReport while files still include failing entries from earlier runs), which can break downstream tooling that trusts runReport as the canonical run summary.

Useful? React with 👍 / 👎.

Comment on lines +245 to +246
tests: counts.tests,
failures: counts.failedTests,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep JUnit root counters aligned with emitted suites

This reporter now fills <testsuites tests/failures/skipped> from runReport.counts, while individual <testsuite>/<testcase> blocks are still generated from the results payload. In watch reruns (where runReport summarizes the current rerun but results can still be the accumulated reporter state), the root counters can disagree with the actual emitted testcases, which causes inconsistent JUnit artifacts for CI parsers and trend tooling.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant