cucumber 7.1.0 → 8.0.0.rc.1

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +79 -1
  3. data/CONTRIBUTING.md +2 -6
  4. data/README.md +4 -7
  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 +1 -1
  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 +1 -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 +3 -1
  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 +4 -9
  53. data/lib/cucumber/glue/hook.rb +2 -1
  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 +4 -18
  57. data/lib/cucumber/glue/step_definition.rb +6 -3
  58. data/lib/cucumber/hooks.rb +1 -0
  59. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -1
  60. data/lib/cucumber/multiline_argument/data_table.rb +58 -71
  61. data/lib/cucumber/platform.rb +2 -2
  62. data/lib/cucumber/rake/task.rb +10 -7
  63. data/lib/cucumber/rspec/disable_option_parser.rb +6 -3
  64. data/lib/cucumber/running_test_case.rb +1 -0
  65. data/lib/cucumber/runtime/meta_message_builder.rb +106 -0
  66. data/lib/cucumber/runtime/support_code.rb +3 -0
  67. data/lib/cucumber/runtime/user_interface.rb +5 -4
  68. data/lib/cucumber/runtime.rb +21 -37
  69. data/lib/cucumber/step_match.rb +5 -3
  70. data/lib/cucumber/step_match_search.rb +3 -2
  71. data/lib/cucumber/term/ansicolor.rb +74 -50
  72. data/lib/cucumber/term/banner.rb +1 -0
  73. data/lib/cucumber/version +1 -1
  74. data/lib/cucumber.rb +2 -1
  75. data/lib/simplecov_setup.rb +1 -1
  76. metadata +74 -73
@@ -15,11 +15,13 @@ module Cucumber
15
15
 
16
16
  def attempt_by_test_case(test_case)
17
17
  raise TestCaseUnknownError, "No test case found for #{test_case.id} }. Known: #{@attempts_by_test_case_id.keys}" unless @attempts_by_test_case_id.key?(test_case.id)
18
+
18
19
  @attempts_by_test_case_id[test_case.id]
19
20
  end
20
21
 
21
22
  def test_case_started_id_by_test_case(test_case)
22
23
  raise TestCaseUnknownError, "No test case found for #{test_case.id} }. Known: #{@test_case_started_id_by_test_case_id.keys}" unless @test_case_started_id_by_test_case_id.key?(test_case.id)
24
+
23
25
  @test_case_started_id_by_test_case_id[test_case.id]
24
26
  end
25
27
 
@@ -15,6 +15,7 @@ module Cucumber
15
15
  test_case, result = *event.attributes
16
16
  if @config.strict.strict?(:flaky)
17
17
  next if result.ok?(@config.strict)
18
+
18
19
  add_to_failures(test_case)
19
20
  else
20
21
  unless @latest_failed_test_case.nil?
@@ -31,6 +32,7 @@ module Cucumber
31
32
  config.on_event :test_run_finished do
32
33
  add_to_failures(@latest_failed_test_case) unless @latest_failed_test_case.nil?
33
34
  next if @failures.empty?
35
+
34
36
  @io.print file_failures.join("\n")
35
37
  end
36
38
  end
@@ -47,6 +47,7 @@ module Cucumber
47
47
  def print_feature(test_case)
48
48
  uri = test_case.location.file
49
49
  return if @current_feature_uri == uri
50
+
50
51
  feature_name = gherkin_document(uri).feature.name
51
52
  @io.puts unless @current_feature_uri.nil?
52
53
  @io.puts feature_name
@@ -17,7 +17,7 @@ if Cucumber::WINDOWS
17
17
  end
18
18
  else
19
19
  Cucumber::CODEPAGE = 'cp1252'.freeze
20
- STDERR.puts("WARNING: Couldn't detect your output codepage. Assuming it is 1252. You may have to chcp 1252 or SET CUCUMBER_OUTPUT_ENCODING=cp1252.")
20
+ $stderr.puts("WARNING: Couldn't detect your output codepage. Assuming it is 1252. You may have to chcp 1252 or SET CUCUMBER_OUTPUT_ENCODING=cp1252.")
21
21
  end
22
22
 
23
23
  module Cucumber
@@ -28,7 +28,7 @@ if Cucumber::WINDOWS
28
28
  def cucumber_preprocess_output(*out)
29
29
  out.map { |arg| arg.to_s.encode(Encoding.default_external) }
30
30
  rescue Encoding::UndefinedConversionError => e
31
- STDERR.cucumber_puts("WARNING: #{e.message}")
31
+ $stderr.cucumber_puts("WARNING: #{e.message}")
32
32
  out
33
33
  end
34
34
 
@@ -45,8 +45,8 @@ if Cucumber::WINDOWS
45
45
  end
46
46
 
47
47
  Kernel.extend(self)
