cucumber 11.1.0 → 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 (58) 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 +28 -0
  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 +7 -2
  27. data/lib/cucumber/filters/retry.rb +7 -22
  28. data/lib/cucumber/formatter/console.rb +5 -5
  29. data/lib/cucumber/formatter/console_counts.rb +5 -3
  30. data/lib/cucumber/formatter/console_issues.rb +3 -3
  31. data/lib/cucumber/formatter/duration_extractor.rb +0 -2
  32. data/lib/cucumber/formatter/fail_fast.rb +2 -2
  33. data/lib/cucumber/formatter/html.rb +10 -6
  34. data/lib/cucumber/formatter/json.rb +10 -8
  35. data/lib/cucumber/formatter/junit.rb +3 -3
  36. data/lib/cucumber/formatter/message.rb +1 -5
  37. data/lib/cucumber/formatter/message_builder.rb +39 -147
  38. data/lib/cucumber/formatter/message_handlers.rb +13 -0
  39. data/lib/cucumber/formatter/pretty.rb +7 -6
  40. data/lib/cucumber/formatter/progress.rb +1 -0
  41. data/lib/cucumber/formatter/rerun.rb +15 -42
  42. data/lib/cucumber/formatter/summary.rb +15 -1
  43. data/lib/cucumber/glue/hook.rb +0 -1
  44. data/lib/cucumber/glue/multiple_world.rb +26 -0
  45. data/lib/cucumber/glue/nil_world.rb +12 -0
  46. data/lib/cucumber/glue/proto_world.rb +4 -5
  47. data/lib/cucumber/glue/registry_and_more.rb +15 -32
  48. data/lib/cucumber/multiline_argument/data_table.rb +5 -7
  49. data/lib/cucumber/retry_policy.rb +27 -0
  50. data/lib/cucumber/rspec/doubles.rb +1 -7
  51. data/lib/cucumber/runtime/meta_message_builder.rb +1 -1
  52. data/lib/cucumber/runtime/user_interface.rb +18 -3
  53. data/lib/cucumber/runtime.rb +34 -53
  54. metadata +53 -26
  55. data/lib/cucumber/events/base.rb +0 -25
  56. data/lib/cucumber/formatter/fanout.rb +0 -28
  57. data/lib/cucumber/query.rb +0 -273
  58. data/lib/cucumber/repository.rb +0 -136
@@ -22,6 +22,7 @@ module Cucumber
22
22
  config.on_event :test_step_started, &method(:on_test_step_started)
23
23
  config.on_event :test_step_finished, &method(:on_test_step_finished)
24
24
  config.on_event :test_run_finished, &method(:on_test_run_finished)
25
+ config.on_event :attach_called, &method(:on_attach_called)
25
26
  end
26
27
 
27
28
  def on_test_case_started(event)
@@ -87,18 +88,19 @@ module Cucumber
87
88
  @io.write(JSON.pretty_generate(@feature_hashes))
88
89
  end
89
90
 
90
- def attach(src, mime_type, _filename, _streamed_file)
91
- if mime_type == 'text/x.cucumber.log+plain'
92
- test_step_output << src
91
+ def on_attach_called(event)
92
+ if event.media_type == 'text/x.cucumber.log+plain'
93
+ test_step_output << event.src
93
94
  return
94
95
  end
95
- if mime_type =~ /;base64$/
96
- mime_type = mime_type[0..-8]
97
- data = src
96
+ if event.media_type =~ /;base64$/
97
+ media_type = event.media_type[0..-8]
98
+ data = event.src
98
99
  else
99
- data = encode64(src)
100
+ data = encode64(event.src)
101
+ media_type = event.media_type
100
102
  end
101
- test_step_embeddings << { mime_type: mime_type, data: data }
103
+ test_step_embeddings << { mime_type: media_type, data: data }
102
104
  end
103
105
 
104
106
  private
