cucumber 8.0.0.rc.1 → 9.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 → CHANGELOG.old.md} +655 -493
- data/README.md +4 -3
- data/lib/cucumber/cli/options.rb +14 -3
- data/lib/cucumber/configuration.rb +6 -1
- data/lib/cucumber/filters/retry.rb +20 -1
- data/lib/cucumber/formatter/ansicolor.rb +1 -1
- data/lib/cucumber/formatter/backtrace_filter.rb +1 -0
- data/lib/cucumber/formatter/io.rb +2 -2
- data/lib/cucumber/glue/dsl.rb +16 -16
- data/lib/cucumber/glue/hook.rb +4 -2
- data/lib/cucumber/glue/proto_world.rb +2 -2
- data/lib/cucumber/glue/registry_and_more.rb +2 -2
- data/lib/cucumber/platform.rb +1 -1
- data/lib/cucumber/rake/task.rb +11 -3
- metadata +89 -90
- data/lib/cucumber/version +0 -1
data/README.md
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
|
5
5
|
# Cucumber
|
6
6
|
|
7
|
+
[](https://vshymanskyy.github.io/StandWithUkraine)
|
7
8
|
[](https://opencollective.com/cucumber)
|
8
9
|
[](https://opencollective.com/cucumber)
|
9
10
|
[](https://oselvar.com/github/cucumber/oselvar-github-metrics/main/cucumber/cucumber-ruby)
|
@@ -50,13 +51,13 @@ Later in this document, bundler is considered being used so all commands are usi
|
|
50
51
|
|
51
52
|
### Supported platforms
|
52
53
|
|
54
|
+
- Ruby 3.2
|
53
55
|
- Ruby 3.1
|
54
56
|
- Ruby 3.0
|
55
57
|
- Ruby 2.7
|
56
|
-
-
|
58
|
+
- TruffleRuby 22.0.0+
|
57
59
|
- JRuby (with [some limitations](https://github.com/cucumber/cucumber-ruby/blob/main/docs/jruby-limitations.md))
|
58
|
-
- 9.
|
59
|
-
be found in the [PR#1571](https://github.com/cucumber/cucumber-ruby/pull/1571).)
|
60
|
+
- 9.4
|
60
61
|
|
61
62
|
### Ruby on Rails
|
62
63
|
|
data/lib/cucumber/cli/options.rb
CHANGED
@@ -56,11 +56,12 @@ module Cucumber
|
|
56
56
|
NO_PROFILE_LONG_FLAG = '--no-profile'.freeze
|
57
57
|
FAIL_FAST_FLAG = '--fail-fast'.freeze
|
58
58
|
RETRY_FLAG = '--retry'.freeze
|
59
|
+
RETRY_TOTAL_FLAG = '--retry-total'.freeze
|
59
60
|
OPTIONS_WITH_ARGS = [
|
60
61
|
'-r', '--require', '--i18n-keywords', '-f', '--format', '-o',
|
61
62
|
'--out', '-t', '--tags', '-n', '--name', '-e', '--exclude',
|
62
|
-
PROFILE_SHORT_FLAG, PROFILE_LONG_FLAG, RETRY_FLAG,
|
63
|
-
'--lines', '--port', '-I', '--snippet-type'
|
63
|
+
PROFILE_SHORT_FLAG, PROFILE_LONG_FLAG, RETRY_FLAG, RETRY_TOTAL_FLAG,
|
64
|
+
'-l', '--lines', '--port', '-I', '--snippet-type'
|
64
65
|
].freeze
|
65
66
|
ORDER_TYPES = %w[defined random].freeze
|
66
67
|
TAG_LIMIT_MATCHER = /(?<tag_name>@\w+):(?<limit>\d+)/x
|
@@ -108,6 +109,7 @@ module Cucumber
|
|
108
109
|
opts.on('-j DIR', '--jars DIR', 'Load all the jars under DIR') { |jars| load_jars(jars) } if Cucumber::JRUBY
|
109
110
|
|
110
111
|
opts.on("#{RETRY_FLAG} ATTEMPTS", *retry_msg) { |v| set_option :retry, v.to_i }
|
112
|
+
opts.on("#{RETRY_TOTAL_FLAG} TESTS", *retry_total_msg) { |v| set_option :retry_total, v.to_i }
|
111
113
|
opts.on('--i18n-languages', *i18n_languages_msg) { list_languages_and_exit }
|
112
114
|
opts.on('--i18n-keywords LANG', *i18n_keywords_msg) { |lang| language lang }
|
113
115
|
opts.on(FAIL_FAST_FLAG, 'Exit immediately following the first failing scenario') { set_option :fail_fast }
|
@@ -271,6 +273,13 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
271
273
|
['Specify the number of times to retry failing tests (default: 0)']
|
272
274
|
end
|
273
275
|
|
276
|
+
def retry_total_msg
|
277
|
+
[
|
278
|
+
'The total number of failing test after which retrying of tests is suspended.',
|
279
|
+
'Example: --retry-total 10 -> Will stop retrying tests after 10 failing tests.'
|
280
|
+
]
|
281
|
+
end
|
282
|
+
|
274
283
|
def name_msg
|
275
284
|
[
|
276
285
|
'Only execute the feature elements which match part of the given name.',
|
@@ -543,6 +552,7 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
543
552
|
end
|
544
553
|
|
545
554
|
@options[:retry] = other_options[:retry] if @options[:retry].zero?
|
555
|
+
@options[:retry_total] = other_options[:retry_total] if @options[:retry_total].infinite?
|
546
556
|
|
547
557
|
self
|
548
558
|
end
|
@@ -616,7 +626,8 @@ Specify SEED to reproduce the shuffling from a previous run.
|
|
616
626
|
snippets: true,
|
617
627
|
source: true,
|
618
628
|
duration: true,
|
619
|
-
retry: 0
|
629
|
+
retry: 0,
|
630
|
+
retry_total: Float::INFINITY
|
620
631
|
}
|
621
632
|
end
|
622
633
|
end
|
@@ -78,6 +78,10 @@ module Cucumber
|
|
78
78
|
@options[:retry]
|
79
79
|
end
|
80
80
|
|
81
|
+
def retry_total_tests
|
82
|
+
@options[:retry_total]
|
83
|
+
end
|
84
|
+
|
81
85
|
def guess?
|
82
86
|
@options[:guess]
|
83
87
|
end
|
@@ -273,7 +277,8 @@ module Cucumber
|
|
273
277
|
snippets: true,
|
274
278
|
source: true,
|
275
279
|
duration: true,
|
276
|
-
event_bus: Cucumber::Events.make_event_bus
|
280
|
+
event_bus: Cucumber::Events.make_event_bus,
|
281
|
+
retry_total: Float::INFINITY
|
277
282
|
}
|
278
283
|
end
|
279
284
|
|
@@ -7,6 +7,11 @@ require 'cucumber/events'
|
|
7
7
|
module Cucumber
|
8
8
|
module Filters
|
9
9
|
class Retry < Core::Filter.new(:configuration)
|
10
|
+
def initialize(*_args)
|
11
|
+
super
|
12
|
+
@total_permanently_failed = 0
|
13
|
+
end
|
14
|
+
|
10
15
|
def test_case(test_case)
|
11
16
|
configuration.on_event(:test_case_finished) do |event|
|
12
17
|
next unless retry_required?(test_case, event)
|
@@ -21,7 +26,21 @@ module Cucumber
|
|
21
26
|
private
|
22
27
|
|
23
28
|
def retry_required?(test_case, event)
|
24
|
-
|
29
|
+
return false unless event.test_case == test_case
|
30
|
+
|
31
|
+
return false unless event.result.failed?
|
32
|
+
|
33
|
+
return false if @total_permanently_failed >= configuration.retry_total_tests
|
34
|
+
|
35
|
+
retry_required = test_case_counts[test_case] < configuration.retry_attempts
|
36
|
+
if retry_required
|
37
|
+
# retry test
|
38
|
+
true
|
39
|
+
else
|
40
|
+
# test failed after max. attempts
|
41
|
+
@total_permanently_failed += 1
|
42
|
+
false
|
43
|
+
end
|
25
44
|
end
|
26
45
|
|
27
46
|
def test_case_counts
|
@@ -22,6 +22,7 @@ module Cucumber
|
|
22
22
|
@backtrace_filters << RbConfig::CONFIG['rubylibdir'] if RbConfig::CONFIG['rubylibdir']
|
23
23
|
|
24
24
|
@backtrace_filters << 'org/jruby/' if ::Cucumber::JRUBY
|
25
|
+
@backtrace_filters << '<internal:' if RUBY_ENGINE == 'truffleruby'
|
25
26
|
|
26
27
|
BACKTRACE_FILTER_PATTERNS = Regexp.new(@backtrace_filters.join('|'))
|
27
28
|
|
@@ -61,7 +61,7 @@ module Cucumber
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def ensure_file(path, name)
|
64
|
-
raise "You *must* specify --out FILE for the #{name} formatter" unless
|
64
|
+
raise "You *must* specify --out FILE for the #{name} formatter" unless path.instance_of? String
|
65
65
|
raise "I can't write #{name} to a directory - it has to be a file" if File.directory?(path)
|
66
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
67
|
|
@@ -69,7 +69,7 @@ module Cucumber
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def ensure_dir(path, name)
|
72
|
-
raise "You *must* specify --out DIR for the #{name} formatter" unless
|
72
|
+
raise "You *must* specify --out DIR for the #{name} formatter" unless path.instance_of? String
|
73
73
|
raise "I can't write #{name} reports to a file - it has to be a directory" if File.file?(path)
|
74
74
|
|
75
75
|
FileUtils.mkdir_p(path) unless File.directory?(path)
|
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)
|
@@ -108,20 +108,20 @@ module Cucumber
|
|
108
108
|
end
|
109
109
|
|
110
110
|
# Registers a proc that will run after Cucumber is configured in order to install an external plugin.
|
111
|
-
def InstallPlugin(&proc)
|
112
|
-
Dsl.register_rb_hook('install_plugin', [], proc)
|
111
|
+
def InstallPlugin(name: nil, &proc)
|
112
|
+
Dsl.register_rb_hook('install_plugin', [], proc, name: name)
|
113
113
|
end
|
114
114
|
|
115
115
|
# Registers a proc that will run before the execution of the scenarios.
|
116
116
|
# Use it for your final set-ups
|
117
|
-
def BeforeAll(&proc)
|
118
|
-
Dsl.register_rb_hook('before_all', [], proc)
|
117
|
+
def BeforeAll(name: nil, &proc)
|
118
|
+
Dsl.register_rb_hook('before_all', [], proc, name: name)
|
119
119
|
end
|
120
120
|
|
121
121
|
# Registers a proc that will run after the execution of the scenarios.
|
122
122
|
# Use it for your final clean-ups
|
123
|
-
def AfterAll(&proc)
|
124
|
-
Dsl.register_rb_hook('after_all', [], proc)
|
123
|
+
def AfterAll(name: nil, &proc)
|
124
|
+
Dsl.register_rb_hook('after_all', [], proc, name: name)
|
125
125
|
end
|
126
126
|
|
127
127
|
# Registers a new Ruby StepDefinition. This method is aliased
|
data/lib/cucumber/glue/hook.rb
CHANGED
@@ -6,11 +6,12 @@ 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 :id, :tag_expressions, :location
|
9
|
+
attr_reader :id, :tag_expressions, :location, :name
|
10
10
|
|
11
|
-
def initialize(id, registry, tag_expressions, proc)
|
11
|
+
def initialize(id, registry, tag_expressions, proc, name: nil)
|
12
12
|
@id = id
|
13
13
|
@registry = registry
|
14
|
+
@name = name
|
14
15
|
@tag_expressions = sanitize_tag_expressions(tag_expressions)
|
15
16
|
@proc = proc
|
16
17
|
@location = Cucumber::Core::Test::Location.from_source_location(*@proc.source_location)
|
@@ -32,6 +33,7 @@ module Cucumber
|
|
32
33
|
Cucumber::Messages::Envelope.new(
|
33
34
|
hook: Cucumber::Messages::Hook.new(
|
34
35
|
id: id,
|
36
|
+
name: name,
|
35
37
|
tag_expression: tag_expressions.empty? ? nil : tag_expressions.join(' '),
|
36
38
|
source_reference: Cucumber::Messages::SourceReference.new(
|
37
39
|
uri: location.file,
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'cucumber/gherkin/formatter/ansi_escapes'
|
4
4
|
require 'cucumber/core/test/data_table'
|
5
5
|
require 'cucumber/deprecate'
|
6
|
-
require '
|
6
|
+
require 'mini_mime'
|
7
7
|
|
8
8
|
module Cucumber
|
9
9
|
module Glue
|
@@ -92,7 +92,7 @@ module Cucumber
|
|
92
92
|
return super unless File.file?(file)
|
93
93
|
|
94
94
|
content = File.read(file, mode: 'rb')
|
95
|
-
media_type =
|
95
|
+
media_type = MiniMime.lookup_by_filename(file)&.content_type if media_type.nil?
|
96
96
|
|
97
97
|
super(content, media_type.to_s)
|
98
98
|
rescue StandardError
|
@@ -72,8 +72,8 @@ module Cucumber
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
def register_rb_hook(phase, tag_expressions, proc)
|
76
|
-
hook = add_hook(phase, Hook.new(@configuration.id_generator.new_id, self, tag_expressions, proc))
|
75
|
+
def register_rb_hook(phase, tag_expressions, proc, name: nil)
|
76
|
+
hook = add_hook(phase, Hook.new(@configuration.id_generator.new_id, self, tag_expressions, proc, name: name))
|
77
77
|
@configuration.notify :envelope, hook.to_envelope
|
78
78
|
hook
|
79
79
|
end
|
data/lib/cucumber/platform.rb
CHANGED
@@ -7,7 +7,7 @@ require 'cucumber/core/platform'
|
|
7
7
|
|
8
8
|
module Cucumber
|
9
9
|
unless defined?(Cucumber::VERSION)
|
10
|
-
VERSION = File.read(File.expand_path('
|
10
|
+
VERSION = File.read(File.expand_path('../../VERSION', __dir__)).strip
|
11
11
|
BINARY = File.expand_path("#{File.dirname(__FILE__)}/../../bin/cucumber")
|
12
12
|
LIBDIR = File.expand_path("#{File.dirname(__FILE__)}/../../lib")
|
13
13
|
RAILS = defined?(Rails)
|
data/lib/cucumber/rake/task.rb
CHANGED
@@ -36,7 +36,7 @@ module Cucumber
|
|
36
36
|
attr_reader :args
|
37
37
|
|
38
38
|
def initialize(libs, cucumber_opts, feature_files)
|
39
|
-
raise 'libs must be an Array when running in-process' unless
|
39
|
+
raise 'libs must be an Array when running in-process' unless libs.instance_of? Array
|
40
40
|
|
41
41
|
libs.reverse_each { |lib| $LOAD_PATH.unshift(lib) }
|
42
42
|
@args = (
|
@@ -113,7 +113,15 @@ module Cucumber
|
|
113
113
|
attr_reader :cucumber_opts
|
114
114
|
|
115
115
|
def cucumber_opts=(opts) # :nodoc:
|
116
|
-
|
116
|
+
unless opts.instance_of? String
|
117
|
+
@cucumber_opts = opts
|
118
|
+
return
|
119
|
+
end
|
120
|
+
|
121
|
+
@cucumber_opts = opts.split(' ')
|
122
|
+
return if @cucumber_opts.length <= 1
|
123
|
+
|
124
|
+
$stderr.puts 'WARNING: consider using an array rather than a space-delimited string with cucumber_opts to avoid undesired behavior.'
|
117
125
|
end
|
118
126
|
|
119
127
|
# Whether or not to fork a new ruby interpreter. Defaults to true. You may gain
|
@@ -155,7 +163,7 @@ module Cucumber
|
|
155
163
|
end
|
156
164
|
|
157
165
|
def runner(_task_args = nil) # :nodoc:
|
158
|
-
cucumber_opts = [
|
166
|
+
cucumber_opts = [ENV['CUCUMBER_OPTS']&.split(/\s+/) || cucumber_opts_with_profile]
|
159
167
|
return ForkedCucumberRunner.new(libs, binary, cucumber_opts, bundler, feature_files) if fork
|
160
168
|
|
161
169
|
InProcessCucumberRunner.new(libs, cucumber_opts, feature_files)
|