cucumber 3.1.2 → 4.0.0.rc.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -13
  3. data/CONTRIBUTING.md +1 -0
  4. data/bin/cucumber +1 -1
  5. data/lib/autotest/cucumber_mixin.rb +42 -39
  6. data/lib/cucumber/cli/configuration.rb +4 -4
  7. data/lib/cucumber/cli/main.rb +11 -12
  8. data/lib/cucumber/cli/options.rb +53 -62
  9. data/lib/cucumber/cli/profile_loader.rb +32 -20
  10. data/lib/cucumber/configuration.rb +20 -21
  11. data/lib/cucumber/constantize.rb +2 -5
  12. data/lib/cucumber/deprecate.rb +5 -5
  13. data/lib/cucumber/errors.rb +4 -6
  14. data/lib/cucumber/events.rb +1 -0
  15. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  16. data/lib/cucumber/events/step_activated.rb +2 -1
  17. data/lib/cucumber/file_specs.rb +6 -6
  18. data/lib/cucumber/filters/activate_steps.rb +5 -3
  19. data/lib/cucumber/filters/prepare_world.rb +5 -9
  20. data/lib/cucumber/filters/quit.rb +1 -3
  21. data/lib/cucumber/filters/tag_limits/verifier.rb +2 -4
  22. data/lib/cucumber/formatter/ansicolor.rb +40 -45
  23. data/lib/cucumber/formatter/ast_lookup.rb +160 -0
  24. data/lib/cucumber/formatter/backtrace_filter.rb +5 -7
  25. data/lib/cucumber/formatter/console.rb +28 -59
  26. data/lib/cucumber/formatter/console_counts.rb +4 -9
  27. data/lib/cucumber/formatter/console_issues.rb +6 -3
  28. data/lib/cucumber/formatter/duration_extractor.rb +1 -1
  29. data/lib/cucumber/formatter/fanout.rb +2 -0
  30. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  31. data/lib/cucumber/formatter/interceptor.rb +5 -7
  32. data/lib/cucumber/formatter/io.rb +3 -3
  33. data/lib/cucumber/formatter/json.rb +92 -110
  34. data/lib/cucumber/formatter/junit.rb +55 -57
  35. data/lib/cucumber/formatter/pretty.rb +346 -152
  36. data/lib/cucumber/formatter/progress.rb +28 -32
  37. data/lib/cucumber/formatter/rerun.rb +22 -4
  38. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  39. data/lib/cucumber/formatter/steps.rb +2 -3
  40. data/lib/cucumber/formatter/summary.rb +16 -8
  41. data/lib/cucumber/formatter/unicode.rb +15 -17
  42. data/lib/cucumber/formatter/usage.rb +9 -8
  43. data/lib/cucumber/gherkin/data_table_parser.rb +8 -6
  44. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
  45. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  46. data/lib/cucumber/gherkin/steps_parser.rb +7 -8
  47. data/lib/cucumber/glue/dsl.rb +1 -1
  48. data/lib/cucumber/glue/hook.rb +16 -9
  49. data/lib/cucumber/glue/invoke_in_world.rb +13 -18
  50. data/lib/cucumber/glue/proto_world.rb +14 -16
  51. data/lib/cucumber/glue/registry_and_more.rb +7 -9
  52. data/lib/cucumber/glue/snippet.rb +21 -20
  53. data/lib/cucumber/glue/step_definition.rb +14 -15
  54. data/lib/cucumber/glue/world_factory.rb +1 -1
  55. data/lib/cucumber/hooks.rb +11 -11
  56. data/lib/cucumber/multiline_argument.rb +4 -6
  57. data/lib/cucumber/multiline_argument/data_table.rb +88 -59
  58. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +1 -1
  59. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  60. data/lib/cucumber/platform.rb +3 -3
  61. data/lib/cucumber/rake/task.rb +13 -16
  62. data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
  63. data/lib/cucumber/running_test_case.rb +2 -53
  64. data/lib/cucumber/runtime.rb +27 -57
  65. data/lib/cucumber/runtime/after_hooks.rb +3 -3
  66. data/lib/cucumber/runtime/before_hooks.rb +3 -3
  67. data/lib/cucumber/runtime/for_programming_languages.rb +3 -2
  68. data/lib/cucumber/runtime/step_hooks.rb +1 -1
  69. data/lib/cucumber/runtime/support_code.rb +10 -12
  70. data/lib/cucumber/runtime/user_interface.rb +4 -6
  71. data/lib/cucumber/step_definition_light.rb +4 -3
  72. data/lib/cucumber/step_match.rb +12 -11
  73. data/lib/cucumber/step_match_search.rb +2 -1
  74. data/lib/cucumber/term/ansicolor.rb +9 -9
  75. data/lib/cucumber/version +1 -1
  76. metadata +36 -29
  77. data/lib/cucumber/events/gherkin_source_parsed.rb~ +0 -14
  78. data/lib/cucumber/formatter/ast_lookup.rb~ +0 -9
  79. data/lib/cucumber/formatter/cucumber.css +0 -286
  80. data/lib/cucumber/formatter/cucumber.sass +0 -247
  81. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  82. data/lib/cucumber/formatter/html.rb +0 -611
  83. data/lib/cucumber/formatter/html_builder.rb +0 -121
  84. data/lib/cucumber/formatter/inline-js.js +0 -30
  85. data/lib/cucumber/formatter/jquery-min.js +0 -154
  86. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  87. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  88. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  89. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  90. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  91. data/lib/cucumber/step_argument.rb +0 -25
