cucumber 3.1.2 → 4.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 +173 -14
- data/CONTRIBUTING.md +2 -18
- data/README.md +4 -5
- data/bin/cucumber +1 -1
- data/lib/autotest/cucumber_mixin.rb +34 -39
- data/lib/cucumber.rb +1 -1
- data/lib/cucumber/cli/configuration.rb +5 -5
- data/lib/cucumber/cli/main.rb +12 -12
- data/lib/cucumber/cli/options.rb +69 -74
- data/lib/cucumber/cli/profile_loader.rb +49 -26
- data/lib/cucumber/configuration.rb +31 -23
- data/lib/cucumber/constantize.rb +2 -5
- data/lib/cucumber/deprecate.rb +31 -7
- data/lib/cucumber/errors.rb +5 -7
- data/lib/cucumber/events.rb +13 -6
- 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 +13 -0
- data/lib/cucumber/events/step_activated.rb +2 -1
- data/lib/cucumber/events/test_case_created.rb +13 -0
- data/lib/cucumber/events/test_case_ready.rb +12 -0
- data/lib/cucumber/events/test_step_created.rb +13 -0
- data/lib/cucumber/events/undefined_parameter_type.rb +10 -0
- data/lib/cucumber/file_specs.rb +6 -6
- data/lib/cucumber/filters.rb +1 -0
- data/lib/cucumber/filters/activate_steps.rb +5 -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 +2 -4
- data/lib/cucumber/formatter/ansicolor.rb +40 -45
- data/lib/cucumber/formatter/ast_lookup.rb +165 -0
- data/lib/cucumber/formatter/backtrace_filter.rb +9 -8
- data/lib/cucumber/formatter/console.rb +58 -66
- data/lib/cucumber/formatter/console_counts.rb +4 -9
- data/lib/cucumber/formatter/console_issues.rb +6 -3
- data/lib/cucumber/formatter/duration.rb +1 -1
- data/lib/cucumber/formatter/duration_extractor.rb +3 -1
- data/lib/cucumber/formatter/errors.rb +6 -0
- data/lib/cucumber/formatter/fanout.rb +2 -0
- data/lib/cucumber/formatter/html.rb +11 -598
- data/lib/cucumber/formatter/http_io.rb +146 -0
- data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
- data/lib/cucumber/formatter/interceptor.rb +8 -28
- data/lib/cucumber/formatter/io.rb +17 -11
- data/lib/cucumber/formatter/json.rb +101 -109
- data/lib/cucumber/formatter/junit.rb +56 -56
- data/lib/cucumber/formatter/message.rb +22 -0
- data/lib/cucumber/formatter/message_builder.rb +255 -0
- data/lib/cucumber/formatter/pretty.rb +359 -153
- data/lib/cucumber/formatter/progress.rb +30 -32
- data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -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 +40 -0
- data/lib/cucumber/formatter/rerun.rb +22 -4
- data/lib/cucumber/formatter/stepdefs.rb +1 -2
- data/lib/cucumber/formatter/steps.rb +2 -3
- data/lib/cucumber/formatter/summary.rb +16 -8
- data/lib/cucumber/formatter/unicode.rb +15 -17
- data/lib/cucumber/formatter/usage.rb +11 -10
- data/lib/cucumber/gherkin/data_table_parser.rb +17 -6
- data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
- data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
- data/lib/cucumber/gherkin/steps_parser.rb +17 -8
- data/lib/cucumber/glue/dsl.rb +1 -1
- data/lib/cucumber/glue/hook.rb +34 -11
- data/lib/cucumber/glue/invoke_in_world.rb +13 -18
- data/lib/cucumber/glue/proto_world.rb +42 -33
- data/lib/cucumber/glue/registry_and_more.rb +42 -12
- data/lib/cucumber/glue/snippet.rb +23 -22
- data/lib/cucumber/glue/step_definition.rb +42 -19
- data/lib/cucumber/glue/world_factory.rb +1 -1
- data/lib/cucumber/hooks.rb +11 -11
- data/lib/cucumber/multiline_argument.rb +4 -6
- data/lib/cucumber/multiline_argument/data_table.rb +97 -64
- data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +1 -1
- data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
- data/lib/cucumber/platform.rb +3 -3
- data/lib/cucumber/rake/task.rb +16 -16
- data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
- data/lib/cucumber/running_test_case.rb +2 -53
- data/lib/cucumber/runtime.rb +54 -58
- 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/step_hooks.rb +3 -2
- data/lib/cucumber/runtime/support_code.rb +13 -15
- data/lib/cucumber/runtime/user_interface.rb +6 -16
- data/lib/cucumber/step_definition_light.rb +4 -3
- data/lib/cucumber/step_definitions.rb +2 -2
- data/lib/cucumber/step_match.rb +12 -11
- data/lib/cucumber/step_match_search.rb +2 -1
- data/lib/cucumber/term/ansicolor.rb +9 -9
- data/lib/cucumber/version +1 -1
- metadata +224 -82
- 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,146 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
module Cucumber
|
5
|
+
module Formatter
|
6
|
+
class HTTPIO
|
7
|
+
class << self
|
8
|
+
# Returns an IO that will write to a HTTP request's body
|
9
|
+
def open(url, https_verify_mode = nil)
|
10
|
+
@https_verify_mode = https_verify_mode
|
11
|
+
uri, method, headers = CurlOptionParser.parse(url)
|
12
|
+
IOHTTPBuffer.new(uri, method, headers, https_verify_mode)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class CurlOptionParser
|
18
|
+
def self.parse(options)
|
19
|
+
chunks = options.split(/\s/).compact
|
20
|
+
http_method = 'PUT'
|
21
|
+
url = chunks[0]
|
22
|
+
headers = ''
|
23
|
+
|
24
|
+
last_flag = nil
|
25
|
+
chunks.each do |chunk|
|
26
|
+
if ['-X', '--request'].include?(chunk)
|
27
|
+
last_flag = '-X'
|
28
|
+
next
|
29
|
+
end
|
30
|
+
|
31
|
+
if chunk == '-H'
|
32
|
+
last_flag = '-H'
|
33
|
+
next
|
34
|
+
end
|
35
|
+
|
36
|
+
if last_flag == '-X'
|
37
|
+
http_method = chunk
|
38
|
+
last_flag = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
headers += chunk if last_flag == '-H'
|
42
|
+
end
|
43
|
+
|
44
|
+
[
|
45
|
+
url,
|
46
|
+
http_method,
|
47
|
+
make_headers(headers)
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.make_headers(headers)
|
52
|
+
hash_headers = {}
|
53
|
+
str_scanner = /("(?<key>[^":]+)\s*:\s*(?<value>[^":]+)")|('(?<key1>[^':]+)\s*:\s*(?<value1>[^':]+)')/
|
54
|
+
|
55
|
+
headers.scan(str_scanner) do |header|
|
56
|
+
header = header.compact!
|
57
|
+
hash_headers[header[0]] = header[1]&.strip
|
58
|
+
end
|
59
|
+
|
60
|
+
hash_headers
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class IOHTTPBuffer
|
65
|
+
attr_reader :uri, :method, :headers
|
66
|
+
|
67
|
+
def initialize(uri, method, headers = {}, https_verify_mode = nil)
|
68
|
+
@uri = URI(uri)
|
69
|
+
@method = method
|
70
|
+
@headers = headers
|
71
|
+
@write_io = Tempfile.new('cucumber', encoding: 'UTF-8')
|
72
|
+
@https_verify_mode = https_verify_mode
|
73
|
+
end
|
74
|
+
|
75
|
+
def close
|
76
|
+
post_content(@uri, @method, @headers)
|
77
|
+
@write_io.close
|
78
|
+
end
|
79
|
+
|
80
|
+
def write(data)
|
81
|
+
@write_io.write(data)
|
82
|
+
end
|
83
|
+
|
84
|
+
def flush
|
85
|
+
@write_io.flush
|
86
|
+
end
|
87
|
+
|
88
|
+
def closed?
|
89
|
+
@write_io.closed?
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def post_content(uri, method, headers, attempt = 10)
|
95
|
+
content = @write_io
|
96
|
+
http = build_client(uri, @https_verify_mode)
|
97
|
+
|
98
|
+
raise StandardError, "request to #{uri} failed (too many redirections)" if attempt <= 0
|
99
|
+
req = build_request(
|
100
|
+
uri,
|
101
|
+
method,
|
102
|
+
headers.merge(
|
103
|
+
'Content-Length' => content.size.to_s
|
104
|
+
)
|
105
|
+
)
|
106
|
+
|
107
|
+
content.rewind
|
108
|
+
req.body_stream = content
|
109
|
+
|
110
|
+
begin
|
111
|
+
response = http.request(req)
|
112
|
+
rescue SystemCallError
|
113
|
+
# We may get the redirect response before pushing the file.
|
114
|
+
response = http.request(build_request(uri, method, headers))
|
115
|
+
end
|
116
|
+
|
117
|
+
case response
|
118
|
+
when Net::HTTPSuccess
|
119
|
+
response
|
120
|
+
when Net::HTTPRedirection
|
121
|
+
post_content(URI(response['Location']), method, headers, attempt - 1)
|
122
|
+
else
|
123
|
+
raise StandardError, "request to #{uri} failed with status #{response.code}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def build_request(uri, method, headers)
|
128
|
+
method_class_name = "#{method[0].upcase}#{method[1..-1].downcase}"
|
129
|
+
req = Net::HTTP.const_get(method_class_name).new(uri)
|
130
|
+
headers.each do |header, value|
|
131
|
+
req[header] = value
|
132
|
+
end
|
133
|
+
req
|
134
|
+
end
|
135
|
+
|
136
|
+
def build_client(uri, https_verify_mode)
|
137
|
+
http = Net::HTTP.new(uri.hostname, uri.port)
|
138
|
+
if uri.scheme == 'https'
|
139
|
+
http.use_ssl = true
|
140
|
+
http.verify_mode = https_verify_mode if https_verify_mode
|
141
|
+
end
|
142
|
+
http
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -8,7 +8,7 @@ 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)
|
@@ -9,30 +9,18 @@ module Cucumber
|
|
9
9
|
@pipe = pipe
|
10
10
|
@buffer = StringIO.new
|
11
11
|
@wrapped = true
|
12
|
+
@lock = Mutex.new
|
12
13
|
end
|
13
14
|
|
14
15
|
def write(str)
|
15
|
-
lock.synchronize do
|
16
|
+
@lock.synchronize do
|
16
17
|
@buffer << str if @wrapped
|
17
18
|
return @pipe.write(str)
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
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
22
|
def buffer_string
|
35
|
-
lock.synchronize do
|
23
|
+
@lock.synchronize do
|
36
24
|
return @buffer.string.dup
|
37
25
|
end
|
38
26
|
end
|
@@ -43,17 +31,15 @@ module Cucumber
|
|
43
31
|
end
|
44
32
|
|
45
33
|
def method_missing(method, *args, &blk)
|
46
|
-
@pipe.send(method, *args, &blk)
|
34
|
+
@pipe.send(method, *args, &blk) || super
|
47
35
|
end
|
48
36
|
|
49
|
-
def
|
37
|
+
def respond_to_missing?(method, include_private = false)
|
50
38
|
super || @pipe.respond_to?(method, include_private)
|
51
39
|
end
|
52
40
|
|
53
41
|
def self.validate_pipe(pipe)
|
54
|
-
unless [
|
55
|
-
raise ArgumentError, '#wrap only accepts :stderr or :stdout'
|
56
|
-
end
|
42
|
+
raise ArgumentError, '#wrap only accepts :stderr or :stdout' unless %i[stdout stderr].include? pipe
|
57
43
|
end
|
58
44
|
|
59
45
|
def self.unwrap!(pipe)
|
@@ -75,19 +61,13 @@ module Cucumber
|
|
75
61
|
|
76
62
|
case pipe
|
77
63
|
when :stderr
|
78
|
-
$stderr =
|
64
|
+
$stderr = new($stderr)
|
79
65
|
return $stderr
|
80
66
|
when :stdout
|
81
|
-
$stdout =
|
67
|
+
$stdout = new($stdout)
|
82
68
|
return $stdout
|
83
69
|
end
|
84
70
|
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
def lock
|
89
|
-
@lock ||= Mutex.new
|
90
|
-
end
|
91
71
|
end
|
92
72
|
end
|
93
73
|
end
|
@@ -1,32 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'cucumber/formatter/http_io'
|
4
|
+
|
3
5
|
module Cucumber
|
4
6
|
module Formatter
|
5
7
|
module Io
|
6
8
|
module_function
|
7
9
|
|
8
|
-
def ensure_io(
|
9
|
-
return nil if
|
10
|
-
return
|
11
|
-
|
10
|
+
def ensure_io(path_or_url_or_io)
|
11
|
+
return nil if path_or_url_or_io.nil?
|
12
|
+
return path_or_url_or_io if path_or_url_or_io.respond_to?(:write)
|
13
|
+
io = if path_or_url_or_io.match(%r{^https?://})
|
14
|
+
HTTPIO.open(path_or_url_or_io)
|
15
|
+
else
|
16
|
+
File.open(path_or_url_or_io, Cucumber.file_mode('w'))
|
17
|
+
end
|
12
18
|
at_exit do
|
13
|
-
unless
|
14
|
-
|
15
|
-
|
19
|
+
unless io.closed?
|
20
|
+
io.flush
|
21
|
+
io.close
|
16
22
|
end
|
17
23
|
end
|
18
|
-
|
24
|
+
io
|
19
25
|
end
|
20
26
|
|
21
27
|
def ensure_file(path, name)
|
22
|
-
raise "You *must* specify --out FILE for the #{name} formatter" unless String
|
28
|
+
raise "You *must* specify --out FILE for the #{name} formatter" unless String == path.class
|
23
29
|
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)}"
|
30
|
+
raise "I can't write #{name} to a file in the non-existing directory #{File.dirname(path)}" unless File.directory?(File.dirname(path))
|
25
31
|
ensure_io(path)
|
26
32
|
end
|
27
33
|
|
28
34
|
def ensure_dir(path, name)
|
29
|
-
raise "You *must* specify --out DIR for the #{name} formatter" unless String
|
35
|
+
raise "You *must* specify --out DIR for the #{name} formatter" unless String == path.class
|
30
36
|
raise "I can't write #{name} reports to a file - it has to be a directory" if File.file?(path)
|
31
37
|
FileUtils.mkdir_p(path) unless File.directory?(path)
|
32
38
|
File.absolute_path path
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'json'
|
4
4
|
require 'base64'
|
5
5
|
require 'cucumber/formatter/backtrace_filter'
|
6
6
|
require 'cucumber/formatter/io'
|
7
|
-
require 'cucumber/formatter/
|
7
|
+
require 'cucumber/formatter/ast_lookup'
|
8
|
+
require 'cucumber/deprecate'
|
8
9
|
|
9
10
|
module Cucumber
|
10
11
|
module Formatter
|
@@ -13,7 +14,16 @@ module Cucumber
|
|
13
14
|
include Io
|
14
15
|
|
15
16
|
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
|
+
'5.0.0'
|
23
|
+
)
|
24
|
+
|
16
25
|
@io = ensure_io(config.out_stream)
|
26
|
+
@ast_lookup = AstLookup.new(config)
|
17
27
|
@feature_hashes = []
|
18
28
|
@step_or_hook_hash = {}
|
19
29
|
config.on_event :test_case_started, &method(:on_test_case_started)
|
@@ -25,16 +35,18 @@ module Cucumber
|
|
25
35
|
|
26
36
|
def on_test_case_started(event)
|
27
37
|
test_case = event.test_case
|
28
|
-
builder = Builder.new(test_case)
|
29
|
-
unless same_feature_as_previous_test_case?(test_case
|
38
|
+
builder = Builder.new(test_case, @ast_lookup)
|
39
|
+
unless same_feature_as_previous_test_case?(test_case)
|
30
40
|
@feature_hash = builder.feature_hash
|
31
41
|
@feature_hashes << @feature_hash
|
32
42
|
end
|
33
43
|
@test_case_hash = builder.test_case_hash
|
34
44
|
if builder.background?
|
45
|
+
@in_background = true
|
35
46
|
feature_elements << builder.background_hash
|
36
47
|
@element_hash = builder.background_hash
|
37
48
|
else
|
49
|
+
@in_background = false
|
38
50
|
feature_elements << @test_case_hash
|
39
51
|
@element_hash = @test_case_hash
|
40
52
|
end
|
@@ -44,17 +56,17 @@ module Cucumber
|
|
44
56
|
def on_test_step_started(event)
|
45
57
|
test_step = event.test_step
|
46
58
|
return if internal_hook?(test_step)
|
47
|
-
|
48
|
-
if hook_query.hook?
|
59
|
+
if test_step.hook?
|
49
60
|
@step_or_hook_hash = {}
|
50
|
-
hooks_of_type(
|
61
|
+
hooks_of_type(test_step) << @step_or_hook_hash
|
51
62
|
return
|
52
63
|
end
|
53
64
|
if first_step_after_background?(test_step)
|
65
|
+
@in_background = false
|
54
66
|
feature_elements << @test_case_hash
|
55
67
|
@element_hash = @test_case_hash
|
56
68
|
end
|
57
|
-
@step_or_hook_hash = create_step_hash(test_step
|
69
|
+
@step_or_hook_hash = create_step_hash(test_step)
|
58
70
|
steps << @step_or_hook_hash
|
59
71
|
@step_hash = @step_or_hook_hash
|
60
72
|
end
|
@@ -74,44 +86,42 @@ module Cucumber
|
|
74
86
|
end
|
75
87
|
|
76
88
|
def on_test_run_finished(_event)
|
77
|
-
@io.write(
|
78
|
-
end
|
79
|
-
|
80
|
-
def puts(message)
|
81
|
-
test_step_output << message
|
89
|
+
@io.write(JSON.generate(@feature_hashes, pretty: true))
|
82
90
|
end
|
83
91
|
|
84
|
-
def
|
92
|
+
def attach(src, mime_type)
|
93
|
+
if mime_type == 'text/x.cucumber.log+plain'
|
94
|
+
test_step_output << src
|
95
|
+
return
|
96
|
+
end
|
85
97
|
if File.file?(src)
|
86
98
|
content = File.open(src, 'rb', &:read)
|
87
99
|
data = encode64(content)
|
100
|
+
elsif mime_type =~ /;base64$/
|
101
|
+
mime_type = mime_type[0..-8]
|
102
|
+
data = src
|
88
103
|
else
|
89
|
-
|
90
|
-
mime_type = mime_type[0..-8]
|
91
|
-
data = src
|
92
|
-
else
|
93
|
-
data = encode64(src)
|
94
|
-
end
|
104
|
+
data = encode64(src)
|
95
105
|
end
|
96
106
|
test_step_embeddings << { mime_type: mime_type, data: data }
|
97
107
|
end
|
98
108
|
|
99
109
|
private
|
100
110
|
|
101
|
-
def same_feature_as_previous_test_case?(
|
102
|
-
current_feature[:uri] ==
|
111
|
+
def same_feature_as_previous_test_case?(test_case)
|
112
|
+
current_feature[:uri] == test_case.location.file
|
103
113
|
end
|
104
114
|
|
105
115
|
def first_step_after_background?(test_step)
|
106
|
-
test_step.
|
116
|
+
@in_background && test_step.location.lines.max >= @test_case_hash[:line]
|
107
117
|
end
|
108
118
|
|
109
119
|
def internal_hook?(test_step)
|
110
|
-
test_step.
|
120
|
+
test_step.location.file.include?('lib/cucumber/')
|
111
121
|
end
|
112
122
|
|
113
123
|
def current_feature
|
114
|
-
@feature_hash ||= {}
|
124
|
+
@feature_hash ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
|
115
125
|
end
|
116
126
|
|
117
127
|
def feature_elements
|
@@ -122,16 +132,16 @@ module Cucumber
|
|
122
132
|
@element_hash[:steps] ||= []
|
123
133
|
end
|
124
134
|
|
125
|
-
def hooks_of_type(
|
126
|
-
case
|
127
|
-
when
|
128
|
-
|
129
|
-
when
|
130
|
-
|
131
|
-
when
|
132
|
-
|
135
|
+
def hooks_of_type(hook_step)
|
136
|
+
case hook_step.text
|
137
|
+
when 'Before hook'
|
138
|
+
before_hooks
|
139
|
+
when 'After hook'
|
140
|
+
after_hooks
|
141
|
+
when 'AfterStep hook'
|
142
|
+
after_step_hooks
|
133
143
|
else
|
134
|
-
|
144
|
+
raise 'Unknown hook type ' + hook_step.to_s
|
135
145
|
end
|
136
146
|
end
|
137
147
|
|
@@ -159,20 +169,20 @@ module Cucumber
|
|
159
169
|
@step_or_hook_hash[:embeddings] ||= []
|
160
170
|
end
|
161
171
|
|
162
|
-
def create_step_hash(
|
172
|
+
def create_step_hash(test_step)
|
173
|
+
step_source = @ast_lookup.step_source(test_step).step
|
163
174
|
step_hash = {
|
164
175
|
keyword: step_source.keyword,
|
165
|
-
name:
|
166
|
-
line:
|
176
|
+
name: test_step.text,
|
177
|
+
line: test_step.location.lines.min
|
167
178
|
}
|
168
|
-
step_hash[:
|
169
|
-
step_hash[:
|
170
|
-
step_hash[:rows] = create_data_table_value(step_source.multiline_arg) if step_source.multiline_arg.data_table?
|
179
|
+
step_hash[:doc_string] = create_doc_string_hash(step_source.doc_string) unless step_source.doc_string.nil?
|
180
|
+
step_hash[:rows] = create_data_table_value(step_source.data_table) unless step_source.data_table.nil?
|
171
181
|
step_hash
|
172
182
|
end
|
173
183
|
|
174
184
|
def create_doc_string_hash(doc_string)
|
175
|
-
content_type = doc_string.
|
185
|
+
content_type = doc_string.media_type || ''
|
176
186
|
{
|
177
187
|
value: doc_string.content,
|
178
188
|
content_type: content_type,
|
@@ -181,14 +191,15 @@ module Cucumber
|
|
181
191
|
end
|
182
192
|
|
183
193
|
def create_data_table_value(data_table)
|
184
|
-
data_table.
|
185
|
-
{ cells: row }
|
194
|
+
data_table.rows.map do |row|
|
195
|
+
{ cells: row.cells.map(&:value) }
|
186
196
|
end
|
187
197
|
end
|
188
198
|
|
189
199
|
def add_match_and_result(test_step, result)
|
190
200
|
@step_or_hook_hash[:match] = create_match_hash(test_step, result)
|
191
201
|
@step_or_hook_hash[:result] = create_result_hash(result)
|
202
|
+
result.embeddings.each { |e| embed(e['src'], e['mime_type'], e['label']) } if result.respond_to?(:embeddings)
|
192
203
|
end
|
193
204
|
|
194
205
|
def add_failed_around_hook(result)
|
@@ -226,113 +237,94 @@ module Cucumber
|
|
226
237
|
class Builder
|
227
238
|
attr_reader :feature_hash, :background_hash, :test_case_hash
|
228
239
|
|
229
|
-
def initialize(test_case)
|
240
|
+
def initialize(test_case, ast_lookup)
|
230
241
|
@background_hash = nil
|
231
|
-
test_case.
|
232
|
-
|
242
|
+
uri = test_case.location.file
|
243
|
+
feature = ast_lookup.gherkin_document(uri).feature
|
244
|
+
feature(feature, uri)
|
245
|
+
background(feature.children.first.background) unless feature.children.first.background.nil?
|
246
|
+
scenario(ast_lookup.scenario_source(test_case), test_case)
|
233
247
|
end
|
234
248
|
|
235
249
|
def background?
|
236
250
|
@background_hash != nil
|
237
251
|
end
|
238
252
|
|
239
|
-
def feature(feature)
|
253
|
+
def feature(feature, uri)
|
240
254
|
@feature_hash = {
|
241
|
-
|
242
|
-
|
255
|
+
id: create_id(feature.name),
|
256
|
+
uri: uri,
|
243
257
|
keyword: feature.keyword,
|
244
|
-
name: feature.
|
245
|
-
description: feature.description,
|
258
|
+
name: feature.name,
|
259
|
+
description: value_or_empty_string(feature.description),
|
246
260
|
line: feature.location.line
|
247
261
|
}
|
248
|
-
|
249
|
-
|
250
|
-
@test_case_hash[:tags] = if @test_case_hash[:tags]
|
251
|
-
@feature_hash[:tags] + @test_case_hash[:tags]
|
252
|
-
else
|
253
|
-
@feature_hash[:tags]
|
254
|
-
end
|
255
|
-
end
|
256
|
-
@feature_hash[:comments] = Formatter.create_comments_array(feature.comments) unless feature.comments.empty?
|
257
|
-
@test_case_hash[:id].insert(0, @feature_hash[:id] + ';')
|
262
|
+
return if feature.tags.empty?
|
263
|
+
@feature_hash[:tags] = create_tags_array_from_hash_array(feature.tags)
|
258
264
|
end
|
259
265
|
|
260
266
|
def background(background)
|
261
267
|
@background_hash = {
|
262
268
|
keyword: background.keyword,
|
263
|
-
name: background.
|
264
|
-
description: background.description,
|
269
|
+
name: background.name,
|
270
|
+
description: value_or_empty_string(background.description),
|
265
271
|
line: background.location.line,
|
266
272
|
type: 'background'
|
267
273
|
}
|
268
|
-
@background_hash[:comments] = Formatter.create_comments_array(background.comments) unless background.comments.empty?
|
269
274
|
end
|
270
275
|
|
271
|
-
def scenario(
|
276
|
+
def scenario(scenario_source, test_case)
|
277
|
+
scenario = scenario_source.type == :Scenario ? scenario_source.scenario : scenario_source.scenario_outline
|
272
278
|
@test_case_hash = {
|
273
|
-
id:
|
279
|
+
id: "#{@feature_hash[:id]};#{create_id_from_scenario_source(scenario_source)}",
|
274
280
|
keyword: scenario.keyword,
|
275
|
-
name:
|
276
|
-
description: scenario.description,
|
277
|
-
line:
|
281
|
+
name: test_case.name,
|
282
|
+
description: value_or_empty_string(scenario.description),
|
283
|
+
line: test_case.location.lines.max,
|
278
284
|
type: 'scenario'
|
279
285
|
}
|
280
|
-
@test_case_hash[:tags] =
|
281
|
-
@test_case_hash[:comments] = Formatter.create_comments_array(scenario.comments) unless scenario.comments.empty?
|
286
|
+
@test_case_hash[:tags] = create_tags_array_from_tags_array(test_case.tags) unless test_case.tags.empty?
|
282
287
|
end
|
283
288
|
|
284
|
-
|
285
|
-
@test_case_hash = {
|
286
|
-
id: create_id(scenario) + ';' + @example_id,
|
287
|
-
keyword: scenario.keyword,
|
288
|
-
name: scenario.to_s,
|
289
|
-
description: scenario.description,
|
290
|
-
line: @row.location.line,
|
291
|
-
type: 'scenario'
|
292
|
-
}
|
293
|
-
tags = []
|
294
|
-
tags += create_tags_array(scenario.tags) unless scenario.tags.empty?
|
295
|
-
tags += @examples_table_tags if @examples_table_tags
|
296
|
-
@test_case_hash[:tags] = tags unless tags.empty?
|
297
|
-
comments = []
|
298
|
-
comments += Formatter.create_comments_array(scenario.comments) unless scenario.comments.empty?
|
299
|
-
comments += @examples_table_comments if @examples_table_comments
|
300
|
-
comments += @row_comments if @row_comments
|
301
|
-
@test_case_hash[:comments] = comments unless comments.empty?
|
302
|
-
end
|
289
|
+
private
|
303
290
|
|
304
|
-
def
|
305
|
-
|
306
|
-
|
307
|
-
@example_id = create_id(examples_table) + ";#{@row.number + 1}"
|
291
|
+
def value_or_empty_string(value)
|
292
|
+
value.nil? ? '' : value
|
293
|
+
end
|
308
294
|
|
309
|
-
|
310
|
-
|
295
|
+
def create_id(name)
|
296
|
+
name.downcase.tr(' ', '-')
|
311
297
|
end
|
312
298
|
|
313
|
-
def
|
314
|
-
|
315
|
-
|
299
|
+
def create_id_from_scenario_source(scenario_source)
|
300
|
+
if scenario_source.type == :Scenario
|
301
|
+
create_id(scenario_source.scenario.name)
|
302
|
+
else
|
303
|
+
scenario_outline_name = scenario_source.scenario_outline.name
|
304
|
+
examples_name = scenario_source.examples.name
|
305
|
+
row_number = calculate_row_number(scenario_source)
|
306
|
+
"#{create_id(scenario_outline_name)};#{create_id(examples_name)};#{row_number}"
|
307
|
+
end
|
316
308
|
end
|
317
309
|
|
318
|
-
|
310
|
+
def calculate_row_number(scenario_source)
|
311
|
+
scenario_source.examples.table_body.each_with_index do |row, index|
|
312
|
+
return index + 2 if row == scenario_source.row
|
313
|
+
end
|
314
|
+
end
|
319
315
|
|
320
|
-
def
|
321
|
-
|
316
|
+
def create_tags_array_from_hash_array(tags)
|
317
|
+
tags_array = []
|
318
|
+
tags.each { |tag| tags_array << { name: tag.name, line: tag.location.line } }
|
319
|
+
tags_array
|
322
320
|
end
|
323
321
|
|
324
|
-
def
|
322
|
+
def create_tags_array_from_tags_array(tags)
|
325
323
|
tags_array = []
|
326
324
|
tags.each { |tag| tags_array << { name: tag.name, line: tag.location.line } }
|
327
325
|
tags_array
|
328
326
|
end
|
329
327
|
end
|
330
328
|
end
|
331
|
-
|
332
|
-
def self.create_comments_array(comments)
|
333
|
-
comments_array = []
|
334
|
-
comments.each { |comment| comments_array << { value: comment.to_s.strip, line: comment.location.line } }
|
335
|
-
comments_array
|
336
|
-
end
|
337
329
|
end
|
338
330
|
end
|