cucumber 9.0.2 → 9.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -8
  3. data/VERSION +1 -1
  4. data/lib/cucumber/cli/main.rb +1 -1
  5. data/lib/cucumber/cli/options.rb +37 -37
  6. data/lib/cucumber/cli/profile_loader.rb +5 -5
  7. data/lib/cucumber/configuration.rb +1 -1
  8. data/lib/cucumber/deprecate.rb +6 -47
  9. data/lib/cucumber/formatter/ansicolor.rb +18 -26
  10. data/lib/cucumber/formatter/ast_lookup.rb +14 -6
  11. data/lib/cucumber/formatter/console.rb +14 -9
  12. data/lib/cucumber/formatter/json.rb +2 -6
  13. data/lib/cucumber/formatter/junit.rb +1 -1
  14. data/lib/cucumber/formatter/message_builder.rb +19 -6
  15. data/lib/cucumber/formatter/pretty.rb +7 -3
  16. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +19 -20
  17. data/lib/cucumber/glue/invoke_in_world.rb +1 -1
  18. data/lib/cucumber/glue/proto_world.rb +17 -22
  19. data/lib/cucumber/glue/registry_and_more.rb +7 -4
  20. data/lib/cucumber/multiline_argument/data_table.rb +32 -33
  21. data/lib/cucumber/rake/task.rb +1 -5
  22. data/lib/cucumber/running_test_case.rb +1 -1
  23. data/lib/cucumber/runtime/for_programming_languages.rb +1 -2
  24. data/lib/cucumber/runtime/meta_message_builder.rb +2 -2
  25. data/lib/cucumber/runtime/user_interface.rb +2 -2
  26. data/lib/cucumber/runtime.rb +1 -1
  27. metadata +35 -241
  28. data/lib/autotest/cucumber.rb +0 -8
  29. data/lib/autotest/cucumber_mixin.rb +0 -133
  30. data/lib/autotest/cucumber_rails.rb +0 -8
  31. data/lib/autotest/cucumber_rails_rspec.rb +0 -8
  32. data/lib/autotest/cucumber_rails_rspec2.rb +0 -8
  33. data/lib/autotest/cucumber_rspec.rb +0 -8
  34. data/lib/autotest/cucumber_rspec2.rb +0 -8
  35. data/lib/autotest/discover.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 313150f1f463d99e105864744641cd87c56e55f5d4d7856a437214a8db79dbdd
4
- data.tar.gz: 90cdd041c890ab05108d3951be679f424ae096bc2df09044b26320096074eee3
3
+ metadata.gz: d937b723774ea37f4739f86d3888163faec44b5e815b344c3cb7c8cd2dae8b42
4
+ data.tar.gz: e7cc47f9e50d7d355baacc10594db8e93b7a3dc961a8b43417c21e11a664929d
5
5
  SHA512:
