cucumber 4.0.0.rc.1 → 4.0.0.rc.6

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 (66) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +97 -4
  3. data/CONTRIBUTING.md +1 -18
  4. data/README.md +4 -5
  5. data/lib/autotest/cucumber_mixin.rb +2 -10
  6. data/lib/cucumber.rb +1 -1
  7. data/lib/cucumber/cli/configuration.rb +1 -1
  8. data/lib/cucumber/cli/main.rb +1 -0
  9. data/lib/cucumber/cli/options.rb +18 -13
  10. data/lib/cucumber/cli/profile_loader.rb +23 -12
  11. data/lib/cucumber/configuration.rb +11 -2
  12. data/lib/cucumber/deprecate.rb +29 -5
  13. data/lib/cucumber/errors.rb +5 -2
  14. data/lib/cucumber/events.rb +12 -7
  15. data/lib/cucumber/events/envelope.rb +9 -0
  16. data/lib/cucumber/events/hook_test_step_created.rb +13 -0
  17. data/lib/cucumber/events/test_case_created.rb +13 -0
  18. data/lib/cucumber/events/test_case_ready.rb +12 -0
  19. data/lib/cucumber/events/test_step_created.rb +13 -0
  20. data/lib/cucumber/filters.rb +1 -0
  21. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  22. data/lib/cucumber/formatter/ast_lookup.rb +43 -38
  23. data/lib/cucumber/formatter/backtrace_filter.rb +4 -1
  24. data/lib/cucumber/formatter/console.rb +4 -9
  25. data/lib/cucumber/formatter/console_issues.rb +1 -1
  26. data/lib/cucumber/formatter/duration.rb +1 -1
  27. data/lib/cucumber/formatter/duration_extractor.rb +2 -0
  28. data/lib/cucumber/formatter/errors.rb +6 -0
  29. data/lib/cucumber/formatter/html.rb +24 -0
  30. data/lib/cucumber/formatter/http_io.rb +146 -0
  31. data/lib/cucumber/formatter/interceptor.rb +3 -21
  32. data/lib/cucumber/formatter/io.rb +14 -8
  33. data/lib/cucumber/formatter/json.rb +46 -36
  34. data/lib/cucumber/formatter/junit.rb +13 -11
  35. data/lib/cucumber/formatter/message.rb +22 -0
  36. data/lib/cucumber/formatter/message_builder.rb +243 -0
  37. data/lib/cucumber/formatter/pretty.rb +65 -60
  38. data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -0
  39. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  40. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  41. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  42. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +40 -0
  43. data/lib/cucumber/formatter/summary.rb +1 -1
  44. data/lib/cucumber/formatter/usage.rb +3 -3
  45. data/lib/cucumber/gherkin/data_table_parser.rb +12 -3
  46. data/lib/cucumber/gherkin/steps_parser.rb +13 -3
  47. data/lib/cucumber/glue/hook.rb +18 -2
  48. data/lib/cucumber/glue/proto_world.rb +30 -18
  49. data/lib/cucumber/glue/registry_and_more.rb +40 -3
  50. data/lib/cucumber/glue/snippet.rb +2 -2
  51. data/lib/cucumber/glue/step_definition.rb +28 -4
  52. data/lib/cucumber/hooks.rb +8 -8
  53. data/lib/cucumber/multiline_argument.rb +1 -1
  54. data/lib/cucumber/multiline_argument/data_table.rb +17 -13
  55. data/lib/cucumber/platform.rb +1 -1
  56. data/lib/cucumber/rake/task.rb +3 -0
  57. data/lib/cucumber/runtime.rb +29 -3
  58. data/lib/cucumber/runtime/after_hooks.rb +6 -2
  59. data/lib/cucumber/runtime/before_hooks.rb +6 -2
  60. data/lib/cucumber/runtime/for_programming_languages.rb +1 -0
  61. data/lib/cucumber/runtime/step_hooks.rb +3 -2
  62. data/lib/cucumber/runtime/support_code.rb +3 -3
  63. data/lib/cucumber/runtime/user_interface.rb +2 -10
  64. data/lib/cucumber/step_definitions.rb +2 -2
  65. data/lib/cucumber/version +1 -1
  66. metadata +227 -73
