cucumber 11.1.1 → 12.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/cucumber/cli/configuration.rb +0 -6
  4. data/lib/cucumber/cli/options.rb +0 -16
  5. data/lib/cucumber/configuration.rb +5 -5
  6. data/lib/cucumber/errors.rb +43 -23
  7. data/lib/cucumber/events/attach_called.rb +12 -3
  8. data/lib/cucumber/events/envelope.rb +2 -2
  9. data/lib/cucumber/events/gherkin_source_parsed.rb +1 -3
  10. data/lib/cucumber/events/gherkin_source_read.rb +1 -1
  11. data/lib/cucumber/events/hook_test_step_created.rb +1 -3
  12. data/lib/cucumber/events/step_activated.rb +1 -1
  13. data/lib/cucumber/events/step_definition_registered.rb +1 -3
  14. data/lib/cucumber/events/test_case_created.rb +1 -1
  15. data/lib/cucumber/events/test_case_finished.rb +1 -1
  16. data/lib/cucumber/events/test_case_ready.rb +1 -1
  17. data/lib/cucumber/events/test_case_started.rb +1 -3
  18. data/lib/cucumber/events/test_run_finished.rb +1 -1
  19. data/lib/cucumber/events/test_run_hook_finished.rb +1 -1
  20. data/lib/cucumber/events/test_run_hook_started.rb +1 -3
  21. data/lib/cucumber/events/test_run_started.rb +1 -3
  22. data/lib/cucumber/events/test_step_created.rb +1 -3
  23. data/lib/cucumber/events/test_step_finished.rb +1 -3
  24. data/lib/cucumber/events/test_step_started.rb +1 -3
  25. data/lib/cucumber/events/undefined_parameter_type.rb +1 -1
  26. data/lib/cucumber/events.rb +6 -2
  27. data/lib/cucumber/filters/retry.rb +7 -22
  28. data/lib/cucumber/formatter/console_counts.rb +5 -3
  29. data/lib/cucumber/formatter/console_issues.rb +3 -3
  30. data/lib/cucumber/formatter/fail_fast.rb +2 -2
  31. data/lib/cucumber/formatter/html.rb +6 -4
  32. data/lib/cucumber/formatter/junit.rb +3 -3
  33. data/lib/cucumber/formatter/message_builder.rb +25 -139
  34. data/lib/cucumber/formatter/pretty.rb +1 -1
  35. data/lib/cucumber/formatter/rerun.rb +13 -41
  36. data/lib/cucumber/glue/hook.rb +0 -1
  37. data/lib/cucumber/glue/multiple_world.rb +26 -0
  38. data/lib/cucumber/glue/nil_world.rb +12 -0
  39. data/lib/cucumber/glue/registry_and_more.rb +15 -32
  40. data/lib/cucumber/multiline_argument/data_table.rb +5 -7
  41. data/lib/cucumber/retry_policy.rb +27 -0
  42. data/lib/cucumber/rspec/doubles.rb +1 -7
  43. data/lib/cucumber/runtime/meta_message_builder.rb +1 -1
  44. data/lib/cucumber/runtime/user_interface.rb +13 -0
  45. data/lib/cucumber/runtime.rb +26 -47
  46. metadata +51 -25
  47. data/lib/cucumber/events/base.rb +0 -25
  48. data/lib/cucumber/query.rb +0 -273
  49. data/lib/cucumber/repository.rb +0 -136
@@ -3,9 +3,9 @@
3
3
  require 'base64'
4
4
  require 'json'
5
5
 
6
- require 'cucumber/formatter/backtrace_filter'
7
6
  require 'cucumber/query'
8
7
 
8
+ require_relative 'backtrace_filter'
9
9
  require_relative 'message_handlers'
10
10
 
11
11
  module Cucumber
@@ -23,8 +23,7 @@ module Cucumber
23
23
 
24
24
  @test_run_started_id = config.test_run_started_id
25
25
 
26
- # Fake Query objects
27
- @test_case_by_step_id = {}
26
+ # Fake Query objects - used to build specific parts of the messages defined inside handlers
28
27
  @pickle_id_by_test_case_id = {}
29
28
  @pickle_id_step_by_test_step_id = {}
