You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add a support to report failures which happen session/test setup/tierdown #65
shared test/testsuite setup and some tests are not going to be processed or run. Framework might not even give us the names of the skipped tests
shared test/testsuite tierdown which might also fail and cause original test framwork to fail
In both cases, there is no straight forward place in ctrf report to put such things in except of smuggling the results behind the dummy test failed test case. Can the schema be extended for Results object to support something like 'testFrameworkChecks' which is similar TestObject?
May you please elaborate a little my questions and comments?
To your first item:
IMHO if a framework does not report the tests not being processed or run in some way (e.g. by listing them under results.tests[].status as skipped or pending), I don't see any way to work around this issue without additional framework external tooling. Or did I understood you wrongly?
How is this related to "shared test/testsuite"?
How is this related to "report failures ..." in this issues title?
Regarding the second item: To my understanding of the CTRF spec, ...
a failure in the tests tear down may (or should) be reported as failed by results.tests[].status. There is no distinction between failures in the fixture (set up/tear down) reported as "error" and failures in the test code reported as "failure" as being typical for xUnit frameworks. For this results.tests[].rawStatus could be used.
test suites are not modeled by CTRF and probably won't be in future. So you'll end in inventing your own extra objects to somehow document the affected suite. Maybe under results.environment.extra.<your-namespace>.suitesWithErrors?
a failure in the test run (mentioned here for completeness sake) could be documented by setting results.environment.healthyto false and maybe additional information results.environment.extra.<your-namespace>.runErrors?
Regarding "smuggling the results behind the dummy test failed test case" and "like testFrameworkChecks":
I'm not sure if I understand this correctly: May you please sketch a small CTRF JSON example to give some extra information?
We plan to add optional results.errors support in CTRF 0.3.0 for global errors associated with the run rather than an individual test result, including shared setup and teardown failures:
Each entry can contain optional message, trace and extra fields.
This gives these errors a standard home without creating dummy test results or changing the test counts. Global errors can be reported even when every reported test passed. Failures attributed to an individual test remain on that test result.
We’re keeping the initial shape simple. It does not yet identify the affected suite or tests, distinguish lifecycle phases, or define an overall run status. It also cannot recover tests that the framework never reported, so that part of the question remains open.
Thank you for working on this, @Ma11hewThomas. I opened ctrf-io/playwright-ctrf-json-reporter#123 before I'd seen this thread. I agree run-level errors shouldn't become synthetic tests if we can avoid it. I surveyed how reporters in other ecosystems handle this. The full write-up is below. The research was AI-assisted. Each claim links to pinned source or docs.
Short version: yes to results.errors[].{message, trace, extra} as drafted in #88. It's the dominant pattern wherever a format has somewhere to put run-level errors: xUnit <errors>, Allure Globals.errors, TRX RunInfos, Playwright JSON errors, and RSpec messages. xUnit v3 already ships a CTRF writer that has to park these errors under extra because the field doesn't exist yet.
A few "yes, and" suggestions:
And make a non-empty list a dependable signal. Suggested wording: a producer SHOULD emit at least one results.errors entry when the framework reports the run as unsuccessful but no test result failed (for example, Playwright's FullResult.status of failed, timedout, or interrupted). Some runs fail with only a status and no error object. Without this rule, those runs produce a report that looks green, and the "Consumers MUST account for reported global errors" text in Add global errors to results #88 has nothing to act on.
And define merge semantics. Suggested wording: when merging documents (shards), results.errors SHOULD be concatenated, not de-duplicated, and producers MAY record the shard in extra. Playwright's merge-reports and Allure 3 both concatenate. Today ctrf merge and ctrf-js merge() keep only the first document's extra and environment, so errors from later shards are silently dropped.
And, as a later follow-up, an optional run outcome. Every format that keeps errors separate from tests and still wants the report to carry a verdict adds one: TRX ResultSummary/@outcome, Cucumber TestRunFinished.success, Jest success, node:test test:summary.success, and Datadog session status. I don't think this should block 0.3.0, but it's the natural next step for the "overall-run-outcome questions" that Add global errors to results #88 leaves open.
And plan consumer adoption alongside the release. None of the CI consumers I checked read run-level error fields. That includes ctrf-io/github-test-reporter, which gates exit-on-fail only on summary.failed. Having exit-on-fail also fail on a non-empty results.errors would put the MUST into practice for the most common CTRF consumer.
Once this lands, I'm happy to rework playwright-ctrf-json-reporter#123 to emit results.errors this way.
Report: how test reporters represent run-level failures
Context
playwright-ctrf-json-reporter#123 adds a synthetic failed test when Playwright's run status (FullResult.status) isn't passed and no test failed.
That alignment is now concrete: ctrf#88 is open and adds results.errors: [{message, trace, extra}]. Its diff says "Global errors MUST NOT be counted as test results" and "Consumers MUST account for reported global errors when interpreting the run". The PR body says "overall-run-outcome questions remain open."
So playwright-ctrf-json-reporter#123 as written would contradict the upcoming spec text.
Finding 1: frameworks split into three patterns, decided mostly by output format
Pattern A: a synthetic test entry. Almost all of these write JUnit XML, which has no top-level place for errors.
jest-junit adds "Test execution failure: could be caused by test hooks like 'afterAll'". Its handling of suite load errors is off by default and enabled with reportTestSuiteErrors.
Vitest JUnit gained a synthetic vitest unhandled errors suite in May 2026 (PR #10244). It counts these as errors, not failures.
go-junit-report explains why: "JUnit doesn't have a good way of dealing with build or runtime errors… so we create a single failing test." gotestsum emits a synthetic TestMain case.
Maven Surefire and Gradle synthesize initializationError / executionError tests.
The closest precedent:xUnit v3 ships its own CTRF writer. It puts cleanup and fatal errors in results.extra.suites[].errors[] and counts them only in summary.other. tests and failed stay untouched.
Pattern C: the error is attached to a real test or to its container.
On before(:context) / @BeforeAll / OneTimeSetUp failures, RSpec, Jest, NUnit and JUnit's legacy XML mark every affected test failed. JUnit Platform and Open Test Reporting give the container itself a FAILED status.
Dropping the error is the bug the ecosystem keeps fixing.
Playwright's own JUnit reporter has no onError handler, so it reports exit 1 with 0 failures.
ctrf-io/github-test-reporter gates only on summary.failed > 0. Nothing in its src reads extra.errors or environment.healthy.
mikepenz/action-junit-report ignores suite-level errors. The maintainer confirmed it in issue #1005, where a report with 11 suite-level errors and 0 failures passed.
dorny/test-reporter computes its verdict from test cases only.
GitLab doesn't parse the suite-level errors attribute, and says job status comes from the script's exit code.
The one consumer that converts a suite-level error is Jenkins: a suite-level <error> becomes a synthetic <init> test (SuiteResult.java).
So the #88 sentence "Consumers MUST account for reported global errors" is ahead of today's tooling.
Finding 4: merging is where run-level errors get lost
Playwright's merge-reports replays every shard's onError with no de-duplication. It merges run status worst-first (failed, then timedout, then interrupted, then passed). Allure 3 also appends errors without de-duplication.
ctrf merge in ctrf-cli keeps extra and environment from the first shard only, so errors from shards 2 through N are silently lost.
The ctrf-js merge() keeps the first value for each key and rebuilds the result without any results.errors handling.
Activity
gregweb commentedon Sep 10, 2026
May you please elaborate a little my questions and comments?
To your first item:
results.tests[].statusasskippedorpending), I don't see any way to work around this issue without additional framework external tooling. Or did I understood you wrongly?Regarding the second item: To my understanding of the CTRF spec, ...
failedbyresults.tests[].status. There is no distinction between failures in the fixture (set up/tear down) reported as "error" and failures in the test code reported as "failure" as being typical for xUnit frameworks. For thisresults.tests[].rawStatuscould be used.extraobjects to somehow document the affected suite. Maybe underresults.environment.extra.<your-namespace>.suitesWithErrors?results.environment.healthytofalseand maybe additional informationresults.environment.extra.<your-namespace>.runErrors?Regarding "smuggling the results behind the dummy test failed test case" and "like
testFrameworkChecks":Ma11hewThomas commentedon Oct 25, 2026
Thank you both for the input and discussion.
We plan to add optional results.errors support in CTRF 0.3.0 for global errors associated with the run rather than an individual test result, including shared setup and teardown failures:
{ "results": { "errors": [ { "message": "Shared teardown failed", "trace": "Error: Database cleanup failed\n at cleanup.js:24:5" } ] } }Each entry can contain optional message, trace and extra fields.
This gives these errors a standard home without creating dummy test results or changing the test counts. Global errors can be reported even when every reported test passed. Failures attributed to an individual test remain on that test result.
We’re keeping the initial shape simple. It does not yet identify the affected suite or tests, distinguish lifecycle phases, or define an overall run status. It also cannot recover tests that the framework never reported, so that part of the question remains open.
Thank you again for helping shape CTRF.
jamesarosen commentedon Oct 31, 2026
Thank you for working on this, @Ma11hewThomas. I opened ctrf-io/playwright-ctrf-json-reporter#123 before I'd seen this thread. I agree run-level errors shouldn't become synthetic tests if we can avoid it. I surveyed how reporters in other ecosystems handle this. The full write-up is below. The research was AI-assisted. Each claim links to pinned source or docs.
Short version: yes to
results.errors[].{message, trace, extra}as drafted in #88. It's the dominant pattern wherever a format has somewhere to put run-level errors: xUnit<errors>, AllureGlobals.errors, TRXRunInfos, Playwright JSONerrors, and RSpecmessages. xUnit v3 already ships a CTRF writer that has to park these errors underextrabecause the field doesn't exist yet.A few "yes, and" suggestions:
results.errorsentry when the framework reports the run as unsuccessful but no test result failed (for example, Playwright'sFullResult.statusoffailed,timedout, orinterrupted). Some runs fail with only a status and no error object. Without this rule, those runs produce a report that looks green, and the "Consumers MUST account for reported global errors" text in Add global errors to results #88 has nothing to act on.results.errorsSHOULD be concatenated, not de-duplicated, and producers MAY record the shard inextra. Playwright'smerge-reportsand Allure 3 both concatenate. Todayctrf mergeand ctrf-jsmerge()keep only the first document'sextraandenvironment, so errors from later shards are silently dropped.ResultSummary/@outcome, CucumberTestRunFinished.success, Jestsuccess, node:testtest:summary.success, and Datadog session status. I don't think this should block 0.3.0, but it's the natural next step for the "overall-run-outcome questions" that Add global errors to results #88 leaves open.exit-on-failonly onsummary.failed. Havingexit-on-failalso fail on a non-emptyresults.errorswould put the MUST into practice for the most common CTRF consumer.Once this lands, I'm happy to rework playwright-ctrf-json-reporter#123 to emit
results.errorsthis way.Report: how test reporters represent run-level failures
Context
FullResult.status) isn'tpassedand no test failed.results.errors: [{message, trace, extra}]. Its diff says "Global errors MUST NOT be counted as test results" and "Consumers MUST account for reported global errors when interpreting the run". The PR body says "overall-run-outcome questions remain open."So playwright-ctrf-json-reporter#123 as written would contradict the upcoming spec text.
Finding 1: frameworks split into three patterns, decided mostly by output format
Pattern A: a synthetic test entry. Almost all of these write JUnit XML, which has no top-level place for errors.
reportTestSuiteErrors.vitest unhandled errorssuite in May 2026 (PR #10244). It counts these aserrors, notfailures.TestMaincase.initializationError/executionErrortests._ErrorHolder("setUpClass (mod.Class)"), Mocha's"before all" hookrunnables, Cypress's "An uncaught error was detected outside of a test", Kotest placeholders, and MSTest's[Class ClassCleanup]result.Pattern B: a separate list of run-level errors, with test counts unchanged. This is what formats with a native place for errors do.
errors, and the HTML and blob reporters keep their own lists too.<errors>, which it describes as "separate from failing tests (for example, errors that occurred while cleaning up…)".RunInfos.Globals.errors.errors_outside_of_examples_countplusmessages.testExecErroron the file result plusnumRuntimeErrorTestSuites. Its source gives the reason forafterAll: "AttachingafterAllerrors to each test makes execution flow too complicated, so we'll consider them to be global."results.extra.suites[].errors[]and counts them only insummary.other.testsandfailedstay untouched.Pattern C: the error is attached to a real test or to its container.
error, notfailed.before(:context)/@BeforeAll/OneTimeSetUpfailures, RSpec, Jest, NUnit and JUnit's legacy XML mark every affected test failed. JUnit Platform and Open Test Reporting give the container itself aFAILEDstatus.Dropping the error is the bug the ecosystem keeps fixing.
onErrorhandler, so it reports exit 1 with 0 failures.onError(): void {}.Finding 2: an error list is almost always paired with a run outcome
When a format keeps errors out of the test list and still wants the report to carry a verdict, it adds a run-level outcome:
successis false whennumRuntimeErrorTestSuites > 0.ResultSummary/@outcome="Failed"even when every test passed, with the run-level counters left alone.TestRunFinished.successis required, along with anexceptionfield.test:summary.successis set false for uncaught errors outside tests, without changingfailed(harness.js).exitcode.The exceptions are xUnit v2 and Playwright JSON. Both have an error list but no outcome, which is the shape CTRF 0.3.0 is heading toward.
Finding 3: consumers ignore run-level error fields
summary.failed > 0. Nothing in itssrcreadsextra.errorsorenvironment.healthy.errors. The maintainer confirmed it in issue #1005, where a report with 11 suite-level errors and 0 failures passed.errorsattribute, and says job status comes from the script's exit code.<error>becomes a synthetic<init>test (SuiteResult.java).So the #88 sentence "Consumers MUST account for reported global errors" is ahead of today's tooling.
Finding 4: merging is where run-level errors get lost
merge-reportsreplays every shard'sonErrorwith no de-duplication. It merges run status worst-first (failed, then timedout, then interrupted, then passed). Allure 3 also appends errors without de-duplication.ctrf mergein ctrf-cli keepsextraandenvironmentfrom the first shard only, so errors from shards 2 through N are silently lost.merge()keeps the first value for each key and rebuilds the result without anyresults.errorshandling.Options
results.errorsonlytestExecError, xUnit v3 CTRF writerreportTestSuiteErrorsMa11hewThomas commentedon Oct 31, 2026
Thank you for the research @jamesarosen
At a high level, I like these ideas for the spec.
Could you open a separate issue in the this repo for each:
making a non-empty error list a dependable signal,
defining error merge semantics,
and an optional run outcome?
Thanks again!