cucumber 3.2.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +192 -19
  3. data/CONTRIBUTING.md +2 -18
  4. data/README.md +4 -5
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber_mixin.rb +34 -39
  7. data/lib/cucumber/cli/configuration.rb +5 -5
  8. data/lib/cucumber/cli/main.rb +12 -12
  9. data/lib/cucumber/cli/options.rb +62 -71
  10. data/lib/cucumber/cli/profile_loader.rb +49 -26
  11. data/lib/cucumber/configuration.rb +31 -23
  12. data/lib/cucumber/constantize.rb +2 -5
  13. data/lib/cucumber/deprecate.rb +31 -7
  14. data/lib/cucumber/errors.rb +5 -7
  15. data/lib/cucumber/events/envelope.rb +9 -0
  16. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  17. data/lib/cucumber/events/hook_test_step_created.rb +13 -0
  18. data/lib/cucumber/events/step_activated.rb +2 -1
  19. data/lib/cucumber/events/test_case_created.rb +13 -0
  20. data/lib/cucumber/events/test_case_ready.rb +12 -0
  21. data/lib/cucumber/events/test_step_created.rb +13 -0
  22. data/lib/cucumber/events/undefined_parameter_type.rb +10 -0
  23. data/lib/cucumber/events.rb +13 -6
  24. data/lib/cucumber/file_specs.rb +6 -6
  25. data/lib/cucumber/filters/activate_steps.rb +5 -3
  26. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  27. data/lib/cucumber/filters/prepare_world.rb +5 -9
  28. data/lib/cucumber/filters/quit.rb +1 -3
  29. data/lib/cucumber/filters/tag_limits/verifier.rb +2 -4
  30. data/lib/cucumber/filters.rb +1 -0
  31. data/lib/cucumber/formatter/ansicolor.rb +40 -45
  32. data/lib/cucumber/formatter/ast_lookup.rb +165 -0
  33. data/lib/cucumber/formatter/backtrace_filter.rb +9 -8
  34. data/lib/cucumber/formatter/console.rb +58 -66
  35. data/lib/cucumber/formatter/console_counts.rb +4 -9
  36. data/lib/cucumber/formatter/console_issues.rb +6 -3
  37. data/lib/cucumber/formatter/duration.rb +1 -1
  38. data/lib/cucumber/formatter/duration_extractor.rb +3 -1
  39. data/lib/cucumber/formatter/errors.rb +6 -0
  40. data/lib/cucumber/formatter/fanout.rb +2 -0
  41. data/lib/cucumber/formatter/html.rb +11 -598
  42. data/lib/cucumber/formatter/http_io.rb +1 -1
  43. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  44. data/lib/cucumber/formatter/interceptor.rb +8 -28
  45. data/lib/cucumber/formatter/io.rb +1 -1
  46. data/lib/cucumber/formatter/json.rb +101 -115
  47. data/lib/cucumber/formatter/junit.rb +56 -56
  48. data/lib/cucumber/formatter/message.rb +22 -0
  49. data/lib/cucumber/formatter/message_builder.rb +255 -0
  50. data/lib/cucumber/formatter/pretty.rb +359 -153
  51. data/lib/cucumber/formatter/progress.rb +30 -32
  52. data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -0
  53. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  54. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  55. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  56. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +40 -0
  57. data/lib/cucumber/formatter/rerun.rb +22 -4
  58. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  59. data/lib/cucumber/formatter/steps.rb +2 -3
  60. data/lib/cucumber/formatter/summary.rb +16 -8
  61. data/lib/cucumber/formatter/unicode.rb +15 -17
  62. data/lib/cucumber/formatter/usage.rb +11 -10
  63. data/lib/cucumber/gherkin/data_table_parser.rb +17 -6
  64. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
  65. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  66. data/lib/cucumber/gherkin/steps_parser.rb +17 -8
  67. data/lib/cucumber/glue/dsl.rb +1 -1
  68. data/lib/cucumber/glue/hook.rb +34 -11
  69. data/lib/cucumber/glue/invoke_in_world.rb +13 -18
  70. data/lib/cucumber/glue/proto_world.rb +42 -33
  71. data/lib/cucumber/glue/registry_and_more.rb +42 -12
  72. data/lib/cucumber/glue/snippet.rb +23 -22
  73. data/lib/cucumber/glue/step_definition.rb +42 -19
  74. data/lib/cucumber/glue/world_factory.rb +1 -1
  75. data/lib/cucumber/hooks.rb +11 -11
  76. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +1 -1
  77. data/lib/cucumber/multiline_argument/data_table.rb +97 -64
  78. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  79. data/lib/cucumber/multiline_argument.rb +4 -6
  80. data/lib/cucumber/platform.rb +3 -3
  81. data/lib/cucumber/rake/task.rb +16 -16
  82. data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
  83. data/lib/cucumber/running_test_case.rb +2 -53
  84. data/lib/cucumber/runtime/after_hooks.rb +8 -4
  85. data/lib/cucumber/runtime/before_hooks.rb +8 -4
  86. data/lib/cucumber/runtime/for_programming_languages.rb +4 -2
  87. data/lib/cucumber/runtime/step_hooks.rb +6 -2
  88. data/lib/cucumber/runtime/support_code.rb +13 -15
  89. data/lib/cucumber/runtime/user_interface.rb +6 -16
  90. data/lib/cucumber/runtime.rb +34 -58
  91. data/lib/cucumber/step_definition_light.rb +4 -3
  92. data/lib/cucumber/step_definitions.rb +2 -2
  93. data/lib/cucumber/step_match.rb +12 -11
  94. data/lib/cucumber/step_match_search.rb +2 -1
  95. data/lib/cucumber/term/ansicolor.rb +9 -9
  96. data/lib/cucumber/version +1 -1
  97. data/lib/cucumber.rb +1 -1
  98. metadata +251 -81
  99. data/lib/cucumber/formatter/cucumber.css +0 -286
  100. data/lib/cucumber/formatter/cucumber.sass +0 -247
  101. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  102. data/lib/cucumber/formatter/html_builder.rb +0 -121
  103. data/lib/cucumber/formatter/inline-js.js +0 -30
  104. data/lib/cucumber/formatter/jquery-min.js +0 -154
  105. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  106. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  107. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  108. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  109. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  110. data/lib/cucumber/step_argument.rb +0 -25
