From 62a957aaf14a71bf6f5b54f2a56820b094e9d07d Mon Sep 17 00:00:00 2001 From: David Goss Date: Fri, 8 Apr 2022 15:53:15 +0100 Subject: [PATCH 01/13] update compatibility kit --- package-lock.json | 24 +++++++++++++++--------- package.json | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3b816b43..16f5b0684 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,7 +48,7 @@ "cucumber-js": "bin/cucumber-js" }, "devDependencies": { - "@cucumber/compatibility-kit": "9.1.2", + "@cucumber/compatibility-kit": "9.2.0", "@cucumber/message-streams": "4.0.1", "@cucumber/query": "11.0.0", "@microsoft/api-documenter": "7.17.0", @@ -536,10 +536,13 @@ "integrity": "sha512-da6H/wtVerhGUP4OCWTOmbNd4+gC1FhAcLzYgn6O68HgQbMwkmV3M8AwtbQWZkfF+Ph7z0M/UQYYdNIDu5V5MA==" }, "node_modules/@cucumber/compatibility-kit": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@cucumber/compatibility-kit/-/compatibility-kit-9.1.2.tgz", - "integrity": "sha512-oB01JROFcwFfbqMV+jtJtj8bWU6mrLPUomuki/f9TvXsHMjYgqkBopeJqjcWWtgIfA7Y2CZEnTMWdLxoyBd4RA==", - "dev": true + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/compatibility-kit/-/compatibility-kit-9.2.0.tgz", + "integrity": "sha512-NsXLgmVorgBIJpNrP6ISqIsFD62XCfmzAmgVE0HEz5DkfRbE78adT1UvzzAvrlJPNOG5FX6RF3Ss9Sg/f8gYOg==", + "dev": true, + "dependencies": { + "@cucumber/message-streams": "^4.0.1" + } }, "node_modules/@cucumber/cucumber-expressions": { "version": "15.0.2", @@ -7999,10 +8002,13 @@ "integrity": "sha512-da6H/wtVerhGUP4OCWTOmbNd4+gC1FhAcLzYgn6O68HgQbMwkmV3M8AwtbQWZkfF+Ph7z0M/UQYYdNIDu5V5MA==" }, "@cucumber/compatibility-kit": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@cucumber/compatibility-kit/-/compatibility-kit-9.1.2.tgz", - "integrity": "sha512-oB01JROFcwFfbqMV+jtJtj8bWU6mrLPUomuki/f9TvXsHMjYgqkBopeJqjcWWtgIfA7Y2CZEnTMWdLxoyBd4RA==", - "dev": true + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/compatibility-kit/-/compatibility-kit-9.2.0.tgz", + "integrity": "sha512-NsXLgmVorgBIJpNrP6ISqIsFD62XCfmzAmgVE0HEz5DkfRbE78adT1UvzzAvrlJPNOG5FX6RF3Ss9Sg/f8gYOg==", + "dev": true, + "requires": { + "@cucumber/message-streams": "^4.0.1" + } }, "@cucumber/cucumber-expressions": { "version": "15.0.2", diff --git a/package.json b/package.json index f7804af9c..7ddbb1046 100644 --- a/package.json +++ b/package.json @@ -230,7 +230,7 @@ "yup": "^0.32.11" }, "devDependencies": { - "@cucumber/compatibility-kit": "9.1.2", + "@cucumber/compatibility-kit": "9.2.0", "@cucumber/message-streams": "4.0.1", "@cucumber/query": "11.0.0", "@microsoft/api-documenter": "7.17.0", From 130014017dec9599443e65667c01c3616fce3662 Mon Sep 17 00:00:00 2001 From: David Goss Date: Fri, 8 Apr 2022 16:01:29 +0100 Subject: [PATCH 02/13] add failing feature test --- features/named_hooks.feature | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 features/named_hooks.feature diff --git a/features/named_hooks.feature b/features/named_hooks.feature new file mode 100644 index 000000000..0bd708aa7 --- /dev/null +++ b/features/named_hooks.feature @@ -0,0 +1,28 @@ +Feature: Named hooks + + Scenario: + Given a file named "features/a.feature" with: + """ + Feature: some feature + Scenario: some scenario + Given a step + """ + Given a file named "features/step_definitions/hooks.js" with: + """ + const {After, Before} = require('@cucumber/cucumber') + + Before({name: 'hook 1'}, function() {}) + Before({name: 'hook 2'}, function() {}) + After({name: 'hook 3'}, function() {}) + """ + Given a file named "features/step_definitions/steps.js" with: + """ + const {Given} = require('@cucumber/cucumber') + + Given('a step', function() {}) + """ + When I run cucumber-js + Then the output contains the text: + """ + Before "hook 2" + """ From 4b4d3375f3a0712ebbfd63d032fecf1dde96d9fa Mon Sep 17 00:00:00 2001 From: David Goss Date: Fri, 8 Apr 2022 16:03:56 +0100 Subject: [PATCH 03/13] add failing type test --- test-d/hooks.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test-d/hooks.ts b/test-d/hooks.ts index 3446f3bbe..7b582c2f4 100644 --- a/test-d/hooks.ts +++ b/test-d/hooks.ts @@ -23,6 +23,10 @@ After(function (param: ITestCaseHookParameter) {}) BeforeStep(function (param: ITestStepHookParameter) {}) AfterStep(function (param: ITestStepHookParameter) {}) +// should allow an object with tags and/or name in hooks +Before({ tags: '@foo', name: 'before hook' }, function () {}) +After({ tags: '@foo', name: 'after hook' }, function () {}) + // should allow us to return 'skipped' from a test case hook Before(async function () { return 'skipped' From dbc4198c9b23abf09d229014146287387d7941a5 Mon Sep 17 00:00:00 2001 From: David Goss Date: Fri, 8 Apr 2022 16:06:04 +0100 Subject: [PATCH 04/13] add name to object --- src/support_code_library_builder/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/support_code_library_builder/types.ts b/src/support_code_library_builder/types.ts index 1dae3542b..5c03d3f77 100644 --- a/src/support_code_library_builder/types.ts +++ b/src/support_code_library_builder/types.ts @@ -48,6 +48,7 @@ export interface IDefineStepOptions { } export interface IDefineTestCaseHookOptions { + name?: string tags?: string timeout?: number } From bc718af884ea7d9da8ee47c76a5c9ca7b9bf073f Mon Sep 17 00:00:00 2001 From: David Goss Date: Fri, 8 Apr 2022 16:13:38 +0100 Subject: [PATCH 05/13] enough to make cck pass --- compatibility/features/hooks/hooks.ts | 4 ++++ src/cli/helpers.ts | 1 + src/models/definition.ts | 1 + src/models/test_case_hook_definition.ts | 2 ++ 4 files changed, 8 insertions(+) diff --git a/compatibility/features/hooks/hooks.ts b/compatibility/features/hooks/hooks.ts index 1ce001c6f..073b355c8 100644 --- a/compatibility/features/hooks/hooks.ts +++ b/compatibility/features/hooks/hooks.ts @@ -6,6 +6,10 @@ Before(function () { // no-op }) +Before({ name: 'A named hook' }, function () { + // no-op +}) + When('a step passes', function () { // no-op }) diff --git a/src/cli/helpers.ts b/src/cli/helpers.ts index 1d54419f6..866321202 100644 --- a/src/cli/helpers.ts +++ b/src/cli/helpers.ts @@ -195,6 +195,7 @@ function emitTestCaseHooks( const envelope: messages.Envelope = { hook: { id: testCaseHookDefinition.id, + name: testCaseHookDefinition.name, tagExpression: testCaseHookDefinition.tagExpression, sourceReference: { uri: testCaseHookDefinition.uri, diff --git a/src/models/definition.ts b/src/models/definition.ts index 171e89648..a7a64d9ae 100644 --- a/src/models/definition.ts +++ b/src/models/definition.ts @@ -20,6 +20,7 @@ export interface IDefinitionOptions { } export interface IHookDefinitionOptions extends IDefinitionOptions { + name?: string tags?: string } diff --git a/src/models/test_case_hook_definition.ts b/src/models/test_case_hook_definition.ts index 21d8b47e7..737eb5ada 100644 --- a/src/models/test_case_hook_definition.ts +++ b/src/models/test_case_hook_definition.ts @@ -12,11 +12,13 @@ export default class TestCaseHookDefinition extends Definition implements IDefinition { + public readonly name: string public readonly tagExpression: string private readonly pickleTagFilter: PickleTagFilter constructor(data: IDefinitionParameters) { super(data) + this.name = data.options.name this.tagExpression = data.options.tags this.pickleTagFilter = new PickleTagFilter(data.options.tags) } From e077564d3a267d16e94a974989bdaa3f46247aec Mon Sep 17 00:00:00 2001 From: David Goss Date: Fri, 8 Apr 2022 16:36:24 +0100 Subject: [PATCH 06/13] update formatter stuff so feature test passes --- features/named_hooks.feature | 13 ++++++++----- .../helpers/test_case_attempt_formatter.ts | 4 ++++ src/formatter/helpers/test_case_attempt_parser.ts | 2 ++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/features/named_hooks.feature b/features/named_hooks.feature index 0bd708aa7..f0e3ef26f 100644 --- a/features/named_hooks.feature +++ b/features/named_hooks.feature @@ -7,7 +7,7 @@ Feature: Named hooks Scenario: some scenario Given a step """ - Given a file named "features/step_definitions/hooks.js" with: + And a file named "features/step_definitions/hooks.js" with: """ const {After, Before} = require('@cucumber/cucumber') @@ -15,14 +15,17 @@ Feature: Named hooks Before({name: 'hook 2'}, function() {}) After({name: 'hook 3'}, function() {}) """ - Given a file named "features/step_definitions/steps.js" with: + And a file named "features/step_definitions/steps.js" with: """ const {Given} = require('@cucumber/cucumber') - Given('a step', function() {}) + Given('a step', function() { + throw 'nope' + }) """ When I run cucumber-js - Then the output contains the text: + Then it fails + And the output contains the text: """ - Before "hook 2" + Before (hook 2) # """ diff --git a/src/formatter/helpers/test_case_attempt_formatter.ts b/src/formatter/helpers/test_case_attempt_formatter.ts index 87aa30f9b..7988affa6 100644 --- a/src/formatter/helpers/test_case_attempt_formatter.ts +++ b/src/formatter/helpers/test_case_attempt_formatter.ts @@ -49,6 +49,7 @@ function formatStep({ printAttachments, }: IFormatStepRequest): string { const { + name, result: { status }, actionLocation, attachments, @@ -56,6 +57,9 @@ function formatStep({ const colorFn = colorFns.forStatus(status) const identifier = testStep.keyword + valueOrDefault(testStep.text, '') let text = colorFn(`${CHARACTERS.get(status)} ${identifier}`) + if (doesHaveValue(name)) { + text += colorFn(` (${name})`) + } if (doesHaveValue(actionLocation)) { text += ` # ${colorFns.location(formatLocation(actionLocation))}` } diff --git a/src/formatter/helpers/test_case_attempt_parser.ts b/src/formatter/helpers/test_case_attempt_parser.ts index ddf04d4f5..3f92285fa 100644 --- a/src/formatter/helpers/test_case_attempt_parser.ts +++ b/src/formatter/helpers/test_case_attempt_parser.ts @@ -18,6 +18,7 @@ export interface IParsedTestStep { argument?: messages.PickleStepArgument attachments: messages.Attachment[] keyword: string + name?: string result: messages.TestStepResult snippet?: string sourceLocation?: ILineAndUri @@ -87,6 +88,7 @@ function parseStep({ uri: hookDefinition.uri, line: hookDefinition.line, } + out.name = hookDefinition.name } if ( doesHaveValue(testStep.stepDefinitionIds) && From 31c8d09ef58244b9000fa7a611c219cfcd684c0a Mon Sep 17 00:00:00 2001 From: David Goss Date: Fri, 8 Apr 2022 16:45:33 +0100 Subject: [PATCH 07/13] fix this test --- src/cli/helpers_spec.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cli/helpers_spec.ts b/src/cli/helpers_spec.ts index 214269d60..1c658c789 100644 --- a/src/cli/helpers_spec.ts +++ b/src/cli/helpers_spec.ts @@ -220,6 +220,7 @@ describe('helpers', () => { id: '0', line: 3, options: { + name: 'before hook', tags: '@hooks-tho', }, uri: 'features/support/hooks.js', @@ -231,7 +232,9 @@ describe('helpers', () => { unwrappedCode: noopFunction, id: '1', line: 7, - options: {}, + options: { + name: 'after hook', + }, uri: 'features/support/hooks.js', }), new TestCaseHookDefinition({ @@ -249,6 +252,7 @@ describe('helpers', () => { { hook: { id: '0', + name: 'before hook', tagExpression: '@hooks-tho', sourceReference: { uri: 'features/support/hooks.js', @@ -261,6 +265,7 @@ describe('helpers', () => { { hook: { id: '1', + name: 'after hook', tagExpression: undefined, sourceReference: { uri: 'features/support/hooks.js', @@ -273,6 +278,7 @@ describe('helpers', () => { { hook: { id: '2', + name: undefined, tagExpression: undefined, sourceReference: { uri: 'features/support/hooks.js', From 0b3413f3f15a9e3f32136ca3369c01f6d6f254bc Mon Sep 17 00:00:00 2001 From: David Goss Date: Sun, 10 Apr 2022 10:42:01 +0100 Subject: [PATCH 08/13] add doco --- docs/support_files/hooks.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/support_files/hooks.md b/docs/support_files/hooks.md index df9e25a63..4f5d4b61c 100644 --- a/docs/support_files/hooks.md +++ b/docs/support_files/hooks.md @@ -6,7 +6,7 @@ Note that your hook functions cannot reference the [world](./world.md) as `this` arrow functions. See [FAQ](../faq.md) for details. ```javascript -var {After, Before} = require('@cucumber/cucumber'); +const {After, Before} = require('@cucumber/cucumber'); // Synchronous Before(function () { @@ -33,12 +33,26 @@ After(function () { }); ``` +## Named hooks (since v8.1.0) + +Hooks can optionally be named: + +```javascript +const {Before} = require('@cucumber/cucumber'); + +Before({name: "Set up some test state"}, function () { +// do stuff here +}); +``` + +Such hooks will then be referenced by name in [formatter](../formatters.md) output, which can be useful to help you understand what's happening with your tests. + ## Tagged hooks Hooks can be conditionally selected for execution based on the tags of the scenario. ```javascript -var {After, Before} = require('@cucumber/cucumber'); +const {After, Before} = require('@cucumber/cucumber'); Before(function () { // This hook will be executed before all scenarios @@ -85,7 +99,7 @@ If you have some setup / teardown that needs to be done before or after all scen Unlike `Before` / `After` these methods will not have a world instance as `this`. This is because each scenario gets its own world instance and these hooks run before / after **all** scenarios. ```javascript -var {AfterAll, BeforeAll} = require('@cucumber/cucumber'); +const {AfterAll, BeforeAll} = require('@cucumber/cucumber'); // Synchronous BeforeAll(function () { @@ -111,7 +125,7 @@ AfterAll(function () { If you have some code execution that needs to be done before or after all steps, use `BeforeStep` / `AfterStep`. Like the `Before` / `After` hooks, these also have a world instance as 'this', and can be conditionally selected for execution based on the tags of the scenario. ```javascript -var {AfterStep, BeforeStep} = require('@cucumber/cucumber'); +const {AfterStep, BeforeStep} = require('@cucumber/cucumber'); BeforeStep({tags: "@foo"}, function () { // This hook will be executed before all steps in a scenario with tag @foo From 324f4faa90903bbed79e4cf51292b4d5cec5f6da Mon Sep 17 00:00:00 2001 From: David Goss Date: Sun, 10 Apr 2022 10:43:39 +0100 Subject: [PATCH 09/13] more feature file text --- features/named_hooks.feature | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/features/named_hooks.feature b/features/named_hooks.feature index f0e3ef26f..be68a4f34 100644 --- a/features/named_hooks.feature +++ b/features/named_hooks.feature @@ -1,6 +1,10 @@ Feature: Named hooks - Scenario: + As a developer + I want to name a `Before` or `After` hook + So that I can easily identify which hooks are run when reporting + + Scenario: Hook is named and then referenced by its name in formatter output Given a file named "features/a.feature" with: """ Feature: some feature From a758535fb94bb6d3094e243a8c6a9e31ae613101 Mon Sep 17 00:00:00 2001 From: David Goss Date: Tue, 12 Apr 2022 08:20:20 +0100 Subject: [PATCH 10/13] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcea99e94..f7c84b531 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CONTRIBUTING.md) on how to contribute to Cucumber. ## [Unreleased] +### Added +- Add support for named hooks (see [documentation](./docs/support_files/hooks.md#named-hooks)) ([#1994](https://github.com/cucumber/cucumber-js/pull/1994)) + ### Changed - Rename the `cucumber-js` binary's underlying file to be `cucumber.js`, so it doesn't fall foul of Node.js module conventions and plays nicely with ESM loaders (see [documentation](./docs/esm.md#transpiling)) ([#1993](https://github.com/cucumber/cucumber-js/pull/1993)) From dd646cd030398b69a9a4b55b0cfeb416ef2ccd6e Mon Sep 17 00:00:00 2001 From: David Goss Date: Tue, 12 Apr 2022 08:20:48 +0100 Subject: [PATCH 11/13] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7c84b531..b6e2ad3e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CONTRIBUTING.md) on how to contribute to Cucumber. ## [Unreleased] -### Added +### Added - Add support for named hooks (see [documentation](./docs/support_files/hooks.md#named-hooks)) ([#1994](https://github.com/cucumber/cucumber-js/pull/1994)) ### Changed From 790cc027289324ab7bb9904a9c8cbd57b7301a9a Mon Sep 17 00:00:00 2001 From: David Goss Date: Tue, 12 Apr 2022 08:21:35 +0100 Subject: [PATCH 12/13] tweak documentation --- docs/support_files/hooks.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/support_files/hooks.md b/docs/support_files/hooks.md index 4f5d4b61c..c7aba803d 100644 --- a/docs/support_files/hooks.md +++ b/docs/support_files/hooks.md @@ -33,7 +33,9 @@ After(function () { }); ``` -## Named hooks (since v8.1.0) +## Named hooks + +ℹ️ Added in v8.1.0 Hooks can optionally be named: From 53f824838a8861aa2936480dff1e34efa9c58919 Mon Sep 17 00:00:00 2001 From: David Goss Date: Fri, 15 Apr 2022 18:53:29 +0100 Subject: [PATCH 13/13] update html-formatter dep --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5c2df6421..fa57ace66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@cucumber/gherkin": "23.0.1", "@cucumber/gherkin-streams": "5.0.1", "@cucumber/gherkin-utils": "^7.0.0", - "@cucumber/html-formatter": "19.0.0", + "@cucumber/html-formatter": "19.1.0", "@cucumber/messages": "18.0.0", "@cucumber/tag-expressions": "4.1.0", "assertion-error-formatter": "^3.0.0", @@ -611,11 +611,11 @@ } }, "node_modules/@cucumber/html-formatter": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-19.0.0.tgz", - "integrity": "sha512-7PCnouI7BVmTU0eXFbJHQkxSQVIoAVa6PSmdcjG+jK8yn2X+YFYQinmLrcZkvEWlYgTHB/8GPle/8EGKQ0Ij9A==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-19.1.0.tgz", + "integrity": "sha512-VCsRa34SNg9plfziFwOaoCSfsphHUb1Ivk8px8eLJc0rBFLDPDgJcHJtcufAu6AxFamGiptt2dt0XoqVq2Gr/Q==", "peerDependencies": { - "@cucumber/messages": ">=17" + "@cucumber/messages": ">=18" } }, "node_modules/@cucumber/message-streams": { @@ -8109,9 +8109,9 @@ } }, "@cucumber/html-formatter": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-19.0.0.tgz", - "integrity": "sha512-7PCnouI7BVmTU0eXFbJHQkxSQVIoAVa6PSmdcjG+jK8yn2X+YFYQinmLrcZkvEWlYgTHB/8GPle/8EGKQ0Ij9A==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-19.1.0.tgz", + "integrity": "sha512-VCsRa34SNg9plfziFwOaoCSfsphHUb1Ivk8px8eLJc0rBFLDPDgJcHJtcufAu6AxFamGiptt2dt0XoqVq2Gr/Q==", "requires": {} }, "@cucumber/message-streams": { diff --git a/package.json b/package.json index 0fb41b2cb..911aa7dc2 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,7 @@ "@cucumber/gherkin": "23.0.1", "@cucumber/gherkin-streams": "5.0.1", "@cucumber/gherkin-utils": "^7.0.0", - "@cucumber/html-formatter": "19.0.0", + "@cucumber/html-formatter": "19.1.0", "@cucumber/messages": "18.0.0", "@cucumber/tag-expressions": "4.1.0", "assertion-error-formatter": "^3.0.0",