30
29
  @hook_id_by_test_step_id = {}
@@ -33,22 +32,17 @@ module Cucumber
33
32
 
34
33
  # Ensure all handlers for events occur after all ivars are instantiated
35
34
 
36
- config.on_event :gherkin_source_parsed, &method(:on_gherkin_source_parsed)
37
-
38
35
  config.on_event :hook_test_step_created, &method(:on_hook_test_step_created)
39
36
 
40
37
  config.on_event :step_activated, &method(:on_step_activated)
41
38
 
42
39
  config.on_event :test_case_created, &method(:on_test_case_created)
43
40
  config.on_event :test_case_ready, &method(:on_test_case_ready)
44
- config.on_event :test_case_started, &method(:on_test_case_started)
45
- config.on_event :test_case_finished, &method(:on_test_case_finished)
46
41
 
47
42
  config.on_event :test_run_started, &method(:on_test_run_started)
48
43
  config.on_event :test_run_finished, &method(:on_test_run_finished)
49
44
 
50
45
  config.on_event :test_step_created, &method(:on_test_step_created)
51
- config.on_event :test_step_started, &method(:on_test_step_started)
52
46
  config.on_event :test_step_finished, &method(:on_test_step_finished)
53
47
 
54
48
  config.on_event :attach_called, &method(:on_attach_called)
@@ -56,7 +50,13 @@ module Cucumber
56
50
  end
57
51
 
58
52
  def on_envelope(event)
53
+ @repository.update(event.envelope)
59
54
  store_current_test_run_hook_started_id(event)
55
+ @current_test_step_id = event.envelope.test_step_started.test_step_id if event.envelope.test_step_started
56
+ return unless event.envelope.test_case_started
57
+
58
+ @current_test_case_started_id = event.envelope.test_case_started.id
59
+ @current_test_run_hook_started_id = nil
60
60
  end
61
61
 
62
62
  def on_attach_called(event)
@@ -94,28 +94,31 @@ module Cucumber
94
94
 
95
95
  private
96
96
 
97
- def on_gherkin_source_parsed(_event)
98
- # TODO: Handle GherkinSourceParsed
99
- end
100
-
101
97
  def on_hook_test_step_created(event)
98
+ # Set iVar value for `hook_id`
102
99
  @hook_id_by_test_step_id[event.test_step.id] = event.hook.id
103
100
  end
104
101
 
105
102
  def on_step_activated(event)
103
+ # Add/Update iVar value for `step_definition_ids...`
106
104
  @step_definition_ids_by_test_step_id[event.test_step.id] << event.step_match.step_definition.id
105
+ # Add/Update iVar value for `step_match_arguments...`
107
106
  @step_match_arguments_by_test_step_id[event.test_step.id] = event.step_match.step_arguments
108
107
  end
109
108
 
110
109
  def on_test_case_created(event)
110
+ # Set iVar value for `pickle_id`
111
111
  @pickle_id_by_test_case_id[event.test_case.id] = event.pickle.id
112
112
  end
113
113
 
114
114
  def on_test_case_ready(event)
