cucumber 3.2.0 → 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 (90) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +61 -18
  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 +56 -69
  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 +8 -14
  33. data/lib/cucumber/formatter/json.rb +93 -117
  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 +37 -28
  77. data/lib/cucumber/formatter/cucumber.css +0 -286
  78. data/lib/cucumber/formatter/cucumber.sass +0 -247
  79. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  80. data/lib/cucumber/formatter/html.rb +0 -611
  81. data/lib/cucumber/formatter/html_builder.rb +0 -121
  82. data/lib/cucumber/formatter/http_io.rb +0 -146
  83. data/lib/cucumber/formatter/inline-js.js +0 -30
  84. data/lib/cucumber/formatter/jquery-min.js +0 -154
  85. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  86. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  87. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  88. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  89. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  90. data/lib/cucumber/step_argument.rb +0 -25
@@ -1,18 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'fileutils'
4
+ require 'gherkin/dialect'
4
5
  require 'cucumber/formatter/console'
5
6
  require 'cucumber/formatter/io'
6
7
  require 'cucumber/gherkin/formatter/escaping'
7
8
  require 'cucumber/formatter/console_counts'
8
9
  require 'cucumber/formatter/console_issues'
10
+ require 'cucumber/formatter/duration_extractor'
11
+ require 'cucumber/formatter/backtrace_filter'
12
+ require 'cucumber/formatter/ast_lookup'
9
13
 
10
14
  module Cucumber
11
15
  module Formatter
12
16
  # The formatter used for <tt>--format pretty</tt> (the default formatter).
13
17
  #
14
- # This formatter prints features to plain text - exactly how they were parsed,
15
- # just prettier. That means with proper indentation and alignment of table columns.
18
+ # This formatter prints the result of the feature executions to plain text - exactly how they were parsed.
16
19
  #
17
20
  # If the output is STDOUT (and not a file), there are bright colours to watch too.
18
21
  #
@@ -21,233 +24,424 @@ module Cucumber
21
24
  include Console
22
25
  include Io
23
26
  include Cucumber::Gherkin::Formatter::Escaping
24
- attr_writer :indent
25
- attr_reader :runtime
26
-
27
- def initialize(runtime, path_or_io, options)
28
- @runtime, @io, @options = runtime, ensure_io(path_or_io), options
29
- @config = runtime.configuration
30
- @exceptions = []
31
- @indent = 0
32
- @prefixes = options[:prefixes] || {}
33
- @delayed_messages = []
34
- @previous_step_keyword = nil
27
+ attr_reader :config, :options
28
+ private :config, :options
29
+ attr_reader :current_feature_uri, :current_scenario_outline, :current_examples, :current_test_case
30
+ private :current_feature_uri, :current_scenario_outline, :current_examples, :current_test_case
31
+ attr_reader :in_scenario_outline, :print_background_steps
32
+ private :in_scenario_outline, :print_background_steps
33
+
34
+ def initialize(config)
35
+ @io = ensure_io(config.out_stream)
36
+ @config = config
37
+ @options = config.to_hash
35
38
  @snippets_input = []
