fix(gh,github,junit): attach test:log to the test that emitted it - #275
Merged
Merged
Conversation
`test:log` is execution-ordered: Node forwards it the moment `t.log()` runs,
bypassing the per-file declaration-order buffer that `test:diagnostic` waits
in (`kExecutionOrderedEvents` in lib/internal/test_runner/runner.js). These
three reporters structure their output by test, so rendering a log on arrival
files it under whichever test happens to be printing.
Two concurrent files, before:
A1 log before
B1 log <- file B's log, before A reports at all
A1 log after
::group::✔ A one
A1 diagnostic <- correct, Node ordered it
A2 log <- A two's log, inside A one's group
::endgroup::
::group::✔ A two
B2 log late <- B two's log, inside A two's group
::endgroup::
junit was worse: the misplacement is in the document, not just the display.
Hold each log until its owner reports, keyed by (entryFile ?? file, testId),
and emit it with that test - the position `test:diagnostic` already occupies.
gh and github share a LogBuffer from `@reporters/github/log_buffer`; junit
keeps its own copy so the package stays dependency-free. gh buffers in and out
of GitHub Actions, so local output matches CI. Logs whose test never reports,
from an interrupted run or a `t.log()` that outlived its test, are emitted at
the end rather than dropped.
Two bugs fixed in the same path: a log with no location crashed the github
reporter in `extractLocation`, and its `data` payload was dropped from the
annotation.
`live`, `web` and `testwatch` are unchanged - real-time output is what
`test:log` exists for, and they are the reporters built for it.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #275 +/- ##
========================================
Coverage 99.83% 99.83%
========================================
Files 40 41 +1
Lines 4129 4294 +165
Branches 604 604
========================================
+ Hits 4122 4287 +165
Misses 5 5
Partials 2 2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The reporter reads GITHUB_ACTIONS once, at construction, so the drain test covered whichever branch the environment selected: locally the plain one, and in Actions - where CI runs - the grouped one, leaving the other two lines uncovered. Pin the variable when loading the module instead.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
test:logis execution-ordered: Node forwards it the momentt.log()runs, bypassing the per-file declaration-order buffer thattest:diagnosticwaits in (kExecutionOrderedEvents).gh,githubandjunitstructure their output by test, so rendering a log on arrival files it under whichever test happens to be printing.Two concurrent files,
@reporters/ghunder Actions — before:after:
@reporters/junithad it worse — the misplacement is in the document, not just the display:What changed
Each log is held keyed by
(entryFile ?? file, testId)and emitted when its owner reports — the positiontest:diagnosticalready occupies.@reporters/github— newlog_buffer.js, exported as@reporters/github/log_buffer. Annotations flush attest:pass/test:fail, including thesubtestsFailedpath that emits no annotation of its own.@reporters/gh— its own buffer instance; flushed lines land after the group header. Buffers in and out of GitHub Actions so local output matches CI.@reporters/junit— its own copy of the map, so the package stays dependency-free. Logs attach after thetestcase/testsuitedecision, so a logged test can't become a suite.Logs whose test never reports — an interrupted run, or a
t.log()that outlived its test — are emitted at the end rather than dropped.Two bugs fixed in the same code path: a log with no location crashed
@reporters/githubinextractLocation(Cannot read properties of undefined (reading 'startsWith')), and the log'sdatapayload was dropped from the annotation (ghrendered it,githubdidn't).Not changed
live,webandtestwatchstream their output — real-time logging is whattest:logexists for, and they're the reporters built for it.junit logs stay XML comments.
<system-out>would be more useful to CI parsers, but it's a format change, and comments are deliberately excluded from thenonCommentChildrencount that decidestestcasevstestsuite.Known limitation
Under process isolation,
(file, testId)collides across processes when tests are declared in a shared helper file — the same defect already tracked for the tree store. ReadingentryFilefirst makes this correct automatically once nodejs/node#64309 lands.Upstream
Node's built-in
junitandtapreporters have the identical defect; a fix is in progress separately.specis left live by design.Verification
436 tests pass, 100% statements/lines, branch coverage 95.38% → 95.79%, lint clean. New regression tests in all three packages cover: a log flushing inside its owner's group, a sibling's log not leaking into a foreign group, cross-file interleaving, multiple logs keeping their order, an orphan drained at end of run,
ghrendering a log exactly once (it wraps upstreamspec, which would double-print if the event were forwarded), andgithubtolerating a location-less log.Also verified end-to-end against real Node v26.6.0 runs — nested, concurrent, in and out of Actions — and the suite re-run with
GITHUB_ACTIONS=1to exercise the CI paths.🤖 Generated with Claude Code