cucumber 10.2.0 → 11.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +17 -8
  3. data/VERSION +1 -1
  4. data/lib/cucumber/cli/configuration.rb +1 -1
  5. data/lib/cucumber/cli/main.rb +22 -0
  6. data/lib/cucumber/cli/options.rb +3 -18
  7. data/lib/cucumber/cli/profile_loader.rb +2 -2
  8. data/lib/cucumber/cli/rerun_file.rb +1 -1
  9. data/lib/cucumber/cli.rb +3 -0
  10. data/lib/cucumber/configuration.rb +33 -24
  11. data/lib/cucumber/events/attach_called.rb +19 -0
  12. data/lib/cucumber/events/base.rb +25 -0
  13. data/lib/cucumber/events/envelope.rb +29 -2
  14. data/lib/cucumber/events/gherkin_source_parsed.rb +12 -3
  15. data/lib/cucumber/events/gherkin_source_read.rb +11 -3
  16. data/lib/cucumber/events/hook_test_step_created.rb +12 -2
  17. data/lib/cucumber/events/step_activated.rb +13 -7
  18. data/lib/cucumber/events/step_definition_registered.rb +12 -4
  19. data/lib/cucumber/events/test_case_created.rb +11 -3
  20. data/lib/cucumber/events/test_case_finished.rb +12 -4
  21. data/lib/cucumber/events/test_case_ready.rb +10 -4
  22. data/lib/cucumber/events/test_case_started.rb +11 -2
  23. data/lib/cucumber/events/test_run_finished.rb +10 -3
  24. data/lib/cucumber/events/test_run_hook_finished.rb +19 -0
  25. data/lib/cucumber/events/test_run_hook_started.rb +20 -0
  26. data/lib/cucumber/events/test_run_started.rb +11 -2
  27. data/lib/cucumber/events/test_step_created.rb +12 -2
  28. data/lib/cucumber/events/test_step_finished.rb +12 -2
  29. data/lib/cucumber/events/test_step_started.rb +11 -2
  30. data/lib/cucumber/events/undefined_parameter_type.rb +11 -3
  31. data/lib/cucumber/events.rb +3 -0
  32. data/lib/cucumber/filters/fire_before_all_hooks.rb +36 -0
  33. data/lib/cucumber/filters/randomizer.rb +5 -5
  34. data/lib/cucumber/filters/reverser.rb +41 -0
  35. data/lib/cucumber/filters.rb +1 -12
  36. data/lib/cucumber/formatter/ansicolor.rb +3 -0
  37. data/lib/cucumber/formatter/console.rb +19 -11
  38. data/lib/cucumber/formatter/console_issues.rb +5 -5
  39. data/lib/cucumber/formatter/duration_extractor.rb +2 -2
  40. data/lib/cucumber/formatter/fail_fast.rb +3 -4
  41. data/lib/cucumber/formatter/global_hooks_summary.rb +36 -0
  42. data/lib/cucumber/formatter/html.rb +6 -6
  43. data/lib/cucumber/formatter/json.rb +14 -10
  44. data/lib/cucumber/formatter/junit.rb +5 -34
  45. data/lib/cucumber/formatter/message.rb +5 -5
  46. data/lib/cucumber/formatter/message_builder.rb +255 -143
  47. data/lib/cucumber/formatter/message_handlers.rb +13 -0
  48. data/lib/cucumber/formatter/pretty.rb +7 -9
  49. data/lib/cucumber/formatter/progress.rb +2 -2
  50. data/lib/cucumber/formatter/rerun.rb +82 -31
  51. data/lib/cucumber/formatter/summary.rb +15 -1
  52. data/lib/cucumber/formatter/usage.rb +2 -3
  53. data/lib/cucumber/formatter.rb +3 -0
  54. data/lib/cucumber/glue/proto_world.rb +3 -2
  55. data/lib/cucumber/glue/registry_and_more.rb +122 -20
  56. data/lib/cucumber/glue.rb +3 -0
  57. data/lib/cucumber/multiline_argument/data_table.rb +0 -11
  58. data/lib/cucumber/platform.rb +2 -2
  59. data/lib/cucumber/query.rb +273 -0
  60. data/lib/cucumber/rake/forked_cucumber_runner.rb +57 -0
  61. data/lib/cucumber/rake/in_process_cucumber_runner.rb +27 -0
  62. data/lib/cucumber/rake/task.rb +36 -106
  63. data/lib/cucumber/rake.rb +3 -0
  64. data/lib/cucumber/repository.rb +136 -0
  65. data/lib/cucumber/rspec/disable_option_parser.rb +3 -3
  66. data/lib/cucumber/runtime/user_interface.rb +4 -2
  67. data/lib/cucumber/runtime.rb +39 -17
  68. data/lib/cucumber/step_match.rb +1 -1
  69. data/lib/cucumber.rb +2 -13
  70. metadata +35 -31
  71. data/lib/cucumber/formatter/errors.rb +0 -9
  72. data/lib/cucumber/formatter/fanout.rb +0 -28
  73. data/lib/cucumber/formatter/query/hook_by_test_step.rb +0 -34
  74. data/lib/cucumber/formatter/query/pickle_by_test.rb +0 -28
  75. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +0 -28
  76. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +0 -42
  77. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +0 -44
  78. data/lib/cucumber/formatter/query/test_run_started.rb +0 -17
  79. data/lib/cucumber/step_definition_light.rb +0 -29