@@ -19,14 +19,14 @@ module Cucumber
19
19
  end
20
20
 
21
21
  def ensure_file(path, name)
22
- raise "You *must* specify --out FILE for the #{name} formatter" unless String === path
22
+ raise "You *must* specify --out FILE for the #{name} formatter" unless String == path.class
23
23
  raise "I can't write #{name} to a directory - it has to be a file" if File.directory?(path)
24
- raise "I can't write #{name} to a file in the non-existing directory #{File.dirname(path)}" if !File.directory?(File.dirname(path))
24
+ raise "I can't write #{name} to a file in the non-existing directory #{File.dirname(path)}" unless File.directory?(File.dirname(path))
25
25
  ensure_io(path)
26
26
  end
27
27
 
28
28
  def ensure_dir(path, name)
29
- raise "You *must* specify --out DIR for the #{name} formatter" unless String === path
29
+ raise "You *must* specify --out DIR for the #{name} formatter" unless String == path.class
30
30
  raise "I can't write #{name} reports to a file - it has to be a directory" if File.file?(path)
31
31
  FileUtils.mkdir_p(path) unless File.directory?(path)
32
32
  File.absolute_path path
@@ -4,7 +4,7 @@ require 'multi_json'
4
4
  require 'base64'
5
5
  require 'cucumber/formatter/backtrace_filter'
6
6
  require 'cucumber/formatter/io'
7
- require 'cucumber/formatter/hook_query_visitor'
7
+ require 'cucumber/formatter/ast_lookup'
8
8
 
9
9
  module Cucumber
10
10
  module Formatter
@@ -14,6 +14,7 @@ module Cucumber
14
14
 
15
15
  def initialize(config)
16
16
  @io = ensure_io(config.out_stream)
17
+ @ast_lookup = AstLookup.new(config)
17
18
  @feature_hashes = []
18
19
  @step_or_hook_hash = {}
19
20
  config.on_event :test_case_started, &method(:on_test_case_started)
@@ -25,16 +26,18 @@ module Cucumber
25
26
 
26
27
  def on_test_case_started(event)
27
28
  test_case = event.test_case
