cucumber 3.2.0 → 5.2.0
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 +280 -19
- data/CONTRIBUTING.md +11 -25
- data/README.md +4 -5
- data/bin/cucumber +1 -1
- data/lib/autotest/cucumber_mixin.rb +46 -53
- data/lib/cucumber/cli/configuration.rb +5 -5
- data/lib/cucumber/cli/main.rb +12 -12
- data/lib/cucumber/cli/options.rb +90 -73
- data/lib/cucumber/cli/profile_loader.rb +49 -26
- data/lib/cucumber/configuration.rb +44 -29
- data/lib/cucumber/constantize.rb +2 -5
- data/lib/cucumber/deprecate.rb +31 -7
- data/lib/cucumber/errors.rb +5 -7
- data/lib/cucumber/events/envelope.rb +9 -0
- data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
- data/lib/cucumber/events/hook_test_step_created.rb +13 -0
- data/lib/cucumber/events/step_activated.rb +2 -1
- data/lib/cucumber/events/test_case_created.rb +13 -0
- data/lib/cucumber/events/test_case_ready.rb +12 -0
- data/lib/cucumber/events/test_step_created.rb +13 -0
- data/lib/cucumber/events/undefined_parameter_type.rb +10 -0
- data/lib/cucumber/events.rb +13 -6
- data/lib/cucumber/file_specs.rb +6 -6
- data/lib/cucumber/filters/activate_steps.rb +5 -3
- data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
- 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/filters.rb +1 -0
- data/lib/cucumber/formatter/ansicolor.rb +40 -45
- data/lib/cucumber/formatter/ast_lookup.rb +163 -0
- data/lib/cucumber/formatter/backtrace_filter.rb +9 -8
- data/lib/cucumber/formatter/console.rb +58 -66
- data/lib/cucumber/formatter/console_counts.rb +4 -9
- data/lib/cucumber/formatter/console_issues.rb +6 -3
- data/lib/cucumber/formatter/duration.rb +1 -1
- data/lib/cucumber/formatter/duration_extractor.rb +3 -1
- data/lib/cucumber/formatter/errors.rb +6 -0
- data/lib/cucumber/formatter/fanout.rb +2 -0
- data/lib/cucumber/formatter/html.rb +11 -598
- data/lib/cucumber/formatter/http_io.rb +43 -42
- data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
- data/lib/cucumber/formatter/interceptor.rb +11 -30
- data/lib/cucumber/formatter/io.rb +46 -10
- data/lib/cucumber/formatter/json.rb +102 -116
- data/lib/cucumber/formatter/junit.rb +55 -55
- data/lib/cucumber/formatter/message.rb +22 -0
- data/lib/cucumber/formatter/message_builder.rb +255 -0
- data/lib/cucumber/formatter/pretty.rb +359 -153
- data/lib/cucumber/formatter/progress.rb +30 -32
- data/lib/cucumber/formatter/publish_banner_printer.rb +77 -0
- data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -0
- data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
- data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
- data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
- data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +40 -0
- data/lib/cucumber/formatter/rerun.rb +22 -4
- data/lib/cucumber/formatter/stepdefs.rb +1 -2
- data/lib/cucumber/formatter/steps.rb +3 -4
- data/lib/cucumber/formatter/summary.rb +16 -8
- data/lib/cucumber/formatter/unicode.rb +15 -17
- data/lib/cucumber/formatter/url_reporter.rb +17 -0
- data/lib/cucumber/formatter/usage.rb +11 -10
- data/lib/cucumber/gherkin/data_table_parser.rb +17 -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 +17 -8
- data/lib/cucumber/glue/hook.rb +34 -11
- data/lib/cucumber/glue/invoke_in_world.rb +13 -18
- data/lib/cucumber/glue/proto_world.rb +42 -33
- data/lib/cucumber/glue/registry_and_more.rb +42 -12
- data/lib/cucumber/glue/snippet.rb +23 -22
- data/lib/cucumber/glue/step_definition.rb +42 -19
- data/lib/cucumber/glue/world_factory.rb +1 -1
- data/lib/cucumber/hooks.rb +11 -11
- data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -2
- data/lib/cucumber/multiline_argument/data_table.rb +97 -64
- data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
- data/lib/cucumber/multiline_argument.rb +4 -6
- data/lib/cucumber/platform.rb +3 -3
- data/lib/cucumber/rake/task.rb +16 -18
- data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
- data/lib/cucumber/rspec/doubles.rb +3 -5
- data/lib/cucumber/running_test_case.rb +2 -53
- data/lib/cucumber/runtime/after_hooks.rb +8 -4
- data/lib/cucumber/runtime/before_hooks.rb +8 -4
- data/lib/cucumber/runtime/for_programming_languages.rb +4 -2
- data/lib/cucumber/runtime/step_hooks.rb +6 -2
- data/lib/cucumber/runtime/support_code.rb +13 -15
- data/lib/cucumber/runtime/user_interface.rb +6 -16
- data/lib/cucumber/runtime.rb +41 -58
- data/lib/cucumber/step_definition_light.rb +4 -3
- data/lib/cucumber/step_definitions.rb +2 -2
- 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/term/banner.rb +56 -0
- data/lib/cucumber/version +1 -1
- data/lib/cucumber.rb +1 -1
- metadata +253 -80
- 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_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
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require '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
|
+
require 'cucumber/deprecate'
|
8
9
|
|
9
10
|
module Cucumber
|
10
11
|
module Formatter
|
@@ -13,7 +14,16 @@ module Cucumber
|
|
13
14
|
include Io
|
14
15
|
|
15
16
|
def initialize(config)
|
16
|
-
|
17
|
+
Cucumber::Deprecate::CliOption.deprecate(
|
18
|
+
config.error_stream,
|
19
|
+
'--format=json',
|
20
|
+
"Please use --format=message and stand-alone json-formatter.\n" \
|
21
|
+
'json-formatter homepage: https://github.com/cucumber/cucumber/tree/master/json-formatter#cucumber-json-formatter',
|
22
|
+
'6.0.0'
|
23
|
+
)
|
24
|
+
|
25
|
+
@io = ensure_io(config.out_stream, config.error_stream)
|
26
|
+
@ast_lookup = AstLookup.new(config)
|
17
27
|
@feature_hashes = []
|
18
28
|
@step_or_hook_hash = {}
|
19
29
|
config.on_event :test_case_started, &method(:on_test_case_started)
|
@@ -25,16 +35,18 @@ module Cucumber
|
|
25
35
|
|
26
36
|
def on_test_case_started(event)
|
27
37
|
test_case = event.test_case
|
28
|
-
builder = Builder.new(test_case)
|
29
|
-
unless same_feature_as_previous_test_case?(test_case
|
38
|
+
builder = Builder.new(test_case, @ast_lookup)
|
39
|
+
unless same_feature_as_previous_test_case?(test_case)
|
30
40
|
@feature_hash = builder.feature_hash
|
31
41
|
@feature_hashes << @feature_hash
|
32
42
|
end
|
33
43
|
@test_case_hash = builder.test_case_hash
|
34
44
|
if builder.background?
|
45
|
+
@in_background = true
|
35
46
|
feature_elements << builder.background_hash
|
36
47
|
@element_hash = builder.background_hash
|
37
48
|
else
|
49
|
+
@in_background = false
|
38
50
|
feature_elements << @test_case_hash
|
39
51
|
@element_hash = @test_case_hash
|
40
52
|
end
|
@@ -44,17 +56,17 @@ module Cucumber
|
|
44
56
|
def on_test_step_started(event)
|
45
57
|
test_step = event.test_step
|
46
58
|
return if internal_hook?(test_step)
|
47
|
-
|
48
|
-
if hook_query.hook?
|
59
|
+
if test_step.hook?
|
49
60
|
@step_or_hook_hash = {}
|
50
|
-
hooks_of_type(
|
61
|
+
hooks_of_type(test_step) << @step_or_hook_hash
|
51
62
|
return
|
52
63
|
end
|
53
64
|
if first_step_after_background?(test_step)
|
65
|
+
@in_background = false
|
54
66
|
feature_elements << @test_case_hash
|
55
67
|
@element_hash = @test_case_hash
|
56
68
|
end
|
57
|
-
@step_or_hook_hash = create_step_hash(test_step
|
69
|
+
@step_or_hook_hash = create_step_hash(test_step)
|
58
70
|
steps << @step_or_hook_hash
|
59
71
|
@step_hash = @step_or_hook_hash
|
60
72
|
end
|
@@ -74,50 +86,42 @@ module Cucumber
|
|
74
86
|
end
|
75
87
|
|
76
88
|
def on_test_run_finished(_event)
|
77
|
-
@io.write(
|
89
|
+
@io.write(JSON.generate(@feature_hashes, pretty: true))
|
78
90
|
end
|
79
91
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
def embed(src, mime_type, _label)
|
85
|
-
begin
|
86
|
-
is_file = File.file?(src)
|
87
|
-
rescue ArgumentError
|
88
|
-
is_file = false
|
92
|
+
def attach(src, mime_type)
|
93
|
+
if mime_type == 'text/x.cucumber.log+plain'
|
94
|
+
test_step_output << src
|
95
|
+
return
|
89
96
|
end
|
90
|
-
|
91
|
-
if is_file
|
97
|
+
if File.file?(src)
|
92
98
|
content = File.open(src, 'rb', &:read)
|
93
99
|
data = encode64(content)
|
100
|
+
elsif mime_type =~ /;base64$/
|
101
|
+
mime_type = mime_type[0..-8]
|
102
|
+
data = src
|
94
103
|
else
|
95
|
-
|
96
|
-
mime_type = mime_type[0..-8]
|
97
|
-
data = src
|
98
|
-
else
|
99
|
-
data = encode64(src)
|
100
|
-
end
|
104
|
+
data = encode64(src)
|
101
105
|
end
|
102
106
|
test_step_embeddings << { mime_type: mime_type, data: data }
|
103
107
|
end
|
104
108
|
|
105
109
|
private
|
106
110
|
|
107
|
-
def same_feature_as_previous_test_case?(
|
108
|
-
current_feature[:uri] ==
|
111
|
+
def same_feature_as_previous_test_case?(test_case)
|
112
|
+
current_feature[:uri] == test_case.location.file
|
109
113
|
end
|
110
114
|
|
111
115
|
def first_step_after_background?(test_step)
|
112
|
-
test_step.
|
116
|
+
@in_background && test_step.location.lines.max >= @test_case_hash[:line]
|
113
117
|
end
|
114
118
|
|
115
119
|
def internal_hook?(test_step)
|
116
|
-
test_step.
|
120
|
+
test_step.location.file.include?('lib/cucumber/')
|
117
121
|
end
|
118
122
|
|
119
123
|
def current_feature
|
120
|
-
@feature_hash ||= {}
|
124
|
+
@feature_hash ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
|
121
125
|
end
|
122
126
|
|
123
127
|
def feature_elements
|
@@ -128,16 +132,16 @@ module Cucumber
|
|
128
132
|
@element_hash[:steps] ||= []
|
129
133
|
end
|
130
134
|
|
131
|
-
def hooks_of_type(
|
132
|
-
case
|
133
|
-
when
|
134
|
-
|
135
|
-
when
|
136
|
-
|
137
|
-
when
|
138
|
-
|
135
|
+
def hooks_of_type(hook_step)
|
136
|
+
case hook_step.text
|
137
|
+
when 'Before hook'
|
138
|
+
before_hooks
|
139
|
+
when 'After hook'
|
140
|
+
after_hooks
|
141
|
+
when 'AfterStep hook'
|
142
|
+
after_step_hooks
|
139
143
|
else
|
140
|
-
|
144
|
+
raise 'Unknown hook type ' + hook_step.to_s
|
141
145
|
end
|
142
146
|
end
|
143
147
|
|
@@ -165,20 +169,20 @@ module Cucumber
|
|
165
169
|
@step_or_hook_hash[:embeddings] ||= []
|
166
170
|
end
|
167
171
|
|
168
|
-
def create_step_hash(
|
172
|
+
def create_step_hash(test_step)
|
173
|
+
step_source = @ast_lookup.step_source(test_step).step
|
169
174
|
step_hash = {
|
170
175
|
keyword: step_source.keyword,
|
171
|
-
name:
|
172
|
-
line:
|
176
|
+
name: test_step.text,
|
177
|
+
line: test_step.location.lines.min
|
173
178
|
}
|
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?
|
179
|
+
step_hash[:doc_string] = create_doc_string_hash(step_source.doc_string) unless step_source.doc_string.nil?
|
180
|
+
step_hash[:rows] = create_data_table_value(step_source.data_table) unless step_source.data_table.nil?
|
177
181
|
step_hash
|
178
182
|
end
|
179
183
|
|
180
184
|
def create_doc_string_hash(doc_string)
|
181
|
-
content_type = doc_string.
|
185
|
+
content_type = doc_string.media_type || ''
|
182
186
|
{
|
183
187
|
value: doc_string.content,
|
184
188
|
content_type: content_type,
|
@@ -187,14 +191,15 @@ module Cucumber
|
|
187
191
|
end
|
188
192
|
|
189
193
|
def create_data_table_value(data_table)
|
190
|
-
data_table.
|
191
|
-
{ cells: row }
|
194
|
+
data_table.rows.map do |row|
|
195
|
+
{ cells: row.cells.map(&:value) }
|
192
196
|
end
|
193
197
|
end
|
194
198
|
|
195
199
|
def add_match_and_result(test_step, result)
|
196
200
|
@step_or_hook_hash[:match] = create_match_hash(test_step, result)
|
197
201
|
@step_or_hook_hash[:result] = create_result_hash(result)
|
202
|
+
result.embeddings.each { |e| embed(e['src'], e['mime_type'], e['label']) } if result.respond_to?(:embeddings)
|
198
203
|
end
|
199
204
|
|
200
205
|
def add_failed_around_hook(result)
|
@@ -232,113 +237,94 @@ module Cucumber
|
|
232
237
|
class Builder
|
233
238
|
attr_reader :feature_hash, :background_hash, :test_case_hash
|
234
239
|
|
235
|
-
def initialize(test_case)
|
240
|
+
def initialize(test_case, ast_lookup)
|
236
241
|
@background_hash = nil
|
237
|
-
test_case.
|
238
|
-
|
242
|
+
uri = test_case.location.file
|
243
|
+
feature = ast_lookup.gherkin_document(uri).feature
|
244
|
+
feature(feature, uri)
|
245
|
+
background(feature.children.first.background) unless feature.children.first.background.nil?
|
246
|
+
scenario(ast_lookup.scenario_source(test_case), test_case)
|
239
247
|
end
|
240
248
|
|
241
249
|
def background?
|
242
250
|
@background_hash != nil
|
243
251
|
end
|
244
252
|
|
245
|
-
def feature(feature)
|
253
|
+
def feature(feature, uri)
|
246
254
|
@feature_hash = {
|
247
|
-
|
248
|
-
|
255
|
+
id: create_id(feature.name),
|
256
|
+
uri: uri,
|
249
257
|
keyword: feature.keyword,
|
250
|
-
name: feature.
|
251
|
-
description: feature.description,
|
258
|
+
name: feature.name,
|
259
|
+
description: value_or_empty_string(feature.description),
|
252
260
|
line: feature.location.line
|
253
261
|
}
|
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] + ';')
|
262
|
+
return if feature.tags.empty?
|
263
|
+
@feature_hash[:tags] = create_tags_array_from_hash_array(feature.tags)
|
264
264
|
end
|
265
265
|
|
266
266
|
def background(background)
|
267
267
|
@background_hash = {
|
268
268
|
keyword: background.keyword,
|
269
|
-
name: background.
|
270
|
-
description: background.description,
|
269
|
+
name: background.name,
|
270
|
+
description: value_or_empty_string(background.description),
|
271
271
|
line: background.location.line,
|
272
272
|
type: 'background'
|
273
273
|
}
|
274
|
-
@background_hash[:comments] = Formatter.create_comments_array(background.comments) unless background.comments.empty?
|
275
274
|
end
|
276
275
|
|
277
|
-
def scenario(
|
276
|
+
def scenario(scenario_source, test_case)
|
277
|
+
scenario = scenario_source.type == :Scenario ? scenario_source.scenario : scenario_source.scenario_outline
|
278
278
|
@test_case_hash = {
|
279
|
-
id:
|
279
|
+
id: "#{@feature_hash[:id]};#{create_id_from_scenario_source(scenario_source)}",
|
280
280
|
keyword: scenario.keyword,
|
281
|
-
name:
|
282
|
-
description: scenario.description,
|
283
|
-
line:
|
281
|
+
name: test_case.name,
|
282
|
+
description: value_or_empty_string(scenario.description),
|
283
|
+
line: test_case.location.lines.max,
|
284
284
|
type: 'scenario'
|
285
285
|
}
|
286
|
-
@test_case_hash[:tags] =
|
287
|
-
@test_case_hash[:comments] = Formatter.create_comments_array(scenario.comments) unless scenario.comments.empty?
|
286
|
+
@test_case_hash[:tags] = create_tags_array_from_tags_array(test_case.tags) unless test_case.tags.empty?
|
288
287
|
end
|
289
288
|
|
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
|
289
|
+
private
|
309
290
|
|
310
|
-
def
|
311
|
-
|
312
|
-
|
313
|
-
@example_id = create_id(examples_table) + ";#{@row.number + 1}"
|
291
|
+
def value_or_empty_string(value)
|
292
|
+
value.nil? ? '' : value
|
293
|
+
end
|
314
294
|
|
315
|
-
|
316
|
-
|
295
|
+
def create_id(name)
|
296
|
+
name.downcase.tr(' ', '-')
|
317
297
|
end
|
318
298
|
|
319
|
-
def
|
320
|
-
|
321
|
-
|
299
|
+
def create_id_from_scenario_source(scenario_source)
|
300
|
+
if scenario_source.type == :Scenario
|
301
|
+
create_id(scenario_source.scenario.name)
|
302
|
+
else
|
303
|
+
scenario_outline_name = scenario_source.scenario_outline.name
|
304
|
+
examples_name = scenario_source.examples.name
|
305
|
+
row_number = calculate_row_number(scenario_source)
|
306
|
+
"#{create_id(scenario_outline_name)};#{create_id(examples_name)};#{row_number}"
|
307
|
+
end
|
322
308
|
end
|
323
309
|
|
324
|
-
|
310
|
+
def calculate_row_number(scenario_source)
|
311
|
+
scenario_source.examples.table_body.each_with_index do |row, index|
|
312
|
+
return index + 2 if row == scenario_source.row
|
313
|
+
end
|
314
|
+
end
|
325
315
|
|
326
|
-
def
|
327
|
-
|
316
|
+
def create_tags_array_from_hash_array(tags)
|
317
|
+
tags_array = []
|
318
|
+
tags.each { |tag| tags_array << { name: tag.name, line: tag.location.line } }
|
319
|
+
tags_array
|
328
320
|
end
|
329
321
|
|
330
|
-
def
|
322
|
+
def create_tags_array_from_tags_array(tags)
|
331
323
|
tags_array = []
|
332
324
|
tags.each { |tag| tags_array << { name: tag.name, line: tag.location.line } }
|
333
325
|
tags_array
|
334
326
|
end
|
335
327
|
end
|
336
328
|
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
329
|
end
|
344
330
|
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('%<time>.6f', time: 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
109
|
def create_output_string(test_case, scenario, result, row_name)
|
107
|
-
|
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
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('%<duration>.6f', duration: 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(&:value).join(' | ') + ' |'
|
216
215
|
@name_suffix = " (outline example : #{@row_name})"
|
217
|
-
self
|
218
216
|
end
|
219
217
|
end
|
220
218
|
|
@@ -238,8 +236,10 @@ 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
|
241
|
+
|
242
|
+
def attach(*) end
|
243
243
|
end
|
244
244
|
end
|
245
245
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cucumber/formatter/io'
|
4
|
+
require 'cucumber/formatter/message_builder'
|
5
|
+
|
6
|
+
module Cucumber
|
7
|
+
module Formatter
|
8
|
+
# The formatter used for <tt>--format message</tt>
|
9
|
+
class Message < MessageBuilder
|
10
|
+
include Io
|
11
|
+
|
12
|
+
def initialize(config)
|
13
|
+
@io = ensure_io(config.out_stream, config.error_stream)
|
14
|
+
super(config)
|
15
|
+
end
|
16
|
+
|
17
|
+
def output_envelope(envelope)
|
18
|
+
envelope.write_ndjson_to(@io)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|