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

Sign up to get free protection for your applications and to get access to all the features.
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