28
- builder = Builder.new(test_case)
29
- unless same_feature_as_previous_test_case?(test_case.feature)
29
+ builder = Builder.new(test_case, @ast_lookup)
30
+ unless same_feature_as_previous_test_case?(test_case)
30
31
  @feature_hash = builder.feature_hash
31
32
  @feature_hashes << @feature_hash
32
33
  end
33
34
  @test_case_hash = builder.test_case_hash
34
35
  if builder.background?
36
+ @in_background = true
35
37
  feature_elements << builder.background_hash
36
38
  @element_hash = builder.background_hash
37
39
  else
40
+ @in_background = false
38
41
  feature_elements << @test_case_hash
39
42
  @element_hash = @test_case_hash
40
43
  end
@@ -44,17 +47,17 @@ module Cucumber
44
47
  def on_test_step_started(event)
45
48
  test_step = event.test_step
46
49
  return if internal_hook?(test_step)
47
- hook_query = HookQueryVisitor.new(test_step)
48
- if hook_query.hook?
50
+ if test_step.hook?
49
51
  @step_or_hook_hash = {}
50
- hooks_of_type(hook_query) << @step_or_hook_hash
52
+ hooks_of_type(test_step) << @step_or_hook_hash
51
53
  return
52
54
  end
53
55
  if first_step_after_background?(test_step)
56
+ @in_background = false
54
57
  feature_elements << @test_case_hash
55
58
  @element_hash = @test_case_hash
56
59
  end
57
- @step_or_hook_hash = create_step_hash(test_step.source.last)
60
+ @step_or_hook_hash = create_step_hash(test_step)
58
61
  steps << @step_or_hook_hash
59
62
  @step_hash = @step_or_hook_hash
60
63
  end
@@ -85,33 +88,31 @@ module Cucumber
85
88
  if File.file?(src)
86
89
  content = File.open(src, 'rb', &:read)
87
90
  data = encode64(content)
91
+ elsif mime_type =~ /;base64$/
92
+ mime_type = mime_type[0..-8]
93
+ data = src
88
94
  else
89
- if mime_type =~ /;base64$/
90
- mime_type = mime_type[0..-8]
91
- data = src
92
- else
93
- data = encode64(src)
94
- end
95
+ data = encode64(src)
95
96
  end
96
97
  test_step_embeddings << { mime_type: mime_type, data: data }
97
98
  end
98
99
 
99
100
  private
100
101
 
101
- def same_feature_as_previous_test_case?(feature)
102
- current_feature[:uri] == feature.file && current_feature[:line] == feature.location.line
102
+ def same_feature_as_previous_test_case?(test_case)
103
+ current_feature[:uri] == test_case.location.file
103
104
  end
104
105
 
105
106
  def first_step_after_background?(test_step)
106
- test_step.source[1].to_s != @element_hash[:name]
107
+ @in_background && test_step.location.lines.max >= @test_case_hash[:line]
107
108
  end
108
109
 
109
110
  def internal_hook?(test_step)
110
- test_step.source.last.location.file.include?('lib/cucumber/')
111
+ test_step.location.file.include?('lib/cucumber/')
111
112
  end
112
113
 
113
114
  def current_feature
114
- @feature_hash ||= {}
115
+ @feature_hash ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
115
116
  end
116
117
 
117
118
  def feature_elements
@@ -122,16 +123,16 @@ module Cucumber
122
123
  @element_hash[:steps] ||= []
123
124
  end
124
125
 
125
- def hooks_of_type(hook_query)
126
- case hook_query.type
127
- when :before
128
- return before_hooks
129
- when :after
130
- return after_hooks
131
- when :after_step
132
- return after_step_hooks
126
+ def hooks_of_type(hook_step)
127
+ case hook_step.text
128
+ when 'Before hook'
129
+ before_hooks
130
+ when 'After hook'
131
+ after_hooks
132
+ when 'AfterStep hook'
133
+ after_step_hooks
133
134
  else
134
- fail 'Unknown hook type ' + hook_query.type.to_s
135
+ raise 'Unknown hook type ' + hook_step.to_s
135
136
  end