@@ -53,7 +53,7 @@ module Cucumber
53
53
  def on_test_step_finished(event)
54
54
  return if @failing_test_step
55
55
 
56
- @failing_test_step = event.test_step unless event.result.ok?(strict: @config.strict)
56
+ @failing_test_step = event.test_step unless event.result.ok?
57
57
  end
58
58
 
59
59
  def on_test_case_finished(event)
@@ -109,7 +109,7 @@ module Cucumber
109
109
  scenario_source = @ast_lookup.scenario_source(test_case)
110
110
  keyword = scenario_source.type == :Scenario ? scenario_source.scenario.keyword : scenario_source.scenario_outline.keyword
111
111
  output = "#{keyword}: #{scenario}\n\n"
112
- return output if result.ok?(strict: @config.strict)
112
+ return output if result.ok?
113
113
 
114
114
  if scenario_source.type == :Scenario
115
115
  if @failing_test_step
@@ -138,7 +138,7 @@ module Cucumber
138
138
  testcase_attributes = get_testcase_attributes(classname, name, duration, filename)
139
139
 
140
140
  @current_feature_data[:builder].testcase(testcase_attributes) do
141
- if !result.passed? && result.ok?(strict: @config.strict)
141
+ if !result.passed? && result.ok?
142
142
  @current_feature_data[:builder].skipped
143
143
  @current_feature_data[:skipped] += 1
144
144
  elsif !result.passed?
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cucumber/formatter/io'
4
- require 'cucumber/query'
3
+ require_relative 'io'
5
4
 
6
5
  module Cucumber
7
6
  module Formatter
@@ -11,14 +10,11 @@ module Cucumber
11
10
 
12
11
  def initialize(config)
13
12
  @io = ensure_io(config.out_stream, config.error_stream)
14
- @repository = Cucumber::Repository.new
15
- @query = Cucumber::Query.new(@repository)
16
13
  config.on_event :envelope, &method(:output_envelope)
17
14
  end
18
15
 
19
16
  def output_envelope(event)
20
17
  envelope = event.envelope
21
- @repository.update(envelope)
22
18
  @io.write(envelope.to_json)
23
19
  @io.write("\n")
24
20
  end
@@ -3,14 +3,17 @@
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
+ require_relative 'message_handlers'
10
+
9
11
  module Cucumber
10
12
  module Formatter
11
13
  class MessageBuilder
12
14
  include Cucumber::Messages::Helpers::TimeConversion
13
15
  include Io
16
+ include MessageHandlers
14
17
  include Console
15
18
 
16
19
  def initialize(config)
@@ -20,8 +23,7 @@ module Cucumber
20
23
 
21
24
  @test_run_started_id = config.test_run_started_id
22
25
 
23
- # Fake Query objects
24
- @test_case_by_step_id = {}
26
+ # Fake Query objects - used to build specific parts of the messages defined inside handlers
25
27
  @pickle_id_by_test_case_id = {}
26
28
  @pickle_id_step_by_test_step_id = {}
27
29
  @hook_id_by_test_step_id = {}
@@ -30,56 +32,60 @@ module Cucumber
30
32
 
31
33
  # Ensure all handlers for events occur after all ivars are instantiated
32
34
 
33
- config.on_event :gherkin_source_parsed, &method(:on_gherkin_source_parsed)
34
-
35
35
  config.on_event :hook_test_step_created, &method(:on_hook_test_step_created)
36
36
 
37
37
  config.on_event :step_activated, &method(:on_step_activated)
38
38
 
39
39
  config.on_event :test_case_created, &method(:on_test_case_created)
40
40
  config.on_event :test_case_ready, &method(:on_test_case_ready)
41
- config.on_event :test_case_started, &method(:on_test_case_started)
42
- config.on_event :test_case_finished, &method(:on_test_case_finished)
43
41
 
44
42
  config.on_event :test_run_started, &method(:on_test_run_started)