@@ -1,201 +1,246 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'base64'
4
+ require 'json'
5
+
4
6
  require 'cucumber/formatter/backtrace_filter'
5
- require 'cucumber/formatter/query/hook_by_test_step'
6
- require 'cucumber/formatter/query/pickle_by_test'
7
- require 'cucumber/formatter/query/pickle_step_by_test_step'
8
- require 'cucumber/formatter/query/step_definitions_by_test_step'
9
- require 'cucumber/formatter/query/test_case_started_by_test_case'
10
- require 'cucumber/formatter/query/test_run_started'
7
+ require 'cucumber/query'
8
+
9
+ require_relative 'message_handlers'
11
10
 
12
11
  module Cucumber
13
12
  module Formatter
14
13
  class MessageBuilder
15
14
  include Cucumber::Messages::Helpers::TimeConversion
15
+ include Io
16
+ include MessageHandlers
17
+ include Console
16
18
 
17
19
  def initialize(config)
18
20
  @config = config
21
+ @ast_lookup = AstLookup.new(config)
22
+ @repository = Cucumber::Repository.new
19
23
 
20
- @hook_by_test_step = Query::HookByTestStep.new(config)
21
- @pickle_by_test = Query::PickleByTest.new(config)
22
- @pickle_step_by_test_step = Query::PickleStepByTestStep.new(config)
23
- @step_definitions_by_test_step = Query::StepDefinitionsByTestStep.new(config)
24
- @test_case_started_by_test_case = Query::TestCaseStartedByTestCase.new(config)
25
- @test_run_started = Query::TestRunStarted.new(config)
24
+ @test_run_started_id = config.test_run_started_id
26
25
 
27
- config.on_event :envelope, &method(:on_envelope)
28
- config.on_event :gherkin_source_read, &method(:on_gherkin_source_read)
26
+ # Fake Query objects
27
+ @test_case_by_step_id = {}
28
+ @pickle_id_by_test_case_id = {}
29
+ @pickle_id_step_by_test_step_id = {}
30
+ @hook_id_by_test_step_id = {}
31
+ @step_definition_ids_by_test_step_id = {}
32
+ @step_match_arguments_by_test_step_id = {}
33
+
34
+ # Ensure all handlers for events occur after all ivars are instantiated
35
+
36
+ config.on_event :gherkin_source_parsed, &method(:on_gherkin_source_parsed)
37
+
38
+ config.on_event :hook_test_step_created, &method(:on_hook_test_step_created)
39
+
40
+ config.on_event :step_activated, &method(:on_step_activated)
41
+
42
+ config.on_event :test_case_created, &method(:on_test_case_created)
29
43
  config.on_event :test_case_ready, &method(:on_test_case_ready)