136
137
  end
137
138
 
@@ -159,30 +160,30 @@ module Cucumber
159
160
  @step_or_hook_hash[:embeddings] ||= []
160
161
  end
161
162
 
162
- def create_step_hash(step_source)
163
+ def create_step_hash(test_step)
164
+ step_source = @ast_lookup.step_source(test_step).step
163
165
  step_hash = {
164
- keyword: step_source.keyword,
165
- name: step_source.to_s,
166
- line: step_source.original_location.line
166
+ keyword: step_source[:keyword],
167
+ name: test_step.text,
168
+ line: test_step.location.lines.min
167
169
  }
168
- step_hash[:comments] = Formatter.create_comments_array(step_source.comments) unless step_source.comments.empty?
169
- step_hash[:doc_string] = create_doc_string_hash(step_source.multiline_arg) if step_source.multiline_arg.doc_string?
170
- step_hash[:rows] = create_data_table_value(step_source.multiline_arg) if step_source.multiline_arg.data_table?
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?
171
172
  step_hash
172
173
  end
173
174
 
174
175
  def create_doc_string_hash(doc_string)
175
- content_type = doc_string.content_type ? doc_string.content_type : ''
176
+ content_type = doc_string[:content_type] || ''
176
177
  {
177
- value: doc_string.content,
178
+ value: doc_string[:content],
178
179
  content_type: content_type,
179
- line: doc_string.location.line
180
+ line: doc_string[:location][:line]
180
181
  }
181
182
  end
182
183
 
183
184
  def create_data_table_value(data_table)
184
- data_table.raw.map do |row|
185
- { cells: row }
185
+ data_table[:rows].map do |row|
186
+ { cells: row[:cells].map { |cell| cell[:value] } }
186
187
  end
187
188
  end
188
189
 
@@ -226,113 +227,94 @@ module Cucumber
226
227
  class Builder
227
228
  attr_reader :feature_hash, :background_hash, :test_case_hash
228
229
 
229
- def initialize(test_case)
230
+ def initialize(test_case, ast_lookup)
230
231
  @background_hash = nil
231
- test_case.describe_source_to(self)
232
- test_case.feature.background.describe_to(self)
232
+ uri = test_case.location.file
233
+ feature = ast_lookup.gherkin_document(uri)[:feature]
234
+ feature(feature, uri)
235
+ background(feature[:children].first[:background]) unless feature[:children].first[:background].nil?
236
+ scenario(ast_lookup.scenario_source(test_case), test_case)
233
237
  end
234
238
 
235
239
  def background?
236
240
  @background_hash != nil
237
241
  end
238
242
 
239
- def feature(feature)
243
+ def feature(feature, uri)
240
244
  @feature_hash = {
241
- uri: feature.file,
242
- id: create_id(feature),
243
- keyword: feature.keyword,
244
- name: feature.to_s,
245
- description: feature.description,
246
- line: feature.location.line
245
+ id: create_id(feature[:name]),
246
+ uri: uri,
247
+ keyword: feature[:keyword],
248
+ name: feature[:name],
249
+ description: value_or_empty_string(feature[:description]),
250
+ line: feature[:location][:line]
247
251
  }
248
- unless feature.tags.empty?
249
- @feature_hash[:tags] = create_tags_array(feature.tags)
250
- @test_case_hash[:tags] = if @test_case_hash[:tags]
251
- @feature_hash[:tags] + @test_case_hash[:tags]
252
- else
253
- @feature_hash[:tags]
254
- end
255
- end
256
- @feature_hash[:comments] = Formatter.create_comments_array(feature.comments) unless feature.comments.empty?
257
- @test_case_hash[:id].insert(0, @feature_hash[:id] + ';')
252
+ return if feature[:tags].empty?
253
+ @feature_hash[:tags] = create_tags_array_from_hash_array(feature[:tags])
258
254
  end