45
43
  config.on_event :test_run_finished, &method(:on_test_run_finished)
46
44
 
47
45
  config.on_event :test_step_created, &method(:on_test_step_created)
48
- config.on_event :test_step_started, &method(:on_test_step_started)
49
46
  config.on_event :test_step_finished, &method(:on_test_step_finished)
50
47
 
48
+ config.on_event :attach_called, &method(:on_attach_called)
51
49
  config.on_event :envelope, &method(:on_envelope)
52
50
  end
53
51
 
54
52
  def on_envelope(event)
55
- @current_test_run_hook_started_id = event.envelope.test_run_hook_started.id if event.envelope.test_run_hook_started
53
+ @repository.update(event.envelope)
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
56
60
  end
57
61
 
58
- def attach(src, media_type, filename, streamed_file)
62
+ def on_attach_called(event)
59
63
  attachment_data =
60
64
  if @current_test_run_hook_started_id.nil?
61
65
  {
62
66
  test_step_id: @current_test_step_id,
63
67
  test_case_started_id: @current_test_case_started_id,
64
- media_type: media_type,
65
- file_name: filename,
68
+ media_type: event.media_type,
69
+ file_name: event.filename,
66
70
  timestamp: time_to_timestamp(Time.now)
67
71
  }
68
72
  else
69
73
  {
70
74
  test_run_hook_started_id: @current_test_run_hook_started_id,
71
- media_type: media_type,
72
- file_name: filename,
75
+ media_type: event.media_type,
76
+ file_name: event.filename,
73
77
  timestamp: time_to_timestamp(Time.now)
74
78
  }
75
79
  end
76
80
 
81
+ streamed_file = event.src.encoding == Encoding::BINARY
82
+
77
83
  if streamed_file
78
84
  attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::BASE64
79
- attachment_data[:body] = Base64.strict_encode64(src)
85
+ attachment_data[:body] = Base64.strict_encode64(event.src)
80
86
  else
81
87
  attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY
82
- attachment_data[:body] = src.is_a?(Hash) ? src.to_json : src
88
+ attachment_data[:body] = event.src.is_a?(Hash) ? event.src.to_json : event.src
83
89
  end
84
90
 
85
91
  message = Cucumber::Messages::Envelope.new(attachment: Cucumber::Messages::Attachment.new(**attachment_data))
@@ -88,28 +94,31 @@ module Cucumber
88
94
 
89
95
  private
90
96
 
91
- def on_gherkin_source_parsed(_event)
92
- # TODO: Handle GherkinSourceParsed
93
- end
94
-
95
97
  def on_hook_test_step_created(event)
98
+ # Set iVar value for `hook_id`
96
99
  @hook_id_by_test_step_id[event.test_step.id] = event.hook.id
97
100
  end
98
101
 
99
102
  def on_step_activated(event)
