cucumber 3.1.2 → 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 +4 -4
- data/CHANGELOG.md +68 -13
- 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 +53 -62
- 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 +3 -3
- data/lib/cucumber/formatter/json.rb +92 -110
- 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 +36 -29
- data/lib/cucumber/events/gherkin_source_parsed.rb~ +0 -14
- data/lib/cucumber/formatter/ast_lookup.rb~ +0 -9
- 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/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
@@ -19,14 +19,14 @@ module Cucumber
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def ensure_file(path, name)
|
22
|
-
raise "You *must* specify --out FILE for the #{name} formatter" unless String
|
22
|
+
raise "You *must* specify --out FILE for the #{name} formatter" unless String == path.class
|
23
23
|
raise "I can't write #{name} to a directory - it has to be a file" if File.directory?(path)
|
24
|
-
raise "I can't write #{name} to a file in the non-existing directory #{File.dirname(path)}"
|
24
|
+
raise "I can't write #{name} to a file in the non-existing directory #{File.dirname(path)}" unless File.directory?(File.dirname(path))
|
25
25
|
ensure_io(path)
|
26
26
|
end
|
27
27
|
|
28
28
|
def ensure_dir(path, name)
|
29
|
-
raise "You *must* specify --out DIR for the #{name} formatter" unless String
|
29
|
+
raise "You *must* specify --out DIR for the #{name} formatter" unless String == path.class
|
30
30
|
raise "I can't write #{name} reports to a file - it has to be a directory" if File.file?(path)
|
31
31
|
FileUtils.mkdir_p(path) unless File.directory?(path)
|
32
32
|
File.absolute_path path
|
@@ -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
|
@@ -85,33 +88,31 @@ module Cucumber
|
|
85
88
|
if File.file?(src)
|
86
89
|
content = File.open(src, 'rb', &:read)
|
87
90
|
data = encode64(content)
|
91
|
+
elsif mime_type =~ /;base64$/
|
92
|
+
mime_type = mime_type[0..-8]
|
93
|
+
data = src
|
88
94
|
else
|
89
|
-
|
90
|
-
mime_type = mime_type[0..-8]
|
91
|
-
data = src
|
92
|
-
else
|
93
|
-
data = encode64(src)
|
94
|
-
end
|
95
|
+
data = encode64(src)
|
95
96
|
end
|
96
97
|
test_step_embeddings << { mime_type: mime_type, data: data }
|
97
98
|
end
|
98
99
|
|
99
100
|
private
|
100
101
|
|
101
|
-
def same_feature_as_previous_test_case?(
|
102
|
-
current_feature[:uri] ==
|
102
|
+
def same_feature_as_previous_test_case?(test_case)
|
103
|
+
current_feature[:uri] == test_case.location.file
|
103
104
|
end
|
104
105
|
|
105
106
|
def first_step_after_background?(test_step)
|
106
|
-
test_step.
|
107
|
+
@in_background && test_step.location.lines.max >= @test_case_hash[:line]
|
107
108
|
end
|
108
109
|
|
109
110
|
def internal_hook?(test_step)
|
110
|
-
test_step.
|
111
|
+
test_step.location.file.include?('lib/cucumber/')
|
111
112
|
end
|
112
113
|
|
113
114
|
def current_feature
|
114
|
-
@feature_hash ||= {}
|
115
|
+
@feature_hash ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
|
115
116
|
end
|
116
117
|
|
117
118
|
def feature_elements
|
@@ -122,16 +123,16 @@ module Cucumber
|
|
122
123
|
@element_hash[:steps] ||= []
|
123
124
|
end
|
124
125
|
|
125
|
-
def hooks_of_type(
|
126
|
-
case
|
127
|
-
when
|
128
|
-
|
129
|
-
when
|
130
|
-
|
131
|
-
when
|
132
|
-
|
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
|
133
134
|
else
|
134
|
-
|
135
|
+
raise 'Unknown hook type ' + hook_step.to_s
|
135
136
|
end
|
136
137
|
end
|
137
138
|
|
@@ -159,30 +160,30 @@ module Cucumber
|
|
159
160
|
@step_or_hook_hash[:embeddings] ||= []
|
160
161
|
end
|
161
162
|
|
162
|
-
def create_step_hash(
|
163
|
+
def create_step_hash(test_step)
|
164
|
+
step_source = @ast_lookup.step_source(test_step).step
|
163
165
|
step_hash = {
|
164
|
-
keyword: step_source
|
165
|
-
name:
|
166
|
-
line:
|
166
|
+
keyword: step_source[:keyword],
|
167
|
+
name: test_step.text,
|
168
|
+
line: test_step.location.lines.min
|
167
169
|
}
|
168
|
-
step_hash[:
|
169
|
-
step_hash[:
|
170
|
-
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?
|
171
172
|
step_hash
|
172
173
|
end
|
173
174
|
|
174
175
|
def create_doc_string_hash(doc_string)
|
175
|
-
content_type = doc_string
|
176
|
+
content_type = doc_string[:content_type] || ''
|
176
177
|
{
|
177
|
-
value: doc_string
|
178
|
+
value: doc_string[:content],
|
178
179
|
content_type: content_type,
|
179
|
-
line: doc_string
|
180
|
+
line: doc_string[:location][:line]
|
180
181
|
}
|
181
182
|
end
|
182
183
|
|
183
184
|
def create_data_table_value(data_table)
|
184
|
-
data_table.
|
185
|
-
{ cells: row }
|
185
|
+
data_table[:rows].map do |row|
|
186
|
+
{ cells: row[:cells].map { |cell| cell[:value] } }
|
186
187
|
end
|
187
188
|
end
|
188
189
|
|
@@ -226,113 +227,94 @@ module Cucumber
|
|
226
227
|
class Builder
|
227
228
|
attr_reader :feature_hash, :background_hash, :test_case_hash
|
228
229
|
|
229
|
-
def initialize(test_case)
|
230
|
+
def initialize(test_case, ast_lookup)
|
230
231
|
@background_hash = nil
|
231
|
-
test_case.
|
232
|
-
|
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)
|
233
237
|
end
|
234
238
|
|
235
239
|
def background?
|
236
240
|
@background_hash != nil
|
237
241
|
end
|
238
242
|
|
239
|
-
def feature(feature)
|
243
|
+
def feature(feature, uri)
|
240
244
|
@feature_hash = {
|
241
|
-
|
242
|
-
|
243
|
-
keyword: feature
|
244
|
-
name: feature
|
245
|
-
description: feature
|
246
|
-
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]
|
247
251
|
}
|
248
|
-
|
249
|
-
|
250
|
-
@test_case_hash[:tags] = if @test_case_hash[:tags]
|
251
|
-
@feature_hash[:tags] + @test_case_hash[:tags]
|
252
|
-
else
|
253
|
-
@feature_hash[:tags]
|
254
|
-
end
|
255
|
-
end
|
256
|
-
@feature_hash[:comments] = Formatter.create_comments_array(feature.comments) unless feature.comments.empty?
|
257
|
-
@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])
|
258
254
|
end
|
259
255
|
|
260
256
|
def background(background)
|
261
257
|
@background_hash = {
|
262
|
-
keyword: background
|
263
|
-
name: background
|
264
|
-
description: background
|
265
|
-
line: background
|
258
|
+
keyword: background[:keyword],
|
259
|
+
name: background[:name],
|
260
|
+
description: value_or_empty_string(background[:description]),
|
261
|
+
line: background[:location][:line],
|
266
262
|
type: 'background'
|
267
263
|
}
|
268
|
-
@background_hash[:comments] = Formatter.create_comments_array(background.comments) unless background.comments.empty?
|
269
264
|
end
|
270
265
|
|
271
|
-
def scenario(
|
266
|
+
def scenario(scenario_source, test_case)
|
267
|
+
scenario = scenario_source.type == :Scenario ? scenario_source.scenario : scenario_source.scenario_outline
|
272
268
|
@test_case_hash = {
|
273
|
-
id:
|
274
|
-
keyword: scenario
|
275
|
-
name: scenario
|
276
|
-
description: scenario
|
277
|
-
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,
|
278
274
|
type: 'scenario'
|
279
275
|
}
|
280
|
-
@test_case_hash[:tags] =
|
281
|
-
@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?
|
282
277
|
end
|
283
278
|
|
284
|
-
|
285
|
-
@test_case_hash = {
|
286
|
-
id: create_id(scenario) + ';' + @example_id,
|
287
|
-
keyword: scenario.keyword,
|
288
|
-
name: scenario.to_s,
|
289
|
-
description: scenario.description,
|
290
|
-
line: @row.location.line,
|
291
|
-
type: 'scenario'
|
292
|
-
}
|
293
|
-
tags = []
|
294
|
-
tags += create_tags_array(scenario.tags) unless scenario.tags.empty?
|
295
|
-
tags += @examples_table_tags if @examples_table_tags
|
296
|
-
@test_case_hash[:tags] = tags unless tags.empty?
|
297
|
-
comments = []
|
298
|
-
comments += Formatter.create_comments_array(scenario.comments) unless scenario.comments.empty?
|
299
|
-
comments += @examples_table_comments if @examples_table_comments
|
300
|
-
comments += @row_comments if @row_comments
|
301
|
-
@test_case_hash[:comments] = comments unless comments.empty?
|
302
|
-
end
|
279
|
+
private
|
303
280
|
|
304
|
-
def
|
305
|
-
|
306
|
-
|
307
|
-
@example_id = create_id(examples_table) + ";#{@row.number + 1}"
|
281
|
+
def value_or_empty_string(value)
|
282
|
+
value.nil? ? '' : value
|
283
|
+
end
|
308
284
|
|
309
|
-
|
310
|
-
|
285
|
+
def create_id(name)
|
286
|
+
name.downcase.tr(' ', '-')
|
311
287
|
end
|
312
288
|
|
313
|
-
def
|
314
|
-
|
315
|
-
|
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
|
316
298
|
end
|
317
299
|
|
318
|
-
|
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
|
319
305
|
|
320
|
-
def
|
321
|
-
|
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
|
322
310
|
end
|
323
311
|
|
324
|
-
def
|
312
|
+
def create_tags_array_from_tags_array(tags)
|
325
313
|
tags_array = []
|
326
314
|
tags.each { |tag| tags_array << { name: tag.name, line: tag.location.line } }
|
327
315
|
tags_array
|
328
316
|
end
|
329
317
|
end
|
330
318
|
end
|
331
|
-
|
332
|
-
def self.create_comments_array(comments)
|
333
|
-
comments_array = []
|
334
|
-
comments.each { |comment| comments_array << { value: comment.to_s.strip, line: comment.location.line } }
|
335
|
-
comments_array
|
336
|
-
end
|
337
319
|
end
|
338
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
|