36
- @counts = ConsoleCounts.new(runtime.configuration)
37
- @issues = ConsoleIssues.new(runtime.configuration)
39
+ @total_duration = 0
40
+ @exceptions = []
41
+ @gherkin_sources = {}
42
+ @step_matches = {}
43
+ @ast_lookup = AstLookup.new(config)
44
+ @counts = ConsoleCounts.new(config)
45
+ @issues = ConsoleIssues.new(config, @ast_lookup)
46
+ @first_feature = true
47
+ @current_feature_uri = ''
48
+ @current_scenario_outline = nil
49
+ @current_examples = nil
50
+ @current_test_case = nil
51
+ @in_scenario_outline = false
52
+ @print_background_steps = false
53
+ @test_step_output = []
54
+ @passed_test_cases = []
55
+ @source_indent = 0
56
+ @next_comment_to_be_printed = 0
57
+ config.on_event :gherkin_source_read, &method(:on_gherkin_source_read)
58
+ config.on_event :step_activated, &method(:on_step_activated)
59
+ config.on_event :test_case_started, &method(:on_test_case_started)
60
+ config.on_event :test_step_started, &method(:on_test_step_started)
61
+ config.on_event :test_step_finished, &method(:on_test_step_finished)
62
+ config.on_event :test_case_finished, &method(:on_test_case_finished)
63
+ config.on_event :test_run_finished, &method(:on_test_run_finished)
64
+ end
65
+
66
+ def on_gherkin_source_read(event)
67
+ @gherkin_sources[event.path] = event.body
68
+ end
69
+
70
+ def on_step_activated(event)
71
+ test_step, step_match = *event.attributes
72
+ @step_matches[test_step.to_s] = step_match
73
+ end
74
+
75
+ def on_test_case_started(event)
76
+ if !same_feature_as_previous_test_case?(event.test_case.location)
77
+ if first_feature?
78
+ @first_feature = false
79
+ print_profile_information
80
+ else
81
+ print_comments(gherkin_source.split("\n").length, 0)
82
+ @io.puts
83
+ end
84
+ @current_feature_uri = event.test_case.location.file
85
+ @exceptions = []
86
+ print_feature_data
87
+ if feature_has_background?
88
+ print_background_data
89
+ @print_background_steps = true
90
+ @in_scenario_outline = false
91
+ end
92
+ else
93
+ @print_background_steps = false
94
+ end
95
+ @current_test_case = event.test_case
96
+ print_step_header(current_test_case) unless print_background_steps
97
+ end
98
+
99
+ def on_test_step_started(event)
100
+ return if event.test_step.hook?
101
+ print_step_header(current_test_case) if first_step_after_printing_background_steps?(event.test_step)
102
+ end
103
+
104
+ def on_test_step_finished(event) # rubocop:disable Metrics/PerceivedComplexity
105
+ collect_snippet_data(event.test_step, @ast_lookup) if event.result.undefined?
106
+ return if in_scenario_outline && !options[:expand]
107
+ exception_to_be_printed = find_exception_to_be_printed(event.result)
108
+ # rubocop:disable Metrics/LineLength
109
+ print_step_data(event.test_step, event.result) if !event.test_step.hook? && (print_background_steps || event.test_step.location.lines.max >= current_test_case.location.lines.max || exception_to_be_printed)
110
+ # rubocop:enable Metrics/LineLength
111
+ print_step_output
112
+ return unless exception_to_be_printed
113
+ print_exception(exception_to_be_printed, event.result.to_sym, 6)
114
+ @exceptions << exception_to_be_printed
115
+ end
116
+
117
+ def on_test_case_finished(event)
118
+ @total_duration += DurationExtractor.new(event.result).result_duration
119
+ @passed_test_cases << event.test_case if config.wip? && event.result.passed?
120
+ if in_scenario_outline && !options[:expand]
121
+ print_row_data(event.test_case, event.result)
122
+ else
123
+ exception_to_be_printed = find_exception_to_be_printed(event.result)
124
+ return unless exception_to_be_printed
125
+ print_exception(exception_to_be_printed, event.result.to_sym, 6)
126
+ @exceptions << exception_to_be_printed
127
+ end
38
128
  end
39
129
 
40
- def before_features(_features)
41
- print_profile_information
130
+ def on_test_run_finished(_event)
131
+ print_comments(gherkin_source.split("\n").length, 0) unless current_feature_uri.empty?
132
+ @io.puts
133
+ print_summary
42
134
  end
43
135
 
44
- def after_features(features)
45
- print_summary(features)
136
+ def puts(*messages)
137
+ messages.each do |message|
138
+ @test_step_output.push message
139
+ end
46
140
  end
47
141
 
48
- def before_feature(_feature)
49
- @exceptions = []
50
- @indent = 0
142
+ private
143
+
144
+ def find_exception_to_be_printed(result)
145
+ return nil if result.ok?(options[:strict])
146
+ result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
147
+ exception = result.failed? ? result.exception : result
148
+ return nil if @exceptions.include?(exception)
149
+ exception
51
150
  end
52
151
 
53
- def comment_line(comment_line)
54
- @io.puts(comment_line.indent(@indent))
55
- @io.flush
152
+ def calculate_source_indent(test_case)
153
+ scenario = scenario_source(test_case).scenario
154
+ @source_indent = calculate_source_indent_for_ast_node(scenario)
56
155
  end
57
156
 
58
- def after_tags(_tags)
59
- if @indent == 1
60
- @io.puts
61
- @io.flush
157
+ def calculate_source_indent_for_ast_node(ast_node)
158
+ indent = 4 + ast_node[:keyword].length
159
+ indent += 1 + ast_node[:name].length
160
+ ast_node[:steps].each do |step|
161
+ step_indent = 5 + step[:keyword].length + step[:text].length
162
+ indent = step_indent if step_indent > indent
62
163
  end
