cucumber 7.0.0 → 8.0.0

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +329 -170
  3. data/CONTRIBUTING.md +2 -6
  4. data/README.md +7 -5
  5. data/lib/autotest/cucumber_mixin.rb +5 -2
  6. data/lib/autotest/discover.rb +3 -2
  7. data/lib/cucumber/cli/configuration.rb +4 -1
  8. data/lib/cucumber/cli/main.rb +4 -3
  9. data/lib/cucumber/cli/options.rb +8 -2
  10. data/lib/cucumber/cli/profile_loader.rb +1 -5
  11. data/lib/cucumber/cli/rerun_file.rb +1 -1
  12. data/lib/cucumber/configuration.rb +5 -4
  13. data/lib/cucumber/constantize.rb +1 -1
  14. data/lib/cucumber/deprecate.rb +2 -1
  15. data/lib/cucumber/errors.rb +1 -1
  16. data/lib/cucumber/events/hook_test_step_created.rb +1 -2
  17. data/lib/cucumber/events/step_activated.rb +0 -6
  18. data/lib/cucumber/events/step_definition_registered.rb +0 -5
  19. data/lib/cucumber/events/test_case_created.rb +1 -2
  20. data/lib/cucumber/events/test_run_finished.rb +2 -1
  21. data/lib/cucumber/events/test_step_created.rb +1 -2
  22. data/lib/cucumber/events/undefined_parameter_type.rb +1 -2
  23. data/lib/cucumber/events.rb +2 -2
  24. data/lib/cucumber/file_specs.rb +2 -1
  25. data/lib/cucumber/filters/activate_steps.rb +1 -0
  26. data/lib/cucumber/filters/tag_limits/verifier.rb +1 -3
  27. data/lib/cucumber/filters/tag_limits.rb +1 -3
  28. data/lib/cucumber/formatter/ansicolor.rb +63 -63
  29. data/lib/cucumber/formatter/ast_lookup.rb +2 -2
  30. data/lib/cucumber/formatter/backtrace_filter.rb +2 -1
  31. data/lib/cucumber/formatter/console.rb +10 -2
  32. data/lib/cucumber/formatter/console_issues.rb +6 -1
  33. data/lib/cucumber/formatter/duration_extractor.rb +1 -0
  34. data/lib/cucumber/formatter/errors.rb +1 -0
  35. data/lib/cucumber/formatter/fanout.rb +1 -1
  36. data/lib/cucumber/formatter/http_io.rb +6 -1
  37. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  38. data/lib/cucumber/formatter/io.rb +5 -3
  39. data/lib/cucumber/formatter/json.rb +8 -6
  40. data/lib/cucumber/formatter/junit.rb +6 -3
  41. data/lib/cucumber/formatter/message_builder.rb +3 -2
  42. data/lib/cucumber/formatter/pretty.rb +15 -5
  43. data/lib/cucumber/formatter/progress.rb +1 -0
  44. data/lib/cucumber/formatter/query/hook_by_test_step.rb +1 -0
  45. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +2 -0
  46. data/lib/cucumber/formatter/rerun.rb +2 -0
  47. data/lib/cucumber/formatter/summary.rb +1 -0
  48. data/lib/cucumber/formatter/unicode.rb +4 -4
  49. data/lib/cucumber/formatter/usage.rb +3 -3
  50. data/lib/cucumber/gherkin/data_table_parser.rb +1 -0
  51. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +2 -2
  52. data/lib/cucumber/glue/dsl.rb +29 -15
  53. data/lib/cucumber/glue/hook.rb +6 -3
  54. data/lib/cucumber/glue/invoke_in_world.rb +4 -4
  55. data/lib/cucumber/glue/proto_world.rb +10 -9
  56. data/lib/cucumber/glue/registry_and_more.rb +22 -7
  57. data/lib/cucumber/glue/registry_wrapper.rb +31 -0
  58. data/lib/cucumber/glue/step_definition.rb +6 -3
  59. data/lib/cucumber/hooks.rb +1 -0
  60. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -1
  61. data/lib/cucumber/multiline_argument/data_table.rb +58 -71
  62. data/lib/cucumber/platform.rb +2 -2
  63. data/lib/cucumber/rake/task.rb +20 -9
  64. data/lib/cucumber/rspec/disable_option_parser.rb +6 -3
  65. data/lib/cucumber/running_test_case.rb +1 -0
  66. data/lib/cucumber/runtime/meta_message_builder.rb +106 -0
  67. data/lib/cucumber/runtime/support_code.rb +3 -0
  68. data/lib/cucumber/runtime/user_interface.rb +5 -4
  69. data/lib/cucumber/runtime.rb +36 -22
  70. data/lib/cucumber/step_match.rb +5 -3
  71. data/lib/cucumber/step_match_search.rb +3 -2
  72. data/lib/cucumber/term/ansicolor.rb +74 -50
  73. data/lib/cucumber/term/banner.rb +1 -0
  74. data/lib/cucumber/version +1 -1
  75. data/lib/cucumber.rb +2 -1
  76. data/lib/simplecov_setup.rb +1 -1
  77. metadata +96 -88