@@ -1,253 +1,459 @@
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
  #
19
- class Pretty
22
+ class Pretty # rubocop:disable Metrics/ClassLength
20
23
  include FileUtils
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
+ @undefined_parameter_types = []
40
+ @total_duration = 0
41
+ @exceptions = []
42
+ @gherkin_sources = {}
43
+ @step_matches = {}
44
+ @ast_lookup = AstLookup.new(config)
45
+ @counts = ConsoleCounts.new(config)
46
+ @issues = ConsoleIssues.new(config, @ast_lookup)
47
+ @first_feature = true
48
+ @current_feature_uri = ''
49
+ @current_scenario_outline = nil
50
+ @current_examples = nil
51
+ @current_test_case = nil
52
+ @in_scenario_outline = false
53
+ @print_background_steps = false
54
+ @test_step_output = []
55
+ @passed_test_cases = []
56
+ @source_indent = 0
57
+ @next_comment_to_be_printed = 0
58
+
59
+ bind_events(config)
60
+ end
61
+
62
+ def bind_events(config)
63
+ config.on_event :gherkin_source_read, &method(:on_gherkin_source_read)
64
+ config.on_event :step_activated, &method(:on_step_activated)
65
+ config.on_event :test_case_started, &method(:on_test_case_started)
66
+ config.on_event :test_step_started, &method(:on_test_step_started)
67
+ config.on_event :test_step_finished, &method(:on_test_step_finished)
68
+ config.on_event :test_case_finished, &method(:on_test_case_finished)
69
+ config.on_event :test_run_finished, &method(:on_test_run_finished)
70
+ config.on_event :undefined_parameter_type, &method(:collect_undefined_parameter_type_names)
71
+ end
72
+
73
+ def on_gherkin_source_read(event)
74
+ @gherkin_sources[event.path] = event.body
75
+ end
76
+
77
+ def on_step_activated(event)
78
+ test_step, step_match = *event.attributes
79
+ @step_matches[test_step.to_s] = step_match
80
+ end
81
+
82
+ def on_test_case_started(event)
83
+ if !same_feature_as_previous_test_case?(event.test_case.location)
84
+ if first_feature?
85
+ @first_feature = false
86
+ print_profile_information
87
+ else
88
+ print_comments(gherkin_source.split("\n").length, 0)
89
+ @io.puts
90
+ end
91
+ @current_feature_uri = event.test_case.location.file
92
+ @exceptions = []
93
+ print_feature_data
94
+ if feature_has_background?
95
+ print_background_data
96
+ @print_background_steps = true
97
+ @in_scenario_outline = false
98
+ end
99
+ else
100
+ @print_background_steps = false
101
+ end
102
+ @current_test_case = event.test_case
103
+ print_step_header(current_test_case) unless print_background_steps
104
+ end
105
+
106
+ def on_test_step_started(event)
107
+ return if event.test_step.hook?
108
+ print_step_header(current_test_case) if first_step_after_printing_background_steps?(event.test_step)
109
+ end
110
+
111
+ def on_test_step_finished(event)
112
+ collect_snippet_data(event.test_step, @ast_lookup) if event.result.undefined?
113
+ return if in_scenario_outline && !options[:expand]
114
+ exception_to_be_printed = find_exception_to_be_printed(event.result)
115
+ print_step_data(event.test_step, event.result) if print_step_data?(event, exception_to_be_printed)
116
+ print_step_output
117
+ return unless exception_to_be_printed
118
+ print_exception(exception_to_be_printed, event.result.to_sym, 6)
119
+ @exceptions << exception_to_be_printed
120
+ end
121
+
122
+ def on_test_case_finished(event)
123
+ @total_duration += DurationExtractor.new(event.result).result_duration
124
+ @passed_test_cases << event.test_case if config.wip? && event.result.passed?
125
+ if in_scenario_outline && !options[:expand]
126
+ print_row_data(event.test_case, event.result)
127
+ else
128
+ exception_to_be_printed = find_exception_to_be_printed(event.result)
129
+ return unless exception_to_be_printed
130
+ print_exception(exception_to_be_printed, event.result.to_sym, 6)
131
+ @exceptions << exception_to_be_printed
132
+ end
38
133
  end
