cucumber 8.0.0 → 9.2.1
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/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(
|