@@ -3,21 +3,48 @@
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 if !$stdout.tty? && !ENV.key?('AUTOTEST')
7
7
 
8
8
  module Cucumber
9
9
  module Formatter
10
- # Defines aliases for coloured output. You don't invoke any methods from this
11
- # module directly, but you can change the output colours by defining
12
- # a <tt>CUCUMBER_COLORS</tt> variable in your shell, very much like how you can
13
- # tweak the familiar POSIX command <tt>ls</tt> with
14
- # <a href="http://mipsisrisc.com/rambling/2008/06/27/lscolorsls_colors-now-with-linux-support/">$LSCOLORS/$LS_COLORS</a>
10
+ # This module allows to format cucumber related outputs using ANSI escape sequences.
11
+ #
12
+ # For example, it provides a `passed` method which returns the string with
13
+ # the ANSI escape sequence to format it green per default.
14
+ #
15
+ # To use this, include or extend it in your class.
16
+ #
17
+ # Example:
18
+ #
19
+ # require 'cucumber/formatter/ansicolor'
20
+ #
21
+ # class MyFormatter
22
+ # extend Cucumber::Term::ANSIColor
23
+ #
24
+ # def on_test_step_finished(event)
25
+ # $stdout.puts undefined(event.test_step) if event.result.undefined?
26
+ # $stdout.puts passed(event.test_step) if event.result.passed?
27
+ # end
28
+ # end
29
+ #
30
+ # This module also allows the user to customize the format of cucumber outputs
31
+ # using environment variables.
32
+ #
33
+ # For instance, if your shell has a black background and a green font (like the
34
+ # "Homebrew" settings for OS X' Terminal.app), you may want to override passed
35
+ # steps to be white instead of green.
36
+ #
37
+ # Example:
38
+ #
39
+ # export CUCUMBER_COLORS="passed=white,bold:passed_param=white,bold,underline"
15
40
  #
16
41
  # The colours that you can change are:
17
42
  #
18
43
  # * <tt>undefined</tt> - defaults to <tt>yellow</tt>
19
44
  # * <tt>pending</tt> - defaults to <tt>yellow</tt>
20
45
  # * <tt>pending_param</tt> - defaults to <tt>yellow,bold</tt>
46
+ # * <tt>flaky</tt> - defaults to <tt>yellow</tt>
47
+ # * <tt>flaky_param</tt> - defaults to <tt>yellow,bold</tt>
21
48
  # * <tt>failed</tt> - defaults to <tt>red</tt>
22
49
  # * <tt>failed_param</tt> - defaults to <tt>red,bold</tt>
23
50
  # * <tt>passed</tt> - defaults to <tt>green</tt>
@@ -29,26 +56,18 @@ module Cucumber
29
56
  # * <tt>comment</tt> - defaults to <tt>grey</tt>
30
57
  # * <tt>tag</tt> - defaults to <tt>cyan</tt>
31
58
  #
32
- # For instance, if your shell has a black background and a green font (like the
33
- # "Homebrew" settings for OS X' Terminal.app), you may want to override passed
34
- # steps to be white instead of green.
35
- #
36
- # Although not listed, you can also use <tt>grey</tt>.
37
- #
38
- # Examples: (On Windows, use SET instead of export.)
39
- #
40
- # export CUCUMBER_COLORS="passed=white"
41
- # export CUCUMBER_COLORS="passed=white,bold:passed_param=white,bold,underline"
42
- #
43
59
  # To see what colours and effects are available, just run this in your shell:
44
60
  #
45
- # ruby -e "require 'rubygems'; require 'term/ansicolor'; puts Cucumber::Term::ANSIColor.attributes"
61
+ # ruby -e "require 'rubygems'; require 'cucumber/term/ansicolor'; puts Cucumber::Term::ANSIColor.attributes"
46
62
  #