103
+ # Add/Update iVar value for `step_definition_ids...`
100
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...`
101
106
  @step_match_arguments_by_test_step_id[event.test_step.id] = event.step_match.step_arguments
102
107
  end
103
108
 
104
109
  def on_test_case_created(event)
110
+ # Set iVar value for `pickle_id`
105
111
  @pickle_id_by_test_case_id[event.test_case.id] = event.pickle.id
106
112
  end
107
113
 
108
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
+
109
118
  message = Cucumber::Messages::Envelope.new(
110
119
  test_case: Cucumber::Messages::TestCase.new(
111
120
  id: event.test_case.id,
112
- pickle_id: fake_query_pickle_id(event.test_case),
121
+ pickle_id: pickle_id,
113
122
  test_steps: event.test_case.test_steps.map { |step| test_step_to_message(step) },
114
123
  test_run_started_id: @test_run_started_id
115
124
  )
@@ -121,55 +130,6 @@ module Cucumber
121
130
  @config.event_bus.envelope(message)
122
131
  end
123
132
 
124
- def on_test_case_started(event)
125
- # For any new test_case_started events, we must ALWAYS generate a new id for a new run
126
- @current_test_case_started_id = @config.id_generator.new_id
127
- @current_test_run_hook_started_id = nil
128
-
129
- find_all_test_case_started_by_test_case_id =
130
- @repository.test_case_started_by_id
131
- .values
132
- .select { |test_case_started| test_case_started.test_case_id == event.test_case.id }
133
-
134
- # If no TestCaseStarted messages exist. We must be on attempt 1 (Hence the .to_i casting for a `nil` value)
135
- attempts_previously_made = find_all_test_case_started_by_test_case_id.map(&:attempt).max.to_i
136
-
137
- message = Cucumber::Messages::Envelope.new(
138
- test_case_started: Cucumber::Messages::TestCaseStarted.new(
139
- id: @current_test_case_started_id,
140
- test_case_id: event.test_case.id,
141
- timestamp: time_to_timestamp(Time.now),
142
- attempt: attempts_previously_made + 1
143
- )
144
- )
145
-
146
- @config.event_bus.envelope(message)
147
- @repository.update(message)
148
- end
149
-
150
- def on_test_case_finished(event)
151
- test_case_started_id =
152
- @repository.test_case_started_by_id
153
- .values
154
- .detect { |test_case_started_message| test_case_started_message.test_case_id == event.test_case.id }
155
- .id
156
-
157
- test_case_started_message = @repository.test_case_started_by_id[test_case_started_id]
158
- max_attempts = @config.retry_attempts
159
- retries_attempted = test_case_started_message.attempt - 1
160
- will_be_retried = event.result.failed? && (retries_attempted < max_attempts)
161
-
162
- message = Cucumber::Messages::Envelope.new(
163
- test_case_finished: Cucumber::Messages::TestCaseFinished.new(
164
- test_case_started_id: test_case_started_id,
165
- timestamp: time_to_timestamp(Time.now),
166
- will_be_retried: will_be_retried
167
- )
168
- )
169
-
170
- @config.event_bus.envelope(message)
171
- end
172
-
173
133
  def on_test_run_started(*)
174
134
  message = Cucumber::Messages::Envelope.new(
175
135
  test_run_started: Cucumber::Messages::TestRunStarted.new(
@@ -194,71 +154,14 @@ module Cucumber
194
154
  end
195
155
 
196
156
  def on_test_step_created(event)
157
+ # Set iVar value for `pickle_id_step_...`
197
158
  @pickle_id_step_by_test_step_id[event.test_step.id] = event.pickle_step.id
159
+ # Set iVar value for `step_definition_ids...`
198
160
  @step_definition_ids_by_test_step_id[event.test_step.id] = []
199
161
  end
200
162
 
201
- def on_test_step_started(event)
202
- @current_test_step_id = event.test_step.id
203
- find_test_case_by_step_id =
204
- @repository.test_case_by_id
205
- .values
206
- .detect { |test_case_message| test_case_message.test_steps.any? { |step_message| step_message.id == event.test_step.id } }
207
-
208
- find_test_case_started_by_test_case =
209
- @repository.test_case_started_by_id
210
- .values
211
- .select { |test_case_started_message| test_case_started_message.test_case_id == find_test_case_by_step_id.id }
212
- .max_by(&:attempt)
213
-
214
- message = Cucumber::Messages::Envelope.new(
215
- test_step_started: Cucumber::Messages::TestStepStarted.new(
216
- test_step_id: event.test_step.id,
217
- test_case_started_id: find_test_case_started_by_test_case.id,
218
- timestamp: time_to_timestamp(Time.now)
219
- )
220
- )
221
-
222
- @config.event_bus.envelope(message)
223
- end
224
-
225
163
  def on_test_step_finished(event)
226
- find_test_case_by_step_id =
227
- @repository.test_case_by_id
228
- .values
229
- .detect { |test_case_message| test_case_message.test_steps.any? { |step_message| step_message.id == event.test_step.id } }
230
-
231
- find_test_case_started_by_test_case =
232
- @repository.test_case_started_by_id
233
- .values
234
- .select { |test_case_started_message| test_case_started_message.test_case_id == find_test_case_by_step_id.id }
235
- .max_by(&:attempt)
236
-
237
- result = event.result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
238
- result_message = result.to_message
239
- if result.failed? || result.pending?
240
- message_element = result.failed? ? result.exception : result
241
-
242
- result_message = Cucumber::Messages::TestStepResult.new(
243
- status: result_message.status,
244
- duration: result_message.duration,
245
- message: create_error_message(message_element),
246
- exception: create_exception_object(result, message_element)
247
- )
248
- end
249
-
250
164
  output_snippet_envelope(event)
251
-
252
- message = Cucumber::Messages::Envelope.new(
253
- test_step_finished: Cucumber::Messages::TestStepFinished.new(
254
- test_step_id: event.test_step.id,
255
- test_case_started_id: find_test_case_started_by_test_case.id,
256
- test_step_result: result_message,
257
- timestamp: time_to_timestamp(Time.now)
258
- )
259
- )
260
-
261
- @config.event_bus.envelope(message)
262
165
  end
263
166
 
264
167
  def output_snippet_envelope(event)
@@ -305,8 +208,10 @@ module Cucumber
305
208
 
306
209
  Cucumber::Messages::TestStep.new(
307
210
  id: step.id,
211
+ # Use iVar value for `pickle_id_step_...`
308
212
  pickle_step_id: @pickle_id_step_by_test_step_id[step.id],
309
- 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),
310
215
  step_match_arguments_lists: step_match_arguments_lists(step)
311
216
  )
312
217
  end
@@ -314,6 +219,7 @@ module Cucumber
314
219
  def hook_step_to_message(step)
315
220
  Cucumber::Messages::TestStep.new(
316
221
  id: step.id,
222
+ # Use iVar value for `hook_id`
317
223
  hook_id: @hook_id_by_test_step_id[step.id]
318
224
  )
319
225
  end
@@ -328,7 +234,9 @@ module Cucumber
328
234
  end
329
235
 
330
236
  def step_match_arguments(step)
331
- 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|
332
240
  Cucumber::Messages::StepMatchArgument.new(
333
241
  group: argument_group_to_message(argument.group),
334
242
  parameter_type_name: parameter_type_name(argument)
@@ -364,22 +272,6 @@ module Cucumber
364
272
  stack_trace: message_element.backtrace.join("\n")
365
273
  )
366
274
  end
367
-
368
- def fake_query_hook_id(test_step)
369
- @hook_id_by_test_step_id.fetch(test_step.id)
370
- end
371
-
372
- def fake_query_pickle_id(test_case)
373
- @pickle_id_by_test_case_id.fetch(test_case.id)
374
- end
375
-
376
- def fake_query_step_definition_ids(test_step)
377
- @step_definition_ids_by_test_step_id.fetch(test_step.id)
378
- end
379
-
380
- def fake_query_step_match_arguments(test_step)
381
- @step_match_arguments_by_test_step_id.fetch(test_step.id, nil)
382
- end
383
275
  end
384
276
  end
385
277
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Formatter
5
+ # Common Message Handlers to be used across all message-based formatters
6
+ # Designed to work solely with events of type `Envelope`
7
+ module MessageHandlers
8
+ def store_current_test_run_hook_started_id(event)
9
+ @current_test_run_hook_started_id = event.envelope.test_run_hook_started.id if event.envelope.test_run_hook_started
10
+ end
11
+ end
12
+ end
13
+ end
@@ -64,6 +64,7 @@ module Cucumber
64
64
  config.on_event :test_case_finished, &method(:on_test_case_finished)
65
65
  config.on_event :test_run_finished, &method(:on_test_run_finished)
66
66
  config.on_event :undefined_parameter_type, &method(:collect_undefined_parameter_type_names)
67
+ config.on_event :attach_called, &method(:on_attach_called)
67
68
  end
68
69
 
69
70
  def on_gherkin_source_read(event)
@@ -137,20 +138,20 @@ module Cucumber
137
138
  print_summary
138
139
  end
139
140
 
140
- def attach(src, media_type, filename, _streamed_file)
141
- return unless media_type == 'text/x.cucumber.log+plain'
141
+ def on_attach_called(event)
142
+ return unless event.media_type == 'text/x.cucumber.log+plain'
142
143
 
143
- if filename
144
- @test_step_output.push("#{filename}: #{src}")
144
+ if event.filename
145
+ @test_step_output.push("#{event.filename}: #{event.src}")
145
146
  else
146
- @test_step_output.push(src)
147
+ @test_step_output.push(event.src)
147
148
  end
148
149
  end
149
150
 
150
151
  private
151
152
 
152
153
  def find_exception_to_be_printed(result)
153
- return nil if result.ok?(strict: options[:strict])
154
+ return nil if result.ok?
154
155
 
155
156
  result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
156
157
  exception = result.failed? ? result.exception : result
@@ -36,6 +36,7 @@ module Cucumber
36
36
  config.on_event :test_case_finished, &method(:on_test_case_finished)
37
37
  config.on_event :test_run_finished, &method(:on_test_run_finished)
38
38
  config.on_event :undefined_parameter_type, &method(:collect_undefined_parameter_type_names)
39
+ config.on_event :attach_called, &method(:on_attach_called)
39
40
  end
40
41
 
41
42
  def on_step_activated(event)
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cucumber/formatter/io'
4
3
  require 'cucumber/query'
5
4
 
5
+ require_relative 'io'
6
+
6
7
  module Cucumber
7
8
  module Formatter
8
9
  class Rerun
@@ -27,42 +28,20 @@ module Cucumber
27
28
  def finish_report
28
29
  @query.find_all_test_case_started.each do |test_case|
29
30
  status = @query.find_most_severe_test_step_result_by(test_case).status
30
- # 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)
31
33
  pickle = @query.find_pickle_by(test_case)
32
34
  next if pickle.nil?
33
35
 
34
- # RULE: (Configuration specific)
35
- # -> If the test case has already been logged (And so we're retrying), we remove prior references of failures
36
- 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
37
39
  uri_and_location_hash[pickle.uri].delete(pickle.location.line)
38
40
  next
39
41
  end
40
42
 
41
- # RULE: (Configuration specific - to be amended once CCK conformance is finalised)
42
- # -> If the strict configuration permits the result - handle it accordingly
43
- if status == 'UNDEFINED' && !@config.strict.strict?(:undefined)
44
- Cucumber.deprecate(
45
- 'The strict configuration in cucumber is going away and moving towards a standardised set of behaviours',
46
- '--strict=undefined',
47
- '12.0.0'
48
- )
49
- next
50
- end
51
-
52
- if status == 'PENDING' && !@config.strict.strict?(:pending)
53
- Cucumber.deprecate(
54
- 'The strict configuration in cucumber is going away and moving towards a standardised set of behaviours',
55
- '--strict=pending',
56
- '12.0.0'
57
- )
58
- next
59
- end
60
-
61
- # RULE: Passing test cases are not considered failures (Don't log these)
62
- next if passing?(test_case)
63
-
64
- # RULE: Skipped test cases are not considered failures (on their own, don't log these)
65
- 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)
66
45
 
67
46
  # RULE: Before logging a failure, ensure we are not on a retried test case (Don't log a retry multiple times)
68
47
  next if test_case.attempt > 1
@@ -85,18 +64,12 @@ module Cucumber
85
64
  @uri_and_location_hash ||= Hash.new { |hash, key| hash[key] = Set.new }
86
65
  end
87
66
 
88
- def rerun_flaky_tests?
89
- @config.strict.strict?(:flaky)
90
- end
91
-
92
- def passing?(test_case_started)
93
- most_severe_test_step_result = @query.find_most_severe_test_step_result_by(test_case_started)
94
- most_severe_test_step_result.status == Cucumber::Messages::TestStepResultStatus::PASSED
95
- end
96
-
97
- def skipped?(test_case_started)
98
- most_severe_test_step_result = @query.find_most_severe_test_step_result_by(test_case_started)
99
- 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)
100
73
  end
101
74
  end
102
75
  end
@@ -4,12 +4,13 @@ require 'cucumber/formatter/io'
4
4
  require 'cucumber/formatter/console'
5
5
  require 'cucumber/formatter/console_counts'
6
6
  require 'cucumber/formatter/console_issues'
7
+ require 'cucumber/formatter/backtrace_filter'
7
8
  require 'cucumber/core/test/result'
8
9
  require 'cucumber/formatter/ast_lookup'
9
10
 
10
11
  module Cucumber
11
12
  module Formatter
12
- # Summary formatter, outputting only feature / scenario titles
13
+ # Summary formatter, outputting feature / scenario titles plus failure details
13
14
  class Summary
14
15
  include Io
15
16
  include Console
@@ -20,6 +21,7 @@ module Cucumber
20
21
  @ast_lookup = AstLookup.new(config)
21
22
  @counts = ConsoleCounts.new(@config)
22
23
  @issues = ConsoleIssues.new(@config, @ast_lookup)
24
+ @failed_results = []
23
25
  @start_time = Time.now
24
26
 
25
27
  @config.on_event :test_case_started do |event|
@@ -31,9 +33,14 @@ module Cucumber
31
33
  print_result event.result
32
34
  end
33
35
 
36
+ @config.on_event :test_step_finished do |event|
37
+ collect_failed_result(event.test_step, event.result)
38
+ end
39
+
34
40
  @config.on_event :test_run_finished do |_event|
35
41
  duration = Time.now - @start_time
36
42
  @io.puts
43
+ print_elements(@failed_results, :failed, 'steps')
37
44
  print_statistics(duration, @config, @counts, @issues)
38
45
  end
39
46
  end
@@ -61,6 +68,13 @@ module Cucumber
61
68
  def print_result(result)
62
69
  @io.puts format_string(result, result.to_sym)
63
70
  end
71
+
72
+ def collect_failed_result(test_step, result)
73
+ return if test_step.hook?
74
+
75
+ result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
76
+ @failed_results << result if result.failed?
77
+ end
64
78
  end
65
79
  end
66
80
  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
@@ -93,12 +93,11 @@ module Cucumber
93
93
  if File.file?(file)
94
94
  media_type = MiniMime.lookup_by_filename(file)&.content_type if media_type.nil?
95
95
  file = File.read(file, mode: 'rb')
96
- streamed_file = true
97
96
  end
98
97
  # We pass in the concept of whether the file is streamed to ensure that the envelope encoding is correct
99
- super(file, media_type, filename, streamed_file)
98
+ super(file, media_type, filename)
100
99
  rescue StandardError
101
- super(file, media_type, filename, streamed_file)
100
+ super(file, media_type, filename)
102
101
  end
103
102
 
104
103
  # Mark the matched step as pending.
@@ -155,8 +154,8 @@ module Cucumber
155
154
  runtime.ask(question, timeout_seconds)
156
155
  end
157
156
 
158
- define_method(:attach) do |file, media_type, filename, streamed_file|
159
- runtime.attach(file, media_type, filename, streamed_file)
157
+ define_method(:attach) do |file, media_type, filename|
158
+ runtime.attach(file, media_type, filename)
160
159
  end
161
160
 
162
161
  # Prints the list of modules that are included in the World