cucumber 3.2.0 → 4.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
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,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cucumber/formatter/json'
4
-
5
- module Cucumber
6
- module Formatter
7
- # The formatter used for <tt>--format json_pretty</tt>
8
- class JsonPretty < Json
9
- end
10
- end
11
- end
@@ -1,1028 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'forwardable'
3
- require 'delegate'
4
- require 'cucumber/errors'
5
- require 'cucumber/multiline_argument'
6
- require 'cucumber/formatter/backtrace_filter'
7
- require 'cucumber/formatter/legacy_api/ast'
8
-
9
- module Cucumber
10
- module Formatter
11
- module LegacyApi
12
-
13
- Adapter = Struct.new(:formatter, :results, :config) do
14
- extend Forwardable
15
-
16
- def initialize(*)
17
- super
18
- emit_deprecation_warning
19
-
20
- @matches = collect_matches
21
- config.on_event(:test_case_started) do |event|
22
- formatter.before_test_case(event.test_case)
23
- printer.before_test_case(event.test_case)
24
- end
25
- config.on_event(:test_step_started) do |event|
26
- formatter.before_test_step(event.test_step)
27
- printer.before_test_step(event.test_step)
28
- end
29
- config.on_event(:test_step_finished) do |event|
30
- test_step, result = *event.attributes
31
- printer.after_test_step(test_step, result)
32
- formatter.after_test_step(test_step, result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter))
33
- end
34
- config.on_event(:test_case_finished) do |event|
35
- test_case, result = *event.attributes
36
- record_test_case_result(test_case, result)
37
- printer.after_test_case(test_case, result)
38
- formatter.after_test_case(test_case, result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter))
39
- end
40
- config.on_event(:test_run_finished) do
41
- printer.after
42
- formatter.done
43
- end
44
- end
45
-
46
- def_delegators :formatter, :ask
47
- def_delegators :printer, :embed
48
-
49
- def puts(*messages)
50
- printer.puts(messages)
51
- end
52
-
53
- private
54
-
55
- def emit_deprecation_warning
56
- parent_name = formatter_class_name =~ /::[^:]+\Z/ ? $`.freeze : nil
57
- return if parent_name == 'Cucumber::Formatter'
58
- return if !config.out_stream # some unit tests don't set it
59
- config.out_stream.puts "WARNING: The formatter #{formatter.class.name} is using the deprecated formatter API which will be removed in v4.0 of Cucumber."
60
- config.out_stream.puts
61
- end
62
-
63
- def formatter_class_name
64
- formatter.class.name
65
- rescue NoMethodError # when we use the Fanout, things get gnarly
66
- formatter.class[0].class.name
67
- end
68
-
69
- def printer
70
- @printer ||= FeaturesPrinter.new(formatter, results, config, @matches).before
71
- end
72
-
73
- def record_test_case_result(test_case, result)
74
- scenario = LegacyResultBuilder.new(result).scenario("#{test_case.keyword}: #{test_case.name}", test_case.location)
75
- results.scenario_visited(scenario)
76
- end
77
-
78
- def collect_matches
79
- result = {}
80
- config.on_event(:step_activated) do |event|
81
- test_step, step_match = *event.attributes
82
- result[test_step.source.last] = step_match
83
- end
84
- result
85
- end
86
-
87
- require 'cucumber/core/test/timer'
88
- FeaturesPrinter = Struct.new(:formatter, :results, :config, :matches) do
89
- extend Forwardable
90
-
91
- def before
92
- timer.start
93
- formatter.before_features(nil)
94
- self
95
- end
96
-
97
- def before_test_case(test_case)
98
- test_case.describe_source_to(self)
99
- @child.before_test_case(test_case)
100
- end
101
-
102
- def before_test_step(*args)
103
- @child.before_test_step(*args)
104
- end
105
-
106
- def after_test_step(test_step, result)
107
- @child.after_test_step(test_step, result)
108
- end
109
-
110
- def after_test_case(*args)
111
- @child.after_test_case(*args)
112
- end
113
-
114
- def feature(node, *)
115
- if node != @current_feature
116
- @child.after if @child
117
- @child = FeaturePrinter.new(formatter, results, matches, config, node).before
118
- @current_feature = node
119
- end
120
- end
121
-
122
- def scenario(node, *)
123
- end
124
-
125
- def scenario_outline(node, *)
126
- end
127
-
128
- def examples_table(node, *)
129
- end
130
-
131
- def examples_table_row(node, *)
132
- end
133
-
134
- def after
135
- @child.after if @child
136
- formatter.after_features Ast::Features.new(timer.sec)
137
- self
138
- end
139
-
140
- def puts(messages)
141
- @child.puts(messages)
142
- end
143
-
144
- def embed(src, mime_type, label)
145
- @child.embed(src, mime_type, label)
146
- end
147
-
148
- private
149
-
150
- def timer
151
- @timer ||= Cucumber::Core::Test::Timer.new
152
- end
153
-
154
- end
155
-
156
- module TestCaseSource
157
- def self.for(test_case, result)
158
- collector = Collector.new
159
- test_case.describe_source_to collector, result
160
- collector.result.freeze
161
- end
162
-
163
- class Collector
164
- attr_reader :result
165
-
166
- def initialize
167
- @result = CaseSource.new
168
- end
169
-
170
- def method_missing(name, node, _test_case_result, *_args)
171
- result.send "#{name}=", node
172
- end
173
- end
174
-
175
- require 'ostruct'
176
- class CaseSource < OpenStruct
177
- end
178
- end
179
-
180
- module TestStepSource
181
- def self.for(test_step, result)
182
- collector = Collector.new
183
- test_step.describe_source_to collector, result
184
- collector.result.freeze
185
- end
186
-
187
- class Collector
188
- attr_reader :result
189
-
190
- def initialize
191
- @result = StepSource.new
192
- end
193
-
194
- def method_missing(name, node, step_result, *_args)
195
- result.send "#{name}=", node
196
- result.send "#{name}_result=", LegacyResultBuilder.new(step_result)
197
- end
198
- end
199
-
200
- require 'ostruct'
201
- class StepSource < OpenStruct
202
- def build_step_invocation(indent, matches, config, messages, embeddings)
203
- step_result.step_invocation(
204
- matches.fetch(step) { NoStepMatch.new(step, step.text) },
205
- step,
206
- indent,
207
- background,
208
- config,
209
- messages,
210
- embeddings
211
- )
212
- end
213
- end
214
-
215
- end
216
-
217
- Embedding = Struct.new(:src, :mime_type, :label) do
218
-
219
- def send_to_formatter(formatter)
220
- formatter.embed(src, mime_type, label)
221
- end
222
- end
223
-
224
- FeaturePrinter = Struct.new(:formatter, :results, :matches, :config, :node) do
225
-
226
- def before
227
- formatter.before_feature(node)
228
- language_comment = node.language.iso_code != 'en' ? ["# language: #{node.language.iso_code}"] : []
229
- Ast::Comments.new(language_comment + node.comments).accept(formatter)
230
- Ast::Tags.new(node.tags).accept(formatter)
231
- formatter.feature_name node.keyword, node.legacy_conflated_name_and_description
232
- @delayed_messages = []
233
- @delayed_embeddings = []
234
- self
235
- end
236
-
237
- attr_reader :current_test_step_source
238
-
239
- def before_test_case(_test_case)
240
- @before_hook_results = Ast::HookResultCollection.new
241
- @test_step_results = []
242
- end
243
-
244
- def before_test_step(test_step)
245
- end
246
-
247
- def after_test_step(test_step, result)
248
- @current_test_step_source = TestStepSource.for(test_step, result)
249
- # TODO: stop calling self, and describe source to another object
250
- test_step.describe_source_to(self, result)
251
- print_step
252
- @test_step_results << result
253
- end
254
-
255
- def after_test_case(test_case, test_case_result)
256
- if current_test_step_source && current_test_step_source.step_result.nil?
257
- switch_step_container
258
- end
259
-
260
- if test_case_result.failed? && !any_test_steps_failed?
261
- # around hook must have failed. Print the error.
262
- switch_step_container(TestCaseSource.for(test_case, test_case_result))
263
- LegacyResultBuilder.new(test_case_result).describe_exception_to formatter
264
- end
265
-
266
- # messages and embedding should already have been handled, but just in case...
267
- @delayed_messages.each { |message| formatter.puts(message) }
268
- @delayed_embeddings.each { |embedding| embedding.send_to_formatter(formatter) }
269
- @delayed_messages = []
270
- @delayed_embeddings = []
271
-
272
- @child.after_test_case(test_case, test_case_result) if @child
273
- @previous_test_case_background = @current_test_case_background
274
- @previous_test_case_scenario_outline = current_test_step_source && current_test_step_source.scenario_outline
275
- end
276
-
277
- def before_hook(_location, result)
278
- @before_hook_results << Ast::HookResult.new(LegacyResultBuilder.new(result), @delayed_messages, @delayed_embeddings)
279
- @delayed_messages = []
280
- @delayed_embeddings = []
281
- end
282
-
283
- def after_hook(_location, result)
284
- # if the scenario has no steps, we can hit this before we've created the scenario printer
285
- # ideally we should call switch_step_container in before_step_step
286
- switch_step_container if !@child
287
- @child.after_hook Ast::HookResult.new(LegacyResultBuilder.new(result), @delayed_messages, @delayed_embeddings)
288
- @delayed_messages = []
289
- @delayed_embeddings = []
290
- end
291
-
292
- def after_step_hook(_hook, result)
293
- p current_test_step_source if current_test_step_source.step.nil?
294
- line = current_test_step_source.step.backtrace_line
295
- @child.after_step_hook Ast::HookResult.new(LegacyResultBuilder.new(result).
296
- append_to_exception_backtrace(line), @delayed_messages, @delayed_embeddings)
297
- @delayed_messages = []
298
- @delayed_embeddings = []
299
- end
300
-
301
- def background(node, *)
302
- @current_test_case_background = node
303
- end
304
-
305
- def puts(messages)
306
- @delayed_messages.push *messages
307
- end
308
-
309
- def embed(src, mime_type, label)
310
- @delayed_embeddings.push Embedding.new(src, mime_type, label)
311
- end
312
-
313
- def step(*);end
314
- def scenario(*);end
315
- def scenario_outline(*);end
316
- def examples_table(*);end
317
- def examples_table_row(*);end
318
- def feature(*);end
319
-
320
- def after
321
- @child.after if @child
322
- formatter.after_feature(node)
323
- self
324
- end
325
-
326
- private
327
-
328
- attr_reader :before_hook_results
329
- private :before_hook_results
330
-
331
- def any_test_steps_failed?
332
- @test_step_results.any? &:failed?
333
- end
334
-
335
- def switch_step_container(source = current_test_step_source)
336
- switch_to_child select_step_container(source), source
337
- end
338
-
339
- def select_step_container(source)
340
- if source.background
341
- if same_background_as_previous_test_case?(source)
342
- HiddenBackgroundPrinter.new(formatter, source.background)
343
- else
344
- BackgroundPrinter.new(formatter, node, source.background, before_hook_results)
345
- end
346
- elsif source.scenario
347
- ScenarioPrinter.new(formatter, source.scenario, before_hook_results)
348
- elsif source.scenario_outline
349
- if same_scenario_outline_as_previous_test_case?(source) && @previous_outline_child
350
- @previous_outline_child
351
- else
352
- ScenarioOutlinePrinter.new(formatter, config, source.scenario_outline)
353
- end
354
- else
355
- raise 'unknown step container'
356
- end
357
- end
358
-
359
- def same_background_as_previous_test_case?(source)
360
- source.background == @previous_test_case_background
361
- end
362
-
363
- def same_scenario_outline_as_previous_test_case?(source)
364
- source.scenario_outline == @previous_test_case_scenario_outline
365
- end
366
-
367
- def print_step
368
- return unless current_test_step_source.step_result
369
- switch_step_container
370
-
371
- if current_test_step_source.scenario_outline
372
- @child.examples_table(current_test_step_source.examples_table)
373
- @child.examples_table_row(current_test_step_source.examples_table_row, before_hook_results)
374
- end
375
-
376
- if @failed_hidden_background_step
377
- indent = Indent.new(@child.node)
378
- step_invocation = @failed_hidden_background_step.build_step_invocation(indent, matches, config, [], [])
379
- @child.step_invocation(step_invocation, @failed_hidden_background_step)
380
- @failed_hidden_background_step = nil
381
- end
382
-
383
- unless @last_step == current_test_step_source.step
384
- indent ||= Indent.new(@child.node)
385
- step_invocation = current_test_step_source.build_step_invocation(indent, matches, config, @delayed_messages, @delayed_embeddings)
386
- results.step_visited step_invocation
387
- @child.step_invocation(step_invocation, current_test_step_source)
388
- @last_step = current_test_step_source.step
389
- end
390
- @delayed_messages = []
391
- @delayed_embeddings = []
392
- end
393
-
394
- def switch_to_child(child, source)
395
- return if @child == child
396
- if @child
397
- if from_first_background(@child)
398
- @first_background_failed = @child.failed?
399
- elsif from_hidden_background(@child)
400
- if not @first_background_failed
401
- @failed_hidden_background_step = @child.get_failed_step_source
402
- end
403
- if @previous_outline_child
404
- @previous_outline_child.after unless same_scenario_outline_as_previous_test_case?(source)
405
- end
406
- end
407
- if from_scenario_outline_to_hidden_background(@child, child)
408
- @previous_outline_child = @child
409
- else
410
- @child.after
411
- @previous_outline_child = nil
412
- end
413
- end
414
- child.before unless to_scenario_outline(child) && same_scenario_outline_as_previous_test_case?(source)
415
- @child = child
416
- end
417
-
418
- def from_scenario_outline_to_hidden_background(from, to)
419
- from.class.name == ScenarioOutlinePrinter.name &&
420
- to.class.name == HiddenBackgroundPrinter.name
421
- end
422
-
423
- def from_first_background(from)
424
- from.class.name == BackgroundPrinter.name
425
- end
426
-
427
- def from_hidden_background(from)
428
- from.class.name == HiddenBackgroundPrinter.name
429
- end
430
-
431
- def to_scenario_outline(to)
432
- to.class.name == ScenarioOutlinePrinter.name
433
- end
434
-
435
- end
436
-
437
- module PrintsAfterHooks
438
- def after_hook_results
439
- @after_hook_results ||= Ast::HookResultCollection.new
440
- end
441
-
442
- def after_hook(result)
443
- after_hook_results << result
444
- end
445
- end
446
-
447
- # Basic printer used by default
448
- class AfterHookPrinter
449
- attr_reader :formatter
450
-
451
- def initialize(formatter)
452
- @formatter = formatter
453
- end
454
-
455
- include PrintsAfterHooks
456
-
457
- def after
458
- after_hook_results.accept(formatter)
459
- end
460
- end
461
-
462
- BackgroundPrinter = Struct.new(:formatter, :feature, :node, :before_hook_results) do
463
-
464
- def after_test_case(*)
465
- end
466
-
467
- def after_hook(*)
468
- end
469
-
470
- def before
471
- formatter.before_background Ast::Background.new(feature, node)
472
- Ast::Comments.new(node.comments).accept(formatter)
473
- formatter.background_name node.keyword, node.legacy_conflated_name_and_description, node.location.to_s, indent.of(node)
474
- before_hook_results.accept(formatter)
475
- self
476
- end
477
-
478
- def after_step_hook(result)
479
- result.accept formatter
480
- end
481
-
482
- def step_invocation(step_invocation, source)
483
- @child ||= StepsPrinter.new(formatter).before
484
- @child.step_invocation step_invocation
485
- if source.step_result.status == :failed
486
- @failed = true
487
- end
488
- end
489
-
490
- def after
491
- @child.after if @child
492
- formatter.after_background(Ast::Background.new(feature, node))
493
- self
494
- end
495
-
496
- def failed?
497
- @failed
498
- end
499
-
500
- private
501
-
502
- def indent
503
- @indent ||= Indent.new(node)
504
- end
505
- end
506
-
507
- # Printer to handle background steps for anything but the first scenario in a
508
- # feature. These steps should not be printed.
509
- class HiddenBackgroundPrinter < Struct.new(:formatter, :node)
510
- def get_failed_step_source
511
- return @source_of_failed_step
512
- end
513
-
514
- def step_invocation(_step_invocation, source)
515
- if source.step_result.status == :failed
516
- @source_of_failed_step = source
517
- end
518
- end
519
-
520
- def before;self;end
521
- def after;self;end
522
- def before_hook(*);end
523
- def after_hook(*);end
524
- def after_step_hook(*);end
525
- def examples_table(*);end
526
- def after_test_case(*);end
527
- end
528
-
529
- ScenarioPrinter = Struct.new(:formatter, :node, :before_hook_results) do
530
- include PrintsAfterHooks
531
-
532
- def before
533
- formatter.before_feature_element(node)
534
- Ast::Comments.new(node.comments).accept(formatter)
535
- Ast::Tags.new(node.tags).accept(formatter)
536
- formatter.scenario_name node.keyword, node.legacy_conflated_name_and_description, node.location.to_s, indent.of(node)
537
- before_hook_results.accept(formatter)
538
- self
539
- end
540
-
541
- def step_invocation(step_invocation, _source)
542
- @child ||= StepsPrinter.new(formatter).before
543
- @child.step_invocation step_invocation
544
- end
545
-
546
- def after_step_hook(result)
547
- result.accept formatter
548
- end
549
-
550
- def after_test_case(_test_case, result)
551
- @test_case_result = result
552
- after
553
- end
554
-
555
- def after
556
- return if @done
557
- @child.after if @child
558
- scenario = LegacyResultBuilder.new(@test_case_result).scenario(node.name, node.location)
559
- after_hook_results.accept(formatter)
560
- formatter.after_feature_element(scenario)
561
- @done = true
562
- self
563
- end
564
-
565
- private
566
-
567
- def indent
568
- @indent ||= Indent.new(node)
569
- end
570
- end
571
-
572
- StepsPrinter = Struct.new(:formatter) do
573
- def before
574
- formatter.before_steps(nil)
575
- self
576
- end
577
-
578
- def step_invocation(step_invocation)
579
- steps << step_invocation
580
- step_invocation.accept(formatter)
581
- self
582
- end
583
-
584
- def after
585
- formatter.after_steps(steps)
586
- self
587
- end
588
-
589
- private
590
-
591
- def steps
592
- @steps ||= Ast::StepInvocations.new
593
- end
594
-
595
- end
596
-
597
- ScenarioOutlinePrinter = Struct.new(:formatter, :configuration, :node) do
598
- extend Forwardable
599
- def_delegators :@child, :after_hook, :after_step_hook
600
-
601
- def before
602
- formatter.before_feature_element(node)
603
- Ast::Comments.new(node.comments).accept(formatter)
604
- Ast::Tags.new(node.tags).accept(formatter)
605
- formatter.scenario_name node.keyword, node.legacy_conflated_name_and_description, node.location.to_s, indent.of(node)
606
- OutlineStepsPrinter.new(formatter, configuration, indent).print(node)
607
- self
608
- end
609
-
610
- def step_invocation(step_invocation, source)
611
- _node, result = source.step, source.step_result
612
- @last_step_result = result
613
- @child.step_invocation(step_invocation, source)
614
- end
615
-
616
- def examples_table(examples_table)
617
- @child ||= ExamplesArrayPrinter.new(formatter, configuration).before
618
- @child.examples_table(examples_table)
619
- end
620
-
621
- def examples_table_row(node, before_hook_results)
622
- @child.examples_table_row(node, before_hook_results)
623
- end
624
-
625
- def after_test_case(test_case, result)
626
- @child.after_test_case(test_case, result)
627
- end
628
-
629
- def after
630
- @child.after if @child
631
- # TODO: the last step result might not accurately reflect the
632
- # overall scenario result.
633
- scenario_outline = last_step_result.scenario_outline(node.name, node.location)
634
- formatter.after_feature_element(scenario_outline)
635
- self
636
- end
637
-
638
- private
639
-
640
- def last_step_result
641
- @last_step_result || Core::Test::Result::Unknown.new
642
- end
643
-
644
- def indent
645
- @indent ||= Indent.new(node)
646
- end
647
- end
648
-
649
- OutlineStepsPrinter = Struct.new(:formatter, :configuration, :indent, :outline) do
650
- def print(node)
651
- node.describe_to self
652
- steps_printer.after
653
- end
654
-
655
- def scenario_outline(_node, &descend)
656
- descend.call(self)
657
- end
658
-
659
- def outline_step(step)
660
- step_match = NoStepMatch.new(step, step.text)
661
- step_invocation = LegacyResultBuilder.new(Core::Test::Result::Skipped.new).
662
- step_invocation(step_match, step, indent, nil, configuration, [], [])
663
- steps_printer.step_invocation step_invocation
664
- end
665
-
666
- def examples_table(*);end
667
-
668
- private
669
-
670
- def steps_printer
671
- @steps_printer ||= StepsPrinter.new(formatter).before
672
- end
673
- end
674
-
675
- ExamplesArrayPrinter = Struct.new(:formatter, :configuration) do
676
- extend Forwardable
677
- def_delegators :@child, :step_invocation, :after_hook, :after_step_hook, :after_test_case, :examples_table_row
678
-
679
- def before
680
- formatter.before_examples_array(:examples_array)
681
- self
682
- end
683
-
684
- def examples_table(examples_table)
685
- return if examples_table == @current
686
- @child.after if @child
687
- @child = ExamplesTablePrinter.new(formatter, configuration, examples_table).before
688
- @current = examples_table
689
- end
690
-
691
- def after
692
- @child.after if @child
693
- formatter.after_examples_array
694
- self
695
- end
696
- end
697
-
698
- ExamplesTablePrinter = Struct.new(:formatter, :configuration, :node) do
699
- extend Forwardable
700
- def_delegators :@child, :step_invocation, :after_hook, :after_step_hook, :after_test_case
701
-
702
- def before
703
- formatter.before_examples(node)
704
- Ast::Comments.new(node.comments).accept(formatter)
705
- Ast::Tags.new(node.tags).accept(formatter)
706
- formatter.examples_name(node.keyword, node.legacy_conflated_name_and_description)
707
- formatter.before_outline_table(legacy_table)
708
- if !configuration.expand?
709
- HeaderTableRowPrinter.new(formatter, ExampleTableRow.new(node.header), Ast::Node.new).before.after
710
- end
711
- self
712
- end
713
-
714
- def examples_table_row(examples_table_row, before_hook_results)
715
- return if examples_table_row == @current
716
- @child.after if @child
717
- row = ExampleTableRow.new(examples_table_row)
718
- @child = if !configuration.expand?
719
- TableRowPrinter.new(formatter, row, before_hook_results).before
720
- else
721
- ExpandTableRowPrinter.new(formatter, row, before_hook_results).before
722
- end
723
- @current = examples_table_row
724
- end
725
-
726
- def after_test_case(*args)
727
- @child.after_test_case(*args)
728
- end
729
-
730
- def after
731
- @child.after if @child
732
- formatter.after_outline_table(node)
733
- formatter.after_examples(node)
734
- self
735
- end
736
-
737
- private
738
-
739
- def legacy_table
740
- LegacyTable.new(node)
741
- end
742
-
743
- class ExampleTableRow < SimpleDelegator
744
- def dom_id
745
- file_colon_line.gsub(/[\/\.:]/, '_')
746
- end
747
- end
748
-
749
- LegacyTable = Struct.new(:node) do
750
- def col_width(index)
751
- max_width = FindMaxWidth.new(index)
752
- node.describe_to max_width
753
- max_width.result
754
- end
755
-
756
- require 'cucumber/gherkin/formatter/escaping'
757
- FindMaxWidth = Struct.new(:index) do
758
- include ::Cucumber::Gherkin::Formatter::Escaping
759
-
760
- def examples_table(table, &descend)
761
- @result = char_length_of(table.header.values[index])
762
- descend.call(self)
763
- end
764
-
765
- def examples_table_row(row, &_descend)
766
- width = char_length_of(row.values[index])
767
- @result = width if width > result
768
- end
769
-
770
- def result
771
- @result ||= 0
772
- end
773
-
774
- private
775
- def char_length_of(cell)
776
- escape_cell(cell).unpack('U*').length
777
- end
778
- end
779
- end
780
- end
781
-
782
- class TableRowPrinterBase < Struct.new(:formatter, :node, :before_hook_results)
783
- include PrintsAfterHooks
784
-
785
- def after_step_hook(result)
786
- @after_step_hook_result ||= Ast::HookResultCollection.new
787
- @after_step_hook_result << result
788
- end
789
-
790
- def after_test_case(*_args)
791
- after
792
- end
793
-
794
- private
795
-
796
- def indent
797
- :not_needed
798
- end
799
-
800
- def legacy_table_row
801
- Ast::ExampleTableRow.new(exception, @status, node.values, node.location, node.language)
802
- end
803
-
804
- def exception
805
- return nil unless @failed_step
806
- @failed_step.exception
807
- end
808
- end
809
-
810
- class HeaderTableRowPrinter < TableRowPrinterBase
811
- def legacy_table_row
812
- Ast::ExampleTableRow.new(exception, @status, node.values, node.location, Ast::NullLanguage.new)
813
- end
814
-
815
- def before
816
- Ast::Comments.new(node.comments).accept(formatter)
817
- formatter.before_table_row(node)
818
- self
819
- end
820
-
821
- def after
822
- node.values.each do |value|
823
- formatter.before_table_cell(value)
824
- formatter.table_cell_value(value, :skipped_param)
825
- formatter.after_table_cell(value)
826
- end
827
- formatter.after_table_row(legacy_table_row)
828
- self
829
- end
830
- end
831
-
832
-
833
- class TableRowPrinter < TableRowPrinterBase
834
- def before
835
- before_hook_results.accept(formatter)
836
- Ast::Comments.new(node.comments).accept(formatter)
837
- formatter.before_table_row(node)
838
- self
839
- end
840
-
841
- def step_invocation(step_invocation, source)
842
- result = source.step_result
843
- step_invocation.messages.each { |message| formatter.puts(message) }
844
- step_invocation.embeddings.each { |embedding| embedding.send_to_formatter(formatter) }
845
- @failed_step = step_invocation if result.status == :failed
846
- @status = step_invocation.status unless already_failed?
847
- end
848
-
849
- def after
850
- return if @done
851
- @child.after if @child
852
- node.values.each do |value|
853
- formatter.before_table_cell(value)
854
- formatter.table_cell_value(value, @status || :skipped)
855
- formatter.after_table_cell(value)
856
- end
857
- @after_step_hook_result.send_output_to(formatter) if @after_step_hook_result
858
- after_hook_results.send_output_to(formatter)
859
- formatter.after_table_row(legacy_table_row)
860
- @after_step_hook_result.describe_exception_to(formatter) if @after_step_hook_result
861
- after_hook_results.describe_exception_to(formatter)
862
- @done = true
863
- self
864
- end
865
-
866
- private
867
-
868
- def already_failed?
869
- @status == :failed || @status == :undefined || @status == :pending
870
- end
871
- end
872
-
873
- class ExpandTableRowPrinter < TableRowPrinterBase
874
- def before
875
- before_hook_results.accept(formatter)
876
- self
877
- end
878
-
879
- def step_invocation(step_invocation, source)
880
- result = source.step_result
881
- @table_row ||= legacy_table_row
882
- step_invocation.indent.record_width_of(@table_row)
883
- if !@scenario_name_printed
884
- print_scenario_name(step_invocation, @table_row)
885
- @scenario_name_printed = true
886
- end
887
- step_invocation.accept(formatter)
888
- @failed_step = step_invocation if result.status == :failed
889
- @status = step_invocation.status unless @status == :failed
890
- end
891
-
892
- def after
893
- return if @done
894
- @child.after if @child
895
- @after_step_hook_result.accept(formatter) if @after_step_hook_result
896
- after_hook_results.accept(formatter)
897
- @done = true
898
- self
899
- end
900
-
901
- private
902
-
903
- def print_scenario_name(step_invocation, table_row)
904
- formatter.scenario_name table_row.keyword, table_row.name, node.location.to_s, step_invocation.indent.of(table_row)
905
- end
906
- end
907
-
908
- class Indent
909
- def initialize(node)
910
- @widths = []
911
- node.describe_to(self)
912
- end
913
-
914
- [:background, :scenario, :scenario_outline].each do |node_name|
915
- define_method(node_name) do |node, &descend|
916
- record_width_of node
917
- descend.call(self)
918
- end
919
- end
920
-
921
- [:step, :outline_step].each do |node_name|
922
- define_method(node_name) do |node|
923
- record_width_of node
924
- end
925
- end
926
-
927
- def examples_table(*); end
928
- def examples_table_row(*); end
929
-
930
- def of(node)
931
- # The length of the instantiated steps in --expand mode are currently
932
- # not included in the calculation of max => make sure to return >= 1
933
- [1, max - node.to_s.length - node.keyword.length].max
934
- end
935
-
936
- def record_width_of(node)
937
- @widths << node.keyword.length + node.to_s.length + 1
938
- end
939
-
940
- private
941
-
942
- def max
943
- @widths.max
944
- end
945
- end
946
-
947
- class LegacyResultBuilder
948
- attr_reader :status
949
- def initialize(result)
950
- @result = result
951
- @result.describe_to(self)
952
- end
953
-
954
- def passed
955
- @status = :passed
956
- end
957
-
958
- def failed
959
- @status = :failed
960
- end
961
-
962
- def undefined
963
- @status = :undefined
964
- end
965
-
966
- def skipped
967
- @status = :skipped
968
- end
969
-
970
- def pending(exception, *)
971
- @exception = exception
972
- @status = :pending
973
- end
974
-
975
- def exception(exception, *)
976
- @exception = exception
977
- end
978
-
979
- def append_to_exception_backtrace(line)
980
- @exception.set_backtrace(@exception.backtrace + [line.to_s]) if @exception
981
- return self
982
- end
983
-
984
- def duration(duration, *)
985
- @duration = duration
986
- end
987
-
988
- def step_invocation(step_match, step, indent, background, configuration, messages, embeddings)
989
- Ast::StepInvocation.new(step_match, @status, @duration, step_exception(step, configuration), indent, background, step, messages, embeddings)
990
- end
991
-
992
- def scenario(name, location)
993
- Ast::Scenario.new(@status, name, location)
994
- end
995
-
996
- def scenario_outline(name, location)
997
- Ast::ScenarioOutline.new(@status, name, location)
998
- end
999
-
1000
- def describe_exception_to(formatter)
1001
- formatter.exception(filtered_exception, @status) if @exception
1002
- end
1003
-
1004
- private
1005
-
1006
- def step_exception(step, configuration)
1007
- return filtered_step_exception(step) if @exception
1008
- return nil unless @status == :undefined && configuration.strict.strict?(:undefined)
1009
- @exception = Cucumber::Undefined.from(@result, step.text)
1010
- @exception.backtrace << step.backtrace_line
1011
- filtered_step_exception(step)
1012
- end
1013
-
1014
- def filtered_exception
1015
- Cucumber::Formatter::BacktraceFilter.new(@exception.dup).exception
1016
- end
1017
-
1018
- def filtered_step_exception(_step)
1019
- exception = filtered_exception
1020
- return Cucumber::Formatter::BacktraceFilter.new(exception).exception
1021
- end
1022
- end
1023
-
1024
- end
1025
-
1026
- end
1027
- end
1028
- end