39
134
 
40
- def before_features(_features)
41
- print_profile_information
135
+ def on_test_run_finished(_event)
136
+ print_comments(gherkin_source.split("\n").length, 0) unless current_feature_uri.empty?
137
+ @io.puts
138
+ print_summary
42
139
  end
43
140
 
44
- def after_features(features)
45
- print_summary(features)
141
+ def attach(src, media_type)
142
+ return unless media_type == 'text/x.cucumber.log+plain'
143
+ @test_step_output.push src
46
144
  end
47
145
 
48
- def before_feature(_feature)
49
- @exceptions = []
50
- @indent = 0
146
+ private
147
+
148
+ def find_exception_to_be_printed(result)
149
+ return nil if result.ok?(options[:strict])
150
+ result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
151
+ exception = result.failed? ? result.exception : result
152
+ return nil if @exceptions.include?(exception)
153
+ exception
51
154
  end
52
155
 
53
- def comment_line(comment_line)
54
- @io.puts(comment_line.indent(@indent))
55
- @io.flush
156
+ def calculate_source_indent(test_case)
157
+ scenario = scenario_source(test_case).scenario
158
+ @source_indent = calculate_source_indent_for_ast_node(scenario)
56
159
  end
57
160
 
58
- def after_tags(_tags)
59
- if @indent == 1
60
- @io.puts
61
- @io.flush
161
+ def calculate_source_indent_for_ast_node(ast_node)
162
+ indent = 4 + ast_node.keyword.length
163
+ indent += 1 + ast_node.name.length
164
+ ast_node.steps.each do |step|
165
+ step_indent = 5 + step.keyword.length + step.text.length
166
+ indent = step_indent if step_indent > indent
62
167
  end
168
+ indent
63
169
  end
64
170
 
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
171
+ def calculate_source_indent_for_expanded_test_case(test_case, scenario_keyword, expanded_name)
172
+ indent = 7 + scenario_keyword.length
173
+ indent += 2 + expanded_name.length
174
+ test_case.test_steps.each do |step|
175
+ if !step.hook? && step.location.lines.max >= test_case.location.lines.max
176
+ step_indent = 9 + test_step_keyword(step).length + step.text.length
177
+ indent = step_indent if step_indent > indent
178
+ end
179
+ end
180
+ indent
70
181
  end
71
182
 
72
- def feature_name(keyword, name)
73
- @io.puts("#{keyword}: #{name}")
74
- @io.puts
75
- @io.flush
183
+ def print_step_output
184
+ @test_step_output.each { |message| @io.puts(format_string(message, :tag).indent(6)) }
185
+ @test_step_output = []
76
186
  end
77
187
 
78
- def before_feature_element(_feature_element)
79
- @indent = 2
80
- @scenario_indent = 2
188
+ def first_feature?
189
+ @first_feature
81
190
  end
82
191
 