259
255
 
260
256
  def background(background)
261
257
  @background_hash = {
262
- keyword: background.keyword,
263
- name: background.to_s,
264
- description: background.description,
265
- line: background.location.line,
258
+ keyword: background[:keyword],
259
+ name: background[:name],
260
+ description: value_or_empty_string(background[:description]),
261
+ line: background[:location][:line],
266
262
  type: 'background'
267
263
  }
268
- @background_hash[:comments] = Formatter.create_comments_array(background.comments) unless background.comments.empty?
269
264
  end
270
265
 
271
- def scenario(scenario)
266
+ def scenario(scenario_source, test_case)
267
+ scenario = scenario_source.type == :Scenario ? scenario_source.scenario : scenario_source.scenario_outline
272
268
  @test_case_hash = {
273
- id: create_id(scenario),
274
- keyword: scenario.keyword,
275
- name: scenario.to_s,
276
- description: scenario.description,
277
- line: scenario.location.line,
269
+ 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]),
273
+ line: test_case.location.lines.max,
278
274
  type: 'scenario'
279
275
  }
280
- @test_case_hash[:tags] = create_tags_array(scenario.tags) unless scenario.tags.empty?
281
- @test_case_hash[:comments] = Formatter.create_comments_array(scenario.comments) unless scenario.comments.empty?
276
+ @test_case_hash[:tags] = create_tags_array_from_tags_array(test_case.tags) unless test_case.tags.empty?
282
277
  end
283
278
 
284
- def scenario_outline(scenario)
285
- @test_case_hash = {
286
- id: create_id(scenario) + ';' + @example_id,
287
- keyword: scenario.keyword,
288
- name: scenario.to_s,
289
- description: scenario.description,
290
- line: @row.location.line,
291
- type: 'scenario'
292
- }
293
- tags = []
294
- tags += create_tags_array(scenario.tags) unless scenario.tags.empty?
295
- tags += @examples_table_tags if @examples_table_tags
296
- @test_case_hash[:tags] = tags unless tags.empty?
297
- comments = []
298
- comments += Formatter.create_comments_array(scenario.comments) unless scenario.comments.empty?
299
- comments += @examples_table_comments if @examples_table_comments
300
- comments += @row_comments if @row_comments
301
- @test_case_hash[:comments] = comments unless comments.empty?
302
- end
279
+ private
303
280
 
304
- def examples_table(examples_table)
305
- # the json file have traditionally used the header row as row 1,
306
- # wheras cucumber-ruby-core used the first example row as row 1.
307
- @example_id = create_id(examples_table) + ";#{@row.number + 1}"
281
+ def value_or_empty_string(value)
282
+ value.nil? ? '' : value
283
+ end
308
284
 
309
- @examples_table_tags = create_tags_array(examples_table.tags) unless examples_table.tags.empty?
310
- @examples_table_comments = Formatter.create_comments_array(examples_table.comments) unless examples_table.comments.empty?
285
+ def create_id(name)
286
+ name.downcase.tr(' ', '-')
311
287
  end
312
288
 
313
- def examples_table_row(row)
314
- @row = row
315
- @row_comments = Formatter.create_comments_array(row.comments) unless row.comments.empty?
289
+ def create_id_from_scenario_source(scenario_source)
290
+ if scenario_source.type == :Scenario
291
+ create_id(scenario_source.scenario[:name])
292
+ else
293
+ scenario_outline_name = scenario_source.scenario_outline[:name]
294
+ examples_name = scenario_source.examples[:name]
295
+ row_number = calculate_row_number(scenario_source)
296
+ "#{create_id(scenario_outline_name)};#{create_id(examples_name)};#{row_number}"
297
+ end
316
298
  end
317
299
 
318
- private
300
+ def calculate_row_number(scenario_source)
301
+ scenario_source.examples[:table_body].each_with_index do |row, index|
302
+ return index + 2 if row == scenario_source.row
303
+ end
304
+ end
319
305
 
