rspec-rewind 0.1.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 +7 -0
- data/CHANGELOG.md +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +211 -0
- data/lib/rspec/rewind/attempt_runner.rb +46 -0
- data/lib/rspec/rewind/backoff.rb +64 -0
- data/lib/rspec/rewind/configuration.rb +125 -0
- data/lib/rspec/rewind/event.rb +24 -0
- data/lib/rspec/rewind/example_context.rb +27 -0
- data/lib/rspec/rewind/example_methods.rb +30 -0
- data/lib/rspec/rewind/example_state_resetter.rb +57 -0
- data/lib/rspec/rewind/flaky_reporter.rb +54 -0
- data/lib/rspec/rewind/flaky_transition.rb +26 -0
- data/lib/rspec/rewind/matcher_validation.rb +21 -0
- data/lib/rspec/rewind/retry_budget.rb +52 -0
- data/lib/rspec/rewind/retry_count_resolver.rb +54 -0
- data/lib/rspec/rewind/retry_decision.rb +70 -0
- data/lib/rspec/rewind/retry_delay_resolver.rb +49 -0
- data/lib/rspec/rewind/retry_event_builder.rb +29 -0
- data/lib/rspec/rewind/retry_gate.rb +43 -0
- data/lib/rspec/rewind/retry_loop.rb +72 -0
- data/lib/rspec/rewind/retry_notifier.rb +47 -0
- data/lib/rspec/rewind/retry_policy.rb +51 -0
- data/lib/rspec/rewind/retry_transition.rb +40 -0
- data/lib/rspec/rewind/runner.rb +36 -0
- data/lib/rspec/rewind/runner_components.rb +66 -0
- data/lib/rspec/rewind/runner_logger.rb +27 -0
- data/lib/rspec/rewind/version.rb +7 -0
- data/lib/rspec/rewind.rb +68 -0
- data/lib/rspec-rewind.rb +3 -0
- data/rspec-rewind.gemspec +38 -0
- data/sig/rspec/rewind.rbs +211 -0
- metadata +97 -0
data/lib/rspec/rewind.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec/core'
|
|
4
|
+
require 'time'
|
|
5
|
+
|
|
6
|
+
require_relative 'rewind/version'
|
|
7
|
+
require_relative 'rewind/backoff'
|
|
8
|
+
require_relative 'rewind/retry_budget'
|
|
9
|
+
require_relative 'rewind/flaky_reporter'
|
|
10
|
+
require_relative 'rewind/matcher_validation'
|
|
11
|
+
require_relative 'rewind/configuration'
|
|
12
|
+
require_relative 'rewind/example_context'
|
|
13
|
+
require_relative 'rewind/event'
|
|
14
|
+
require_relative 'rewind/retry_count_resolver'
|
|
15
|
+
require_relative 'rewind/retry_delay_resolver'
|
|
16
|
+
require_relative 'rewind/retry_event_builder'
|
|
17
|
+
require_relative 'rewind/retry_notifier'
|
|
18
|
+
require_relative 'rewind/flaky_transition'
|
|
19
|
+
require_relative 'rewind/attempt_runner'
|
|
20
|
+
require_relative 'rewind/retry_transition'
|
|
21
|
+
require_relative 'rewind/retry_gate'
|
|
22
|
+
require_relative 'rewind/retry_loop'
|
|
23
|
+
require_relative 'rewind/runner_logger'
|
|
24
|
+
require_relative 'rewind/runner_components'
|
|
25
|
+
require_relative 'rewind/example_state_resetter'
|
|
26
|
+
require_relative 'rewind/retry_policy'
|
|
27
|
+
require_relative 'rewind/retry_decision'
|
|
28
|
+
require_relative 'rewind/runner'
|
|
29
|
+
require_relative 'rewind/example_methods'
|
|
30
|
+
|
|
31
|
+
module RSpec
|
|
32
|
+
module Rewind
|
|
33
|
+
class << self
|
|
34
|
+
def configuration
|
|
35
|
+
@configuration ||= Configuration.new
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def configure
|
|
39
|
+
yield(configuration)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def reset_configuration!
|
|
43
|
+
@configuration = Configuration.new
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def install!
|
|
47
|
+
return if @installed
|
|
48
|
+
|
|
49
|
+
::RSpec::Core::Example.include(ExampleMethods)
|
|
50
|
+
::RSpec::Core::Example::Procsy.include(ExampleMethods) if defined?(::RSpec::Core::Example::Procsy)
|
|
51
|
+
|
|
52
|
+
::RSpec.configure do |config|
|
|
53
|
+
config.around(:each) do |example|
|
|
54
|
+
if example.metadata[:rewind] == false
|
|
55
|
+
example.run
|
|
56
|
+
else
|
|
57
|
+
example.run_with_rewind
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
@installed = true
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
RSpec::Rewind.install!
|
data/lib/rspec-rewind.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/rspec/rewind/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'rspec-rewind'
|
|
7
|
+
spec.version = RSpec::Rewind::VERSION
|
|
8
|
+
spec.authors = ['Yudai Takada']
|
|
9
|
+
spec.email = ['t.yudai92@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'Modern retry orchestration for flaky RSpec examples'
|
|
12
|
+
spec.description = <<~DESC
|
|
13
|
+
rspec-rewind provides deterministic retry policies, exception filters,
|
|
14
|
+
configurable backoff strategies, and flaky test observability for RSpec.
|
|
15
|
+
DESC
|
|
16
|
+
spec.homepage = 'https://github.com/ydah/rspec-rewind'
|
|
17
|
+
spec.license = 'MIT'
|
|
18
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 3.1')
|
|
19
|
+
|
|
20
|
+
spec.metadata = {
|
|
21
|
+
'homepage_uri' => spec.homepage,
|
|
22
|
+
'source_code_uri' => spec.homepage,
|
|
23
|
+
'changelog_uri' => "#{spec.homepage}/blob/main/CHANGELOG.md",
|
|
24
|
+
'rubygems_mfa_required' => 'true'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
spec.files = Dir.chdir(__dir__) do
|
|
28
|
+
code_and_signatures = Dir.glob('{lib,sig}/**/*', File::FNM_DOTMATCH).reject do |path|
|
|
29
|
+
path.include?('/.') || File.directory?(path)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
code_and_signatures + %w[CHANGELOG.md LICENSE.txt README.md rspec-rewind.gemspec]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
spec.require_paths = ['lib']
|
|
36
|
+
|
|
37
|
+
spec.add_dependency 'rspec-core', '>= 3.12', '< 4.0'
|
|
38
|
+
end
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
module RSpec
|
|
2
|
+
module Rewind
|
|
3
|
+
VERSION: String
|
|
4
|
+
EVENT_SCHEMA_VERSION: Integer
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
def configuration: () -> Configuration
|
|
8
|
+
def configure: () { (Configuration) -> void } -> void
|
|
9
|
+
def reset_configuration!: () -> Configuration
|
|
10
|
+
def install!: () -> bool?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module Backoff
|
|
14
|
+
def self.fixed: (Numeric seconds) -> untyped
|
|
15
|
+
def self.linear: (step: Numeric, ?max: Numeric?) -> untyped
|
|
16
|
+
def self.exponential: (base: Numeric, ?factor: Numeric, ?max: Numeric?, ?jitter: Numeric) -> untyped
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Configuration
|
|
20
|
+
attr_reader default_retries: Integer
|
|
21
|
+
attr_reader backoff: Numeric | untyped
|
|
22
|
+
attr_reader retry_on: Array[untyped]
|
|
23
|
+
attr_reader skip_retry_on: Array[untyped]
|
|
24
|
+
attr_reader retry_if: untyped?
|
|
25
|
+
attr_reader retry_callback: untyped?
|
|
26
|
+
attr_reader flaky_callback: untyped?
|
|
27
|
+
attr_reader verbose: bool
|
|
28
|
+
attr_reader display_retry_failure_messages: bool
|
|
29
|
+
attr_reader clear_lets_on_failure: bool
|
|
30
|
+
attr_reader retry_budget: RetryBudget
|
|
31
|
+
attr_reader flaky_reporter: FlakyReporter::NullReporter | FlakyReporter::JsonlReporter | untyped
|
|
32
|
+
|
|
33
|
+
def initialize: () -> void
|
|
34
|
+
def default_retries=: (untyped) -> Integer
|
|
35
|
+
def backoff=: (untyped) -> Numeric | untyped
|
|
36
|
+
def retry_on=: (untyped) -> Array[untyped]
|
|
37
|
+
def skip_retry_on=: (untyped) -> Array[untyped]
|
|
38
|
+
def retry_if=: (untyped?) -> untyped?
|
|
39
|
+
def retry_callback=: (untyped?) -> untyped?
|
|
40
|
+
def flaky_callback=: (untyped?) -> untyped?
|
|
41
|
+
def verbose=: (bool) -> bool
|
|
42
|
+
def display_retry_failure_messages=: (bool) -> bool
|
|
43
|
+
def clear_lets_on_failure=: (bool) -> bool
|
|
44
|
+
def retry_budget=: (RetryBudget | untyped) -> RetryBudget
|
|
45
|
+
def flaky_reporter=: (FlakyReporter::NullReporter | FlakyReporter::JsonlReporter | untyped?) -> FlakyReporter::NullReporter | FlakyReporter::JsonlReporter | untyped
|
|
46
|
+
def flaky_report_path=: (String?) -> FlakyReporter::NullReporter | FlakyReporter::JsonlReporter
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class RetryBudget
|
|
50
|
+
attr_reader limit: Integer?
|
|
51
|
+
attr_reader used: Integer
|
|
52
|
+
|
|
53
|
+
def initialize: (untyped) -> void
|
|
54
|
+
def consume!: () -> bool
|
|
55
|
+
def remaining: () -> Integer | Float
|
|
56
|
+
def unlimited?: () -> bool
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class FlakyReporter
|
|
60
|
+
def self.null: () -> NullReporter
|
|
61
|
+
def self.jsonl: (String path) -> JsonlReporter
|
|
62
|
+
|
|
63
|
+
class NullReporter
|
|
64
|
+
def record: (Event event) -> void
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class JsonlReporter
|
|
68
|
+
def initialize: (String path) -> void
|
|
69
|
+
def record: (Event event) -> void
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class Event < ::Struct
|
|
74
|
+
attr_accessor schema_version: Integer
|
|
75
|
+
attr_accessor status: Symbol
|
|
76
|
+
attr_accessor retry_reason: Symbol?
|
|
77
|
+
attr_accessor example_id: String
|
|
78
|
+
attr_accessor description: String
|
|
79
|
+
attr_accessor location: String
|
|
80
|
+
attr_accessor attempt: Integer
|
|
81
|
+
attr_accessor retries: Integer
|
|
82
|
+
attr_accessor exception_class: String?
|
|
83
|
+
attr_accessor exception_message: String?
|
|
84
|
+
attr_accessor duration: Numeric
|
|
85
|
+
attr_accessor sleep_seconds: Numeric
|
|
86
|
+
attr_accessor timestamp: String
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class ExampleContext
|
|
90
|
+
def initialize: (example: untyped) -> void
|
|
91
|
+
def source: () -> untyped
|
|
92
|
+
def metadata: () -> Hash[Symbol, untyped]
|
|
93
|
+
def id: () -> String
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
module ExampleMethods
|
|
97
|
+
def run_with_rewind: (?Hash[Symbol | String, untyped]) -> void
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class RetryDecision
|
|
101
|
+
def initialize: (exception: Exception?, example: untyped, retry_on: untyped, skip_retry_on: untyped, retry_if: untyped?) -> void
|
|
102
|
+
def retry?: () -> bool
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class RetryPolicy
|
|
106
|
+
def initialize: (example: untyped, configuration: Configuration, metadata: Hash[Symbol, untyped]?) -> void
|
|
107
|
+
def retry_allowed?: (exception: Exception, retry_on: untyped, skip_retry_on: untyped, retry_if: untyped?) -> bool
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class RetryCountResolver
|
|
111
|
+
ENV_RETRIES_KEY: String
|
|
112
|
+
|
|
113
|
+
def initialize: (configuration: Configuration, metadata: Hash[Symbol, untyped]?) -> void
|
|
114
|
+
def resolve: (explicit_retries: untyped) -> Integer
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
class RetryDelayResolver
|
|
118
|
+
def initialize: (configuration: Configuration, metadata: Hash[Symbol, untyped]?, example: untyped) -> void
|
|
119
|
+
def resolve: (retry_number: Integer, backoff: untyped, wait: untyped, exception: Exception) -> Float
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class AttemptRunner
|
|
123
|
+
def run: (run_target: untyped, exception_source: untyped) -> [Exception?, Float, bool]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
class FlakyTransition
|
|
127
|
+
def initialize: (event_builder: untyped, notifier: untyped) -> void
|
|
128
|
+
def perform: (attempt: Integer, retries: Integer, duration: Numeric) -> void
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class RetryTransition
|
|
132
|
+
def initialize: (
|
|
133
|
+
configuration: Configuration,
|
|
134
|
+
retry_delay_resolver: untyped,
|
|
135
|
+
event_builder: untyped,
|
|
136
|
+
notifier: untyped,
|
|
137
|
+
state_resetter: untyped,
|
|
138
|
+
sleep: untyped
|
|
139
|
+
) -> void
|
|
140
|
+
def perform: (
|
|
141
|
+
retry_number: Integer,
|
|
142
|
+
resolved_retries: Integer,
|
|
143
|
+
duration: Numeric,
|
|
144
|
+
exception: Exception,
|
|
145
|
+
backoff: untyped,
|
|
146
|
+
wait: untyped,
|
|
147
|
+
example_source: untyped
|
|
148
|
+
) -> void
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
class RetryGate
|
|
152
|
+
def initialize: (configuration: Configuration, retry_policy: RetryPolicy, debug: untyped) -> void
|
|
153
|
+
def allow?: (
|
|
154
|
+
exception: Exception,
|
|
155
|
+
retry_number: Integer,
|
|
156
|
+
resolved_retries: Integer,
|
|
157
|
+
retry_on: untyped,
|
|
158
|
+
skip_retry_on: untyped,
|
|
159
|
+
retry_if: untyped?,
|
|
160
|
+
example_id: String
|
|
161
|
+
) -> bool
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
class RunnerLogger
|
|
165
|
+
def initialize: (configuration: Configuration, warn_output: untyped) -> void
|
|
166
|
+
def reporter_message: (String) -> void
|
|
167
|
+
def debug: (String) -> void
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
class RetryLoop
|
|
171
|
+
def initialize: (
|
|
172
|
+
example: untyped,
|
|
173
|
+
context: ExampleContext,
|
|
174
|
+
retry_count_resolver: RetryCountResolver,
|
|
175
|
+
attempt_runner: AttemptRunner,
|
|
176
|
+
retry_gate: RetryGate,
|
|
177
|
+
retry_transition: RetryTransition,
|
|
178
|
+
flaky_transition: FlakyTransition
|
|
179
|
+
) -> void
|
|
180
|
+
def run: (
|
|
181
|
+
retries: untyped,
|
|
182
|
+
backoff: untyped,
|
|
183
|
+
wait: untyped,
|
|
184
|
+
retry_on: untyped,
|
|
185
|
+
skip_retry_on: untyped,
|
|
186
|
+
retry_if: untyped
|
|
187
|
+
) -> void
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
class RunnerComponents
|
|
191
|
+
attr_reader retry_count_resolver: RetryCountResolver
|
|
192
|
+
attr_reader attempt_runner: AttemptRunner
|
|
193
|
+
attr_reader retry_gate: RetryGate
|
|
194
|
+
attr_reader retry_transition: RetryTransition
|
|
195
|
+
attr_reader flaky_transition: FlakyTransition
|
|
196
|
+
attr_reader retry_loop: RetryLoop
|
|
197
|
+
|
|
198
|
+
def initialize: (
|
|
199
|
+
example: untyped,
|
|
200
|
+
configuration: Configuration,
|
|
201
|
+
context: ExampleContext,
|
|
202
|
+
logger: RunnerLogger
|
|
203
|
+
) -> void
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
class Runner
|
|
207
|
+
def initialize: (example: untyped, configuration: Configuration) -> void
|
|
208
|
+
def run: (?retries: untyped, ?backoff: untyped, ?wait: untyped, ?retry_on: untyped, ?skip_retry_on: untyped, ?retry_if: untyped) -> void
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rspec-rewind
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yudai Takada
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rspec-core
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.12'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '4.0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '3.12'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '4.0'
|
|
32
|
+
description: |
|
|
33
|
+
rspec-rewind provides deterministic retry policies, exception filters,
|
|
34
|
+
configurable backoff strategies, and flaky test observability for RSpec.
|
|
35
|
+
email:
|
|
36
|
+
- t.yudai92@gmail.com
|
|
37
|
+
executables: []
|
|
38
|
+
extensions: []
|
|
39
|
+
extra_rdoc_files: []
|
|
40
|
+
files:
|
|
41
|
+
- CHANGELOG.md
|
|
42
|
+
- LICENSE.txt
|
|
43
|
+
- README.md
|
|
44
|
+
- lib/rspec-rewind.rb
|
|
45
|
+
- lib/rspec/rewind.rb
|
|
46
|
+
- lib/rspec/rewind/attempt_runner.rb
|
|
47
|
+
- lib/rspec/rewind/backoff.rb
|
|
48
|
+
- lib/rspec/rewind/configuration.rb
|
|
49
|
+
- lib/rspec/rewind/event.rb
|
|
50
|
+
- lib/rspec/rewind/example_context.rb
|
|
51
|
+
- lib/rspec/rewind/example_methods.rb
|
|
52
|
+
- lib/rspec/rewind/example_state_resetter.rb
|
|
53
|
+
- lib/rspec/rewind/flaky_reporter.rb
|
|
54
|
+
- lib/rspec/rewind/flaky_transition.rb
|
|
55
|
+
- lib/rspec/rewind/matcher_validation.rb
|
|
56
|
+
- lib/rspec/rewind/retry_budget.rb
|
|
57
|
+
- lib/rspec/rewind/retry_count_resolver.rb
|
|
58
|
+
- lib/rspec/rewind/retry_decision.rb
|
|
59
|
+
- lib/rspec/rewind/retry_delay_resolver.rb
|
|
60
|
+
- lib/rspec/rewind/retry_event_builder.rb
|
|
61
|
+
- lib/rspec/rewind/retry_gate.rb
|
|
62
|
+
- lib/rspec/rewind/retry_loop.rb
|
|
63
|
+
- lib/rspec/rewind/retry_notifier.rb
|
|
64
|
+
- lib/rspec/rewind/retry_policy.rb
|
|
65
|
+
- lib/rspec/rewind/retry_transition.rb
|
|
66
|
+
- lib/rspec/rewind/runner.rb
|
|
67
|
+
- lib/rspec/rewind/runner_components.rb
|
|
68
|
+
- lib/rspec/rewind/runner_logger.rb
|
|
69
|
+
- lib/rspec/rewind/version.rb
|
|
70
|
+
- rspec-rewind.gemspec
|
|
71
|
+
- sig/rspec/rewind.rbs
|
|
72
|
+
homepage: https://github.com/ydah/rspec-rewind
|
|
73
|
+
licenses:
|
|
74
|
+
- MIT
|
|
75
|
+
metadata:
|
|
76
|
+
homepage_uri: https://github.com/ydah/rspec-rewind
|
|
77
|
+
source_code_uri: https://github.com/ydah/rspec-rewind
|
|
78
|
+
changelog_uri: https://github.com/ydah/rspec-rewind/blob/main/CHANGELOG.md
|
|
79
|
+
rubygems_mfa_required: 'true'
|
|
80
|
+
rdoc_options: []
|
|
81
|
+
require_paths:
|
|
82
|
+
- lib
|
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '3.1'
|
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
|
+
requirements:
|
|
90
|
+
- - ">="
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: '0'
|
|
93
|
+
requirements: []
|
|
94
|
+
rubygems_version: 4.0.6
|
|
95
|
+
specification_version: 4
|
|
96
|
+
summary: Modern retry orchestration for flaky RSpec examples
|
|
97
|
+
test_files: []
|