83
- def after_feature_element(_feature_element)
84
- print_messages
85
- @io.puts
86
- @io.flush
192
+ def same_feature_as_previous_test_case?(location)
193
+ location.file == current_feature_uri
87
194
  end
88
195
 
89
- def before_background(_background)
90
- @indent = 2
91
- @scenario_indent = 2
92
- @in_background = true
196
+ def feature_has_background?
197
+ feature_children = gherkin_document.feature.children
198
+ return false if feature_children.empty?
199
+ !feature_children.first.background.nil?
93
200
  end
94
201
 
95
- def after_background(_background)
96
- print_messages
97
- @in_background = nil
98
- @io.puts
99
- @io.flush
202
+ def print_step_header(test_case)
203
+ if from_scenario_outline?(test_case)
204
+ @in_scenario_outline = true
205
+ unless same_outline_as_previous_test_case?(test_case)
206
+ @current_scenario_outline = scenario_source(test_case).scenario_outline
207
+ @io.puts
208
+ print_outline_data(current_scenario_outline)
209
+ end
210
+ unless same_examples_as_previous_test_case?(test_case)
211
+ @current_examples = scenario_source(test_case).examples
212
+ @io.puts
213
+ print_examples_data(current_examples)
214
+ end
215
+ print_expanded_row_data(current_test_case) if options[:expand]
216
+ else
217
+ @in_scenario_outline = false
218
+ @current_scenario_outline = nil
219
+ @current_examples = nil
220
+ @io.puts
221
+ @source_indent = calculate_source_indent(current_test_case)
222
+ print_scenario_data(test_case)
223
+ end
100
224
  end
101
225
 
102
- def background_name(keyword, name, file_colon_line, source_indent)
103
- print_feature_element_name(keyword, name, file_colon_line, source_indent)
226
+ def same_outline_as_previous_test_case?(test_case)
227
+ scenario_source(test_case).scenario_outline == current_scenario_outline
104
228
  end
105
229
 
106
- def before_examples_array(_examples_array)
107
- @indent = 4
108
- @io.puts
109
- @visiting_first_example_name = true
230
+ def same_examples_as_previous_test_case?(test_case)
231
+ scenario_source(test_case).examples == current_examples
232
+ end
233
+
234
+ def from_scenario_outline?(test_case)
235
+ scenario = scenario_source(test_case)
236
+ scenario.type != :Scenario
110
237
  end
111
238
 
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}")
239
+ def first_step_after_printing_background_steps?(test_step)
240
+ return false unless print_background_steps
241
+ return false unless test_step.location.lines.max >= current_test_case.location.lines.max
242
+ @print_background_steps = false
243
+ true
244
+ end
245
+
246
+ def print_feature_data
247
+ feature = gherkin_document.feature
248
+ print_language_comment(feature.location.line)
249
+ print_comments(feature.location.line, 0)
250
+ print_tags(feature.tags, 0)
251
+ print_feature_line(feature)
252
+ print_description(feature.description)
116
253
  @io.flush
117
- @indent = 6
118
- @scenario_indent = 6
119
254
  end
120
255
 
121
- def before_outline_table(outline_table)
122
- @table = outline_table
256
+ def print_language_comment(feature_line)
257
+ gherkin_source.split("\n")[0..feature_line].each do |line|
258
+ @io.puts(format_string(line, :comment)) if /# *language *:/ =~ line
259
+ end
123
260
  end
124
261
 
125
- def after_outline_table(_outline_table)
126
- @table = nil
127
- @indent = 4
262
+ def print_comments(up_to_line, indent)
263
+ comments = gherkin_document.comments
264
+ return if comments.empty? || comments.length <= @next_comment_to_be_printed
265
+ comments[@next_comment_to_be_printed..-1].each do |comment|
266
+ if comment.location.line <= up_to_line
267
+ @io.puts(format_string(comment.text.strip, :comment).indent(indent))
268
+ @next_comment_to_be_printed += 1
269
+ end
270
+ break if @next_comment_to_be_printed >= comments.length
271
+ end
128
272
  end
129
273
 
130
- def scenario_name(keyword, name, file_colon_line, source_indent)
131
- print_feature_element_name(keyword, name, file_colon_line, source_indent)
274
+ def print_tags(tags, indent)
275
+ return if !tags || tags.empty?
276
+ @io.puts(tags.map { |tag| format_string(tag.name, :tag) }.join(' ').indent(indent))
132
277
  end
133
278
 
134
- def before_step(step)
135
- @current_step = step
136
- @indent = 6
137
- print_messages
279
+ def print_feature_line(feature)
280
+ print_keyword_name(feature.keyword, feature.name, 0)
138
281
  end
