cucumber 2.0.0.beta.3 → 2.0.0.beta.4

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +20 -3
  3. data/cucumber.gemspec +2 -1
  4. data/examples/tcl/features/step_definitions/fib_steps.rb +1 -1
  5. data/features/docs/extending_cucumber/custom_formatter.feature +65 -7
  6. data/features/docs/formatters/debug_formatter.feature +24 -17
  7. data/features/docs/formatters/pretty_formatter.feature +42 -0
  8. data/features/docs/formatters/rerun_formatter.feature +3 -2
  9. data/lib/cucumber/cli/configuration.rb +3 -7
  10. data/lib/cucumber/cli/main.rb +1 -1
  11. data/lib/cucumber/{runtime → filters}/gated_receiver.rb +5 -1
  12. data/lib/cucumber/filters/quit.rb +24 -0
  13. data/lib/cucumber/filters/randomizer.rb +36 -0
  14. data/lib/cucumber/filters/tag_limits.rb +40 -0
  15. data/lib/cucumber/{runtime → filters}/tag_limits/test_case_index.rb +4 -2
  16. data/lib/cucumber/{runtime → filters}/tag_limits/verifier.rb +4 -2
  17. data/lib/cucumber/formatter/console.rb +2 -2
  18. data/lib/cucumber/formatter/debug.rb +1 -8
  19. data/lib/cucumber/formatter/fanout.rb +27 -0
  20. data/lib/cucumber/formatter/gherkin_formatter_adapter.rb +1 -3
  21. data/lib/cucumber/formatter/html.rb +12 -4
  22. data/lib/cucumber/formatter/ignore_missing_messages.rb +20 -0
  23. data/lib/cucumber/formatter/junit.rb +2 -2
  24. data/lib/cucumber/formatter/legacy_api/adapter.rb +1008 -0
  25. data/lib/cucumber/formatter/legacy_api/ast.rb +374 -0
  26. data/lib/cucumber/formatter/legacy_api/results.rb +51 -0
  27. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +30 -0
  28. data/lib/cucumber/formatter/pretty.rb +4 -0
  29. data/lib/cucumber/formatter/rerun.rb +14 -88
  30. data/lib/cucumber/language_support/language_methods.rb +0 -54
  31. data/lib/cucumber/multiline_argument/data_table.rb +3 -4
  32. data/lib/cucumber/platform.rb +1 -1
  33. data/lib/cucumber/runtime.rb +41 -107
  34. data/spec/cucumber/{runtime → filters}/gated_receiver_spec.rb +3 -3
  35. data/spec/cucumber/{runtime → filters}/tag_limits/test_case_index_spec.rb +3 -3
  36. data/spec/cucumber/{runtime → filters}/tag_limits/verifier_spec.rb +4 -4
  37. data/spec/cucumber/{runtime/tag_limits/filter_spec.rb → filters/tag_limits_spec.rb} +6 -6
  38. data/spec/cucumber/formatter/debug_spec.rb +39 -530
  39. data/spec/cucumber/formatter/html_spec.rb +56 -0
  40. data/spec/cucumber/formatter/legacy_api/adapter_spec.rb +1902 -0
  41. data/spec/cucumber/formatter/pretty_spec.rb +128 -0
  42. data/spec/cucumber/formatter/rerun_spec.rb +106 -0
  43. data/spec/cucumber/formatter/spec_helper.rb +6 -2
  44. data/spec/cucumber/rb_support/rb_language_spec.rb +2 -2
  45. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +1 -1
  46. data/spec/cucumber/runtime_spec.rb +1 -5
  47. data/spec/spec_helper.rb +2 -0
  48. metadata +44 -29
  49. data/features/docs/extending_cucumber/formatter_callbacks.feature +0 -370
  50. data/features/docs/output_from_hooks.feature +0 -128
  51. data/lib/cucumber/reports/legacy_formatter.rb +0 -1349
  52. data/lib/cucumber/runtime/results.rb +0 -64
  53. data/lib/cucumber/runtime/tag_limits.rb +0 -15
  54. data/lib/cucumber/runtime/tag_limits/filter.rb +0 -31
  55. data/spec/cucumber/reports/legacy_formatter_spec.rb +0 -1860
  56. data/spec/cucumber/runtime/results_spec.rb +0 -88