30
- config.on_event :test_run_started, &method(:on_test_run_started)
31
44
  config.on_event :test_case_started, &method(:on_test_case_started)
32
- config.on_event :test_step_started, &method(:on_test_step_started)
33
- config.on_event :test_step_finished, &method(:on_test_step_finished)
34
45
  config.on_event :test_case_finished, &method(:on_test_case_finished)
46
+
47
+ config.on_event :test_run_started, &method(:on_test_run_started)
35
48
  config.on_event :test_run_finished, &method(:on_test_run_finished)
36
- config.on_event :undefined_parameter_type, &method(:on_undefined_parameter_type)
37
49
 
38
- @test_case_by_step_id = {}
39
- @current_test_run_started_id = nil
40
- @current_test_case_started_id = nil
41
- @current_test_step_id = nil
42
- end
50
+ config.on_event :test_step_created, &method(:on_test_step_created)
51
+ config.on_event :test_step_started, &method(:on_test_step_started)
52
+ config.on_event :test_step_finished, &method(:on_test_step_finished)
43
53
 
44
- def output_message
45
- raise 'To be implemented'
54
+ config.on_event :attach_called, &method(:on_attach_called)
55
+ config.on_event :envelope, &method(:on_envelope)
46
56
  end
47
57
 
48
- def attach(src, media_type, filename)
49
- attachment_data = {
50
- test_step_id: @current_test_step_id,
51
- test_case_started_id: @current_test_case_started_id,
52
- media_type: media_type,
53
- file_name: filename
54
- }
58
+ def on_envelope(event)
59
+ store_current_test_run_hook_started_id(event)
60
+ end
55
61
 
56
- if media_type&.start_with?('text/')
57
- attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY
58
- attachment_data[:body] = src
59
- else
60
- body = src.respond_to?(:read) ? src.read : src
62
+ def on_attach_called(event)
63
+ attachment_data =
64
+ if @current_test_run_hook_started_id.nil?
65
+ {
66
+ test_step_id: @current_test_step_id,
67
+ test_case_started_id: @current_test_case_started_id,
68
+ media_type: event.media_type,
69
+ file_name: event.filename,
70
+ timestamp: time_to_timestamp(Time.now)
71
+ }
72
+ else
73
+ {
74
+ test_run_hook_started_id: @current_test_run_hook_started_id,
75
+ media_type: event.media_type,
76
+ file_name: event.filename,
77
+ timestamp: time_to_timestamp(Time.now)
78
+ }
79
+ end
80
+
81
+ streamed_file = event.src.encoding == Encoding::BINARY
82
+
83
+ if streamed_file
61
84
  attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::BASE64
62
- attachment_data[:body] = Base64.strict_encode64(body)
85
+ attachment_data[:body] = Base64.strict_encode64(event.src)
86
+ else
87
+ attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY
88
+ attachment_data[:body] = event.src.is_a?(Hash) ? event.src.to_json : event.src
63
89
  end
64
90
 
65
91
  message = Cucumber::Messages::Envelope.new(attachment: Cucumber::Messages::Attachment.new(**attachment_data))
66
- output_envelope(message)
92
+ @config.event_bus.envelope(message)
67
93
  end
68
94
 
69
95
  private
70
96
 
71
- def on_envelope(event)
72
- output_envelope(event.envelope)
97
+ def on_gherkin_source_parsed(_event)
98
+ # TODO: Handle GherkinSourceParsed
73
99
  end
74
100
 
