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