Skip to content

Commit

Permalink
Make cucumber CCK compliant (#1738)
Browse files Browse the repository at this point in the history
* First interim working update

* Update of all runtime dependencies

* Remove pry from suite - use a local gemfile

* Update to use latest cck

* Remove another personalised gem

* Add fix into message builder that now generates a new exception type object in the messages

* Enable the structure for making the parameter-type message pass the v12 cck. However this is a WIP and will need further touching up

* Add in a new param to last call for #attach which is the filename - Leave it as a silly default for now to test it passing through

* Add in filename to #attach called from world

* Pass in filename to each method chain

* CCK should run locally too

* Permit filename to be passed into legacy JSONFormatter

* Add in filename to console formatter puts statement

* Add filename to pretty formatter

* When pretty formatter encounters something WITH a filename, show it accordingly, otherwise fall back to default behaviour

* Also revert console formatter to old behaviour when no filename is encountered

* Fix tests for user interface receiving nil arg

* Remove silly default for filename in final consumer before create message object

* WIP: Better spike / structure

* Bump messages to 17.0 to enable transformer fetching

* Fix envelope preparation for ParameterType message to be CCK conformant

* Re-order required rubygems and required ruby versions

* Add filename param doc for ProtoWorld

* CCK 13 is released

* Fix spacing of example feature

* Bump CCK to v14

* Mark pending spec from CCK as currently untested whilst it is investigated

* Remove redundant rake task code

* Ensure cck tests aren't ran mixed in to regular tests

* Fix failing tests for hook order that were using a previously defined version of

* Remove pending guard

* spec_helper fixes

* Update changelog

* Remove final trace of legacy cucumber 1.7 pending flags

* Update changelog with ref to removed guards

* Update RSpec skipped messages to make more sense

* update changelog
  • Loading branch information
luke-hill committed Nov 14, 2023
1 parent e491902 commit 4a603c0
Show file tree
Hide file tree
Showing 19 changed files with 89 additions and 73 deletions.
2 changes: 1 addition & 1 deletion .rspec
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
--require spec_helper
--color
--tag "~cck"
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,27 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
Please visit [cucumber/CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CONTRIBUTING.md) for more info on how to contribute to Cucumber.

## [Unreleased]
### Added
- `ParameterType` message now contains a new (sourceReference), property
(This contains a uri string and a `Location` message -> for where the ParameterType `transformer` is located) ([#1738](https://github.com/cucumber/cucumber-ruby/pull/1738) [luke-hill](https://github.com/luke-hill))

### Changed
- First couple of passes of tidying up approximately 40% of the manual fix cops
([#1739](https://github.com/cucumber/cucumber-ruby/pull/1739) [#1740](https://github.com/cucumber/cucumber-ruby/pull/1740) [#1741](https://github.com/cucumber/cucumber-ruby/pull/1741) [#1742](https://github.com/cucumber/cucumber-ruby/pull/1742) [luke-hill](https://github.com/luke-hill))
- Removed a bunch of example files / sample projects from ancient projects no longer viable
[#1740](https://github.com/cucumber/cucumber-ruby/pull/1740) [luke-hill](https://github.com/luke-hill))
([#1740](https://github.com/cucumber/cucumber-ruby/pull/1740) [luke-hill](https://github.com/luke-hill))
- When a `testStepResult` is of type `FAILED` we now pass in a new (Exception), message property
([#1738](https://github.com/cucumber/cucumber-ruby/pull/1738) [luke-hill](https://github.com/luke-hill))
- `#attach` now can take an optional filename parameter which will rename attachments like PDF's
([#1738](https://github.com/cucumber/cucumber-ruby/pull/1738) [luke-hill](https://github.com/luke-hill))

### Fixed
- Clear up a couple of tiny "nuances" that hide lots of issues when running local vs remote (Primarily CCK tests should always be runnable)
([#1738](https://github.com/cucumber/cucumber-ruby/pull/1738) [luke-hill](https://github.com/luke-hill))

### Removed
- Removed a variety of overrides / hacks for travis CI (No longer in use) ([#1738](https://github.com/cucumber/cucumber-ruby/pull/1738) [luke-hill](https://github.com/luke-hill))
- Removed some legacy rspec pending flags present since cucumber 1.x ([#1738](https://github.com/cucumber/cucumber-ruby/pull/1738) [luke-hill](https://github.com/luke-hill))

## [9.0.2] - 2023-09-11
### Changed
Expand Down
3 changes: 0 additions & 3 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,3 @@ default_tasks = %i[spec cucumber cck]
default_tasks << :examples if ENV['CI']

task default: default_tasks

require 'rake/clean'
CLEAN.include %w[**/*.{log,pyc,rbc,tgz} doc]
11 changes: 5 additions & 6 deletions cucumber.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ Gem::Specification.new do |s|
}

s.required_ruby_version = '>= 2.7'
s.required_rubygems_version = '>= 3.0.1'

s.add_dependency 'builder', '~> 3.2', '>= 3.2.4'
s.add_dependency 'cucumber-ci-environment', '~> 9.2', '>= 9.2.0'
s.add_dependency 'cucumber-core', '~> 11.1', '>= 11.1.0'
s.add_dependency 'cucumber-cucumber-expressions', '~> 16.1', '>= 16.1.2'
s.add_dependency 'cucumber-gherkin', '>= 24', '< 26.2.1'
s.add_dependency 'cucumber-core', '~> 12.0'
s.add_dependency 'cucumber-cucumber-expressions', '~> 17.0'
s.add_dependency 'cucumber-gherkin', '>= 24', '< 27'
s.add_dependency 'cucumber-html-formatter', '~> 20.4', '>= 20.4.0'
s.add_dependency 'cucumber-messages', '>= 19', '< 23'
s.add_dependency 'diff-lcs', '~> 1.5', '>= 1.5.0'
s.add_dependency 'mini_mime', '~> 1.1', '>= 1.1.5'
s.add_dependency 'multi_test', '~> 1.1', '>= 1.1.0'
s.add_dependency 'sys-uname', '~> 1.2', '>= 1.2.3'

s.add_development_dependency 'cucumber-compatibility-kit', '~> 10.0'
s.add_development_dependency 'cucumber-compatibility-kit', '~> 14.0'
# Only needed whilst we are testing the formatters. Can be removed once we remove tests for those
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.12', '>= 3.12.0'
s.add_development_dependency 'rubocop', '~> 1.56.4'
Expand All @@ -54,7 +54,6 @@ Gem::Specification.new do |s|
s.add_development_dependency 'rack-test', '~> 2.1', '>= 2.1.0'
s.add_development_dependency 'sinatra', '~> 3.1', '>= 3.1.0'

s.required_rubygems_version = '>= 3.0.1'
s.files = Dir[
'README.md',
'LICENSE',
Expand Down
21 changes: 11 additions & 10 deletions features/docs/writing_support_code/hooks/hook_order.feature
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
Feature: Hooks execute in defined order

Feature: Hooks execute in defined order
Background:
Given a file named "features/step_definitions/steps.rb" with:
"""
Given /^background step$/ do; log(:background_step) end
Given /^scenario step$/ do; log(:scenario_step) end
Given('background step') { log(:background_step) }
Given('scenario step') { log(:scenario_step) }
"""
And a file named "features/support/hooks.rb" with:
"""
$EventOrder = []
Around('@around') do |scenario,block|
log :around_begin
log(:around_begin)
block.call
log :around_end
log(:around_end)
end
Before('@before') do
log :before
log(:before)
end
After('@after') do |scenario|
log :after
log(:after)
end
"""
And a file named "features/around_hook_covers_background.feature" with:
Expand All @@ -46,17 +45,18 @@
And log only formatter is declared

Scenario: Around hooks cover background steps
When I run `cucumber features/around_hook_covers_background.feature --format LogOnlyFormatter`
When I run `cucumber features/around_hook_covers_background.feature --format LogOnlyFormatter --publish-quiet`
Then the output should contain:
"""
around_begin
background_step
scenario_step
around_end
"""
And the exit status should be 0

Scenario: All hooks execute in expected order
When I run `cucumber features/all_hook_order.feature --format LogOnlyFormatter`
When I run `cucumber features/all_hook_order.feature --format LogOnlyFormatter --publish-quiet`
Then the output should contain:
"""
around_begin
Expand All @@ -66,3 +66,4 @@
after
around_end
"""
And the exit status should be 0
4 changes: 2 additions & 2 deletions features/lib/step_definitions/cucumber_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
' @io = config.out_stream',
' end',
'',
' def attach(src, media_type)',
' @io.puts src',
' def attach(src, media_type, _filename)',
' @io.puts(src)',
' end',
'end'
].join("\n"))
Expand Down
2 changes: 1 addition & 1 deletion gem_tasks/examples.rake
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
desc 'Run all examples'
task :examples do
Dir['examples/*'].each do |example_dir|
next if !File.directory?(example_dir) || %w[examples/tcl].index(example_dir)
next unless File.directory?(example_dir)

puts "Running #{example_dir}"
Dir.chdir(example_dir) do
Expand Down
1 change: 1 addition & 0 deletions gem_tasks/rspec.rake
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ require 'rspec/core/rake_task'
desc 'Run RSpec'
RSpec::Core::RakeTask.new do |t|
t.verbose = true
t.rspec_opts = '--tag ~cck'
end
9 changes: 7 additions & 2 deletions lib/cucumber/formatter/console.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,17 @@ def do_print_passing_wip(passed_messages)
end
end

def attach(src, media_type)
def attach(src, media_type, filename)
return unless media_type == 'text/x.cucumber.log+plain'
return unless @io

@io.puts
@io.puts(format_string(src, :tag))
if filename
@io.puts("#{filename}: #{format_string(src, :tag)}")
else
@io.puts(format_string(src, :tag))
end

@io.flush
end

Expand Down
2 changes: 1 addition & 1 deletion lib/cucumber/formatter/json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def on_test_run_finished(_event)
@io.write(JSON.pretty_generate(@feature_hashes))
end

def attach(src, mime_type)
def attach(src, mime_type, _filename)
if mime_type == 'text/x.cucumber.log+plain'
test_step_output << src
return
Expand Down
19 changes: 14 additions & 5 deletions lib/cucumber/formatter/message_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ def output_message
raise 'To be implemented'
end

def attach(src, media_type)
def attach(src, media_type, filename)
attachment_data = {
test_step_id: @current_test_step_id,
test_case_started_id: @current_test_case_started_id,
media_type: media_type
media_type: media_type,
file_name: filename
}

if media_type.start_with?('text/')
Expand Down Expand Up @@ -192,10 +193,13 @@ def on_test_step_finished(event)

result_message = result.to_message
if result.failed? || result.pending?
message_element = result.failed? ? result.exception : result

result_message = Cucumber::Messages::TestStepResult.new(
status: result_message.status,
duration: result_message.duration,
message: create_error_message(result)
message: create_error_message(message_element),
exception: create_exception_object(result, message_element)
)
end

Expand All @@ -211,12 +215,17 @@ def on_test_step_finished(event)
output_envelope(message)
end

def create_error_message(result)
message_element = result.failed? ? result.exception : result
def create_error_message(message_element)
message = "#{message_element.message} (#{message_element.class})"
([message] + message_element.backtrace).join("\n")
end

def create_exception_object(result, message_element)
return unless result.failed?

Cucumber::Messages::Exception.from_h({ type: message_element.class, message: message_element.message })
end

def on_test_case_finished(event)
message = Cucumber::Messages::Envelope.new(
test_case_finished: Cucumber::Messages::TestCaseFinished.new(
Expand Down
8 changes: 6 additions & 2 deletions lib/cucumber/formatter/pretty.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,14 @@ def on_test_run_finished(_event)
print_summary
end

def attach(src, media_type)
def attach(src, media_type, filename)
return unless media_type == 'text/x.cucumber.log+plain'

@test_step_output.push src
if filename
@test_step_output.push("#{filename}: #{src}")
else
@test_step_output.push(src)
end
end

private
Expand Down
15 changes: 9 additions & 6 deletions lib/cucumber/glue/proto_world.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

module Cucumber
module Glue
# Defines the basic API methods availlable in all Cucumber step definitions.
# Defines the basic API methods available in all Cucumber step definitions.
#
# You can, and probably should, extend this API with your own methods that
# make sense in your domain. For more on that, see {Cucumber::Glue::Dsl#World}
Expand Down Expand Up @@ -86,14 +86,17 @@ def log(*messages)
# @param file [string|io] the file to attach.
# It can be a string containing the file content itself, the file path, or an IO ready to be read.
# @param media_type [string] the media type.
# If file is a valid path, media_type can be omitted, it will then be inferred from the file name.
def attach(file, media_type = nil)
# If file is a valid path, media_type can be omitted, it will then be inferred from the file name.
# @param filename [string] the name of the file you wish to specify.
# This is only needed in situations where you want to rename a PDF download e.t.c. - In most situations
# you should not need to pass a filename
def attach(file, media_type = nil, filename = nil)
return super unless File.file?(file)

content = File.read(file, mode: 'rb')
media_type = MiniMime.lookup_by_filename(file)&.content_type if media_type.nil?

super(content, media_type.to_s)
super(content, media_type.to_s, filename)
rescue StandardError
super
end
Expand Down Expand Up @@ -152,8 +155,8 @@ def add_modules!(world_modules, namespaced_world_modules)
runtime.ask(question, timeout_seconds)
end

define_method(:attach) do |file, media_type|
runtime.attach(file, media_type)
define_method(:attach) do |file, media_type, filename|
runtime.attach(file, media_type, filename)
end

# Prints the list of modules that are included in the World
Expand Down
11 changes: 7 additions & 4 deletions lib/cucumber/glue/registry_and_more.rb
Original file line number Diff line number Diff line change
Expand Up @@ -198,16 +198,19 @@ def self.cli_snippet_type_options
private

def parameter_type_envelope(parameter_type)
# TODO: should me moved to Cucumber::Expression::ParameterType#to_envelope ?
# TODO: should this be moved to Cucumber::Expression::ParameterType#to_envelope ??
# Note: that would mean that cucumber-expression would depend on cucumber-messages

Cucumber::Messages::Envelope.new(
parameter_type: Cucumber::Messages::ParameterType.new(
id: @configuration.id_generator.new_id,
name: parameter_type.name,
regular_expressions: parameter_type.regexps.map(&:to_s),
prefer_for_regular_expression_match: parameter_type.prefer_for_regexp_match?,
use_for_snippets: parameter_type.use_for_snippets?
prefer_for_regular_expression_match: parameter_type.prefer_for_regexp_match,
use_for_snippets: parameter_type.use_for_snippets,
source_reference: Cucumber::Messages::SourceReference.new(
uri: parameter_type.transformer.source_location[0],
location: Cucumber::Messages::Location.new(line: parameter_type.transformer.source_location[1])
)
)
)
end
Expand Down
4 changes: 2 additions & 2 deletions lib/cucumber/runtime/user_interface.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def ask(question, timeout_seconds)
# be a path to a file, or if it's an image it may also be a Base64 encoded image.
# The embedded data may or may not be ignored, depending on what kind of formatter(s) are active.
#
def attach(src, media_type)
@visitor.attach(src, media_type)
def attach(src, media_type, filename)
@visitor.attach(src, media_type, filename)
end

private
Expand Down
2 changes: 0 additions & 2 deletions spec/cucumber/cli/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ def reset_config
end

context 'when using the --profile flag' do
include RSpec::WorkInProgress

it 'expands args from profiles in the cucumber.yml file' do
given_cucumber_yml_defined_as('bongo' => '--require from/yml')
config.parse!(%w[--format progress --profile bongo])
Expand Down
4 changes: 2 additions & 2 deletions spec/cucumber/cli/profile_loader_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def given_cucumber_yml_defined_as(hash_or_string)
end

context 'when on a Windows OS' do
before { skip('Only run these tests on non-Windows') unless Cucumber::WINDOWS }
before { skip('These tests are only to be ran on Windows') unless Cucumber::WINDOWS }

it 'treats backslashes as literals in rerun.txt when on Windows (JRuby or MRI)' do
given_cucumber_yml_defined_as('default' => '--format "pretty" features\sync_imap_mailbox.feature:16:22')
Expand Down Expand Up @@ -54,7 +54,7 @@ def given_cucumber_yml_defined_as(hash_or_string)
end

context 'when on non-Windows OS' do
before { skip('Only run these tests on non-Windows') if Cucumber::WINDOWS }
before { skip('These tests are only to be ran on a "Non-Windows" OS') if Cucumber::WINDOWS }

it 'treats backslashes as literals in rerun.txt when on Windows (JRuby or MRI)' do
given_cucumber_yml_defined_as('default' => '--format "pretty" features\sync_imap_mailbox.feature:16:22')
Expand Down
4 changes: 2 additions & 2 deletions spec/cucumber/glue/step_definition_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,15 @@ def step_match(text)

context 'allows log' do
it 'calls "attach" with the correct media type' do
expect(user_interface).to receive(:attach).with('wasup', 'text/x.cucumber.log+plain')
expect(user_interface).to receive(:attach).with('wasup', 'text/x.cucumber.log+plain', nil)
dsl.Given(/Loud/) do
log 'wasup'
end
run_step 'Loud'
end

it 'calls `to_s` if the message is not a String' do
expect(user_interface).to receive(:attach).with('["Not", 1, "string"]', 'text/x.cucumber.log+plain')
expect(user_interface).to receive(:attach).with('["Not", 1, "string"]', 'text/x.cucumber.log+plain', nil)
dsl.Given(/Loud/) do
log ['Not', 1, 'string']
end
Expand Down
Loading

0 comments on commit 4a603c0

Please sign in to comment.