diff --git a/CHANGELOG.md b/CHANGELOG.md index 633ea7fc5..1614e6856 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ See the [migration guide](./docs/migration.md) for details of how to migrate fro ### Added +* `BeforeStep` and `AfterStep` hook functions now have access to the `pickleStep` in their argument object. * `--config` option to the CLI. It allows you to specify a configuration file other than `cucumber.js`. See [docs/profiles.md](./docs/profiles.md#using-another-file-than-cucumberjs) for more info. [#1794](https://github.com/cucumber/cucumber-js/pull/1794) diff --git a/docs/support_files/api_reference.md b/docs/support_files/api_reference.md index e47a01570..0e9ee94cd 100644 --- a/docs/support_files/api_reference.md +++ b/docs/support_files/api_reference.md @@ -69,8 +69,9 @@ Defines a hook which is run after each step. * `tags`: String tag expression used to apply this hook to only specific scenarios. See [cucumber-tag-expressions](https://docs.cucumber.io/tag-expressions/) for more information. * `timeout`: A hook-specific timeout, to override the default timeout. * `fn`: A function, defined as follows: - * The first argument will be an object of the form `{pickle, gherkinDocument, result, testCaseStartedId, testStepId}` - * The pickle object comes from the [gherkin](https://github.com/cucumber/cucumber/tree/gherkin/v15.0.2/gherkin) library. See `testdata/good/*.pickles.ndjson` for examples of its structure. + * The first argument will be an object of the form `{pickle, pickleStep, gherkinDocument, result, testCaseStartedId, testStepId}` + * The `pickle` object comes from the [gherkin](https://github.com/cucumber/cucumber/tree/gherkin/v15.0.2/gherkin) library. See `testdata/good/*.pickles.ndjson` for examples of its structure. + * The `pickleStep` is the step in the `pickle` that this hook has been invoked for * When using the asynchronous callback interface, have one final argument for the callback function. `options` can also be a string as a shorthand for specifying `tags`. diff --git a/src/runtime/test_case_runner.ts b/src/runtime/test_case_runner.ts index 308e04c22..9b77ce904 100644 --- a/src/runtime/test_case_runner.ts +++ b/src/runtime/test_case_runner.ts @@ -248,12 +248,14 @@ export default class TestCaseRunner { async runStepHooks( stepHooks: TestStepHookDefinition[], + pickleStep: messages.PickleStep, stepResult?: messages.TestStepResult ): Promise { const stepHooksResult = [] const hookParameter: ITestStepHookParameter = { gherkinDocument: this.gherkinDocument, pickle: this.pickle, + pickleStep, testCaseStartedId: this.currentTestCaseStartedId, testStepId: this.currentTestStepId, result: stepResult, @@ -295,7 +297,8 @@ export default class TestCaseRunner { let stepResult let stepResults = await this.runStepHooks( - this.getBeforeStepHookDefinitions() + this.getBeforeStepHookDefinitions(), + pickleStep ) if ( getWorstTestStepResult(stepResults).status !== @@ -306,6 +309,7 @@ export default class TestCaseRunner { } const afterStepHookResults = await this.runStepHooks( this.getAfterStepHookDefinitions(), + pickleStep, stepResult ) stepResults = stepResults.concat(afterStepHookResults) diff --git a/src/runtime/test_case_runner_spec.ts b/src/runtime/test_case_runner_spec.ts index 76ad1c77b..1d5b1594d 100644 --- a/src/runtime/test_case_runner_spec.ts +++ b/src/runtime/test_case_runner_spec.ts @@ -1,5 +1,6 @@ import { afterEach, beforeEach, describe, it } from 'mocha' import { expect } from 'chai' +import sinon from 'sinon' import TestCaseRunner from './test_case_runner' import { EventEmitter } from 'events' import { IdGenerator } from '@cucumber/messages' @@ -434,14 +435,17 @@ describe('TestCaseRunner', () => { describe('with step hooks', () => { it('emits the expected envelopes and returns a skipped result', async () => { + const beforeStep = sinon.stub() + const afterStep = sinon.stub() + // Arrange const supportCodeLibrary = buildSupportCodeLibrary( ({ Given, BeforeStep, AfterStep }) => { Given('a step', function () { clock.tick(1) }) - BeforeStep(function () {}) // eslint-disable-line @typescript-eslint/no-empty-function - AfterStep(function () {}) // eslint-disable-line @typescript-eslint/no-empty-function + BeforeStep(beforeStep) // eslint-disable-line @typescript-eslint/no-empty-function + AfterStep(afterStep) // eslint-disable-line @typescript-eslint/no-empty-function } ) const { @@ -464,6 +468,22 @@ describe('TestCaseRunner', () => { expect(result).to.eql( envelopes[2].testStepFinished.testStepResult.status ) + expect(beforeStep).to.have.been.calledOnceWith({ + gherkinDocument, + pickle, + pickleStep: pickle.steps[0], + testCaseStartedId: envelopes[1].testStepStarted.testCaseStartedId, + testStepId: envelopes[1].testStepStarted.testStepId, + result: undefined, + }) + expect(afterStep).to.have.been.calledOnceWith({ + gherkinDocument, + pickle, + pickleStep: pickle.steps[0], + testCaseStartedId: envelopes[2].testStepFinished.testCaseStartedId, + testStepId: envelopes[2].testStepFinished.testStepId, + result: envelopes[2].testStepFinished.testStepResult, + }) }) }) }) diff --git a/src/support_code_library_builder/types.ts b/src/support_code_library_builder/types.ts index 693dd8a2b..4738353e6 100644 --- a/src/support_code_library_builder/types.ts +++ b/src/support_code_library_builder/types.ts @@ -18,6 +18,7 @@ export interface ITestCaseHookParameter { export interface ITestStepHookParameter { gherkinDocument: messages.GherkinDocument pickle: messages.Pickle + pickleStep: messages.PickleStep result: messages.TestStepResult testCaseStartedId: string testStepId: string