75
- def on_gherkin_source_read(event)
76
- message = Cucumber::Messages::Envelope.new(
77
- source: Cucumber::Messages::Source.new(
78
- uri: event.path,
79
- data: event.body,
80
- media_type: 'text/x.cucumber.gherkin+plain'
81
- )
82
- )
101
+ def on_hook_test_step_created(event)
102
+ @hook_id_by_test_step_id[event.test_step.id] = event.hook.id
103
+ end
83
104
 
84
- output_envelope(message)
105
+ def on_step_activated(event)
106
+ @step_definition_ids_by_test_step_id[event.test_step.id] << event.step_match.step_definition.id
107
+ @step_match_arguments_by_test_step_id[event.test_step.id] = event.step_match.step_arguments
85
108
  end
86
109
 
87
- def on_test_case_ready(event)
88
- event.test_case.test_steps.each do |step|
89
- @test_case_by_step_id[step.id] = event.test_case
90
- end
110
+ def on_test_case_created(event)
111
+ @pickle_id_by_test_case_id[event.test_case.id] = event.pickle.id
112
+ end
91
113
 
114
+ def on_test_case_ready(event)
92
115
  message = Cucumber::Messages::Envelope.new(
93
116
  test_case: Cucumber::Messages::TestCase.new(
94
117
  id: event.test_case.id,
95
- pickle_id: @pickle_by_test.pickle_id(event.test_case),
118
+ pickle_id: fake_query_pickle_id(event.test_case),
96
119
  test_steps: event.test_case.test_steps.map { |step| test_step_to_message(step) },
97
- test_run_started_id: @current_test_run_started_id
120
+ test_run_started_id: @test_run_started_id
98
121
  )
99
122
  )
100
123
 
101
- output_envelope(message)
124
+ # TODO: This may be a redundant update. But for now we're leaving this in whilst we're in the transitory phase
125
+ @repository.update(message)
126
+
127
+ @config.event_bus.envelope(message)
102
128
  end
103
129
 
104
- def test_step_to_message(step)
105
- return hook_step_to_message(step) if step.hook?
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
106
134
 
107
- Cucumber::Messages::TestStep.new(
108
- id: step.id,
109
- pickle_step_id: @pickle_step_by_test_step.pickle_step_id(step),
110
- step_definition_ids: @step_definitions_by_test_step.step_definition_ids(step),
111
- step_match_arguments_lists: step_match_arguments_lists(step)
112
- )
113
- end
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 }
114
139
 
115
- def hook_step_to_message(step)
116
- Cucumber::Messages::TestStep.new(
117
- id: step.id,
118
- hook_id: @hook_by_test_step.hook_id(step)
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
+ )
119
150
  )
120
- end
121
151
 
122
- def step_match_arguments_lists(step)
123
- match_arguments = step_match_arguments(step)
124
- [Cucumber::Messages::StepMatchArgumentsList.new(
125
- step_match_arguments: match_arguments
126
- )]
127
- rescue Cucumber::Formatter::TestStepUnknownError
128
- []
152
+ @config.event_bus.envelope(message)
153
+ @repository.update(message)
129
154
  end
130
155
 
131
- def step_match_arguments(step)
132
- @step_definitions_by_test_step.step_match_arguments(step).map do |argument|
133
- Cucumber::Messages::StepMatchArgument.new(
134
- group: argument_group_to_message(argument.group),
135
- parameter_type_name: parameter_type_name(argument)
136
- )
137
- end
138
- end
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
139
162
 
140
- def argument_group_to_message(group)
141
- Cucumber::Messages::Group.new(
142
- start: group.start,
143
- value: group.value,
144
- children: group.children.map { |child| argument_group_to_message(child) }
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
+ )
145
174
  )
146
- end
147
175
 
148
- def parameter_type_name(step_match_argument)
149
- step_match_argument.parameter_type&.name if step_match_argument.respond_to?(:parameter_type)
176
+ @config.event_bus.envelope(message)
150
177
  end
151
178
 
152
179
  def on_test_run_started(*)