164
+ indent
63
165
  end
64
166
 
65
- def tag_name(tag_name)
66
- tag = format_string(tag_name, :tag).indent(@indent)
67
- @io.print(tag)
68
- @io.flush
69
- @indent = 1
167
+ def calculate_source_indent_for_expanded_test_case(test_case, scenario_keyword, expanded_name)
168
+ indent = 7 + scenario_keyword.length
169
+ indent += 2 + expanded_name.length
170
+ test_case.test_steps.each do |step|
171
+ if !step.hook? && step.location.lines.max >= test_case.location.lines.max
172
+ step_indent = 9 + test_step_keyword(step).length + step.text.length
173
+ indent = step_indent if step_indent > indent
174
+ end
175
+ end
176
+ indent
70
177
  end
71
178
 
72
- def feature_name(keyword, name)
73
- @io.puts("#{keyword}: #{name}")
74
- @io.puts
75
- @io.flush
179
+ def print_step_output
180
+ @test_step_output.each { |message| @io.puts(format_string(message, :tag).indent(6)) }
181
+ @test_step_output = []
76
182
  end
77
183
 
78
- def before_feature_element(_feature_element)
79
- @indent = 2
80
- @scenario_indent = 2
184
+ def first_feature?
185
+ @first_feature
81
186
  end
82
187
 
83
- def after_feature_element(_feature_element)
84
- print_messages
85
- @io.puts
86
- @io.flush
188
+ def same_feature_as_previous_test_case?(location)
189
+ location.file == current_feature_uri
87
190
  end
88
191
 
89
- def before_background(_background)
90
- @indent = 2
91
- @scenario_indent = 2
92
- @in_background = true
192
+ def feature_has_background?
193
+ feature_children = gherkin_document[:feature][:children]
194
+ return false if feature_children.empty?
195
+ !feature_children.first[:background].nil?
93
196
  end
94
197
 
95
- def after_background(_background)
96
- print_messages
97
- @in_background = nil
98
- @io.puts
99
- @io.flush
198
+ def print_step_header(test_case)
199
+ if from_scenario_outline?(test_case)
200
+ @in_scenario_outline = true
201
+ unless same_outline_as_previous_test_case?(test_case)
202
+ @current_scenario_outline = scenario_source(test_case).scenario_outline
203
+ @io.puts
204
+ print_outline_data(current_scenario_outline)
205
+ end
206
+ unless same_examples_as_previous_test_case?(test_case)
207
+ @current_examples = scenario_source(test_case).examples
208
+ @io.puts
209
+ print_examples_data(current_examples)
210
+ end
211
+ print_expanded_row_data(current_test_case) if options[:expand]
212
+ else
213
+ @in_scenario_outline = false
214
+ @current_scenario_outline = nil
215
+ @current_examples = nil
216
+ @io.puts
217
+ @source_indent = calculate_source_indent(current_test_case)
218
+ print_scenario_data(test_case)
219
+ end
100
220
  end
101
221
 
102
- def background_name(keyword, name, file_colon_line, source_indent)
103
- print_feature_element_name(keyword, name, file_colon_line, source_indent)
222
+ def same_outline_as_previous_test_case?(test_case)
223
+ scenario_source(test_case).scenario_outline == current_scenario_outline
104
224
  end
105
225
 
106
- def before_examples_array(_examples_array)
107
- @indent = 4
108
- @io.puts
109
- @visiting_first_example_name = true
226
+ def same_examples_as_previous_test_case?(test_case)
227
+ scenario_source(test_case).examples == current_examples
228
+ end
229
+
230
+ def from_scenario_outline?(test_case)
231
+ scenario = scenario_source(test_case)
232
+ scenario.type != :Scenario
233
+ end
234
+
235
+ def first_step_after_printing_background_steps?(test_step)
236
+ return false unless print_background_steps
237
+ return false unless test_step.location.lines.max >= current_test_case.location.lines.max
238
+ @print_background_steps = false
239
+ true
110
240
  end
111
241
 