320
- def create_id(element)
321
- element.to_s.downcase.tr(' ', '-')
306
+ def create_tags_array_from_hash_array(tags)
307
+ tags_array = []
308
+ tags.each { |tag| tags_array << { name: tag[:name], line: tag[:location][:line] } }
309
+ tags_array
322
310
  end
323
311
 
324
- def create_tags_array(tags)
312
+ def create_tags_array_from_tags_array(tags)
325
313
  tags_array = []
326
314
  tags.each { |tag| tags_array << { name: tag.name, line: tag.location.line } }
327
315
  tags_array
328
316
  end
329
317
  end
330
318
  end
331
-
332
- def self.create_comments_array(comments)
333
- comments_array = []
334
- comments.each { |comment| comments_array << { value: comment.to_s.strip, line: comment.location.line } }
335
- comments_array
336
- end
337
319
  end
338
320
  end
@@ -5,6 +5,7 @@ require 'cucumber/formatter/backtrace_filter'
5
5
  require 'cucumber/formatter/io'
6
6
  require 'cucumber/formatter/interceptor'
7
7
  require 'fileutils'
8
+ require 'cucumber/formatter/ast_lookup'
8
9
 
9
10
  module Cucumber
10
11
  module Formatter
@@ -19,6 +20,7 @@ module Cucumber
19
20
  end
20
21
 
21
22
  def initialize(config)
23
+ @ast_lookup = AstLookup.new(config)
22
24
  config.on_event :test_case_started, &method(:on_test_case_started)
23
25
  config.on_event :test_case_finished, &method(:on_test_case_finished)
24
26
  config.on_event :test_step_finished, &method(:on_test_step_finished)
@@ -33,17 +35,15 @@ module Cucumber
33
35
  tests: 0,
34
36
  skipped: 0,
35
37
  time: 0,
36
- builder: Builder::XmlMarkup.new(:indent => 2)
38
+ builder: Builder::XmlMarkup.new(indent: 2)
37
39
  }
38
40
  end
39
41
  end
40
42
 
41
43
  def on_test_case_started(event)
42
44
  test_case = event.test_case
43
- unless same_feature_as_previous_test_case?(test_case.feature)
44
- start_feature(test_case.feature)
45
- end
46
- @failing_step_source = nil
45
+ start_feature(test_case) unless same_feature_as_previous_test_case?(test_case)
46
+ @failing_test_step = nil
47
47
  # In order to fill out <system-err/> and <system-out/>, we need to
48
48
  # intercept the $stderr and $stdout
49
49
  @interceptedout = Interceptor::Pipe.wrap(:stdout)
@@ -52,15 +52,15 @@ module Cucumber
52
52
 
53
53
  def on_test_step_finished(event)
54
54
  test_step, result = *event.attributes
55
- return if @failing_step_source
55
+ return if @failing_test_step
56
56
 
57
- @failing_step_source = test_step.source.last unless result.ok?(@config.strict)
57
+ @failing_test_step = test_step unless result.ok?(@config.strict)
58
58
  end
59
59
 
60
60
  def on_test_case_finished(event)
61
61
  test_case, result = *event.attributes
62
62
  result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
63
- test_case_name = NameBuilder.new(test_case)
63
+ test_case_name = NameBuilder.new(test_case, @ast_lookup)
64
64
  scenario = test_case_name.scenario_name
65
65
  scenario_designation = "#{scenario}#{test_case_name.name_suffix}"
66
66
  output = create_output_string(test_case, scenario, result, test_case_name.row_name)
@@ -71,45 +71,54 @@ module Cucumber
71
71
  end
72
72
 
73
73
  def on_test_run_finished(_event)
74
- @features_data.each { |file, data| end_feature(data) }
74
+ @features_data.each { |_file, data| end_feature(data) }
75
75
  end
76
76
 
77
77
  private
78
78
 