47
63
  module ANSIColor
48
64
  include Cucumber::Term::ANSIColor
49
65
 
66
+ # :stopdoc:
50
67
  ALIASES = Hash.new do |h, k|
51
- h[Regexp.last_match(1)] + ',bold' if k.to_s =~ /(.*)_param/
68
+ next unless k.to_s =~ /(.*)_param/
69
+
70
+ "#{h[Regexp.last_match(1)]},bold"
52
71
  end.merge(
53
72
  'undefined' => 'yellow',
54
73
  'pending' => 'yellow',
@@ -60,15 +79,22 @@ module Cucumber
60
79
  'comment' => 'grey',
61
80
  'tag' => 'cyan'
62
81
  )
82
+ # :startdoc:
63
83
 
64
- if ENV['CUCUMBER_COLORS'] # Example: export CUCUMBER_COLORS="passed=red:failed=yellow"
65
- ENV['CUCUMBER_COLORS'].split(':').each do |pair|
84
+ # Apply the custom color scheme
85
+ #
86
+ # example:
87
+ #
88
+ # apply_custom_colors('passed=white')
89
+ def apply_custom_colors(colors)
90
+ colors.split(':').each do |pair|
66
91
  a = pair.split('=')
67
92
  ALIASES[a[0]] = a[1]
68
93
  end
69
94
  end
95
+ apply_custom_colors(ENV['CUCUMBER_COLORS']) if ENV['CUCUMBER_COLORS']
70
96
 
71
- # Eval to define the color-named methods required by Term::ANSIColor.
97
+ # Define the color-named methods required by Term::ANSIColor.
72
98
  #
73
99
  # Examples:
74
100
  #
@@ -80,53 +106,18 @@ module Cucumber
80
106
  # red(bold(string, &proc)) + red
81
107
  # end
82
108
  ALIASES.each_key do |method_name|
83
- next if method_name =~ /.*_param/
84
- code = <<-COLOR
85
- def #{method_name}(string=nil, &proc)
86
- #{ALIASES[method_name].split(',').join('(') + '(string, &proc' + ')' * ALIASES[method_name].split(',').length}
87
- end
88
- # This resets the colour to the non-param colour
89
- def #{method_name}_param(string=nil, &proc)
90
- #{ALIASES[method_name + '_param'].split(',').join('(') + '(string, &proc' + ')' * ALIASES[method_name + '_param'].split(',').length} + #{ALIASES[method_name].split(',').join(' + ')}
91
- end
92
- COLOR
93
- eval(code) # rubocop:disable Security/Eval
94
- end
109
+ next if method_name.end_with?('_param')
95
110
 
96
- def self.define_grey #:nodoc:
97
- gem 'genki-ruby-terminfo'
98
- require 'terminfo'
99
- case TermInfo.default_object.tigetnum('colors')
100
- when 0
101
- raise "Your terminal doesn't support colours."
102
- when 1
103
- ::Cucumber::Term::ANSIColor.coloring = false
104
- alias_method :grey, :white
105
- when 2..8
106
- alias_method :grey, :white # rubocop:disable Lint/DuplicateMethods
107
- else
108
- define_real_grey
109
- end
110
- rescue Exception => e # rubocop:disable Lint/RescueException
111
- if e.class.name == 'TermInfo::TermInfoError'
112
- STDERR.puts '*** WARNING ***'
113
- STDERR.puts "You have the genki-ruby-terminfo gem installed, but you haven't set your TERM variable."
114
- STDERR.puts 'Try setting it to TERM=xterm-256color to get grey colour in output.'
115
- STDERR.puts "\n"
116
- alias_method :grey, :white
117
- else
118
- define_real_grey
111
+ define_method(method_name) do |text = nil, &proc|
112
+ apply_styles(ALIASES[method_name], text, &proc)
119
113
  end
120
- end
121
114
 
122
- def self.define_real_grey #:nodoc:
123
- define_method :grey do |string|
124
- ::Cucumber::Term::ANSIColor.coloring? ? "\e[90m#{string}\e[0m" : string
115
+ define_method("#{method_name}_param") do |text = nil, &proc|
116
+ apply_styles(ALIASES["#{method_name}_param"], text, &proc) + apply_styles(ALIASES[method_name])
125
117
  end
126
118
  end
127
119
 
128
- define_grey
129
-
120
+ # :stopdoc:
130
121
  def cukes(n)
131
122
  ('(::) ' * n).strip
132
123
  end