115
+ # Use iVar value for `pickle_id`
116
+ pickle_id = @pickle_id_by_test_case_id.fetch(event.test_case.id)
117
+
115
118
  message = Cucumber::Messages::Envelope.new(
116
119
  test_case: Cucumber::Messages::TestCase.new(
117
120
  id: event.test_case.id,
118
- pickle_id: fake_query_pickle_id(event.test_case),
121
+ pickle_id: pickle_id,
119
122
  test_steps: event.test_case.test_steps.map { |step| test_step_to_message(step) },
120
123
  test_run_started_id: @test_run_started_id
121
124
  )
@@ -127,55 +130,6 @@ module Cucumber
127
130
  @config.event_bus.envelope(message)
128
131
  end
129
132
 
130
- def on_test_case_started(event)
131
- # For any new test_case_started events, we must ALWAYS generate a new id for a new run
132
- @current_test_case_started_id = @config.id_generator.new_id
133
- @current_test_run_hook_started_id = nil
134
-
135
- find_all_test_case_started_by_test_case_id =
136
- @repository.test_case_started_by_id
137
- .values
138
- .select { |test_case_started| test_case_started.test_case_id == event.test_case.id }
139
-
140
- # If no TestCaseStarted messages exist. We must be on attempt 1 (Hence the .to_i casting for a `nil` value)
141
- attempts_previously_made = find_all_test_case_started_by_test_case_id.map(&:attempt).max.to_i
142
-
143
- message = Cucumber::Messages::Envelope.new(
144
- test_case_started: Cucumber::Messages::TestCaseStarted.new(
145
- id: @current_test_case_started_id,
146
- test_case_id: event.test_case.id,
147
- timestamp: time_to_timestamp(Time.now),
148
- attempt: attempts_previously_made + 1
149
- )
150
- )
151
-
152
- @config.event_bus.envelope(message)
153
- @repository.update(message)
154
- end
155
-
156
- def on_test_case_finished(event)
157
- test_case_started_id =
158
- @repository.test_case_started_by_id
159
- .values
160
- .detect { |test_case_started_message| test_case_started_message.test_case_id == event.test_case.id }
161
- .id
162
-
163
- test_case_started_message = @repository.test_case_started_by_id[test_case_started_id]
164
- max_attempts = @config.retry_attempts
165
- retries_attempted = test_case_started_message.attempt - 1
166
- will_be_retried = event.result.failed? && (retries_attempted < max_attempts)
167
-
168
- message = Cucumber::Messages::Envelope.new(
169
- test_case_finished: Cucumber::Messages::TestCaseFinished.new(
170
- test_case_started_id: test_case_started_id,
171
- timestamp: time_to_timestamp(Time.now),
172
- will_be_retried: will_be_retried
173
- )
174
- )
175
-
176
- @config.event_bus.envelope(message)
177
- end
178
-
179
133
  def on_test_run_started(*)
180
134
  message = Cucumber::Messages::Envelope.new(
181
135
  test_run_started: Cucumber::Messages::TestRunStarted.new(
@@ -200,71 +154,14 @@ module Cucumber
200
154
  end
201
155
 
202
156
  def on_test_step_created(event)
157
+ # Set iVar value for `pickle_id_step_...`
203
158
  @pickle_id_step_by_test_step_id[event.test_step.id] = event.pickle_step.id
159
+ # Set iVar value for `step_definition_ids...`
204
160
  @step_definition_ids_by_test_step_id[event.test_step.id] = []
205
161
  end
206
162
 
207
- def on_test_step_started(event)
208
- @current_test_step_id = event.test_step.id
209
- find_test_case_by_step_id =
210
- @repository.test_case_by_id
211
- .values
212
- .detect { |test_case_message| test_case_message.test_steps.any? { |step_message| step_message.id == event.test_step.id } }
213
-
214
- find_test_case_started_by_test_case =
215
- @repository.test_case_started_by_id
216
- .values
217
- .select { |test_case_started_message| test_case_started_message.test_case_id == find_test_case_by_step_id.id }
218
- .max_by(&:attempt)
219
-
220
- message = Cucumber::Messages::Envelope.new(
221
- test_step_started: Cucumber::Messages::TestStepStarted.new(
222
- test_step_id: event.test_step.id,
223
- test_case_started_id: find_test_case_started_by_test_case.id,
224
- timestamp: time_to_timestamp(Time.now)
225
- )
226
- )
227
-
228
- @config.event_bus.envelope(message)
229
- end
230
-
231
163
  def on_test_step_finished(event)
232
- find_test_case_by_step_id =
233
- @repository.test_case_by_id
234
- .values
235
- .detect { |test_case_message| test_case_message.test_steps.any? { |step_message| step_message.id == event.test_step.id } }
236
-
237
- find_test_case_started_by_test_case =
238
- @repository.test_case_started_by_id
239
- .values
240
- .select { |test_case_started_message| test_case_started_message.test_case_id == find_test_case_by_step_id.id }
241
- .max_by(&:attempt)
242
-
243
- result = event.result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
244
- result_message = result.to_message
245
- if result.failed? || result.pending?
246
- message_element = result.failed? ? result.exception : result
247
-
248
- result_message = Cucumber::Messages::TestStepResult.new(
249
- status: result_message.status,
250
- duration: result_message.duration,
251
- message: create_error_message(message_element),
252
- exception: create_exception_object(result, message_element)
253
- )
254
- end
255
-
256
164
  output_snippet_envelope(event)
257
-
258
- message = Cucumber::Messages::Envelope.new(
259
- test_step_finished: Cucumber::Messages::TestStepFinished.new(
260
- test_step_id: event.test_step.id,
261
- test_case_started_id: find_test_case_started_by_test_case.id,
262
- test_step_result: result_message,
263
- timestamp: time_to_timestamp(Time.now)
264
- )
265
- )
266
-
267
- @config.event_bus.envelope(message)
268
165
  end
269
166
 
270
167
  def output_snippet_envelope(event)
@@ -311,8 +208,10 @@ module Cucumber
311
208
 
312
209
  Cucumber::Messages::TestStep.new(
313
210
  id: step.id,
211
+ # Use iVar value for `pickle_id_step_...`
314
212
  pickle_step_id: @pickle_id_step_by_test_step_id[step.id],
315
- step_definition_ids: fake_query_step_definition_ids(step),
213
+ # Use iVar value for `step_definition_ids...`
214
+ step_definition_ids: @step_definition_ids_by_test_step_id.fetch(step.id),
316
215
  step_match_arguments_lists: step_match_arguments_lists(step)
317
216
  )
318
217
  end
@@ -320,6 +219,7 @@ module Cucumber
320
219
  def hook_step_to_message(step)
321
220
  Cucumber::Messages::TestStep.new(
322
221
  id: step.id,
222
+ # Use iVar value for `hook_id`
323
223
  hook_id: @hook_id_by_test_step_id[step.id]
324
224
  )
325
225
  end
@@ -334,7 +234,9 @@ module Cucumber
334
234
  end
335
235
 
336
236
  def step_match_arguments(step)
337
- fake_query_step_match_arguments(step)&.map do |argument|
237
+ # Use iVar value for `step_match_arguments...`
238
+ step_matches = @step_match_arguments_by_test_step_id[step.id]
239
+ step_matches&.map do |argument|
338
240
  Cucumber::Messages::StepMatchArgument.new(
339
241
  group: argument_group_to_message(argument.group),
340
242
  parameter_type_name: parameter_type_name(argument)
@@ -370,22 +272,6 @@ module Cucumber
370
272
  stack_trace: message_element.backtrace.join("\n")
371
273
  )
372
274
  end
373
-
374
- def fake_query_hook_id(test_step)
375
- @hook_id_by_test_step_id.fetch(test_step.id)
376
- end
377
-
378
- def fake_query_pickle_id(test_case)
379
- @pickle_id_by_test_case_id.fetch(test_case.id)
380
- end
381
-
382
- def fake_query_step_definition_ids(test_step)
383
- @step_definition_ids_by_test_step_id.fetch(test_step.id)
384
- end
385
-
386
- def fake_query_step_match_arguments(test_step)
387
- @step_match_arguments_by_test_step_id.fetch(test_step.id, nil)
388
- end
389
275
  end
390
276
  end
391
277
  end
@@ -151,7 +151,7 @@ module Cucumber
151
151
  private
152
152
 
153
153
  def find_exception_to_be_printed(result)
154
- return nil if result.ok?(strict: options[:strict])
154
+ return nil if result.ok?
155
155
 
156
156
  result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
157
157
  exception = result.failed? ? result.exception : result
@@ -28,42 +28,20 @@ module Cucumber
28
28
  def finish_report
29
29
  @query.find_all_test_case_started.each do |test_case|
30
30
  status = @query.find_most_severe_test_step_result_by(test_case).status
31
- # RULE: Don't log test cases without a pickle (Unsure what these could be?)
31
+
32
+ # RULE: Don't log test cases without a pickle (We cannot query their location data to log them)
32
33
  pickle = @query.find_pickle_by(test_case)
33
34
  next if pickle.nil?
34
35
 
35
- # RULE: (Configuration specific)
36
- # -> If the test case has already been logged (And so we're retrying), we remove prior references of failures
37
- if passing?(test_case) && !rerun_flaky_tests?
36
+ # RULE: Test cases with the worst result as Passing is not considered a failure (Don't log these)
37
+ if status == Cucumber::Messages::TestStepResultStatus::PASSED
38
+ # If the test case in question had already been logged as a failure (And we're retrying), remove the prior reference of failure
38
39
  uri_and_location_hash[pickle.uri].delete(pickle.location.line)
39
40
  next
40
41
  end
41
42
 
42
- # RULE: (Configuration specific - to be amended once CCK conformance is finalised)
43
- # -> If the strict configuration permits the result - handle it accordingly
44
- if status == 'UNDEFINED' && !@config.strict.strict?(:undefined)
45
- Cucumber.deprecate(
46
- 'The strict configuration in cucumber is going away and moving towards a standardised set of behaviours',
47
- '--strict=undefined',
48
- '12.0.0'
49
- )
50
- next
51
- end
52
-
53
- if status == 'PENDING' && !@config.strict.strict?(:pending)
54
- Cucumber.deprecate(
55
- 'The strict configuration in cucumber is going away and moving towards a standardised set of behaviours',
56
- '--strict=pending',
57
- '12.0.0'
58
- )
59
- next
60
- end
61
-
62
- # RULE: Passing test cases are not considered failures (Don't log these)
63
- next if passing?(test_case)
64
-
65
- # RULE: Skipped test cases are not considered failures (on their own, don't log these)
66
- next if skipped?(test_case)
43
+ # RULE: Test cases with the worst result as Skipped/Pending/Undefined are not considered failures (don't log these)
44
+ next if non_rerunnable_status?(status)
67
45
 
68
46
  # RULE: Before logging a failure, ensure we are not on a retried test case (Don't log a retry multiple times)
69
47
  next if test_case.attempt > 1
@@ -86,18 +64,12 @@ module Cucumber
86
64
  @uri_and_location_hash ||= Hash.new { |hash, key| hash[key] = Set.new }
87
65
  end
88
66
 
89
- def rerun_flaky_tests?
90
- @config.strict.strict?(:flaky)
91
- end
92
-
93
- def passing?(test_case_started)
94
- most_severe_test_step_result = @query.find_most_severe_test_step_result_by(test_case_started)
95
- most_severe_test_step_result.status == Cucumber::Messages::TestStepResultStatus::PASSED
96
- end
97
-
98
- def skipped?(test_case_started)
99
- most_severe_test_step_result = @query.find_most_severe_test_step_result_by(test_case_started)
100
- most_severe_test_step_result.status == Cucumber::Messages::TestStepResultStatus::SKIPPED
67
+ def non_rerunnable_status?(status)
68
+ [
69
+ Cucumber::Messages::TestStepResultStatus::SKIPPED,
70
+ Cucumber::Messages::TestStepResultStatus::PENDING,
71
+ Cucumber::Messages::TestStepResultStatus::UNDEFINED
72
+ ].include?(status)
101
73
  end
102
74
  end
103
75
  end
@@ -4,7 +4,6 @@ require 'cucumber/glue/invoke_in_world'
4
4
 
5
5
  module Cucumber
6
6
  module Glue
7
- # TODO: Kill pointless wrapper for Before, After and AfterStep hooks with fire
8
7
  class Hook
9
8
  attr_reader :id, :tag_expressions, :location, :name
10
9
 
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Glue
5
+ # Raised if there are 2 or more World blocks.
6
+ class MultipleWorld < StandardError
7
+ def initialize(first_proc, second_proc)
8
+ super(error_message(first_proc, second_proc))
9
+ end
10
+
11
+ def error_message(first_proc, second_proc)
12
+ <<~MESSAGE
13
+ You can only pass a proc to #World once, but it's happening
14
+ in 2 places:
15
+ #{Glue.backtrace_line(first_proc, 'World')}
16
+ #{Glue.backtrace_line(second_proc, 'World')}
17
+ Use Ruby modules instead to extend your worlds. See the Cucumber::Glue::Dsl#World RDoc
18
+ or http://wiki.github.com/cucumber/cucumber/a-whole-new-world.
19
+
20
+
21
+ MESSAGE
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Glue
5
+ # Raised if a World block returns Nil.
6
+ class NilWorld < StandardError
7
+ def initialize
8
+ super('World procs should never return nil')
9
+ end
10
+ end
11
+ end
12
+ end
@@ -6,17 +6,22 @@ require 'cucumber/cucumber_expressions/regular_expression'
6
6
  require 'cucumber/cucumber_expressions/cucumber_expression_generator'
7
7
  require 'cucumber/messages/helpers/time_conversion'
8
8
 
9
- require 'cucumber/glue/dsl'
10
- require 'cucumber/glue/snippet'
11
- require 'cucumber/glue/hook'
12
- require 'cucumber/glue/proto_world'
13
- require 'cucumber/glue/step_definition'
14
- require 'cucumber/glue/world_factory'
15
- require 'cucumber/gherkin/i18n'
9
+ require_relative 'dsl'
10
+ require_relative 'snippet'
11
+ require_relative 'hook'
12
+ require_relative 'proto_world'
13
+ require_relative 'step_definition'
14
+ require_relative 'world_factory'
15
+ require_relative '../step_match'
16
+ require_relative '../gherkin/i18n'
17
+
16
18
  require 'multi_test'
17
- require 'cucumber/step_match'
18
- require 'cucumber/events/base'
19
- require 'cucumber/events/step_definition_registered'
19
+ require 'cucumber/core/event/base'
20
+
21
+ require_relative '../events/step_definition_registered'
22
+
23
+ require_relative 'multiple_world'
24
+ require_relative 'nil_world'
20
25
 
21
26
  module Cucumber
22
27
  module Glue
@@ -25,28 +30,6 @@ module Cucumber
25
30
  "#{location}:in `#{name}'"
26
31
  end
27
32
 
28
- # Raised if a World block returns Nil.
29
- class NilWorld < StandardError
30
- def initialize
31
- super('World procs should never return nil')
32
- end
33
- end
34
-
35
- # Raised if there are 2 or more World blocks.
36
- class MultipleWorld < StandardError
37
- def initialize(first_proc, second_proc)
38
- # TODO: [LH] - Just use a heredoc here to fix this up
39
- message = String.new
40
- message << "You can only pass a proc to #World once, but it's happening\n"
41
- message << "in 2 places:\n\n"
42
- message << Glue.backtrace_line(first_proc, 'World') << "\n"
43
- message << Glue.backtrace_line(second_proc, 'World') << "\n\n"
44
- message << "Use Ruby modules instead to extend your worlds. See the Cucumber::Glue::Dsl#World RDoc\n"
45
- message << "or http://wiki.github.com/cucumber/cucumber/a-whole-new-world.\n\n"
46
- super(message)
47
- end
48
- end
49
-
50
33
  # TODO: This class has too many responsibilities, split off
51
34
  class RegistryAndMore
52
35
  attr_reader :current_world, :step_definitions
@@ -77,7 +77,7 @@ module Cucumber
77
77
 
78
78
  # This is a Hash being initialized with a default value of a Hash
79
79
  # DO NOT REFORMAT TO REMOVE {} - Ruby 3.4+ will interpret these as keywords and cucumber will not work
80
- NULL_CONVERSIONS = Hash.new({ strict: false, proc: ->(cell_value) { cell_value } }).freeze
80
+ NULL_CONVERSIONS = Hash.new({ proc: ->(cell_value) { cell_value } }).freeze
81
81
 
82
82
  # @param data [Core::Test::DataTable] the data for the table
83
83
  # @param conversion_procs [Hash] see map_column
@@ -265,9 +265,7 @@ module Cucumber
265
265
  # Returns a new Table with an additional column mapping.
266
266
  #
267
267
  # Change how #hashes converts column values. The +column_name+ argument identifies the column
268
- # and +conversion_proc+ performs the conversion for each cell in that column. If +strict+ is
269
- # true, an error will be raised if the column named +column_name+ is not found. If +strict+
270
- # is false, no error will be raised. Example:
268
+ # and +conversion_proc+ performs the conversion for each cell in that column.
271
269
  #
272
270
  # Given /^an expense report for (.*) with the following posts:$/ do |table|
273
271
  # posts_table = posts_table.map_column('amount') { |a| a.to_i }
@@ -276,9 +274,9 @@ module Cucumber
276
274
  # end
277
275
  # end
278
276
  #
279
- def map_column(column_name, strict: true, &conversion_proc)
277
+ def map_column(column_name, &conversion_proc)
280
278
  conversion_procs = @conversion_procs.dup
281
- conversion_procs[column_name.to_s] = { strict: strict, proc: conversion_proc }
279
+ conversion_procs[column_name.to_s] = { proc: conversion_proc }
282
280
  self.class.new(Core::Test::DataTable.new(raw), conversion_procs, @header_mappings.dup, @header_conversion_proc)
283
281
  end
284
282
 
@@ -467,7 +465,7 @@ module Cucumber
467
465
 
468
466
  def convert_columns!
469
467
  @conversion_procs.each do |column_name, conversion_proc|
470
- verify_column(column_name) if conversion_proc[:strict]
468
+ verify_column(column_name)
471
469
  end
472
470
 
473
471
  cell_matrix.transpose.each do |col|
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ # Decides whether a failed test case is run again, honouring both the `--retry` and the `--retry-total` options.
5
+ #
6
+ # The retry filter asks it and then records what it did, the test runner asks it when it reports the outcome of a
7
+ # test case. Both have to get the same answer, so #will_be_retried? never changes the state of the policy.
8
+ class RetryPolicy
9
+ def initialize(max_retries, max_permanent_failures)
10
+ @max_retries = max_retries
11
+ @max_permanent_failures = max_permanent_failures
12
+ @retries = Hash.new(0)
13
+ @permanent_failures = 0
14
+ end
15
+
16
+ def will_be_retried?(test_case, result)
17
+ return false unless result.failed?
18
+ return false if @permanent_failures >= @max_permanent_failures
19
+
20
+ @retries[test_case] < @max_retries
21
+ end
22
+
23
+ def record_retry(test_case) = @retries[test_case] += 1
24
+
25
+ def record_permanent_failure = @permanent_failures += 1
26
+ end
27
+ end
@@ -5,15 +5,9 @@ require 'rspec/mocks'
5
5
  World(RSpec::Mocks::ExampleMethods)
6
6
 
7
7
  Before do
8
- if RSpec::Mocks::Version::STRING >= '2.9.9'
9
- RSpec::Mocks.setup
10
- else
11
- RSpec::Mocks.setup(self)
12
- end
8
+ RSpec::Mocks.setup(self)
13
9
  end
14
10
 
15
11
  After do
16
- RSpec::Mocks.verify
17
- ensure
18
12
  RSpec::Mocks.teardown
19
13
  end
@@ -70,7 +70,7 @@ module Cucumber
70
70
  def os
71
71
  Cucumber::Messages::Product.new(
72
72
  name: RbConfig::CONFIG['target_os'],
73
- version: Sys::Uname.uname.version
73
+ version: Sys::Uname.version
74
74
  )
75
75
  end
76
76
 
@@ -22,6 +22,7 @@ module Cucumber
22
22
  # that makes a sound before invoking #ask.
23
23
  #
24
24
  def ask(question, timeout_seconds)
25
+ Cucumber.deprecate(ask_deprecation_message, 'Cucumber::Runtime#ask (From UserInterface Module)', 'v12')
25
26
  $stdout.puts(question)
26
27
  $stdout.flush
27
28
  puts(question)
@@ -49,6 +50,18 @@ module Cucumber
49
50
 
50
51
  private
51
52
 
53
+ def ask_deprecation_message
54
+ <<~MESSAGE
55
+ The `#ask` method has been present in cucumber for a while, but its purpose outside of cucumber is
56
+ questionable. It is being deprecated and will be removed in a future version (Targeting v12).
57
+
58
+ If you are using it in your own code, you should remove the dependency on it and implement your own version
59
+ of it. If you are using it in a step definition, you should consider whether that is really necessary and
60
+ use either a conditional waiter or use a tag so that the scenario doesn't automatically run in a CI
61
+ environment where there is no operator to answer the question.
62
+ MESSAGE
63
+ end
64
+
52
65
  def mri_gets(timeout_seconds)
53
66
  Timeout.timeout(timeout_seconds) do
54
67
  $stdin.gets