Skip to content

Commit 21e066c

Browse files
authored
Enable event dispatcher in workers (#5464)
* add event.dispatcher.emit * add fix * remove brush * add runInParent for plagins * add fix * add helper * fix * fix regular mode * fix regular mode 2 * fix regular mode 3 * fix workers again * fix step * fix old fit step
1 parent 27eea12 commit 21e066c

File tree

5 files changed

+91
-29
lines changed

5 files changed

+91
-29
lines changed

lib/codecept.js

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -120,23 +120,26 @@ class Codecept {
120120
* Executes hooks.
121121
*/
122122
async runHooks() {
123-
// default hooks - dynamic imports for ESM
124-
const listenerModules = [
125-
'./listener/store.js',
126-
'./listener/steps.js',
127-
'./listener/config.js',
128-
'./listener/result.js',
129-
'./listener/helpers.js',
130-
'./listener/globalTimeout.js',
131-
'./listener/globalRetry.js',
132-
'./listener/retryEnhancer.js',
133-
'./listener/exit.js',
134-
'./listener/emptyRun.js',
135-
]
136-
137-
for (const modulePath of listenerModules) {
138-
const module = await import(modulePath)
139-
runHook(module.default || module)
123+
// For workers parent process we only need plugins/hooks.
124+
// Core listeners are executed inside worker threads.
125+
if (!this.opts?.skipDefaultListeners) {
126+
const listenerModules = [
127+
'./listener/store.js',
128+
'./listener/steps.js',
129+
'./listener/config.js',
130+
'./listener/result.js',
131+
'./listener/helpers.js',
132+
'./listener/globalTimeout.js',
133+
'./listener/globalRetry.js',
134+
'./listener/retryEnhancer.js',
135+
'./listener/exit.js',
136+
'./listener/emptyRun.js',
137+
]
138+
139+
for (const modulePath of listenerModules) {
140+
const module = await import(modulePath)
141+
runHook(module.default || module)
142+
}
140143
}
141144

142145
// custom hooks (previous iteration of plugins)

lib/command/run-workers.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export default async function (workerCount, selectedRuns, options) {
4141
output.print(`CodeceptJS v${Codecept.version()} ${output.standWithUkraine()}`)
4242
output.print(`Running tests in ${output.styles.bold(numberOfWorkers)} workers...`)
4343
store.hasWorkers = true
44+
process.env.RUNS_WITH_WORKERS = 'true'
4445

4546
const workers = new Workers(numberOfWorkers, config)
4647
workers.overrideConfig(overrideConfigs)

lib/container.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -657,13 +657,28 @@ async function createPlugins(config, options = {}) {
657657
const enabledPluginsByOptions = (options.plugins || '').split(',')
658658
for (const pluginName in config) {
659659
if (!config[pluginName]) config[pluginName] = {}
660-
if (!config[pluginName].enabled && enabledPluginsByOptions.indexOf(pluginName) < 0) {
660+
const pluginConfig = config[pluginName]
661+
if (!pluginConfig.enabled && enabledPluginsByOptions.indexOf(pluginName) < 0) {
661662
continue // plugin is disabled
662663
}
664+
665+
// Generic workers gate:
666+
// - runInWorker / runInWorkers controls plugin execution inside worker threads.
667+
// - runInParent / runInMain can disable plugin in workers parent process.
668+
const runInWorker = pluginConfig.runInWorker ?? pluginConfig.runInWorkers ?? (pluginName === 'testomatio' ? false : true)
669+
const runInParent = pluginConfig.runInParent ?? pluginConfig.runInMain ?? true
670+
671+
if (options.child && !runInWorker) {
672+
continue
673+
}
674+
675+
if (!options.child && process.env.RUNS_WITH_WORKERS === 'true' && !runInParent) {
676+
continue
677+
}
663678
let module
664679
try {
665-
if (config[pluginName].require) {
666-
module = config[pluginName].require
680+
if (pluginConfig.require) {
681+
module = pluginConfig.require
667682
if (module.startsWith('.')) {
668683
// local
669684
module = path.resolve(global.codecept_dir, module) // custom plugin
@@ -673,7 +688,7 @@ async function createPlugins(config, options = {}) {
673688
}
674689

675690
// Use async loading for all plugins (ESM and CJS)
676-
plugins[pluginName] = await loadPluginAsync(module, config[pluginName])
691+
plugins[pluginName] = await loadPluginAsync(module, pluginConfig)
677692
debug(`plugin ${pluginName} loaded via async import`)
678693
} catch (err) {
679694
throw new Error(`Could not load plugin ${pluginName} from module '${module}':\n${err.message}\n${err.stack}`)

lib/workers.js

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const pathToWorker = path.join(__dirname, 'command', 'workers', 'runTests.js')
2828

2929
const initializeCodecept = async (configPath, options = {}) => {
3030
const config = await mainConfig.load(configPath || '.')
31-
const codecept = new Codecept(config, options)
31+
const codecept = new Codecept(config, { ...options, skipDefaultListeners: true })
3232
await codecept.init(getTestRoot(configPath))
3333
codecept.loadTests()
3434

@@ -625,13 +625,32 @@ class Workers extends EventEmitter {
625625

626626
break
627627
case event.suite.before:
628-
this.emit(event.suite.before, deserializeSuite(message.data))
628+
{
629+
const suite = deserializeSuite(message.data)
630+
this.emit(event.suite.before, suite)
631+
event.dispatcher.emit(event.suite.before, suite)
632+
}
633+
break
634+
case event.suite.after:
635+
{
636+
const suite = deserializeSuite(message.data)
637+
this.emit(event.suite.after, suite)
638+
event.dispatcher.emit(event.suite.after, suite)
639+
}
629640
break
630641
case event.test.before:
631-
this.emit(event.test.before, deserializeTest(message.data))
642+
{
643+
const test = deserializeTest(message.data)
644+
this.emit(event.test.before, test)
645+
event.dispatcher.emit(event.test.before, test)
646+
}
632647
break
633648
case event.test.started:
634-
this.emit(event.test.started, deserializeTest(message.data))
649+
{
650+
const test = deserializeTest(message.data)
651+
this.emit(event.test.started, test)
652+
event.dispatcher.emit(event.test.started, test)
653+
}
635654
break
636655
case event.test.failed:
637656
// For hook failures, emit immediately as there won't be a test.finished event
@@ -645,7 +664,11 @@ class Workers extends EventEmitter {
645664
// Skip individual passed events - we'll emit based on finished state
646665
break
647666
case event.test.skipped:
648-
this.emit(event.test.skipped, deserializeTest(message.data))
667+
{
668+
const test = deserializeTest(message.data)
669+
this.emit(event.test.skipped, test)
670+
event.dispatcher.emit(event.test.skipped, test)
671+
}
649672
break
650673
case event.test.finished:
651674
// Handle different types of test completion properly
@@ -674,28 +697,47 @@ class Workers extends EventEmitter {
674697
}
675698
}
676699

677-
this.emit(event.test.finished, deserializeTest(data))
700+
const test = deserializeTest(data)
701+
this.emit(event.test.finished, test)
702+
event.dispatcher.emit(event.test.finished, test)
678703
}
679704
break
680705
case event.test.after:
681-
this.emit(event.test.after, deserializeTest(message.data))
706+
{
707+
const test = deserializeTest(message.data)
708+
this.emit(event.test.after, test)
709+
event.dispatcher.emit(event.test.after, test)
710+
}
682711
break
683712
case event.step.finished:
684713
this.emit(event.step.finished, message.data)
714+
event.dispatcher.emit(event.step.finished, message.data)
685715
break
686716
case event.step.started:
687717
this.emit(event.step.started, message.data)
718+
event.dispatcher.emit(event.step.started, message.data)
688719
break
689720
case event.step.passed:
690721
this.emit(event.step.passed, message.data)
722+
event.dispatcher.emit(event.step.passed, message.data)
691723
break
692724
case event.step.failed:
693725
this.emit(event.step.failed, message.data, message.data.error)
726+
event.dispatcher.emit(event.step.failed, message.data, message.data.error)
694727
break
695728
case event.hook.failed:
696729
// Hook failures are already reported as test failures by the worker
697730
// Just emit the hook.failed event for listeners
698731
this.emit(event.hook.failed, message.data)
732+
event.dispatcher.emit(event.hook.failed, message.data)
733+
break
734+
case event.hook.passed:
735+
this.emit(event.hook.passed, message.data)
736+
event.dispatcher.emit(event.hook.passed, message.data)
737+
break
738+
case event.hook.finished:
739+
this.emit(event.hook.finished, message.data)
740+
event.dispatcher.emit(event.hook.finished, message.data)
699741
break
700742
}
701743
})

test/helper/webapi.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1580,7 +1580,8 @@ export function tests() {
15801580
},
15811581
)
15821582
} catch (e) {
1583-
expect(e.message).to.include('expected all elements ({css: a[href="/codeceptjs/CodeceptJS"]}) to have attributes {"disable":true} "0" to equal "3"')
1583+
expect(e.message).to.include('expected all elements ({css: a[href="/codeceptjs/CodeceptJS"]}) to have attributes {"disable":true}')
1584+
expect(e.message).to.match(/"0" to equal "\d+"/)
15841585
}
15851586
})
15861587

0 commit comments

Comments
 (0)