6
- metadata.gz: 76252cc34ed4bc38ba6e7c0cec30d31b5319d14cdc9745f3e589bad8abf4713c95ec27527095b969dfd983f59ecea57f27ef9d119b68f8c032a19143d332d549
7
- data.tar.gz: bef2260cd88f77daa139256c03e68b1744849667d71a3a71931d6c57a4c546e437e996ef6e7aed157520d5ff7dc1b86d2c1a1041b9dd9747f20fe53fd61dec2a
6
+ metadata.gz: 104816fd17fed2d4302d43ecfe1d72465a0b863cf79f449ecc44c25bcc6fe8c00824f0f07014fa0b3986f1476f3ab490c5bb36f9f6b9676715e342682d4c3c40
7
+ data.tar.gz: 06cdb6fd33a1347415e85590658a0972243798bf653eb3b2a23bc65b5d2d25fccf39b48e85024d2b58ad3868d86f05630a77c719dd6b244def9741eb04736ced
data/README.md CHANGED
@@ -5,9 +5,7 @@
5
5
  [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://vshymanskyy.github.io/StandWithUkraine)
6
6
  [![OpenCollective](https://opencollective.com/cucumber/backers/badge.svg)](https://opencollective.com/cucumber)
7
7
  [![OpenCollective](https://opencollective.com/cucumber/sponsors/badge.svg)](https://opencollective.com/cucumber)
8
- [![pull requests](https://oselvar.com/api/badge?label=pull%20requests&csvUrl=https%3A%2F%2Fraw.githubusercontent.com%2Fcucumber%2Foselvar-github-metrics%2Fmain%2Fdata%2Fcucumber%2Fcucumber-ruby%2FpullRequests.csv)](https://oselvar.com/github/cucumber/oselvar-github-metrics/main/cucumber/cucumber-ruby)
9
- [![issues](https://oselvar.com/api/badge?label=issues&csvUrl=https%3A%2F%2Fraw.githubusercontent.com%2Fcucumber%2Foselvar-github-metrics%2Fmain%2Fdata%2Fcucumber%2Fcucumber-ruby%2Fissues.csv)](https://oselvar.com/github/cucumber/oselvar-github-metrics/main/cucumber/cucumber-ruby)
10
- [![Test cucumber](https://github.com/cucumber/cucumber-ruby/actions/workflows/cucumber-ruby.yml/badge.svg)](https://github.com/cucumber/cucumber-ruby/actions/workflows/cucumber-ruby.yml)
8
+ [![Test cucumber](https://github.com/cucumber/cucumber-ruby/actions/workflows/test.yaml/badge.svg)](https://github.com/cucumber/cucumber-ruby/actions/workflows/test.yaml)
11
9
  [![Code Climate](https://codeclimate.com/github/cucumber/cucumber-ruby.svg)](https://codeclimate.com/github/cucumber/cucumber-ruby)
12
10
  [![Coverage Status](https://coveralls.io/repos/cucumber/cucumber-ruby/badge.svg?branch=main)](https://coveralls.io/r/cucumber/cucumber-ruby?branch=main)
13
11
 
@@ -56,8 +54,7 @@ Later in this document, bundler is considered being used so all commands are usi
56
54
 
57
55
  ### Ruby on Rails
58
56
 
59
- Using Ruby on Rails? You can use [cucumber-rails](https://github.com/cucumber/cucumber-rails)
60
- to bring Cucumber into your Rails project.
57
+ Using Ruby on Rails? You can use [cucumber-rails](https://github.com/cucumber/cucumber-rails) to bring Cucumber into your Rails project.
61
58
 
62
59
  ## Usage
63
60
 
@@ -133,7 +130,7 @@ To execute a single example, indicates the line of the name of the example:
133
130
 
134
131
  $ bundle exec cucumber features/rule.feature:5
135
132
 
136
- To summarize the results on the standard output, and writte a HTML report on disk:
133
+ To summarize the results on the standard output, and generate a HTML report on disk:
137
134
 
138
135
  $ bundle exec cucumber --format summary --format html --out report.html
139
136
 
@@ -141,8 +138,7 @@ For more command line options
141
138
 
142
139
  $ bundle exec cucumber --help
143
140
 
144
- You can also find documentation on the command line possibilities in
145
- [features/docs/cli](features/docs/cli).
141
+ You can also find documentation on the command line possibilities in [features/docs/cli](features/docs/cli).
146
142
 
147
143
  ## Documentation and support
148
144
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 9.0.2
1
+ 9.1.2
@@ -49,7 +49,7 @@ module Cucumber
49
49
  rescue Errno::EACCES, Errno::ENOENT => e
50
50
  @err.puts("#{e.message} (#{e.class})")
51
51
  exit_unable_to_finish
52
- rescue Exception => e # rubocop:disable Lint/RescueException
52
+ rescue Exception => e
53
53
  @err.puts("#{e.message} (#{e.class})")
54
54
  @err.puts(e.backtrace.join("\n"))
55
55
  exit_unable_to_finish
@@ -12,26 +12,26 @@ module Cucumber
12
12
  CUCUMBER_PUBLISH_URL = ENV['CUCUMBER_PUBLISH_URL'] || 'https://messages.cucumber.io/api/reports -X GET'
13
13
  INDENT = ' ' * 53
14
14
  BUILTIN_FORMATS = {
15
- 'pretty' => ['Cucumber::Formatter::Pretty', 'Prints the feature as is - in colours.'],
16
- 'progress' => ['Cucumber::Formatter::Progress', 'Prints one character per scenario.'],
17
- 'rerun' => ['Cucumber::Formatter::Rerun', 'Prints failing files with line numbers.'],
18
- 'usage' => ['Cucumber::Formatter::Usage', "Prints where step definitions are used.\n" \
19
- "#{INDENT}The slowest step definitions (with duration) are\n" \
20
- "#{INDENT}listed first. If --dry-run is used the duration\n" \
21
- "#{INDENT}is not shown, and step definitions are sorted by\n" \
22
- "#{INDENT}filename instead."],
23
- 'stepdefs' => ['Cucumber::Formatter::Stepdefs', "Prints All step definitions with their locations. Same as\n" \
24
- "#{INDENT}the usage formatter, except that steps are not printed."],
25
- 'junit' => ['Cucumber::Formatter::Junit', "Generates a report similar to Ant+JUnit. Use\n" \
26
- "#{INDENT}junit,fileattribute=true to include a file attribute."],
27
- 'json' => ['Cucumber::Formatter::Json', "Prints the feature as JSON.\n" \
28
- "#{INDENT}The JSON format is in maintenance mode.\n" \
29
- "#{INDENT}Please consider using the message formatter\n"\
30
- "#{INDENT}with the standalone json-formatter\n" \
31
- "#{INDENT}(https://github.com/cucumber/cucumber/tree/master/json-formatter)."],
32
- 'message' => ['Cucumber::Formatter::Message', 'Prints each message in NDJSON form, which can then be consumed by other tools.'],
33
- 'html' => ['Cucumber::Formatter::HTML', 'Outputs HTML report'],
34
- 'summary' => ['Cucumber::Formatter::Summary', 'Summary output of feature and scenarios']
15
+ 'pretty' => ['Cucumber::Formatter::Pretty', 'Prints the feature as is - in colours.'],
16
+ 'progress' => ['Cucumber::Formatter::Progress', 'Prints one character per scenario.'],
17
+ 'rerun' => ['Cucumber::Formatter::Rerun', 'Prints failing files with line numbers.'],
18
+ 'usage' => ['Cucumber::Formatter::Usage', "Prints where step definitions are used.\n" \
19
+ "#{INDENT}The slowest step definitions (with duration) are\n" \
20
+ "#{INDENT}listed first. If --dry-run is used the duration\n" \
21
+ "#{INDENT}is not shown, and step definitions are sorted by\n" \
22
+ "#{INDENT}filename instead."],
23
+ 'stepdefs' => ['Cucumber::Formatter::Stepdefs', "Prints All step definitions with their locations. Same as\n" \
24
+ "#{INDENT}the usage formatter, except that steps are not printed."],
25
+ 'junit' => ['Cucumber::Formatter::Junit', "Generates a report similar to Ant+JUnit. Use\n" \
26
+ "#{INDENT}junit,fileattribute=true to include a file attribute."],
27
+ 'json' => ['Cucumber::Formatter::Json', "Prints the feature as JSON.\n" \
28
+ "#{INDENT}The JSON format is in maintenance mode.\n" \
29
+ "#{INDENT}Please consider using the message formatter\n"\
30
+ "#{INDENT}with the standalone json-formatter\n" \
31
+ "#{INDENT}(https://github.com/cucumber/cucumber/tree/master/json-formatter)."],
32
+ 'message' => ['Cucumber::Formatter::Message', 'Prints each message in NDJSON form, which can then be consumed by other tools.'],
33
+ 'html' => ['Cucumber::Formatter::HTML', 'Outputs HTML report'],
34
+ 'summary' => ['Cucumber::Formatter::Summary', 'Summary output of feature and scenarios']
35
35
  }.freeze
36
36
  max = BUILTIN_FORMATS.keys.map(&:length).max
37
37
  FORMAT_HELP_MSG = [
@@ -92,13 +92,13 @@ module Cucumber
92
92
  @options[key] = value
93
93
  end
94
94
 
95
- def parse!(args) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
95
+ def parse!(args)
96
96
  @args = args
97
97
  @expanded_args = @args.dup
98
98
 
99
99
  @args.extend(::OptionParser::Arguable)
100
100
 
101
- @args.options do |opts| # rubocop:disable Metrics/BlockLength
101
+ @args.options do |opts|
102
102
  opts.banner = banner
103
103
  opts.on('--publish', 'Publish a report to https://reports.cucumber.io') do
104
104
  set_option :publish_enabled, true
@@ -143,11 +143,11 @@ module Cucumber
143
143
  opts.on('-x', '--expand', 'Expand Scenario Outline Tables in output.') { set_option :expand }
144
144
 
145
145
  opts.on('--order TYPE[:SEED]', 'Run examples in the specified order. Available types:',
146
- *<<-TEXT.split("\n")) do |order|
147
- [defined] Run scenarios in the order they were defined (default).
148
- [random] Shuffle scenarios before running.
149
- Specify SEED to reproduce the shuffling from a previous run.
150
- e.g. --order random:5738
146
+ *<<~TEXT.split("\n")) do |order|
147
+ [defined] Run scenarios in the order they were defined (default).
148
+ [random] Shuffle scenarios before running.
149
+ Specify SEED to reproduce the shuffling from a previous run.
150
+ e.g. --order random:5738
151
151
  TEXT
152
152
  @options[:order], @options[:seed] = *order.split(':')
153
153
  raise "'#{@options[:order]}' is not a recognised order type. Please use one of #{ORDER_TYPES.join(', ')}." unless ORDER_TYPES.include?(@options[:order])
@@ -295,9 +295,9 @@ Specify SEED to reproduce the shuffling from a previous run.
295
295
  ]
296
296
  end
297
297
 
298
- def parse_formats(v)
299
- formatter, *formatter_options = v.split(',')
300
- options_hash = Hash[formatter_options.map { |s| s.split('=') }]
298
+ def parse_formats(value)
299
+ formatter, *formatter_options = value.split(',')
300
+ options_hash = Hash[formatter_options.map { |string| string.split('=') }]
301
301
  [formatter, options_hash]
302
302
  end
303
303
 
@@ -375,12 +375,12 @@ Specify SEED to reproduce the shuffling from a previous run.
375
375
  ].join("\n")
376
376
  end
377
377
 
378
- def require_files(v)
379
- @options[:require] << v
380
- return unless Cucumber::JRUBY && File.directory?(v)
378
+ def require_files(filenames)
379
+ @options[:require] << filenames
380
+ return unless Cucumber::JRUBY && File.directory?(filenames)
381
381
 
382
382
  require 'java'
383
- $CLASSPATH << v
383
+ $CLASSPATH << filenames
384
384
  end
385
385
 
386
386
  def require_jars(jars)
@@ -441,8 +441,8 @@ Specify SEED to reproduce the shuffling from a previous run.
441
441
  ProjectInitializer.new.run && Kernel.exit(0)
442
442
  end
443
443
 
444
- def add_profile(p)
445
- @profiles << p
444
+ def add_profile(profile)
445
+ @profiles << profile
446
446
  end
447
447
 
448
448
  def set_option(option, value = nil)
@@ -522,7 +522,7 @@ Specify SEED to reproduce the shuffling from a previous run.
522
522
  @profile_loader ||= ProfileLoader.new
523
523
  end
524
524
 
525
- def reverse_merge(other_options) # rubocop:disable Metrics/AbcSize
525
+ def reverse_merge(other_options)
526
526
  @options = other_options.options.merge(@options)
527
527
  @options[:require] += other_options[:require]
528
528
  @options[:excludes] += other_options[:excludes]
@@ -11,11 +11,11 @@ module Cucumber
11
11
 
12
12
  def args_from(profile)
13
13
  unless cucumber_yml.key?(profile)
14
- raise(ProfileNotFound, <<-END_OF_ERROR)
15
- Could not find profile: '#{profile}'
14
+ raise(ProfileNotFound, <<~END_OF_ERROR)
15
+ Could not find profile: '#{profile}'
16
16
 
17
- Defined profiles in cucumber.yml:
18
- * #{cucumber_yml.keys.sort.join("\n * ")}
17
+ Defined profiles in cucumber.yml:
18
+ * #{cucumber_yml.keys.sort.join("\n * ")}
19
19
  END_OF_ERROR
20
20
  end
21
21
 
@@ -84,7 +84,7 @@ Defined profiles in cucumber.yml:
84
84
  def load_configuration
85
85
  require 'yaml'
86
86
  begin
87
- @cucumber_yml = YAML.load(@cucumber_erb) # rubocop:disable Security/YAMLLoad
87
+ @cucumber_yml = YAML.load(@cucumber_erb)
88
88
  rescue StandardError
89
89
  raise(YmlLoadError, "cucumber.yml was found, but could not be parsed. Please refer to cucumber's documentation on correct profile usage.\n")
90
90
  end
@@ -213,7 +213,7 @@ module Cucumber
213
213
  yield factory,
214
214
  formatter_options,
215
215
  path_or_io
216
- rescue Exception => e # rubocop:disable Lint/RescueException
216
+ rescue Exception => e
217
217
  raise e, "#{e.message}\nError creating formatter: #{format}", e.backtrace
218
218
  end
219
219
  end
@@ -4,52 +4,11 @@ require 'cucumber/platform'
4
4
  require 'cucumber/gherkin/formatter/ansi_escapes'
5
5
 
6
6
  module Cucumber
7
- module Deprecate
8
- class AnsiString
9
- include Cucumber::Gherkin::Formatter::AnsiEscapes
10
-
11
- def self.failure_message(message)
12
- AnsiString.new.failure_message(message)
13
- end
14
-
15
- def failure_message(message)
16
- failed + message + reset
17
- end
18
- end
19
-
20
- class CliOption
21
- def self.deprecate(stream, option, message, remove_after_version)
22
- return if stream.nil?
23
-
24
- stream.puts(
25
- AnsiString.failure_message(
26
- "\nWARNING: #{option} is deprecated" \
27
- " and will be removed after version #{remove_after_version}.\n#{message}.\n"
28
- )
29
- )
30
- end
31
- end
32
-
33
- module ForUsers
34
- def self.call(message, method, remove_after_version)
35
- $stderr.puts AnsiString.failure_message(
36
- "\nWARNING: ##{method} is deprecated" \
37
- " and will be removed after version #{remove_after_version}. #{message}.\n" \
38
- "(Called from #{caller(3..3).first})"
39
- )
40
- end
41
- end
42
-
43
- module ForDevelopers
44
- def self.call(_message, _method, remove_after_version)
45
- raise "This method is due for removal after version #{remove_after_version}" if remove_after_version <= Cucumber::VERSION
46
- end
47
- end
48
-
49
- STRATEGY = $PROGRAM_NAME =~ /rspec$/ ? ForDevelopers : ForUsers
50
- end
51
-
52
- def self.deprecate(*args)
53
- Deprecate::STRATEGY.call(*args)
7
+ def self.deprecate(message, method, remove_after_version)
8
+ $stderr.puts(
9
+ "\nWARNING: ##{method} is deprecated" \
10
+ " and will be removed after version #{remove_after_version}. #{message}.\n" \
11
+ "(Called from #{caller(3..3).first})"
12
+ )
54
13
  end
55
14
  end
@@ -3,7 +3,7 @@
3
3
  require 'cucumber/platform'
4
4
  require 'cucumber/term/ansicolor'
5
5
 
6
- Cucumber::Term::ANSIColor.coloring = false if !$stdout.tty? && !ENV.key?('AUTOTEST')
6
+ Cucumber::Term::ANSIColor.coloring = false unless $stdout.tty?
7
7
 
8
8
  module Cucumber
9
9
  module Formatter
@@ -63,29 +63,23 @@ module Cucumber
63
63
  module ANSIColor
64
64
  include Cucumber::Term::ANSIColor
65
65
 
66
- # :stopdoc:
67
66
  ALIASES = Hash.new do |h, k|
68
67
  next unless k.to_s =~ /(.*)_param/
69
68
 
70
69
  "#{h[Regexp.last_match(1)]},bold"
71
70
  end.merge(
72
71
  'undefined' => 'yellow',
73
- 'pending' => 'yellow',
74
- 'flaky' => 'yellow',
75
- 'failed' => 'red',
76
- 'passed' => 'green',
77
- 'outline' => 'cyan',
78
- 'skipped' => 'cyan',
79
- 'comment' => 'grey',
80
- 'tag' => 'cyan'
72
+ 'pending' => 'yellow',
73
+ 'flaky' => 'yellow',
74
+ 'failed' => 'red',
75
+ 'passed' => 'green',
76
+ 'outline' => 'cyan',
77
+ 'skipped' => 'cyan',
78
+ 'comment' => 'grey',
79
+ 'tag' => 'cyan'
81
80
  )
82
- # :startdoc:
83
81
 
84
- # Apply the custom color scheme
85
- #
86
- # example:
87
- #
88
- # apply_custom_colors('passed=white')
82
+ # Apply the custom color scheme -> i.e. apply_custom_colors('passed=white')
89
83
  def self.apply_custom_colors(colors)
90
84
  colors.split(':').each do |pair|
91
85
  a = pair.split('=')
@@ -117,23 +111,21 @@ module Cucumber
117
111
  end
118
112
  end
119
113
 
120
- # :stopdoc:
121
- def cukes(n)
122
- ('(::) ' * n).strip
114
+ def cukes(amount)
115
+ ('(::) ' * amount).strip
123
116
  end
124
117
 
125
- def green_cukes(n)
126
- blink(green(cukes(n)))
118
+ def green_cukes(amount)
119
+ blink(green(cukes(amount)))
127
120
  end
128
121
 
129
- def red_cukes(n)
130
- blink(red(cukes(n)))
122
+ def red_cukes(amount)
123
+ blink(red(cukes(amount)))
131
124
  end
132
125
 
133
- def yellow_cukes(n)
134
- blink(yellow(cukes(n)))
126
+ def yellow_cukes(amount)
127
+ blink(yellow(cukes(amount)))
135
128
  end
136
- # :startdoc:
137
129
 
138
130
  private
139
131
 
@@ -112,16 +112,24 @@ module Cucumber
112
112
  if child.respond_to?(:rule) && child.rule
113
113
  process_scenario_container(child.rule)
114
114
  elsif child.respond_to?(:scenario) && child.scenario
115
- child.scenario.steps.each do |step|
116
- @lookup_hash[step.location.line] = StepSource.new(:Step, step)
117
- end
115
+ store_scenario_source_steps(child.scenario)
118
116
  elsif !child.background.nil?
119
- child.background.steps.each do |step|
120
- @lookup_hash[step.location.line] = StepSource.new(:Step, step)
121
- end
117
+ store_background_source_steps(child.background)
122
118
  end
123
119
  end
124
120
  end
121
+
122
+ def store_scenario_source_steps(scenario)
123
+ scenario.steps.each do |step|
124
+ @lookup_hash[step.location.line] = StepSource.new(:Step, step)
125
+ end
126
+ end
127
+
128
+ def store_background_source_steps(background)
129
+ background.steps.each do |step|
130
+ @lookup_hash[step.location.line] = StepSource.new(:Step, step)
131
+ end
132
+ end
125
133
  end
126
134
 
127
135
  KeywordSearchNode = Struct.new(:keyword, :previous_node)
@@ -43,9 +43,9 @@ module Cucumber
43
43
  format_string(line, status)
44
44
  end
45
45
 
46
- def format_string(o, status)
46
+ def format_string(input, status)
47
47
  fmt = format_for(status)
48
- o.to_s.split("\n").map do |line|
48
+ input.to_s.split("\n").map do |line|
49
49
  if fmt.instance_of?(Proc)
50
50
  fmt.call(line)
51
51
  else
@@ -92,16 +92,16 @@ module Cucumber
92
92
  @io.flush
93
93
  end
94
94
 
95
- def print_exception(e, status, indent)
96
- string = exception_message_string(e, indent)
95
+ def print_exception(exception, status, indent)
96
+ string = exception_message_string(exception, indent)
97
97
  @io.puts(format_string(string, status))
98
98
  end
99
99
 
100
- def exception_message_string(e, indent_amount)
101
- message = "#{e.message} (#{e.class})".dup.force_encoding('UTF-8')
100
+ def exception_message_string(exception, indent_amount)
101
+ message = "#{exception.message} (#{exception.class})".dup.force_encoding('UTF-8')
102
102
  message = linebreaks(message, ENV['CUCUMBER_TRUNCATE_OUTPUT'].to_i)
103
103
 
104
- indent("#{message}\n#{e.backtrace.join("\n")}", indent_amount)
104
+ indent("#{message}\n#{exception.backtrace.join("\n")}", indent_amount)
105
105
  end
106
106
 
107
107
  # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/10655
@@ -169,12 +169,17 @@ module Cucumber
169
169
  end
170
170
  end
171
171
 
172
- def attach(src, media_type)
172
+ def attach(src, media_type, filename)
173
173
  return unless media_type == 'text/x.cucumber.log+plain'
174
174
  return unless @io
175
175
 
176
176
  @io.puts
177
- @io.puts(format_string(src, :tag))
177
+ if filename
178
+ @io.puts("#{filename}: #{format_string(src, :tag)}")
179
+ else
180
+ @io.puts(format_string(src, :tag))
181
+ end
182
+
178
183
  @io.flush
179
184
  end
180
185
 
@@ -85,7 +85,7 @@ module Cucumber
85
85
  @io.write(JSON.pretty_generate(@feature_hashes))
86
86
  end
87
87
 
88
- def attach(src, mime_type)
88
+ def attach(src, mime_type, _filename)
89
89
  if mime_type == 'text/x.cucumber.log+plain'
90
90
  test_step_output << src
91
91
  return
@@ -102,7 +102,7 @@ module Cucumber
102
102
  private
103
103
 
104
104
  def same_feature_as_previous_test_case?(test_case)
105
- current_feature[:uri] == test_case.location.file
105
+ @feature_hash&.fetch(:uri, nil) == test_case.location.file
106
106
  end
107
107
 
108
108
  def first_step_after_background?(test_step)
@@ -113,10 +113,6 @@ module Cucumber
113
113
  test_step.location.file.include?('lib/cucumber/')
114
114
  end
115
115
 
116
- def current_feature
117
- @feature_hash ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
118
- end
119
-
120
116
  def feature_elements
121
117
  @feature_hash[:elements] ||= []
122
118
  end
@@ -71,7 +71,7 @@ module Cucumber
71
71
  end
72
72
 
73
73
  def on_test_run_finished(_event)
74
- @features_data.each { |_file, data| end_feature(data) }
74
+ @features_data.each_value { |data| end_feature(data) }
75
75
  end
76
76
 
77
77
  private
@@ -42,11 +42,12 @@ module Cucumber
42
42
  raise 'To be implemented'
43
43
  end
44
44
 
45
- def attach(src, media_type)
45
+ def attach(src, media_type, filename)
46
46
  attachment_data = {
47
47
  test_step_id: @current_test_step_id,
48
48
  test_case_started_id: @current_test_case_started_id,
49
- media_type: media_type
49
+ media_type: media_type,
50
+ file_name: filename
50
51
  }
51
52
 
52
53
  if media_type.start_with?('text/')
@@ -131,7 +132,7 @@ module Cucumber
131
132
  @step_definitions_by_test_step.step_match_arguments(step).map do |argument|
132
133
  Cucumber::Messages::StepMatchArgument.new(
133
134
  group: argument_group_to_message(argument.group),
134
- parameter_type_name: argument.parameter_type.name
135
+ parameter_type_name: parameter_type_name(argument)
135
136
  )
136
137
  end
137
138
  end
@@ -144,6 +145,10 @@ module Cucumber
144
145
  )
145
146
  end
146
147
 
148
+ def parameter_type_name(step_match_argument)
149
+ step_match_argument.parameter_type&.name if step_match_argument.respond_to?(:parameter_type)
150
+ end
151
+
147
152
  def on_test_run_started(*)
148
153
  message = Cucumber::Messages::Envelope.new(
149
154
  test_run_started: Cucumber::Messages::TestRunStarted.new(
@@ -192,10 +197,13 @@ module Cucumber
192
197
 
193
198
  result_message = result.to_message
194
199
  if result.failed? || result.pending?
200
+ message_element = result.failed? ? result.exception : result
201
+
195
202
  result_message = Cucumber::Messages::TestStepResult.new(
196
203
  status: result_message.status,
197
204
  duration: result_message.duration,
198
- message: create_error_message(result)
205
+ message: create_error_message(message_element),
206
+ exception: create_exception_object(result, message_element)
199
207
  )
200
208
  end
201
209
 
@@ -211,12 +219,17 @@ module Cucumber
211
219
  output_envelope(message)
212
220
  end
213
221
 
214
- def create_error_message(result)
215
- message_element = result.failed? ? result.exception : result
222
+ def create_error_message(message_element)
216
223
  message = "#{message_element.message} (#{message_element.class})"
217
224
  ([message] + message_element.backtrace).join("\n")
218
225
  end
219
226
 
227
+ def create_exception_object(result, message_element)
228
+ return unless result.failed?
229
+
230
+ Cucumber::Messages::Exception.from_h({ type: message_element.class, message: message_element.message })
231
+ end
232
+
220
233
  def on_test_case_finished(event)
221
234
  message = Cucumber::Messages::Envelope.new(
222
235
  test_case_finished: Cucumber::Messages::TestCaseFinished.new(
@@ -140,10 +140,14 @@ module Cucumber
140
140
  print_summary
141
141
  end
142
142
 
143
- def attach(src, media_type)
143
+ def attach(src, media_type, filename)
144
144
  return unless media_type == 'text/x.cucumber.log+plain'
145
145
 
146
- @test_step_output.push src
146
+ if filename
147
+ @test_step_output.push("#{filename}: #{src}")
148
+ else
149
+ @test_step_output.push(src)
150
+ end
147
151
  end
148
152
 
149
153
  private
@@ -391,7 +395,7 @@ module Cucumber
391
395
  end
392
396
  end
393
397
 
394
- def print_outline_data(scenario_outline) # rubocop:disable Metrics/AbcSize
398
+ def print_outline_data(scenario_outline)
395
399
  print_comments(scenario_outline.location.line, 2)
396
400
  print_tags(scenario_outline.tags, 2)
397
401
  @source_indent = calculate_source_indent_for_ast_node(scenario_outline) if options[:source]