diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6acaf0685f..24df863e24 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,9 @@ Please visit [cucumber/CONTRIBUTING.md](https://github.com/cucumber/cucumber/blo
([PR#1612](https://github.com/cucumber/cucumber-ruby/pull/1612)
[gogainda](https://github.com/gogainda))
+- Add support for named hooks
+ ([PR#1636](https://github.com/cucumber/cucumber-ruby/pull/1636))
+
### Fixed
- Use `required_rubygems_version` instead of `rubygems_version`([PR#1629](https://github.com/cucumber/cucumber-ruby/pull/1629))
diff --git a/cucumber.gemspec b/cucumber.gemspec
index b2d9f68949..b76f5d917e 100644
--- a/cucumber.gemspec
+++ b/cucumber.gemspec
@@ -20,22 +20,22 @@ Gem::Specification.new do |s|
# Keep in sync with .circleci/config.yml & .rubocop.yml
s.required_ruby_version = '>= 2.6'
s.add_dependency 'builder', '~> 3.2', '>= 3.2.4'
- s.add_dependency 'cucumber-ci-environment', '~> 8.1', '>= 8.1.0'
- s.add_dependency 'cucumber-core', '~> 10.1', '>= 10.1.1'
- s.add_dependency 'cucumber-cucumber-expressions', '~> 15.0', '>= 15.0.1'
- s.add_dependency 'cucumber-gherkin', '~> 22.0', '>= 22.0.0'
- s.add_dependency 'cucumber-html-formatter', '~> 17.0', '>= 17.0.0'
- s.add_dependency 'cucumber-messages', '~> 17.1', '>= 17.1.1'
+ s.add_dependency 'cucumber-ci-environment', '~> 9.0', '>= 9.0.4'
+ s.add_dependency 'cucumber-core', '~> 11.0', '>= 11.0.0'
+ s.add_dependency 'cucumber-cucumber-expressions', '~> 15.1', '>= 15.1.1'
+ s.add_dependency 'cucumber-gherkin', '~> 23.0', '>= 23.0.1'
+ s.add_dependency 'cucumber-html-formatter', '~> 19.1', '>= 19.1.0'
+ s.add_dependency 'cucumber-messages', '~> 18.0', '>= 18.0.0'
s.add_dependency 'diff-lcs', '~> 1.5', '>= 1.5.0'
s.add_dependency 'mime-types', '~> 3.4', '>= 3.4.1'
- s.add_dependency 'multi_test', '~> 0.1', '>= 0.1.2'
+ s.add_dependency 'multi_test', '~> 1.1', '>= 1.1.0'
s.add_dependency 'sys-uname', '~> 1.2', '>= 1.2.2'
- s.add_development_dependency 'cucumber-compatibility-kit', '~> 9.1', '>= 9.1.2'
- s.add_development_dependency 'nokogiri', '~> 1.13', '>= 1.13.1'
+ s.add_development_dependency 'cucumber-compatibility-kit', '~> 9.2', '>= 9.2.1'
+ s.add_development_dependency 'nokogiri', '~> 1.13', '>= 1.13.6'
s.add_development_dependency 'pry', '~> 0.14', '>= 0.14.1'
s.add_development_dependency 'rake', '~> 13.0', '>= 13.0.6'
- s.add_development_dependency 'rspec', '~> 3.10', '>= 3.10.0'
+ s.add_development_dependency 'rspec', '~> 3.11', '>= 3.11.0'
s.add_development_dependency 'simplecov', '~> 0.21', '>= 0.21.2'
s.add_development_dependency 'syntax', '~> 1.2', '>= 1.2.2'
s.add_development_dependency 'test-unit', '~> 3.5', '>= 3.5.3'
@@ -45,9 +45,9 @@ Gem::Specification.new do |s|
s.add_development_dependency 'octokit', '~> 4.22', '>= 4.22.0'
# Needed for examples (rake examples)
- s.add_development_dependency 'capybara', '~> 3.36', '>= 3.36.0'
+ s.add_development_dependency 'capybara', '~> 3.36', '>= 3.36.0', '< 3.37'
s.add_development_dependency 'rack-test', '~> 1.1', '>= 1.1.0'
- s.add_development_dependency 'sinatra', '~> 2.1', '>= 2.1.0'
+ s.add_development_dependency 'sinatra', '~> 2.2', '>= 2.2.0'
s.required_rubygems_version = '>= 1.6.1'
s.files = Dir[
diff --git a/features/docs/writing_support_code/hooks/README.md b/features/docs/writing_support_code/hooks/README.md
index a26fd79619..ef7799718d 100644
--- a/features/docs/writing_support_code/hooks/README.md
+++ b/features/docs/writing_support_code/hooks/README.md
@@ -25,6 +25,10 @@ Multiple hooks of the same type are executed in the order that they were defined
If you wish to control this order, use manual requires in `env.rb` - This file is
loaded first - or migrate them all to one `hooks.rb` file.
+Finaly, all hooks can be given a name to improve reporting and help debugging.
+
+Note: Hooks names are only reported when using the message and the html formatters.
+
## InstallPlugin
[`InstallPlugin`](#installplugin) hook is dedicated to using plugins and is meant to
@@ -43,6 +47,12 @@ InstallPlugin do |configuration, registry|
# registry is an instance of Cucumber::Glue::RegistryWrapper defined in
# lib/cucumber/glue/registry_wrapper.rb
end
+
+# named hook:
+
+InstallPlugin(name: 'Installation of a plugin') do |configuration, registry|
+ # The name is optional
+end
```
You can see an example in the [Cucumber Wire plugin](https://github.com/cucumber/cucumber-ruby-wire).
@@ -63,6 +73,16 @@ end
AfterAll do
# snip
end
+
+# Named hooks:
+
+BeforeAll(name: 'Name of the hook') do
+ # snip
+end
+
+AfterAll(name: 'Name of the hook') do
+ # snip
+end
```
## Around
@@ -79,6 +99,12 @@ Around do |scenario, block|
block.call
end
end
+
+# with a name:
+
+Around(name: 'Name of the hook') do |scenario, block|
+ # snip
+end
```
## Before and After
@@ -96,6 +122,16 @@ After do |test_case|
log test_case.failed?
log test_case.status
end
+
+# With names:
+
+Before(name: 'Name of the hook') do |test_case|
+ # snip
+end
+
+After(name: 'Name of the hook') do |test_case|
+ # snip
+end
```
## AfterStep
@@ -108,4 +144,10 @@ AfterStep do |result, test_step|
log test_step.inspect # test_step is a Cucumber::Core::Test::Step
log result.inspect # result is a Cucumber::Core::Test::Result
end
+
+# with a name:
+
+AfterStep(name: 'Named hook') do |result, test_step|
+ # snip
+end
```
diff --git a/features/docs/writing_support_code/hooks/named_hooks.feature b/features/docs/writing_support_code/hooks/named_hooks.feature
new file mode 100644
index 0000000000..146892077d
--- /dev/null
+++ b/features/docs/writing_support_code/hooks/named_hooks.feature
@@ -0,0 +1,49 @@
+Feature: Named hooks
+
+ In order to spot errors easily in hooks
+ As a developer
+ I can give names to hooks
+
+ Scenario: Hooks can be named
+ Given a file named "features/support/env.rb" with:
+ """
+ Before(name: 'Named before hook') do
+ # no-op
+ end
+ """
+ And a file named "features/simple_scenario.feature" with:
+ """
+ Feature:
+ Scenario:
+ Given a step
+ """
+ When I run `cucumber features --publish-quiet --format message`
+ Then the stderr should not contain anything
+ And the output should contain NDJSON with key "name" and value "Named before hook"
+
+ Scenario: All kind of hooks can be named
+ Given a file named "features/support/env.rb" with:
+ """
+ Before(name: 'Named before hook') {}
+ After(name: 'Named after hook') {}
+ BeforeAll(name: 'Named before_all hook') {}
+ AfterAll(name: 'Named after_all hook') {}
+ AfterStep(name: 'Named after_step hook') {}
+ Around(name: 'Named around hook') {}
+ InstallPlugin(name: 'Named install_plugin hook') {}
+ """
+ And a file named "features/simple_scenario.feature" with:
+ """
+ Feature:
+ Scenario:
+ Given a step
+ """
+ When I run `cucumber features --publish-quiet --format message`
+ Then the stderr should not contain anything
+ And the output should contain NDJSON with key "name" and value "Named before hook"
+ And the output should contain NDJSON with key "name" and value "Named after hook"
+ And the output should contain NDJSON with key "name" and value "Named before_all hook"
+ And the output should contain NDJSON with key "name" and value "Named after_all hook"
+ And the output should contain NDJSON with key "name" and value "Named after_step hook"
+ And the output should contain NDJSON with key "name" and value "Named around hook"
+ And the output should contain NDJSON with key "name" and value "Named install_plugin hook"
diff --git a/lib/cucumber/glue/dsl.rb b/lib/cucumber/glue/dsl.rb
index af313304b2..728d010756 100644
--- a/lib/cucumber/glue/dsl.rb
+++ b/lib/cucumber/glue/dsl.rb
@@ -19,8 +19,8 @@ def build_rb_world_factory(world_modules, namespaced_world_modules, proc)
@rb_language.build_rb_world_factory(world_modules, namespaced_world_modules, proc)
end
- def register_rb_hook(phase, tag_names, proc)
- @rb_language.register_rb_hook(phase, tag_names, proc)
+ def register_rb_hook(phase, tag_names, proc, name: nil)
+ @rb_language.register_rb_hook(phase, tag_names, proc, name: name)
end
def define_parameter_type(parameter_type)
@@ -62,14 +62,14 @@ def World(*world_modules, **namespaced_world_modules, &proc)
# Registers a proc that will run before each Scenario. You can register as many
# as you want (typically from ruby scripts under support/hooks.rb).
- def Before(*tag_expressions, &proc)
- Dsl.register_rb_hook('before', tag_expressions, proc)
+ def Before(*tag_expressions, name: nil, &proc)
+ Dsl.register_rb_hook('before', tag_expressions, proc, name: name)
end
# Registers a proc that will run after each Scenario. You can register as many
# as you want (typically from ruby scripts under support/hooks.rb).
- def After(*tag_expressions, &proc)
- Dsl.register_rb_hook('after', tag_expressions, proc)
+ def After(*tag_expressions, name: nil, &proc)
+ Dsl.register_rb_hook('after', tag_expressions, proc, name: name)
end
# Registers a proc that will be wrapped around each scenario. The proc
@@ -77,14 +77,14 @@ def After(*tag_expressions, &proc)
# argument (but passed as a regular argument, since blocks cannot accept
# blocks in 1.8), on which it should call the .call method. You can register
# as many as you want (typically from ruby scripts under support/hooks.rb).
- def Around(*tag_expressions, &proc)
- Dsl.register_rb_hook('around', tag_expressions, proc)
+ def Around(*tag_expressions, name: nil, &proc)
+ Dsl.register_rb_hook('around', tag_expressions, proc, name: name)
end
# Registers a proc that will run after each Step. You can register as
# as you want (typically from ruby scripts under support/hooks.rb).
- def AfterStep(*tag_expressions, &proc)
- Dsl.register_rb_hook('after_step', tag_expressions, proc)
+ def AfterStep(*tag_expressions, name: nil, &proc)
+ Dsl.register_rb_hook('after_step', tag_expressions, proc, name: name)
end
def ParameterType(options)
@@ -108,20 +108,20 @@ def if_nil(value, default)
end
# Registers a proc that will run after Cucumber is configured in order to install an external plugin.
- def InstallPlugin(&proc)
- Dsl.register_rb_hook('install_plugin', [], proc)
+ def InstallPlugin(name: nil, &proc)
+ Dsl.register_rb_hook('install_plugin', [], proc, name: name)
end
# Registers a proc that will run before the execution of the scenarios.
# Use it for your final set-ups
- def BeforeAll(&proc)
- Dsl.register_rb_hook('before_all', [], proc)
+ def BeforeAll(name: nil, &proc)
+ Dsl.register_rb_hook('before_all', [], proc, name: name)
end
# Registers a proc that will run after the execution of the scenarios.
# Use it for your final clean-ups
- def AfterAll(&proc)
- Dsl.register_rb_hook('after_all', [], proc)
+ def AfterAll(name: nil, &proc)
+ Dsl.register_rb_hook('after_all', [], proc, name: name)
end
# Registers a new Ruby StepDefinition. This method is aliased
diff --git a/lib/cucumber/glue/hook.rb b/lib/cucumber/glue/hook.rb
index 6f8ed94750..9444d29d2d 100644
--- a/lib/cucumber/glue/hook.rb
+++ b/lib/cucumber/glue/hook.rb
@@ -6,11 +6,12 @@ module Cucumber
module Glue
# TODO: Kill pointless wrapper for Before, After and AfterStep hooks with fire
class Hook
- attr_reader :id, :tag_expressions, :location
+ attr_reader :id, :tag_expressions, :location, :name
- def initialize(id, registry, tag_expressions, proc)
+ def initialize(id, registry, tag_expressions, proc, name: nil)
@id = id
@registry = registry
+ @name = name
@tag_expressions = sanitize_tag_expressions(tag_expressions)
@proc = proc
@location = Cucumber::Core::Test::Location.from_source_location(*@proc.source_location)
@@ -32,6 +33,7 @@ def to_envelope
Cucumber::Messages::Envelope.new(
hook: Cucumber::Messages::Hook.new(
id: id,
+ name: name,
tag_expression: tag_expressions.empty? ? nil : tag_expressions.join(' '),
source_reference: Cucumber::Messages::SourceReference.new(
uri: location.file,
diff --git a/lib/cucumber/glue/registry_and_more.rb b/lib/cucumber/glue/registry_and_more.rb
index 1aec188e2a..73c2cf8fd1 100644
--- a/lib/cucumber/glue/registry_and_more.rb
+++ b/lib/cucumber/glue/registry_and_more.rb
@@ -72,8 +72,8 @@ def step_matches(name_to_match)
end
end
- def register_rb_hook(phase, tag_expressions, proc)
- hook = add_hook(phase, Hook.new(@configuration.id_generator.new_id, self, tag_expressions, proc))
+ def register_rb_hook(phase, tag_expressions, proc, name: nil)
+ hook = add_hook(phase, Hook.new(@configuration.id_generator.new_id, self, tag_expressions, proc, name: name))
@configuration.notify :envelope, hook.to_envelope
hook
end