@@ -142,6 +133,15 @@ module Cucumber
142
133
  def yellow_cukes(n)
143
134
  blink(yellow(cukes(n)))
144
135
  end
136
+ # :startdoc:
137
+
138
+ private
139
+
140
+ def apply_styles(styles, text = nil, &proc)
141
+ styles.split(',').reverse.reduce(text) do |result, method_name|
142
+ send(method_name, result, &proc)
143
+ end
144
+ end
145
145
  end
146
146
  end
147
147
  end
@@ -45,11 +45,11 @@ module Cucumber
45
45
  break
46
46
  end
47
47
  break if node.previous_node.nil?
48
+
48
49
  node = node.previous_node
49
50
  end
50
51
  keyword = dialect.given_keywords.reject { |kw| kw == '* ' }[0] if keyword.nil?
51
- keyword = Cucumber::Gherkin::I18n.code_keyword_for(keyword)
52
- keyword
52
+ Cucumber::Gherkin::I18n.code_keyword_for(keyword)
53
53
  end
54
54
 
55
55
  ScenarioSource = Struct.new(:type, :scenario)
@@ -22,6 +22,7 @@ module Cucumber
22
22
  @backtrace_filters << RbConfig::CONFIG['rubylibdir'] if RbConfig::CONFIG['rubylibdir']
23
23
 
24
24
  @backtrace_filters << 'org/jruby/' if ::Cucumber::JRUBY
25
+ @backtrace_filters << '<internal:' if RUBY_ENGINE == 'truffleruby'
25
26
 
26
27
  BACKTRACE_FILTER_PATTERNS = Regexp.new(@backtrace_filters.join('|'))
27
28
 
@@ -33,7 +34,7 @@ module Cucumber
33
34
  def exception
34
35
  return @exception if ::Cucumber.use_full_backtrace
35
36
 
36
- pwd_pattern = /#{::Regexp.escape(::Dir.pwd)}\//m # rubocop:disable Style/RegexpLiteral
37
+ pwd_pattern = /#{::Regexp.escape(::Dir.pwd)}\//m
37
38
  backtrace = @exception.backtrace.map { |line| line.gsub(pwd_pattern, './') }
38
39
 
39
40
  filtered = (backtrace || []).reject do |line|
@@ -34,7 +34,7 @@ module Cucumber
34
34
 
35
35
  def format_step(keyword, step_match, status, source_indent)
36
36
  comment = if source_indent
37
- c = indent(('# ' + step_match.location.to_s), source_indent)
37
+ c = indent("# #{step_match.location}", source_indent)
38
38
  format_string(c, :comment)
39
39
  else
40
40
  ''
@@ -109,7 +109,10 @@ module Cucumber
109
109
  # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/10655
110
110
  def linebreaks(msg, max)
111
111
  return msg unless max && max > 0