@@ -9,30 +9,18 @@ module Cucumber
9
9
  @pipe = pipe
10
10
  @buffer = StringIO.new
11
11
  @wrapped = true
12
+ @lock = Mutex.new
12
13
  end
13
14
 
14
15
  def write(str)
15
- lock.synchronize do
16
+ @lock.synchronize do
16
17
  @buffer << str if @wrapped
17
18
  return @pipe.write(str)
18
19
  end
19
20
  end
20
21
 
21
- # @deprecated use #buffer_string
22
- def buffer
23
- require 'cucumber/deprecate.rb'
24
- Cucumber.deprecate(
25
- 'Use Cucumber::Formatter::Interceptor::Pipe#buffer_string instead',
26
- 'Cucumber::Formatter::Interceptor::Pipe#buffer',
27
- '3.99'
28
- )
29
- lock.synchronize do
30
- return @buffer.string.lines
31
- end
32
- end
33
-
34
22
  def buffer_string
35
- lock.synchronize do
23
+ @lock.synchronize do
36
24
  return @buffer.string.dup
37
25
  end
38
26
  end
@@ -80,12 +68,6 @@ module Cucumber
80
68
  return $stdout
81
69
  end
82
70
  end
83
-
84
- private
85
-
86
- def lock
87
- @lock ||= Mutex.new
88
- end
89
71
  end
90
72
  end
91
73
  end
@@ -1,21 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'cucumber/formatter/http_io'
4
+
3
5
  module Cucumber
4
6
  module Formatter
5
7
  module Io
6
8
  module_function
7
9
 
