cucumber 8.0.0 → 9.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -23
- data/VERSION +1 -0
- data/lib/cucumber/cli/main.rb +1 -1
- data/lib/cucumber/cli/options.rb +66 -66
- data/lib/cucumber/cli/profile_loader.rb +5 -5
- data/lib/cucumber/configuration.rb +7 -2
- data/lib/cucumber/deprecate.rb +6 -47
- data/lib/cucumber/errors.rb +2 -1
- data/lib/cucumber/events/envelope.rb +2 -0
- data/lib/cucumber/events/gherkin_source_parsed.rb +2 -0
- data/lib/cucumber/events/gherkin_source_read.rb +2 -0
- data/lib/cucumber/events/test_case_finished.rb +2 -0
- data/lib/cucumber/events/test_case_started.rb +2 -0
- data/lib/cucumber/events/test_step_finished.rb +2 -0
- data/lib/cucumber/events/test_step_started.rb +2 -0
- data/lib/cucumber/events/undefined_parameter_type.rb +2 -0
- data/lib/cucumber/file_specs.rb +1 -1
- data/lib/cucumber/filters/retry.rb +20 -1
- data/lib/cucumber/formatter/ansicolor.rb +19 -27
- data/lib/cucumber/formatter/ast_lookup.rb +14 -6
- data/lib/cucumber/formatter/console.rb +16 -14
- data/lib/cucumber/formatter/console_counts.rb +3 -1
- data/lib/cucumber/formatter/console_issues.rb +4 -2
- data/lib/cucumber/formatter/curl_option_parser.rb +49 -0
- data/lib/cucumber/formatter/errors.rb +2 -0
- data/lib/cucumber/formatter/fail_fast.rb +1 -1
- data/lib/cucumber/formatter/html.rb +2 -0
- data/lib/cucumber/formatter/http_io.rb +10 -142
- data/lib/cucumber/formatter/io_http_buffer.rb +88 -0
- data/lib/cucumber/formatter/json.rb +2 -6
- data/lib/cucumber/formatter/junit.rb +4 -4
- data/lib/cucumber/formatter/message_builder.rb +21 -6
- data/lib/cucumber/formatter/pretty.rb +9 -5
- data/lib/cucumber/formatter/publish_banner_printer.rb +0 -2
- data/lib/cucumber/formatter/query/hook_by_test_step.rb +2 -0
- data/lib/cucumber/formatter/query/pickle_by_test.rb +2 -0
- data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +2 -0
- data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +2 -0
- data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +2 -0
- data/lib/cucumber/formatter/rerun.rb +3 -3
- data/lib/cucumber/formatter/unicode.rb +3 -3
- data/lib/cucumber/formatter/url_reporter.rb +3 -1
- data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +23 -25
- data/lib/cucumber/glue/invoke_in_world.rb +2 -2
- data/lib/cucumber/glue/proto_world.rb +20 -25
- data/lib/cucumber/glue/registry_and_more.rb +9 -5
- data/lib/cucumber/glue/snippet.rb +4 -2
- data/lib/cucumber/glue/world_factory.rb +2 -0
- data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -0
- data/lib/cucumber/multiline_argument/data_table.rb +37 -36
- data/lib/cucumber/platform.rb +11 -16
- data/lib/cucumber/rake/task.rb +2 -6
- data/lib/cucumber/running_test_case.rb +1 -1
- data/lib/cucumber/runtime/for_programming_languages.rb +1 -2
- data/lib/cucumber/runtime/meta_message_builder.rb +4 -2
- data/lib/cucumber/runtime/user_interface.rb +2 -2
- data/lib/cucumber/runtime.rb +5 -5
- data/lib/cucumber/step_match.rb +1 -1
- data/lib/cucumber/term/ansicolor.rb +1 -1
- data/lib/cucumber/term/banner.rb +2 -0
- metadata +86 -242
- data/CHANGELOG.md +0 -3231
- data/CONTRIBUTING.md +0 -246
- data/lib/autotest/cucumber.rb +0 -8
- data/lib/autotest/cucumber_mixin.rb +0 -133
- data/lib/autotest/cucumber_rails.rb +0 -8
- data/lib/autotest/cucumber_rails_rspec.rb +0 -8
- data/lib/autotest/cucumber_rails_rspec2.rb +0 -8
- data/lib/autotest/cucumber_rspec.rb +0 -8
- data/lib/autotest/cucumber_rspec2.rb +0 -8
- data/lib/autotest/discover.rb +0 -14
- data/lib/cucumber/version +0 -1
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'cucumber/platform'
|
4
4
|
require 'cucumber/term/ansicolor'
|
5
5
|
|
6
|
-
Cucumber::Term::ANSIColor.coloring = false
|
6
|
+
Cucumber::Term::ANSIColor.coloring = false unless $stdout.tty?
|
7
7
|
|
8
8
|
module Cucumber
|
9
9
|
module Formatter
|
@@ -63,30 +63,24 @@ module Cucumber
|
|
63
63
|
module ANSIColor
|
64
64
|
include Cucumber::Term::ANSIColor
|
65
65
|
|
66
|
-
# :stopdoc:
|
67
66
|
ALIASES = Hash.new do |h, k|
|
68
67
|
next unless k.to_s =~ /(.*)_param/
|
69
68
|
|
70
69
|
"#{h[Regexp.last_match(1)]},bold"
|
71
70
|
end.merge(
|
72
71
|
'undefined' => 'yellow',
|
73
|
-
'pending'
|
74
|
-
'flaky'
|
75
|
-
'failed'
|
76
|
-
'passed'
|
77
|
-
'outline'
|
78
|
-
'skipped'
|
79
|
-
'comment'
|
80
|
-
'tag'
|
72
|
+
'pending' => 'yellow',
|
73
|
+
'flaky' => 'yellow',
|
74
|
+
'failed' => 'red',
|
75
|
+
'passed' => 'green',
|
76
|
+
'outline' => 'cyan',
|
77
|
+
'skipped' => 'cyan',
|
78
|
+
'comment' => 'grey',
|
79
|
+
'tag' => 'cyan'
|
81
80
|
)
|
82
|
-
# :startdoc:
|
83
81
|
|
84
|
-
# Apply the custom color scheme
|
85
|
-
|
86
|
-
# example:
|
87
|
-
#
|
88
|
-
# apply_custom_colors('passed=white')
|
89
|
-
def apply_custom_colors(colors)
|
82
|
+
# Apply the custom color scheme -> i.e. apply_custom_colors('passed=white')
|
83
|
+
def self.apply_custom_colors(colors)
|
90
84
|
colors.split(':').each do |pair|
|
91
85
|
a = pair.split('=')
|
92
86
|
ALIASES[a[0]] = a[1]
|
@@ -117,23 +111,21 @@ module Cucumber
|
|
117
111
|
end
|
118
112
|
end
|
119
113
|
|
120
|
-
|
121
|
-
|
122
|
-
('(::) ' * n).strip
|
114
|
+
def cukes(amount)
|
115
|
+
('(::) ' * amount).strip
|
123
116
|
end
|
124
117
|
|
125
|
-
def green_cukes(
|
126
|
-
blink(green(cukes(
|
118
|
+
def green_cukes(amount)
|
119
|
+
blink(green(cukes(amount)))
|
127
120
|
end
|
128
121
|
|
129
|
-
def red_cukes(
|
130
|
-
blink(red(cukes(
|
122
|
+
def red_cukes(amount)
|
123
|
+
blink(red(cukes(amount)))
|
131
124
|
end
|
132
125
|
|
133
|
-
def yellow_cukes(
|
134
|
-
blink(yellow(cukes(
|
126
|
+
def yellow_cukes(amount)
|
127
|
+
blink(yellow(cukes(amount)))
|
135
128
|
end
|
136
|
-
# :startdoc:
|
137
129
|
|
138
130
|
private
|
139
131
|
|
@@ -112,16 +112,24 @@ module Cucumber
|
|
112
112
|
if child.respond_to?(:rule) && child.rule
|
113
113
|
process_scenario_container(child.rule)
|
114
114
|
elsif child.respond_to?(:scenario) && child.scenario
|
115
|
-
child.scenario
|
116
|
-
@lookup_hash[step.location.line] = StepSource.new(:Step, step)
|
117
|
-
end
|
115
|
+
store_scenario_source_steps(child.scenario)
|
118
116
|
elsif !child.background.nil?
|
119
|
-
child.background
|
120
|
-
@lookup_hash[step.location.line] = StepSource.new(:Step, step)
|
121
|
-
end
|
117
|
+
store_background_source_steps(child.background)
|
122
118
|
end
|
123
119
|
end
|
124
120
|
end
|
121
|
+
|
122
|
+
def store_scenario_source_steps(scenario)
|
123
|
+
scenario.steps.each do |step|
|
124
|
+
@lookup_hash[step.location.line] = StepSource.new(:Step, step)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def store_background_source_steps(background)
|
129
|
+
background.steps.each do |step|
|
130
|
+
@lookup_hash[step.location.line] = StepSource.new(:Step, step)
|
131
|
+
end
|
132
|
+
end
|
125
133
|
end
|
126
134
|
|
127
135
|
KeywordSearchNode = Struct.new(:keyword, :previous_node)
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# rubocop:disable Metrics/ModuleLength
|
4
|
-
|
5
3
|
require 'cucumber/formatter/ansicolor'
|
6
4
|
require 'cucumber/formatter/duration'
|
7
5
|
require 'cucumber/gherkin/i18n'
|
@@ -45,10 +43,10 @@ module Cucumber
|
|
45
43
|
format_string(line, status)
|
46
44
|
end
|
47
45
|
|
48
|
-
def format_string(
|
46
|
+
def format_string(input, status)
|
49
47
|
fmt = format_for(status)
|
50
|
-
|
51
|
-
if
|
48
|
+
input.to_s.split("\n").map do |line|
|
49
|
+
if fmt.instance_of?(Proc)
|
52
50
|
fmt.call(line)
|
53
51
|
else
|
54
52
|
fmt % line
|
@@ -94,21 +92,21 @@ module Cucumber
|
|
94
92
|
@io.flush
|
95
93
|
end
|
96
94
|
|
97
|
-
def print_exception(
|
98
|
-
string = exception_message_string(
|
95
|
+
def print_exception(exception, status, indent)
|
96
|
+
string = exception_message_string(exception, indent)
|
99
97
|
@io.puts(format_string(string, status))
|
100
98
|
end
|
101
99
|
|
102
|
-
def exception_message_string(
|
103
|
-
message = "#{
|
100
|
+
def exception_message_string(exception, indent_amount)
|
101
|
+
message = "#{exception.message} (#{exception.class})".dup.force_encoding('UTF-8')
|
104
102
|
message = linebreaks(message, ENV['CUCUMBER_TRUNCATE_OUTPUT'].to_i)
|
105
103
|
|
106
|
-
indent("#{message}\n#{
|
104
|
+
indent("#{message}\n#{exception.backtrace.join("\n")}", indent_amount)
|
107
105
|
end
|
108
106
|
|
109
107
|
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/10655
|
110
108
|
def linebreaks(msg, max)
|
111
|
-
return msg unless max
|
109
|
+
return msg unless max.positive?
|
112
110
|
|
113
111
|
msg.gsub(/.{1,#{max}}(?:\s|\Z)/) do
|
114
112
|
(Regexp.last_match(0) + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n")
|
@@ -171,12 +169,17 @@ module Cucumber
|
|
171
169
|
end
|
172
170
|
end
|
173
171
|
|
174
|
-
def attach(src, media_type)
|
172
|
+
def attach(src, media_type, filename)
|
175
173
|
return unless media_type == 'text/x.cucumber.log+plain'
|
176
174
|
return unless @io
|
177
175
|
|
178
176
|
@io.puts
|
179
|
-
|
177
|
+
if filename
|
178
|
+
@io.puts("#{filename}: #{format_string(src, :tag)}")
|
179
|
+
else
|
180
|
+
@io.puts(format_string(src, :tag))
|
181
|
+
end
|
182
|
+
|
180
183
|
@io.flush
|
181
184
|
end
|
182
185
|
|
@@ -262,4 +265,3 @@ module Cucumber
|
|
262
265
|
end
|
263
266
|
end
|
264
267
|
end
|
265
|
-
# rubocop:enable Metrics/ModuleLength
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cucumber/formatter/console'
|
2
4
|
|
3
5
|
module Cucumber
|
@@ -30,7 +32,7 @@ module Cucumber
|
|
30
32
|
|
31
33
|
def status_counts(summary)
|
32
34
|
counts = Core::Test::Result::TYPES.map { |status| [status, summary.total(status)] }
|
33
|
-
counts = counts.select { |_status, count| count
|
35
|
+
counts = counts.select { |_status, count| count.positive? }
|
34
36
|
counts = counts.map { |status, count| format_string("#{count} #{status}", status) }
|
35
37
|
"(#{counts.join(', ')})" if counts.any?
|
36
38
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cucumber/formatter/console'
|
2
4
|
|
3
5
|
module Cucumber
|
@@ -12,9 +14,9 @@ module Cucumber
|
|
12
14
|
@config.on_event(:test_case_finished) do |event|
|
13
15
|
if event.test_case != @previous_test_case
|
14
16
|
@previous_test_case = event.test_case
|
15
|
-
@issues[event.result.to_sym] << event.test_case unless event.result.ok?(@config.strict)
|
17
|
+
@issues[event.result.to_sym] << event.test_case unless event.result.ok?(strict: @config.strict)
|
16
18
|
elsif event.result.passed?
|
17
|
-
@issues[:flaky] << event.test_case unless Core::Test::Result::Flaky.ok?(@config.strict.strict?(:flaky))
|
19
|
+
@issues[:flaky] << event.test_case unless Core::Test::Result::Flaky.ok?(strict: @config.strict.strict?(:flaky))
|
18
20
|
@issues[:failed].delete(event.test_case)
|
19
21
|
end
|
20
22
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'shellwords'
|
4
|
+
|
5
|
+
module Cucumber
|
6
|
+
module Formatter
|
7
|
+
class CurlOptionParser
|
8
|
+
def self.parse(options)
|
9
|
+
args = Shellwords.split(options)
|
10
|
+
|
11
|
+
url = nil
|
12
|
+
http_method = 'PUT'
|
13
|
+
headers = {}
|
14
|
+
|
15
|
+
until args.empty?
|
16
|
+
arg = args.shift
|
17
|
+
case arg
|
18
|
+
when '-X', '--request'
|
19
|
+
http_method = remove_arg_for(args, arg)
|
20
|
+
when '-H'
|
21
|
+
header_arg = remove_arg_for(args, arg)
|
22
|
+
headers = headers.merge(parse_header(header_arg))
|
23
|
+
else
|
24
|
+
raise StandardError, "#{options} was not a valid curl command. Can't set url to #{arg} it is already set to #{url}" if url
|
25
|
+
|
26
|
+
url = arg
|
27
|
+
end
|
28
|
+
end
|
29
|
+
raise StandardError, "#{options} was not a valid curl command" unless url
|
30
|
+
|
31
|
+
[url, http_method, headers]
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO: [LH] -> Switch below methods to private
|
35
|
+
def self.remove_arg_for(args, arg)
|
36
|
+
return args.shift unless args.empty?
|
37
|
+
|
38
|
+
raise StandardError, "Missing argument for #{arg}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.parse_header(header_arg)
|
42
|
+
parts = header_arg.split(':', 2)
|
43
|
+
raise StandardError, "#{header_arg} was not a valid header" unless parts.length == 2
|
44
|
+
|
45
|
+
{ parts[0].strip => parts[1].strip }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -12,7 +12,7 @@ module Cucumber
|
|
12
12
|
test_case, result = *event.attributes
|
13
13
|
if test_case != @previous_test_case
|
14
14
|
@previous_test_case = event.test_case
|
15
|
-
Cucumber.wants_to_quit = true unless result.ok?(configuration.strict)
|
15
|
+
Cucumber.wants_to_quit = true unless result.ok?(strict: configuration.strict)
|
16
16
|
elsif result.passed?
|
17
17
|
Cucumber.wants_to_quit = false
|
18
18
|
end
|
@@ -1,151 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'net/http'
|
2
4
|
require 'tempfile'
|
3
|
-
|
5
|
+
require_relative 'curl_option_parser'
|
6
|
+
require_relative 'io_http_buffer'
|
4
7
|
|
5
8
|
module Cucumber
|
6
9
|
module Formatter
|
7
10
|
class HTTPIO
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
uri, method, headers = CurlOptionParser.parse(url)
|
15
|
-
IOHTTPBuffer.new(uri, method, headers, https_verify_mode, reporter)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class CurlOptionParser
|
21
|
-
def self.parse(options)
|
22
|
-
args = Shellwords.split(options)
|
23
|
-
|
24
|
-
url = nil
|
25
|
-
http_method = 'PUT'
|
26
|
-
headers = {}
|
27
|
-
|
28
|
-
until args.empty?
|
29
|
-
arg = args.shift
|
30
|
-
case arg
|
31
|
-
when '-X', '--request'
|
32
|
-
http_method = remove_arg_for(args, arg)
|
33
|
-
when '-H'
|
34
|
-
header_arg = remove_arg_for(args, arg)
|
35
|
-
headers = headers.merge(parse_header(header_arg))
|
36
|
-
else
|
37
|
-
raise StandardError, "#{options} was not a valid curl command. Can't set url to #{arg} it is already set to #{url}" if url
|
38
|
-
|
39
|
-
url = arg
|
40
|
-
end
|
41
|
-
end
|
42
|
-
raise StandardError, "#{options} was not a valid curl command" unless url
|
43
|
-
|
44
|
-
[
|
45
|
-
url,
|
46
|
-
http_method,
|
47
|
-
headers
|
48
|
-
]
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.remove_arg_for(args, arg)
|
52
|
-
return args.shift unless args.empty?
|
53
|
-
|
54
|
-
raise StandardError, "Missing argument for #{arg}"
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.parse_header(header_arg)
|
58
|
-
parts = header_arg.split(':', 2)
|
59
|
-
raise StandardError, "#{header_arg} was not a valid header" unless parts.length == 2
|
60
|
-
|
61
|
-
{ parts[0].strip => parts[1].strip }
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class IOHTTPBuffer
|
66
|
-
attr_reader :uri, :method, :headers
|
67
|
-
|
68
|
-
def initialize(uri, method, headers = {}, https_verify_mode = nil, reporter = nil)
|
69
|
-
@uri = URI(uri)
|
70
|
-
@method = method
|
71
|
-
@headers = headers
|
72
|
-
@write_io = Tempfile.new('cucumber', encoding: 'UTF-8')
|
73
|
-
@https_verify_mode = https_verify_mode
|
74
|
-
@reporter = reporter || NoReporter.new
|
75
|
-
end
|
76
|
-
|
77
|
-
def close
|
78
|
-
response = send_content(@uri, @method, @headers)
|
79
|
-
@reporter.report(response.body)
|
80
|
-
@write_io.close
|
81
|
-
return if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPRedirection)
|
82
|
-
|
83
|
-
raise StandardError, "request to #{uri} failed with status #{response.code}"
|
84
|
-
end
|
85
|
-
|
86
|
-
def write(data)
|
87
|
-
@write_io.write(data)
|
88
|
-
end
|
89
|
-
|
90
|
-
def flush
|
91
|
-
@write_io.flush
|
92
|
-
end
|
93
|
-
|
94
|
-
def closed?
|
95
|
-
@write_io.closed?
|
96
|
-
end
|
97
|
-
|
98
|
-
private
|
99
|
-
|
100
|
-
def send_content(uri, method, headers, attempt = 10)
|
101
|
-
content = (method == 'GET' ? StringIO.new : @write_io)
|
102
|
-
http = build_client(uri, @https_verify_mode)
|
103
|
-
|
104
|
-
raise StandardError, "request to #{uri} failed (too many redirections)" if attempt <= 0
|
105
|
-
|
106
|
-
req = build_request(
|
107
|
-
uri,
|
108
|
-
method,
|
109
|
-
headers.merge(
|
110
|
-
'Content-Length' => content.size.to_s
|
111
|
-
)
|
112
|
-
)
|
113
|
-
|
114
|
-
content.rewind
|
115
|
-
req.body_stream = content
|
116
|
-
|
117
|
-
begin
|
118
|
-
response = http.request(req)
|
119
|
-
rescue SystemCallError
|
120
|
-
# We may get the redirect response before pushing the file.
|
121
|
-
response = http.request(build_request(uri, method, headers))
|
122
|
-
end
|
123
|
-
|
124
|
-
case response
|
125
|
-
when Net::HTTPAccepted
|
126
|
-
send_content(URI(response['Location']), 'PUT', {}, attempt - 1) if response['Location']
|
127
|
-
when Net::HTTPRedirection
|
128
|
-
send_content(URI(response['Location']), method, headers, attempt - 1)
|
129
|
-
end
|
130
|
-
response
|
131
|
-
end
|
132
|
-
|
133
|
-
def build_request(uri, method, headers)
|
134
|
-
method_class_name = "#{method[0].upcase}#{method[1..].downcase}"
|
135
|
-
req = Net::HTTP.const_get(method_class_name).new(uri)
|
136
|
-
headers.each do |header, value|
|
137
|
-
req[header] = value
|
138
|
-
end
|
139
|
-
req
|
140
|
-
end
|
141
|
-
|
142
|
-
def build_client(uri, https_verify_mode)
|
143
|
-
http = Net::HTTP.new(uri.hostname, uri.port)
|
144
|
-
if uri.scheme == 'https'
|
145
|
-
http.use_ssl = true
|
146
|
-
http.verify_mode = https_verify_mode if https_verify_mode
|
147
|
-
end
|
148
|
-
http
|
11
|
+
# Returns an IO that will write to a HTTP request's body
|
12
|
+
# https_verify_mode can be set to OpenSSL::SSL::VERIFY_NONE
|
13
|
+
# to ignore unsigned certificate - setting to nil will verify the certificate
|
14
|
+
def self.open(url, https_verify_mode = nil, reporter = nil)
|
15
|
+
uri, method, headers = CurlOptionParser.parse(url)
|
16
|
+
IOHTTPBuffer.new(uri, method, headers, https_verify_mode, reporter)
|
149
17
|
end
|
150
18
|
end
|
151
19
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
module Formatter
|
5
|
+
class IOHTTPBuffer
|
6
|
+
attr_reader :uri, :method, :headers
|
7
|
+
|
8
|
+
def initialize(uri, method, headers = {}, https_verify_mode = nil, reporter = nil)
|
9
|
+
@uri = URI(uri)
|
10
|
+
@method = method
|
11
|
+
@headers = headers
|
12
|
+
@write_io = Tempfile.new('cucumber', encoding: 'UTF-8')
|
13
|
+
@https_verify_mode = https_verify_mode
|
14
|
+
@reporter = reporter || NoReporter.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def close
|
18
|
+
@reporter.report(response.body)
|
19
|
+
@write_io.close
|
20
|
+
return if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPRedirection)
|
21
|
+
|
22
|
+
raise StandardError, "request to #{uri} failed with status #{response.code}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def write(data)
|
26
|
+
@write_io.write(data)
|
27
|
+
end
|
28
|
+
|
29
|
+
def flush
|
30
|
+
@write_io.flush
|
31
|
+
end
|
32
|
+
|
33
|
+
def closed?
|
34
|
+
@write_io.closed?
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def response
|
40
|
+
@response ||= send_content(uri, method, headers)
|
41
|
+
end
|
42
|
+
|
43
|
+
def send_content(uri, method, headers, attempts_remaining = 10)
|
44
|
+
content = (method == 'GET' ? StringIO.new : @write_io)
|
45
|
+
http = build_client(uri)
|
46
|
+
|
47
|
+
raise StandardError, "request to #{uri} failed (too many redirections)" if attempts_remaining <= 0
|
48
|
+
|
49
|
+
request = build_request(uri, method, headers.merge('Content-Length' => content.size.to_s))
|
50
|
+
content.rewind
|
51
|
+
request.body_stream = content
|
52
|
+
|
53
|
+
begin
|
54
|
+
response = http.request(request)
|
55
|
+
rescue SystemCallError
|
56
|
+
# We may get the redirect response before pushing the file.
|
57
|
+
response = http.request(build_request(uri, method, headers))
|
58
|
+
end
|
59
|
+
|
60
|
+
case response
|
61
|
+
when Net::HTTPAccepted
|
62
|
+
send_content(URI(response['Location']), 'PUT', {}, attempts_remaining - 1) if response['Location']
|
63
|
+
when Net::HTTPRedirection
|
64
|
+
send_content(URI(response['Location']), method, headers, attempts_remaining - 1)
|
65
|
+
end
|
66
|
+
response
|
67
|
+
end
|
68
|
+
|
69
|
+
def build_request(uri, method, headers)
|
70
|
+
method_class_name = "#{method[0].upcase}#{method[1..].downcase}"
|
71
|
+
Net::HTTP.const_get(method_class_name).new(uri).tap do |request|
|
72
|
+
headers.each do |header, value|
|
73
|
+
request[header] = value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def build_client(uri)
|
79
|
+
Net::HTTP.new(uri.hostname, uri.port).tap do |http|
|
80
|
+
if uri.scheme == 'https'
|
81
|
+
http.use_ssl = true
|
82
|
+
http.verify_mode = @https_verify_mode if @https_verify_mode
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -85,7 +85,7 @@ module Cucumber
|
|
85
85
|
@io.write(JSON.pretty_generate(@feature_hashes))
|
86
86
|
end
|
87
87
|
|
88
|
-
def attach(src, mime_type)
|
88
|
+
def attach(src, mime_type, _filename)
|
89
89
|
if mime_type == 'text/x.cucumber.log+plain'
|
90
90
|
test_step_output << src
|
91
91
|
return
|
@@ -102,7 +102,7 @@ module Cucumber
|
|
102
102
|
private
|
103
103
|
|
104
104
|
def same_feature_as_previous_test_case?(test_case)
|
105
|
-
|
105
|
+
@feature_hash&.fetch(:uri, nil) == test_case.location.file
|
106
106
|
end
|
107
107
|
|
108
108
|
def first_step_after_background?(test_step)
|
@@ -113,10 +113,6 @@ module Cucumber
|
|
113
113
|
test_step.location.file.include?('lib/cucumber/')
|
114
114
|
end
|
115
115
|
|
116
|
-
def current_feature
|
117
|
-
@feature_hash ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
|
118
|
-
end
|
119
|
-
|
120
116
|
def feature_elements
|
121
117
|
@feature_hash[:elements] ||= []
|
122
118
|
end
|
@@ -54,7 +54,7 @@ module Cucumber
|
|
54
54
|
test_step, result = *event.attributes
|
55
55
|
return if @failing_test_step
|
56
56
|
|
57
|
-
@failing_test_step = test_step unless result.ok?(@config.strict)
|
57
|
+
@failing_test_step = test_step unless result.ok?(strict: @config.strict)
|
58
58
|
end
|
59
59
|
|
60
60
|
def on_test_case_finished(event)
|
@@ -71,7 +71,7 @@ module Cucumber
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def on_test_run_finished(_event)
|
74
|
-
@features_data.
|
74
|
+
@features_data.each_value { |data| end_feature(data) }
|
75
75
|
end
|
76
76
|
|
77
77
|
private
|
@@ -111,7 +111,7 @@ module Cucumber
|
|
111
111
|
scenario_source = @ast_lookup.scenario_source(test_case)
|
112
112
|
keyword = scenario_source.type == :Scenario ? scenario_source.scenario.keyword : scenario_source.scenario_outline.keyword
|
113
113
|
output = "#{keyword}: #{scenario}\n\n"
|
114
|
-
return output if result.ok?(@config.strict)
|
114
|
+
return output if result.ok?(strict: @config.strict)
|
115
115
|
|
116
116
|
if scenario_source.type == :Scenario
|
117
117
|
if @failing_test_step
|
@@ -140,7 +140,7 @@ module Cucumber
|
|
140
140
|
testcase_attributes = get_testcase_attributes(classname, name, duration, filename)
|
141
141
|
|
142
142
|
@current_feature_data[:builder].testcase(testcase_attributes) do
|
143
|
-
if !result.passed? && result.ok?(@config.strict)
|
143
|
+
if !result.passed? && result.ok?(strict: @config.strict)
|
144
144
|
@current_feature_data[:builder].skipped
|
145
145
|
@current_feature_data[:skipped] += 1
|
146
146
|
elsif !result.passed?
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'base64'
|
2
4
|
require 'cucumber/formatter/backtrace_filter'
|
3
5
|
require 'cucumber/formatter/query/hook_by_test_step'
|
@@ -40,11 +42,12 @@ module Cucumber
|
|
40
42
|
raise 'To be implemented'
|
41
43
|
end
|
42
44
|
|
43
|
-
def attach(src, media_type)
|
45
|
+
def attach(src, media_type, filename)
|
44
46
|
attachment_data = {
|
45
47
|
test_step_id: @current_test_step_id,
|
46
48
|
test_case_started_id: @current_test_case_started_id,
|
47
|
-
media_type: media_type
|
49
|
+
media_type: media_type,
|
50
|
+
file_name: filename
|
48
51
|
}
|
49
52
|
|
50
53
|
if media_type.start_with?('text/')
|
@@ -129,7 +132,7 @@ module Cucumber
|
|
129
132
|
@step_definitions_by_test_step.step_match_arguments(step).map do |argument|
|
130
133
|
Cucumber::Messages::StepMatchArgument.new(
|
131
134
|
group: argument_group_to_message(argument.group),
|
132
|
-
parameter_type_name: argument
|
135
|
+
parameter_type_name: parameter_type_name(argument)
|
133
136
|
)
|
134
137
|
end
|
135
138
|
end
|
@@ -142,6 +145,10 @@ module Cucumber
|
|
142
145
|
)
|
143
146
|
end
|
144
147
|
|
148
|
+
def parameter_type_name(step_match_argument)
|
149
|
+
step_match_argument.parameter_type&.name if step_match_argument.respond_to?(:parameter_type)
|
150
|
+
end
|
151
|
+
|
145
152
|
def on_test_run_started(*)
|
146
153
|
message = Cucumber::Messages::Envelope.new(
|
147
154
|
test_run_started: Cucumber::Messages::TestRunStarted.new(
|
@@ -190,10 +197,13 @@ module Cucumber
|
|
190
197
|
|
191
198
|
result_message = result.to_message
|
192
199
|
if result.failed? || result.pending?
|
200
|
+
message_element = result.failed? ? result.exception : result
|
201
|
+
|
193
202
|
result_message = Cucumber::Messages::TestStepResult.new(
|
194
203
|
status: result_message.status,
|
195
204
|
duration: result_message.duration,
|
196
|
-
message: create_error_message(
|
205
|
+
message: create_error_message(message_element),
|
206
|
+
exception: create_exception_object(result, message_element)
|
197
207
|
)
|
198
208
|
end
|
199
209
|
|
@@ -209,12 +219,17 @@ module Cucumber
|
|
209
219
|
output_envelope(message)
|
210
220
|
end
|
211
221
|
|
212
|
-
def create_error_message(
|
213
|
-
message_element = result.failed? ? result.exception : result
|
222
|
+
def create_error_message(message_element)
|
214
223
|
message = "#{message_element.message} (#{message_element.class})"
|
215
224
|
([message] + message_element.backtrace).join("\n")
|
216
225
|
end
|
217
226
|
|
227
|
+
def create_exception_object(result, message_element)
|
228
|
+
return unless result.failed?
|
229
|
+
|
230
|
+
Cucumber::Messages::Exception.from_h({ type: message_element.class, message: message_element.message })
|
231
|
+
end
|
232
|
+
|
218
233
|
def on_test_case_finished(event)
|
219
234
|
message = Cucumber::Messages::Envelope.new(
|
220
235
|
test_case_finished: Cucumber::Messages::TestCaseFinished.new(
|