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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/cucumber/cli/configuration.rb +0 -6
- data/lib/cucumber/cli/options.rb +0 -16
- data/lib/cucumber/configuration.rb +5 -5
- data/lib/cucumber/errors.rb +43 -23
- data/lib/cucumber/events/attach_called.rb +12 -3
- data/lib/cucumber/events/envelope.rb +2 -2
- data/lib/cucumber/events/gherkin_source_parsed.rb +1 -3
- data/lib/cucumber/events/gherkin_source_read.rb +1 -1
- data/lib/cucumber/events/hook_test_step_created.rb +1 -3
- data/lib/cucumber/events/step_activated.rb +1 -1
- data/lib/cucumber/events/step_definition_registered.rb +1 -3
- data/lib/cucumber/events/test_case_created.rb +1 -1
- data/lib/cucumber/events/test_case_finished.rb +1 -1
- data/lib/cucumber/events/test_case_ready.rb +1 -1
- data/lib/cucumber/events/test_case_started.rb +1 -3
- data/lib/cucumber/events/test_run_finished.rb +1 -1
- data/lib/cucumber/events/test_run_hook_finished.rb +1 -1
- data/lib/cucumber/events/test_run_hook_started.rb +1 -3
- data/lib/cucumber/events/test_run_started.rb +1 -3
- data/lib/cucumber/events/test_step_created.rb +1 -3
- data/lib/cucumber/events/test_step_finished.rb +1 -3
- data/lib/cucumber/events/test_step_started.rb +1 -3
- data/lib/cucumber/events/undefined_parameter_type.rb +1 -1
- data/lib/cucumber/events.rb +6 -2
- data/lib/cucumber/filters/retry.rb +7 -22
- data/lib/cucumber/formatter/console_counts.rb +5 -3
- data/lib/cucumber/formatter/console_issues.rb +3 -3
- data/lib/cucumber/formatter/fail_fast.rb +2 -2
- data/lib/cucumber/formatter/html.rb +6 -4
- data/lib/cucumber/formatter/junit.rb +3 -3
- data/lib/cucumber/formatter/message_builder.rb +25 -139
- data/lib/cucumber/formatter/pretty.rb +1 -1
- data/lib/cucumber/formatter/rerun.rb +13 -41
- data/lib/cucumber/glue/hook.rb +0 -1
- data/lib/cucumber/glue/multiple_world.rb +26 -0
- data/lib/cucumber/glue/nil_world.rb +12 -0
- data/lib/cucumber/glue/registry_and_more.rb +15 -32
- data/lib/cucumber/multiline_argument/data_table.rb +5 -7
- data/lib/cucumber/retry_policy.rb +27 -0
- data/lib/cucumber/rspec/doubles.rb +1 -7
- data/lib/cucumber/runtime/meta_message_builder.rb +1 -1
- data/lib/cucumber/runtime/user_interface.rb +13 -0
- data/lib/cucumber/runtime.rb +26 -47
- metadata +51 -25
- data/lib/cucumber/events/base.rb +0 -25
- data/lib/cucumber/query.rb +0 -273
- 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:
|
|
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
|
|
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
|
-
|
|
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?
|
|
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
|
-
|
|
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: (
|
|
36
|
-
|
|
37
|
-
|
|
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:
|
|
43
|
-
|
|
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
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
data/lib/cucumber/glue/hook.rb
CHANGED
|
@@ -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
|
|
@@ -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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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/
|
|
18
|
-
|
|
19
|
-
|
|
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({
|
|
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.
|
|
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,
|
|
277
|
+
def map_column(column_name, &conversion_proc)
|
|
280
278
|
conversion_procs = @conversion_procs.dup
|
|
281
|
-
conversion_procs[column_name.to_s] = {
|
|
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)
|
|
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
|
-
|
|
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
|
|
@@ -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
|