112
- def examples_name(keyword, name)
113
- @io.puts unless @visiting_first_example_name
114
- @visiting_first_example_name = false
115
- @io.puts(" #{keyword}: #{name}")
242
+ def print_feature_data
243
+ feature = gherkin_document[:feature]
244
+ print_language_comment(feature[:location][:line])
245
+ print_comments(feature[:location][:line], 0)
246
+ print_tags(feature[:tags], 0)
247
+ print_feature_line(feature)
248
+ print_description(feature[:description])
116
249
  @io.flush
117
- @indent = 6
118
- @scenario_indent = 6
119
250
  end
120
251
 
121
- def before_outline_table(outline_table)
122
- @table = outline_table
252
+ def print_language_comment(feature_line)
253
+ gherkin_source.split("\n")[0..feature_line].each do |line|
254
+ @io.puts(format_string(line, :comment)) if /# *language *:/ =~ line
255
+ end
123
256
  end
124
257
 
125
- def after_outline_table(_outline_table)
126
- @table = nil
127
- @indent = 4
258
+ def print_comments(up_to_line, indent)
259
+ comments = gherkin_document[:comments]
260
+ return if comments.empty? || comments.length <= @next_comment_to_be_printed
261
+ comments[@next_comment_to_be_printed..-1].each do |comment|
262
+ if comment[:location][:line] <= up_to_line
263
+ @io.puts(format_string(comment[:text].strip, :comment).indent(indent))
264
+ @next_comment_to_be_printed += 1
265
+ end
266
+ break if @next_comment_to_be_printed >= comments.length
267
+ end
128
268
  end
129
269
 
130
- def scenario_name(keyword, name, file_colon_line, source_indent)
131
- print_feature_element_name(keyword, name, file_colon_line, source_indent)
270
+ def print_tags(tags, indent)
271
+ return if !tags || tags.empty?
272
+ @io.puts(tags.map { |tag| format_string(tag[:name], :tag) }.join(' ').indent(indent))
132
273
  end
133
274
 
134
- def before_step(step)
135
- @current_step = step
136
- @indent = 6
137
- print_messages
275
+ def print_feature_line(feature)
276
+ print_keyword_name(feature[:keyword], feature[:name], 0)
138
277
  end
139
278
 
140
- def before_step_result(_keyword, _step_match, _multiline_arg, status, exception, _source_indent, background, _file_colon_line)
141
- @hide_this_step = false
142
- if exception
143
- if @exceptions.include?(exception)
144
- @hide_this_step = true
145
- return
146
- end
147
- @exceptions << exception
279
+ def print_keyword_name(keyword, name, indent, location = nil)
280
+ line = "#{keyword}:"
281
+ line += " #{name}"
282
+ @io.print(line.indent(indent))
283
+ if location && options[:source]
284
+ line_comment = format_string("# #{location}", :comment).indent(@source_indent - line.length - indent)
285
+ @io.print(line_comment)
148
286
  end
149
- if status != :failed && @in_background ^ background
150
- @hide_this_step = true
151
- return
287
+ @io.puts
288
+ end
289
+
290
+ def print_description(description)
291
+ return unless description
292
+ description.split("\n").each do |line|
293
+ @io.puts(line)
152
294
  end
153
- @status = status
154
295
  end
155
296
 
156
- def step_name(keyword, step_match, status, source_indent, _background, _file_colon_line)
157
- return if @hide_this_step
158
- source_indent = nil unless @options[:source]
159
- name_to_report = format_step(keyword, step_match, status, source_indent)
160
- @io.puts(name_to_report.indent(@scenario_indent + 2))
161
- print_messages
297
+ def print_background_data
298
+ @io.puts
299
+ background = gherkin_document[:feature][:children].first[:background]
300
+ @source_indent = calculate_source_indent_for_ast_node(background) if options[:source]
301
+ print_comments(background[:location][:line], 2)
302
+ print_background_line(background)
303
+ print_description(background[:description])
304
+ @io.flush
305
+ end
306
+
307
+ def print_background_line(background)
308
+ print_keyword_name(background[:keyword], background[:name], 2, "#{current_feature_uri}:#{background[:location][:line]}")
162
309
  end
163
310
 
164
- def doc_string(string)
165
- return if @options[:no_multiline] || @hide_this_step
166
- s = %{"""\n#{string}\n"""}.indent(@indent)
167
- s = s.split("\n").map { |l| l =~ /^\s+$/ ? '' : l }.join("\n")
168
- @io.puts(format_string(s, @current_step.status))
311
+ def print_scenario_data(test_case)
312
+ scenario = scenario_source(test_case).scenario
313
+ print_comments(scenario[:location][:line], 2)
314
+ print_tags(scenario[:tags], 2)
315
+ print_scenario_line(scenario, test_case.location)
316
+ print_description(scenario[:description])
169
317
  @io.flush
