cucumber 5.2.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -126,13 +126,35 @@ module Cucumber
126
126
  end
127
127
 
128
128
  def arrange_formats
129
- @options[:formats] << ['pretty', {}, @out_stream] if @options[:formats].empty?
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
@@ -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', 'Generates a report similar to Ant+JUnit.'],
26
- 'json' => ['Cucumber::Formatter::Json', '[DEPRECATED] Prints the feature as JSON'],
27
- 'message' => ['Cucumber::Formatter::Message', 'Outputs protobuf messages'],
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
@@ -15,6 +15,7 @@ module Cucumber
15
15
  test/unit
16
16
  .gem/ruby
17
17
  bin/bundle
18
+ rdebug-ide
18
19
  ]
19
20
 
20
21
  @backtrace_filters << RbConfig::CONFIG['rubyarchdir'] if RbConfig::CONFIG['rubyarchdir']
@@ -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).indent(source_indent)
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, indent)
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")}".indent(indent)
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
- if builder.background?
45
- @in_background = true
46
- feature_elements << builder.background_hash
47
- @element_hash = builder.background_hash
48
- else
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 File.file?(src)
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
- @current_feature_data[:builder].testcase(classname: classname, name: name, time: format('%<duration>.6f', duration: duration)) do
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
@@ -15,7 +15,8 @@ module Cucumber
15
15
  end
16
16
 
17
17
  def output_envelope(envelope)
18
- envelope.write_ndjson_to(@io)
18
+ @io.write(envelope.to_json)
19
+ @io.write("\n")
19
20
  end
20
21
  end
21
22
  end
@@ -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::Attachment::ContentEncoding::IDENTITY
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::Attachment::ContentEncoding::BASE64
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::TestCase::TestStep.new(
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::TestCase::TestStep.new(
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::TestCase::TestStep::StepMatchArgumentsList.new(
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::TestCase::TestStep::StepMatchArgumentsList::StepMatchArgument.new(
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::TestCase::TestStep::StepMatchArgumentsList::StepMatchArgument::Group.new(
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::TestStepFinished::TestStepResult.new(
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).indent(6)) }
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, indent)
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).indent(indent))
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, indent)
274
+ def print_tags(tags, indent_amount)
275
275
  return if !tags || tags.empty?
276
- @io.puts(tags.map { |tag| format_string(tag.name, :tag) }.join(' ').indent(indent))
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, indent, location = nil)
284
+ def print_keyword_name(keyword, name, indent_amount, location = nil)
284
285
  line = "#{keyword}:"
285
286
  line += " #{name}"
286
- @io.print(line.indent(indent))
287
+ @io.print(indent(line, indent_amount))
287
288
  if location && options[:source]
288
- line_comment = format_string("# #{location}", :comment).indent(@source_indent - line.length - indent)
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(name_to_report.indent(base_indent))
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, indent)
378
+ def print_data_table(data_table, status, indent_amount)
378
379
  data_table.rows.each do |row|
379
- print_comments(row.location.line, indent)
380
- @io.puts format_string(gherkin_source.split("\n")[row.location.line - 1].strip, status).indent(indent)
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(comment_line.indent(@source_indent - step_line.length))
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, indent)
407
- s = %("""\n#{content}\n""").indent(indent)
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.indent(6))
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).indent(6))
427
- @io.print(format_string(@test_step_output.join(', '), :tag).indent(2)) unless @test_step_output.empty?
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?