139
282
 
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
283
+ def print_keyword_name(keyword, name, indent, location = nil)
284
+ line = "#{keyword}:"
285
+ line += " #{name}"
286
+ @io.print(line.indent(indent))
287
+ if location && options[:source]
288
+ line_comment = format_string("# #{location}", :comment).indent(@source_indent - line.length - indent)
289
+ @io.print(line_comment)
148
290
  end
149
- if status != :failed && @in_background ^ background
150
- @hide_this_step = true
151
- return
291
+ @io.puts
292
+ end
293
+
294
+ def print_description(description)
295
+ return unless description
296
+ description.split("\n").each do |line|
297
+ @io.puts(line)
152
298
  end
153
- @status = status
154
299
  end
155
300
 
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
301
+ def print_background_data
302
+ @io.puts
303
+ background = gherkin_document.feature.children.first.background
304
+ @source_indent = calculate_source_indent_for_ast_node(background) if options[:source]
305
+ print_comments(background.location.line, 2)
306
+ print_background_line(background)
307
+ print_description(background.description)
308
+ @io.flush
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_background_line(background)
312
+ print_keyword_name(background.keyword, background.name, 2, "#{current_feature_uri}:#{background.location.line}")
313
+ end
314
+
315
+ def print_scenario_data(test_case)
316
+ scenario = scenario_source(test_case).scenario
317
+ print_comments(scenario.location.line, 2)
318
+ print_tags(scenario.tags, 2)
319
+ print_scenario_line(scenario, test_case.location)
320
+ print_description(scenario.description)
169
321
  @io.flush
170
322
  end
171
323
 
172
- def exception(exception, status)
173
- return if @hide_this_step
174
- print_messages
175
- print_exception(exception, status, @indent)
324
+ def print_scenario_line(scenario, location = nil)
325
+ print_keyword_name(scenario.keyword, scenario.name, 2, location)
326
+ end
327
+
328
+ def print_step_data?(event, exception_to_be_printed)
329
+ !event.test_step.hook? && (
330
+ print_background_steps ||
331
+ event.test_step.location.lines.max >= current_test_case.location.lines.max ||
332
+ exception_to_be_printed
333
+ )
334
+ end
335
+
336
+ def print_step_data(test_step, result)
337
+ base_indent = options[:expand] && in_scenario_outline ? 8 : 4
338
+ step_keyword = test_step_keyword(test_step)
339
+ indent = options[:source] ? @source_indent - step_keyword.length - test_step.text.length - base_indent : nil
340
+ print_comments(test_step.location.lines.max, base_indent)
341
+ 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)
342
+ @io.puts(name_to_report.indent(base_indent))
343
+ print_multiline_argument(test_step, result, base_indent + 2) unless options[:no_multiline]
176
344
  @io.flush
177
345
  end
178
346
 
179
- def before_multiline_arg(multiline_arg)
180
- return if @options[:no_multiline] || @hide_this_step
181
- @table = multiline_arg
347
+ def test_step_keyword(test_step)
348
+ step = step_source(test_step).step
349
+ step.keyword
182
350
  end
183
351
 
184
- def after_multiline_arg(_multiline_arg)
185
- @table = nil
352
+ def step_source(test_step)
353
+ @ast_lookup.step_source(test_step)
186
354
  end
187
355
 
188
- def before_table_row(_table_row)
189
- return if !@table || @hide_this_step
190
- @col_index = 0
191
- @io.print ' |'.indent(@indent - 2)
356
+ def scenario_source(test_case)
357
+ @ast_lookup.scenario_source(test_case)
192
358
  end
193
359
 
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
360
+ def gherkin_source
361
+ @gherkin_sources[current_feature_uri]
201
362
  end
202
363
 
203
- def after_table_cell(_cell)
204
- return unless @table
205
- @col_index += 1
364
+ def gherkin_document
365
+ @ast_lookup.gherkin_document(current_feature_uri)
206
366
  end
207
367
 
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
368
+ def print_multiline_argument(test_step, result, indent)
369
+ step = step_source(test_step).step
370
+ if !step.doc_string.nil?
371
+ print_doc_string(step.doc_string.content, result.to_sym, indent)
372
+ elsif !step.data_table.nil?
373
+ print_data_table(step.data_table, result.to_sym, indent)
374
+ end
217
375
  end
218
376
 