170
318
  end
171
319
 
172
- def exception(exception, status)
173
- return if @hide_this_step
174
- print_messages
175
- print_exception(exception, status, @indent)
320
+ def print_scenario_line(scenario, location = nil)
321
+ print_keyword_name(scenario[:keyword], scenario[:name], 2, location)
322
+ end
323
+
324
+ def print_step_data(test_step, result)
325
+ base_indent = options[:expand] && in_scenario_outline ? 8 : 4
326
+ step_keyword = test_step_keyword(test_step)
327
+ indent = options[:source] ? @source_indent - step_keyword.length - test_step.text.length - base_indent : nil
328
+ print_comments(test_step.location.lines.max, base_indent)
329
+ name_to_report = format_step(step_keyword, @step_matches.fetch(test_step.to_s) { NoStepMatch.new(test_step, test_step.text) }, result.to_sym, indent)
330
+ @io.puts(name_to_report.indent(base_indent))
331
+ print_multiline_argument(test_step, result, base_indent + 2) unless options[:no_multiline]
176
332
  @io.flush
177
333
  end
178
334
 
179
- def before_multiline_arg(multiline_arg)
180
- return if @options[:no_multiline] || @hide_this_step
181
- @table = multiline_arg
335
+ def test_step_keyword(test_step)
336
+ step = step_source(test_step).step
337
+ step[:keyword]
182
338
  end
183
339
 
184
- def after_multiline_arg(_multiline_arg)
185
- @table = nil
340
+ def step_source(test_step)
341
+ @ast_lookup.step_source(test_step)
186
342
  end
187
343
 
188
- def before_table_row(_table_row)
189
- return if !@table || @hide_this_step
190
- @col_index = 0
191
- @io.print ' |'.indent(@indent - 2)
344
+ def scenario_source(test_case)
345
+ @ast_lookup.scenario_source(test_case)
192
346
  end
193
347
 
194
- def after_table_row(table_row)
195
- return if !@table || @hide_this_step
196
- print_table_row_messages
197
- @io.puts
198
- if table_row.exception && !@exceptions.include?(table_row.exception)
199
- print_exception(table_row.exception, table_row.status, @indent)
200
- end
348
+ def gherkin_source
349
+ @gherkin_sources[current_feature_uri]
201
350
  end
202
351
 
203
- def after_table_cell(_cell)
204
- return unless @table
205
- @col_index += 1
352
+ def gherkin_document
353
+ @ast_lookup.gherkin_document(current_feature_uri)
206
354
  end
207
355
 
208
- def table_cell_value(value, status)
209
- return if !@table || @hide_this_step
210
- status ||= @status || :passed
211
- width = @table.col_width(@col_index)
212
- cell_text = escape_cell(value.to_s || '')
213
- padded = cell_text + (' ' * (width - cell_text.unpack('U*').length))
214
- prefix = cell_prefix(status)
215
- @io.print(' ' + format_string("#{prefix}#{padded}", status) + ::Cucumber::Term::ANSIColor.reset(' |'))
216
- @io.flush
356
+ def print_multiline_argument(test_step, result, indent)
357
+ step = step_source(test_step).step
358
+ if !step[:doc_string].nil?
359
+ print_doc_string(step[:doc_string][:content], result.to_sym, indent)
360
+ elsif !step[:data_table].nil?
361
+ print_data_table(step[:data_table], result.to_sym, indent)
362
+ end
217
363
  end
218
364
 
219
- def before_test_case(_test_case)
220
- @previous_step_keyword = nil
365
+ def print_data_table(data_table, status, indent)
366
+ data_table[:rows].each do |row|
367
+ print_comments(row[:location][:line], indent)
368
+ @io.puts format_string(gherkin_source.split("\n")[row[:location][:line] - 1].strip, status).indent(indent)
369
+ end
221
370
  end
222
371
 
