Skip to content

Commit 44002f3

Browse files
authored
Fix failed unit tests - achieve 100% pass rate (#5305)
1 parent 4e4efb3 commit 44002f3

File tree

1 file changed

+35
-40
lines changed

1 file changed

+35
-40
lines changed

lib/workers.js

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { mkdirp } from 'mkdirp'
55
import { Worker } from 'worker_threads'
66
import { EventEmitter } from 'events'
77
import ms from 'ms'
8+
import merge from 'lodash.merge'
89

910
const __filename = fileURLToPath(import.meta.url)
1011
const __dirname = dirname(__filename)
@@ -66,21 +67,21 @@ const createWorker = (workerObject, isPoolMode = false) => {
6667
stdout: true,
6768
stderr: true,
6869
})
69-
70+
7071
// Pipe worker stdout/stderr to main process
7172
if (worker.stdout) {
7273
worker.stdout.setEncoding('utf8')
73-
worker.stdout.on('data', (data) => {
74+
worker.stdout.on('data', data => {
7475
process.stdout.write(data)
7576
})
7677
}
7778
if (worker.stderr) {
7879
worker.stderr.setEncoding('utf8')
79-
worker.stderr.on('data', (data) => {
80+
worker.stderr.on('data', data => {
8081
process.stderr.write(data)
8182
})
8283
}
83-
84+
8485
worker.on('error', err => {
8586
console.error(`[Main] Worker Error:`, err)
8687
output.error(`Worker Error: ${err.stack}`)
@@ -221,13 +222,13 @@ class WorkerObject {
221222

222223
addConfig(config) {
223224
const oldConfig = JSON.parse(this.options.override || '{}')
224-
225+
225226
// Remove customLocatorStrategies from both old and new config before JSON serialization
226227
// since functions cannot be serialized and will be lost, causing workers to have empty strategies
227228
const configWithoutFunctions = { ...config }
228-
229+
229230
// Clean both old and new config
230-
const cleanConfig = (cfg) => {
231+
const cleanConfig = cfg => {
231232
if (cfg.helpers) {
232233
cfg.helpers = { ...cfg.helpers }
233234
Object.keys(cfg.helpers).forEach(helperName => {
@@ -239,14 +240,12 @@ class WorkerObject {
239240
}
240241
return cfg
241242
}
242-
243+
243244
const cleanedOldConfig = cleanConfig(oldConfig)
244245
const cleanedNewConfig = cleanConfig(configWithoutFunctions)
245-
246-
const newConfig = {
247-
...cleanedOldConfig,
248-
...cleanedNewConfig,
249-
}
246+
247+
// Deep merge configurations to preserve all helpers from base config
248+
const newConfig = merge({}, cleanedOldConfig, cleanedNewConfig)
250249
this.options.override = JSON.stringify(newConfig)
251250
}
252251

@@ -280,8 +279,8 @@ class Workers extends EventEmitter {
280279
this.setMaxListeners(50)
281280
this.codeceptPromise = initializeCodecept(config.testConfig, config.options)
282281
this.codecept = null
283-
this.config = config // Save config
284-
this.numberOfWorkersRequested = numberOfWorkers // Save requested worker count
282+
this.config = config // Save config
283+
this.numberOfWorkersRequested = numberOfWorkers // Save requested worker count
285284
this.options = config.options || {}
286285
this.errors = []
287286
this.numberOfWorkers = 0
@@ -304,11 +303,8 @@ class Workers extends EventEmitter {
304303
// Initialize workers in these cases:
305304
// 1. Positive number requested AND no manual workers pre-spawned
306305
// 2. Function-based grouping (indicated by negative number) AND no manual workers pre-spawned
307-
const shouldAutoInit = this.workers.length === 0 && (
308-
(Number.isInteger(this.numberOfWorkersRequested) && this.numberOfWorkersRequested > 0) ||
309-
(this.numberOfWorkersRequested < 0 && isFunction(this.config.by))
310-
)
311-
306+
const shouldAutoInit = this.workers.length === 0 && ((Number.isInteger(this.numberOfWorkersRequested) && this.numberOfWorkersRequested > 0) || (this.numberOfWorkersRequested < 0 && isFunction(this.config.by)))
307+
312308
if (shouldAutoInit) {
313309
this._initWorkers(this.numberOfWorkersRequested, this.config)
314310
}
@@ -371,9 +367,9 @@ class Workers extends EventEmitter {
371367
* @param {Number} numberOfWorkers
372368
*/
373369
createGroupsOfTests(numberOfWorkers) {
374-
// If Codecept isn't initialized yet, return empty groups as a safe fallback
375-
if (!this.codecept) return populateGroups(numberOfWorkers)
376-
const files = this.codecept.testFiles
370+
// If Codecept isn't initialized yet, return empty groups as a safe fallback
371+
if (!this.codecept) return populateGroups(numberOfWorkers)
372+
const files = this.codecept.testFiles
377373
const mocha = Container.mocha()
378374
mocha.files = files
379375
mocha.loadFiles()
@@ -430,7 +426,7 @@ class Workers extends EventEmitter {
430426
for (const file of files) {
431427
this.testPool.push(file)
432428
}
433-
429+
434430
this.testPoolInitialized = true
435431
}
436432

@@ -443,17 +439,17 @@ class Workers extends EventEmitter {
443439
if (!this.testPoolInitialized) {
444440
this._initializeTestPool()
445441
}
446-
442+
447443
return this.testPool.shift()
448444
}
449445

450446
/**
451447
* @param {Number} numberOfWorkers
452448
*/
453449
createGroupsOfSuites(numberOfWorkers) {
454-
// If Codecept isn't initialized yet, return empty groups as a safe fallback
455-
if (!this.codecept) return populateGroups(numberOfWorkers)
456-
const files = this.codecept.testFiles
450+
// If Codecept isn't initialized yet, return empty groups as a safe fallback
451+
if (!this.codecept) return populateGroups(numberOfWorkers)
452+
const files = this.codecept.testFiles
457453
const groups = populateGroups(numberOfWorkers)
458454

459455
const mocha = Container.mocha()
@@ -494,7 +490,7 @@ class Workers extends EventEmitter {
494490
recorder.startUnlessRunning()
495491
event.dispatcher.emit(event.workers.before)
496492
process.env.RUNS_WITH_WORKERS = 'true'
497-
493+
498494
// Create workers and set up message handlers immediately (not in recorder queue)
499495
// This prevents a race condition where workers start sending messages before handlers are attached
500496
const workerThreads = []
@@ -503,11 +499,11 @@ class Workers extends EventEmitter {
503499
this._listenWorkerEvents(workerThread)
504500
workerThreads.push(workerThread)
505501
}
506-
502+
507503
recorder.add('workers started', () => {
508504
// Workers are already running, this is just a placeholder step
509505
})
510-
506+
511507
return new Promise(resolve => {
512508
this.on('end', resolve)
513509
})
@@ -591,7 +587,7 @@ class Workers extends EventEmitter {
591587
// Otherwise skip - we'll emit based on finished state
592588
break
593589
case event.test.passed:
594-
// Skip individual passed events - we'll emit based on finished state
590+
// Skip individual passed events - we'll emit based on finished state
595591
break
596592
case event.test.skipped:
597593
this.emit(event.test.skipped, deserializeTest(message.data))
@@ -602,15 +598,15 @@ class Workers extends EventEmitter {
602598
const data = message.data
603599
const uid = data?.uid
604600
const isFailed = !!data?.err || data?.state === 'failed'
605-
601+
606602
if (uid) {
607603
// Track states for each test UID
608604
if (!this._testStates) this._testStates = new Map()
609-
605+
610606
if (!this._testStates.has(uid)) {
611607
this._testStates.set(uid, { states: [], lastData: data })
612608
}
613-
609+
614610
const testState = this._testStates.get(uid)
615611
testState.states.push({ isFailed, data })
616612
testState.lastData = data
@@ -622,7 +618,7 @@ class Workers extends EventEmitter {
622618
this.emit(event.test.passed, deserializeTest(data))
623619
}
624620
}
625-
621+
626622
this.emit(event.test.finished, deserializeTest(data))
627623
}
628624
break
@@ -682,11 +678,10 @@ class Workers extends EventEmitter {
682678
// For tests with retries configured, emit all failures + final success
683679
// For tests without retries, emit only final state
684680
const lastState = states[states.length - 1]
685-
681+
686682
// Check if this test had retries by looking for failure followed by success
687-
const hasRetryPattern = states.length > 1 &&
688-
states.some((s, i) => s.isFailed && i < states.length - 1 && !states[i + 1].isFailed)
689-
683+
const hasRetryPattern = states.length > 1 && states.some((s, i) => s.isFailed && i < states.length - 1 && !states[i + 1].isFailed)
684+
690685
if (hasRetryPattern) {
691686
// Emit all intermediate failures and final success for retries
692687
for (const state of states) {

0 commit comments

Comments
 (0)