@@ -19,13 +19,45 @@ const stderr = ''
1919
2020const { options, tests, testRoot, workerIndex, poolMode } = workerData
2121
22+ // Global error handlers to catch critical errors but not test failures
23+ process . on ( 'uncaughtException' , ( err ) => {
24+ // Log to stderr to bypass stdout suppression
25+ process . stderr . write ( `[Worker ${ workerIndex } ] UNCAUGHT EXCEPTION: ${ err . message } \n` )
26+ process . stderr . write ( `${ err . stack } \n` )
27+
28+ // Don't exit on test assertion errors - those are handled by mocha
29+ if ( err . name === 'AssertionError' || err . message ?. includes ( 'expected' ) ) {
30+ return
31+ }
32+ process . exit ( 1 )
33+ } )
34+
35+ process . on ( 'unhandledRejection' , ( reason , promise ) => {
36+ // Log to stderr to bypass stdout suppression
37+ const msg = reason ?. message || String ( reason )
38+ process . stderr . write ( `[Worker ${ workerIndex } ] UNHANDLED REJECTION: ${ msg } \n` )
39+ if ( reason ?. stack ) {
40+ process . stderr . write ( `${ reason . stack } \n` )
41+ }
42+
43+ // Don't exit on test-related rejections
44+ if ( msg . includes ( 'expected' ) || msg . includes ( 'AssertionError' ) ) {
45+ return
46+ }
47+ process . exit ( 1 )
48+ } )
49+
2250// hide worker output
2351// In pool mode, only suppress output if debug is NOT enabled
2452// In regular mode, hide result output but allow step output in verbose/debug
2553if ( poolMode && ! options . debug ) {
2654 // In pool mode without debug, allow test names and important output but suppress verbose details
2755 const originalWrite = process . stdout . write
2856 process . stdout . write = string => {
57+ // Always allow Worker logs
58+ if ( string . includes ( '[Worker' ) ) {
59+ return originalWrite . call ( process . stdout , string )
60+ }
2961 // Allow test names (✔ or ✖), Scenario Steps, failures, and important markers
3062 if (
3163 string . includes ( '✔' ) ||
@@ -45,7 +77,12 @@ if (poolMode && !options.debug) {
4577 return originalWrite . call ( process . stdout , string )
4678 }
4779} else if ( ! poolMode && ! options . debug && ! options . verbose ) {
80+ const originalWrite = process . stdout . write
4881 process . stdout . write = string => {
82+ // Always allow Worker logs
83+ if ( string . includes ( '[Worker' ) ) {
84+ return originalWrite . call ( process . stdout , string )
85+ }
4986 stdout += string
5087 return true
5188 }
@@ -82,6 +119,13 @@ let config
82119// Load test and run
83120initPromise = ( async function ( ) {
84121 try {
122+ // Add staggered delay at the very start to prevent resource conflicts
123+ // Longer delay for browser initialization conflicts
124+ const delay = ( workerIndex - 1 ) * 2000 // 0ms, 2s, 4s, etc.
125+ if ( delay > 0 ) {
126+ await new Promise ( resolve => setTimeout ( resolve , delay ) )
127+ }
128+
85129 // Import modules dynamically to avoid ES Module loader race conditions in Node 22.x
86130 const eventModule = await import ( '../../event.js' )
87131 const containerModule = await import ( '../../container.js' )
@@ -98,16 +142,32 @@ initPromise = (async function () {
98142
99143 const overrideConfigs = tryOrDefault ( ( ) => JSON . parse ( options . override ) , { } )
100144
101- // IMPORTANT: await is required here since getConfig is async
102- const baseConfig = await getConfig ( options . config || testRoot )
145+ let baseConfig
146+ try {
147+ // IMPORTANT: await is required here since getConfig is async
148+ baseConfig = await getConfig ( options . config || testRoot )
149+ } catch ( configErr ) {
150+ process . stderr . write ( `[Worker ${ workerIndex } ] FAILED loading config: ${ configErr . message } \n` )
151+ process . stderr . write ( `${ configErr . stack } \n` )
152+ await new Promise ( resolve => setTimeout ( resolve , 100 ) )
153+ process . exit ( 1 )
154+ }
103155
104156 // important deep merge so dynamic things e.g. functions on config are not overridden
105157 config = deepMerge ( baseConfig , overrideConfigs )
106158
107159 // Pass workerIndex as child option for output.process() to display worker prefix
108160 const optsWithChild = { ...options , child : workerIndex }
109161 codecept = new Codecept ( config , optsWithChild )
110- await codecept . init ( testRoot )
162+
163+ try {
164+ await codecept . init ( testRoot )
165+ } catch ( initErr ) {
166+ process . stderr . write ( `[Worker ${ workerIndex } ] FAILED during codecept.init(): ${ initErr . message } \n` )
167+ process . stderr . write ( `${ initErr . stack } \n` )
168+ process . exit ( 1 )
169+ }
170+
111171 codecept . loadTests ( )
112172 mocha = container . mocha ( )
113173
@@ -126,10 +186,12 @@ initPromise = (async function () {
126186 await runTests ( )
127187 } else {
128188 // No tests to run, close the worker
189+ console . error ( `[Worker ${ workerIndex } ] ERROR: No tests found after filtering! Assigned ${ tests . length } UIDs but none matched.` )
129190 parentPort ?. close ( )
130191 }
131192 } catch ( err ) {
132- console . error ( 'Error in worker initialization:' , err )
193+ process . stderr . write ( `[Worker ${ workerIndex } ] FATAL ERROR: ${ err . message } \n` )
194+ process . stderr . write ( `${ err . stack } \n` )
133195 process . exit ( 1 )
134196 }
135197} ) ( )
@@ -147,8 +209,14 @@ async function runTests() {
147209 disablePause ( )
148210 try {
149211 await codecept . run ( )
212+ } catch ( err ) {
213+ throw err
150214 } finally {
151- await codecept . teardown ( )
215+ try {
216+ await codecept . teardown ( )
217+ } catch ( err ) {
218+ // Ignore teardown errors
219+ }
152220 }
153221}
154222
@@ -336,8 +404,16 @@ function filterTests() {
336404 mocha . files = files
337405 mocha . loadFiles ( )
338406
339- for ( const suite of mocha . suite . suites ) {
407+ // Recursively filter tests in all suites (including nested ones)
408+ const filterSuiteTests = ( suite ) => {
340409 suite . tests = suite . tests . filter ( test => tests . indexOf ( test . uid ) >= 0 )
410+ for ( const childSuite of suite . suites ) {
411+ filterSuiteTests ( childSuite )
412+ }
413+ }
414+
415+ for ( const suite of mocha . suite . suites ) {
416+ filterSuiteTests ( suite )
341417 }
342418}
343419
0 commit comments