79
- def same_feature_as_previous_test_case?(feature)
80
- @current_feature_data && @current_feature_data[:feature].file == feature.file && @current_feature_data[:feature].location == feature.location
79
+ def same_feature_as_previous_test_case?(test_case)
80
+ @current_feature_data && @current_feature_data[:uri] == test_case.location.file
81
81
  end
82
82
 
83
- def start_feature(feature)
84
- raise UnNamedFeatureError.new(feature.file) if feature.name.empty?
85
- @current_feature_data = @features_data[feature.file]
83
+ def start_feature(test_case)
84
+ uri = test_case.location.file
85
+ feature = @ast_lookup.gherkin_document(uri)[:feature]
86
+ raise UnNamedFeatureError, uri if feature[:name].empty?
87
+ @current_feature_data = @features_data[uri]
88
+ @current_feature_data[:uri] = uri unless @current_feature_data[:uri]
86
89
  @current_feature_data[:feature] = feature unless @current_feature_data[:feature]
87
90
  end
88
91
 
89
92
  def end_feature(feature_data)
90
- @testsuite = Builder::XmlMarkup.new(:indent => 2)
93
+ @testsuite = Builder::XmlMarkup.new(indent: 2)
91
94
  @testsuite.instruct!
92
95
  @testsuite.testsuite(
93
- :failures => feature_data[:failures],
94
- :errors => feature_data[:errors],
95
- :skipped => feature_data[:skipped],
96
- :tests => feature_data[:tests],
97
- :time => format('%.6f', feature_data[:time]),
98
- :name => feature_data[:feature].name
96
+ failures: feature_data[:failures],
97
+ errors: feature_data[:errors],
98
+ skipped: feature_data[:skipped],
99
+ tests: feature_data[:tests],
100
+ time: format('%.6f', feature_data[:time]),
101
+ name: feature_data[:feature][:name]
99
102
  ) do
100
103
  @testsuite << feature_data[:builder].target!
101
104
  end
102
105
 
103
- write_file(feature_result_filename(feature_data[:feature].file), @testsuite.target!)
106
+ write_file(feature_result_filename(feature_data[:uri]), @testsuite.target!)
104
107
  end
105
108
 
106
- def create_output_string(test_case, scenario, result, row_name)
107
- output = "#{test_case.keyword}: #{scenario}\n\n"
109
+ def create_output_string(test_case, scenario, result, row_name) # rubocop:disable Metrics/PerceivedComplexity
110
+ scenario_source = @ast_lookup.scenario_source(test_case)
111
+ keyword = scenario_source.type == :Scenario ? scenario_source.scenario[:keyword] : scenario_source.scenario_outline[:keyword]
112
+ output = "#{keyword}: #{scenario}\n\n"
108
113
  return output if result.ok?(@config.strict)
109
- if test_case.keyword == 'Scenario'
110
- if @failing_step_source
111
- output += @failing_step_source.keyword.to_s unless hook?(@failing_step_source)
112
- output += "#{@failing_step_source}\n"
114
+ if scenario_source.type == :Scenario
115
+ if @failing_test_step
116
+ if @failing_test_step.hook?
117
+ output += "#{@failing_test_step.text} at #{@failing_test_step.location}\n"
118
+ else
119
+ step_source = @ast_lookup.step_source(@failing_test_step).step
120
+ output += "#{step_source[:keyword]}#{@failing_test_step.text}\n"
121
+ end
113
122
  else # An Around hook has failed
114
123
  output += "Around hook\n"
115
124
  end
@@ -119,24 +128,20 @@ module Cucumber
119
128
  output + "\nMessage:\n"
120
129
  end
121
130
 
122
- def hook?(step)
123
- ['Before hook', 'After hook', 'AfterStep hook'].include? step.text
124
- end
125
-
126
131
  def build_testcase(result, scenario_designation, output)
127
132
  duration = ResultBuilder.new(result).test_case_duration