@@ -1,1349 +0,0 @@
1
- require 'forwardable'
2
- require 'delegate'
3
- require 'cucumber/errors'
4
- require 'cucumber/multiline_argument'
5
-
6
- module Cucumber
7
- module Reports
8
-
9
- class FormatterWrapper < BasicObject
10
- attr_reader :formatters
11
- private :formatters
12
-
13
- def initialize(formatters)
14
- @formatters = formatters
15
- end
16
-
17
- def method_missing(message, *args)
18
- formatters.each do |formatter|
19
- formatter.send(message, *args) if formatter.respond_to?(message)
20
- end
21
- end
22
-
23
- def respond_to_missing?(name, include_private = false)
24
- formatters.any? { |formatter| formatter.respond_to?(name, include_private) }
25
- end
26
-
27
- end
28
-
29
- LegacyFormatter = Struct.new(:runtime, :formatter) do
30
-
31
- def initialize(runtime, formatters)
32
- super runtime, FormatterWrapper.new(formatters)
33
- end
34
-
35
- extend Forwardable
36
-
37
- def_delegators :formatter,
38
- :ask
39
-
40
- def_delegators :printer,
41
- :before_test_case,
42
- :before_test_step,
43
- :after_test_step
44
-
45
- def after_test_case(test_case, result)
46
- record_test_case_result(test_case, result)
47
- printer.after_test_case(test_case, result)
48
- end
49
-
50
- def puts(*messages)
51
- printer.puts(messages)
52
- end
53
-
54
- def embed(src, mime_type, label)
55
- printer.embed(src, mime_type, label)
56
- end
57
-
58
- def done
59
- printer.after
60
- end
61
-
62
- private
63
-
64
- def printer
65
- @printer ||= FeaturesPrinter.new(formatter, runtime).before
66
- end
67
-
68
- def record_test_case_result(test_case, result)
69
- scenario = LegacyResultBuilder.new(result).scenario(test_case.name, test_case.location)
70
- runtime.record_result(scenario)
71
- end
72
-
73
- require 'cucumber/core/test/timer'
74
- FeaturesPrinter = Struct.new(:formatter, :runtime) do
75
- extend Forwardable
76
-
77
- def before
78
- timer.start
79
- formatter.before_features(nil)
80
- self
81
- end
82
-
83
- def before_test_case(test_case)
84
- test_case.describe_source_to(self)
85
- @child.before_test_case(test_case)
86
- end
87
-
88
- def before_test_step(*args)
89
- @child.before_test_step(*args)
90
- end
91
-
92
- def after_test_step(test_step, result)
93
- @child.after_test_step(test_step, result)
94
- end
95
-
96
- def after_test_case(*args)
97
- @child.after_test_case(*args)
98
- end
99
-
100
- def feature(node, *)
101
- if node != @current_feature
102
- @child.after if @child
103
- @child = FeaturePrinter.new(formatter, runtime, node).before
104
- @current_feature = node
105
- end
106
- end
107
-
108
- def scenario(node, *)
109
- end
110
-
111
- def scenario_outline(node, *)
112
- end
113
-
114
- def examples_table(node, *)
115
- end
116
-
117
- def examples_table_row(node, *)
118
- end
119
-
120
- def after
121
- @child.after if @child
122
- formatter.after_features Legacy::Ast::Features.new(timer.sec)
123
- self
124
- end
125
-
126
- def puts(messages)
127
- @child.puts(messages)
128
- end
129
-
130
- def embed(src, mime_type, label)
131
- @child.embed(src, mime_type, label)
132
- end
133
-
134
- private
135
-
136
- def timer
137
- @timer ||= Cucumber::Core::Test::Timer.new
138
- end
139
- end
140
-
141
- module TestStepSource
142
- def self.for(test_step, result)
143
- collector = Collector.new
144
- test_step.describe_source_to collector, result
145
- collector.result.freeze
146
- end
147
-
148
- class Collector
149
- attr_reader :result
150
-
151
- def initialize
152
- @result = StepSource.new
153
- end
154
-
155
- def method_missing(name, node, step_result, *args)
156
- result.send "#{name}=", node
157
- result.send "#{name}_result=", LegacyResultBuilder.new(step_result)
158
- end
159
- end
160
-
161
- require 'ostruct'
162
- class StepSource < OpenStruct
163
- def build_step_invocation(indent, runtime, messages, embeddings)
164
- step_result.step_invocation(
165
- step_match(runtime),
166
- step,
167
- indent,
168
- background,
169
- runtime.configuration,
170
- messages,
171
- embeddings
172
- )
173
- end
174
-
175
- private
176
-
177
- def step_match(runtime)
178
- runtime.step_match(step.name)
179
- rescue Cucumber::Undefined
180
- NoStepMatch.new(step, step.name)
181
- end
182
- end
183
-
184
- end
185
-
186
- Embedding = Struct.new(:src, :mime_type, :label) do
187
-
188
- def send_to_formatter(formatter)
189
- formatter.embed(src, mime_type, label)
190
- end
191
- end
192
-
193
- FeaturePrinter = Struct.new(:formatter, :runtime, :node) do
194
-
195
- def before
196
- formatter.before_feature(node)
197
- Legacy::Ast::Comments.new(node.comments).accept(formatter)
198
- Legacy::Ast::Tags.new(node.tags).accept(formatter)
199
- formatter.feature_name node.keyword, indented(node.legacy_conflated_name_and_description)
200
- @delayed_messages = []
201
- @delayed_embeddings = []
202
- self
203
- end
204
-
205
- attr_reader :current_test_step_source
206
-
207
- def before_test_case(test_case)
208
- @before_hook_results = Legacy::Ast::NodeCollection.new
209
- end
210
-
211
- def before_test_step(test_step)
212
- end
213
-
214
- def after_test_step(test_step, result)
215
- @current_test_step_source = TestStepSource.for(test_step, result)
216
- # TODO: stop calling self, and describe source to another object
217
- test_step.describe_source_to(self, result)
218
- print_step
219
- end
220
-
221
- def after_test_case(*args)
222
- if current_test_step_source.step_result.nil?
223
- switch_step_container
224
- end
225
-
226
- # messages and embedding should already have been handled, but just in case...
227
- @delayed_messages.each { |message| formatter.puts(message) }
228
- @delayed_embeddings.each { |embedding| embedding.send_to_formatter(formatter) }
229
- @delayed_messages = []
230
- @delayed_embeddings = []
231
-
232
- @child.after_test_case
233
- @previous_test_case_background = @current_test_case_background
234
- @previous_test_case_scenario_outline = current_test_step_source.scenario_outline
235
- end
236
-
237
- def before_hook(location, result)
238
- @before_hook_results << Legacy::Ast::HookResult.new(LegacyResultBuilder.new(result), @delayed_messages, @delayed_embeddings)
239
- @delayed_messages = []
240
- @delayed_embeddings = []
241
- end
242
-
243
- def after_hook(location, result)
244
- # if the scenario has no steps, we can hit this before we've created the scenario printer
245
- # ideally we should call switch_step_container in before_step_step
246
- switch_step_container if !@child
247
- @child.after_hook Legacy::Ast::HookResult.new(LegacyResultBuilder.new(result), @delayed_messages, @delayed_embeddings)
248
- @delayed_messages = []
249
- @delayed_embeddings = []
250
- end
251
-
252
- def after_step_hook(hook, result)
253
- line = StepBacktraceLine.new(current_test_step_source.step)
254
- @child.after_step_hook Legacy::Ast::HookResult.new(LegacyResultBuilder.new(result).
255
- append_to_exception_backtrace(line), @delayed_messages, @delayed_embeddings)
256
- @delayed_messages = []
257
- @delayed_embeddings = []
258
-
259
- end
260
-
261
- def background(node, *)
262
- @current_test_case_background = node
263
- end
264
-
265
- def puts(messages)
266
- @delayed_messages.push *messages
267
- end
268
-
269
- def embed(src, mime_type, label)
270
- @delayed_embeddings.push Embedding.new(src, mime_type, label)
271
- end
272
-
273
- def step(*);end
274
- def scenario(*);end
275
- def scenario_outline(*);end
276
- def examples_table(*);end
277
- def examples_table_row(*);end
278
- def feature(*);end
279
-
280
- def after
281
- @child.after
282
- formatter.after_feature(node)
283
- self
284
- end
285
-
286
- private
287
-
288
- attr_reader :before_hook_results
289
- private :before_hook_results
290
-
291
- def switch_step_container
292
- switch_to_child select_step_container(current_test_step_source), current_test_step_source
293
- end
294
-
295
- def select_step_container(source)
296
- if source.background
297
- if same_background_as_previous_test_case?(source)
298
- HiddenBackgroundPrinter.new(formatter, source.background)
299
- else
300
- BackgroundPrinter.new(formatter, source.background, before_hook_results)
301
- end
302
- elsif source.scenario
303
- ScenarioPrinter.new(formatter, source.scenario, before_hook_results)
304
- elsif source.scenario_outline
305
- if same_scenario_outline_as_previous_test_case?(source) and @previous_outline_child
306
- @previous_outline_child
307
- else
308
- ScenarioOutlinePrinter.new(formatter, runtime.configuration, source.scenario_outline)
309
- end
310
- else
311
- raise 'unknown step container'
312
- end
313
- end
314
-
315
- def same_background_as_previous_test_case?(source)
316
- source.background == @previous_test_case_background
317
- end
318
-
319
- def same_scenario_outline_as_previous_test_case?(source)
320
- source.scenario_outline == @previous_test_case_scenario_outline
321
- end
322
-
323
- def print_step
324
- return unless current_test_step_source.step_result
325
- switch_step_container
326
-
327
- if current_test_step_source.scenario_outline
328
- @child.examples_table(current_test_step_source.examples_table)
329
- @child.examples_table_row(current_test_step_source.examples_table_row, before_hook_results)
330
- end
331
-
332
- if @failed_hidden_background_step
333
- indent = Indent.new(@child.node)
334
- step_invocation = @failed_hidden_background_step.build_step_invocation(indent, runtime, messages = [], embeddings = [])
335
- @child.step_invocation(step_invocation, @failed_hidden_background_step)
336
- @failed_hidden_background_step = nil
337
- end
338
-
339
- unless @last_step == current_test_step_source.step
340
- indent ||= Indent.new(@child.node)
341
- step_invocation = current_test_step_source.build_step_invocation(indent, runtime, @delayed_messages, @delayed_embeddings)
342
- runtime.step_visited step_invocation
343
- @child.step_invocation(step_invocation, current_test_step_source)
344
- @last_step = current_test_step_source.step
345
- end
346
- @delayed_messages = []
347
- @delayed_embeddings = []
348
- end
349
-
350
- def switch_to_child(child, source)
351
- return if @child == child
352
- if @child
353
- if from_first_background(@child)
354
- @first_background_failed = @child.failed?
355
- elsif from_hidden_background(@child)
356
- if not @first_background_failed
357
- @failed_hidden_background_step = @child.get_failed_step_source
358
- end
359
- if @previous_outline_child
360
- @previous_outline_child.after unless same_scenario_outline_as_previous_test_case?(source)
361
- end
362
- end
363
- unless from_scenario_outline_to_hidden_backgroud(@child, child)
364
- @child.after
365
- @previous_outline_child = nil
366
- else
367
- @previous_outline_child = @child
368
- end
369
- end
370
- child.before unless to_scenario_outline(child) and same_scenario_outline_as_previous_test_case?(source)
371
- @child = child
372
- end
373
-
374
- def from_scenario_outline_to_hidden_backgroud(from, to)
375
- from.class.name == "Cucumber::Reports::ScenarioOutlinePrinter" and
376
- to.class.name == "Cucumber::Reports::HiddenBackgroundPrinter"
377
- end
378
-
379
- def from_first_background(from)
380
- from.class.name == "Cucumber::Reports::BackgroundPrinter"
381
- end
382
-
383
- def from_hidden_background(from)
384
- from.class.name == "Cucumber::Reports::HiddenBackgroundPrinter"
385
- end
386
-
387
- def to_scenario_outline(to)
388
- to.class.name == "Cucumber::Reports::ScenarioOutlinePrinter"
389
- end
390
-
391
- def indented(nasty_old_conflation_of_name_and_description)
392
- indent = ""
393
- nasty_old_conflation_of_name_and_description.split("\n").map do |l|
394
- s = "#{indent}#{l}"
395
- indent = " "
396
- s
397
- end.join("\n")
398
- end
399
-
400
- end
401
-
402
- module PrintsAfterHooks
403
- def after_hook_results
404
- @after_hook_results ||= Legacy::Ast::NodeCollection.new
405
- end
406
-
407
- def after_hook(result)
408
- after_hook_results << result
409
- end
410
- end
411
-
412
- # Basic printer used by default
413
- class AfterHookPrinter
414
- include Cucumber.initializer(:formatter)
415
-
416
- include PrintsAfterHooks
417
-
418
- def after
419
- after_hook_results.accept(formatter)
420
- end
421
- end
422
-
423
- BackgroundPrinter = Struct.new(:formatter, :node, :before_hook_results) do
424
-
425
- def before
426
- formatter.before_background node
427
- formatter.background_name node.keyword, node.legacy_conflated_name_and_description, node.location.to_s, indent.of(node)
428
- before_hook_results.accept(formatter)
429
- self
430
- end
431
-
432
- def after_step_hook(result)
433
- result.accept formatter
434
- end
435
-
436
- def step_invocation(step_invocation, source)
437
- @child ||= StepsPrinter.new(formatter).before
438
- @child.step_invocation step_invocation
439
- if source.step_result.status == :failed
440
- @failed = true
441
- end
442
- end
443
-
444
- def after
445
- @child.after if @child
446
- formatter.after_background(node)
447
- self
448
- end
449
-
450
- def failed?
451
- @failed
452
- end
453
-
454
- private
455
-
456
- def indent
457
- @indent ||= Indent.new(node)
458
- end
459
- end
460
-
461
- # Printer to handle background steps for anything but the first scenario in a
462
- # feature. These steps should not be printed.
463
- class HiddenBackgroundPrinter < Struct.new(:formatter, :node)
464
- def get_failed_step_source
465
- return @source_of_failed_step
466
- end
467
-
468
- def step_invocation(step_invocation, source)
469
- if source.step_result.status == :failed
470
- @source_of_failed_step = source
471
- end
472
- end
473
-
474
- def before;self;end
475
- def after;self;end
476
- def before_hook(*);end
477
- def after_hook(*);end
478
- def after_step_hook(*);end
479
- def examples_table(*);end
480
- def after_test_case(*);end
481
- end
482
-
483
- ScenarioPrinter = Struct.new(:formatter, :node, :before_hook_results) do
484
- include PrintsAfterHooks
485
-
486
- def before
487
- formatter.before_feature_element(node)
488
- Legacy::Ast::Tags.new(node.tags).accept(formatter)
489
- formatter.scenario_name node.keyword, node.legacy_conflated_name_and_description, node.location.to_s, indent.of(node)
490
- before_hook_results.accept(formatter)
491
- self
492
- end
493
-
494
- def step_invocation(step_invocation, source)
495
- @child ||= StepsPrinter.new(formatter).before
496
- @child.step_invocation step_invocation
497
- @last_step_result = source.step_result
498
- end
499
-
500
- def after_step_hook(result)
501
- result.accept formatter
502
- end
503
-
504
- def after_test_case(*args)
505
- after
506
- end
507
-
508
- def after
509
- return if @done
510
- @child.after if @child
511
- # TODO - the last step result might not accurately reflect the
512
- # overall scenario result.
513
- scenario = last_step_result.scenario(node.name, node.location)
514
- after_hook_results.accept(formatter)
515
- formatter.after_feature_element(scenario)
516
- @done = true
517
- self
518
- end
519
-
520
- private
521
-
522
- def last_step_result
523
- @last_step_result || LegacyResultBuilder.new(Core::Test::Result::Unknown.new)
524
- end
525
-
526
- def indent
527
- @indent ||= Indent.new(node)
528
- end
529
- end
530
-
531
- StepsPrinter = Struct.new(:formatter) do
532
- def before
533
- formatter.before_steps(nil)
534
- self
535
- end
536
-
537
- def step_invocation(step_invocation)
538
- steps << step_invocation
539
- step_invocation.accept(formatter)
540
- self
541
- end
542
-
543
- def after
544
- formatter.after_steps(steps)
545
- self
546
- end
547
-
548
- private
549
-
550
- def steps
551
- @steps ||= Legacy::Ast::StepInvocations.new
552
- end
553
-
554
- end
555
-
556
- ScenarioOutlinePrinter = Struct.new(:formatter, :configuration, :node) do
557
- extend Forwardable
558
- def_delegators :@child, :after_hook, :after_step_hook
559
-
560
- def before
561
- formatter.before_feature_element(node)
562
- Legacy::Ast::Tags.new(node.tags).accept(formatter)
563
- formatter.scenario_name node.keyword, node.legacy_conflated_name_and_description, node.location.to_s, indent.of(node)
564
- OutlineStepsPrinter.new(formatter, configuration, indent).print(node)
565
- self
566
- end
567
-
568
- def step_invocation(step_invocation, source)
569
- node, result = source.step, source.step_result
570
- @last_step_result = result
571
- @child.step_invocation(step_invocation, source)
572
- end
573
-
574
- def examples_table(examples_table)
575
- @child ||= ExamplesArrayPrinter.new(formatter, configuration).before
576
- @child.examples_table(examples_table)
577
- end
578
-
579
- def examples_table_row(node, before_hook_results)
580
- @child.examples_table_row(node, before_hook_results)
581
- end
582
-
583
- def after_test_case
584
- @child.after_test_case
585
- end
586
-
587
- def after
588
- @child.after if @child
589
- # TODO - the last step result might not accurately reflect the
590
- # overall scenario result.
591
- scenario_outline = last_step_result.scenario_outline(node.name, node.location)
592
- formatter.after_feature_element(scenario_outline)
593
- self
594
- end
595
-
596
- private
597
-
598
- def last_step_result
599
- @last_step_result || Core::Test::Result::Unknown.new
600
- end
601
-
602
- def indent
603
- @indent ||= Indent.new(node)
604
- end
605
- end
606
-
607
- OutlineStepsPrinter = Struct.new(:formatter, :configuration, :indent, :outline) do
608
- def print(node)
609
- node.describe_to self
610
- steps_printer.after
611
- end
612
-
613
- def scenario_outline(node, &descend)
614
- descend.call(self)
615
- end
616
-
617
- def outline_step(step)
618
- step_match = NoStepMatch.new(step, step.name)
619
- step_invocation = LegacyResultBuilder.new(Core::Test::Result::Skipped.new).
620
- step_invocation(step_match, step, indent, background = nil, configuration, messages = [], embeddings = [])
621
- steps_printer.step_invocation step_invocation
622
- end
623
-
624
- def examples_table(*);end
625
-
626
- private
627
-
628
- def steps_printer
629
- @steps_printer ||= StepsPrinter.new(formatter).before
630
- end
631
- end
632
-
633
- ExamplesArrayPrinter = Struct.new(:formatter, :configuration) do
634
- extend Forwardable
635
- def_delegators :@child, :step_invocation, :after_hook, :after_step_hook, :after_test_case, :examples_table_row
636
-
637
- def before
638
- formatter.before_examples_array(:examples_array)
639
- self
640
- end
641
-
642
- def examples_table(examples_table)
643
- return if examples_table == @current
644
- @child.after if @child
645
- @child = ExamplesTablePrinter.new(formatter, configuration, examples_table).before
646
- @current = examples_table
647
- end
648
-
649
- def after
650
- @child.after if @child
651
- formatter.after_examples_array
652
- self
653
- end
654
- end
655
-
656
- ExamplesTablePrinter = Struct.new(:formatter, :configuration, :node) do
657
- extend Forwardable
658
- def_delegators :@child, :step_invocation, :after_hook, :after_step_hook, :after_test_case
659
-
660
- def before
661
- formatter.before_examples(node)
662
- formatter.examples_name(node.keyword, node.legacy_conflated_name_and_description)
663
- formatter.before_outline_table(legacy_table)
664
- if !configuration.expand?
665
- HeaderTableRowPrinter.new(formatter, ExampleTableRow.new(node.header), Legacy::Ast::Node.new).before.after
666
- end
667
- self
668
- end
669
-
670
- def examples_table_row(examples_table_row, before_hook_results)
671
- return if examples_table_row == @current
672
- @child.after if @child
673
- row = ExampleTableRow.new(examples_table_row)
674
- if !configuration.expand?
675
- @child = TableRowPrinter.new(formatter, row, before_hook_results).before
676
- else
677
- @child = ExpandTableRowPrinter.new(formatter, row, before_hook_results).before
678
- end
679
- @current = examples_table_row
680
- end
681
-
682
- def after_test_case(*args)
683
- @child.after_test_case
684
- end
685
-
686
- def after
687
- @child.after if @child
688
- formatter.after_outline_table(node)
689
- formatter.after_examples(node)
690
- self
691
- end
692
-
693
- private
694
-
695
- def legacy_table
696
- LegacyTable.new(node)
697
- end
698
-
699
- class ExampleTableRow < SimpleDelegator
700
- def dom_id
701
- file_colon_line.gsub(/[\/\.:]/, '_')
702
- end
703
- end
704
-
705
- LegacyTable = Struct.new(:node) do
706
- def col_width(index)
707
- max_width = FindMaxWidth.new(index)
708
- node.describe_to max_width
709
- max_width.result
710
- end
711
-
712
- require 'gherkin/formatter/escaping'
713
- FindMaxWidth = Struct.new(:index) do
714
- include ::Gherkin::Formatter::Escaping
715
-
716
- def examples_table(table, &descend)
717
- @result = char_length_of(table.header.values[index])
718
- descend.call(self)
719
- end
720
-
721
- def examples_table_row(row, &descend)
722
- width = char_length_of(row.values[index])
723
- @result = width if width > result
724
- end
725
-
726
- def result
727
- @result ||= 0
728
- end
729
-
730
- private
731
- def char_length_of(cell)
732
- escape_cell(cell).unpack('U*').length
733
- end
734
- end
735
- end
736
- end
737
-
738
- class TableRowPrinterBase < Struct.new(:formatter, :node, :before_hook_results)
739
- include PrintsAfterHooks
740
-
741
- def after_step_hook(result)
742
- @after_step_hook_result ||= []
743
- @after_step_hook_result.push result
744
- end
745
-
746
- def after_test_case(*args)
747
- after
748
- end
749
-
750
- private
751
-
752
- def indent
753
- :not_needed
754
- end
755
-
756
- def legacy_table_row
757
- LegacyExampleTableRow.new(exception, @status, node.values, node.location)
758
- end
759
-
760
- def exception
761
- return nil unless @failed_step
762
- @failed_step.exception
763
- end
764
- end
765
-
766
- class HeaderTableRowPrinter < TableRowPrinterBase
767
- def before
768
- formatter.before_table_row(node)
769
- self
770
- end
771
-
772
- def after
773
- node.values.each do |value|
774
- formatter.before_table_cell(value)
775
- formatter.table_cell_value(value, :skipped_param)
776
- formatter.after_table_cell(value)
777
- end
778
- formatter.after_table_row(legacy_table_row)
779
- self
780
- end
781
- end
782
-
783
-
784
- class TableRowPrinter < TableRowPrinterBase
785
- def before
786
- before_hook_results.accept(formatter)
787
- formatter.before_table_row(node)
788
- self
789
- end
790
-
791
- def step_invocation(step_invocation, source)
792
- result = source.step_result
793
- step_invocation.messages.each { |message| formatter.puts(message) }
794
- step_invocation.embeddings.each { |embedding| embedding.send_to_formatter(formatter) }
795
- @failed_step = step_invocation if result.status == :failed
796
- @status = step_invocation.status unless already_failed?
797
- end
798
-
799
- def after
800
- return if @done
801
- @child.after if @child
802
- node.values.each do |value|
803
- formatter.before_table_cell(value)
804
- formatter.table_cell_value(value, @status || :skipped)
805
- formatter.after_table_cell(value)
806
- end
807
- formatter.after_table_row(legacy_table_row)
808
- @after_step_hook_result.each { |result| result.accept formatter } if @after_step_hook_result
809
- after_hook_results.accept(formatter)
810
- @done = true
811
- self
812
- end
813
-
814
- private
815
-
816
- def already_failed?
817
- @status == :failed || @status == :undefined || @status == :pending
818
- end
819
- end
820
-
821
- class ExpandTableRowPrinter < TableRowPrinterBase
822
- def before
823
- before_hook_results.accept(formatter)
824
- self
825
- end
826
-
827
- def step_invocation(step_invocation, source)
828
- result = source.step_result
829
- @table_row ||= legacy_table_row
830
- step_invocation.indent.record_width_of(@table_row)
831
- if !@scenario_name_printed
832
- print_scenario_name(step_invocation, @table_row)
833
- @scenario_name_printed = true
834
- end
835
- step_invocation.accept(formatter)
836
- @failed_step = step_invocation if result.status == :failed
837
- @status = step_invocation.status unless @status == :failed
838
- end
839
-
840
- def after
841
- return if @done
842
- @child.after if @child
843
- @after_step_hook_result.each { |result| result.accept formatter } if @after_step_hook_result
844
- after_hook_results.accept(formatter)
845
- @done = true
846
- self
847
- end
848
-
849
- private
850
-
851
- def print_scenario_name(step_invocation, table_row)
852
- formatter.scenario_name table_row.keyword, table_row.name, node.location.to_s, step_invocation.indent.of(table_row)
853
- end
854
- end
855
-
856
- LegacyExampleTableRow = Struct.new(:exception, :status, :cells, :location) do
857
- def name
858
- '| ' + cells.join(' | ') + ' |'
859
- end
860
-
861
- def failed?
862
- status == :failed
863
- end
864
-
865
- def line
866
- location.line
867
- end
868
-
869
- def keyword
870
- # This method is only called when used for the scenario name line with
871
- # the expand option, and on that line the keyword is "Scenario"
872
- "Scenario"
873
- end
874
- end
875
-
876
- class Indent
877
- def initialize(node)
878
- @widths = []
879
- node.describe_to(self)
880
- end
881
-
882
- [:background, :scenario, :scenario_outline].each do |node_name|
883
- define_method(node_name) do |node, &descend|
884
- record_width_of node
885
- descend.call(self)
886
- end
887
- end
888
-
889
- [:step, :outline_step].each do |node_name|
890
- define_method(node_name) do |node|
891
- record_width_of node
892
- end
893
- end
894
-
895
- def examples_table(*); end
896
- def examples_table_row(*); end
897
-
898
- def of(node)
899
- # The length of the instantiated steps in --expand mode are currently
900
- # not included in the calculation of max => make sure to return >= 1
901
- [1, max - node.name.length - node.keyword.length].max
902
- end
903
-
904
- def record_width_of(node)
905
- @widths << node.keyword.length + node.name.length + 1
906
- end
907
-
908
- private
909
-
910
- def max
911
- @widths.max
912
- end
913
- end
914
-
915
- class LegacyResultBuilder
916
- attr_reader :status
917
- def initialize(result)
918
- @result = result
919
- @result.describe_to(self)
920
- end
921
-
922
- def passed
923
- @status = :passed
924
- end
925
-
926
- def failed
927
- @status = :failed
928
- end
929
-
930
- def undefined
931
- @status = :undefined
932
- end
933
-
934
- def skipped
935
- @status = :skipped
936
- end
937
-
938
- def pending(exception, *)
939
- @exception = exception
940
- @status = :pending
941
- end
942
-
943
- def exception(exception, *)
944
- @exception = exception
945
- end
946
-
947
- def append_to_exception_backtrace(line)
948
- @exception.set_backtrace(@exception.backtrace + [line.to_s]) if @exception
949
- return self
950
- end
951
-
952
- def duration(*); end
953
-
954
- def step_invocation(step_match, step, indent, background, configuration, messages, embeddings)
955
- Legacy::Ast::StepInvocation.new(step_match, @status, step_exception(step, configuration), indent, background, step, messages, embeddings)
956
- end
957
-
958
- def scenario(name, location)
959
- Legacy::Ast::Scenario.new(@status, name, location)
960
- end
961
-
962
- def scenario_outline(name, location)
963
- Legacy::Ast::ScenarioOutline.new(@status, name, location)
964
- end
965
-
966
- def describe_exception_to(formatter)
967
- formatter.exception(filtered_exception, @status) if @exception
968
- end
969
-
970
- private
971
-
972
- def step_exception(step, configuration)
973
- return filtered_step_exception(step) if @exception
974
- return nil unless @status == :undefined && configuration.strict?
975
- @exception = Cucumber::Undefined.from(@result, step.name)
976
- filtered_step_exception(step)
977
- end
978
-
979
- def filtered_exception
980
- BacktraceFilter.new(@exception.dup).exception
981
- end
982
-
983
- def filtered_step_exception(step)
984
- exception = filtered_exception
985
- exception.backtrace << StepBacktraceLine.new(step).to_s
986
- return exception
987
- end
988
- end
989
-
990
- end
991
-
992
- class StepBacktraceLine < Struct.new(:step)
993
- def to_s
994
- step.backtrace_line
995
- end
996
- end
997
-
998
- class BacktraceFilter
999
- BACKTRACE_FILTER_PATTERNS = \
1000
- [/vendor\/rails|lib\/cucumber|bin\/cucumber:|lib\/rspec|gems\/|minitest|test\/unit|.gem\/ruby|lib\/ruby/]
1001
- if(::Cucumber::JRUBY)
1002
- BACKTRACE_FILTER_PATTERNS << /org\/jruby/
1003
- end
1004
- PWD_PATTERN = /#{::Regexp.escape(::Dir.pwd)}\//m
1005
-
1006
- def initialize(exception)
1007
- @exception = exception
1008
- end
1009
-
1010
- def exception
1011
- return @exception if ::Cucumber.use_full_backtrace
1012
- @exception.backtrace.each{|line| line.gsub!(PWD_PATTERN, "./")}
1013
-
1014
- filtered = (@exception.backtrace || []).reject do |line|
1015
- BACKTRACE_FILTER_PATTERNS.detect { |p| line =~ p }
1016
- end
1017
-
1018
- if ::ENV['CUCUMBER_TRUNCATE_OUTPUT']
1019
- # Strip off file locations
1020
- filtered = filtered.map do |line|
1021
- line =~ /(.*):in `/ ? $1 : line
1022
- end
1023
- end
1024
-
1025
- @exception.set_backtrace(filtered)
1026
- @exception
1027
- end
1028
- end
1029
-
1030
-
1031
- # Adapters to pass to the legacy API formatters that provide the interface
1032
- # of the old AST classes
1033
- module Legacy
1034
- module Ast
1035
-
1036
- # Acts as a null object, or a base class
1037
- class Node
1038
- def initialize(node = nil)
1039
- @node = node
1040
- end
1041
-
1042
- def accept(formatter)
1043
- end
1044
-
1045
- attr_reader :node
1046
- private :node
1047
- end
1048
-
1049
- class NodeCollection
1050
- def initialize
1051
- @children = []
1052
- end
1053
-
1054
- def accept(formatter)
1055
- @children.each { |child| child.accept(formatter) }
1056
- end
1057
-
1058
- def <<(child)
1059
- @children << child
1060
- end
1061
- end
1062
-
1063
- Comments = Struct.new(:comments) do
1064
- def accept(formatter)
1065
- return if comments.empty?
1066
- formatter.before_comment comments
1067
- comments.each do |comment|
1068
- formatter.comment_line comment.to_s.strip
1069
- end
1070
- end
1071
- end
1072
-
1073
- class HookResult
1074
- def initialize(result, messages, embeddings)
1075
- @result, @messages, @embeddings = result, messages, embeddings
1076
- @already_accepted = false
1077
- end
1078
-
1079
- def accept(formatter)
1080
- unless @already_accepted
1081
- @messages.each { |message| formatter.puts(message) }
1082
- @embeddings.each { |embedding| embedding.send_to_formatter(formatter) }
1083
- @result.describe_exception_to(formatter)
1084
- @already_accepted = true
1085
- end
1086
- self
1087
- end
1088
- end
1089
-
1090
- StepInvocation = Struct.new(:step_match,
1091
- :status,
1092
- :exception,
1093
- :indent,
1094
- :background,
1095
- :step,
1096
- :messages,
1097
- :embeddings) do
1098
- extend Forwardable
1099
-
1100
- def_delegators :step, :keyword, :name, :multiline_arg, :location, :gherkin_statement
1101
-
1102
- def accept(formatter)
1103
- formatter.before_step(self)
1104
- messages.each { |message| formatter.puts(message) }
1105
- embeddings.each { |embedding| embedding.send_to_formatter(formatter) }
1106
- formatter.before_step_result *step_result_attributes
1107
- print_step_name(formatter)
1108
- Legacy::Ast::MultilineArg.for(multiline_arg).accept(formatter)
1109
- print_exception(formatter)
1110
- formatter.after_step_result *step_result_attributes
1111
- formatter.after_step(self)
1112
- end
1113
-
1114
- def step_result_attributes
1115
- [keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line]
1116
- end
1117
-
1118
- def failed?
1119
- status != :passed
1120
- end
1121
-
1122
- def passed?
1123
- status == :passed
1124
- end
1125
-
1126
- def dom_id
1127
-
1128
- end
1129
-
1130
- def actual_keyword
1131
- # TODO: This should return the keyword for the snippet
1132
- # `actual_keyword` translates 'And', 'But', etc. to 'Given', 'When',
1133
- # 'Then' as appropriate
1134
- "Given"
1135
- end
1136
-
1137
- def file_colon_line
1138
- location.to_s
1139
- end
1140
-
1141
- def backtrace_line
1142
- step_match.backtrace_line
1143
- end
1144
-
1145
- def step_invocation
1146
- self
1147
- end
1148
-
1149
- private
1150
-
1151
- def source_indent
1152
- indent.of(self)
1153
- end
1154
-
1155
- def print_step_name(formatter)
1156
- formatter.step_name(
1157
- keyword,
1158
- step_match,
1159
- status,
1160
- source_indent,
1161
- background,
1162
- location.to_s)
1163
- end
1164
-
1165
- def print_exception(formatter)
1166
- return unless exception
1167
- raise exception if ENV['FAIL_FAST']
1168
- formatter.exception(exception, status)
1169
- end
1170
- end
1171
-
1172
- class StepInvocations < Array
1173
- def failed?
1174
- any?(&:failed?)
1175
- end
1176
-
1177
- def passed?
1178
- all?(&:passed?)
1179
- end
1180
-
1181
- def status
1182
- return :passed if passed?
1183
- failed_step.status
1184
- end
1185
-
1186
- def exception
1187
- failed_step.exception if failed_step
1188
- end
1189
-
1190
- private
1191
-
1192
- def failed_step
1193
- detect(&:failed?)
1194
- end
1195
- end
1196
-
1197
- class DataTableRow
1198
- def initialize(row, line)
1199
- @values = row
1200
- @line = line
1201
- end
1202
-
1203
- def dom_id
1204
- "row_#{line}"
1205
- end
1206
-
1207
- def accept(formatter)
1208
- formatter.before_table_row(self)
1209
- values.each do |value|
1210
- formatter.before_table_cell(value)
1211
- formatter.table_cell_value(value, status)
1212
- formatter.after_table_cell(value)
1213
- end
1214
- formatter.after_table_row(self)
1215
- end
1216
-
1217
- def status
1218
- :skipped
1219
- end
1220
-
1221
- def exception
1222
- nil
1223
- end
1224
-
1225
- attr_reader :values, :line
1226
- private :values, :line
1227
- end
1228
-
1229
- class LegacyTableRow < DataTableRow
1230
- def accept(formatter)
1231
- formatter.before_table_row(self)
1232
- values.each do |value|
1233
- formatter.before_table_cell(value.value)
1234
- formatter.table_cell_value(value.value, value.status)
1235
- formatter.after_table_cell(value.value)
1236
- end
1237
- formatter.after_table_row(self)
1238
- end
1239
- end
1240
-
1241
- Tags = Struct.new(:tags) do
1242
- def accept(formatter)
1243
- formatter.before_tags tags
1244
- tags.each do |tag|
1245
- formatter.tag_name tag.name
1246
- end
1247
- formatter.after_tags tags
1248
- end
1249
- end
1250
-
1251
- Scenario = Struct.new(:status, :name, :location) do
1252
- def backtrace_line(step_name = "#{name}", line = self.location.line)
1253
- "#{location.on_line(line)}:in `#{step_name}'"
1254
- end
1255
-
1256
- def failed?
1257
- :failed == status
1258
- end
1259
-
1260
- def line
1261
- location.line
1262
- end
1263
- end
1264
-
1265
- ScenarioOutline = Struct.new(:status, :name, :location) do
1266
- def backtrace_line(step_name = "#{name}", line = self.location.line)
1267
- "#{location.on_line(line)}:in `#{step_name}'"
1268
- end
1269
-
1270
- def failed?
1271
- :failed == status
1272
- end
1273
-
1274
- def line
1275
- location.line
1276
- end
1277
- end
1278
-
1279
- module MultilineArg
1280
- class << self
1281
- def for(node)
1282
- Builder.new(node).result
1283
- end
1284
- end
1285
-
1286
- class Builder
1287
- def initialize(node)
1288
- node.describe_to(self)
1289
- end
1290
-
1291
- def doc_string(node)
1292
- @result = DocString.new(node)
1293
- end
1294
-
1295
- def data_table(node)
1296
- @result = DataTable.new(node)
1297
- end
1298
-
1299
- def legacy_table(node)
1300
- @result = LegacyTable.new(node)
1301
- end
1302
-
1303
- def result
1304
- @result || Node.new(nil)
1305
- end
1306
- end
1307
-
1308
- class DocString < Node
1309
- def accept(formatter)
1310
- formatter.before_multiline_arg node
1311
- formatter.doc_string(node)
1312
- formatter.after_multiline_arg node
1313
- end
1314
- end
1315
-
1316
- class DataTable < Cucumber::MultilineArgument::DataTable
1317
- def node
1318
- @ast_table
1319
- end
1320
-
1321
- def accept(formatter)
1322
- formatter.before_multiline_arg self
1323
- node.raw.each_with_index do |row, index|
1324
- line = node.location.line + index
1325
- Legacy::Ast::DataTableRow.new(row, line).accept(formatter)
1326
- end
1327
- formatter.after_multiline_arg self
1328
- end
1329
- end
1330
- end
1331
-
1332
- class LegacyTable < SimpleDelegator
1333
- def accept(formatter)
1334
- formatter.before_multiline_arg self
1335
- cells_rows.each_with_index do |row, index|
1336
- line = location.line + index
1337
- Legacy::Ast::LegacyTableRow.new(row, line).accept(formatter)
1338
- end
1339
- formatter.after_multiline_arg self
1340
- end
1341
- end
1342
-
1343
- Features = Struct.new(:duration)
1344
-
1345
- end
1346
-
1347
- end
1348
- end
1349
- end