153
- @current_test_run_started_id = @test_run_started.id
154
-
155
180
  message = Cucumber::Messages::Envelope.new(
156
181
  test_run_started: Cucumber::Messages::TestRunStarted.new(
157
182
  timestamp: time_to_timestamp(Time.now),
158
- id: @current_test_run_started_id
183
+ id: @test_run_started_id
159
184
  )
160
185
  )
161
186
 
162
- output_envelope(message)
187
+ @config.event_bus.envelope(message)
163
188
  end
164
189
 
165
- def on_test_case_started(event)
166
- @current_test_case_started_id = test_case_started_id(event.test_case)
167
-
190
+ def on_test_run_finished(event)
168
191
  message = Cucumber::Messages::Envelope.new(
169
- test_case_started: Cucumber::Messages::TestCaseStarted.new(
170
- id: test_case_started_id(event.test_case),
171
- test_case_id: event.test_case.id,
192
+ test_run_finished: Cucumber::Messages::TestRunFinished.new(
172
193
  timestamp: time_to_timestamp(Time.now),
173
- attempt: @test_case_started_by_test_case.attempt_by_test_case(event.test_case)
194
+ success: event.success,
195
+ test_run_started_id: @test_run_started_id
174
196
  )
175
197
  )
176
198
 
177
- output_envelope(message)
199
+ @config.event_bus.envelope(message)
200
+ end
201
+
202
+ def on_test_step_created(event)
203
+ @pickle_id_step_by_test_step_id[event.test_step.id] = event.pickle_step.id
204
+ @step_definition_ids_by_test_step_id[event.test_step.id] = []
178
205
  end
179
206
 
180
207
  def on_test_step_started(event)
181
208
  @current_test_step_id = event.test_step.id
182
- test_case = @test_case_by_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)
183
219
 
184
220
  message = Cucumber::Messages::Envelope.new(
185
221
  test_step_started: Cucumber::Messages::TestStepStarted.new(
186
222
  test_step_id: event.test_step.id,
187
- test_case_started_id: test_case_started_id(test_case),
223
+ test_case_started_id: find_test_case_started_by_test_case.id,
188
224
  timestamp: time_to_timestamp(Time.now)
189
225
  )
190
226
  )
191
227
 
192
- output_envelope(message)
228
+ @config.event_bus.envelope(message)
193
229
  end
194
230
 
195
231
  def on_test_step_finished(event)
196
- test_case = @test_case_by_step_id[event.test_step.id]
197
- result = event.result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
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 } }
198
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)
199
244
  result_message = result.to_message
200
245
  if result.failed? || result.pending?
201
246
  message_element = result.failed? ? result.exception : result
@@ -208,16 +253,105 @@ module Cucumber
208
253
  )
209
254
  end
210
255
 
256
+ output_snippet_envelope(event)
257
+
211
258
  message = Cucumber::Messages::Envelope.new(
212
259
  test_step_finished: Cucumber::Messages::TestStepFinished.new(
213
260
  test_step_id: event.test_step.id,
214
- test_case_started_id: test_case_started_id(test_case),
261
+ test_case_started_id: find_test_case_started_by_test_case.id,
215
262
  test_step_result: result_message,
216
263
  timestamp: time_to_timestamp(Time.now)
217
264
  )
218
265
  )
219
266
 
