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