48
- STDOUT.extend(self)
49
- STDERR.extend(self)
48
+ $stdout.extend(self)
49
+ $stderr.extend(self)
50
50
  end
51
51
  end
52
52
  end
@@ -78,7 +78,7 @@ module Cucumber
78
78
  if @stepdef_to_match[stepdef_key].any?
79
79
  print_steps(stepdef_key)
80
80
  else
81
- @io.puts(' ' + format_string('NOT MATCHED BY ANY STEPS', :failed))
81
+ @io.puts(" #{format_string('NOT MATCHED BY ANY STEPS', :failed)}")
82
82
  end
83
83
  end
84
84
  @io.puts
@@ -86,7 +86,7 @@ module Cucumber
86
86
  end
87
87
 
88
88
  def print_step_definition(stepdef_key)
89
- @io.print format_string(format('%<duration>.7f', duration: stepdef_key.mean_duration), :skipped) + ' ' unless config.dry_run?
89
+ @io.print "#{format_string(format('%<duration>.7f', duration: stepdef_key.mean_duration), :skipped)} " unless config.dry_run?
90
90
  @io.print format_string(stepdef_key.regexp_source, stepdef_key.status)
91
91
  if config.source?
92
92
  indent_amount = max_length - stepdef_key.regexp_source.unpack('U*').length
@@ -99,7 +99,7 @@ module Cucumber
99
99
  def print_steps(stepdef_key)
100
100
  @stepdef_to_match[stepdef_key].each do |step|
101
101
  @io.print ' '
102
- @io.print format_string(format('%<duration>.7f', duration: step[:duration]), :skipped) + ' ' unless config.dry_run?
102
+ @io.print "#{format_string(format('%<duration>.7f', duration: step[:duration]), :skipped)} " unless config.dry_run?
103
103
  @io.print format_step(step[:keyword], step[:step_match], step[:status], nil)
104
104
  if config.source?
105
105
  indent_amount = max_length - (step[:keyword].unpack('U*').length + step[:step_match].format_args.unpack('U*').length)
@@ -19,6 +19,7 @@ module Cucumber
19
19
  end
20
20
 
21
21
  return if gherkin_document.nil?
22
+
22
23
  gherkin_document[:feature][:children][0][:scenario][:steps][0][:data_table][:rows].each do |row|
23
24
  @builder.row(row[:cells].map { |cell| cell[:value] })
24
25
  end
@@ -54,7 +54,7 @@ module Cucumber
54
54
  }.freeze
55
55
 
56
56
  ALIASES = Hash.new do |h, k|