220
- output_envelope(message)
267
+ @config.event_bus.envelope(message)
268
+ end
269
+
270
+ def output_snippet_envelope(event)
271
+ return unless event.result.undefined?
272
+
273
+ collect_snippet_data(event.test_step, @ast_lookup)
274
+ snippet_text_proc = lambda do |step_keyword, step_name, multiline_arg|
275
+ snippet_text(step_keyword, step_name, multiline_arg)
276
+ end
277
+
278
+ message = generate_snippet_envelope(snippet_text_proc, event)
279
+ @config.event_bus.envelope(message)
280
+ # To ensure we don't redistribute the "same" snippets over and over again
281
+ snippets_input.clear
282
+ end
283
+
284
+ def generate_snippet_envelope(snippet_text_proc, event)
285
+ snippets_array = snippets_input.map do |data|
286
+ snippet_text_proc.call(data.actual_keyword, data.step.text, data.step.multiline_arg)
287
+ end.uniq
288
+
289
+ Cucumber::Messages::Envelope.new(
290
+ suggestion: Cucumber::Messages::Suggestion.new(
291
+ id: @config.id_generator.new_id,
292
+ pickle_step_id: @repository.test_step_by_id[event.test_step.id].pickle_step_id,
293
+ snippets: snippets_array.map { |code_snippet| Cucumber::Messages::Snippet.new(language: 'ruby', code: code_snippet) }
294
+ )
295
+ )
296
+ end
297
+
298
+ def on_undefined_parameter_type(event)
299
+ message = Cucumber::Messages::Envelope.new(
300
+ undefined_parameter_type: Cucumber::Messages::UndefinedParameterType.new(
301
+ name: event.type_name,
302
+ expression: event.expression
303
+ )
304
+ )
305
+
306
+ @config.event_bus.envelope(message)
307
+ end
308
+
309
+ def test_step_to_message(step)
310
+ return hook_step_to_message(step) if step.hook?
311
+
312
+ Cucumber::Messages::TestStep.new(
313
+ id: step.id,
314
+ pickle_step_id: @pickle_id_step_by_test_step_id[step.id],
315
+ step_definition_ids: fake_query_step_definition_ids(step),
316
+ step_match_arguments_lists: step_match_arguments_lists(step)
317
+ )
318
+ end
319
+
320
+ def hook_step_to_message(step)
321
+ Cucumber::Messages::TestStep.new(
322
+ id: step.id,
323
+ hook_id: @hook_id_by_test_step_id[step.id]
324
+ )
325
+ end
326
+
327
+ def step_match_arguments_lists(step)
328
+ match_arguments = step_match_arguments(step)
329
+ if match_arguments.nil?
330
+ []
331
+ else
332
+ [Cucumber::Messages::StepMatchArgumentsList.new(step_match_arguments: match_arguments)]
333
+ end
334
+ end
335
+
336
+ def step_match_arguments(step)
337
+ fake_query_step_match_arguments(step)&.map do |argument|
338
+ Cucumber::Messages::StepMatchArgument.new(
339
+ group: argument_group_to_message(argument.group),
340
+ parameter_type_name: parameter_type_name(argument)
341
+ )
342
+ end
343
+ end
344
+
345
+ def argument_group_to_message(group)
346
+ Cucumber::Messages::Group.new(
347
+ start: group.start,
348
+ value: group.value,
349
+ children: group.children&.map { |child| argument_group_to_message(child) }
350
+ )
351
+ end
352
+
353
+ def parameter_type_name(step_match_argument)
354
+ step_match_argument.parameter_type&.name if step_match_argument.respond_to?(:parameter_type)
221
355
  end
222
356
 
223
357
  def create_error_message(message_element)
@@ -237,42 +371,20 @@ module Cucumber
237
371
  )
238
372
  end
239
373
 
240
- def on_test_case_finished(event)
241
- message = Cucumber::Messages::Envelope.new(
242
- test_case_finished: Cucumber::Messages::TestCaseFinished.new(
243
- test_case_started_id: test_case_started_id(event.test_case),
244
- timestamp: time_to_timestamp(Time.now)
245
- )
246
- )
247
-
248
- output_envelope(message)
374
+ def fake_query_hook_id(test_step)
375
+ @hook_id_by_test_step_id.fetch(test_step.id)
249
376
  end
250
377
 
251
- def on_test_run_finished(event)
252
- message = Cucumber::Messages::Envelope.new(
253
- test_run_finished: Cucumber::Messages::TestRunFinished.new(
254
- timestamp: time_to_timestamp(Time.now),
255
- success: event.success,
256
- test_run_started_id: @current_test_run_started_id
257
- )
258
- )
259
-
260
- output_envelope(message)
378
+ def fake_query_pickle_id(test_case)
379
+ @pickle_id_by_test_case_id.fetch(test_case.id)
261
380
  end
