cucumber 5.2.0 → 7.0.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 +1400 -1098
- data/CONTRIBUTING.md +229 -52
- data/README.md +140 -20
- data/lib/cucumber/cli/configuration.rb +23 -1
- data/lib/cucumber/cli/options.rb +8 -3
- data/lib/cucumber/formatter/ansicolor.rb +0 -7
- data/lib/cucumber/formatter/backtrace_filter.rb +1 -0
- data/lib/cucumber/formatter/console.rb +11 -3
- data/lib/cucumber/formatter/json.rb +25 -24
- data/lib/cucumber/formatter/junit.rb +18 -1
- data/lib/cucumber/formatter/message.rb +2 -1
- data/lib/cucumber/formatter/message_builder.rb +8 -8
- data/lib/cucumber/formatter/pretty.rb +19 -18
- data/lib/cucumber/formatter/publish_banner_printer.rb +1 -1
- data/lib/cucumber/formatter/steps.rb +5 -2
- data/lib/cucumber/formatter/usage.rb +6 -4
- data/lib/cucumber/gherkin/data_table_parser.rb +1 -1
- data/lib/cucumber/gherkin/steps_parser.rb +1 -1
- data/lib/cucumber/glue/proto_world.rb +15 -31
- data/lib/cucumber/glue/registry_and_more.rb +1 -1
- data/lib/cucumber/glue/step_definition.rb +3 -4
- data/lib/cucumber/runtime.rb +6 -1
- data/lib/cucumber/step_match.rb +1 -7
- data/lib/cucumber/version +1 -1
- metadata +88 -69
- data/lib/cucumber/core_ext/string.rb +0 -11
@@ -126,13 +126,35 @@ module Cucumber
|
|
126
126
|
end
|
127
127
|
|
128
128
|
def arrange_formats
|
129
|
-
|
129
|
+
add_default_formatter if needs_default_formatter?
|
130
|
+
|
130
131
|
@options[:formats] = @options[:formats].sort_by do |f|
|
131
132
|
f[2] == @out_stream ? -1 : 1
|
132
133
|
end
|
133
134
|
@options[:formats].uniq!
|
134
135
|
@options.check_formatter_stream_conflicts
|
135
136
|
end
|
137
|
+
|
138
|
+
def add_default_formatter
|
139
|
+
@options[:formats] << ['pretty', {}, @out_stream]
|
140
|
+
end
|
141
|
+
|
142
|
+
def needs_default_formatter?
|
143
|
+
formatter_missing? || publish_only?
|
144
|
+
end
|
145
|
+
|
146
|
+
def formatter_missing?
|
147
|
+
@options[:formats].empty?
|
148
|
+
end
|
149
|
+
|
150
|
+
def publish_only?
|
151
|
+
@options[:formats]
|
152
|
+
.uniq
|
153
|
+
.map { |formatter, _, stream| [formatter, stream] }
|
154
|
+
.uniq
|
155
|
+
.reject { |formatter, stream| formatter == 'message' && stream != @out_stream }
|
156
|
+
.empty?
|
157
|
+
end
|
136
158
|
end
|
137
159
|
end
|
138
160
|
end
|
data/lib/cucumber/cli/options.rb
CHANGED
@@ -22,9 +22,14 @@ module Cucumber
|
|
22
22
|
"#{INDENT}filename instead."],
|
23
23
|
'stepdefs' => ['Cucumber::Formatter::Stepdefs', "Prints All step definitions with their locations. Same as\n" \
|
24
24
|
"#{INDENT}the usage formatter, except that steps are not printed."],
|
25
|
-
'junit' => ['Cucumber::Formatter::Junit',
|
26
|
-
|
27
|
-
'
|
25
|
+
'junit' => ['Cucumber::Formatter::Junit', "Generates a report similar to Ant+JUnit. Use\n" \
|
26
|
+
"#{INDENT}junit,fileattribute=true to include a file attribute."],
|
27
|
+
'json' => ['Cucumber::Formatter::Json', "Prints the feature as JSON.\n" \
|
28
|
+
"#{INDENT}The JSON format is in maintenance mode.\n" \
|
29
|
+
"#{INDENT}Please consider using the message formatter\n"\
|
30
|
+
"#{INDENT}with the standalone json-formatter\n" \
|
31
|
+
"#{INDENT}(https://github.com/cucumber/cucumber/tree/master/json-formatter)."],
|
32
|
+
'message' => ['Cucumber::Formatter::Message', 'Prints each message in NDJSON form, which can then be consumed by other tools.'],
|
28
33
|
'html' => ['Cucumber::Formatter::HTML', 'Outputs HTML report'],
|
29
34
|
'summary' => ['Cucumber::Formatter::Summary', 'Summary output of feature and scenarios']
|
30
35
|
}.freeze
|
@@ -3,13 +3,6 @@
|
|
3
3
|
require 'cucumber/platform'
|
4
4
|
require 'cucumber/term/ansicolor'
|
5
5
|
|
6
|
-
if Cucumber::WINDOWS_MRI
|
7
|
-
unless ENV['ANSICON']
|
8
|
-
STDERR.puts %{*** WARNING: You must use ANSICON 1.31 or higher (https://github.com/adoxa/ansicon/) to get coloured output on Windows}
|
9
|
-
Cucumber::Term::ANSIColor.coloring = false
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
6
|
Cucumber::Term::ANSIColor.coloring = false if !STDOUT.tty? && !ENV.key?('AUTOTEST')
|
14
7
|
|
15
8
|
module Cucumber
|
@@ -34,7 +34,7 @@ module Cucumber
|
|
34
34
|
|
35
35
|
def format_step(keyword, step_match, status, source_indent)
|
36
36
|
comment = if source_indent
|
37
|
-
c = ('# ' + step_match.location.to_s)
|
37
|
+
c = indent(('# ' + step_match.location.to_s), source_indent)
|
38
38
|
format_string(c, :comment)
|
39
39
|
else
|
40
40
|
''
|
@@ -99,11 +99,11 @@ module Cucumber
|
|
99
99
|
@io.puts(format_string(string, status))
|
100
100
|
end
|
101
101
|
|
102
|
-
def exception_message_string(e,
|
102
|
+
def exception_message_string(e, indent_amount)
|
103
103
|
message = "#{e.message} (#{e.class})".dup.force_encoding('UTF-8')
|
104
104
|
message = linebreaks(message, ENV['CUCUMBER_TRUNCATE_OUTPUT'].to_i)
|
105
105
|
|
106
|
-
"#{message}\n#{e.backtrace.join("\n")}"
|
106
|
+
indent("#{message}\n#{e.backtrace.join("\n")}", indent_amount)
|
107
107
|
end
|
108
108
|
|
109
109
|
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/10655
|
@@ -208,6 +208,14 @@ module Cucumber
|
|
208
208
|
].join("\n")
|
209
209
|
end
|
210
210
|
|
211
|
+
def indent(string, padding)
|
212
|
+
if padding >= 0
|
213
|
+
string.gsub(/^/, ' ' * padding)
|
214
|
+
else
|
215
|
+
string.gsub(/^ {0,#{-padding}}/, '')
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
211
219
|
private
|
212
220
|
|
213
221
|
FORMATS = Hash.new { |hash, format| hash[format] = method(format).to_proc }
|
@@ -5,7 +5,6 @@ require 'base64'
|
|
5
5
|
require 'cucumber/formatter/backtrace_filter'
|
6
6
|
require 'cucumber/formatter/io'
|
7
7
|
require 'cucumber/formatter/ast_lookup'
|
8
|
-
require 'cucumber/deprecate'
|
9
8
|
|
10
9
|
module Cucumber
|
11
10
|
module Formatter
|
@@ -14,14 +13,6 @@ module Cucumber
|
|
14
13
|
include Io
|
15
14
|
|
16
15
|
def initialize(config)
|
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
16
|
@io = ensure_io(config.out_stream, config.error_stream)
|
26
17
|
@ast_lookup = AstLookup.new(config)
|
27
18
|
@feature_hashes = []
|
@@ -41,21 +32,23 @@ module Cucumber
|
|
41
32
|
@feature_hashes << @feature_hash
|
42
33
|
end
|
43
34
|
@test_case_hash = builder.test_case_hash
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
@in_background = false
|
50
|
-
feature_elements << @test_case_hash
|
51
|
-
@element_hash = @test_case_hash
|
52
|
-
end
|
35
|
+
|
36
|
+
@element_hash = nil
|
37
|
+
@element_background_hash = builder.background_hash
|
38
|
+
@in_background = builder.background?
|
39
|
+
|
53
40
|
@any_step_failed = false
|
54
41
|
end
|
55
42
|
|
56
43
|
def on_test_step_started(event)
|
57
44
|
test_step = event.test_step
|
58
45
|
return if internal_hook?(test_step)
|
46
|
+
|
47
|
+
if @element_hash.nil?
|
48
|
+
@element_hash = create_element_hash(test_step)
|
49
|
+
feature_elements << @element_hash
|
50
|
+
end
|
51
|
+
|
59
52
|
if test_step.hook?
|
60
53
|
@step_or_hook_hash = {}
|
61
54
|
hooks_of_type(test_step) << @step_or_hook_hash
|
@@ -80,6 +73,8 @@ module Cucumber
|
|
80
73
|
end
|
81
74
|
|
82
75
|
def on_test_case_finished(event)
|
76
|
+
feature_elements << @test_case_hash if @in_background
|
77
|
+
|
83
78
|
_test_case, result = *event.attributes
|
84
79
|
result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
|
85
80
|
add_failed_around_hook(result) if result.failed? && !@any_step_failed
|
@@ -94,10 +89,7 @@ module Cucumber
|
|
94
89
|
test_step_output << src
|
95
90
|
return
|
96
91
|
end
|
97
|
-
if
|
98
|
-
content = File.open(src, 'rb', &:read)
|
99
|
-
data = encode64(content)
|
100
|
-
elsif mime_type =~ /;base64$/
|
92
|
+
if mime_type =~ /;base64$/
|
101
93
|
mime_type = mime_type[0..-8]
|
102
94
|
data = src
|
103
95
|
else
|
@@ -169,6 +161,13 @@ module Cucumber
|
|
169
161
|
@step_or_hook_hash[:embeddings] ||= []
|
170
162
|
end
|
171
163
|
|
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
|
+
|
172
171
|
def create_step_hash(test_step)
|
173
172
|
step_source = @ast_lookup.step_source(test_step).step
|
174
173
|
step_hash = {
|
@@ -269,7 +268,8 @@ module Cucumber
|
|
269
268
|
name: background.name,
|
270
269
|
description: value_or_empty_string(background.description),
|
271
270
|
line: background.location.line,
|
272
|
-
type: 'background'
|
271
|
+
type: 'background',
|
272
|
+
steps: []
|
273
273
|
}
|
274
274
|
end
|
275
275
|
|
@@ -281,7 +281,8 @@ module Cucumber
|
|
281
281
|
name: test_case.name,
|
282
282
|
description: value_or_empty_string(scenario.description),
|
283
283
|
line: test_case.location.lines.max,
|
284
|
-
type: 'scenario'
|
284
|
+
type: 'scenario',
|
285
|
+
steps: []
|
285
286
|
}
|
286
287
|
@test_case_hash[:tags] = create_tags_array_from_tags_array(test_case.tags) unless test_case.tags.empty?
|
287
288
|
end
|
@@ -132,9 +132,12 @@ module Cucumber
|
|
132
132
|
duration = ResultBuilder.new(result).test_case_duration
|
133
133
|
@current_feature_data[:time] += duration
|
134
134
|
classname = @current_feature_data[:feature].name
|
135
|
+
filename = @current_feature_data[:uri]
|
135
136
|
name = scenario_designation
|
136
137
|
|
137
|
-
|
138
|
+
testcase_attributes = get_testcase_attributes(classname, name, duration, filename)
|
139
|
+
|
140
|
+
@current_feature_data[:builder].testcase(testcase_attributes) do
|
138
141
|
if !result.passed? && result.ok?(@config.strict)
|
139
142
|
@current_feature_data[:builder].skipped
|
140
143
|
@current_feature_data[:skipped] += 1
|
@@ -157,6 +160,20 @@ module Cucumber
|
|
157
160
|
@current_feature_data[:tests] += 1
|
158
161
|
end
|
159
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
|
+
|
160
177
|
def get_backtrace_object(result)
|
161
178
|
if result.failed?
|
162
179
|
result.exception
|
@@ -48,12 +48,12 @@ module Cucumber
|
|
48
48
|
}
|
49
49
|
|
50
50
|
if media_type.start_with?('text/')
|
51
|
-
attachment_data[:content_encoding] = Cucumber::Messages::
|
51
|
+
attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY
|
52
52
|
attachment_data[:body] = src
|
53
53
|
else
|
54
54
|
body = src.respond_to?(:read) ? src.read : src
|
55
55
|
|
56
|
-
attachment_data[:content_encoding] = Cucumber::Messages::
|
56
|
+
attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::BASE64
|
57
57
|
attachment_data[:body] = Base64.strict_encode64(body)
|
58
58
|
end
|
59
59
|
|
@@ -101,7 +101,7 @@ module Cucumber
|
|
101
101
|
def test_step_to_message(step)
|
102
102
|
return hook_step_to_message(step) if step.hook?
|
103
103
|
|
104
|
-
Cucumber::Messages::
|
104
|
+
Cucumber::Messages::TestStep.new(
|
105
105
|
id: step.id,
|
106
106
|
pickle_step_id: @pickle_step_by_test_step.pickle_step_id(step),
|
107
107
|
step_definition_ids: @step_definitions_by_test_step.step_definition_ids(step),
|
@@ -110,7 +110,7 @@ module Cucumber
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def hook_step_to_message(step)
|
113
|
-
Cucumber::Messages::
|
113
|
+
Cucumber::Messages::TestStep.new(
|
114
114
|
id: step.id,
|
115
115
|
hook_id: @hook_by_test_step.hook_id(step)
|
116
116
|
)
|
@@ -118,7 +118,7 @@ module Cucumber
|
|
118
118
|
|
119
119
|
def step_match_arguments_lists(step)
|
120
120
|
match_arguments = step_match_arguments(step)
|
121
|
-
[Cucumber::Messages::
|
121
|
+
[Cucumber::Messages::StepMatchArgumentsList.new(
|
122
122
|
step_match_arguments: match_arguments
|
123
123
|
)]
|
124
124
|
rescue Cucumber::Formatter::TestStepUnknownError
|
@@ -127,7 +127,7 @@ module Cucumber
|
|
127
127
|
|
128
128
|
def step_match_arguments(step)
|
129
129
|
@step_definitions_by_test_step.step_match_arguments(step).map do |argument|
|
130
|
-
Cucumber::Messages::
|
130
|
+
Cucumber::Messages::StepMatchArgument.new(
|
131
131
|
group: argument_group_to_message(argument.group),
|
132
132
|
parameter_type_name: argument.parameter_type.name
|
133
133
|
)
|
@@ -135,7 +135,7 @@ module Cucumber
|
|
135
135
|
end
|
136
136
|
|
137
137
|
def argument_group_to_message(group)
|
138
|
-
Cucumber::Messages::
|
138
|
+
Cucumber::Messages::Group.new(
|
139
139
|
start: group.start,
|
140
140
|
value: group.value,
|
141
141
|
children: group.children.map { |child| argument_group_to_message(child) }
|
@@ -190,7 +190,7 @@ module Cucumber
|
|
190
190
|
|
191
191
|
result_message = result.to_message
|
192
192
|
if result.failed? || result.pending?
|
193
|
-
result_message = Cucumber::Messages::
|
193
|
+
result_message = Cucumber::Messages::TestStepResult.new(
|
194
194
|
status: result_message.status,
|
195
195
|
duration: result_message.duration,
|
196
196
|
message: create_error_message(result)
|
@@ -181,7 +181,7 @@ module Cucumber
|
|
181
181
|
end
|
182
182
|
|
183
183
|
def print_step_output
|
184
|
-
@test_step_output.each { |message| @io.puts(format_string(message, :tag)
|
184
|
+
@test_step_output.each { |message| @io.puts(indent(format_string(message, :tag), 6)) }
|
185
185
|
@test_step_output = []
|
186
186
|
end
|
187
187
|
|
@@ -259,33 +259,34 @@ module Cucumber
|
|
259
259
|
end
|
260
260
|
end
|
261
261
|
|
262
|
-
def print_comments(up_to_line,
|
262
|
+
def print_comments(up_to_line, indent_amount)
|
263
263
|
comments = gherkin_document.comments
|
264
264
|
return if comments.empty? || comments.length <= @next_comment_to_be_printed
|
265
265
|
comments[@next_comment_to_be_printed..-1].each do |comment|
|
266
266
|
if comment.location.line <= up_to_line
|
267
|
-
@io.puts(format_string(comment.text.strip, :comment)
|
267
|
+
@io.puts(indent(format_string(comment.text.strip, :comment), indent_amount))
|
268
268
|
@next_comment_to_be_printed += 1
|
269
269
|
end
|
270
270
|
break if @next_comment_to_be_printed >= comments.length
|
271
271
|
end
|
272
272
|
end
|
273
273
|
|
274
|
-
def print_tags(tags,
|
274
|
+
def print_tags(tags, indent_amount)
|
275
275
|
return if !tags || tags.empty?
|
276
|
-
|
276
|
+
|
277
|
+
@io.puts(indent(tags.map { |tag| format_string(tag.name, :tag) }.join(' '), indent_amount))
|
277
278
|
end
|
278
279
|
|
279
280
|
def print_feature_line(feature)
|
280
281
|
print_keyword_name(feature.keyword, feature.name, 0)
|
281
282
|
end
|
282
283
|
|
283
|
-
def print_keyword_name(keyword, name,
|
284
|
+
def print_keyword_name(keyword, name, indent_amount, location = nil)
|
284
285
|
line = "#{keyword}:"
|
285
286
|
line += " #{name}"
|
286
|
-
@io.print(
|
287
|
+
@io.print(indent(line, indent_amount))
|
287
288
|
if location && options[:source]
|
288
|
-
line_comment = format_string("# #{location}", :comment)
|
289
|
+
line_comment = indent(format_string("# #{location}", :comment), @source_indent - line.length - indent_amount)
|
289
290
|
@io.print(line_comment)
|
290
291
|
end
|
291
292
|
@io.puts
|
@@ -339,7 +340,7 @@ module Cucumber
|
|
339
340
|
indent = options[:source] ? @source_indent - step_keyword.length - test_step.text.length - base_indent : nil
|
340
341
|
print_comments(test_step.location.lines.max, base_indent)
|
341
342
|
name_to_report = format_step(step_keyword, @step_matches.fetch(test_step.to_s) { NoStepMatch.new(test_step, test_step.text) }, result.to_sym, indent)
|
342
|
-
@io.puts(
|
343
|
+
@io.puts(indent(name_to_report, base_indent))
|
343
344
|
print_multiline_argument(test_step, result, base_indent + 2) unless options[:no_multiline]
|
344
345
|
@io.flush
|
345
346
|
end
|
@@ -374,10 +375,10 @@ module Cucumber
|
|
374
375
|
end
|
375
376
|
end
|
376
377
|
|
377
|
-
def print_data_table(data_table, status,
|
378
|
+
def print_data_table(data_table, status, indent_amount)
|
378
379
|
data_table.rows.each do |row|
|
379
|
-
print_comments(row.location.line,
|
380
|
-
@io.puts format_string(gherkin_source.split("\n")[row.location.line - 1].strip, status)
|
380
|
+
print_comments(row.location.line, indent_amount)
|
381
|
+
@io.puts indent(format_string(gherkin_source.split("\n")[row.location.line - 1].strip, status), indent_amount)
|
381
382
|
end
|
382
383
|
end
|
383
384
|
|
@@ -393,7 +394,7 @@ module Cucumber
|
|
393
394
|
@io.print(format_string(step_line, :skipped))
|
394
395
|
if options[:source]
|
395
396
|
comment_line = format_string("# #{current_feature_uri}:#{step.location.line}", :comment)
|
396
|
-
@io.print(
|
397
|
+
@io.print(indent(comment_line, @source_indent - step_line.length))
|
397
398
|
end
|
398
399
|
@io.puts
|
399
400
|
next if options[:no_multiline]
|
@@ -403,8 +404,8 @@ module Cucumber
|
|
403
404
|
@io.flush
|
404
405
|
end
|
405
406
|
|
406
|
-
def print_doc_string(content, status,
|
407
|
-
s = %("""\n#{content}\n""")
|
407
|
+
def print_doc_string(content, status, indent_amount)
|
408
|
+
s = indent(%("""\n#{content}\n"""), indent_amount)
|
408
409
|
s = s.split("\n").map { |l| l =~ /^\s+$/ ? '' : l }.join("\n")
|
409
410
|
@io.puts(format_string(s, status))
|
410
411
|
end
|
@@ -416,15 +417,15 @@ module Cucumber
|
|
416
417
|
print_description(examples.description)
|
417
418
|
unless options[:expand]
|
418
419
|
print_comments(examples.table_header.location.line, 6)
|
419
|
-
@io.puts(gherkin_source.split("\n")[examples.table_header.location.line - 1].strip
|
420
|
+
@io.puts(indent(gherkin_source.split("\n")[examples.table_header.location.line - 1].strip, 6))
|
420
421
|
end
|
421
422
|
@io.flush
|
422
423
|
end
|
423
424
|
|
424
425
|
def print_row_data(test_case, result)
|
425
426
|
print_comments(test_case.location.lines.max, 6)
|
426
|
-
@io.print(format_string(gherkin_source.split("\n")[test_case.location.lines.max - 1].strip, result.to_sym)
|
427
|
-
@io.print(format_string(@test_step_output.join(', '), :tag)
|
427
|
+
@io.print(indent(format_string(gherkin_source.split("\n")[test_case.location.lines.max - 1].strip, result.to_sym), 6))
|
428
|
+
@io.print(indent(format_string(@test_step_output.join(', '), :tag), 2)) unless @test_step_output.empty?
|
428
429
|
@test_step_output = []
|
429
430
|
@io.puts
|
430
431
|
if result.failed? || result.pending?
|