8
- def ensure_io(path_or_io)
9
- return nil if path_or_io.nil?
10
- return path_or_io if path_or_io.respond_to?(:write)
11
- file = File.open(path_or_io, Cucumber.file_mode('w'))
10
+ def ensure_io(path_or_url_or_io)
11
+ return nil if path_or_url_or_io.nil?
12
+ return path_or_url_or_io if path_or_url_or_io.respond_to?(:write)
13
+ io = if path_or_url_or_io.match(%r{^https?://})
14
+ HTTPIO.open(path_or_url_or_io)
15
+ else
16
+ File.open(path_or_url_or_io, Cucumber.file_mode('w'))
17
+ end
12
18
  at_exit do
13
- unless file.closed?
14
- file.flush
15
- file.close
19
+ unless io.closed?
20
+ io.flush
21
+ io.close
16
22
  end
17
23
  end
18
- file
24
+ io
19
25
  end
20
26
 
21
27
  def ensure_file(path, name)
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'multi_json'
3
+ require 'json'
4
4
  require 'base64'
5
5
  require 'cucumber/formatter/backtrace_filter'
6
6
  require 'cucumber/formatter/io'
7
7
  require 'cucumber/formatter/ast_lookup'
8
+ require 'cucumber/deprecate'
8
9
 
9
10
  module Cucumber
10
11
  module Formatter
@@ -13,6 +14,14 @@ module Cucumber
13
14
  include Io
14
15
 
15
16
  def initialize(config)
17
+ Cucumber::Deprecate::CliOption.deprecate(
18
+ config.error_stream,
19
+ '--format=json',
20
+ "Please use --format=message and stand-alone json-formatter.\n" \
21
+ 'json-formatter homepage: https://github.com/cucumber/cucumber/tree/master/json-formatter#cucumber-json-formatter',
22
+ '5.0.0'
23
+ )
24
+
16
25
  @io = ensure_io(config.out_stream)
17
26
  @ast_lookup = AstLookup.new(config)
18
27
  @feature_hashes = []
@@ -77,14 +86,14 @@ module Cucumber
77
86
  end
78
87
 
79
88
  def on_test_run_finished(_event)
80
- @io.write(MultiJson.dump(@feature_hashes, pretty: true))
81
- end
82
-
83
- def puts(message)
84
- test_step_output << message
89
+ @io.write(JSON.generate(@feature_hashes, pretty: true))
85
90
  end
86
91
 
87
- def embed(src, mime_type, _label)
92
+ def attach(src, mime_type)
93
+ if mime_type == 'text/x.cucumber.log+plain'
94
+ test_step_output << src
95
+ return
96
+ end
88
97
  if File.file?(src)
89
98
  content = File.open(src, 'rb', &:read)
90
99
  data = encode64(content)
@@ -163,33 +172,34 @@ module Cucumber
163
172
  def create_step_hash(test_step)
164
173
  step_source = @ast_lookup.step_source(test_step).step
165
174
  step_hash = {
166
- keyword: step_source[:keyword],
175
+ keyword: step_source.keyword,
167
176
  name: test_step.text,
168
177
  line: test_step.location.lines.min
169
178
  }
170
- step_hash[:doc_string] = create_doc_string_hash(step_source[:doc_string]) unless step_source[:doc_string].nil?
171
- step_hash[:rows] = create_data_table_value(step_source[:data_table]) unless step_source[:data_table].nil?
179
+ step_hash[:doc_string] = create_doc_string_hash(step_source.doc_string) unless step_source.doc_string.nil?
180
+ step_hash[:rows] = create_data_table_value(step_source.data_table) unless step_source.data_table.nil?
172
181
  step_hash
173
182
  end
174
183
 
175
184
  def create_doc_string_hash(doc_string)
176
- content_type = doc_string[:content_type] || ''
185
+ content_type = doc_string.media_type || ''
177
186
  {
178
- value: doc_string[:content],
187
+ value: doc_string.content,
179
188
  content_type: content_type,
180
- line: doc_string[:location][:line]
189
+ line: doc_string.location.line
181
190
  }
182
191
  end
183
192
 
184
193
  def create_data_table_value(data_table)
185
- data_table[:rows].map do |row|
186
- { cells: row[:cells].map { |cell| cell[:value] } }
194
+ data_table.rows.map do |row|
195
+ { cells: row.cells.map(&:value) }
187
196
  end
188
197
  end
189
198
 
190
199
  def add_match_and_result(test_step, result)
191
200
  @step_or_hook_hash[:match] = create_match_hash(test_step, result)
192
201
  @step_or_hook_hash[:result] = create_result_hash(result)
202
+ result.embeddings.each { |e| embed(e['src'], e['mime_type'], e['label']) } if result.respond_to?(:embeddings)
193
203
  end
194
204
 
195
205
  def add_failed_around_hook(result)
@@ -230,9 +240,9 @@ module Cucumber
230
240
  def initialize(test_case, ast_lookup)
231
241
  @background_hash = nil
232
242
  uri = test_case.location.file
233
- feature = ast_lookup.gherkin_document(uri)[:feature]
243
+ feature = ast_lookup.gherkin_document(uri).feature
234
244
  feature(feature, uri)
235
- background(feature[:children].first[:background]) unless feature[:children].first[:background].nil?
245
+ background(feature.children.first.background) unless feature.children.first.background.nil?
236
246
  scenario(ast_lookup.scenario_source(test_case), test_case)
237
247
  end
238
248
 
@@ -242,23 +252,23 @@ module Cucumber
242
252
 
243
253
  def feature(feature, uri)
244
254
  @feature_hash = {
245
- id: create_id(feature[:name]),
255
+ id: create_id(feature.name),
246
256
  uri: uri,
247
- keyword: feature[:keyword],
248
- name: feature[:name],
249
- description: value_or_empty_string(feature[:description]),
250
- line: feature[:location][:line]
257
+ keyword: feature.keyword,
258
+ name: feature.name,
259
+ description: value_or_empty_string(feature.description),
260
+ line: feature.location.line
251
261
  }
252
- return if feature[:tags].empty?
253
- @feature_hash[:tags] = create_tags_array_from_hash_array(feature[:tags])
262
+ return if feature.tags.empty?
263
+ @feature_hash[:tags] = create_tags_array_from_hash_array(feature.tags)
254
264
  end
255
265
 
256
266
  def background(background)
257
267
  @background_hash = {
258
- keyword: background[:keyword],
259
- name: background[:name],
260
- description: value_or_empty_string(background[:description]),
261
- line: background[:location][:line],
268
+ keyword: background.keyword,
269
+ name: background.name,
270
+ description: value_or_empty_string(background.description),
271
+ line: background.location.line,
262
272
  type: 'background'
263
273
  }
264
274
  end
@@ -267,9 +277,9 @@ module Cucumber
267
277
  scenario = scenario_source.type == :Scenario ? scenario_source.scenario : scenario_source.scenario_outline
268
278
  @test_case_hash = {
269
279
  id: "#{@feature_hash[:id]};#{create_id_from_scenario_source(scenario_source)}",
270
- keyword: scenario[:keyword],
271
- name: scenario[:name],
272
- description: value_or_empty_string(scenario[:description]),
280
+ keyword: scenario.keyword,
281
+ name: test_case.name,
282
+ description: value_or_empty_string(scenario.description),
273
283
  line: test_case.location.lines.max,
274
284
  type: 'scenario'
275
285
  }
@@ -288,24 +298,24 @@ module Cucumber
288
298
 
289
299
  def create_id_from_scenario_source(scenario_source)
290
300
  if scenario_source.type == :Scenario
291
- create_id(scenario_source.scenario[:name])
301
+ create_id(scenario_source.scenario.name)
292
302
  else
293
- scenario_outline_name = scenario_source.scenario_outline[:name]
294
- examples_name = scenario_source.examples[:name]
303
+ scenario_outline_name = scenario_source.scenario_outline.name
304
+ examples_name = scenario_source.examples.name
295
305
  row_number = calculate_row_number(scenario_source)
296
306
  "#{create_id(scenario_outline_name)};#{create_id(examples_name)};#{row_number}"
297
307
  end
298
308
  end
299
309
 
300
310
  def calculate_row_number(scenario_source)
301
- scenario_source.examples[:table_body].each_with_index do |row, index|
311
+ scenario_source.examples.table_body.each_with_index do |row, index|
302
312
  return index + 2 if row == scenario_source.row
303
313
  end
304
314
  end
305
315
 
306
316
  def create_tags_array_from_hash_array(tags)
307
317
  tags_array = []
308
- tags.each { |tag| tags_array << { name: tag[:name], line: tag[:location][:line] } }
318
+ tags.each { |tag| tags_array << { name: tag.name, line: tag.location.line } }
309
319
  tags_array
310
320
  end
311
321
 
@@ -82,8 +82,8 @@ module Cucumber
82
82
 
83
83
  def start_feature(test_case)
84
84
  uri = test_case.location.file
85
- feature = @ast_lookup.gherkin_document(uri)[:feature]
86
- raise UnNamedFeatureError, uri if feature[:name].empty?
85
+ feature = @ast_lookup.gherkin_document(uri).feature
86
+ raise UnNamedFeatureError, uri if feature.name.empty?
87
87
  @current_feature_data = @features_data[uri]
88
88
  @current_feature_data[:uri] = uri unless @current_feature_data[:uri]
89
89
  @current_feature_data[:feature] = feature unless @current_feature_data[:feature]
@@ -97,8 +97,8 @@ module Cucumber
97
97
  errors: feature_data[:errors],
98
98
  skipped: feature_data[:skipped],
99
99
  tests: feature_data[:tests],
100
- time: format('%.6f', feature_data[:time]),
101
- name: feature_data[:feature][:name]
100
+ time: format('%<time>.6f', time: feature_data[:time]),
101
+ name: feature_data[:feature].name
102
102
  ) do
103
103
  @testsuite << feature_data[:builder].target!
104
104
  end
@@ -108,7 +108,7 @@ module Cucumber
108
108
 
109
109
  def create_output_string(test_case, scenario, result, row_name) # rubocop:disable Metrics/PerceivedComplexity
110
110
  scenario_source = @ast_lookup.scenario_source(test_case)
111
- keyword = scenario_source.type == :Scenario ? scenario_source.scenario[:keyword] : scenario_source.scenario_outline[:keyword]
111
+ keyword = scenario_source.type == :Scenario ? scenario_source.scenario.keyword : scenario_source.scenario_outline.keyword
112
112
  output = "#{keyword}: #{scenario}\n\n"
113
113
  return output if result.ok?(@config.strict)
114
114
  if scenario_source.type == :Scenario
@@ -117,7 +117,7 @@ module Cucumber
117
117
  output += "#{@failing_test_step.text} at #{@failing_test_step.location}\n"
118
118
  else
119
119
  step_source = @ast_lookup.step_source(@failing_test_step).step
120
- output += "#{step_source[:keyword]}#{@failing_test_step.text}\n"
120
+ output += "#{step_source.keyword}#{@failing_test_step.text}\n"
121
121
  end
122
122
  else # An Around hook has failed
123
123
  output += "Around hook\n"
@@ -131,10 +131,10 @@ module Cucumber
131
131
  def build_testcase(result, scenario_designation, output)
132
132
  duration = ResultBuilder.new(result).test_case_duration
133
133
  @current_feature_data[:time] += duration
134
- classname = @current_feature_data[:feature][:name]
134
+ classname = @current_feature_data[:feature].name
135
135
  name = scenario_designation
136
136
 
137
- @current_feature_data[:builder].testcase(classname: classname, name: name, time: format('%.6f', duration)) do
137
+ @current_feature_data[:builder].testcase(classname: classname, name: name, time: format('%<duration>.6f', duration: duration)) do
138
138
  if !result.passed? && result.ok?(@config.strict)
139
139
  @current_feature_data[:builder].skipped
140
140
  @current_feature_data[:skipped] += 1
@@ -203,15 +203,15 @@ module Cucumber
203
203
  end
204
204
 
205
205
  def scenario(scenario)
206
- @scenario_name = scenario[:name].empty? ? 'Unnamed scenario' : scenario[:name]
206
+ @scenario_name = scenario.name.empty? ? 'Unnamed scenario' : scenario.name
207
207
  end
208
208
 
209
209
  def scenario_outline(outline)
210
- @scenario_name = outline[:name].empty? ? 'Unnamed scenario outline' : outline[:name]
210
+ @scenario_name = outline.name.empty? ? 'Unnamed scenario outline' : outline.name
211
211
  end
212
212
 
213
213
  def examples_table_row(row)
214
- @row_name = '| ' + row[:cells].map { |cell| cell[:value] }.join(' | ') + ' |'
214
+ @row_name = '| ' + row.cells.map(&:value).join(' | ') + ' |'
215
215
  @name_suffix = " (outline example : #{@row_name})"
216
216
  end
217
217
  end
@@ -238,6 +238,8 @@ module Cucumber
238
238
  def duration(duration, *)
239
239
  duration.tap { |dur| @test_case_duration = dur.nanoseconds / 10**9.0 }
240
240
  end
241
+
242
+ def attach(*) end
241
243
  end
242
244
  end
243
245
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cucumber/formatter/io'
4
+ require 'cucumber/formatter/message_builder'
5
+
6
+ module Cucumber
7
+ module Formatter
8
+ # The formatter used for <tt>--format message</tt>
9
+ class Message < MessageBuilder
10
+ include Io
11
+
12
+ def initialize(config)
13
+ @io = ensure_io(config.out_stream)
14
+ super(config)
15
+ end
16
+
17
+ def output_envelope(envelope)
18
+ envelope.write_ndjson_to(@io)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,243 @@
1
+ require 'base64'
2
+ require 'cucumber/formatter/backtrace_filter'
3
+ require 'cucumber/formatter/query/hook_by_test_step'
4
+ require 'cucumber/formatter/query/pickle_by_test'
5
+ require 'cucumber/formatter/query/pickle_step_by_test_step'
6
+ require 'cucumber/formatter/query/step_definitions_by_test_step'
7
+ require 'cucumber/formatter/query/test_case_started_by_test_case'
8
+
9
+ module Cucumber
10
+ module Formatter
11
+ class MessageBuilder
12
+ include Cucumber::Messages::TimeConversion
13
+
14
+ def initialize(config)
15
+ @config = config
16
+
17
+ @hook_by_test_step = Query::HookByTestStep.new(config)
18
+ @pickle_by_test = Query::PickleByTest.new(config)
19
+ @pickle_step_by_test_step = Query::PickleStepByTestStep.new(config)
20
+ @step_definitions_by_test_step = Query::StepDefinitionsByTestStep.new(config)
21
+ @test_case_started_by_test_case = Query::TestCaseStartedByTestCase.new(config)
22
+
23
+ config.on_event :envelope, &method(:on_envelope)
24
+ config.on_event :gherkin_source_read, &method(:on_gherkin_source_read)
25
+ config.on_event :test_case_ready, &method(:on_test_case_ready)
26
+ config.on_event :test_run_started, &method(:on_test_run_started)
27
+ config.on_event :test_case_started, &method(:on_test_case_started)
28
+ config.on_event :test_step_started, &method(:on_test_step_started)
29
+ config.on_event :test_step_finished, &method(:on_test_step_finished)
30
+ config.on_event :test_case_finished, &method(:on_test_case_finished)
31
+ config.on_event :test_run_finished, &method(:on_test_run_finished)
32
+
33
+ @test_case_by_step_id = {}
34
+ @current_test_case_started_id = nil
35
+ @current_test_step_id = nil
36
+ end
37
+
38
+ def output_message
39
+ raise 'To be implemented'
40
+ end
41
+
42
+ def attach(src, media_type)
43
+ attachment_data = {
44
+ test_step_id: @current_test_step_id,
45
+ test_case_started_id: @current_test_case_started_id,
46
+ media_type: media_type
47
+ }
48
+
49
+ if media_type.start_with?('text/')
50
+ attachment_data[:content_encoding] = Cucumber::Messages::Attachment::ContentEncoding::IDENTITY
51
+ attachment_data[:body] = src
52
+ else
53
+ body = src.respond_to?(:read) ? src.read : src
54
+
55
+ attachment_data[:content_encoding] = Cucumber::Messages::Attachment::ContentEncoding::BASE64
56
+ attachment_data[:body] = Base64.strict_encode64(body)
57
+ end
58
+
59
+ message = Cucumber::Messages::Envelope.new(
60
+ attachment: Cucumber::Messages::Attachment.new(**attachment_data)
61
+ )
62
+
63
+ output_envelope(message)
64
+ end
65
+
66
+ private
67
+
68
+ def on_envelope(event)
69
+ output_envelope(event.envelope)
70
+ end
71
+
72
+ def on_gherkin_source_read(event)
73
+ message = Cucumber::Messages::Envelope.new(
74
+ source: Cucumber::Messages::Source.new(
75
+ uri: event.path,
76
+ data: event.body,
77
+ media_type: 'text/x.cucumber.gherkin+plain'
78
+ )
79
+ )
80
+
81
+ output_envelope(message)
82
+ end
83
+
84
+ def on_test_case_ready(event)
85
+ event.test_case.test_steps.each do |step|
86
+ @test_case_by_step_id[step.id] = event.test_case
87
+ end
88
+
89
+ message = Cucumber::Messages::Envelope.new(
90
+ test_case: Cucumber::Messages::TestCase.new(
91
+ id: event.test_case.id,
92
+ pickle_id: @pickle_by_test.pickle_id(event.test_case),
93
+ test_steps: event.test_case.test_steps.map { |step| test_step_to_message(step) }
94
+ )
95
+ )
96
+
97
+ output_envelope(message)
98
+ end
99
+
100
+ def test_step_to_message(step)
101
+ return hook_step_to_message(step) if step.hook?
102
+
103
+ Cucumber::Messages::TestCase::TestStep.new(
104
+ id: step.id,
105
+ pickle_step_id: @pickle_step_by_test_step.pickle_step_id(step),
106
+ step_definition_ids: @step_definitions_by_test_step.step_definition_ids(step),
107
+ step_match_arguments_lists: step_match_arguments_lists(step)
108
+ )
109
+ end
110
+
111
+ def hook_step_to_message(step)
112
+ Cucumber::Messages::TestCase::TestStep.new(
113
+ id: step.id,
114
+ hook_id: @hook_by_test_step.hook_id(step)
115
+ )
116
+ end
117
+
118
+ def step_match_arguments_lists(step)
119
+ match_arguments = step_match_arguments(step)
120
+ [Cucumber::Messages::TestCase::TestStep::StepMatchArgumentsList.new(
121
+ step_match_arguments: match_arguments
122
+ )]
123
+ rescue Cucumber::Formatter::TestStepUnknownError
124
+ []
125
+ end
126
+
127
+ def step_match_arguments(step)
128
+ @step_definitions_by_test_step.step_match_arguments(step).map do |argument|
129
+ Cucumber::Messages::TestCase::TestStep::StepMatchArgumentsList::StepMatchArgument.new(
130
+ group: argument_group_to_message(argument.group),
131
+ parameter_type_name: argument.parameter_type.name
132
+ )
133
+ end
134
+ end
135
+
136
+ def argument_group_to_message(group)
137
+ Cucumber::Messages::TestCase::TestStep::StepMatchArgumentsList::StepMatchArgument::Group.new(
138
+ start: group.start,
139
+ value: group.value,
140
+ children: group.children.map { |child| argument_group_to_message(child) }
141
+ )
142
+ end
143
+
144
+ def on_test_run_started(*)
145
+ message = Cucumber::Messages::Envelope.new(
146
+ test_run_started: Cucumber::Messages::TestRunStarted.new(
147
+ timestamp: time_to_timestamp(Time.now)
148
+ )
149
+ )
150
+
151
+ output_envelope(message)
152
+ end
153
+
154
+ def on_test_case_started(event)
155
+ @current_test_case_started_id = test_case_started_id(event.test_case)
156
+
157
+ message = Cucumber::Messages::Envelope.new(
158
+ test_case_started: Cucumber::Messages::TestCaseStarted.new(
159
+ id: test_case_started_id(event.test_case),
160
+ test_case_id: event.test_case.id,
161
+ timestamp: time_to_timestamp(Time.now),
162
+ attempt: @test_case_started_by_test_case.attempt_by_test_case(event.test_case)
163
+ )
164
+ )
165
+
166
+ output_envelope(message)
167
+ end
168
+
169
+ def on_test_step_started(event)
170
+ @current_test_step_id = event.test_step.id
171
+ test_case = @test_case_by_step_id[event.test_step.id]
172
+
173
+ message = Cucumber::Messages::Envelope.new(
174
+ test_step_started: Cucumber::Messages::TestStepStarted.new(
175
+ test_step_id: event.test_step.id,
176
+ test_case_started_id: test_case_started_id(test_case),
177
+ timestamp: time_to_timestamp(Time.now)
178
+ )
179
+ )
180
+
181
+ output_envelope(message)
182
+ end
183
+
184
+ def on_test_step_finished(event)
185
+ test_case = @test_case_by_step_id[event.test_step.id]
186
+ result = event
187
+ .result
188
+ .with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
189
+
190
+ result_message = result.to_message
191
+ if result.failed? || result.pending?
192
+ result_message = Cucumber::Messages::TestStepFinished::TestStepResult.new(
193
+ status: result_message.status,
194
+ duration: result_message.duration,
195
+ message: create_error_message(result)
196
+ )
197
+ end
198
+
199
+ message = Cucumber::Messages::Envelope.new(
200
+ test_step_finished: Cucumber::Messages::TestStepFinished.new(
201
+ test_step_id: event.test_step.id,
202
+ test_case_started_id: test_case_started_id(test_case),
203
+ test_step_result: result_message,
204
+ timestamp: time_to_timestamp(Time.now)
205
+ )
206
+ )
207
+
208
+ output_envelope(message)
209
+ end
210
+
211
+ def create_error_message(result)
212
+ message_element = result.failed? ? result.exception : result
213
+ message = "#{message_element.message} (#{message_element.class})"
214
+ ([message] + message_element.backtrace).join("\n")
215
+ end
216
+
217
+ def on_test_case_finished(event)
218
+ message = Cucumber::Messages::Envelope.new(
219
+ test_case_finished: Cucumber::Messages::TestCaseFinished.new(
220
+ test_case_started_id: test_case_started_id(event.test_case),
221
+ timestamp: time_to_timestamp(Time.now)
222
+ )
223
+ )
224
+
225
+ output_envelope(message)
226
+ end
227
+
228
+ def on_test_run_finished(*)
229
+ message = Cucumber::Messages::Envelope.new(
230
+ test_run_finished: Cucumber::Messages::TestRunFinished.new(
231
+ timestamp: time_to_timestamp(Time.now)
232
+ )
233
+ )
234
+
235
+ output_envelope(message)
236
+ end
237
+
238
+ def test_case_started_id(test_case)
239
+ @test_case_started_by_test_case.test_case_started_id_by_test_case(test_case)
240
+ end
241
+ end
242
+ end
243
+ end