cucumber 8.0.0.rc.1 → 9.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://vshymanskyy.github.io/StandWithUkraine)
|
7
8
|
[![OpenCollective](https://opencollective.com/cucumber/backers/badge.svg)](https://opencollective.com/cucumber)
|
8
9
|
[![OpenCollective](https://opencollective.com/cucumber/sponsors/badge.svg)](https://opencollective.com/cucumber)
|
9
10
|
[![pull requests](https://oselvar.com/api/badge?label=pull%20requests&csvUrl=https%3A%2F%2Fraw.githubusercontent.com%2Fcucumber%2Foselvar-github-metrics%2Fmain%2Fdata%2Fcucumber%2Fcucumber-ruby%2FpullRequests.csv)](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)
|