219
- def before_test_case(_test_case)
220
- @previous_step_keyword = nil
377
+ def print_data_table(data_table, status, indent)
378
+ data_table.rows.each do |row|
379
+ print_comments(row.location.line, indent)
380
+ @io.puts format_string(gherkin_source.split("\n")[row.location.line - 1].strip, status).indent(indent)
381
+ end
221
382
  end
222
383
 
223
- def after_test_step(test_step, result)
224
- collect_snippet_data(test_step, result)
384
+ def print_outline_data(scenario_outline) # rubocop:disable Metrics/AbcSize
385
+ print_comments(scenario_outline.location.line, 2)
386
+ print_tags(scenario_outline.tags, 2)
387
+ @source_indent = calculate_source_indent_for_ast_node(scenario_outline) if options[:source]
388
+ print_scenario_line(scenario_outline, "#{current_feature_uri}:#{scenario_outline.location.line}")
389
+ print_description(scenario_outline.description)
390
+ scenario_outline.steps.each do |step|
391
+ print_comments(step.location.line, 4)
392
+ step_line = " #{step.keyword}#{step.text}"
393
+ @io.print(format_string(step_line, :skipped))
394
+ if options[:source]
395
+ comment_line = format_string("# #{current_feature_uri}:#{step.location.line}", :comment)
396
+ @io.print(comment_line.indent(@source_indent - step_line.length))
397
+ end
398
+ @io.puts
399
+ next if options[:no_multiline]
400
+ print_doc_string(step.doc_string.content, :skipped, 6) unless step.doc_string.nil?
401
+ print_data_table(step.data_table, :skipped, 6) unless step.data_table.nil?
402
+ end
403
+ @io.flush
225
404
  end
226
405
 
227
- private
406
+ def print_doc_string(content, status, indent)
407
+ s = %("""\n#{content}\n""").indent(indent)
408
+ s = s.split("\n").map { |l| l =~ /^\s+$/ ? '' : l }.join("\n")
409
+ @io.puts(format_string(s, status))
410
+ end
228
411
 
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))
412
+ def print_examples_data(examples)
413
+ print_comments(examples.location.line, 4)
414
+ print_tags(examples.tags, 4)
415
+ print_keyword_name(examples.keyword, examples.name, 4)
416
+ print_description(examples.description)
417
+ unless options[:expand]
418
+ print_comments(examples.table_header.location.line, 6)
419
+ @io.puts(gherkin_source.split("\n")[examples.table_header.location.line - 1].strip.indent(6))
237
420
  end
421
+ @io.flush
422
+ end
423
+
424
+ def print_row_data(test_case, result)
425
+ print_comments(test_case.location.lines.max, 6)
426
+ @io.print(format_string(gherkin_source.split("\n")[test_case.location.lines.max - 1].strip, result.to_sym).indent(6))
427
+ @io.print(format_string(@test_step_output.join(', '), :tag).indent(2)) unless @test_step_output.empty?
428
+ @test_step_output = []
238
429
  @io.puts
239
- names[1..-1].each { |s| @io.puts s.to_s }
430
+ if result.failed? || result.pending?
431
+ result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
432
+ exception = result.failed? ? result.exception : result
433
+ unless @exceptions.include?(exception)
434
+ print_exception(exception, result.to_sym, 6)
435
+ @exceptions << exception
436
+ end
437
+ end
240
438
  @io.flush
241
439
  end
242
440
 
243
- def cell_prefix(status)
244
- @prefixes[status]
441
+ def print_expanded_row_data(test_case)
442
+ feature = gherkin_document.feature
443
+ language_code = feature.language || 'en'
444
+ language = ::Gherkin::Dialect.for(language_code)
445
+ scenario_keyword = language.scenario_keywords[0]
446
+ row = scenario_source(test_case).row
447
+ expanded_name = '| ' + row.cells.map(&:value).join(' | ') + ' |'
448
+ @source_indent = calculate_source_indent_for_expanded_test_case(test_case, scenario_keyword, expanded_name)
449
+ @io.puts
450
+ print_keyword_name(scenario_keyword, expanded_name, 6, test_case.location)
245
451
  end
246
452
 
247
- def print_summary(features)
248
- print_statistics(features.duration, @config, @counts, @issues)
249
- print_snippets(@options)
250
- print_passing_wip(@options)
453
+ def print_summary
454
+ print_statistics(@total_duration, config, @counts, @issues)
455
+ print_snippets(options)
456
+ print_passing_wip(config, @passed_test_cases, @ast_lookup)
251
457
  end
252
458
  end
253
459
  end