diff --git a/lib/container.js b/lib/container.js index fc5bfb2e1..b73cbb5ae 100644 --- a/lib/container.js +++ b/lib/container.js @@ -674,6 +674,14 @@ async function createPlugins(config, options = {}) { // Use async loading for all plugins (ESM and CJS) plugins[pluginName] = await loadPluginAsync(module, config[pluginName]) + + // Skip loading plugin in parent process if runInParent is false + if (config[pluginName].runInParent === false && process.env.RUNS_WITH_WORKERS) { + delete plugins[pluginName] + debug(`plugin ${pluginName} skipped in parent process (runInParent: false)`) + continue + } + debug(`plugin ${pluginName} loaded via async import`) } catch (err) { throw new Error(`Could not load plugin ${pluginName} from module '${module}':\n${err.message}\n${err.stack}`) diff --git a/lib/listener/globalTimeout.js b/lib/listener/globalTimeout.js index 7acd9c78e..e569aacc8 100644 --- a/lib/listener/globalTimeout.js +++ b/lib/listener/globalTimeout.js @@ -122,7 +122,7 @@ export default function () { if (typeof timeout !== 'number') return if (!store.timeouts) { - debug('step', step.toCode().trim(), 'timeout disabled') + debug('step', (step.code || (typeof step.toCode === 'function' ? step.toCode() : step.name)).trim(), 'timeout disabled') return } @@ -130,7 +130,7 @@ export default function () { debug('Previous steps timed out, setting timeout to 0.01s') step.setTimeout(0.01, TIMEOUT_ORDER.testOrSuite) } else { - debug(`Setting timeout ${timeout}ms for step ${step.toCode().trim()}`) + debug(`Setting timeout ${timeout}ms for step ${(step.code || (typeof step.toCode === 'function' ? step.toCode() : step.name)).trim()}`) step.setTimeout(timeout, TIMEOUT_ORDER.testOrSuite) } }) @@ -158,17 +158,17 @@ export default function () { event.dispatcher.on(event.step.finished, step => { if (!store.timeouts) { - debug('step', step.toCode().trim(), 'timeout disabled') + debug('step', (step.code || (typeof step.toCode === 'function' ? step.toCode() : step.name)).trim(), 'timeout disabled') return } if (typeof timeout === 'number') debug('Timeout', timeout) - debug(`step ${step.toCode().trim()}:${step.status} duration`, step.duration) + debug(`step ${(step.code || (typeof step.toCode === 'function' ? step.toCode() : step.name)).trim()}:${step.status} duration`, step.duration) if (typeof timeout === 'number' && !Number.isNaN(timeout)) timeout -= step.duration if (typeof timeout === 'number' && timeout <= 0 && recorder.isRunning()) { - debug(`step ${step.toCode().trim()} timed out`) + debug(`step ${(step.code || (typeof step.toCode === 'function' ? step.toCode() : step.name)).trim()} timed out`) recorder.throw(new TestTimeoutError(currentTimeout)) } }) diff --git a/lib/plugin/autoDelay.js b/lib/plugin/autoDelay.js index 4f89b7013..89a1af148 100644 --- a/lib/plugin/autoDelay.js +++ b/lib/plugin/autoDelay.js @@ -15,6 +15,7 @@ const defaultConfig = { methods: methodsToDelay, delayBefore: 100, delayAfter: 200, + runInParent: false, } /** diff --git a/lib/plugin/htmlReporter.js b/lib/plugin/htmlReporter.js index c762d8fae..7e7f89cae 100644 --- a/lib/plugin/htmlReporter.js +++ b/lib/plugin/htmlReporter.js @@ -28,6 +28,7 @@ const defaultConfig = { keepHistory: false, historyPath: './test-history.json', maxHistoryEntries: 50, + runInParent: true, } /** diff --git a/lib/plugin/pauseOnFail.js b/lib/plugin/pauseOnFail.js index 36511ff1b..e9987350b 100644 --- a/lib/plugin/pauseOnFail.js +++ b/lib/plugin/pauseOnFail.js @@ -22,7 +22,11 @@ import pause from '../pause.js' * ``` * */ -export default function() { +const defaultConfig = { + runInParent: false, +} + +export default function(config) { let failed = false event.dispatcher.on(event.test.started, () => { diff --git a/lib/plugin/retryFailedStep.js b/lib/plugin/retryFailedStep.js index 4c1893a4c..4810b1744 100644 --- a/lib/plugin/retryFailedStep.js +++ b/lib/plugin/retryFailedStep.js @@ -9,6 +9,7 @@ const defaultConfig = { defaultIgnoredSteps: ['amOnPage', 'wait*', 'send*', 'execute*', 'run*', 'have*'], factor: 1.5, ignoredSteps: [], + runInParent: false, } /** diff --git a/lib/plugin/screenshotOnFail.js b/lib/plugin/screenshotOnFail.js index fa427e035..793add787 100644 --- a/lib/plugin/screenshotOnFail.js +++ b/lib/plugin/screenshotOnFail.js @@ -17,6 +17,7 @@ const defaultConfig = { uniqueScreenshotNames: false, disableScreenshots: false, fullPageScreenshots: false, + runInParent: false, } const supportedHelpers = Container.STANDARD_ACTING_HELPERS diff --git a/lib/plugin/stepTimeout.js b/lib/plugin/stepTimeout.js index 9250e26e7..831077b42 100644 --- a/lib/plugin/stepTimeout.js +++ b/lib/plugin/stepTimeout.js @@ -7,6 +7,7 @@ const defaultConfig = { overrideStepLimits: false, noTimeoutSteps: ['amOnPage', 'wait*'], customTimeoutSteps: [], + runInParent: false, } /** diff --git a/lib/step/base.js b/lib/step/base.js index 1000c048e..b2ab35381 100644 --- a/lib/step/base.js +++ b/lib/step/base.js @@ -228,6 +228,7 @@ class Step { startTime: step.startTime, endTime: step.endTime, parent, + code: step.toCode(), } } diff --git a/lib/workers.js b/lib/workers.js index 0ed3a71b3..0df6168db 100644 --- a/lib/workers.js +++ b/lib/workers.js @@ -626,18 +626,22 @@ class Workers extends EventEmitter { break case event.suite.before: this.emit(event.suite.before, deserializeSuite(message.data)) + event.dispatcher.emit(event.suite.before, deserializeSuite(message.data)) break case event.test.before: this.emit(event.test.before, deserializeTest(message.data)) + event.dispatcher.emit(event.test.before, deserializeTest(message.data)) break case event.test.started: this.emit(event.test.started, deserializeTest(message.data)) + event.dispatcher.emit(event.test.started, deserializeTest(message.data)) break case event.test.failed: // For hook failures, emit immediately as there won't be a test.finished event // Regular test failures are handled via test.finished to support retries if (message.data?.hookName) { this.emit(event.test.failed, deserializeTest(message.data)) + event.dispatcher.emit(event.test.failed, deserializeTest(message.data)) } // Otherwise skip - we'll emit based on finished state break @@ -646,6 +650,7 @@ class Workers extends EventEmitter { break case event.test.skipped: this.emit(event.test.skipped, deserializeTest(message.data)) + event.dispatcher.emit(event.test.skipped, deserializeTest(message.data)) break case event.test.finished: // Handle different types of test completion properly @@ -669,34 +674,42 @@ class Workers extends EventEmitter { // For tests without UID, emit immediately if (isFailed) { this.emit(event.test.failed, deserializeTest(data)) + event.dispatcher.emit(event.test.failed, deserializeTest(data)) } else { this.emit(event.test.passed, deserializeTest(data)) + event.dispatcher.emit(event.test.passed, deserializeTest(data)) } } this.emit(event.test.finished, deserializeTest(data)) + event.dispatcher.emit(event.test.finished, deserializeTest(data)) } break case event.test.after: this.emit(event.test.after, deserializeTest(message.data)) + event.dispatcher.emit(event.test.after, deserializeTest(message.data)) break case event.step.finished: this.emit(event.step.finished, message.data) break case event.step.started: this.emit(event.step.started, message.data) + event.dispatcher.emit(event.step.started, message.data) break case event.step.passed: this.emit(event.step.passed, message.data) + event.dispatcher.emit(event.step.passed, message.data) break case event.step.failed: this.emit(event.step.failed, message.data, message.data.error) + event.dispatcher.emit(event.step.failed, message.data, message.data.error) break case event.hook.failed: // Hook failures are already reported as test failures by the worker // Just emit the hook.failed event for listeners this.emit(event.hook.failed, message.data) break + } }) @@ -759,16 +772,20 @@ class Workers extends EventEmitter { for (const state of states) { if (state.isFailed) { this.emit(event.test.failed, deserializeTest(state.data)) + event.dispatcher.emit(event.test.failed, deserializeTest(state.data)) } else { this.emit(event.test.passed, deserializeTest(state.data)) + event.dispatcher.emit(event.test.passed, deserializeTest(state.data)) } } } else { // For non-retries (like step failures), emit only the final state if (lastState.isFailed) { this.emit(event.test.failed, deserializeTest(lastState.data)) + event.dispatcher.emit(event.test.failed, deserializeTest(lastState.data)) } else { this.emit(event.test.passed, deserializeTest(lastState.data)) + event.dispatcher.emit(event.test.passed, deserializeTest(lastState.data)) } } }