Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions lib/mocha/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,22 @@ class Hook {
}

simplify() {
// this.runnable (context.ctx.test) is the hook's own runnable; its
// .parent is the real Mocha Suite that owns this hook. Included here so
// run-workers can forward suite identity to the main process, where
// listeners (e.g. junitReporter) have no other way to recover it — the
// worker's live Suite/ctx objects aren't serializable across the thread
// boundary, only these plain fields are.
const suite = this.runnable?.parent
return {
hookName: this.hookName,
title: this.title,
// test: this.test ? serializeTest(this.test) : null,
// suite: this.suite ? serializeSuite(this.suite) : null,
error: this.err ? serializeError(this.err) : null,
suiteTitle: suite?.title || null,
suiteFile: suite?.file || null,
suiteTags: suite?.tags || [],
}
}

Expand Down
18 changes: 17 additions & 1 deletion lib/plugin/junitReporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,29 @@ export default function (config = {}) {

let written = false
const hookFailures = []
// groupBySuite() (below) groups by object identity, not by value — reused
// across BeforeSuite/AfterSuite failures from the same worker-forwarded
// suite so they land in one <testsuite>, not one per failure.
const workerSuiteByKey = new Map()

event.dispatcher.on(event.hook.failed, hook => {
if (!hook || !['BeforeSuite', 'AfterSuite'].includes(hook.hookName)) return
const err = hook.err || hook.error
if (!err) return
const runnable = hook.ctx && hook.ctx.test
const suite = runnable && runnable.parent
// Under run-workers, hook.ctx is absent — the failure arrives as the
// plain object from Hook.simplify() in the main process instead of a
// live Hook instance. Fall back to the suiteTitle/suiteFile/suiteTags
// simplify() carries across the worker boundary so the failure still
// groups under its real suite instead of the "Tests" fallback.
let suite = runnable && runnable.parent
if (!suite && hook.suiteTitle) {
const key = `${hook.suiteTitle} ${hook.suiteFile || ''}`
if (!workerSuiteByKey.has(key)) {
workerSuiteByKey.set(key, { title: hook.suiteTitle, file: hook.suiteFile, tags: hook.suiteTags })
}
suite = workerSuiteByKey.get(key)
}
hookFailures.push({
title: hook.title || `${hook.hookName} hook failed`,
state: 'failed',
Expand Down
Loading