cucumber 3.2.0 → 4.0.0.rc.1
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 +5 -5
- data/CHANGELOG.md +61 -18
- data/CONTRIBUTING.md +1 -0
- data/bin/cucumber +1 -1
- data/lib/autotest/cucumber_mixin.rb +42 -39
- data/lib/cucumber/cli/configuration.rb +4 -4
- data/lib/cucumber/cli/main.rb +11 -12
- data/lib/cucumber/cli/options.rb +56 -69
- data/lib/cucumber/cli/profile_loader.rb +32 -20
- data/lib/cucumber/configuration.rb +20 -21
- data/lib/cucumber/constantize.rb +2 -5
- data/lib/cucumber/deprecate.rb +5 -5
- data/lib/cucumber/errors.rb +4 -6
- data/lib/cucumber/events.rb +1 -0
- data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
- data/lib/cucumber/events/step_activated.rb +2 -1
- data/lib/cucumber/file_specs.rb +6 -6
- data/lib/cucumber/filters/activate_steps.rb +5 -3
- data/lib/cucumber/filters/prepare_world.rb +5 -9
- data/lib/cucumber/filters/quit.rb +1 -3
- data/lib/cucumber/filters/tag_limits/verifier.rb +2 -4
- data/lib/cucumber/formatter/ansicolor.rb +40 -45
- data/lib/cucumber/formatter/ast_lookup.rb +160 -0
- data/lib/cucumber/formatter/backtrace_filter.rb +5 -7
- data/lib/cucumber/formatter/console.rb +28 -59
- data/lib/cucumber/formatter/console_counts.rb +4 -9
- data/lib/cucumber/formatter/console_issues.rb +6 -3
- data/lib/cucumber/formatter/duration_extractor.rb +1 -1
- data/lib/cucumber/formatter/fanout.rb +2 -0
- data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
- data/lib/cucumber/formatter/interceptor.rb +5 -7
- data/lib/cucumber/formatter/io.rb +8 -14
- data/lib/cucumber/formatter/json.rb +93 -117
- data/lib/cucumber/formatter/junit.rb +55 -57
- data/lib/cucumber/formatter/pretty.rb +346 -152
- data/lib/cucumber/formatter/progress.rb +28 -32
- data/lib/cucumber/formatter/rerun.rb +22 -4
- data/lib/cucumber/formatter/stepdefs.rb +1 -2
- data/lib/cucumber/formatter/steps.rb +2 -3
- data/lib/cucumber/formatter/summary.rb +16 -8
- data/lib/cucumber/formatter/unicode.rb +15 -17
- data/lib/cucumber/formatter/usage.rb +9 -8
- data/lib/cucumber/gherkin/data_table_parser.rb +8 -6
- data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
- data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
- data/lib/cucumber/gherkin/steps_parser.rb +7 -8
- data/lib/cucumber/glue/dsl.rb +1 -1
- data/lib/cucumber/glue/hook.rb +16 -9
- data/lib/cucumber/glue/invoke_in_world.rb +13 -18
- data/lib/cucumber/glue/proto_world.rb +14 -16
- data/lib/cucumber/glue/registry_and_more.rb +7 -9
- data/lib/cucumber/glue/snippet.rb +21 -20
- data/lib/cucumber/glue/step_definition.rb +14 -15
- data/lib/cucumber/glue/world_factory.rb +1 -1
- data/lib/cucumber/hooks.rb +11 -11
- data/lib/cucumber/multiline_argument.rb +4 -6
- data/lib/cucumber/multiline_argument/data_table.rb +88 -59
- data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +1 -1
- data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
- data/lib/cucumber/platform.rb +3 -3
- data/lib/cucumber/rake/task.rb +13 -16
- data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
- data/lib/cucumber/running_test_case.rb +2 -53
- data/lib/cucumber/runtime.rb +27 -57
- data/lib/cucumber/runtime/after_hooks.rb +3 -3
- data/lib/cucumber/runtime/before_hooks.rb +3 -3
- data/lib/cucumber/runtime/for_programming_languages.rb +3 -2
- data/lib/cucumber/runtime/step_hooks.rb +1 -1
- data/lib/cucumber/runtime/support_code.rb +10 -12
- data/lib/cucumber/runtime/user_interface.rb +4 -6
- data/lib/cucumber/step_definition_light.rb +4 -3
- data/lib/cucumber/step_match.rb +12 -11
- data/lib/cucumber/step_match_search.rb +2 -1
- data/lib/cucumber/term/ansicolor.rb +9 -9
- data/lib/cucumber/version +1 -1
- metadata +37 -28
- data/lib/cucumber/formatter/cucumber.css +0 -286
- data/lib/cucumber/formatter/cucumber.sass +0 -247
- data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
- data/lib/cucumber/formatter/html.rb +0 -611
- data/lib/cucumber/formatter/html_builder.rb +0 -121
- data/lib/cucumber/formatter/http_io.rb +0 -146
- data/lib/cucumber/formatter/inline-js.js +0 -30
- data/lib/cucumber/formatter/jquery-min.js +0 -154
- data/lib/cucumber/formatter/json_pretty.rb +0 -11
- data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
- data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
- data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
- data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
- data/lib/cucumber/step_argument.rb +0 -25
@@ -1,27 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'cucumber/formatter/http_io'
|
4
|
-
|
5
3
|
module Cucumber
|
6
4
|
module Formatter
|
7
5
|
module Io
|
8
6
|
module_function
|
9
7
|
|
10
|
-
def ensure_io(
|
11
|
-
return nil if
|
12
|
-
return
|
13
|
-
|
14
|
-
HTTPIO.open(path_or_url_or_io)
|
15
|
-
else
|
16
|
-
File.open(path_or_url_or_io, Cucumber.file_mode('w'))
|
17
|
-
end
|
8
|
+
def ensure_io(path_or_io)
|
9
|
+
return nil if path_or_io.nil?
|
10
|
+
return path_or_io if path_or_io.respond_to?(:write)
|
11
|
+
file = File.open(path_or_io, Cucumber.file_mode('w'))
|
18
12
|
at_exit do
|
19
|
-
unless
|
20
|
-
|
21
|
-
|
13
|
+
unless file.closed?
|
14
|
+
file.flush
|
15
|
+
file.close
|
22
16
|
end
|
23
17
|
end
|
24
|
-
|
18
|
+
file
|
25
19
|
end
|
26
20
|
|
27
21
|
def ensure_file(path, name)
|
@@ -4,7 +4,7 @@ require 'multi_json'
|
|
4
4
|
require 'base64'
|
5
5
|
require 'cucumber/formatter/backtrace_filter'
|
6
6
|
require 'cucumber/formatter/io'
|
7
|
-
require 'cucumber/formatter/
|
7
|
+
require 'cucumber/formatter/ast_lookup'
|
8
8
|
|
9
9
|
module Cucumber
|
10
10
|
module Formatter
|
@@ -14,6 +14,7 @@ module Cucumber
|
|
14
14
|
|
15
15
|
def initialize(config)
|
16
16
|
@io = ensure_io(config.out_stream)
|
17
|
+
@ast_lookup = AstLookup.new(config)
|
17
18
|
@feature_hashes = []
|
18
19
|
@step_or_hook_hash = {}
|
19
20
|
config.on_event :test_case_started, &method(:on_test_case_started)
|
@@ -25,16 +26,18 @@ module Cucumber
|
|
25
26
|
|
26
27
|
def on_test_case_started(event)
|
27
28
|
test_case = event.test_case
|
28
|
-
builder = Builder.new(test_case)
|
29
|
-
unless same_feature_as_previous_test_case?(test_case
|
29
|
+
builder = Builder.new(test_case, @ast_lookup)
|
30
|
+
unless same_feature_as_previous_test_case?(test_case)
|
30
31
|
@feature_hash = builder.feature_hash
|
31
32
|
@feature_hashes << @feature_hash
|
32
33
|
end
|
33
34
|
@test_case_hash = builder.test_case_hash
|
34
35
|
if builder.background?
|
36
|
+
@in_background = true
|
35
37
|
feature_elements << builder.background_hash
|
36
38
|
@element_hash = builder.background_hash
|
37
39
|
else
|
40
|
+
@in_background = false
|
38
41
|
feature_elements << @test_case_hash
|
39
42
|
@element_hash = @test_case_hash
|
40
43
|
end
|
@@ -44,17 +47,17 @@ module Cucumber
|
|
44
47
|
def on_test_step_started(event)
|
45
48
|
test_step = event.test_step
|
46
49
|
return if internal_hook?(test_step)
|
47
|
-
|
48
|
-
if hook_query.hook?
|
50
|
+
if test_step.hook?
|
49
51
|
@step_or_hook_hash = {}
|
50
|
-
hooks_of_type(
|
52
|
+
hooks_of_type(test_step) << @step_or_hook_hash
|
51
53
|
return
|
52
54
|
end
|
53
55
|
if first_step_after_background?(test_step)
|
56
|
+
@in_background = false
|
54
57
|
feature_elements << @test_case_hash
|
55
58
|
@element_hash = @test_case_hash
|
56
59
|
end
|
57
|
-
@step_or_hook_hash = create_step_hash(test_step
|
60
|
+
@step_or_hook_hash = create_step_hash(test_step)
|
58
61
|
steps << @step_or_hook_hash
|
59
62
|
@step_hash = @step_or_hook_hash
|
60
63
|
end
|
@@ -82,42 +85,34 @@ module Cucumber
|
|
82
85
|
end
|
83
86
|
|
84
87
|
def embed(src, mime_type, _label)
|
85
|
-
|
86
|
-
is_file = File.file?(src)
|
87
|
-
rescue ArgumentError
|
88
|
-
is_file = false
|
89
|
-
end
|
90
|
-
|
91
|
-
if is_file
|
88
|
+
if File.file?(src)
|
92
89
|
content = File.open(src, 'rb', &:read)
|
93
90
|
data = encode64(content)
|
91
|
+
elsif mime_type =~ /;base64$/
|
92
|
+
mime_type = mime_type[0..-8]
|
93
|
+
data = src
|
94
94
|
else
|
95
|
-
|
96
|
-
mime_type = mime_type[0..-8]
|
97
|
-
data = src
|
98
|
-
else
|
99
|
-
data = encode64(src)
|
100
|
-
end
|
95
|
+
data = encode64(src)
|
101
96
|
end
|
102
97
|
test_step_embeddings << { mime_type: mime_type, data: data }
|
103
98
|
end
|
104
99
|
|
105
100
|
private
|
106
101
|
|
107
|
-
def same_feature_as_previous_test_case?(
|
108
|
-
current_feature[:uri] ==
|
102
|
+
def same_feature_as_previous_test_case?(test_case)
|
103
|
+
current_feature[:uri] == test_case.location.file
|
109
104
|
end
|
110
105
|
|
111
106
|
def first_step_after_background?(test_step)
|
112
|
-
test_step.
|
107
|
+
@in_background && test_step.location.lines.max >= @test_case_hash[:line]
|
113
108
|
end
|
114
109
|
|
115
110
|
def internal_hook?(test_step)
|
116
|
-
test_step.
|
111
|
+
test_step.location.file.include?('lib/cucumber/')
|
117
112
|
end
|
118
113
|
|
119
114
|
def current_feature
|
120
|
-
@feature_hash ||= {}
|
115
|
+
@feature_hash ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
|
121
116
|
end
|
122
117
|
|
123
118
|
def feature_elements
|
@@ -128,16 +123,16 @@ module Cucumber
|
|
128
123
|
@element_hash[:steps] ||= []
|
129
124
|
end
|
130
125
|
|
131
|
-
def hooks_of_type(
|
132
|
-
case
|
133
|
-
when
|
134
|
-
|
135
|
-
when
|
136
|
-
|
137
|
-
when
|
138
|
-
|
126
|
+
def hooks_of_type(hook_step)
|
127
|
+
case hook_step.text
|
128
|
+
when 'Before hook'
|
129
|
+
before_hooks
|
130
|
+
when 'After hook'
|
131
|
+
after_hooks
|
132
|
+
when 'AfterStep hook'
|
133
|
+
after_step_hooks
|
139
134
|
else
|
140
|
-
|
135
|
+
raise 'Unknown hook type ' + hook_step.to_s
|
141
136
|
end
|
142
137
|
end
|
143
138
|
|
@@ -165,30 +160,30 @@ module Cucumber
|
|
165
160
|
@step_or_hook_hash[:embeddings] ||= []
|
166
161
|
end
|
167
162
|
|
168
|
-
def create_step_hash(
|
163
|
+
def create_step_hash(test_step)
|
164
|
+
step_source = @ast_lookup.step_source(test_step).step
|
169
165
|
step_hash = {
|
170
|
-
keyword: step_source
|
171
|
-
name:
|
172
|
-
line:
|
166
|
+
keyword: step_source[:keyword],
|
167
|
+
name: test_step.text,
|
168
|
+
line: test_step.location.lines.min
|
173
169
|
}
|
174
|
-
step_hash[:
|
175
|
-
step_hash[:
|
176
|
-
step_hash[:rows] = create_data_table_value(step_source.multiline_arg) if step_source.multiline_arg.data_table?
|
170
|
+
step_hash[:doc_string] = create_doc_string_hash(step_source[:doc_string]) unless step_source[:doc_string].nil?
|
171
|
+
step_hash[:rows] = create_data_table_value(step_source[:data_table]) unless step_source[:data_table].nil?
|
177
172
|
step_hash
|
178
173
|
end
|
179
174
|
|
180
175
|
def create_doc_string_hash(doc_string)
|
181
|
-
content_type = doc_string
|
176
|
+
content_type = doc_string[:content_type] || ''
|
182
177
|
{
|
183
|
-
value: doc_string
|
178
|
+
value: doc_string[:content],
|
184
179
|
content_type: content_type,
|
185
|
-
line: doc_string
|
180
|
+
line: doc_string[:location][:line]
|
186
181
|
}
|
187
182
|
end
|
188
183
|
|
189
184
|
def create_data_table_value(data_table)
|
190
|
-
data_table.
|
191
|
-
{ cells: row }
|
185
|
+
data_table[:rows].map do |row|
|
186
|
+
{ cells: row[:cells].map { |cell| cell[:value] } }
|
192
187
|
end
|
193
188
|
end
|
194
189
|
|
@@ -232,113 +227,94 @@ module Cucumber
|
|
232
227
|
class Builder
|
233
228
|
attr_reader :feature_hash, :background_hash, :test_case_hash
|
234
229
|
|
235
|
-
def initialize(test_case)
|
230
|
+
def initialize(test_case, ast_lookup)
|
236
231
|
@background_hash = nil
|
237
|
-
test_case.
|
238
|
-
|
232
|
+
uri = test_case.location.file
|
233
|
+
feature = ast_lookup.gherkin_document(uri)[:feature]
|
234
|
+
feature(feature, uri)
|
235
|
+
background(feature[:children].first[:background]) unless feature[:children].first[:background].nil?
|
236
|
+
scenario(ast_lookup.scenario_source(test_case), test_case)
|
239
237
|
end
|
240
238
|
|
241
239
|
def background?
|
242
240
|
@background_hash != nil
|
243
241
|
end
|
244
242
|
|
245
|
-
def feature(feature)
|
243
|
+
def feature(feature, uri)
|
246
244
|
@feature_hash = {
|
247
|
-
|
248
|
-
|
249
|
-
keyword: feature
|
250
|
-
name: feature
|
251
|
-
description: feature
|
252
|
-
line: feature
|
245
|
+
id: create_id(feature[:name]),
|
246
|
+
uri: uri,
|
247
|
+
keyword: feature[:keyword],
|
248
|
+
name: feature[:name],
|
249
|
+
description: value_or_empty_string(feature[:description]),
|
250
|
+
line: feature[:location][:line]
|
253
251
|
}
|
254
|
-
|
255
|
-
|
256
|
-
@test_case_hash[:tags] = if @test_case_hash[:tags]
|
257
|
-
@feature_hash[:tags] + @test_case_hash[:tags]
|
258
|
-
else
|
259
|
-
@feature_hash[:tags]
|
260
|
-
end
|
261
|
-
end
|
262
|
-
@feature_hash[:comments] = Formatter.create_comments_array(feature.comments) unless feature.comments.empty?
|
263
|
-
@test_case_hash[:id].insert(0, @feature_hash[:id] + ';')
|
252
|
+
return if feature[:tags].empty?
|
253
|
+
@feature_hash[:tags] = create_tags_array_from_hash_array(feature[:tags])
|
264
254
|
end
|
265
255
|
|
266
256
|
def background(background)
|
267
257
|
@background_hash = {
|
268
|
-
keyword: background
|
269
|
-
name: background
|
270
|
-
description: background
|
271
|
-
line: background
|
258
|
+
keyword: background[:keyword],
|
259
|
+
name: background[:name],
|
260
|
+
description: value_or_empty_string(background[:description]),
|
261
|
+
line: background[:location][:line],
|
272
262
|
type: 'background'
|
273
263
|
}
|
274
|
-
@background_hash[:comments] = Formatter.create_comments_array(background.comments) unless background.comments.empty?
|
275
264
|
end
|
276
265
|
|
277
|
-
def scenario(
|
266
|
+
def scenario(scenario_source, test_case)
|
267
|
+
scenario = scenario_source.type == :Scenario ? scenario_source.scenario : scenario_source.scenario_outline
|
278
268
|
@test_case_hash = {
|
279
|
-
id:
|
280
|
-
keyword: scenario
|
281
|
-
name: scenario
|
282
|
-
description: scenario
|
283
|
-
line:
|
269
|
+
id: "#{@feature_hash[:id]};#{create_id_from_scenario_source(scenario_source)}",
|
270
|
+
keyword: scenario[:keyword],
|
271
|
+
name: scenario[:name],
|
272
|
+
description: value_or_empty_string(scenario[:description]),
|
273
|
+
line: test_case.location.lines.max,
|
284
274
|
type: 'scenario'
|
285
275
|
}
|
286
|
-
@test_case_hash[:tags] =
|
287
|
-
@test_case_hash[:comments] = Formatter.create_comments_array(scenario.comments) unless scenario.comments.empty?
|
276
|
+
@test_case_hash[:tags] = create_tags_array_from_tags_array(test_case.tags) unless test_case.tags.empty?
|
288
277
|
end
|
289
278
|
|
290
|
-
|
291
|
-
@test_case_hash = {
|
292
|
-
id: create_id(scenario) + ';' + @example_id,
|
293
|
-
keyword: scenario.keyword,
|
294
|
-
name: scenario.to_s,
|
295
|
-
description: scenario.description,
|
296
|
-
line: @row.location.line,
|
297
|
-
type: 'scenario'
|
298
|
-
}
|
299
|
-
tags = []
|
300
|
-
tags += create_tags_array(scenario.tags) unless scenario.tags.empty?
|
301
|
-
tags += @examples_table_tags if @examples_table_tags
|
302
|
-
@test_case_hash[:tags] = tags unless tags.empty?
|
303
|
-
comments = []
|
304
|
-
comments += Formatter.create_comments_array(scenario.comments) unless scenario.comments.empty?
|
305
|
-
comments += @examples_table_comments if @examples_table_comments
|
306
|
-
comments += @row_comments if @row_comments
|
307
|
-
@test_case_hash[:comments] = comments unless comments.empty?
|
308
|
-
end
|
279
|
+
private
|
309
280
|
|
310
|
-
def
|
311
|
-
|
312
|
-
|
313
|
-
@example_id = create_id(examples_table) + ";#{@row.number + 1}"
|
281
|
+
def value_or_empty_string(value)
|
282
|
+
value.nil? ? '' : value
|
283
|
+
end
|
314
284
|
|
315
|
-
|
316
|
-
|
285
|
+
def create_id(name)
|
286
|
+
name.downcase.tr(' ', '-')
|
317
287
|
end
|
318
288
|
|
319
|
-
def
|
320
|
-
|
321
|
-
|
289
|
+
def create_id_from_scenario_source(scenario_source)
|
290
|
+
if scenario_source.type == :Scenario
|
291
|
+
create_id(scenario_source.scenario[:name])
|
292
|
+
else
|
293
|
+
scenario_outline_name = scenario_source.scenario_outline[:name]
|
294
|
+
examples_name = scenario_source.examples[:name]
|
295
|
+
row_number = calculate_row_number(scenario_source)
|
296
|
+
"#{create_id(scenario_outline_name)};#{create_id(examples_name)};#{row_number}"
|
297
|
+
end
|
322
298
|
end
|
323
299
|
|
324
|
-
|
300
|
+
def calculate_row_number(scenario_source)
|
301
|
+
scenario_source.examples[:table_body].each_with_index do |row, index|
|
302
|
+
return index + 2 if row == scenario_source.row
|
303
|
+
end
|
304
|
+
end
|
325
305
|
|
326
|
-
def
|
327
|
-
|
306
|
+
def create_tags_array_from_hash_array(tags)
|
307
|
+
tags_array = []
|
308
|
+
tags.each { |tag| tags_array << { name: tag[:name], line: tag[:location][:line] } }
|
309
|
+
tags_array
|
328
310
|
end
|
329
311
|
|
330
|
-
def
|
312
|
+
def create_tags_array_from_tags_array(tags)
|
331
313
|
tags_array = []
|
332
314
|
tags.each { |tag| tags_array << { name: tag.name, line: tag.location.line } }
|
333
315
|
tags_array
|
334
316
|
end
|
335
317
|
end
|
336
318
|
end
|
337
|
-
|
338
|
-
def self.create_comments_array(comments)
|
339
|
-
comments_array = []
|
340
|
-
comments.each { |comment| comments_array << { value: comment.to_s.strip, line: comment.location.line } }
|
341
|
-
comments_array
|
342
|
-
end
|
343
319
|
end
|
344
320
|
end
|
@@ -5,6 +5,7 @@ require 'cucumber/formatter/backtrace_filter'
|
|
5
5
|
require 'cucumber/formatter/io'
|
6
6
|
require 'cucumber/formatter/interceptor'
|
7
7
|
require 'fileutils'
|
8
|
+
require 'cucumber/formatter/ast_lookup'
|
8
9
|
|
9
10
|
module Cucumber
|
10
11
|
module Formatter
|
@@ -19,6 +20,7 @@ module Cucumber
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def initialize(config)
|
23
|
+
@ast_lookup = AstLookup.new(config)
|
22
24
|
config.on_event :test_case_started, &method(:on_test_case_started)
|
23
25
|
config.on_event :test_case_finished, &method(:on_test_case_finished)
|
24
26
|
config.on_event :test_step_finished, &method(:on_test_step_finished)
|
@@ -33,17 +35,15 @@ module Cucumber
|
|
33
35
|
tests: 0,
|
34
36
|
skipped: 0,
|
35
37
|
time: 0,
|
36
|
-
builder: Builder::XmlMarkup.new(:
|
38
|
+
builder: Builder::XmlMarkup.new(indent: 2)
|
37
39
|
}
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
43
|
def on_test_case_started(event)
|
42
44
|
test_case = event.test_case
|
43
|
-
unless same_feature_as_previous_test_case?(test_case
|
44
|
-
|
45
|
-
end
|
46
|
-
@failing_step_source = nil
|
45
|
+
start_feature(test_case) unless same_feature_as_previous_test_case?(test_case)
|
46
|
+
@failing_test_step = nil
|
47
47
|
# In order to fill out <system-err/> and <system-out/>, we need to
|
48
48
|
# intercept the $stderr and $stdout
|
49
49
|
@interceptedout = Interceptor::Pipe.wrap(:stdout)
|
@@ -52,15 +52,15 @@ module Cucumber
|
|
52
52
|
|
53
53
|
def on_test_step_finished(event)
|
54
54
|
test_step, result = *event.attributes
|
55
|
-
return if @
|
55
|
+
return if @failing_test_step
|
56
56
|
|
57
|
-
@
|
57
|
+
@failing_test_step = test_step unless result.ok?(@config.strict)
|
58
58
|
end
|
59
59
|
|
60
60
|
def on_test_case_finished(event)
|
61
61
|
test_case, result = *event.attributes
|
62
62
|
result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
|
63
|
-
test_case_name = NameBuilder.new(test_case)
|
63
|
+
test_case_name = NameBuilder.new(test_case, @ast_lookup)
|
64
64
|
scenario = test_case_name.scenario_name
|
65
65
|
scenario_designation = "#{scenario}#{test_case_name.name_suffix}"
|
66
66
|
output = create_output_string(test_case, scenario, result, test_case_name.row_name)
|
@@ -71,45 +71,54 @@ module Cucumber
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def on_test_run_finished(_event)
|
74
|
-
@features_data.each { |
|
74
|
+
@features_data.each { |_file, data| end_feature(data) }
|
75
75
|
end
|
76
76
|
|
77
77
|
private
|
78
78
|
|
79
|
-
def same_feature_as_previous_test_case?(
|
80
|
-
@current_feature_data && @current_feature_data[:
|
79
|
+
def same_feature_as_previous_test_case?(test_case)
|
80
|
+
@current_feature_data && @current_feature_data[:uri] == test_case.location.file
|
81
81
|
end
|
82
82
|
|
83
|
-
def start_feature(
|
84
|
-
|
85
|
-
|
83
|
+
def start_feature(test_case)
|
84
|
+
uri = test_case.location.file
|
85
|
+
feature = @ast_lookup.gherkin_document(uri)[:feature]
|
86
|
+
raise UnNamedFeatureError, uri if feature[:name].empty?
|
87
|
+
@current_feature_data = @features_data[uri]
|
88
|
+
@current_feature_data[:uri] = uri unless @current_feature_data[:uri]
|
86
89
|
@current_feature_data[:feature] = feature unless @current_feature_data[:feature]
|
87
90
|
end
|
88
91
|
|
89
92
|
def end_feature(feature_data)
|
90
|
-
@testsuite = Builder::XmlMarkup.new(:
|
93
|
+
@testsuite = Builder::XmlMarkup.new(indent: 2)
|
91
94
|
@testsuite.instruct!
|
92
95
|
@testsuite.testsuite(
|
93
|
-
:
|
94
|
-
:
|
95
|
-
:
|
96
|
-
:
|
97
|
-
:
|
98
|
-
:
|
96
|
+
failures: feature_data[:failures],
|
97
|
+
errors: feature_data[:errors],
|
98
|
+
skipped: feature_data[:skipped],
|
99
|
+
tests: feature_data[:tests],
|
100
|
+
time: format('%.6f', feature_data[:time]),
|
101
|
+
name: feature_data[:feature][:name]
|
99
102
|
) do
|
100
103
|
@testsuite << feature_data[:builder].target!
|
101
104
|
end
|
102
105
|
|
103
|
-
write_file(feature_result_filename(feature_data[:
|
106
|
+
write_file(feature_result_filename(feature_data[:uri]), @testsuite.target!)
|
104
107
|
end
|
105
108
|
|
106
|
-
def create_output_string(test_case, scenario, result, row_name)
|
107
|
-
|
109
|
+
def create_output_string(test_case, scenario, result, row_name) # rubocop:disable Metrics/PerceivedComplexity
|
110
|
+
scenario_source = @ast_lookup.scenario_source(test_case)
|
111
|
+
keyword = scenario_source.type == :Scenario ? scenario_source.scenario[:keyword] : scenario_source.scenario_outline[:keyword]
|
112
|
+
output = "#{keyword}: #{scenario}\n\n"
|
108
113
|
return output if result.ok?(@config.strict)
|
109
|
-
if
|
110
|
-
if @
|
111
|
-
|
112
|
-
|
114
|
+
if scenario_source.type == :Scenario
|
115
|
+
if @failing_test_step
|
116
|
+
if @failing_test_step.hook?
|
117
|
+
output += "#{@failing_test_step.text} at #{@failing_test_step.location}\n"
|
118
|
+
else
|
119
|
+
step_source = @ast_lookup.step_source(@failing_test_step).step
|
120
|
+
output += "#{step_source[:keyword]}#{@failing_test_step.text}\n"
|
121
|
+
end
|
113
122
|
else # An Around hook has failed
|
114
123
|
output += "Around hook\n"
|
115
124
|
end
|
@@ -119,24 +128,20 @@ module Cucumber
|
|
119
128
|
output + "\nMessage:\n"
|
120
129
|
end
|
121
130
|
|
122
|
-
def hook?(step)
|
123
|
-
['Before hook', 'After hook', 'AfterStep hook'].include? step.text
|
124
|
-
end
|
125
|
-
|
126
131
|
def build_testcase(result, scenario_designation, output)
|
127
132
|
duration = ResultBuilder.new(result).test_case_duration
|
128
133
|
@current_feature_data[:time] += duration
|
129
|
-
classname = @current_feature_data[:feature]
|
134
|
+
classname = @current_feature_data[:feature][:name]
|
130
135
|
name = scenario_designation
|
131
136
|
|
132
|
-
@current_feature_data[:builder].testcase(:
|
137
|
+
@current_feature_data[:builder].testcase(classname: classname, name: name, time: format('%.6f', duration)) do
|
133
138
|
if !result.passed? && result.ok?(@config.strict)
|
134
139
|
@current_feature_data[:builder].skipped
|
135
140
|
@current_feature_data[:skipped] += 1
|
136
141
|
elsif !result.passed?
|
137
142
|
status = result.to_sym
|
138
143
|
exception = get_backtrace_object(result)
|
139
|
-
@current_feature_data[:builder].failure(:
|
144
|
+
@current_feature_data[:builder].failure(message: "#{status} #{name}", type: status) do
|
140
145
|
@current_feature_data[:builder].cdata! output
|
141
146
|
@current_feature_data[:builder].cdata!(format_exception(exception)) if exception
|
142
147
|
end
|
@@ -154,11 +159,9 @@ module Cucumber
|
|
154
159
|
|
155
160
|
def get_backtrace_object(result)
|
156
161
|
if result.failed?
|
157
|
-
|
162
|
+
result.exception
|
158
163
|
elsif result.backtrace
|
159
|
-
|
160
|
-
else
|
161
|
-
return nil
|
164
|
+
result
|
162
165
|
end
|
163
166
|
end
|
164
167
|
|
@@ -171,7 +174,7 @@ module Cucumber
|
|
171
174
|
end
|
172
175
|
|
173
176
|
def basename(feature_file)
|
174
|
-
File.basename(feature_file.gsub(/[\\\/]/, '-'), '.feature')
|
177
|
+
File.basename(feature_file.gsub(/[\\\/]/, '-'), '.feature') # rubocop:disable Style/RegexpLiteral
|
175
178
|
end
|
176
179
|
|
177
180
|
def write_file(feature_filename, data)
|
@@ -187,34 +190,29 @@ module Cucumber
|
|
187
190
|
class NameBuilder
|
188
191
|
attr_reader :scenario_name, :name_suffix, :row_name
|
189
192
|
|
190
|
-
def initialize(test_case)
|
193
|
+
def initialize(test_case, ast_lookup)
|
191
194
|
@name_suffix = ''
|
192
195
|
@row_name = ''
|
193
|
-
test_case
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
196
|
+
scenario_source = ast_lookup.scenario_source(test_case)
|
197
|
+
if scenario_source.type == :Scenario
|
198
|
+
scenario(scenario_source.scenario)
|
199
|
+
else
|
200
|
+
scenario_outline(scenario_source.scenario_outline)
|
201
|
+
examples_table_row(scenario_source.row)
|
202
|
+
end
|
198
203
|
end
|
199
204
|
|
200
205
|
def scenario(scenario)
|
201
|
-
@scenario_name =
|
202
|
-
self
|
206
|
+
@scenario_name = scenario[:name].empty? ? 'Unnamed scenario' : scenario[:name]
|
203
207
|
end
|
204
208
|
|
205
209
|
def scenario_outline(outline)
|
206
|
-
@scenario_name =
|
207
|
-
self
|
208
|
-
end
|
209
|
-
|
210
|
-
def examples_table(*)
|
211
|
-
self
|
210
|
+
@scenario_name = outline[:name].empty? ? 'Unnamed scenario outline' : outline[:name]
|
212
211
|
end
|
213
212
|
|
214
213
|
def examples_table_row(row)
|
215
|
-
@row_name = '| ' + row.
|
214
|
+
@row_name = '| ' + row[:cells].map { |cell| cell[:value] }.join(' | ') + ' |'
|
216
215
|
@name_suffix = " (outline example : #{@row_name})"
|
217
|
-
self
|
218
216
|
end
|
219
217
|
end
|
220
218
|
|
@@ -238,7 +236,7 @@ module Cucumber
|
|
238
236
|
def exception(*) end
|
239
237
|
|
240
238
|
def duration(duration, *)
|
241
|
-
duration.tap { |
|
239
|
+
duration.tap { |dur| @test_case_duration = dur.nanoseconds / 10**9.0 }
|
242
240
|
end
|
243
241
|
end
|
244
242
|
end
|