Skip to content

Commit cd81b57

Browse files
DavertMikclaude
andcommitted
refactor: clean up workers.js — remove dead code, extract Config.applyRunConfig
- Remove dead code: activeWorkers Map, maxWorkers, empty recorder placeholder - Simplify _initializeTestPool to 4 lines - Remove redundant configWithoutFunctions variable in WorkerObject.addConfig - Remove hardcoded mochawesome/mocha-junit-reporter path manipulation from createWorkerObjects — fragile, incomplete, and redundant with output dir override - Extract getOverridenConfig to Config.applyRunConfig() in lib/config.js, shared by both workers.js and run-multiple.js Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 77fc33b commit cd81b57

File tree

3 files changed

+40
-118
lines changed

3 files changed

+40
-118
lines changed

lib/command/run-multiple.js

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { createRuns } from './run-multiple/collection.js'
99
import { clearString, replaceValueDeep } from '../utils.js'
1010
import { getConfig, getTestRoot, fail } from './utils.js'
1111
import store from '../store.js'
12+
import Config from '../config.js'
1213

1314
const __filename = fileURLToPath(import.meta.url)
1415
const __dirname = path.dirname(__filename)
@@ -115,17 +116,11 @@ export default async function (selectedRuns, options) {
115116
}
116117

117118
function executeRun(runName, runConfig) {
118-
// clone config
119-
let overriddenConfig = { ...config }
119+
let overriddenConfig = Config.applyRunConfig(config, runConfig)
120120

121-
// get configuration
122121
const browserConfig = runConfig.browser
123122
const browserName = browserConfig.browser
124123

125-
for (const key in browserConfig) {
126-
overriddenConfig.helpers = replaceValueDeep(overriddenConfig.helpers, key, browserConfig[key])
127-
}
128-
129124
let outputDir = `${runName}_`
130125
if (browserConfig.outputName) {
131126
outputDir += typeof browserConfig.outputName === 'function' ? browserConfig.outputName() : browserConfig.outputName
@@ -138,20 +133,10 @@ function executeRun(runName, runConfig) {
138133

139134
outputDir = clearString(outputDir)
140135

141-
// tweaking default output directories and for mochawesome
142136
overriddenConfig = replaceValueDeep(overriddenConfig, 'output', path.join(config.output, outputDir))
143137
overriddenConfig = replaceValueDeep(overriddenConfig, 'reportDir', path.join(config.output, outputDir))
144138
overriddenConfig = replaceValueDeep(overriddenConfig, 'mochaFile', path.join(config.output, outputDir, `${browserName}_report.xml`))
145139

146-
// override tests configuration
147-
if (overriddenConfig.tests) {
148-
overriddenConfig.tests = runConfig.tests
149-
}
150-
151-
if (overriddenConfig.gherkin && runConfig.gherkin && runConfig.gherkin.features) {
152-
overriddenConfig.gherkin.features = runConfig.gherkin.features
153-
}
154-
155140
// override grep param and collect all params
156141
const params = ['run', '--child', `${runId++}.${runName}:${browserName}`, '--override', JSON.stringify(overriddenConfig)]
157142

lib/config.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from 'fs'
22
import path from 'path'
33
import { createRequire } from 'module'
4-
import { fileExists, isFile, deepMerge, deepClone } from './utils.js'
4+
import { fileExists, isFile, deepMerge, deepClone, replaceValueDeep } from './utils.js'
55
import { transpileTypeScript, cleanupTempFiles, fixErrorStack } from './utils/typescript.js'
66

77
const defaultConfig = {
@@ -134,6 +134,33 @@ class Config {
134134
return (config = deepMerge(config, additionalConfig))
135135
}
136136

137+
/**
138+
* Apply run configuration (browser overrides, tests, gherkin) to a base config.
139+
* Used by workers and run-multiple to create per-run configurations.
140+
*
141+
* @param {Object} baseConfig
142+
* @param {Object} runConfig - must have .browser object, optionally .tests and .gherkin
143+
* @return {Object}
144+
*/
145+
static applyRunConfig(baseConfig, runConfig) {
146+
const overriddenConfig = deepClone(baseConfig)
147+
const browserConfig = runConfig.browser
148+
149+
for (const key in browserConfig) {
150+
overriddenConfig.helpers = replaceValueDeep(overriddenConfig.helpers, key, browserConfig[key])
151+
}
152+
153+
if (overriddenConfig.tests && runConfig.tests) {
154+
overriddenConfig.tests = runConfig.tests
155+
}
156+
157+
if (overriddenConfig.gherkin && runConfig.gherkin?.features) {
158+
overriddenConfig.gherkin.features = runConfig.gherkin.features
159+
}
160+
161+
return overriddenConfig
162+
}
163+
137164
/**
138165
* Resets config to default
139166
* @return {Object<string, *>}

lib/workers.js

Lines changed: 10 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Codecept from './codecept.js'
1313
import MochaFactory from './mocha/factory.js'
1414
import Container from './container.js'
1515
import { getTestRoot } from './command/utils.js'
16-
import { isFunction, fileExists, replaceValueDeep, deepClone } from './utils.js'
16+
import { isFunction, fileExists } from './utils.js'
1717
import mainConfig from './config.js'
1818
import output from './output.js'
1919
import event from './event.js'
@@ -117,34 +117,11 @@ const createWorkerObjects = (testGroups, config, testRoot, options, selectedRuns
117117
}
118118
const workersToExecute = []
119119

120-
const currentOutputFolder = config.output
121-
let currentMochawesomeReportDir
122-
let currentMochaJunitReporterFile
123-
124-
if (config.mocha && config.mocha.reporterOptions) {
125-
currentMochawesomeReportDir = config.mocha.reporterOptions?.mochawesome.options.reportDir
126-
currentMochaJunitReporterFile = config.mocha.reporterOptions['mocha-junit-reporter'].options.mochaFile
127-
}
128-
129120
createRuns(selectedRuns, config).forEach(worker => {
130-
const separator = path.sep
131-
const _config = { ...config }
132-
let workerName = worker.name.replace(':', '_')
133-
_config.output = `${currentOutputFolder}${separator}${workerName}`
134-
if (config.mocha && config.mocha.reporterOptions) {
135-
_config.mocha.reporterOptions.mochawesome.options.reportDir = `${currentMochawesomeReportDir}${separator}${workerName}`
136-
137-
const _tempArray = currentMochaJunitReporterFile.split(separator)
138-
_tempArray.splice(
139-
_tempArray.findIndex(item => item.includes('.xml')),
140-
0,
141-
workerName,
142-
)
143-
_config.mocha.reporterOptions['mocha-junit-reporter'].options.mochaFile = _tempArray.join(separator)
144-
}
145-
workerName = worker.getOriginalName() || worker.getName()
146-
const workerConfig = worker.getConfig()
147-
workersToExecute.push(getOverridenConfig(workerName, workerConfig, _config))
121+
const workerName = worker.name.replace(':', '_')
122+
const _config = mainConfig.applyRunConfig(config, worker.getConfig())
123+
_config.output = path.join(config.output, workerName)
124+
workersToExecute.push(_config)
148125
})
149126
const workers = []
150127
let index = 0
@@ -188,27 +165,6 @@ const convertToMochaTests = testGroup => {
188165
return group
189166
}
190167

191-
const getOverridenConfig = (workerName, workerConfig, config) => {
192-
// clone config
193-
const overriddenConfig = deepClone(config)
194-
195-
// get configuration
196-
const browserConfig = workerConfig.browser
197-
198-
for (const key in browserConfig) {
199-
overriddenConfig.helpers = replaceValueDeep(overriddenConfig.helpers, key, browserConfig[key])
200-
}
201-
202-
// override tests configuration
203-
if (overriddenConfig.tests) {
204-
overriddenConfig.tests = workerConfig.tests
205-
}
206-
207-
if (overriddenConfig.gherkin && workerConfig.gherkin && workerConfig.gherkin.features) {
208-
overriddenConfig.gherkin.features = workerConfig.gherkin.features
209-
}
210-
return overriddenConfig
211-
}
212168

213169
class WorkerObject {
214170
/**
@@ -224,17 +180,11 @@ class WorkerObject {
224180
addConfig(config) {
225181
const oldConfig = JSON.parse(this.options.override || '{}')
226182

227-
// Remove customLocatorStrategies from both old and new config before JSON serialization
228-
// since functions cannot be serialized and will be lost, causing workers to have empty strategies.
229-
// Note: Only WebDriver helper supports customLocatorStrategies
230-
const configWithoutFunctions = { ...config }
231-
232-
// Clean both old and new config
233183
const cleanConfig = cfg => {
234184
if (cfg.helpers) {
235185
cfg.helpers = { ...cfg.helpers }
236186
Object.keys(cfg.helpers).forEach(helperName => {
237-
if (cfg.helpers[helperName] && cfg.helpers[helperName].customLocatorStrategies !== undefined) {
187+
if (cfg.helpers[helperName]?.customLocatorStrategies !== undefined) {
238188
cfg.helpers[helperName] = { ...cfg.helpers[helperName] }
239189
delete cfg.helpers[helperName].customLocatorStrategies
240190
}
@@ -243,11 +193,7 @@ class WorkerObject {
243193
return cfg
244194
}
245195

246-
const cleanedOldConfig = cleanConfig(oldConfig)
247-
const cleanedNewConfig = cleanConfig(configWithoutFunctions)
248-
249-
// Deep merge configurations to preserve all helpers from base config
250-
const newConfig = merge({}, cleanedOldConfig, cleanedNewConfig)
196+
const newConfig = merge({}, cleanConfig(oldConfig), cleanConfig(config))
251197
this.options.override = JSON.stringify(newConfig)
252198
}
253199

@@ -292,8 +238,6 @@ class Workers extends EventEmitter {
292238
this.testPool = []
293239
this.testPoolInitialized = false
294240
this.isPoolMode = config.by === 'pool'
295-
this.activeWorkers = new Map()
296-
this.maxWorkers = numberOfWorkers // Track original worker count for pool mode
297241

298242
createOutputDir(config.testConfig)
299243
// Defer worker initialization until codecept is ready
@@ -395,36 +339,11 @@ class Workers extends EventEmitter {
395339
this.testGroups = populateGroups(numberOfWorkers)
396340
}
397341

398-
/**
399-
* Initialize the test pool if not already done
400-
* This is called lazily to avoid state pollution issues during construction
401-
*/
402342
_initializeTestPool() {
403-
if (this.testPoolInitialized) {
404-
return
405-
}
406-
407-
// Ensure codecept is initialized
408-
if (!this.codecept) {
409-
output.log('Warning: codecept not initialized when initializing test pool')
410-
this.testPoolInitialized = true
411-
return
412-
}
413-
414-
const files = this.codecept.testFiles
415-
if (!files || files.length === 0) {
416-
this.testPoolInitialized = true
417-
return
418-
}
419-
420-
// In ESM, test UIDs are not stable across different mocha instances
421-
// So instead of using UIDs, we distribute test FILES
422-
// Each file may contain multiple tests
423-
for (const file of files) {
424-
this.testPool.push(file)
425-
}
426-
343+
if (this.testPoolInitialized) return
427344
this.testPoolInitialized = true
345+
if (!this.codecept) return
346+
this.testPool = [...this.codecept.testFiles]
428347
}
429348

430349
/**
@@ -493,10 +412,6 @@ class Workers extends EventEmitter {
493412
workerThreads.push(workerThread)
494413
}
495414

496-
recorder.add('workers started', () => {
497-
// Workers are already running, this is just a placeholder step
498-
})
499-
500415
// Add overall timeout to prevent infinite hanging
501416
const overallTimeout = setTimeout(() => {
502417
console.error('[Main] Overall timeout reached (10 minutes). Force terminating remaining workers...')
@@ -533,11 +448,6 @@ class Workers extends EventEmitter {
533448
}
534449

535450
_listenWorkerEvents(worker) {
536-
// Track worker thread for pool mode
537-
if (this.isPoolMode) {
538-
this.activeWorkers.set(worker, { available: true, workerIndex: null })
539-
}
540-
541451
// Track last activity time to detect hanging workers
542452
let lastActivity = Date.now()
543453
let currentTest = null

0 commit comments

Comments
 (0)