Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for named hooks #1636

Merged
merged 6 commits into from
May 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
24 changes: 12 additions & 12 deletions cucumber.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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[
Expand Down
42 changes: 42 additions & 0 deletions features/docs/writing_support_code/hooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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).
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
```
49 changes: 49 additions & 0 deletions features/docs/writing_support_code/hooks/named_hooks.feature
Original file line number Diff line number Diff line change
@@ -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"
32 changes: 16 additions & 16 deletions lib/cucumber/glue/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -62,29 +62,29 @@ 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 <tt>support/hooks.rb</tt>).
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 <tt>support/hooks.rb</tt>).
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
# should accept two arguments: two arguments: the scenario and a "block"
# 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 <tt>support/hooks.rb</tt>).
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 <tt>support/hooks.rb</tt>).
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)
Expand All @@ -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
Expand Down
6 changes: 4 additions & 2 deletions lib/cucumber/glue/hook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions lib/cucumber/glue/registry_and_more.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down