cucumber 3.1.2 → 8.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 +5 -5
- data/CHANGELOG.md +1880 -1146
- data/CONTRIBUTING.md +220 -61
- data/README.md +143 -22
- data/bin/cucumber +1 -1
- data/lib/autotest/cucumber_mixin.rb +49 -53
- data/lib/autotest/discover.rb +3 -2
- data/lib/cucumber/cli/configuration.rb +32 -7
- data/lib/cucumber/cli/main.rb +16 -15
- data/lib/cucumber/cli/options.rb +111 -79
- data/lib/cucumber/cli/profile_loader.rb +45 -26
- data/lib/cucumber/cli/rerun_file.rb +1 -1
- data/lib/cucumber/configuration.rb +47 -31
- data/lib/cucumber/constantize.rb +3 -6
- data/lib/cucumber/deprecate.rb +32 -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 +12 -0
- data/lib/cucumber/events/step_activated.rb +0 -5
- data/lib/cucumber/events/step_definition_registered.rb +0 -5
- data/lib/cucumber/events/test_case_created.rb +12 -0
- data/lib/cucumber/events/test_case_ready.rb +12 -0
- data/lib/cucumber/events/test_run_finished.rb +2 -1
- data/lib/cucumber/events/test_step_created.rb +12 -0
- data/lib/cucumber/events/undefined_parameter_type.rb +9 -0
- data/lib/cucumber/events.rb +15 -8
- data/lib/cucumber/file_specs.rb +8 -7
- data/lib/cucumber/filters/activate_steps.rb +6 -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 +3 -7
- data/lib/cucumber/filters/tag_limits.rb +1 -3
- data/lib/cucumber/filters.rb +1 -0
- data/lib/cucumber/formatter/ansicolor.rb +74 -86
- data/lib/cucumber/formatter/ast_lookup.rb +163 -0
- data/lib/cucumber/formatter/backtrace_filter.rb +10 -7
- data/lib/cucumber/formatter/console.rb +76 -68
- data/lib/cucumber/formatter/console_counts.rb +4 -9
- data/lib/cucumber/formatter/console_issues.rb +12 -4
- data/lib/cucumber/formatter/duration.rb +1 -1
- data/lib/cucumber/formatter/duration_extractor.rb +4 -1
- data/lib/cucumber/formatter/errors.rb +7 -0
- data/lib/cucumber/formatter/fanout.rb +3 -1
- data/lib/cucumber/formatter/html.rb +11 -598
- data/lib/cucumber/formatter/http_io.rb +152 -0
- data/lib/cucumber/formatter/ignore_missing_messages.rb +2 -2
- data/lib/cucumber/formatter/interceptor.rb +11 -30
- data/lib/cucumber/formatter/io.rb +57 -13
- data/lib/cucumber/formatter/json.rb +119 -124
- data/lib/cucumber/formatter/junit.rb +75 -55
- data/lib/cucumber/formatter/message.rb +23 -0
- data/lib/cucumber/formatter/message_builder.rb +256 -0
- data/lib/cucumber/formatter/pretty.rb +370 -153
- data/lib/cucumber/formatter/progress.rb +31 -32
- data/lib/cucumber/formatter/publish_banner_printer.rb +77 -0
- data/lib/cucumber/formatter/query/hook_by_test_step.rb +32 -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 +42 -0
- data/lib/cucumber/formatter/rerun.rb +24 -4
- data/lib/cucumber/formatter/stepdefs.rb +1 -2
- data/lib/cucumber/formatter/steps.rb +8 -6
- data/lib/cucumber/formatter/summary.rb +17 -8
- data/lib/cucumber/formatter/unicode.rb +18 -20
- data/lib/cucumber/formatter/url_reporter.rb +17 -0
- data/lib/cucumber/formatter/usage.rb +18 -15
- data/lib/cucumber/gherkin/data_table_parser.rb +18 -6
- data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +14 -18
- data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
- data/lib/cucumber/gherkin/steps_parser.rb +17 -8
- data/lib/cucumber/glue/dsl.rb +29 -15
- data/lib/cucumber/glue/hook.rb +37 -11
- data/lib/cucumber/glue/invoke_in_world.rb +17 -22
- data/lib/cucumber/glue/proto_world.rb +47 -53
- data/lib/cucumber/glue/registry_and_more.rb +62 -17
- data/lib/cucumber/glue/registry_wrapper.rb +31 -0
- data/lib/cucumber/glue/snippet.rb +23 -22
- data/lib/cucumber/glue/step_definition.rb +48 -23
- data/lib/cucumber/glue/world_factory.rb +1 -1
- data/lib/cucumber/hooks.rb +12 -11
- data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +4 -3
- data/lib/cucumber/multiline_argument/data_table.rb +143 -123
- data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
- data/lib/cucumber/multiline_argument.rb +4 -6
- data/lib/cucumber/platform.rb +5 -5
- data/lib/cucumber/rake/task.rb +34 -25
- data/lib/cucumber/rspec/disable_option_parser.rb +15 -11
- data/lib/cucumber/rspec/doubles.rb +3 -5
- data/lib/cucumber/running_test_case.rb +3 -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/meta_message_builder.rb +106 -0
- data/lib/cucumber/runtime/step_hooks.rb +6 -2
- data/lib/cucumber/runtime/support_code.rb +16 -15
- data/lib/cucumber/runtime/user_interface.rb +10 -19
- data/lib/cucumber/runtime.rb +78 -76
- data/lib/cucumber/step_definition_light.rb +4 -3
- data/lib/cucumber/step_definitions.rb +2 -2
- data/lib/cucumber/step_match.rb +17 -20
- data/lib/cucumber/step_match_search.rb +5 -3
- data/lib/cucumber/term/ansicolor.rb +72 -48
- data/lib/cucumber/term/banner.rb +57 -0
- data/lib/cucumber/version +1 -1
- data/lib/cucumber.rb +3 -2
- data/lib/simplecov_setup.rb +1 -1
- metadata +279 -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
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'shellwords'
|
4
|
+
|
5
|
+
module Cucumber
|
6
|
+
module Formatter
|
7
|
+
class HTTPIO
|
8
|
+
class << self
|
9
|
+
# Returns an IO that will write to a HTTP request's body
|
10
|
+
# https_verify_mode can be set to OpenSSL::SSL::VERIFY_NONE
|
11
|
+
# to ignore unsigned certificate - setting to nil will verify the certificate
|
12
|
+
def open(url, https_verify_mode = nil, reporter = nil)
|
13
|
+
@https_verify_mode = https_verify_mode
|
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
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -8,11 +8,11 @@ module Cucumber
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def method_missing(message, *args)
|
11
|
-
@receiver.
|
11
|
+
@receiver.respond_to?(message) ? @receiver.send(message, *args) : super
|
12
12
|
end
|
13
13
|
|
14
14
|
def respond_to_missing?(name, include_private = false)
|
15
|
-
@receiver.respond_to?(name, include_private)
|
15
|
+
@receiver.respond_to?(name, include_private) || super(name, include_private)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -5,34 +5,23 @@ module Cucumber
|
|
5
5
|
module Interceptor
|
6
6
|
class Pipe
|
7
7
|
attr_reader :pipe
|
8
|
+
|
8
9
|
def initialize(pipe)
|
9
10
|
@pipe = pipe
|
10
11
|
@buffer = StringIO.new
|
11
12
|
@wrapped = true
|
13
|
+
@lock = Mutex.new
|
12
14
|
end
|
13
15
|
|
14
16
|
def write(str)
|
15
|
-
lock.synchronize do
|
17
|
+
@lock.synchronize do
|
16
18
|
@buffer << str if @wrapped
|
17
19
|
return @pipe.write(str)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
|
-
# @deprecated use #buffer_string
|
22
|
-
def buffer
|
23
|
-
require 'cucumber/deprecate.rb'
|
24
|
-
Cucumber.deprecate(
|
25
|
-
'Use Cucumber::Formatter::Interceptor::Pipe#buffer_string instead',
|
26
|
-
'Cucumber::Formatter::Interceptor::Pipe#buffer',
|
27
|
-
'3.99'
|
28
|
-
)
|
29
|
-
lock.synchronize do
|
30
|
-
return @buffer.string.lines
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
23
|
def buffer_string
|
35
|
-
lock.synchronize do
|
24
|
+
@lock.synchronize do
|
36
25
|
return @buffer.string.dup
|
37
26
|
end
|
38
27
|
end
|
@@ -43,17 +32,15 @@ module Cucumber
|
|
43
32
|
end
|
44
33
|
|
45
34
|
def method_missing(method, *args, &blk)
|
46
|
-
@pipe.send(method, *args, &blk)
|
35
|
+
@pipe.respond_to?(method) ? @pipe.send(method, *args, &blk) : super
|
47
36
|
end
|
48
37
|
|
49
|
-
def
|
38
|
+
def respond_to_missing?(method, include_private = false)
|
50
39
|
super || @pipe.respond_to?(method, include_private)
|
51
40
|
end
|
52
41
|
|
53
42
|
def self.validate_pipe(pipe)
|
54
|
-
unless [
|
55
|
-
raise ArgumentError, '#wrap only accepts :stderr or :stdout'
|
56
|
-
end
|
43
|
+
raise ArgumentError, '#wrap only accepts :stderr or :stdout' unless %i[stdout stderr].include? pipe
|
57
44
|
end
|
58
45
|
|
59
46
|
def self.unwrap!(pipe)
|
@@ -75,19 +62,13 @@ module Cucumber
|
|
75
62
|
|
76
63
|
case pipe
|
77
64
|
when :stderr
|
78
|
-
$stderr =
|
79
|
-
|
65
|
+
$stderr = new($stderr)
|
66
|
+
$stderr
|
80
67
|
when :stdout
|
81
|
-
$stdout =
|
82
|
-
|
68
|
+
$stdout = new($stdout)
|
69
|
+
$stdout
|
83
70
|
end
|
84
71
|
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
def lock
|
89
|
-
@lock ||= Mutex.new
|
90
|
-
end
|
91
72
|
end
|
92
73
|
end
|
93
74
|
end
|
@@ -1,33 +1,77 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'cucumber/formatter/http_io'
|
4
|
+
require 'cucumber/formatter/url_reporter'
|
5
|
+
require 'cucumber/cli/options'
|
6
|
+
|
3
7
|
module Cucumber
|
4
8
|
module Formatter
|
5
9
|
module Io
|
6
10
|
module_function
|
7
11
|
|
8
|
-
def ensure_io(
|
9
|
-
return nil if
|
10
|
-
return
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
def ensure_io(path_or_url_or_io, error_stream)
|
13
|
+
return nil if path_or_url_or_io.nil?
|
14
|
+
return path_or_url_or_io if io?(path_or_url_or_io)
|
15
|
+
|
16
|
+
io = if url?(path_or_url_or_io)
|
17
|
+
url = path_or_url_or_io
|
18
|
+
reporter = url.start_with?(Cucumber::Cli::Options::CUCUMBER_PUBLISH_URL) ? URLReporter.new(error_stream) : NoReporter.new
|
19
|
+
HTTPIO.open(url, nil, reporter)
|
20
|
+
else
|
21
|
+
File.open(path_or_url_or_io, Cucumber.file_mode('w'))
|
22
|
+
end
|
23
|
+
@io_objects_to_close ||= []
|
24
|
+
@io_objects_to_close.push(io)
|
25
|
+
io
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
def new(*args, &block)
|
30
|
+
instance = super
|
31
|
+
|
32
|
+
config = args[0]
|
33
|
+
if config.respond_to? :on_event
|
34
|
+
config.on_event :test_run_finished do
|
35
|
+
ios = instance.instance_variable_get(:@io_objects_to_close) || []
|
36
|
+
ios.each do |io|
|
37
|
+
at_exit do
|
38
|
+
unless io.closed?
|
39
|
+
io.flush
|
40
|
+
io.close
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
16
45
|
end
|
46
|
+
|
47
|
+
instance
|
17
48
|
end
|
18
|
-
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.included(formatter_class)
|
52
|
+
formatter_class.extend(ClassMethods)
|
53
|
+
end
|
54
|
+
|
55
|
+
def io?(path_or_url_or_io)
|
56
|
+
path_or_url_or_io.respond_to?(:write)
|
57
|
+
end
|
58
|
+
|
59
|
+
def url?(path_or_url_or_io)
|
60
|
+
path_or_url_or_io.match(/^https?:\/\//)
|
19
61
|
end
|
20
62
|
|
21
63
|
def ensure_file(path, name)
|
22
|
-
raise "You *must* specify --out FILE for the #{name} formatter" unless String
|
64
|
+
raise "You *must* specify --out FILE for the #{name} formatter" unless path.instance_of? String
|
23
65
|
raise "I can't write #{name} to a directory - it has to be a file" if File.directory?(path)
|
24
|
-
raise "I can't write #{name} to a file in the non-existing directory #{File.dirname(path)}"
|
25
|
-
|
66
|
+
raise "I can't write #{name} to a file in the non-existing directory #{File.dirname(path)}" unless File.directory?(File.dirname(path))
|
67
|
+
|
68
|
+
ensure_io(path, nil)
|
26
69
|
end
|
27
70
|
|
28
71
|
def ensure_dir(path, name)
|
29
|
-
raise "You *must* specify --out DIR for the #{name} formatter" unless String
|
72
|
+
raise "You *must* specify --out DIR for the #{name} formatter" unless path.instance_of? String
|
30
73
|
raise "I can't write #{name} reports to a file - it has to be a directory" if File.file?(path)
|
74
|
+
|
31
75
|
FileUtils.mkdir_p(path) unless File.directory?(path)
|
32
76
|
File.absolute_path path
|
33
77
|
end
|