cucumber 3.1.2 → 7.1.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 +5 -5
- data/CHANGELOG.md +1780 -1146
- data/CONTRIBUTING.md +224 -61
- data/README.md +144 -22
- data/bin/cucumber +1 -1
- data/lib/autotest/cucumber_mixin.rb +46 -53
- data/lib/cucumber/cli/configuration.rb +28 -6
- data/lib/cucumber/cli/main.rb +12 -12
- data/lib/cucumber/cli/options.rb +103 -77
- 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 +14 -7
- 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 -52
- data/lib/cucumber/formatter/ast_lookup.rb +163 -0
- data/lib/cucumber/formatter/backtrace_filter.rb +10 -8
- data/lib/cucumber/formatter/console.rb +69 -69
- 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 +147 -0
- data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
- data/lib/cucumber/formatter/interceptor.rb +11 -30
- data/lib/cucumber/formatter/io.rb +55 -13
- data/lib/cucumber/formatter/json.rb +115 -122
- data/lib/cucumber/formatter/junit.rb +72 -55
- data/lib/cucumber/formatter/message.rb +23 -0
- data/lib/cucumber/formatter/message_builder.rb +255 -0
- data/lib/cucumber/formatter/pretty.rb +360 -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 +8 -6
- 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 +17 -14
- 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/dsl.rb +19 -0
- 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 +37 -44
- data/lib/cucumber/glue/registry_and_more.rb +71 -12
- data/lib/cucumber/glue/registry_wrapper.rb +31 -0
- data/lib/cucumber/glue/snippet.rb +23 -22
- data/lib/cucumber/glue/step_definition.rb +42 -20
- 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 +77 -59
- data/lib/cucumber/step_definition_light.rb +4 -3
- data/lib/cucumber/step_definitions.rb +2 -2
- data/lib/cucumber/step_match.rb +12 -17
- 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 +272 -81
- data/lib/cucumber/core_ext/string.rb +0 -11
- 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_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,10 @@
|
|
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
8
|
|
9
9
|
module Cucumber
|
10
10
|
module Formatter
|
@@ -13,7 +13,8 @@ module Cucumber
|
|
13
13
|
include Io
|
14
14
|
|
15
15
|
def initialize(config)
|
16
|
-
@io = ensure_io(config.out_stream)
|
16
|
+
@io = ensure_io(config.out_stream, config.error_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,36 +26,40 @@ 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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@element_hash = @test_case_hash
|
40
|
-
end
|
35
|
+
|
36
|
+
@element_hash = nil
|
37
|
+
@element_background_hash = builder.background_hash
|
38
|
+
@in_background = builder.background?
|
39
|
+
|
41
40
|
@any_step_failed = false
|
42
41
|
end
|
43
42
|
|
44
43
|
def on_test_step_started(event)
|
45
44
|
test_step = event.test_step
|
46
45
|
return if internal_hook?(test_step)
|
47
|
-
|
48
|
-
if
|
46
|
+
|
47
|
+
if @element_hash.nil?
|
48
|
+
@element_hash = create_element_hash(test_step)
|
49
|
+
feature_elements << @element_hash
|
50
|
+
end
|
51
|
+
|
52
|
+
if test_step.hook?
|
49
53
|
@step_or_hook_hash = {}
|
50
|
-
hooks_of_type(
|
54
|
+
hooks_of_type(test_step) << @step_or_hook_hash
|
51
55
|
return
|
52
56
|
end
|
53
57
|
if first_step_after_background?(test_step)
|
58
|
+
@in_background = false
|
54
59
|
feature_elements << @test_case_hash
|
55
60
|
@element_hash = @test_case_hash
|
56
61
|
end
|
57
|
-
@step_or_hook_hash = create_step_hash(test_step
|
62
|
+
@step_or_hook_hash = create_step_hash(test_step)
|
58
63
|
steps << @step_or_hook_hash
|
59
64
|
@step_hash = @step_or_hook_hash
|
60
65
|
end
|
@@ -68,50 +73,47 @@ module Cucumber
|
|
68
73
|
end
|
69
74
|
|
70
75
|
def on_test_case_finished(event)
|
76
|
+
feature_elements << @test_case_hash if @in_background
|
77
|
+
|
71
78
|
_test_case, result = *event.attributes
|
72
79
|
result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
|
73
80
|
add_failed_around_hook(result) if result.failed? && !@any_step_failed
|
74
81
|
end
|
75
82
|
|
76
83
|
def on_test_run_finished(_event)
|
77
|
-
@io.write(
|
78
|
-
end
|
79
|
-
|
80
|
-
def puts(message)
|
81
|
-
test_step_output << message
|
84
|
+
@io.write(JSON.generate(@feature_hashes, pretty: true))
|
82
85
|
end
|
83
86
|
|
84
|
-
def
|
85
|
-
if
|
86
|
-
|
87
|
-
|
87
|
+
def attach(src, mime_type)
|
88
|
+
if mime_type == 'text/x.cucumber.log+plain'
|
89
|
+
test_step_output << src
|
90
|
+
return
|
91
|
+
end
|
92
|
+
if mime_type =~ /;base64$/
|
93
|
+
mime_type = mime_type[0..-8]
|
94
|
+
data = src
|
88
95
|
else
|
89
|
-
|
90
|
-
mime_type = mime_type[0..-8]
|
91
|
-
data = src
|
92
|
-
else
|
93
|
-
data = encode64(src)
|
94
|
-
end
|
96
|
+
data = encode64(src)
|
95
97
|
end
|
96
98
|
test_step_embeddings << { mime_type: mime_type, data: data }
|
97
99
|
end
|
98
100
|
|
99
101
|
private
|
100
102
|
|
101
|
-
def same_feature_as_previous_test_case?(
|
102
|
-
current_feature[:uri] ==
|
103
|
+
def same_feature_as_previous_test_case?(test_case)
|
104
|
+
current_feature[:uri] == test_case.location.file
|
103
105
|
end
|
104
106
|
|
105
107
|
def first_step_after_background?(test_step)
|
106
|
-
test_step.
|
108
|
+
@in_background && test_step.location.lines.max >= @test_case_hash[:line]
|
107
109
|
end
|
108
110
|
|
109
111
|
def internal_hook?(test_step)
|
110
|
-
test_step.
|
112
|
+
test_step.location.file.include?('lib/cucumber/')
|
111
113
|
end
|
112
114
|
|
113
115
|
def current_feature
|
114
|
-
@feature_hash ||= {}
|
116
|
+
@feature_hash ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
|
115
117
|
end
|
116
118
|
|
117
119
|
def feature_elements
|
@@ -122,16 +124,16 @@ module Cucumber
|
|
122
124
|
@element_hash[:steps] ||= []
|
123
125
|
end
|
124
126
|
|
125
|
-
def hooks_of_type(
|
126
|
-
case
|
127
|
-
when
|
128
|
-
|
129
|
-
when
|
130
|
-
|
131
|
-
when
|
132
|
-
|
127
|
+
def hooks_of_type(hook_step)
|
128
|
+
case hook_step.text
|
129
|
+
when 'Before hook'
|
130
|
+
before_hooks
|
131
|
+
when 'After hook'
|
132
|
+
after_hooks
|
133
|
+
when 'AfterStep hook'
|
134
|
+
after_step_hooks
|
133
135
|
else
|
134
|
-
|
136
|
+
raise 'Unknown hook type ' + hook_step.to_s
|
135
137
|
end
|
136
138
|
end
|
137
139
|
|
@@ -159,20 +161,27 @@ module Cucumber
|
|
159
161
|
@step_or_hook_hash[:embeddings] ||= []
|
160
162
|
end
|
161
163
|
|
162
|
-
def
|
164
|
+
def create_element_hash(test_step)
|
165
|
+
return @element_background_hash if @in_background && !first_step_after_background?(test_step)
|
166
|
+
|
167
|
+
@in_background = false
|
168
|
+
@test_case_hash
|
169
|
+
end
|
170
|
+
|
171
|
+
def create_step_hash(test_step)
|
172
|
+
step_source = @ast_lookup.step_source(test_step).step
|
163
173
|
step_hash = {
|
164
174
|
keyword: step_source.keyword,
|
165
|
-
name:
|
166
|
-
line:
|
175
|
+
name: test_step.text,
|
176
|
+
line: test_step.location.lines.min
|
167
177
|
}
|
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?
|
178
|
+
step_hash[:doc_string] = create_doc_string_hash(step_source.doc_string) unless step_source.doc_string.nil?
|
179
|
+
step_hash[:rows] = create_data_table_value(step_source.data_table) unless step_source.data_table.nil?
|
171
180
|
step_hash
|
172
181
|
end
|
173
182
|
|
174
183
|
def create_doc_string_hash(doc_string)
|
175
|
-
content_type = doc_string.
|
184
|
+
content_type = doc_string.media_type || ''
|
176
185
|
{
|
177
186
|
value: doc_string.content,
|
178
187
|
content_type: content_type,
|
@@ -181,14 +190,15 @@ module Cucumber
|
|
181
190
|
end
|
182
191
|
|
183
192
|
def create_data_table_value(data_table)
|
184
|
-
data_table.
|
185
|
-
{ cells: row }
|
193
|
+
data_table.rows.map do |row|
|
194
|
+
{ cells: row.cells.map(&:value) }
|
186
195
|
end
|
187
196
|
end
|
188
197
|
|
189
198
|
def add_match_and_result(test_step, result)
|
190
199
|
@step_or_hook_hash[:match] = create_match_hash(test_step, result)
|
191
200
|
@step_or_hook_hash[:result] = create_result_hash(result)
|
201
|
+
result.embeddings.each { |e| embed(e['src'], e['mime_type'], e['label']) } if result.respond_to?(:embeddings)
|
192
202
|
end
|
193
203
|
|
194
204
|
def add_failed_around_hook(result)
|
@@ -226,113 +236,96 @@ module Cucumber
|
|
226
236
|
class Builder
|
227
237
|
attr_reader :feature_hash, :background_hash, :test_case_hash
|
228
238
|
|
229
|
-
def initialize(test_case)
|
239
|
+
def initialize(test_case, ast_lookup)
|
230
240
|
@background_hash = nil
|
231
|
-
test_case.
|
232
|
-
|
241
|
+
uri = test_case.location.file
|
242
|
+
feature = ast_lookup.gherkin_document(uri).feature
|
243
|
+
feature(feature, uri)
|
244
|
+
background(feature.children.first.background) unless feature.children.first.background.nil?
|
245
|
+
scenario(ast_lookup.scenario_source(test_case), test_case)
|
233
246
|
end
|
234
247
|
|
235
248
|
def background?
|
236
249
|
@background_hash != nil
|
237
250
|
end
|
238
251
|
|
239
|
-
def feature(feature)
|
252
|
+
def feature(feature, uri)
|
240
253
|
@feature_hash = {
|
241
|
-
|
242
|
-
|
254
|
+
id: create_id(feature.name),
|
255
|
+
uri: uri,
|
243
256
|
keyword: feature.keyword,
|
244
|
-
name: feature.
|
245
|
-
description: feature.description,
|
257
|
+
name: feature.name,
|
258
|
+
description: value_or_empty_string(feature.description),
|
246
259
|
line: feature.location.line
|
247
260
|
}
|
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] + ';')
|
261
|
+
return if feature.tags.empty?
|
262
|
+
@feature_hash[:tags] = create_tags_array_from_hash_array(feature.tags)
|
258
263
|
end
|
259
264
|
|
260
265
|
def background(background)
|
261
266
|
@background_hash = {
|
262
267
|
keyword: background.keyword,
|
263
|
-
name: background.
|
264
|
-
description: background.description,
|
268
|
+
name: background.name,
|
269
|
+
description: value_or_empty_string(background.description),
|
265
270
|
line: background.location.line,
|
266
|
-
type: 'background'
|
271
|
+
type: 'background',
|
272
|
+
steps: []
|
267
273
|
}
|
268
|
-
@background_hash[:comments] = Formatter.create_comments_array(background.comments) unless background.comments.empty?
|
269
274
|
end
|
270
275
|
|
271
|
-
def scenario(
|
276
|
+
def scenario(scenario_source, test_case)
|
277
|
+
scenario = scenario_source.type == :Scenario ? scenario_source.scenario : scenario_source.scenario_outline
|
272
278
|
@test_case_hash = {
|
273
|
-
id:
|
279
|
+
id: "#{@feature_hash[:id]};#{create_id_from_scenario_source(scenario_source)}",
|
274
280
|
keyword: scenario.keyword,
|
275
|
-
name:
|
276
|
-
description: scenario.description,
|
277
|
-
line:
|
278
|
-
type: 'scenario'
|
281
|
+
name: test_case.name,
|
282
|
+
description: value_or_empty_string(scenario.description),
|
283
|
+
line: test_case.location.lines.max,
|
284
|
+
type: 'scenario',
|
285
|
+
steps: []
|
279
286
|
}
|
280
|
-
@test_case_hash[:tags] =
|
281
|
-
@test_case_hash[:comments] = Formatter.create_comments_array(scenario.comments) unless scenario.comments.empty?
|
287
|
+
@test_case_hash[:tags] = create_tags_array_from_tags_array(test_case.tags) unless test_case.tags.empty?
|
282
288
|
end
|
283
289
|
|
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
|
290
|
+
private
|
303
291
|
|
304
|
-
def
|
305
|
-
|
306
|
-
|
307
|
-
@example_id = create_id(examples_table) + ";#{@row.number + 1}"
|
292
|
+
def value_or_empty_string(value)
|
293
|
+
value.nil? ? '' : value
|
294
|
+
end
|
308
295
|
|
309
|
-
|
310
|
-
|
296
|
+
def create_id(name)
|
297
|
+
name.downcase.tr(' ', '-')
|
311
298
|
end
|
312
299
|
|
313
|
-
def
|
314
|
-
|
315
|
-
|
300
|
+
def create_id_from_scenario_source(scenario_source)
|
301
|
+
if scenario_source.type == :Scenario
|
302
|
+
create_id(scenario_source.scenario.name)
|
303
|
+
else
|
304
|
+
scenario_outline_name = scenario_source.scenario_outline.name
|
305
|
+
examples_name = scenario_source.examples.name
|
306
|
+
row_number = calculate_row_number(scenario_source)
|
307
|
+
"#{create_id(scenario_outline_name)};#{create_id(examples_name)};#{row_number}"
|
308
|
+
end
|
316
309
|
end
|
317
310
|
|
318
|
-
|
311
|
+
def calculate_row_number(scenario_source)
|
312
|
+
scenario_source.examples.table_body.each_with_index do |row, index|
|
313
|
+
return index + 2 if row == scenario_source.row
|
314
|
+
end
|
315
|
+
end
|
319
316
|
|
320
|
-
def
|
321
|
-
|
317
|
+
def create_tags_array_from_hash_array(tags)
|
318
|
+
tags_array = []
|
319
|
+
tags.each { |tag| tags_array << { name: tag.name, line: tag.location.line } }
|
320
|
+
tags_array
|
322
321
|
end
|
323
322
|
|
324
|
-
def
|
323
|
+
def create_tags_array_from_tags_array(tags)
|
325
324
|
tags_array = []
|
326
325
|
tags.each { |tag| tags_array << { name: tag.name, line: tag.location.line } }
|
327
326
|
tags_array
|
328
327
|
end
|
329
328
|
end
|
330
329
|
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
330
|
end
|
338
331
|
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,23 @@ 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
|
135
|
+
filename = @current_feature_data[:uri]
|
130
136
|
name = scenario_designation
|
131
137
|
|
132
|
-
|
138
|
+
testcase_attributes = get_testcase_attributes(classname, name, duration, filename)
|
139
|
+
|
140
|
+
@current_feature_data[:builder].testcase(testcase_attributes) do
|
133
141
|
if !result.passed? && result.ok?(@config.strict)
|
134
142
|
@current_feature_data[:builder].skipped
|
135
143
|
@current_feature_data[:skipped] += 1
|
136
144
|
elsif !result.passed?
|
137
145
|
status = result.to_sym
|
138
146
|
exception = get_backtrace_object(result)
|
139
|
-
@current_feature_data[:builder].failure(:
|
147
|
+
@current_feature_data[:builder].failure(message: "#{status} #{name}", type: status) do
|
140
148
|
@current_feature_data[:builder].cdata! output
|
141
149
|
@current_feature_data[:builder].cdata!(format_exception(exception)) if exception
|
142
150
|
end
|
@@ -152,13 +160,25 @@ module Cucumber
|
|
152
160
|
@current_feature_data[:tests] += 1
|
153
161
|
end
|
154
162
|
|
163
|
+
def get_testcase_attributes(classname, name, duration, filename)
|
164
|
+
{ classname: classname, name: name, time: format('%<duration>.6f', duration: duration) }.tap do |attributes|
|
165
|
+
attributes[:file] = filename if add_fileattribute?
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def add_fileattribute?
|
170
|
+
return false if @config.formats.nil? || @config.formats.empty?
|
171
|
+
|
172
|
+
!!@config.formats.find do |format|
|
173
|
+
format.first == 'junit' && format.dig(1, 'fileattribute') == 'true'
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
155
177
|
def get_backtrace_object(result)
|
156
178
|
if result.failed?
|
157
|
-
|
179
|
+
result.exception
|
158
180
|
elsif result.backtrace
|
159
|
-
|
160
|
-
else
|
161
|
-
return nil
|
181
|
+
result
|
162
182
|
end
|
163
183
|
end
|
164
184
|
|
@@ -171,7 +191,7 @@ module Cucumber
|
|
171
191
|
end
|
172
192
|
|
173
193
|
def basename(feature_file)
|
174
|
-
File.basename(feature_file.gsub(/[\\\/]/, '-'), '.feature')
|
194
|
+
File.basename(feature_file.gsub(/[\\\/]/, '-'), '.feature') # rubocop:disable Style/RegexpLiteral
|
175
195
|
end
|
176
196
|
|
177
197
|
def write_file(feature_filename, data)
|
@@ -187,34 +207,29 @@ module Cucumber
|
|
187
207
|
class NameBuilder
|
188
208
|
attr_reader :scenario_name, :name_suffix, :row_name
|
189
209
|
|
190
|
-
def initialize(test_case)
|
210
|
+
def initialize(test_case, ast_lookup)
|
191
211
|
@name_suffix = ''
|
192
212
|
@row_name = ''
|
193
|
-
test_case
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
213
|
+
scenario_source = ast_lookup.scenario_source(test_case)
|
214
|
+
if scenario_source.type == :Scenario
|
215
|
+
scenario(scenario_source.scenario)
|
216
|
+
else
|
217
|
+
scenario_outline(scenario_source.scenario_outline)
|
218
|
+
examples_table_row(scenario_source.row)
|
219
|
+
end
|
198
220
|
end
|
199
221
|
|
200
222
|
def scenario(scenario)
|
201
|
-
@scenario_name =
|
202
|
-
self
|
223
|
+
@scenario_name = scenario.name.empty? ? 'Unnamed scenario' : scenario.name
|
203
224
|
end
|
204
225
|
|
205
226
|
def scenario_outline(outline)
|
206
|
-
@scenario_name =
|
207
|
-
self
|
208
|
-
end
|
209
|
-
|
210
|
-
def examples_table(*)
|
211
|
-
self
|
227
|
+
@scenario_name = outline.name.empty? ? 'Unnamed scenario outline' : outline.name
|
212
228
|
end
|
213
229
|
|
214
230
|
def examples_table_row(row)
|
215
|
-
@row_name = '| ' + row.
|
231
|
+
@row_name = '| ' + row.cells.map(&:value).join(' | ') + ' |'
|
216
232
|
@name_suffix = " (outline example : #{@row_name})"
|
217
|
-
self
|
218
233
|
end
|
219
234
|
end
|
220
235
|
|
@@ -238,8 +253,10 @@ module Cucumber
|
|
238
253
|
def exception(*) end
|
239
254
|
|
240
255
|
def duration(duration, *)
|
241
|
-
duration.tap { |
|
256
|
+
duration.tap { |dur| @test_case_duration = dur.nanoseconds / 10**9.0 }
|
242
257
|
end
|
258
|
+
|
259
|
+
def attach(*) end
|
243
260
|
end
|
244
261
|
end
|
245
262
|
end
|
@@ -0,0 +1,23 @@
|
|
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
|
+
@io.write(envelope.to_json)
|
19
|
+
@io.write("\n")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|