From 37f83b75f8eddb7109cdfa3fbc5f31dea3d3336f Mon Sep 17 00:00:00 2001 From: netoctone Date: Thu, 8 Oct 2026 09:20:01 +0100 Subject: [PATCH] feat(ts): improve hook types (Background/Before/After/BeforeSuite/AfterSuite) also, introduce hook aliases global.GherkinBefore and global.GherkinAfter because gherkin hooks types are incompatible with mocha global.Before and global.After --- lib/container.js | 6 ++-- lib/mocha/bdd.js | 20 +++++++++++- .../sandbox/codecept.workers-gherkin.conf.js | 2 +- test/data/sandbox/workers-gherkin/hooks.js | 8 +++++ .../sandbox/workers-gherkin/multi.feature | 9 ++++++ test/unit/worker_test.js | 27 ++++++++++++++++ typings/index.d.ts | 32 +++++++++++++++---- typings/tests/global-variables.types.ts | 10 +++--- 8 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 test/data/sandbox/workers-gherkin/hooks.js create mode 100644 test/data/sandbox/workers-gherkin/multi.feature diff --git a/lib/container.js b/lib/container.js index ca0ffcac2..ea10bd570 100644 --- a/lib/container.js +++ b/lib/container.js @@ -778,8 +778,8 @@ async function loadGherkinStepsAsync(paths) { // Import BDD module to access step file tracking functions and step DSL const bddModule = await import('./mocha/bdd.js') - global.Before = fn => event.dispatcher.on(event.test.started, fn) - global.After = fn => event.dispatcher.on(event.test.finished, fn) + global.Before = global.GherkinBefore = fn => event.dispatcher.on(event.test.started, fn) + global.After = global.GherkinAfter = fn => event.dispatcher.on(event.test.finished, fn) global.Fail = fn => event.dispatcher.on(event.test.failed, fn) // Scope-inject Given/When/Then/And while loading step files so they work @@ -818,7 +818,9 @@ async function loadGherkinStepsAsync(paths) { } delete global.Before + delete global.GherkinBefore delete global.After + delete global.GherkinAfter delete global.Fail if (injectStepDsl) { delete global.Given diff --git a/lib/mocha/bdd.js b/lib/mocha/bdd.js index 7b29a4850..68072320b 100644 --- a/lib/mocha/bdd.js +++ b/lib/mocha/bdd.js @@ -145,15 +145,33 @@ const Before = fn => { event.dispatcher.on(event.test.started, fn) } +const GherkinBefore = Before + const After = fn => { event.dispatcher.on(event.test.finished, fn) } +const GherkinAfter = After + const Fail = fn => { event.dispatcher.on(event.test.failed, fn) } -export { Given, When, Then, And, Before, After, Fail, matchStep, getSteps, clearSteps, defineParameterType } +export { + Given, + When, + Then, + And, + Before, + GherkinBefore, + After, + GherkinAfter, + Fail, + matchStep, + getSteps, + clearSteps, + defineParameterType +} export default { Given: addStep, diff --git a/test/data/sandbox/codecept.workers-gherkin.conf.js b/test/data/sandbox/codecept.workers-gherkin.conf.js index 200b089f5..05948fe5a 100644 --- a/test/data/sandbox/codecept.workers-gherkin.conf.js +++ b/test/data/sandbox/codecept.workers-gherkin.conf.js @@ -12,7 +12,7 @@ export const config = { }, gherkin: { features: './workers-gherkin/*.feature', - steps: ['./workers-gherkin/steps.js'], + steps: ['./workers-gherkin/steps.js', './workers-gherkin/hooks.js'], }, include: {}, bootstrap: false, diff --git a/test/data/sandbox/workers-gherkin/hooks.js b/test/data/sandbox/workers-gherkin/hooks.js new file mode 100644 index 000000000..66cdba21b --- /dev/null +++ b/test/data/sandbox/workers-gherkin/hooks.js @@ -0,0 +1,8 @@ +let beforeCounter = 0; +let afterCounter = 0; +GherkinBefore((test) => { + test.addNote('before', `beforeCounter=${++beforeCounter}`); +}); +GherkinAfter((test) => { + test.addNote('after', `afterCounter=${++afterCounter}`); +}); diff --git a/test/data/sandbox/workers-gherkin/multi.feature b/test/data/sandbox/workers-gherkin/multi.feature new file mode 100644 index 000000000..c1910b595 --- /dev/null +++ b/test/data/sandbox/workers-gherkin/multi.feature @@ -0,0 +1,9 @@ +Feature: Worker gherkin multi scenario + + Scenario Outline: runs once @worker_gherkin_multi + Given I open a browser on a site + + Examples: + | id | + | 1 | + | 2 | diff --git a/test/unit/worker_test.js b/test/unit/worker_test.js index c2c422ae4..6b1b5bbc7 100644 --- a/test/unit/worker_test.js +++ b/test/unit/worker_test.js @@ -444,4 +444,31 @@ describe('Workers', function () { }) }) + it('should run GherkinBefore and GherkinAfter hooks', done => { + const workerConfig = { + by: 'test', + testConfig: './test/data/sandbox/codecept.workers-gherkin.conf.js', + options: { + grep: '@worker_gherkin_multi', + }, + } + + let passedCount = 0 + const workers = new Workers(1, workerConfig) + + workers.on(event.test.passed, (test) => { + passedCount += 1 + expect(test.notes[0].type).to.equal('before') + expect(test.notes[0].text).to.equal(`beforeCounter=${passedCount}`) + expect(test.notes[1].type).to.equal('after') + expect(test.notes[1].text).to.equal(`afterCounter=${passedCount}`) + }) + + workers.run() + + workers.on(event.all.result, result => { + done() + }) + }) + }) diff --git a/typings/index.d.ts b/typings/index.d.ts index 9372d48e2..50804f3a9 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -468,7 +468,12 @@ declare namespace CodeceptJS { // Could get extended by user generated typings interface Methods extends ActorStatic {} interface I {} - interface IHook {} + interface IHook { + (callback: HookCallback): HookConfig + } + interface IHookSuite { + (callback: HookSuiteCallback): HookConfig + } interface IScenario {} interface IFeature { (title: string, opts?: { [key: string]: any }): FeatureConfig @@ -508,8 +513,22 @@ declare namespace CodeceptJS { type StringOrSecret = string | CodeceptJS.Secret + interface GherkinCallback { + (test: Test): void | Promise + } + interface GherkinEventEmitter {} + interface GherkinHook { + (callback: GherkinCallback): GherkinEventEmitter + } interface HookCallback { - (args: SupportObject): void | Promise + // `suite` has type Mocha.Suite if hook (Background/Before/After) is called before `Feature()` + // `suite` has type CodeceptJS.Suite if hook is called after `Feature()` + (args: SupportObject & { suite: Mocha.Suite; test: Test }): void | Promise + } + interface HookSuiteCallback { + // `suite` has type Mocha.Suite if hook (BeforeSuite/AfterSuite) is called before `Feature()` + // `suite` has type CodeceptJS.Suite if hook is called after `Feature()` + (args: SupportObject & { suite: Mocha.Suite }): void | Promise } interface Scenario extends IScenario { only: IScenario @@ -530,9 +549,6 @@ declare namespace CodeceptJS { (title: string, callback?: HookCallback): ScenarioConfig (title: string, opts: { [key: string]: any }, callback: HookCallback): ScenarioConfig } - interface IHook { - (callback: HookCallback): HookConfig - } interface Globals { codeceptjs: typeof codeceptjs @@ -580,11 +596,13 @@ declare function xData(data: any): CodeceptJS.IData declare function DefineParameterType(options: CodeceptJS.IParameterTypeDefinition): void // Hooks -declare const BeforeSuite: CodeceptJS.IHook -declare const AfterSuite: CodeceptJS.IHook +declare const BeforeSuite: CodeceptJS.IHookSuite +declare const AfterSuite: CodeceptJS.IHookSuite declare const Background: CodeceptJS.IHook declare const Before: CodeceptJS.IHook declare const After: CodeceptJS.IHook +declare const GherkinBefore: CodeceptJS.GherkinHook +declare const GherkinAfter: CodeceptJS.GherkinHook // Plugins declare const __: any diff --git a/typings/tests/global-variables.types.ts b/typings/tests/global-variables.types.ts index 1d459b880..f93ebd122 100644 --- a/typings/tests/global-variables.types.ts +++ b/typings/tests/global-variables.types.ts @@ -54,7 +54,7 @@ expectType( expectType( Scenario('scenario', args => { // @ts-ignore - expectType(args) + expectType(args) // @ts-ignore expectType(args.I) // $ExpectType I }), @@ -72,7 +72,7 @@ expectType( expectType( Before(args => { // @ts-ignore - expectType(args) + expectType(args) // @ts-ignore expectType(args.I) }), @@ -82,7 +82,7 @@ expectType( expectType( BeforeSuite(args => { // @ts-ignore - expectType(args) + expectType(args) // @ts-ignore expectType(args.I) }), @@ -92,7 +92,7 @@ expectType( expectType( After(args => { // @ts-ignore - expectType(args) + expectType(args) // @ts-ignore expectType(args.I) }), @@ -102,7 +102,7 @@ expectType( expectType( AfterSuite(args => { // @ts-ignore - expectType(args) + expectType(args) // @ts-ignore expectType(args.I) }),