cucumber 3.2.0 → 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 +4 -4
- data/CHANGELOG.md +166 -19
- 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/cli/configuration.rb +5 -5
- data/lib/cucumber/cli/main.rb +12 -12
- data/lib/cucumber/cli/options.rb +62 -71
- 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/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/events.rb +13 -6
- data/lib/cucumber/file_specs.rb +6 -6
- 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/filters.rb +1 -0
- 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 +1 -1
- data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
- data/lib/cucumber/formatter/interceptor.rb +8 -28
- data/lib/cucumber/formatter/io.rb +1 -1
- data/lib/cucumber/formatter/json.rb +101 -115
- 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/data_table/diff_matrices.rb +1 -1
- data/lib/cucumber/multiline_argument/data_table.rb +97 -64
- data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
- data/lib/cucumber/multiline_argument.rb +4 -6
- 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/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/runtime.rb +54 -58
- 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
- data/lib/cucumber.rb +1 -1
- metadata +221 -77
- 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
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'gherkin
|
4
|
-
require 'gherkin/token_matcher'
|
5
|
-
require 'gherkin/parser'
|
3
|
+
require 'gherkin'
|
6
4
|
require 'gherkin/dialect'
|
7
5
|
|
8
6
|
module Cucumber
|
@@ -15,12 +13,23 @@ module Cucumber
|
|
15
13
|
|
16
14
|
def parse(text)
|
17
15
|
dialect = ::Gherkin::Dialect.for(@language)
|
18
|
-
|
19
|
-
|
20
|
-
parser = ::Gherkin::Parser.new
|
21
|
-
gherkin_document = parser.parse(token_scanner, token_matcher)
|
16
|
+
gherkin_document = nil
|
17
|
+
messages = ::Gherkin.from_source('dummy', feature_header(dialect) + text, gherkin_options)
|
22
18
|
|
23
|
-
|
19
|
+
messages.each do |message|
|
20
|
+
gherkin_document = message.gherkin_document.to_hash unless message.gherkin_document.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
@builder.steps(gherkin_document[:feature][:children][0][:scenario][:steps])
|
24
|
+
end
|
25
|
+
|
26
|
+
def gherkin_options
|
27
|
+
{
|
28
|
+
default_dialect: @language,
|
29
|
+
include_source: false,
|
30
|
+
include_gherkin_document: true,
|
31
|
+
include_pickles: false
|
32
|
+
}
|
24
33
|
end
|
25
34
|
|
26
35
|
def feature_header(dialect)
|
data/lib/cucumber/glue/dsl.rb
CHANGED
data/lib/cucumber/glue/hook.rb
CHANGED
@@ -6,14 +6,15 @@ module Cucumber
|
|
6
6
|
module Glue
|
7
7
|
# TODO: Kill pointless wrapper for Before, After and AfterStep hooks with fire
|
8
8
|
class Hook
|
9
|
-
attr_reader :tag_expressions, :location
|
9
|
+
attr_reader :id, :tag_expressions, :location
|
10
10
|
|
11
|
-
def initialize(registry, tag_expressions, proc)
|
11
|
+
def initialize(id, registry, tag_expressions, proc)
|
12
|
+
@id = id
|
12
13
|
@registry = registry
|
13
|
-
@tag_expressions = tag_expressions
|
14
|
+
@tag_expressions = sanitize_tag_expressions(tag_expressions)
|
14
15
|
@proc = proc
|
15
|
-
@location = Cucumber::Core::
|
16
|
-
|
16
|
+
@location = Cucumber::Core::Test::Location.from_source_location(*@proc.source_location)
|
17
|
+
fail_for_old_style_tag_expressions(@tag_expressions)
|
17
18
|
end
|
18
19
|
|
19
20
|
def invoke(pseudo_method, arguments, &block)
|
@@ -27,16 +28,38 @@ module Cucumber
|
|
27
28
|
)
|
28
29
|
end
|
29
30
|
|
31
|
+
def to_envelope
|
32
|
+
Cucumber::Messages::Envelope.new(
|
33
|
+
hook: Cucumber::Messages::Hook.new(
|
34
|
+
id: id,
|
35
|
+
tag_expression: tag_expressions.join(' '),
|
36
|
+
source_reference: Cucumber::Messages::SourceReference.new(
|
37
|
+
uri: location.file,
|
38
|
+
location: Cucumber::Messages::Location.new(
|
39
|
+
line: location.lines.first
|
40
|
+
)
|
41
|
+
)
|
42
|
+
)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
30
46
|
private
|
31
47
|
|
32
|
-
def
|
48
|
+
def sanitize_tag_expressions(tag_expressions)
|
49
|
+
# TODO: remove when '~@no-clobber' has been changed to 'not @no-clobber' in aruba
|
50
|
+
tag_expressions.map { |tag_expression| tag_expression == '~@no-clobber' ? 'not @no-clobber' : tag_expression }
|
51
|
+
end
|
52
|
+
|
53
|
+
def fail_for_old_style_tag_expressions(tag_expressions)
|
33
54
|
tag_expressions.each do |tag_expression|
|
34
|
-
if tag_expression.include?('~')
|
35
|
-
|
36
|
-
|
37
|
-
if tag_expression.include?(',')
|
38
|
-
warn("Deprecated: Found tagged hook with '#{tag_expression}'. Support for '@tag1,@tag2' will be removed from the next release of Cucumber. Please use '@tag or @tag2' instead.")
|
55
|
+
if tag_expression.include?('~')
|
56
|
+
raise("Found tagged hook with '#{tag_expression}'." \
|
57
|
+
"'~@tag' is no longer supported, use 'not @tag' instead.")
|
39
58
|
end
|
59
|
+
|
60
|
+
next unless tag_expression.include?(',')
|
61
|
+
warn("Found tagged hook with '#{tag_expression}'." \
|
62
|
+
"'@tag1,@tag2' is no longer supported, use '@tag or @tag2' instead.")
|
40
63
|
end
|
41
64
|
end
|
42
65
|
end
|
@@ -11,16 +11,15 @@ module Cucumber
|
|
11
11
|
return if Cucumber.use_full_backtrace
|
12
12
|
|
13
13
|
instance_exec_pos = backtrace.index(instance_exec_invocation_line)
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
return unless instance_exec_pos
|
15
|
+
replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET
|
16
|
+
backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
depth = backtrace.count { |line| line == instance_exec_invocation_line }
|
19
|
+
end_pos = depth > 1 ? instance_exec_pos : -1
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
end
|
21
|
+
backtrace[replacement_line + 1..end_pos] = nil
|
22
|
+
backtrace.compact!
|
24
23
|
end
|
25
24
|
|
26
25
|
def self.cucumber_instance_exec_in(world, check_arity, pseudo_method, *args, &block)
|
@@ -31,9 +30,7 @@ module Cucumber
|
|
31
30
|
ari = ari < 0 ? (ari.abs - 1).to_s + '+' : ari
|
32
31
|
s1 = ari == 1 ? '' : 's'
|
33
32
|
s2 = args.length == 1 ? '' : 's'
|
34
|
-
raise ArityMismatchError.
|
35
|
-
"Your block takes #{ari} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
|
36
|
-
)
|
33
|
+
raise ArityMismatchError, "Your block takes #{ari} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
|
37
34
|
end
|
38
35
|
else
|
39
36
|
world.instance_exec(*args, &block)
|
@@ -50,13 +47,11 @@ module Cucumber
|
|
50
47
|
end
|
51
48
|
|
52
49
|
def self.cucumber_run_with_backtrace_filtering(pseudo_method)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
raise e
|
59
|
-
end
|
50
|
+
yield
|
51
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
52
|
+
instance_exec_invocation_line = "#{__FILE__}:#{__LINE__ - 2}:in `cucumber_run_with_backtrace_filtering'"
|
53
|
+
replace_instance_exec_invocation_line!((e.backtrace || []), instance_exec_invocation_line, pseudo_method)
|
54
|
+
raise e
|
60
55
|
end
|
61
56
|
|
62
57
|
INSTANCE_EXEC_OFFSET = -3
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'cucumber/gherkin/formatter/ansi_escapes'
|
4
|
-
require 'cucumber/core/
|
4
|
+
require 'cucumber/core/test/data_table'
|
5
|
+
require 'cucumber/deprecate'
|
5
6
|
|
6
7
|
module Cucumber
|
7
8
|
module Glue
|
@@ -24,7 +25,7 @@ module Cucumber
|
|
24
25
|
# @example Passing a multiline string
|
25
26
|
# step "the email should contain:", "Dear sir,\nYou've won a prize!\n"
|
26
27
|
# @param [String] name The name of the step
|
27
|
-
# @param [String,Cucumber::
|
28
|
+
# @param [String,Cucumber::Test::DocString,Cucumber::Ast::Table] multiline_argument
|
28
29
|
def step(name, raw_multiline_arg = nil)
|
29
30
|
super
|
30
31
|
end
|
@@ -67,9 +68,8 @@ module Cucumber
|
|
67
68
|
# %w{ CUC-101 Peeler 22 }
|
68
69
|
# ])
|
69
70
|
#
|
70
|
-
def table(text_or_table
|
71
|
-
|
72
|
-
MultilineArgument::DataTable.from(text_or_table, location)
|
71
|
+
def table(text_or_table)
|
72
|
+
MultilineArgument::DataTable.from(text_or_table)
|
73
73
|
end
|
74
74
|
|
75
75
|
# Print a message to the output.
|
@@ -80,7 +80,16 @@ module Cucumber
|
|
80
80
|
#
|
81
81
|
# If you'd prefer to see the message immediately, call {Kernel.puts} instead.
|
82
82
|
def puts(*messages)
|
83
|
-
|
83
|
+
Cucumber.deprecate(
|
84
|
+
'Messages emitted with "puts" will no longer be caught by Cucumber ' \
|
85
|
+
'and sent to the formatter. If you want message to be in the formatted output, ' \
|
86
|
+
"please use log(message) instead.\n" \
|
87
|
+
'If you simply want it in the console, '\
|
88
|
+
'keep using "puts" (or Kernel.puts to avoid this message)',
|
89
|
+
'puts(message)',
|
90
|
+
'5.0.0'
|
91
|
+
)
|
92
|
+
messages.each { |message| log(message.to_s) }
|
84
93
|
end
|
85
94
|
|
86
95
|
# Pause the tests and ask the operator for input
|
@@ -89,22 +98,33 @@ module Cucumber
|
|
89
98
|
end
|
90
99
|
|
91
100
|
# Embed an image in the output
|
92
|
-
def embed(file, mime_type,
|
101
|
+
def embed(file, mime_type, _label = 'Screenshot')
|
102
|
+
Cucumber.deprecate(
|
103
|
+
'Please use attach(file, media_type) instead',
|
104
|
+
'embed(file, mime_type, label)',
|
105
|
+
'5.0.0'
|
106
|
+
)
|
107
|
+
attach(file, mime_type)
|
108
|
+
end
|
109
|
+
|
110
|
+
def log(*messages)
|
111
|
+
messages.each { |message| attach(message.to_s.dup, 'text/x.cucumber.log+plain') }
|
112
|
+
end
|
113
|
+
|
114
|
+
def attach(file, media_type)
|
93
115
|
super
|
94
116
|
end
|
95
117
|
|
96
118
|
# Mark the matched step as pending.
|
97
119
|
def pending(message = 'TODO')
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
end
|
104
|
-
raise Pending, "Expected pending '#{message}' to fail. No Error was raised. No longer pending?"
|
105
|
-
else
|
120
|
+
raise Pending, message unless block_given?
|
121
|
+
|
122
|
+
begin
|
123
|
+
yield
|
124
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
106
125
|
raise Pending, message
|
107
126
|
end
|
127
|
+
raise Pending, "Expected pending '#{message}' to fail. No Error was raised. No longer pending?"
|
108
128
|
end
|
109
129
|
|
110
130
|
# Skips this step and the remaining steps in the scenario
|
@@ -123,8 +143,8 @@ module Cucumber
|
|
123
143
|
end
|
124
144
|
|
125
145
|
# Dynamially generate the API module, closuring the dependencies
|
126
|
-
def self.for(runtime, language)
|
127
|
-
Module.new do
|
146
|
+
def self.for(runtime, language) # rubocop:disable Metrics/MethodLength
|
147
|
+
Module.new do # rubocop:disable Metrics/BlockLength
|
128
148
|
def self.extended(object)
|
129
149
|
# wrap the dynamically generated module so that we can document the methods
|
130
150
|
# for yardoc, which doesn't like define_method.
|
@@ -139,32 +159,21 @@ module Cucumber
|
|
139
159
|
end
|
140
160
|
|
141
161
|
define_method(:step) do |name, raw_multiline_arg = nil|
|
142
|
-
location = Core::
|
162
|
+
location = Core::Test::Location.of_caller
|
143
163
|
runtime.invoke_dynamic_step(name, MultilineArgument.from(raw_multiline_arg, location))
|
144
164
|
end
|
145
165
|
|
146
166
|
define_method(:steps) do |steps_text|
|
147
|
-
location = Core::
|
167
|
+
location = Core::Test::Location.of_caller
|
148
168
|
runtime.invoke_dynamic_steps(steps_text, language, location)
|
149
169
|
end
|
150
170
|
|
151
|
-
# rubocop:disable UnneededInterpolation
|
152
|
-
define_method(:puts) do |*messages|
|
153
|
-
# Even though they won't be output until later, converting the messages to
|
154
|
-
# strings right away will protect them from modifications to their original
|
155
|
-
# objects in the mean time
|
156
|
-
messages.collect! { |message| "#{message}" }
|
157
|
-
|
158
|
-
runtime.puts(*messages)
|
159
|
-
end
|
160
|
-
# rubocop:enable UnneededInterpolation
|
161
|
-
|
162
171
|
define_method(:ask) do |question, timeout_seconds = 60|
|
163
172
|
runtime.ask(question, timeout_seconds)
|
164
173
|
end
|
165
174
|
|
166
|
-
define_method(:
|
167
|
-
runtime.
|
175
|
+
define_method(:attach) do |file, media_type|
|
176
|
+
runtime.attach(file, media_type)
|
168
177
|
end
|
169
178
|
|
170
179
|
# Prints the list of modules that are included in the World
|
@@ -174,7 +183,7 @@ module Cucumber
|
|
174
183
|
modules += included_modules
|
175
184
|
end
|
176
185
|
modules << stringify_namespaced_modules
|
177
|
-
format('
|
186
|
+
format('#<%<modules>s:0x%<object_id>x>', modules: modules.join('+'), object_id: object_id)
|
178
187
|
end
|
179
188
|
|
180
189
|
private
|
@@ -28,7 +28,7 @@ module Cucumber
|
|
28
28
|
# Raised if there are 2 or more World blocks.
|
29
29
|
class MultipleWorld < StandardError
|
30
30
|
def initialize(first_proc, second_proc)
|
31
|
-
message = String.new
|
31
|
+
message = String.new # rubocop:disable Style/EmptyLiteral
|
32
32
|
message << "You can only pass a proc to #World once, but it's happening\n"
|
33
33
|
message << "in 2 places:\n\n"
|
34
34
|
message << Glue.backtrace_line(first_proc, 'World') << "\n"
|
@@ -53,7 +53,8 @@ module Cucumber
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def initialize(runtime, configuration)
|
56
|
-
@runtime
|
56
|
+
@runtime = runtime
|
57
|
+
@configuration = configuration
|
57
58
|
@step_definitions = []
|
58
59
|
Glue::Dsl.rb_language = self
|
59
60
|
@world_proc = @world_modules = nil
|
@@ -63,27 +64,36 @@ module Cucumber
|
|
63
64
|
end
|
64
65
|
|
65
66
|
def step_matches(name_to_match)
|
66
|
-
@step_definitions.
|
67
|
+
@step_definitions.each_with_object([]) do |step_definition, result|
|
67
68
|
if (arguments = step_definition.arguments_from(name_to_match))
|
68
69
|
result << StepMatch.new(step_definition, name_to_match, arguments)
|
69
70
|
end
|
70
|
-
result
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
74
|
def register_rb_hook(phase, tag_expressions, proc)
|
75
|
-
add_hook(phase, Hook.new(self, tag_expressions, proc))
|
75
|
+
hook = add_hook(phase, Hook.new(@configuration.id_generator.new_id, self, tag_expressions, proc))
|
76
|
+
@configuration.notify :envelope, hook.to_envelope
|
77
|
+
hook
|
76
78
|
end
|
77
79
|
|
78
80
|
def define_parameter_type(parameter_type)
|
81
|
+
@configuration.notify :envelope, parameter_type_envelope(parameter_type)
|
82
|
+
|
79
83
|
@parameter_type_registry.define_parameter_type(parameter_type)
|
80
84
|
end
|
81
85
|
|
82
86
|
def register_rb_step_definition(string_or_regexp, proc_or_sym, options)
|
83
|
-
step_definition = StepDefinition.new(self, string_or_regexp, proc_or_sym, options)
|
87
|
+
step_definition = StepDefinition.new(@configuration.id_generator.new_id, self, string_or_regexp, proc_or_sym, options)
|
84
88
|
@step_definitions << step_definition
|
85
89
|
@configuration.notify :step_definition_registered, step_definition
|
90
|
+
@configuration.notify :envelope, step_definition.to_envelope
|
86
91
|
step_definition
|
92
|
+
rescue Cucumber::CucumberExpressions::UndefinedParameterTypeError => e
|
93
|
+
# TODO: add a way to extract the parameter type directly from the error.
|
94
|
+
type_name = e.message.match(/^Undefined parameter type \{(.*)\}$/)[1]
|
95
|
+
|
96
|
+
@configuration.notify :undefined_parameter_type, type_name, string_or_regexp
|
87
97
|
end
|
88
98
|
|
89
99
|
def build_rb_world_factory(world_modules, namespaced_world_modules, proc)
|
@@ -96,15 +106,20 @@ module Cucumber
|
|
96
106
|
|
97
107
|
@namespaced_world_modules ||= Hash.new { |h, k| h[k] = [] }
|
98
108
|
namespaced_world_modules.each do |namespace, world_module|
|
99
|
-
unless @namespaced_world_modules[namespace].include?(world_module)
|
100
|
-
@namespaced_world_modules[namespace] << world_module
|
101
|
-
end
|
109
|
+
@namespaced_world_modules[namespace] << world_module unless @namespaced_world_modules[namespace].include?(world_module)
|
102
110
|
end
|
103
111
|
end
|
104
112
|
|
105
113
|
def load_code_file(code_file)
|
106
114
|
return unless File.extname(code_file) == '.rb'
|
107
|
-
|
115
|
+
|
116
|
+
# This will cause self.add_step_definition, self.add_hook, and self.define_parameter_type to be called from Glue::Dsl
|
117
|
+
|
118
|
+
if Cucumber.use_legacy_autoloader
|
119
|
+
load File.expand_path(code_file)
|
120
|
+
else
|
121
|
+
require File.expand_path(code_file)
|
122
|
+
end
|
108
123
|
end
|
109
124
|
|
110
125
|
def begin_scenario(test_case)
|
@@ -153,7 +168,7 @@ module Cucumber
|
|
153
168
|
def create_expression(string_or_regexp)
|
154
169
|
return CucumberExpressions::CucumberExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(String)
|
155
170
|
return CucumberExpressions::RegularExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(Regexp)
|
156
|
-
raise ArgumentError
|
171
|
+
raise ArgumentError, 'Expression must be a String or Regexp'
|
157
172
|
end
|
158
173
|
|
159
174
|
def self.cli_snippet_type_options
|
@@ -166,6 +181,21 @@ module Cucumber
|
|
166
181
|
|
167
182
|
private
|
168
183
|
|
184
|
+
def parameter_type_envelope(parameter_type)
|
185
|
+
# TODO: should me moved to Cucumber::Expression::ParameterType#to_envelope ?
|
186
|
+
# Note: that would mean that cucumber-expression would depend on cucumber-messages
|
187
|
+
|
188
|
+
Cucumber::Messages::Envelope.new(
|
189
|
+
parameter_type: Cucumber::Messages::ParameterType.new(
|
190
|
+
id: @configuration.id_generator.new_id,
|
191
|
+
name: parameter_type.name,
|
192
|
+
regular_expressions: parameter_type.regexps.map(&:to_s),
|
193
|
+
prefer_for_regular_expression_match: parameter_type.prefer_for_regexp_match?,
|
194
|
+
use_for_snippets: parameter_type.use_for_snippets?
|
195
|
+
)
|
196
|
+
)
|
197
|
+
end
|
198
|
+
|
169
199
|
def available_step_definition_hash
|
170
200
|
@available_step_definition_hash ||= {}
|
171
201
|
end
|
@@ -180,7 +210,7 @@ module Cucumber
|
|
180
210
|
end
|
181
211
|
|
182
212
|
def self.backtrace_line(proc, name)
|
183
|
-
location = Cucumber::Core::
|
213
|
+
location = Cucumber::Core::Test::Location.from_source_location(*proc.source_location)
|
184
214
|
"#{location}:in `#{name}'"
|
185
215
|
end
|
186
216
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Cucumber
|
4
4
|
module Glue
|
5
5
|
module Snippet
|
6
|
-
ARGUMENT_PATTERNS = ['"([^"]*)"', '(\d+)']
|
6
|
+
ARGUMENT_PATTERNS = ['"([^"]*)"', '(\d+)'].freeze
|
7
7
|
|
8
8
|
class Generator
|
9
9
|
def self.register_on(configuration)
|
@@ -42,7 +42,7 @@ module Cucumber
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def self.cli_option_string(type, cucumber_expression_generator)
|
45
|
-
format('
|
45
|
+
format('%<type>-7s: %<description>-28s e.g. %<example>s', type: type, description: description, example: example(cucumber_expression_generator))
|
46
46
|
end
|
47
47
|
|
48
48
|
private
|
@@ -61,7 +61,7 @@ module Cucumber
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def do_block
|
64
|
-
do_block = String.new
|
64
|
+
do_block = String.new # rubocop:disable Style/EmptyLiteral
|
65
65
|
do_block << "do#{parameters}\n"
|
66
66
|
multiline_argument.append_comment_to(do_block)
|
67
67
|
do_block << " pending # Write code here that turns the phrase above into concrete actions\n"
|
@@ -72,11 +72,15 @@ module Cucumber
|
|
72
72
|
def parameters
|
73
73
|
block_args = (0...number_of_arguments).map { |n| "arg#{n + 1}" }
|
74
74
|
multiline_argument.append_block_parameter_to(block_args)
|
75
|
-
block_args.empty? ? '' : " |#{block_args.join(
|
75
|
+
block_args.empty? ? '' : " |#{block_args.join(', ')}|"
|
76
76
|
end
|
77
77
|
|
78
|
-
|
79
|
-
|
78
|
+
class << self
|
79
|
+
private
|
80
|
+
|
81
|
+
def example(cucumber_expression_generator)
|
82
|
+
new(cucumber_expression_generator, 'Given', 'I have 2 cukes', MultilineArgument::None.new).step
|
83
|
+
end
|
80
84
|
end
|
81
85
|
end
|
82
86
|
|
@@ -87,11 +91,11 @@ module Cucumber
|
|
87
91
|
|
88
92
|
def to_s
|
89
93
|
header = generated_expressions.each_with_index.map do |expr, i|
|
90
|
-
prefix = i
|
91
|
-
"#{prefix}#{code_keyword}(
|
94
|
+
prefix = i.zero? ? '' : '# '
|
95
|
+
"#{prefix}#{code_keyword}('#{expr.source}') do#{parameters(expr)}"
|
92
96
|
end.join("\n")
|
93
97
|
|
94
|
-
body = String.new
|
98
|
+
body = String.new # rubocop:disable Style/EmptyLiteral
|
95
99
|
multiline_argument.append_comment_to(body)
|
96
100
|
body << " pending # Write code here that turns the phrase above into concrete actions\n"
|
97
101
|
body << 'end'
|
@@ -102,7 +106,7 @@ module Cucumber
|
|
102
106
|
def parameters(expr)
|
103
107
|
parameter_names = expr.parameter_names
|
104
108
|
multiline_argument.append_block_parameter_to(parameter_names)
|
105
|
-
parameter_names.empty? ? '' : " |#{parameter_names.join(
|
109
|
+
parameter_names.empty? ? '' : " |#{parameter_names.join(', ')}|"
|
106
110
|
end
|
107
111
|
|
108
112
|
def self.description
|
@@ -141,11 +145,11 @@ module Cucumber
|
|
141
145
|
end
|
142
146
|
|
143
147
|
SNIPPET_TYPES = {
|
144
|
-
:
|
145
|
-
:
|
146
|
-
:
|
147
|
-
:
|
148
|
-
}
|
148
|
+
cucumber_expression: CucumberExpression,
|
149
|
+
regexp: Regexp,
|
150
|
+
classic: Classic,
|
151
|
+
percent: Percent
|
152
|
+
}.freeze
|
149
153
|
|
150
154
|
module MultilineArgumentSnippet
|
151
155
|
def self.new(multiline_argument)
|
@@ -170,11 +174,10 @@ module Cucumber
|
|
170
174
|
|
171
175
|
class DocString
|
172
176
|
def append_block_parameter_to(array)
|
173
|
-
array << '
|
177
|
+
array << 'doc_string'
|
174
178
|
end
|
175
179
|
|
176
|
-
def append_comment_to(string)
|
177
|
-
end
|
180
|
+
def append_comment_to(string); end
|
178
181
|
end
|
179
182
|
|
180
183
|
class DataTable
|
@@ -192,11 +195,9 @@ module Cucumber
|
|
192
195
|
end
|
193
196
|
|
194
197
|
class None
|
195
|
-
def append_block_parameter_to(array)
|
196
|
-
end
|
198
|
+
def append_block_parameter_to(array); end
|
197
199
|
|
198
|
-
def append_comment_to(string)
|
199
|
-
end
|
200
|
+
def append_comment_to(string); end
|
200
201
|
end
|
201
202
|
end
|
202
203
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'cucumber/step_match'
|
4
|
-
require 'cucumber/step_argument'
|
5
4
|
require 'cucumber/core_ext/string'
|
6
5
|
require 'cucumber/glue/invoke_in_world'
|
7
6
|
|
@@ -25,9 +24,9 @@ module Cucumber
|
|
25
24
|
end
|
26
25
|
|
27
26
|
class << self
|
28
|
-
def new(registry, string_or_regexp, proc_or_sym, options)
|
27
|
+
def new(id, registry, string_or_regexp, proc_or_sym, options)
|
29
28
|
raise MissingProc if proc_or_sym.nil?
|
30
|
-
super registry, registry.create_expression(string_or_regexp), create_proc(proc_or_sym, options)
|
29
|
+
super id, registry, registry.create_expression(string_or_regexp), create_proc(proc_or_sym, options)
|
31
30
|
end
|
32
31
|
|
33
32
|
private
|
@@ -44,33 +43,59 @@ module Cucumber
|
|
44
43
|
end
|
45
44
|
|
46
45
|
def patch_location_onto(block)
|
47
|
-
location = Core::
|
46
|
+
location = Core::Test::Location.of_caller(5)
|
48
47
|
block.define_singleton_method(:source_location) { [location.file, location.line] }
|
49
48
|
block
|
50
49
|
end
|
51
50
|
|
52
51
|
def parse_target_proc_from(options)
|
53
|
-
return
|
52
|
+
return -> { self } unless options.key?(:on)
|
54
53
|
target = options[:on]
|
55
54
|
case target
|
56
55
|
when Proc
|
57
56
|
target
|
58
57
|
when Symbol
|
59
|
-
|
58
|
+
-> { send(target) }
|
60
59
|
else
|
61
|
-
|
60
|
+
-> { raise ArgumentError, 'Target must be a symbol or a proc' }
|
62
61
|
end
|
63
62
|
end
|
64
63
|
end
|
65
64
|
|
66
|
-
attr_reader :expression, :registry
|
65
|
+
attr_reader :id, :expression, :registry
|
67
66
|
|
68
|
-
def initialize(registry, expression, proc)
|
67
|
+
def initialize(id, registry, expression, proc)
|
69
68
|
raise 'No regexp' if expression.is_a?(Regexp)
|
70
|
-
@
|
69
|
+
@id = id
|
70
|
+
@registry = registry
|
71
|
+
@expression = expression
|
72
|
+
@proc = proc
|
71
73
|
# @registry.available_step_definition(regexp_source, location)
|
72
74
|
end
|
73
75
|
|
76
|
+
def to_envelope
|
77
|
+
Cucumber::Messages::Envelope.new(
|
78
|
+
step_definition: Cucumber::Messages::StepDefinition.new(
|
79
|
+
id: id,
|
80
|
+
pattern: Cucumber::Messages::StepDefinition::StepDefinitionPattern.new(
|
81
|
+
source: expression.source.to_s,
|
82
|
+
type: expression_type
|
83
|
+
),
|
84
|
+
source_reference: Cucumber::Messages::SourceReference.new(
|
85
|
+
uri: location.file,
|
86
|
+
location: Cucumber::Messages::Location.new(
|
87
|
+
line: location.lines.first
|
88
|
+
)
|
89
|
+
)
|
90
|
+
)
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
def expression_type
|
95
|
+
return Cucumber::Messages::StepDefinition::StepDefinitionPattern::StepDefinitionPatternType::CUCUMBER_EXPRESSION if expression.is_a?(CucumberExpressions::CucumberExpression)
|
96
|
+
Cucumber::Messages::StepDefinition::StepDefinitionPattern::StepDefinitionPatternType::REGULAR_EXPRESSION
|
97
|
+
end
|
98
|
+
|
74
99
|
# @api private
|
75
100
|
def to_hash
|
76
101
|
type = expression.is_a?(CucumberExpressions::RegularExpression) ? 'regular expression' : 'cucumber expression'
|
@@ -92,8 +117,8 @@ module Cucumber
|
|
92
117
|
end
|
93
118
|
|
94
119
|
# @api private
|
95
|
-
def ==(
|
96
|
-
expression.source ==
|
120
|
+
def ==(other)
|
121
|
+
expression.source == other.expression.source
|
97
122
|
end
|
98
123
|
|
99
124
|
# @api private
|
@@ -106,12 +131,10 @@ module Cucumber
|
|
106
131
|
# @api private
|
107
132
|
# TODO: inline this and step definition just be a value object
|
108
133
|
def invoke(args)
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
raise e
|
114
|
-
end
|
134
|
+
InvokeInWorld.cucumber_instance_exec_in(@registry.current_world, true, @expression.to_s, *args, &@proc)
|
135
|
+
rescue ArityMismatchError => e
|
136
|
+
e.backtrace.unshift(backtrace_line)
|
137
|
+
raise e
|
115
138
|
end
|
116
139
|
|
117
140
|
# @api private
|
@@ -131,7 +154,7 @@ module Cucumber
|
|
131
154
|
|
132
155
|
# The source location where the step definition can be found
|
133
156
|
def location
|
134
|
-
@location ||= Cucumber::Core::
|
157
|
+
@location ||= Cucumber::Core::Test::Location.from_source_location(*@proc.source_location)
|
135
158
|
end
|
136
159
|
|
137
160
|
# @api private
|