128
133
  @current_feature_data[:time] += duration
129
- classname = @current_feature_data[:feature].name
134
+ classname = @current_feature_data[:feature][:name]
130
135
  name = scenario_designation
131
136
 
132
- @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('%.6f', duration)) do
133
138
  if !result.passed? && result.ok?(@config.strict)
134
139
  @current_feature_data[:builder].skipped
135
140
  @current_feature_data[:skipped] += 1
136
141
  elsif !result.passed?
137
142
  status = result.to_sym
138
143
  exception = get_backtrace_object(result)
139
- @current_feature_data[:builder].failure(:message => "#{status} #{name}", :type => status) do
144
+ @current_feature_data[:builder].failure(message: "#{status} #{name}", type: status) do
140
145
  @current_feature_data[:builder].cdata! output
141
146
  @current_feature_data[:builder].cdata!(format_exception(exception)) if exception
142
147
  end
@@ -154,11 +159,9 @@ module Cucumber
154
159
 
155
160
  def get_backtrace_object(result)
156
161
  if result.failed?
157
- return result.exception
162
+ result.exception
158
163
  elsif result.backtrace
159
- return result
160
- else
161
- return nil
164
+ result
162
165
  end
163
166
  end
164
167
 
@@ -171,7 +174,7 @@ module Cucumber
171
174
  end
172
175
 
173
176
  def basename(feature_file)
174
- File.basename(feature_file.gsub(/[\\\/]/, '-'), '.feature')
177
+ File.basename(feature_file.gsub(/[\\\/]/, '-'), '.feature') # rubocop:disable Style/RegexpLiteral
175
178
  end
176
179
 
177
180
  def write_file(feature_filename, data)
@@ -187,34 +190,29 @@ module Cucumber
187
190
  class NameBuilder
188
191
  attr_reader :scenario_name, :name_suffix, :row_name
189
192
 
190
- def initialize(test_case)
193
+ def initialize(test_case, ast_lookup)
191
194
  @name_suffix = ''
192
195
  @row_name = ''
193
- test_case.describe_source_to self
194
- end
195
-
196
- def feature(*)
197
- self
196
+ scenario_source = ast_lookup.scenario_source(test_case)
197
+ if scenario_source.type == :Scenario
198
+ scenario(scenario_source.scenario)
199
+ else
200
+ scenario_outline(scenario_source.scenario_outline)
201
+ examples_table_row(scenario_source.row)
202
+ end
198
203
  end
199
204
 
200
205
  def scenario(scenario)
201
- @scenario_name = (scenario.name.nil? || scenario.name == '') ? 'Unnamed scenario' : scenario.name
202
- self
206
+ @scenario_name = scenario[:name].empty? ? 'Unnamed scenario' : scenario[:name]
203
207
  end
204
208
 
205
209
  def scenario_outline(outline)
206
- @scenario_name = (outline.name.nil? || outline.name == '') ? 'Unnamed scenario outline' : outline.name
207
- self
208
- end
209
-
210
- def examples_table(*)
211
- self
210
+ @scenario_name = outline[:name].empty? ? 'Unnamed scenario outline' : outline[:name]
212
211
  end
213
212
 
214
213
  def examples_table_row(row)
215
- @row_name = '| ' + row.values.join(' | ') + ' |'
214
+ @row_name = '| ' + row[:cells].map { |cell| cell[:value] }.join(' | ') + ' |'
216
215
  @name_suffix = " (outline example : #{@row_name})"
217
- self
218
216
  end
219
217
  end
220
218
 
@@ -238,7 +236,7 @@ module Cucumber
238
236
  def exception(*) end
239
237
 
240
238
  def duration(duration, *)
241
- duration.tap { |duration| @test_case_duration = duration.nanoseconds / 10**9.0 }
239
+ duration.tap { |dur| @test_case_duration = dur.nanoseconds / 10**9.0 }
242
240
  end
243
241
  end
244
242
  end