112
- msg.gsub(/.{1,#{max}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") }.rstrip
112
+
113
+ msg.gsub(/.{1,#{max}}(?:\s|\Z)/) do
114
+ (Regexp.last_match(0) + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n")
115
+ end.rstrip
113
116
  end
114
117
 
115
118
  def collect_snippet_data(test_step, ast_lookup)
@@ -150,6 +153,7 @@ module Cucumber
150
153
 
151
154
  def print_passing_wip(config, passed_test_cases, ast_lookup)
152
155
  return unless config.wip?
156
+
153
157
  messages = passed_test_cases.map do |test_case|
154
158
  scenario_source = ast_lookup.scenario_source(test_case)
155
159
  keyword = scenario_source.type == :Scenario ? scenario_source.scenario.keyword : scenario_source.scenario_outline.keyword
@@ -170,6 +174,7 @@ module Cucumber
170
174
  def attach(src, media_type)
171
175
  return unless media_type == 'text/x.cucumber.log+plain'
172
176
  return unless @io
177
+
173
178
  @io.puts
174
179
  @io.puts(format_string(src, :tag))
175
180
  @io.flush
@@ -177,6 +182,7 @@ module Cucumber
177
182
 
178
183
  def print_profile_information
179
184
  return if @options[:skip_profile_information] || @options[:profiles].nil? || @options[:profiles].empty?
185
+
180
186
  do_print_profile_information(@options[:profiles])
181
187
  end
182
188
 
@@ -224,6 +230,7 @@ module Cucumber
224
230
  key = keys.join('_').to_sym
225
231
  fmt = FORMATS[key]
226
232
  raise "No format for #{key.inspect}: #{FORMATS.inspect}" if fmt.nil?
233
+
227
234
  fmt
228
235
  end
229
236
 
@@ -246,6 +253,7 @@ module Cucumber
246
253
 
247
254
  class SnippetData
248
255
  attr_reader :actual_keyword, :step
256
+
249
257
  def initialize(actual_keyword, step)
250
258
  @actual_keyword = actual_keyword
251
259
  @step = step
@@ -23,6 +23,7 @@ module Cucumber
23
23
 
24
24
  def to_s
25
25
  return if @issues.empty?
26
+
26
27
  result = Core::Test::Result::TYPES.map { |type| scenario_listing(type, @issues[type]) }
27
28
  result.flatten.join("\n")
28
29
  end
@@ -35,6 +36,7 @@ module Cucumber
35
36
 
36
37
  def scenario_listing(type, test_cases)
37
38
  return [] if test_cases.empty?
39
+
38
40
  [format_string("#{type_heading(type)} Scenarios:", type)] + test_cases.map do |test_case|
39
41
  scenario_source = @ast_lookup.scenario_source(test_case)
40
42
  keyword = scenario_source.type == :Scenario ? scenario_source.scenario.keyword : scenario_source.scenario_outline.keyword
@@ -54,7 +56,10 @@ module Cucumber
54
56
 
55
57
  def profiles_string
56
58
  return if @config.custom_profiles.empty?
57
- @config.custom_profiles.map { |profile| "-p #{profile}" }.join(' ') + ' '
59
+
60
+ profiles = @config.custom_profiles.map { |profile| "-p #{profile}" }.join(' ')
61
+
62
+ "#{profiles} "
58
63
  end
59
64
  end
60
65
  end
@@ -4,6 +4,7 @@ module Cucumber
4
4
  module Formatter
5
5
  class DurationExtractor
6
6
  attr_reader :result_duration
7
+
7
8
  def initialize(result)
8
9
  @result_duration = 0
9
10
  result.describe_to(self)
@@ -1,6 +1,7 @@
1
1
  module Cucumber
2
2
  module Formatter
3
3
  class TestCaseUnknownError < StandardError; end
4
+
4
5
  class TestStepUnknownError < StandardError; end
5
6
  end
6
7
  end
@@ -21,7 +21,7 @@ module Cucumber
21
21
  end
22
22
 
23
23
  def respond_to_missing?(name, include_private = false)
24
- recipients.any? { |recipient| recipient.respond_to?(name, include_private) }
24
+ recipients.any? { |recipient| recipient.respond_to?(name, include_private) } || super(name, include_private)
25
25
  end
26
26
  end
27
27
  end
@@ -35,6 +35,7 @@ module Cucumber
35
35
  headers = headers.merge(parse_header(header_arg))
36
36
  else
37
37
  raise StandardError, "#{options} was not a valid curl command. Can't set url to #{arg} it is already set to #{url}" if url
38
+
38
39
  url = arg
39
40
  end
40
41
  end
@@ -49,12 +50,14 @@ module Cucumber
49
50
 
50
51
  def self.remove_arg_for(args, arg)
51
52
  return args.shift unless args.empty?
53
+
52
54
  raise StandardError, "Missing argument for #{arg}"
53
55
  end
54
56
 
55
57
  def self.parse_header(header_arg)
56
58
  parts = header_arg.split(':', 2)
57
59
  raise StandardError, "#{header_arg} was not a valid header" unless parts.length == 2
60
+
58
61
  { parts[0].strip => parts[1].strip }
59
62
  end
60
63
  end
@@ -76,6 +79,7 @@ module Cucumber
76
79
  @reporter.report(response.body)
77
80
  @write_io.close
78
81
  return if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPRedirection)
82
+
79
83
  raise StandardError, "request to #{uri} failed with status #{response.code}"
80
84
  end
81
85
 
@@ -98,6 +102,7 @@ module Cucumber
98
102
  http = build_client(uri, @https_verify_mode)
99
103
 
100
104
  raise StandardError, "request to #{uri} failed (too many redirections)" if attempt <= 0
105
+
101
106
  req = build_request(
102
107
  uri,
103
108
  method,
@@ -126,7 +131,7 @@ module Cucumber
126
131
  end
127
132
 
128
133
  def build_request(uri, method, headers)
129
- method_class_name = "#{method[0].upcase}#{method[1..-1].downcase}"
134
+ method_class_name = "#{method[0].upcase}#{method[1..].downcase}"
130
135
  req = Net::HTTP.const_get(method_class_name).new(uri)
131
136
  headers.each do |header, value|
132
137
  req[header] = value
@@ -12,7 +12,7 @@ module Cucumber
12
12
  end
13
13
 
14
14
  def respond_to_missing?(name, include_private = false)
15
- @receiver.respond_to?(name, include_private)
15
+ @receiver.respond_to?(name, include_private) || super(name, include_private)
16
16
  end
17
17
  end
18
18
  end
@@ -57,19 +57,21 @@ module Cucumber
57
57
  end
58
58
 
59
59
  def url?(path_or_url_or_io)
60
- path_or_url_or_io.match(%r{^https?://})
60
+ path_or_url_or_io.match(/^https?:\/\//)
61
61
  end
62
62
 
63
63
  def ensure_file(path, name)
64
- raise "You *must* specify --out FILE for the #{name} formatter" unless String == path.class
64
+ raise "You *must* specify --out FILE for the #{name} formatter" unless path.instance_of? String
65
65
  raise "I can't write #{name} to a directory - it has to be a file" if File.directory?(path)
66
66
  raise "I can't write #{name} to a file in the non-existing directory #{File.dirname(path)}" unless File.directory?(File.dirname(path))
67
+
67
68
  ensure_io(path, nil)
68
69
  end
69
70
 
70
71
  def ensure_dir(path, name)
71
- raise "You *must* specify --out DIR for the #{name} formatter" unless String == path.class
72
+ raise "You *must* specify --out DIR for the #{name} formatter" unless path.instance_of? String
72
73
  raise "I can't write #{name} reports to a file - it has to be a directory" if File.file?(path)
74
+
73
75
  FileUtils.mkdir_p(path) unless File.directory?(path)
74
76
  File.absolute_path path
75
77
  end
@@ -68,6 +68,7 @@ module Cucumber
68
68
  test_step, result = *event.attributes
69
69
  result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
70
70
  return if internal_hook?(test_step)
71
+
71
72
  add_match_and_result(test_step, result)
72
73
  @any_step_failed = true if result.failed?
73
74
  end
@@ -81,7 +82,7 @@ module Cucumber
81
82
  end
82
83
 
83
84
  def on_test_run_finished(_event)
84
- @io.write(JSON.generate(@feature_hashes, pretty: true))
85
+ @io.write(JSON.pretty_generate(@feature_hashes))
85
86
  end
86
87
 
87
88
  def attach(src, mime_type)
@@ -105,7 +106,7 @@ module Cucumber
105
106
  end
106
107
 
107
108
  def first_step_after_background?(test_step)
108
- @in_background && test_step.location.lines.max >= @test_case_hash[:line]
109
+ @in_background && test_step.location.file == @feature_hash[:uri] && test_step.location.lines.max >= @test_case_hash[:line]
109
110
  end
110
111
 
111
112
  def internal_hook?(test_step)
@@ -133,7 +134,7 @@ module Cucumber
133
134
  when 'AfterStep hook'
134
135
  after_step_hooks
135
136
  else
136
- raise 'Unknown hook type ' + hook_step.to_s
137
+ raise "Unknown hook type #{hook_step}"
137
138
  end
138
139
  end
139
140
 
@@ -175,15 +176,15 @@ module Cucumber
175
176
  name: test_step.text,
176
177
  line: test_step.location.lines.min
177
178
  }
178
- step_hash[:doc_string] = create_doc_string_hash(step_source.doc_string) unless step_source.doc_string.nil?
179
+ step_hash[:doc_string] = create_doc_string_hash(step_source.doc_string, test_step.multiline_arg.content) unless step_source.doc_string.nil?
179
180
  step_hash[:rows] = create_data_table_value(step_source.data_table) unless step_source.data_table.nil?
180
181
  step_hash
181
182
  end
182
183
 
183
- def create_doc_string_hash(doc_string)
184
+ def create_doc_string_hash(doc_string, doc_string_content)
184
185
  content_type = doc_string.media_type || ''
185
186
  {
186
- value: doc_string.content,
187
+ value: doc_string_content,
187
188
  content_type: content_type,
188
189
  line: doc_string.location.line
189
190
  }
@@ -259,6 +260,7 @@ module Cucumber
259
260
  line: feature.location.line
260
261
  }
261
262
  return if feature.tags.empty?
263
+
262
264
  @feature_hash[:tags] = create_tags_array_from_hash_array(feature.tags)
263
265
  end
264
266
 
@@ -84,6 +84,7 @@ module Cucumber
84
84
  uri = test_case.location.file
85
85
  feature = @ast_lookup.gherkin_document(uri).feature
86
86
  raise UnNamedFeatureError, uri if feature.name.empty?
87
+
87
88
  @current_feature_data = @features_data[uri]
88
89
  @current_feature_data[:uri] = uri unless @current_feature_data[:uri]
89
90
  @current_feature_data[:feature] = feature unless @current_feature_data[:feature]
@@ -111,6 +112,7 @@ module Cucumber
111
112
  keyword = scenario_source.type == :Scenario ? scenario_source.scenario.keyword : scenario_source.scenario_outline.keyword
112
113
  output = "#{keyword}: #{scenario}\n\n"
113
114
  return output if result.ok?(@config.strict)
115
+
114
116
  if scenario_source.type == :Scenario
115
117
  if @failing_test_step
116
118
  if @failing_test_step.hook?
@@ -125,7 +127,7 @@ module Cucumber
125
127
  else
126
128
  output += "Example row: #{row_name}\n"
127
129
  end
128
- output + "\nMessage:\n"
130
+ "#{output}\nMessage:\n"
129
131
  end
130
132
 
131
133
  def build_testcase(result, scenario_designation, output)
@@ -191,7 +193,7 @@ module Cucumber
191
193
  end
192
194
 
193
195
  def basename(feature_file)
194
- File.basename(feature_file.gsub(/[\\\/]/, '-'), '.feature') # rubocop:disable Style/RegexpLiteral
196
+ File.basename(feature_file.gsub(/[\\\/]/, '-'), '.feature')
195
197
  end
196
198
 
197
199
  def write_file(feature_filename, data)
@@ -228,13 +230,14 @@ module Cucumber
228
230
  end
229
231
 
230
232
  def examples_table_row(row)
231
- @row_name = '| ' + row.cells.map(&:value).join(' | ') + ' |'
233
+ @row_name = "| #{row.cells.map(&:value).join(' | ')} |"
232
234
  @name_suffix = " (outline example : #{@row_name})"
233
235
  end
234
236
  end
235
237
 
236
238
  class ResultBuilder
237
239
  attr_reader :test_case_duration
240
+
238
241
  def initialize(result)
239
242
  @test_case_duration = 0
240
243
  result.describe_to(self)
@@ -226,10 +226,11 @@ module Cucumber
226
226
  output_envelope(message)
227
227
  end
228
228
 
229
- def on_test_run_finished(*)
229
+ def on_test_run_finished(event)
230
230
  message = Cucumber::Messages::Envelope.new(
231
231
  test_run_finished: Cucumber::Messages::TestRunFinished.new(
232
- timestamp: time_to_timestamp(Time.now)
232
+ timestamp: time_to_timestamp(Time.now),
233
+ success: event.success
233
234
  )
234
235
  )
235
236
 
@@ -24,11 +24,9 @@ module Cucumber
24
24
  include Console
25
25
  include Io
26
26
  include Cucumber::Gherkin::Formatter::Escaping
27
- attr_reader :config, :options
27
+ attr_reader :config, :options, :current_feature_uri, :current_scenario_outline, :current_examples, :current_test_case, :in_scenario_outline, :print_background_steps
28
28
  private :config, :options
29
- attr_reader :current_feature_uri, :current_scenario_outline, :current_examples, :current_test_case
30
29
  private :current_feature_uri, :current_scenario_outline, :current_examples, :current_test_case
31
- attr_reader :in_scenario_outline, :print_background_steps
32
30
  private :in_scenario_outline, :print_background_steps
33
31
 
34
32
  def initialize(config)
@@ -105,16 +103,19 @@ module Cucumber
105
103
 
106
104
  def on_test_step_started(event)
107
105
  return if event.test_step.hook?
106
+
108
107
  print_step_header(current_test_case) if first_step_after_printing_background_steps?(event.test_step)
109
108
  end
110
109
 
111
110
  def on_test_step_finished(event)
112
111
  collect_snippet_data(event.test_step, @ast_lookup) if event.result.undefined?
113
112
  return if in_scenario_outline && !options[:expand]
113
+
114
114
  exception_to_be_printed = find_exception_to_be_printed(event.result)
115
115
  print_step_data(event.test_step, event.result) if print_step_data?(event, exception_to_be_printed)
116
116
  print_step_output
117
117
  return unless exception_to_be_printed
118
+
118
119
  print_exception(exception_to_be_printed, event.result.to_sym, 6)
119
120
  @exceptions << exception_to_be_printed
120
121
  end
@@ -127,6 +128,7 @@ module Cucumber
127
128
  else
128
129
  exception_to_be_printed = find_exception_to_be_printed(event.result)
129
130
  return unless exception_to_be_printed
131
+
130
132
  print_exception(exception_to_be_printed, event.result.to_sym, 6)
131
133
  @exceptions << exception_to_be_printed
132
134
  end
@@ -140,6 +142,7 @@ module Cucumber
140
142
 
141
143
  def attach(src, media_type)
142
144
  return unless media_type == 'text/x.cucumber.log+plain'
145
+
143
146
  @test_step_output.push src
144
147
  end
145
148
 
@@ -147,9 +150,11 @@ module Cucumber
147
150
 
148
151
  def find_exception_to_be_printed(result)
149
152
  return nil if result.ok?(options[:strict])
153
+
150
154
  result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
151
155
  exception = result.failed? ? result.exception : result
152
156
  return nil if @exceptions.include?(exception)
157
+
153
158
  exception
154
159
  end
155
160
 
@@ -196,6 +201,7 @@ module Cucumber
196
201
  def feature_has_background?
197
202
  feature_children = gherkin_document.feature.children
198
203
  return false if feature_children.empty?
204
+
199
205
  !feature_children.first.background.nil?
200
206
  end
201
207
 
@@ -239,6 +245,7 @@ module Cucumber
239
245
  def first_step_after_printing_background_steps?(test_step)
240
246
  return false unless print_background_steps
241
247
  return false unless test_step.location.lines.max >= current_test_case.location.lines.max
248
+
242
249
  @print_background_steps = false
243
250
  true
244
251
  end
@@ -262,7 +269,8 @@ module Cucumber
262
269
  def print_comments(up_to_line, indent_amount)
263
270
  comments = gherkin_document.comments
264
271
  return if comments.empty? || comments.length <= @next_comment_to_be_printed
265
- comments[@next_comment_to_be_printed..-1].each do |comment|
272
+
273
+ comments[@next_comment_to_be_printed..].each do |comment|
266
274
  if comment.location.line <= up_to_line
267
275
  @io.puts(indent(format_string(comment.text.strip, :comment), indent_amount))
268
276
  @next_comment_to_be_printed += 1
@@ -294,6 +302,7 @@ module Cucumber
294
302
 
295
303
  def print_description(description)
296
304
  return unless description
305
+
297
306
  description.split("\n").each do |line|
298
307
  @io.puts(line)
299
308
  end
@@ -398,6 +407,7 @@ module Cucumber
398
407
  end
399
408
  @io.puts
400
409
  next if options[:no_multiline]
410
+
401
411
  print_doc_string(step.doc_string.content, :skipped, 6) unless step.doc_string.nil?
402
412
  print_data_table(step.data_table, :skipped, 6) unless step.data_table.nil?
403
413
  end
@@ -445,7 +455,7 @@ module Cucumber
445
455
  language = ::Gherkin::Dialect.for(language_code)
446
456
  scenario_keyword = language.scenario_keywords[0]
447
457
  row = scenario_source(test_case).row
448
- expanded_name = '| ' + row.cells.map(&:value).join(' | ') + ' |'
458
+ expanded_name = "| #{row.cells.map(&:value).join(' | ')} |"
449
459
  @source_indent = calculate_source_indent_for_expanded_test_case(test_case, scenario_keyword, expanded_name)
450
460
  @io.puts
451
461
  print_keyword_name(scenario_keyword, expanded_name, 6, test_case.location)
@@ -58,6 +58,7 @@ module Cucumber
58
58
  progress(result.to_sym) if !test_step.hook? || result.failed?
59
59
 
60
60
  return if test_step.hook?
61
+
61
62
  collect_snippet_data(test_step, @ast_lookup) if result.undefined?
62
63
  @pending_step_matches << @matches[test_step.to_s] if result.pending?
63
64
  @failed_results << result if result.failed?
@@ -13,6 +13,7 @@ module Cucumber
13
13
 
14
14
  def hook_id(test_step)
15
15
  return @hook_id_by_test_step_id[test_step.id] if @hook_id_by_test_step_id.key?(test_step.id)
16
+
16
17
  raise TestStepUnknownError, "No hook found for #{test_step.id} }. Known: #{@hook_id_by_test_step_id.keys}"
17
18
  end
18
19