223
- def after_test_step(test_step, result)
224
- collect_snippet_data(test_step, result)
372
+ def print_outline_data(scenario_outline) # rubocop:disable Metrics/AbcSize
373
+ print_comments(scenario_outline[:location][:line], 2)
374
+ print_tags(scenario_outline[:tags], 2)
375
+ @source_indent = calculate_source_indent_for_ast_node(scenario_outline) if options[:source]
376
+ print_scenario_line(scenario_outline, "#{current_feature_uri}:#{scenario_outline[:location][:line]}")
377
+ print_description(scenario_outline[:description])
378
+ scenario_outline[:steps].each do |step|
379
+ print_comments(step[:location][:line], 4)
380
+ step_line = " #{step[:keyword]}#{step[:text]}"
381
+ @io.print(format_string(step_line, :skipped))
382
+ if options[:source]
383
+ comment_line = format_string("# #{current_feature_uri}:#{step[:location][:line]}", :comment)
384
+ @io.print(comment_line.indent(@source_indent - step_line.length))
385
+ end
386
+ @io.puts
387
+ next if options[:no_multiline]
388
+ print_doc_string(step[:doc_string][:content], :skipped, 6) unless step[:doc_string].nil?
389
+ print_data_table(step[:data_table], :skipped, 6) unless step[:data_table].nil?
390
+ end
391
+ @io.flush
225
392
  end
226
393
 
227
- private
394
+ def print_doc_string(content, status, indent)
395
+ s = %("""\n#{content}\n""").indent(indent)
396
+ s = s.split("\n").map { |l| l =~ /^\s+$/ ? '' : l }.join("\n")
397
+ @io.puts(format_string(s, status))
398
+ end
228
399
 
229
- def print_feature_element_name(keyword, name, file_colon_line, source_indent)
230
- @io.puts if @scenario_indent == 6
231
- names = name.empty? ? [name] : name.split("\n")
232
- line = "#{keyword}: #{names[0]}".indent(@scenario_indent)
233
- @io.print(line)
234
- if @options[:source]
235
- line_comment = "# #{file_colon_line}".indent(source_indent)
236
- @io.print(format_string(line_comment, :comment))
400
+ def print_examples_data(examples)
401
+ print_comments(examples[:location][:line], 4)
402
+ print_tags(examples[:tags], 4)
403
+ print_keyword_name(examples[:keyword], examples[:name], 4)
404
+ print_description(examples[:description])
405
+ unless options[:expand]
406
+ print_comments(examples[:table_header][:location][:line], 6)
407
+ @io.puts(gherkin_source.split("\n")[examples[:table_header][:location][:line] - 1].strip.indent(6))
237
408
  end
409
+ @io.flush
410
+ end
411
+
412
+ def print_row_data(test_case, result)
413
+ print_comments(test_case.location.lines.max, 6)
414
+ @io.print(format_string(gherkin_source.split("\n")[test_case.location.lines.max - 1].strip, result.to_sym).indent(6))
415
+ @io.print(format_string(@test_step_output.join(', '), :tag).indent(2)) unless @test_step_output.empty?
416
+ @test_step_output = []
238
417
  @io.puts
239
- names[1..-1].each { |s| @io.puts s.to_s }
418
+ if result.failed? || result.pending?
419
+ result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
420
+ exception = result.failed? ? result.exception : result
421
+ unless @exceptions.include?(exception)
422
+ print_exception(exception, result.to_sym, 6)
423
+ @exceptions << exception
424
+ end
425
+ end
240
426
  @io.flush
241
427
  end
242
428
 
243
- def cell_prefix(status)
244
- @prefixes[status]
429
+ def print_expanded_row_data(test_case)
430
+ feature = gherkin_document[:feature]
431
+ language_code = feature[:language] || 'en'
432
+ language = ::Gherkin::Dialect.for(language_code)
433
+ scenario_keyword = language.scenario_keywords[0]
434
+ row = scenario_source(test_case).row
435
+ expanded_name = '| ' + row[:cells].map { |cell| cell[:value] }.join(' | ') + ' |'
436
+ @source_indent = calculate_source_indent_for_expanded_test_case(test_case, scenario_keyword, expanded_name)
437
+ @io.puts
438
+ print_keyword_name(scenario_keyword, expanded_name, 6, test_case.location)
245
439
  end
246
440
 
247
- def print_summary(features)
248
- print_statistics(features.duration, @config, @counts, @issues)
249
- print_snippets(@options)
250
- print_passing_wip(@options)
441
+ def print_summary
442
+ print_statistics(@total_duration, config, @counts, @issues)
443
+ print_snippets(options)
444
+ print_passing_wip(config, @passed_test_cases, @ast_lookup)
251
445
  end
252
446
  end
253
447
  end