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
@@ -2,10 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'cucumber/formatter/progress'
|
4
4
|
require 'cucumber/step_definition_light'
|
5
|
+
require 'cucumber/formatter/console'
|
5
6
|
|
6
7
|
module Cucumber
|
7
8
|
module Formatter
|
8
9
|
class Usage < Progress
|
10
|
+
include Console
|
9
11
|
class StepDefKey < StepDefinitionLight
|
10
12
|
attr_accessor :mean_duration, :status
|
11
13
|
end
|
@@ -17,7 +19,7 @@ module Cucumber
|
|
17
19
|
@matches = {}
|
18
20
|
config.on_event :step_activated do |event|
|
19
21
|
test_step, step_match = *event.attributes
|
20
|
-
@matches[test_step.
|
22
|
+
@matches[test_step.to_s] = step_match
|
21
23
|
end
|
22
24
|
config.on_event :step_definition_registered, &method(:on_step_definition_registered)
|
23
25
|
end
|
@@ -28,25 +30,26 @@ module Cucumber
|
|
28
30
|
end
|
29
31
|
|
30
32
|
def on_step_match(event)
|
31
|
-
@matches[event.test_step.
|
33
|
+
@matches[event.test_step.to_s] = event.step_match
|
32
34
|
super
|
33
35
|
end
|
34
36
|
|
35
37
|
def on_test_step_finished(event)
|
36
|
-
return if
|
38
|
+
return if event.test_step.hook?
|
37
39
|
|
38
40
|
test_step = event.test_step
|
39
41
|
result = event.result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
|
40
|
-
step_match = @matches[test_step.
|
42
|
+
step_match = @matches[test_step.to_s]
|
41
43
|
|
42
44
|
unless step_match.nil?
|
43
45
|
step_definition = step_match.step_definition
|
44
46
|
stepdef_key = StepDefKey.new(step_definition.expression.to_s, step_definition.location)
|
45
47
|
unless @stepdef_to_match[stepdef_key].map { |key| key[:location] }.include? test_step.location
|
46
48
|
duration = DurationExtractor.new(result).result_duration
|
49
|
+
keyword = @ast_lookup.step_source(test_step).step.keyword
|
47
50
|
|
48
51
|
@stepdef_to_match[stepdef_key] << {
|
49
|
-
keyword:
|
52
|
+
keyword: keyword,
|
50
53
|
step_match: step_match,
|
51
54
|
status: result.to_sym,
|
52
55
|
location: test_step.location,
|
@@ -64,9 +67,9 @@ module Cucumber
|
|
64
67
|
aggregate_info
|
65
68
|
|
66
69
|
keys = if config.dry_run?
|
67
|
-
@stepdef_to_match.keys.
|
70
|
+
@stepdef_to_match.keys.sort_by(&:regexp_source)
|
68
71
|
else
|
69
|
-
@stepdef_to_match.keys.
|
72
|
+
@stepdef_to_match.keys.sort_by(&:mean_duration).reverse
|
70
73
|
end
|
71
74
|
|
72
75
|
keys.each do |stepdef_key|
|
@@ -75,7 +78,7 @@ module Cucumber
|
|
75
78
|
if @stepdef_to_match[stepdef_key].any?
|
76
79
|
print_steps(stepdef_key)
|
77
80
|
else
|
78
|
-
@io.puts(
|
81
|
+
@io.puts(" #{format_string('NOT MATCHED BY ANY STEPS', :failed)}")
|
79
82
|
end
|
80
83
|
end
|
81
84
|
@io.puts
|
@@ -83,11 +86,11 @@ module Cucumber
|
|
83
86
|
end
|
84
87
|
|
85
88
|
def print_step_definition(stepdef_key)
|
86
|
-
@io.print format_string(format('
|
89
|
+
@io.print "#{format_string(format('%<duration>.7f', duration: stepdef_key.mean_duration), :skipped)} " unless config.dry_run?
|
87
90
|
@io.print format_string(stepdef_key.regexp_source, stepdef_key.status)
|
88
91
|
if config.source?
|
89
|
-
|
90
|
-
line_comment = " # #{stepdef_key.location}"
|
92
|
+
indent_amount = max_length - stepdef_key.regexp_source.unpack('U*').length
|
93
|
+
line_comment = indent(" # #{stepdef_key.location}", indent_amount)
|
91
94
|
@io.print(format_string(line_comment, :comment))
|
92
95
|
end
|
93
96
|
@io.puts
|
@@ -96,11 +99,11 @@ module Cucumber
|
|
96
99
|
def print_steps(stepdef_key)
|
97
100
|
@stepdef_to_match[stepdef_key].each do |step|
|
98
101
|
@io.print ' '
|
99
|
-
@io.print format_string(format('
|
102
|
+
@io.print "#{format_string(format('%<duration>.7f', duration: step[:duration]), :skipped)} " unless config.dry_run?
|
100
103
|
@io.print format_step(step[:keyword], step[:step_match], step[:status], nil)
|
101
104
|
if config.source?
|
102
|
-
|
103
|
-
line_comment = " # #{step[:location]}"
|
105
|
+
indent_amount = max_length - (step[:keyword].unpack('U*').length + step[:step_match].format_args.unpack('U*').length)
|
106
|
+
line_comment = indent(" # #{step[:location]}", indent_amount)
|
104
107
|
@io.print(format_string(line_comment, :comment))
|
105
108
|
end
|
106
109
|
@io.puts
|
@@ -135,7 +138,7 @@ module Cucumber
|
|
135
138
|
end
|
136
139
|
|
137
140
|
def worst_status(statuses)
|
138
|
-
[
|
141
|
+
%i[passed undefined pending skipped failed].find do |status|
|
139
142
|
statuses.include?(status)
|
140
143
|
end
|
141
144
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'gherkin
|
4
|
-
require 'gherkin/parser'
|
3
|
+
require 'gherkin'
|
5
4
|
require 'gherkin/dialect'
|
6
5
|
|
7
6
|
module Cucumber
|
@@ -12,15 +11,28 @@ module Cucumber
|
|
12
11
|
end
|
13
12
|
|
14
13
|
def parse(text)
|
15
|
-
|
16
|
-
|
17
|
-
gherkin_document = parser.parse(token_scanner)
|
14
|
+
gherkin_document = nil
|
15
|
+
messages = ::Gherkin.from_source('dummy', feature_header + text, gherkin_options)
|
18
16
|
|
19
|
-
|
17
|
+
messages.each do |message|
|
18
|
+
gherkin_document = message.gherkin_document.to_h unless message.gherkin_document.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
return if gherkin_document.nil?
|
22
|
+
|
23
|
+
gherkin_document[:feature][:children][0][:scenario][:steps][0][:data_table][:rows].each do |row|
|
20
24
|
@builder.row(row[:cells].map { |cell| cell[:value] })
|
21
25
|
end
|
22
26
|
end
|
23
27
|
|
28
|
+
def gherkin_options
|
29
|
+
{
|
30
|
+
include_source: false,
|
31
|
+
include_gherkin_document: true,
|
32
|
+
include_pickles: false
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
24
36
|
def feature_header
|
25
37
|
dialect = ::Gherkin::Dialect.for('en')
|
26
38
|
%(#{dialect.feature_keywords[0]}:
|
@@ -51,23 +51,21 @@ module Cucumber
|
|
51
51
|
'white' => "\e[37m",
|
52
52
|
'grey' => "\e[90m",
|
53
53
|
'bold' => "\e[1m"
|
54
|
-
}
|
54
|
+
}.freeze
|
55
55
|
|
56
56
|
ALIASES = Hash.new do |h, k|
|
57
|
-
if k.to_s =~ /(.*)_arg/
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
'tag' => 'cyan'
|
70
|
-
})
|
57
|
+
"#{h[Regexp.last_match(1)]},bold" if k.to_s =~ /(.*)_arg/
|
58
|
+
end.merge(
|
59
|
+
'undefined' => 'yellow',
|
60
|
+
'pending' => 'yellow',
|
61
|
+
'executing' => 'grey',
|
62
|
+
'failed' => 'red',
|
63
|
+
'passed' => 'green',
|
64
|
+
'outline' => 'cyan',
|
65
|
+
'skipped' => 'cyan',
|
66
|
+
'comments' => 'grey',
|
67
|
+
'tag' => 'cyan'
|
68
|
+
)
|
71
69
|
|
72
70
|
if ENV['GHERKIN_COLORS'] # Example: export GHERKIN_COLORS="passed=red:failed=yellow"
|
73
71
|
ENV['GHERKIN_COLORS'].split(':').each do |pair|
|
@@ -76,7 +74,7 @@ module Cucumber
|
|
76
74
|
end
|
77
75
|
end
|
78
76
|
|
79
|
-
ALIASES.
|
77
|
+
ALIASES.each_key do |key|
|
80
78
|
define_method(key) do
|
81
79
|
ALIASES[key].split(',').map { |color| COLORS[color] }.join('')
|
82
80
|
end
|
@@ -93,8 +91,6 @@ module Cucumber
|
|
93
91
|
def up(n)
|
94
92
|
"\e[#{n}A"
|
95
93
|
end
|
96
|
-
|
97
|
-
extend self
|
98
94
|
end
|
99
95
|
end
|
100
96
|
end
|
@@ -10,8 +10,8 @@ module Cucumber
|
|
10
10
|
# * \ becomes \\
|
11
11
|
#
|
12
12
|
# This is used in the pretty formatter.
|
13
|
-
def escape_cell(
|
14
|
-
|
13
|
+
def escape_cell(sym)
|
14
|
+
sym.gsub(/\\(?!\|)/, '\\\\\\\\').gsub(/\n/, '\\n').gsub(/\|/, '\\|')
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -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_h 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
@@ -19,8 +19,8 @@ module Cucumber
|
|
19
19
|
@rb_language.build_rb_world_factory(world_modules, namespaced_world_modules, proc)
|
20
20
|
end
|
21
21
|
|
22
|
-
def register_rb_hook(phase, tag_names, proc)
|
23
|
-
@rb_language.register_rb_hook(phase, tag_names, proc)
|
22
|
+
def register_rb_hook(phase, tag_names, proc, name: nil)
|
23
|
+
@rb_language.register_rb_hook(phase, tag_names, proc, name: name)
|
24
24
|
end
|
25
25
|
|
26
26
|
def define_parameter_type(parameter_type)
|
@@ -62,14 +62,14 @@ module Cucumber
|
|
62
62
|
|
63
63
|
# Registers a proc that will run before each Scenario. You can register as many
|
64
64
|
# as you want (typically from ruby scripts under <tt>support/hooks.rb</tt>).
|
65
|
-
def Before(*tag_expressions, &proc)
|
66
|
-
Dsl.register_rb_hook('before', tag_expressions, proc)
|
65
|
+
def Before(*tag_expressions, name: nil, &proc)
|
66
|
+
Dsl.register_rb_hook('before', tag_expressions, proc, name: name)
|
67
67
|
end
|
68
68
|
|
69
69
|
# Registers a proc that will run after each Scenario. You can register as many
|
70
70
|
# as you want (typically from ruby scripts under <tt>support/hooks.rb</tt>).
|
71
|
-
def After(*tag_expressions, &proc)
|
72
|
-
Dsl.register_rb_hook('after', tag_expressions, proc)
|
71
|
+
def After(*tag_expressions, name: nil, &proc)
|
72
|
+
Dsl.register_rb_hook('after', tag_expressions, proc, name: name)
|
73
73
|
end
|
74
74
|
|
75
75
|
# Registers a proc that will be wrapped around each scenario. The proc
|
@@ -77,14 +77,14 @@ module Cucumber
|
|
77
77
|
# argument (but passed as a regular argument, since blocks cannot accept
|
78
78
|
# blocks in 1.8), on which it should call the .call method. You can register
|
79
79
|
# as many as you want (typically from ruby scripts under <tt>support/hooks.rb</tt>).
|
80
|
-
def Around(*tag_expressions, &proc)
|
81
|
-
Dsl.register_rb_hook('around', tag_expressions, proc)
|
80
|
+
def Around(*tag_expressions, name: nil, &proc)
|
81
|
+
Dsl.register_rb_hook('around', tag_expressions, proc, name: name)
|
82
82
|
end
|
83
83
|
|
84
84
|
# Registers a proc that will run after each Step. You can register as
|
85
85
|
# as you want (typically from ruby scripts under <tt>support/hooks.rb</tt>).
|
86
|
-
def AfterStep(*tag_expressions, &proc)
|
87
|
-
Dsl.register_rb_hook('after_step', tag_expressions, proc)
|
86
|
+
def AfterStep(*tag_expressions, name: nil, &proc)
|
87
|
+
Dsl.register_rb_hook('after_step', tag_expressions, proc, name: name)
|
88
88
|
end
|
89
89
|
|
90
90
|
def ParameterType(options)
|
@@ -107,10 +107,21 @@ module Cucumber
|
|
107
107
|
value.nil? ? default : value
|
108
108
|
end
|
109
109
|
|
110
|
-
# Registers a proc that will run after Cucumber is configured
|
111
|
-
|
112
|
-
|
113
|
-
|
110
|
+
# Registers a proc that will run after Cucumber is configured in order to install an external plugin.
|
111
|
+
def InstallPlugin(name: nil, &proc)
|
112
|
+
Dsl.register_rb_hook('install_plugin', [], proc, name: name)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Registers a proc that will run before the execution of the scenarios.
|
116
|
+
# Use it for your final set-ups
|
117
|
+
def BeforeAll(name: nil, &proc)
|
118
|
+
Dsl.register_rb_hook('before_all', [], proc, name: name)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Registers a proc that will run after the execution of the scenarios.
|
122
|
+
# Use it for your final clean-ups
|
123
|
+
def AfterAll(name: nil, &proc)
|
124
|
+
Dsl.register_rb_hook('after_all', [], proc, name: name)
|
114
125
|
end
|
115
126
|
|
116
127
|
# Registers a new Ruby StepDefinition. This method is aliased
|
@@ -136,5 +147,8 @@ module Cucumber
|
|
136
147
|
end
|
137
148
|
end
|
138
149
|
|
139
|
-
#
|
150
|
+
# rubocop:disable Style/MixinUsage
|
151
|
+
# This "should" always be present, because it allows users to write `Before` and `After`
|
152
|
+
# See. https://github.com/cucumber/cucumber-ruby/pull/1566#discussion_r683235396
|
140
153
|
extend(Cucumber::Glue::Dsl)
|
154
|
+
# rubocop:enable Style/MixinUsage
|
data/lib/cucumber/glue/hook.rb
CHANGED
@@ -6,14 +6,16 @@ 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, :name
|
10
10
|
|
11
|
-
def initialize(registry, tag_expressions, proc)
|
11
|
+
def initialize(id, registry, tag_expressions, proc, name: nil)
|
12
|
+
@id = id
|
12
13
|
@registry = registry
|
13
|
-
@
|
14
|
+
@name = name
|
15
|
+
@tag_expressions = sanitize_tag_expressions(tag_expressions)
|
14
16
|
@proc = proc
|
15
|
-
@location = Cucumber::Core::
|
16
|
-
|
17
|
+
@location = Cucumber::Core::Test::Location.from_source_location(*@proc.source_location)
|
18
|
+
fail_for_old_style_tag_expressions(@tag_expressions)
|
17
19
|
end
|
18
20
|
|
19
21
|
def invoke(pseudo_method, arguments, &block)
|
@@ -27,16 +29,40 @@ module Cucumber
|
|
27
29
|
)
|
28
30
|
end
|
29
31
|
|
32
|
+
def to_envelope
|
33
|
+
Cucumber::Messages::Envelope.new(
|
34
|
+
hook: Cucumber::Messages::Hook.new(
|
35
|
+
id: id,
|
36
|
+
name: name,
|
37
|
+
tag_expression: tag_expressions.empty? ? nil : tag_expressions.join(' '),
|
38
|
+
source_reference: Cucumber::Messages::SourceReference.new(
|
39
|
+
uri: location.file,
|
40
|
+
location: Cucumber::Messages::Location.new(
|
41
|
+
line: location.lines.first
|
42
|
+
)
|
43
|
+
)
|
44
|
+
)
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
30
48
|
private
|
31
49
|
|
32
|
-
def
|
50
|
+
def sanitize_tag_expressions(tag_expressions)
|
51
|
+
# TODO: remove when '~@no-clobber' has been changed to 'not @no-clobber' in aruba
|
52
|
+
tag_expressions.map { |tag_expression| tag_expression == '~@no-clobber' ? 'not @no-clobber' : tag_expression }
|
53
|
+
end
|
54
|
+
|
55
|
+
def fail_for_old_style_tag_expressions(tag_expressions)
|
33
56
|
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.")
|
57
|
+
if tag_expression.include?('~')
|
58
|
+
raise("Found tagged hook with '#{tag_expression}'." \
|
59
|
+
"'~@tag' is no longer supported, use 'not @tag' instead.")
|
39
60
|
end
|
61
|
+
|
62
|
+
next unless tag_expression.include?(',')
|
63
|
+
|
64
|
+
warn("Found tagged hook with '#{tag_expression}'." \
|
65
|
+
"'@tag1,@tag2' is no longer supported, use '@tag or @tag2' instead.")
|
40
66
|
end
|
41
67
|
end
|
42
68
|
end
|
@@ -11,16 +11,16 @@ 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
|
-
replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET
|
16
|
-
backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method
|
14
|
+
return unless instance_exec_pos
|
17
15
|
|
18
|
-
|
19
|
-
|
16
|
+
replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET
|
17
|
+
backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
depth = backtrace.count { |line| line == instance_exec_invocation_line }
|
20
|
+
end_pos = depth > 1 ? instance_exec_pos : -1
|
21
|
+
|
22
|
+
backtrace[replacement_line + 1..end_pos] = nil
|
23
|
+
backtrace.compact!
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.cucumber_instance_exec_in(world, check_arity, pseudo_method, *args, &block)
|
@@ -28,12 +28,10 @@ module Cucumber
|
|
28
28
|
if check_arity && !cucumber_compatible_arity?(args, block)
|
29
29
|
world.instance_exec do
|
30
30
|
ari = block.arity
|
31
|
-
ari = ari < 0 ?
|
31
|
+
ari = ari < 0 ? "#{ari.abs - 1}+" : ari
|
32
32
|
s1 = ari == 1 ? '' : 's'
|
33
33
|
s2 = args.length == 1 ? '' : 's'
|
34
|
-
raise ArityMismatchError.
|
35
|
-
"Your block takes #{ari} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
|
36
|
-
)
|
34
|
+
raise ArityMismatchError, "Your block takes #{ari} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
|
37
35
|
end
|
38
36
|
else
|
39
37
|
world.instance_exec(*args, &block)
|
@@ -43,20 +41,17 @@ module Cucumber
|
|
43
41
|
|
44
42
|
def self.cucumber_compatible_arity?(args, block)
|
45
43
|
return true if block.arity == args.length
|
46
|
-
if block.arity
|
47
|
-
|
48
|
-
end
|
44
|
+
return true if block.arity.negative? && args.length >= (block.arity.abs - 1)
|
45
|
+
|
49
46
|
false
|
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,9 @@
|
|
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'
|
6
|
+
require 'mime/types'
|
5
7
|
|
6
8
|
module Cucumber
|
7
9
|
module Glue
|
@@ -24,7 +26,7 @@ module Cucumber
|
|
24
26
|
# @example Passing a multiline string
|
25
27
|
# step "the email should contain:", "Dear sir,\nYou've won a prize!\n"
|
26
28
|
# @param [String] name The name of the step
|
27
|
-
# @param [String,Cucumber::
|
29
|
+
# @param [String,Cucumber::Test::DocString,Cucumber::Ast::Table] multiline_argument
|
28
30
|
def step(name, raw_multiline_arg = nil)
|
29
31
|
super
|
30
32
|
end
|
@@ -67,20 +69,8 @@ module Cucumber
|
|
67
69
|
# %w{ CUC-101 Peeler 22 }
|
68
70
|
# ])
|
69
71
|
#
|
70
|
-
def table(text_or_table
|
71
|
-
|
72
|
-
MultilineArgument::DataTable.from(text_or_table, location)
|
73
|
-
end
|
74
|
-
|
75
|
-
# Print a message to the output.
|
76
|
-
#
|
77
|
-
# @note Cucumber might surprise you with the behaviour of this method. Instead
|
78
|
-
# of sending the output directly to STDOUT, Cucumber will intercept and cache
|
79
|
-
# the message until the current step has finished, and then display it.
|
80
|
-
#
|
81
|
-
# If you'd prefer to see the message immediately, call {Kernel.puts} instead.
|
82
|
-
def puts(*messages)
|
83
|
-
super
|
72
|
+
def table(text_or_table)
|
73
|
+
MultilineArgument::DataTable.from(text_or_table)
|
84
74
|
end
|
85
75
|
|
86
76
|
# Pause the tests and ask the operator for input
|
@@ -88,23 +78,37 @@ module Cucumber
|
|
88
78
|
super
|
89
79
|
end
|
90
80
|
|
91
|
-
|
92
|
-
|
81
|
+
def log(*messages)
|
82
|
+
messages.each { |message| attach(message.to_s.dup, 'text/x.cucumber.log+plain') }
|
83
|
+
end
|
84
|
+
|
85
|
+
# Attach a file to the output
|
86
|
+
# @param file [string|io] the file to attach.
|
87
|
+
# It can be a string containing the file content itself,
|
88
|
+
# the file path, or an IO ready to be read.
|
89
|
+
# @param media_type [string] the media type. If file is a valid path,
|
90
|
+
# media_type can be ommitted, it will then be inferred from the file name.
|
91
|
+
def attach(file, media_type = nil)
|
92
|
+
return super unless File.file?(file)
|
93
|
+
|
94
|
+
content = File.read(file, mode: 'rb')
|
95
|
+
media_type = MIME::Types.type_for(file).first if media_type.nil?
|
96
|
+
|
97
|
+
super(content, media_type.to_s)
|
98
|
+
rescue StandardError
|
93
99
|
super
|
94
100
|
end
|
95
101
|
|
96
102
|
# Mark the matched step as pending.
|
97
103
|
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
|
104
|
+
raise Pending, message unless block_given?
|
105
|
+
|
106
|
+
begin
|
107
|
+
yield
|
108
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
106
109
|
raise Pending, message
|
107
110
|
end
|
111
|
+
raise Pending, "Expected pending '#{message}' to fail. No Error was raised. No longer pending?"
|
108
112
|
end
|
109
113
|
|
110
114
|
# Skips this step and the remaining steps in the scenario
|
@@ -123,8 +127,8 @@ module Cucumber
|
|
123
127
|
end
|
124
128
|
|
125
129
|
# Dynamially generate the API module, closuring the dependencies
|
126
|
-
def self.for(runtime, language)
|
127
|
-
Module.new do
|
130
|
+
def self.for(runtime, language) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
131
|
+
Module.new do # rubocop:disable Metrics/BlockLength
|
128
132
|
def self.extended(object)
|
129
133
|
# wrap the dynamically generated module so that we can document the methods
|
130
134
|
# for yardoc, which doesn't like define_method.
|
@@ -134,37 +138,26 @@ module Cucumber
|
|
134
138
|
# TODO: pass these in when building the module, instead of mutating them later
|
135
139
|
# Extend the World with user-defined modules
|
136
140
|
def add_modules!(world_modules, namespaced_world_modules)
|
137
|
-
add_world_modules!(world_modules)
|
138
|
-
add_namespaced_modules!(namespaced_world_modules)
|
141
|
+
add_world_modules!(world_modules) if world_modules.any?
|
142
|
+
add_namespaced_modules!(namespaced_world_modules) if namespaced_world_modules.any?
|
139
143
|
end
|
140
144
|
|
141
145
|
define_method(:step) do |name, raw_multiline_arg = nil|
|
142
|
-
location = Core::
|
146
|
+
location = Core::Test::Location.of_caller
|
143
147
|
runtime.invoke_dynamic_step(name, MultilineArgument.from(raw_multiline_arg, location))
|
144
148
|
end
|
145
149
|
|
146
150
|
define_method(:steps) do |steps_text|
|
147
|
-
location = Core::
|
151
|
+
location = Core::Test::Location.of_caller
|
148
152
|
runtime.invoke_dynamic_steps(steps_text, language, location)
|
149
153
|
end
|
150
154
|
|
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
155
|
define_method(:ask) do |question, timeout_seconds = 60|
|
163
156
|
runtime.ask(question, timeout_seconds)
|
164
157
|
end
|
165
158
|
|
166
|
-
define_method(:
|
167
|
-
runtime.
|
159
|
+
define_method(:attach) do |file, media_type|
|
160
|
+
runtime.attach(file, media_type)
|
168
161
|
end
|
169
162
|
|
170
163
|
# Prints the list of modules that are included in the World
|
@@ -174,7 +167,7 @@ module Cucumber
|
|
174
167
|
modules += included_modules
|
175
168
|
end
|
176
169
|
modules << stringify_namespaced_modules
|
177
|
-
format('
|
170
|
+
format('#<%<modules>s:0x%<object_id>x>', modules: modules.join('+'), object_id: object_id)
|
178
171
|
end
|
179
172
|
|
180
173
|
private
|
@@ -192,14 +185,13 @@ module Cucumber
|
|
192
185
|
modules.each do |namespace, world_modules|
|
193
186
|
world_modules.each do |world_module|
|
194
187
|
variable_name = "@__#{namespace}_world"
|
188
|
+
inner_world = instance_variable_get(variable_name) || Object.new
|
189
|
+
|
190
|
+
instance_variable_set(
|
191
|
+
variable_name,
|
192
|
+
inner_world.extend(world_module)
|
193
|
+
)
|
195
194
|
|
196
|
-
inner_world = if self.class.respond_to?(namespace)
|
197
|
-
instance_variable_get(variable_name)
|
198
|
-
else
|
199
|
-
Object.new
|
200
|
-
end
|
201
|
-
instance_variable_set(variable_name,
|
202
|
-
inner_world.extend(world_module))
|
203
195
|
self.class.send(:define_method, namespace) do
|
204
196
|
instance_variable_get(variable_name)
|
205
197
|
end
|
@@ -209,6 +201,8 @@ module Cucumber
|
|
209
201
|
|
210
202
|
# @private
|
211
203
|
def stringify_namespaced_modules
|
204
|
+
return '' if @__namespaced_modules.nil?
|
205
|
+
|
212
206
|
@__namespaced_modules.map { |k, v| "#{v.join(',')} (as #{k})" }.join('+')
|
213
207
|
end
|
214
208
|
end
|