rspec-expectations 3.9.3 → 3.10.2
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
- checksums.yaml.gz.sig +0 -0
- data/Changelog.md +52 -1
- data/README.md +1 -1
- data/lib/rspec/expectations/configuration.rb +15 -0
- data/lib/rspec/expectations/expectation_target.rb +3 -5
- data/lib/rspec/expectations/failure_aggregator.rb +23 -5
- data/lib/rspec/expectations/handler.rb +18 -6
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/matchers/built_in/be.rb +0 -133
- data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
- data/lib/rspec/matchers/built_in/has.rb +89 -45
- data/lib/rspec/matchers/built_in/include.rb +72 -15
- data/lib/rspec/matchers/built_in/raise_error.rb +51 -16
- data/lib/rspec/matchers/built_in/respond_to.rb +46 -45
- data/lib/rspec/matchers/built_in/yield.rb +6 -92
- data/lib/rspec/matchers/built_in.rb +2 -1
- data/lib/rspec/matchers.rb +50 -62
- data.tar.gz.sig +0 -0
- metadata +10 -9
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1042199b6d7c762e4a79740c4996cfb198c2a5599091f9e80c4b934021dcfdd
|
4
|
+
data.tar.gz: 4aadaf8516b655b927d4daa574851e4d2113b7c0294b83f01a751b0ee785d11e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f78ad1ed8b49b3ad14c101221cfc8d380f01e31ea4c374b480ca1a514e26a3933bb05b1a1123e12cadaa5aff1d35b6e556703f0783f297dc0502e3ff2306404f
|
7
|
+
data.tar.gz: 8998ea12bd8f10ae89b18b2e138b338839202db95bb88328b16ce5435422c1cc1f2b6625f2a97aa653749ca33e93a288db7720526da954e01999214d99eb798d
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/Changelog.md
CHANGED
@@ -1,5 +1,55 @@
|
|
1
|
+
### Development
|
2
|
+
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.10.2...3-10-maintenance)
|
3
|
+
|
4
|
+
### 3.10.2 / 2022-01-14
|
5
|
+
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.10.1...v3.10.2)
|
6
|
+
|
7
|
+
Bug Fixes:
|
8
|
+
|
9
|
+
* Fix support for dynamic matchers for expectation target checks (Phil Pirozhkov, #1294)
|
10
|
+
* Fix `expect(array).to include(hash).times`, previously this would fail due to
|
11
|
+
matching the entire array as a single hash, rather than a member of the hash.
|
12
|
+
(Slava Kardakov, #1322)
|
13
|
+
* Ensure `raise_error` matches works with the `error_highlight` option from Ruby 3.1.
|
14
|
+
(Peter Goldstein, #1339)
|
15
|
+
|
16
|
+
### 3.10.1 / 2020-12-27
|
17
|
+
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.10.0...v3.10.1)
|
18
|
+
|
19
|
+
Bug Fixes:
|
20
|
+
|
21
|
+
* Allow JRuby 9.2.x.x to generate backtraces normally rather than via our
|
22
|
+
backfill workaround. (#1230, Jon Rowe)
|
23
|
+
|
24
|
+
### 3.10.0 / 2020-10-30
|
25
|
+
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.9.3...v3.10.0)
|
26
|
+
|
27
|
+
Enhancements:
|
28
|
+
|
29
|
+
* Allow `include` matcher to be chained with `once`, `at_least`, etc. for simple cases.
|
30
|
+
(Marc-André Lafortune, #1168)
|
31
|
+
* Add an explicit warning when `nil` is passed to `raise_error`. (Phil Pirozhkov, #1143)
|
32
|
+
* Improve `include` matcher's composability. (Phil Pirozhkov, #1155)
|
33
|
+
* Mocks expectations can now set a custom failure message.
|
34
|
+
(Benoit Tigeot and Nicolas Zermati, #1156)
|
35
|
+
* `aggregate_failures` now shows the backtrace line for each failure. (Fabricio Bedin, #1163)
|
36
|
+
* Support multiple combinations of `yield_control` modifiers like `at_least`, `at_most`.
|
37
|
+
(Jon Rowe, #1169)
|
38
|
+
* Dynamic `have_<n>` matchers now have output consistent with other dynamic matchers.
|
39
|
+
(Marc-André Lafortune, #1195)
|
40
|
+
* New config option `strict_predicate_matchers` allows predicate matcher to be strict
|
41
|
+
(i.e. match for `true` or `false`) instead of the default (match truthy vs `false` or `nil`).
|
42
|
+
(Marc-André Lafortune, #1196)
|
43
|
+
|
44
|
+
### 3.9.4 / 2020-10-29
|
45
|
+
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.9.3...v3.9.4)
|
46
|
+
|
47
|
+
Bug Fixes:
|
48
|
+
|
49
|
+
* Fix regression with `be_` and `have_` matchers and arguments implementing `to_hash`
|
50
|
+
were they would act like keywords and be cast to a hash. (Jon Rowe, #1222)
|
51
|
+
|
1
52
|
### 3.9.3 / 2020-10-23
|
2
|
-
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.9.2...v3.9.3)
|
3
53
|
|
4
54
|
Bug Fixes:
|
5
55
|
|
@@ -14,6 +64,7 @@ Bug Fixes:
|
|
14
64
|
* Prevent errors from causing false positives when using `be <operator>` comparison, e.g.
|
15
65
|
`expect(1).not_to be < 'a'` will now correctly fail rather than pass. (Jon Rowe, #1208)
|
16
66
|
|
67
|
+
|
17
68
|
### 3.9.2 / 2020-05-08
|
18
69
|
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.9.1...v3.9.2)
|
19
70
|
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# RSpec Expectations [](https://github.com/rspec/rspec-expectations/actions) [](https://codeclimate.com/github/rspec/rspec-expectations)
|
2
2
|
|
3
3
|
RSpec::Expectations lets you express expected outcomes on an object in an
|
4
4
|
example.
|
@@ -28,6 +28,7 @@ module RSpec
|
|
28
28
|
|
29
29
|
def initialize
|
30
30
|
@on_potential_false_positives = :warn
|
31
|
+
@strict_predicate_matchers = false
|
31
32
|
end
|
32
33
|
|
33
34
|
# Configures the supported syntax.
|
@@ -185,6 +186,20 @@ module RSpec
|
|
185
186
|
@on_potential_false_positives = behavior
|
186
187
|
end
|
187
188
|
|
189
|
+
# Configures RSpec to check predicate matchers to `be(true)` / `be(false)` (strict),
|
190
|
+
# or `be_truthy` / `be_falsey` (not strict).
|
191
|
+
# Historically, the default was `false`, but `true` is recommended.
|
192
|
+
def strict_predicate_matchers=(flag)
|
193
|
+
raise ArgumentError, "Pass `true` or `false`" unless flag == true || flag == false
|
194
|
+
@strict_predicate_matchers = flag
|
195
|
+
end
|
196
|
+
|
197
|
+
attr_reader :strict_predicate_matchers
|
198
|
+
|
199
|
+
def strict_predicate_matchers?
|
200
|
+
@strict_predicate_matchers
|
201
|
+
end
|
202
|
+
|
188
203
|
# Indicates what RSpec will do about matcher use which will
|
189
204
|
# potentially cause false positives in tests, generally you want to
|
190
205
|
# avoid such scenarios so this defaults to `true`.
|
@@ -57,7 +57,7 @@ module RSpec
|
|
57
57
|
# expect { perform }.to raise_error
|
58
58
|
# @param [Matcher]
|
59
59
|
# matcher
|
60
|
-
# @param [String
|
60
|
+
# @param [String, Proc] message optional message to display when the expectation fails
|
61
61
|
# @return [Boolean] true if the expectation succeeds (else raises)
|
62
62
|
# @see RSpec::Matchers
|
63
63
|
def to(matcher=nil, message=nil, &block)
|
@@ -70,7 +70,7 @@ module RSpec
|
|
70
70
|
# expect(value).not_to eq(5)
|
71
71
|
# @param [Matcher]
|
72
72
|
# matcher
|
73
|
-
# @param [String
|
73
|
+
# @param [String, Proc] message optional message to display when the expectation fails
|
74
74
|
# @return [Boolean] false if the negative expectation succeeds (else raises)
|
75
75
|
# @see RSpec::Matchers
|
76
76
|
def not_to(matcher=nil, message=nil, &block)
|
@@ -118,9 +118,7 @@ module RSpec
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def supports_block_expectations?(matcher)
|
121
|
-
matcher.supports_block_expectations?
|
122
|
-
rescue NoMethodError
|
123
|
-
false
|
121
|
+
matcher.respond_to?(:supports_block_expectations?) && matcher.supports_block_expectations?
|
124
122
|
end
|
125
123
|
end
|
126
124
|
end
|
@@ -52,10 +52,10 @@ module RSpec
|
|
52
52
|
|
53
53
|
private
|
54
54
|
|
55
|
-
if RSpec::Support::Ruby.jruby?
|
56
|
-
# On JRuby, `caller` and `raise` produce different backtraces with
|
57
|
-
# stack frames. It's important that we use `raise` for JRuby to produce
|
58
|
-
# that has a continuous common section with the raised `MultipleExpectationsNotMetError`,
|
55
|
+
if RSpec::Support::Ruby.jruby? && RSpec::Support::Ruby.jruby_version < '9.2.0.0'
|
56
|
+
# On JRuby 9.1.x.x and before, `caller` and `raise` produce different backtraces with
|
57
|
+
# regards to `.java` stack frames. It's important that we use `raise` for JRuby to produce
|
58
|
+
# a backtrace that has a continuous common section with the raised `MultipleExpectationsNotMetError`,
|
59
59
|
# so that rspec-core's truncation logic can work properly on it to list the backtrace
|
60
60
|
# relative to the `aggregate_failures` block.
|
61
61
|
def assign_backtrace(failure)
|
@@ -150,11 +150,29 @@ module RSpec
|
|
150
150
|
def enumerated(exceptions, index_offset)
|
151
151
|
exceptions.each_with_index.map do |exception, index|
|
152
152
|
index += index_offset
|
153
|
-
formatted_message = yield exception
|
153
|
+
formatted_message = "#{yield exception}\n#{format_backtrace(exception.backtrace).first}"
|
154
154
|
"#{index_label index}#{indented formatted_message, index}"
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
158
|
+
def exclusion_patterns
|
159
|
+
patterns = %w[/lib\d*/ruby/ bin/ exe/rspec /lib/bundler/ /exe/bundle:]
|
160
|
+
patterns << "org/jruby/" if RSpec::Support::Ruby.jruby?
|
161
|
+
patterns.map! { |s| Regexp.new(s.gsub('/', File::SEPARATOR)) }
|
162
|
+
end
|
163
|
+
|
164
|
+
def format_backtrace(backtrace)
|
165
|
+
backtrace.map { |l| backtrace_line(l) }.compact.tap { |filtered| filtered.concat backtrace if filtered.empty? }
|
166
|
+
end
|
167
|
+
|
168
|
+
def backtrace_line(line)
|
169
|
+
return if [Regexp.union(RSpec::CallerFilter::IGNORE_REGEX, *exclusion_patterns)].any? { |p| line =~ p }
|
170
|
+
|
171
|
+
# It changes the current path that is relative to
|
172
|
+
# system root to be relative to the project root.
|
173
|
+
line.sub(/(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/, '\\1.\\2'.freeze).sub(/\A([^:]+:\d+)$/, '\\1'.freeze)
|
174
|
+
end
|
175
|
+
|
158
176
|
def enumerated_failures
|
159
177
|
enumerated(failures, 0, &:message)
|
160
178
|
end
|
@@ -44,10 +44,16 @@ module RSpec
|
|
44
44
|
|
45
45
|
# @private
|
46
46
|
class PositiveExpectationHandler
|
47
|
-
def self.handle_matcher(actual, initial_matcher,
|
48
|
-
ExpectationHelper.with_matcher(self, initial_matcher,
|
47
|
+
def self.handle_matcher(actual, initial_matcher, custom_message=nil, &block)
|
48
|
+
ExpectationHelper.with_matcher(self, initial_matcher, custom_message) do |matcher|
|
49
49
|
return ::RSpec::Matchers::BuiltIn::PositiveOperatorMatcher.new(actual) unless initial_matcher
|
50
|
-
|
50
|
+
|
51
|
+
match_result = matcher.matches?(actual, &block)
|
52
|
+
if custom_message && match_result.respond_to?(:error_generator)
|
53
|
+
match_result.error_generator.opts[:message] = custom_message
|
54
|
+
end
|
55
|
+
|
56
|
+
match_result || ExpectationHelper.handle_failure(matcher, custom_message, :failure_message)
|
51
57
|
end
|
52
58
|
end
|
53
59
|
|
@@ -66,10 +72,16 @@ module RSpec
|
|
66
72
|
|
67
73
|
# @private
|
68
74
|
class NegativeExpectationHandler
|
69
|
-
def self.handle_matcher(actual, initial_matcher,
|
70
|
-
ExpectationHelper.with_matcher(self, initial_matcher,
|
75
|
+
def self.handle_matcher(actual, initial_matcher, custom_message=nil, &block)
|
76
|
+
ExpectationHelper.with_matcher(self, initial_matcher, custom_message) do |matcher|
|
71
77
|
return ::RSpec::Matchers::BuiltIn::NegativeOperatorMatcher.new(actual) unless initial_matcher
|
72
|
-
|
78
|
+
|
79
|
+
negated_match_result = does_not_match?(matcher, actual, &block)
|
80
|
+
if custom_message && negated_match_result.respond_to?(:error_generator)
|
81
|
+
negated_match_result.error_generator.opts[:message] = custom_message
|
82
|
+
end
|
83
|
+
|
84
|
+
negated_match_result || ExpectationHelper.handle_failure(matcher, custom_message, :failure_message_when_negated)
|
73
85
|
end
|
74
86
|
end
|
75
87
|
|
@@ -186,139 +186,6 @@ module RSpec
|
|
186
186
|
@actual.__send__ @operator, @expected
|
187
187
|
end
|
188
188
|
end
|
189
|
-
|
190
|
-
# @api private
|
191
|
-
# Provides the implementation of `be_<predicate>`.
|
192
|
-
# Not intended to be instantiated directly.
|
193
|
-
class BePredicate < BaseMatcher
|
194
|
-
include BeHelpers
|
195
|
-
|
196
|
-
if RSpec::Support::RubyFeatures.kw_args_supported?
|
197
|
-
binding.eval(<<-CODE, __FILE__, __LINE__)
|
198
|
-
def initialize(*args, **kwargs, &block)
|
199
|
-
@expected = parse_expected(args.shift)
|
200
|
-
@args = args
|
201
|
-
@kwargs = kwargs
|
202
|
-
@block = block
|
203
|
-
end
|
204
|
-
CODE
|
205
|
-
else
|
206
|
-
def initialize(*args, &block)
|
207
|
-
@expected = parse_expected(args.shift)
|
208
|
-
@args = args
|
209
|
-
@block = block
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def matches?(actual, &block)
|
214
|
-
@actual = actual
|
215
|
-
@block ||= block
|
216
|
-
predicate_accessible? && predicate_matches?
|
217
|
-
end
|
218
|
-
|
219
|
-
def does_not_match?(actual, &block)
|
220
|
-
@actual = actual
|
221
|
-
@block ||= block
|
222
|
-
predicate_accessible? && !predicate_matches?
|
223
|
-
end
|
224
|
-
|
225
|
-
# @api private
|
226
|
-
# @return [String]
|
227
|
-
def failure_message
|
228
|
-
failure_message_expecting(true)
|
229
|
-
end
|
230
|
-
|
231
|
-
# @api private
|
232
|
-
# @return [String]
|
233
|
-
def failure_message_when_negated
|
234
|
-
failure_message_expecting(false)
|
235
|
-
end
|
236
|
-
|
237
|
-
# @api private
|
238
|
-
# @return [String]
|
239
|
-
def description
|
240
|
-
"#{prefix_to_sentence}#{expected_to_sentence}#{args_to_sentence}"
|
241
|
-
end
|
242
|
-
|
243
|
-
private
|
244
|
-
|
245
|
-
def predicate_accessible?
|
246
|
-
actual.respond_to?(predicate) || actual.respond_to?(present_tense_predicate)
|
247
|
-
end
|
248
|
-
|
249
|
-
# support 1.8.7, evaluate once at load time for performance
|
250
|
-
if String === methods.first
|
251
|
-
# :nocov:
|
252
|
-
def private_predicate?
|
253
|
-
@actual.private_methods.include? predicate.to_s
|
254
|
-
end
|
255
|
-
# :nocov:
|
256
|
-
else
|
257
|
-
def private_predicate?
|
258
|
-
@actual.private_methods.include? predicate
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
if RSpec::Support::RubyFeatures.kw_args_supported?
|
263
|
-
binding.eval(<<-CODE, __FILE__, __LINE__)
|
264
|
-
def predicate_matches?
|
265
|
-
method_name = actual.respond_to?(predicate) ? predicate : present_tense_predicate
|
266
|
-
if @kwargs.empty?
|
267
|
-
@predicate_matches = actual.__send__(method_name, *@args, &@block)
|
268
|
-
else
|
269
|
-
@predicate_matches = actual.__send__(method_name, *@args, **@kwargs, &@block)
|
270
|
-
end
|
271
|
-
end
|
272
|
-
CODE
|
273
|
-
else
|
274
|
-
def predicate_matches?
|
275
|
-
method_name = actual.respond_to?(predicate) ? predicate : present_tense_predicate
|
276
|
-
@predicate_matches = actual.__send__(method_name, *@args, &@block)
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
def predicate
|
281
|
-
:"#{@expected}?"
|
282
|
-
end
|
283
|
-
|
284
|
-
def present_tense_predicate
|
285
|
-
:"#{@expected}s?"
|
286
|
-
end
|
287
|
-
|
288
|
-
def parse_expected(expected)
|
289
|
-
@prefix, expected = prefix_and_expected(expected)
|
290
|
-
expected
|
291
|
-
end
|
292
|
-
|
293
|
-
def prefix_and_expected(symbol)
|
294
|
-
Matchers::BE_PREDICATE_REGEX.match(symbol.to_s).captures.compact
|
295
|
-
end
|
296
|
-
|
297
|
-
def prefix_to_sentence
|
298
|
-
EnglishPhrasing.split_words(@prefix)
|
299
|
-
end
|
300
|
-
|
301
|
-
def failure_message_expecting(value)
|
302
|
-
validity_message ||
|
303
|
-
"expected `#{actual_formatted}.#{predicate}#{args_to_s}` to return #{value}, got #{description_of @predicate_matches}"
|
304
|
-
end
|
305
|
-
|
306
|
-
def validity_message
|
307
|
-
return nil if predicate_accessible?
|
308
|
-
|
309
|
-
msg = "expected #{actual_formatted} to respond to `#{predicate}`".dup
|
310
|
-
|
311
|
-
if private_predicate?
|
312
|
-
msg << " but `#{predicate}` is a private method"
|
313
|
-
elsif predicate == :true?
|
314
|
-
msg << " or perhaps you meant `be true` or `be_truthy`"
|
315
|
-
elsif predicate == :false?
|
316
|
-
msg << " or perhaps you meant `be false` or `be_falsey`"
|
317
|
-
end
|
318
|
-
|
319
|
-
msg
|
320
|
-
end
|
321
|
-
end
|
322
189
|
end
|
323
190
|
end
|
324
191
|
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
module BuiltIn
|
4
|
+
# @api private
|
5
|
+
# Asbtract class to implement `once`, `at_least` and other
|
6
|
+
# count constraints.
|
7
|
+
module CountExpectation
|
8
|
+
# @api public
|
9
|
+
# Specifies that the method is expected to match once.
|
10
|
+
def once
|
11
|
+
exactly(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @api public
|
15
|
+
# Specifies that the method is expected to match twice.
|
16
|
+
def twice
|
17
|
+
exactly(2)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @api public
|
21
|
+
# Specifies that the method is expected to match thrice.
|
22
|
+
def thrice
|
23
|
+
exactly(3)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @api public
|
27
|
+
# Specifies that the method is expected to match the given number of times.
|
28
|
+
def exactly(number)
|
29
|
+
set_expected_count(:==, number)
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api public
|
34
|
+
# Specifies the maximum number of times the method is expected to match
|
35
|
+
def at_most(number)
|
36
|
+
set_expected_count(:<=, number)
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# @api public
|
41
|
+
# Specifies the minimum number of times the method is expected to match
|
42
|
+
def at_least(number)
|
43
|
+
set_expected_count(:>=, number)
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# @api public
|
48
|
+
# No-op. Provides syntactic sugar.
|
49
|
+
def times
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
# @api private
|
55
|
+
attr_reader :count_expectation_type, :expected_count
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
if RUBY_VERSION.to_f > 1.8
|
60
|
+
def cover?(count, number)
|
61
|
+
count.cover?(number)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
def cover?(count, number)
|
65
|
+
number >= count.first && number <= count.last
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def expected_count_matches?(actual_count)
|
70
|
+
@actual_count = actual_count
|
71
|
+
return @actual_count > 0 unless count_expectation_type
|
72
|
+
return cover?(expected_count, actual_count) if count_expectation_type == :<=>
|
73
|
+
|
74
|
+
@actual_count.__send__(count_expectation_type, expected_count)
|
75
|
+
end
|
76
|
+
|
77
|
+
def has_expected_count?
|
78
|
+
!!count_expectation_type
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_expected_count(relativity, n)
|
82
|
+
raise_unsupported_count_expectation if unsupported_count_expectation?(relativity)
|
83
|
+
|
84
|
+
count = count_constraint_to_number(n)
|
85
|
+
|
86
|
+
if count_expectation_type == :<= && relativity == :>=
|
87
|
+
raise_impossible_count_expectation(count) if count > expected_count
|
88
|
+
@count_expectation_type = :<=>
|
89
|
+
@expected_count = count..expected_count
|
90
|
+
elsif count_expectation_type == :>= && relativity == :<=
|
91
|
+
raise_impossible_count_expectation(count) if count < expected_count
|
92
|
+
@count_expectation_type = :<=>
|
93
|
+
@expected_count = expected_count..count
|
94
|
+
else
|
95
|
+
@count_expectation_type = relativity
|
96
|
+
@expected_count = count
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def raise_impossible_count_expectation(count)
|
101
|
+
text =
|
102
|
+
case count_expectation_type
|
103
|
+
when :<= then "at_least(#{count}).at_most(#{expected_count})"
|
104
|
+
when :>= then "at_least(#{expected_count}).at_most(#{count})"
|
105
|
+
end
|
106
|
+
raise ArgumentError, "The constraint #{text} is not possible"
|
107
|
+
end
|
108
|
+
|
109
|
+
def raise_unsupported_count_expectation
|
110
|
+
text =
|
111
|
+
case count_expectation_type
|
112
|
+
when :<= then "at_least"
|
113
|
+
when :>= then "at_most"
|
114
|
+
when :<=> then "at_least/at_most combination"
|
115
|
+
else "count"
|
116
|
+
end
|
117
|
+
raise ArgumentError, "Multiple #{text} constraints are not supported"
|
118
|
+
end
|
119
|
+
|
120
|
+
def count_constraint_to_number(n)
|
121
|
+
case n
|
122
|
+
when Numeric then n
|
123
|
+
when :once then 1
|
124
|
+
when :twice then 2
|
125
|
+
when :thrice then 3
|
126
|
+
else
|
127
|
+
raise ArgumentError, "Expected a number, :once, :twice or :thrice," \
|
128
|
+
" but got #{n}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def unsupported_count_expectation?(relativity)
|
133
|
+
return true if count_expectation_type == :==
|
134
|
+
return true if count_expectation_type == :<=>
|
135
|
+
(count_expectation_type == :<= && relativity == :<=) ||
|
136
|
+
(count_expectation_type == :>= && relativity == :>=)
|
137
|
+
end
|
138
|
+
|
139
|
+
def count_expectation_description
|
140
|
+
"#{human_readable_expectation_type}#{human_readable_count(expected_count)}"
|
141
|
+
end
|
142
|
+
|
143
|
+
def count_failure_reason(action)
|
144
|
+
"#{count_expectation_description}" \
|
145
|
+
" but #{action}#{human_readable_count(@actual_count)}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def human_readable_expectation_type
|
149
|
+
case count_expectation_type
|
150
|
+
when :<= then ' at most'
|
151
|
+
when :>= then ' at least'
|
152
|
+
when :<=> then ' between'
|
153
|
+
else ''
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def human_readable_count(count)
|
158
|
+
case count
|
159
|
+
when Range then " #{count.first} and #{count.last} times"
|
160
|
+
when nil then ''
|
161
|
+
when 1 then ' once'
|
162
|
+
when 2 then ' twice'
|
163
|
+
else " #{count} times"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|