262
381
 
263
- def on_undefined_parameter_type(event)
264
- message = Cucumber::Messages::Envelope.new(
265
- undefined_parameter_type: Cucumber::Messages::UndefinedParameterType.new(
266
- name: event.type_name,
267
- expression: event.expression
268
- )
269
- )
270
-
271
- output_envelope(message)
382
+ def fake_query_step_definition_ids(test_step)
383
+ @step_definition_ids_by_test_step_id.fetch(test_step.id)
272
384
  end
273
385
 
274
- def test_case_started_id(test_case)
275
- @test_case_started_by_test_case.test_case_started_id_by_test_case(test_case)
386
+ def fake_query_step_match_arguments(test_step)
387
+ @step_match_arguments_by_test_step_id.fetch(test_step.id, nil)
276
388
  end
277
389
  end
278
390
  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
@@ -33,8 +33,6 @@ module Cucumber
33
33
  @io = ensure_io(config.out_stream, config.error_stream)
34
34
  @config = config
35
35
  @options = config.to_hash
36
- @snippets_input = []
37
- @undefined_parameter_types = []
38
36
  @total_duration = 0
39
37
  @exceptions = []
40
38
  @gherkin_sources = {}
@@ -66,6 +64,7 @@ module Cucumber
66
64
  config.on_event :test_case_finished, &method(:on_test_case_finished)
67
65
  config.on_event :test_run_finished, &method(:on_test_run_finished)
68
66
  config.on_event :undefined_parameter_type, &method(:collect_undefined_parameter_type_names)
67
+ config.on_event :attach_called, &method(:on_attach_called)
69
68
  end
70
69
 
71
70
  def on_gherkin_source_read(event)
@@ -73,8 +72,7 @@ module Cucumber
73
72
  end
74
73
 
75
74
  def on_step_activated(event)
76
- test_step, step_match = *event.attributes
77
- @step_matches[test_step.to_s] = step_match
75
+ @step_matches[event.test_step.to_s] = event.step_match
78
76
  end
79
77
 
80
78
  def on_test_case_started(event)
@@ -140,13 +138,13 @@ module Cucumber
140
138
  print_summary
141
139
  end
142
140
 
143
- def attach(src, media_type, filename)
144
- 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'
145
143
 
146
- if filename
147
- @test_step_output.push("#{filename}: #{src}")
144
+ if event.filename
145
+ @test_step_output.push("#{event.filename}: #{event.src}")
148
146
  else
149
- @test_step_output.push(src)
147
+ @test_step_output.push(event.src)
150
148
  end
151
149
  end
152
150
 
@@ -20,8 +20,6 @@ module Cucumber
20
20
  def initialize(config)
21
21
  @config = config
22
22
  @io = ensure_io(config.out_stream, config.error_stream)
23
- @snippets_input = []
24
- @undefined_parameter_types = []
25
23
  @total_duration = 0
26
24
  @matches = {}
27
25
  @pending_step_matches = []
@@ -38,6 +36,7 @@ module Cucumber
38
36
  config.on_event :test_case_finished, &method(:on_test_case_finished)
39
37
  config.on_event :test_run_finished, &method(:on_test_run_finished)
40
38
  config.on_event :undefined_parameter_type, &method(:collect_undefined_parameter_type_names)
39
+ config.on_event :attach_called, &method(:on_attach_called)
41
40
  end
42
41
 
43
42
  def on_step_activated(event)
@@ -94,6 +93,7 @@ module Cucumber
94
93
  CHARS = {
95
94
  passed: '.',
96
95
  failed: 'F',
96
+ ambiguous: 'A',
97
97
  undefined: 'U',
98
98
  pending: 'P',
99
99
  skipped: '-'