cucumber 7.1.0 → 8.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
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]