57
- h[Regexp.last_match(1)] + ',bold' if k.to_s =~ /(.*)_arg/
57
+ "#{h[Regexp.last_match(1)]},bold" if k.to_s =~ /(.*)_arg/
58
58
  end.merge(
59
59
  'undefined' => 'yellow',
60
60
  'pending' => 'yellow',
@@ -74,7 +74,7 @@ module Cucumber
74
74
  end
75
75
  end
76
76
 
77
- ALIASES.keys.each do |key|
77
+ ALIASES.each_key do |key|
78
78
  define_method(key) do
79
79
  ALIASES[key].split(',').map { |color| COLORS[color] }.join('')
80
80
  end
@@ -107,14 +107,6 @@ module Cucumber
107
107
  value.nil? ? default : value
108
108
  end
109
109
 
110
- # Registers a proc that will run after Cucumber is configured. You can register as
111
- # as you want (typically from ruby scripts under <tt>support/hooks.rb</tt>).
112
- #
113
- # DEPRECATED: please use InstallPlugin or BeforeAll instead
114
- def AfterConfiguration(&proc)
115
- Dsl.register_rb_hook('after_configuration', [], proc)
116
- end
117
-
118
110
  # Registers a proc that will run after Cucumber is configured in order to install an external plugin.
119
111
  def InstallPlugin(&proc)
120
112
  Dsl.register_rb_hook('install_plugin', [], proc)
@@ -155,5 +147,8 @@ module Cucumber
155
147
  end
156
148
  end
157
149
 
158
- # TODO: can we avoid adding methods to the global namespace (Kernel)
150
+ # rubocop:disable Style/MixinUsage
151
+ # This "should" always be present, because it allows users to write `Before` and `After`
152
+ # See. https://github.com/cucumber/cucumber-ruby/pull/1566#discussion_r683235396
159
153
  extend(Cucumber::Glue::Dsl)
154
+ # rubocop:enable Style/MixinUsage
@@ -32,7 +32,7 @@ module Cucumber
32
32
  Cucumber::Messages::Envelope.new(
33
33
  hook: Cucumber::Messages::Hook.new(
34
34
  id: id,
35
- tag_expression: tag_expressions.join(' '),
35
+ tag_expression: tag_expressions.empty? ? nil : tag_expressions.join(' '),
36
36
  source_reference: Cucumber::Messages::SourceReference.new(
37
37
  uri: location.file,
38
38
  location: Cucumber::Messages::Location.new(
@@ -58,6 +58,7 @@ module Cucumber
58
58
  end
59
59
 
60
60
  next unless tag_expression.include?(',')
61
+
61
62
  warn("Found tagged hook with '#{tag_expression}'." \
62
63
  "'@tag1,@tag2' is no longer supported, use '@tag or @tag2' instead.")
63
64
  end
@@ -12,6 +12,7 @@ module Cucumber
12
12
 
13
13
  instance_exec_pos = backtrace.index(instance_exec_invocation_line)
14
14
  return unless instance_exec_pos
15
+
15
16
  replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET
16
17
  backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method
17
18
 
@@ -27,7 +28,7 @@ module Cucumber
27
28
  if check_arity && !cucumber_compatible_arity?(args, block)
28
29
  world.instance_exec do
29
30
  ari = block.arity
30
- ari = ari < 0 ? (ari.abs - 1).to_s + '+' : ari
31
+ ari = ari < 0 ? "#{ari.abs - 1}+" : ari
31
32
  s1 = ari == 1 ? '' : 's'
32
33
  s2 = args.length == 1 ? '' : 's'
33
34
  raise ArityMismatchError, "Your block takes #{ari} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
@@ -40,9 +41,8 @@ module Cucumber
40
41
 
41
42
  def self.cucumber_compatible_arity?(args, block)
42
43
  return true if block.arity == args.length
43
- if block.arity < 0
44
- return true if args.length >= (block.arity.abs - 1)
45
- end
44
+ return true if block.arity.negative? && args.length >= (block.arity.abs - 1)
45
+
46
46
  false
47
47
  end
48
48
 
@@ -138,8 +138,8 @@ module Cucumber
138
138
  # TODO: pass these in when building the module, instead of mutating them later
139
139
  # Extend the World with user-defined modules
140
140
  def add_modules!(world_modules, namespaced_world_modules)
141
- add_world_modules!(world_modules)
142
- add_namespaced_modules!(namespaced_world_modules)
141
+ add_world_modules!(world_modules) if world_modules.any?
142
+ add_namespaced_modules!(namespaced_world_modules) if namespaced_world_modules.any?
143
143
  end
144
144
 
145
145
  define_method(:step) do |name, raw_multiline_arg = nil|
@@ -185,14 +185,13 @@ module Cucumber
185
185
  modules.each do |namespace, world_modules|
186
186
  world_modules.each do |world_module|
187
187
  variable_name = "@__#{namespace}_world"
188
+ inner_world = instance_variable_get(variable_name) || Object.new
189
+
190
+ instance_variable_set(
191
+ variable_name,
192
+ inner_world.extend(world_module)
193
+ )
188
194
 
189
- inner_world = if self.class.respond_to?(namespace)
190
- instance_variable_get(variable_name)
191
- else
192
- Object.new
193
- end
194
- instance_variable_set(variable_name,
195
- inner_world.extend(world_module))
196
195
  self.class.send(:define_method, namespace) do
197
196
  instance_variable_get(variable_name)
198
197
  end
@@ -202,6 +201,8 @@ module Cucumber
202
201
 
203
202
  # @private
204
203
  def stringify_namespaced_modules
204
+ return '' if @__namespaced_modules.nil?
205
+
205
206
  @__namespaced_modules.map { |k, v| "#{v.join(',')} (as #{k})" }.join('+')
206
207
  end
207
208
  end
@@ -92,7 +92,7 @@ module Cucumber
92
92
  step_definition
93
93
  rescue Cucumber::CucumberExpressions::UndefinedParameterTypeError => e
94
94
  # TODO: add a way to extract the parameter type directly from the error.
95
- type_name = e.message.match(/^Undefined parameter type ['|\{](.*)['|\}].?$/)[1]
95
+ type_name = e.message.match(/^Undefined parameter type ['|{](.*)['|}].?$/)[1]
96
96
 
97
97
  @configuration.notify :undefined_parameter_type, type_name, string_or_regexp
98
98
  end
@@ -100,6 +100,7 @@ module Cucumber
100
100
  def build_rb_world_factory(world_modules, namespaced_world_modules, proc)
101
101
  if proc
102
102
  raise MultipleWorld.new(@world_proc, proc) if @world_proc
103
+
103
104
  @world_proc = proc
104
105
  end
105
106
  @world_modules ||= []
@@ -135,14 +136,6 @@ module Cucumber
135
136
  @current_world = nil
136
137
  end
137
138
 
138
- def after_configuration(configuration)
139
- deprecate_after_configuration_hook if hooks[:after_configuration].any?
140
-
141
- hooks[:after_configuration].each do |hook|
142
- hook.invoke('AfterConfiguration', configuration)
143
- end
144
- end
145
-
146
139
  def install_plugin(configuration, registry)
147
140
  hooks[:install_plugin].each do |hook|
148
141
  hook.invoke('InstallPlugin', [configuration, registry])
@@ -170,7 +163,7 @@ module Cucumber
170
163
  @hooks = nil
171
164
  end
172
165
 
173
- def hooks_for(phase, scenario) #:nodoc:
166
+ def hooks_for(phase, scenario) # :nodoc:
174
167
  hooks[phase.to_sym].select { |hook| scenario.accept_hook?(hook) }
175
168
  end
176
169
 
@@ -189,6 +182,7 @@ module Cucumber
189
182
  def create_expression(string_or_regexp)
190
183
  return CucumberExpressions::CucumberExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(String)
191
184
  return CucumberExpressions::RegularExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(Regexp)
185
+
192
186
  raise ArgumentError, 'Expression must be a String or Regexp'
193
187
  end
194
188
 
@@ -228,14 +222,6 @@ module Cucumber
228
222
  def hooks
229
223
  @hooks ||= Hash.new { |h, k| h[k] = [] }
230
224
  end
231
-
232
- def deprecate_after_configuration_hook
233
- Cucumber.deprecate(
234
- 'See https://github.com/cucumber/cucumber-ruby/blob/main/UPGRADING.md#upgrading-to-710 for more info',
235
- ' AfterConfiguration hook',
236
- '8.0.0'
237
- )
238
- end
239
225
  end
240
226
 
241
227
  def self.backtrace_line(proc, name)
@@ -25,6 +25,7 @@ module Cucumber
25
25
  class << self
26
26
  def new(id, registry, string_or_regexp, proc_or_sym, options)
27
27
  raise MissingProc if proc_or_sym.nil?
28
+
28
29
  super id, registry, registry.create_expression(string_or_regexp), create_proc(proc_or_sym, options)
29
30
  end
30
31
 
@@ -33,6 +34,7 @@ module Cucumber
33
34
  def create_proc(proc_or_sym, options)
34
35
  return proc_or_sym if proc_or_sym.is_a?(Proc)
35
36
  raise ArgumentError unless proc_or_sym.is_a?(Symbol)
37
+
36
38
  message = proc_or_sym
37
39
  target_proc = parse_target_proc_from(options)
38
40
  patch_location_onto lambda { |*args|
@@ -49,6 +51,7 @@ module Cucumber
49
51
 
50
52
  def parse_target_proc_from(options)
51
53
  return -> { self } unless options.key?(:on)
54
+
52
55
  target = options[:on]
53
56
  case target
54
57
  when Proc
@@ -65,6 +68,7 @@ module Cucumber
65
68
 
66
69
  def initialize(id, registry, expression, proc)
67
70
  raise 'No regexp' if expression.is_a?(Regexp)
71
+
68
72
  @id = id
69
73
  @registry = registry
70
74
  @expression = expression
@@ -92,6 +96,7 @@ module Cucumber
92
96
 
93
97
  def expression_type
94
98
  return Cucumber::Messages::StepDefinitionPatternType::CUCUMBER_EXPRESSION if expression.is_a?(CucumberExpressions::CucumberExpression)
99
+
95
100
  Cucumber::Messages::StepDefinitionPatternType::REGULAR_EXPRESSION
96
101
  end
97
102
 
@@ -122,9 +127,7 @@ module Cucumber
122
127
 
123
128
  # @api private
124
129
  def arguments_from(step_name)
125
- args = @expression.match(step_name)
126
- # @registry.invoked_step_definition(regexp_source, location) if args
127
- args
130
+ @expression.match(step_name)
128
131
  end
129
132
 
130
133
  # @api private
@@ -19,6 +19,7 @@ module Cucumber
19
19
 
20
20
  def after_step_hook(id, test_step, location, &block)
21
21
  raise ArgumentError if test_step.hook?
22
+
22
23
  build_hook_step(id, location, block, AfterStepHook, Core::Test::Action)
23
24
  end
24
25
 
@@ -1,7 +1,7 @@
1
1
  module Cucumber
2
2
  module MultilineArgument
3
3
  class DataTable
4
- class DiffMatrices #:nodoc:
4
+ class DiffMatrices # :nodoc:
5
5
  attr_accessor :cell_matrix, :other_table_cell_matrix, :options
6
6
 
7
7
  def initialize(cell_matrix, other_table_cell_matrix, options)
@@ -113,6 +113,7 @@ module Cucumber
113
113
  row_index = row_indices.index(i)
114
114
  row = cell_matrix[row_index] if row_index
115
115
  next unless row
116
+
116
117
  (original_width..padded_width).each do |col_index|
117
118
  surplus_cell = other_row[col_index]
118
119
  row[col_index].value = surplus_cell.value if row[col_index]