rspec-expectations 3.8.1 → 3.12.3
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
- checksums.yaml.gz.sig +0 -0
- data/Changelog.md +203 -4
- data/README.md +35 -20
- data/lib/rspec/expectations/configuration.rb +15 -0
- data/lib/rspec/expectations/expectation_target.rb +42 -6
- data/lib/rspec/expectations/failure_aggregator.rb +41 -6
- data/lib/rspec/expectations/handler.rb +20 -8
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/matchers/aliased_matcher.rb +3 -3
- data/lib/rspec/matchers/built_in/all.rb +1 -0
- data/lib/rspec/matchers/built_in/base_matcher.rb +5 -0
- data/lib/rspec/matchers/built_in/be.rb +10 -107
- data/lib/rspec/matchers/built_in/be_instance_of.rb +5 -1
- data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -1
- data/lib/rspec/matchers/built_in/be_within.rb +2 -2
- data/lib/rspec/matchers/built_in/change.rb +26 -2
- data/lib/rspec/matchers/built_in/compound.rb +20 -1
- data/lib/rspec/matchers/built_in/contain_exactly.rb +10 -2
- data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
- data/lib/rspec/matchers/built_in/exist.rb +1 -1
- data/lib/rspec/matchers/built_in/has.rb +88 -24
- data/lib/rspec/matchers/built_in/have_attributes.rb +1 -1
- data/lib/rspec/matchers/built_in/include.rb +82 -18
- data/lib/rspec/matchers/built_in/output.rb +7 -0
- data/lib/rspec/matchers/built_in/raise_error.rb +63 -22
- data/lib/rspec/matchers/built_in/respond_to.rb +53 -18
- data/lib/rspec/matchers/built_in/throw_symbol.rb +10 -4
- data/lib/rspec/matchers/built_in/yield.rb +26 -83
- data/lib/rspec/matchers/built_in.rb +2 -1
- data/lib/rspec/matchers/composable.rb +1 -1
- data/lib/rspec/matchers/dsl.rb +29 -11
- data/lib/rspec/matchers/english_phrasing.rb +1 -1
- data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +16 -7
- data/lib/rspec/matchers/matcher_protocol.rb +6 -0
- data/lib/rspec/matchers.rb +78 -68
- data.tar.gz.sig +0 -0
- metadata +29 -24
- metadata.gz.sig +0 -0
@@ -93,7 +93,7 @@ module RSpec
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def respond_to_matcher
|
96
|
-
@respond_to_matcher ||= RespondTo.new(*expected.keys).with(0).arguments
|
96
|
+
@respond_to_matcher ||= RespondTo.new(*expected.keys).with(0).arguments.tap { |m| m.ignoring_method_signature_failure! }
|
97
97
|
end
|
98
98
|
|
99
99
|
def respond_to_failure_message_or
|
@@ -1,13 +1,17 @@
|
|
1
|
+
require 'rspec/matchers/built_in/count_expectation'
|
2
|
+
|
1
3
|
module RSpec
|
2
4
|
module Matchers
|
3
5
|
module BuiltIn
|
4
6
|
# @api private
|
5
7
|
# Provides the implementation for `include`.
|
6
8
|
# Not intended to be instantiated directly.
|
7
|
-
class Include < BaseMatcher
|
9
|
+
class Include < BaseMatcher # rubocop:disable Metrics/ClassLength
|
10
|
+
include CountExpectation
|
8
11
|
# @private
|
9
12
|
attr_reader :expecteds
|
10
13
|
|
14
|
+
# @api private
|
11
15
|
def initialize(*expecteds)
|
12
16
|
@expecteds = expecteds
|
13
17
|
end
|
@@ -15,21 +19,29 @@ module RSpec
|
|
15
19
|
# @api private
|
16
20
|
# @return [Boolean]
|
17
21
|
def matches?(actual)
|
18
|
-
|
19
|
-
|
22
|
+
check_actual?(actual) &&
|
23
|
+
if check_expected_count?
|
24
|
+
expected_count_matches?(count_inclusions)
|
25
|
+
else
|
26
|
+
perform_match { |v| v }
|
27
|
+
end
|
20
28
|
end
|
21
29
|
|
22
30
|
# @api private
|
23
31
|
# @return [Boolean]
|
24
32
|
def does_not_match?(actual)
|
25
|
-
|
26
|
-
|
33
|
+
check_actual?(actual) &&
|
34
|
+
if check_expected_count?
|
35
|
+
!expected_count_matches?(count_inclusions)
|
36
|
+
else
|
37
|
+
perform_match { |v| !v }
|
38
|
+
end
|
27
39
|
end
|
28
40
|
|
29
41
|
# @api private
|
30
42
|
# @return [String]
|
31
43
|
def description
|
32
|
-
improve_hash_formatting("include#{readable_list_of(expecteds)}")
|
44
|
+
improve_hash_formatting("include#{readable_list_of(expecteds)}#{count_expectation_description}")
|
33
45
|
end
|
34
46
|
|
35
47
|
# @api private
|
@@ -62,12 +74,33 @@ module RSpec
|
|
62
74
|
|
63
75
|
private
|
64
76
|
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
77
|
+
def check_actual?(actual)
|
78
|
+
actual = actual.to_hash if convert_to_hash?(actual)
|
79
|
+
@actual = actual
|
80
|
+
@actual.respond_to?(:include?)
|
81
|
+
end
|
82
|
+
|
83
|
+
def check_expected_count?
|
84
|
+
case
|
85
|
+
when !has_expected_count?
|
86
|
+
return false
|
87
|
+
when expecteds.size != 1
|
88
|
+
raise NotImplementedError, 'Count constraint supported only when testing for a single value being included'
|
89
|
+
when actual.is_a?(Hash)
|
90
|
+
raise NotImplementedError, 'Count constraint on hash keys not implemented'
|
70
91
|
end
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
def format_failure_message(preposition)
|
96
|
+
msg = if actual.respond_to?(:include?)
|
97
|
+
"expected #{description_of @actual} #{preposition}" \
|
98
|
+
" include#{readable_list_of @divergent_items}" \
|
99
|
+
"#{count_failure_reason('it is included') if has_expected_count?}"
|
100
|
+
else
|
101
|
+
"#{yield}, but it does not respond to `include?`"
|
102
|
+
end
|
103
|
+
improve_hash_formatting(msg)
|
71
104
|
end
|
72
105
|
|
73
106
|
def readable_list_of(items)
|
@@ -79,10 +112,9 @@ module RSpec
|
|
79
112
|
end
|
80
113
|
end
|
81
114
|
|
82
|
-
def perform_match(
|
83
|
-
@actual = actual
|
115
|
+
def perform_match(&block)
|
84
116
|
@divergent_items = excluded_from_actual(&block)
|
85
|
-
|
117
|
+
@divergent_items.empty?
|
86
118
|
end
|
87
119
|
|
88
120
|
def excluded_from_actual
|
@@ -107,7 +139,10 @@ module RSpec
|
|
107
139
|
end
|
108
140
|
|
109
141
|
def actual_hash_includes?(expected_key, expected_value)
|
110
|
-
actual_value =
|
142
|
+
actual_value =
|
143
|
+
actual.fetch(expected_key) do
|
144
|
+
actual.find(Proc.new { return false }) { |actual_key, _| values_match?(expected_key, actual_key) }[1]
|
145
|
+
end
|
111
146
|
values_match?(expected_value, actual_value)
|
112
147
|
end
|
113
148
|
|
@@ -118,8 +153,15 @@ module RSpec
|
|
118
153
|
def actual_hash_has_key?(expected_key)
|
119
154
|
# We check `key?` first for perf:
|
120
155
|
# `key?` is O(1), but `any?` is O(N).
|
121
|
-
|
122
|
-
|
156
|
+
|
157
|
+
has_exact_key =
|
158
|
+
begin
|
159
|
+
actual.key?(expected_key)
|
160
|
+
rescue
|
161
|
+
false
|
162
|
+
end
|
163
|
+
|
164
|
+
has_exact_key || actual.keys.any? { |key| values_match?(expected_key, key) }
|
123
165
|
end
|
124
166
|
|
125
167
|
def actual_collection_includes?(expected_item)
|
@@ -131,6 +173,28 @@ module RSpec
|
|
131
173
|
actual.any? { |value| values_match?(expected_item, value) }
|
132
174
|
end
|
133
175
|
|
176
|
+
if RUBY_VERSION < '1.9'
|
177
|
+
def count_enumerable(expected_item)
|
178
|
+
actual.select { |value| values_match?(expected_item, value) }.size
|
179
|
+
end
|
180
|
+
else
|
181
|
+
def count_enumerable(expected_item)
|
182
|
+
actual.count { |value| values_match?(expected_item, value) }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def count_inclusions
|
187
|
+
@divergent_items = expected
|
188
|
+
case actual
|
189
|
+
when String
|
190
|
+
actual.scan(expected.first).length
|
191
|
+
when Enumerable
|
192
|
+
count_enumerable(Hash === expected ? expected : expected.first)
|
193
|
+
else
|
194
|
+
raise NotImplementedError, 'Count constraints are implemented for Enumerable and String values only'
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
134
198
|
def diff_would_wrongly_highlight_matched_item?
|
135
199
|
return false unless actual.is_a?(String) && expected.is_a?(Array)
|
136
200
|
|
@@ -141,7 +205,7 @@ module RSpec
|
|
141
205
|
end
|
142
206
|
|
143
207
|
def convert_to_hash?(obj)
|
144
|
-
!(
|
208
|
+
!obj.respond_to?(:include?) && obj.respond_to?(:to_hash)
|
145
209
|
end
|
146
210
|
end
|
147
211
|
end
|
@@ -4,18 +4,25 @@ module RSpec
|
|
4
4
|
# @api private
|
5
5
|
# Provides the implementation for `raise_error`.
|
6
6
|
# Not intended to be instantiated directly.
|
7
|
-
# rubocop:disable ClassLength
|
8
|
-
# rubocop:disable RescueException
|
7
|
+
# rubocop:disable Metrics/ClassLength
|
8
|
+
# rubocop:disable Lint/RescueException
|
9
9
|
class RaiseError
|
10
10
|
include Composable
|
11
11
|
|
12
|
-
|
12
|
+
# Used as a sentinel value to be able to tell when the user did not pass an
|
13
|
+
# argument. We can't use `nil` for that because we need to warn when `nil` is
|
14
|
+
# passed in a different way. It's an Object, not a Module, since Module's `===`
|
15
|
+
# does not evaluate to true when compared to itself.
|
16
|
+
UndefinedValue = Object.new.freeze
|
17
|
+
|
18
|
+
def initialize(expected_error_or_message, expected_message, &block)
|
13
19
|
@block = block
|
14
20
|
@actual_error = nil
|
15
|
-
@warn_about_bare_error = expected_error_or_message
|
21
|
+
@warn_about_bare_error = UndefinedValue === expected_error_or_message
|
22
|
+
@warn_about_nil_error = expected_error_or_message.nil?
|
16
23
|
|
17
24
|
case expected_error_or_message
|
18
|
-
when nil
|
25
|
+
when nil, UndefinedValue
|
19
26
|
@expected_error = Exception
|
20
27
|
@expected_message = expected_message
|
21
28
|
when String
|
@@ -36,7 +43,7 @@ module RSpec
|
|
36
43
|
self
|
37
44
|
end
|
38
45
|
|
39
|
-
# rubocop:disable MethodLength
|
46
|
+
# rubocop:disable Metrics/MethodLength
|
40
47
|
# @private
|
41
48
|
def matches?(given_proc, negative_expectation=false, &block)
|
42
49
|
@given_proc = given_proc
|
@@ -52,22 +59,25 @@ module RSpec
|
|
52
59
|
given_proc.call
|
53
60
|
rescue Exception => @actual_error
|
54
61
|
if values_match?(@expected_error, @actual_error) ||
|
55
|
-
values_match?(@expected_error,
|
62
|
+
values_match?(@expected_error, actual_error_message)
|
56
63
|
@raised_expected_error = true
|
57
64
|
@with_expected_message = verify_message
|
58
65
|
end
|
59
66
|
end
|
60
67
|
|
61
|
-
|
62
|
-
|
68
|
+
unless negative_expectation
|
69
|
+
warn_about_bare_error! if warn_about_bare_error?
|
70
|
+
warn_about_nil_error! if warn_about_nil_error?
|
71
|
+
eval_block if ready_to_eval_block?
|
72
|
+
end
|
63
73
|
|
64
74
|
expectation_matched?
|
65
75
|
end
|
66
|
-
# rubocop:enable MethodLength
|
76
|
+
# rubocop:enable Metrics/MethodLength
|
67
77
|
|
68
78
|
# @private
|
69
79
|
def does_not_match?(given_proc)
|
70
|
-
|
80
|
+
warn_for_negative_false_positives!
|
71
81
|
!matches?(given_proc, :negative_expectation) && Proc === given_proc
|
72
82
|
end
|
73
83
|
|
@@ -76,6 +86,12 @@ module RSpec
|
|
76
86
|
true
|
77
87
|
end
|
78
88
|
|
89
|
+
# @private
|
90
|
+
def supports_value_expectations?
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
94
|
+
# @private
|
79
95
|
def expects_call_stack_jump?
|
80
96
|
true
|
81
97
|
end
|
@@ -83,7 +99,7 @@ module RSpec
|
|
83
99
|
# @api private
|
84
100
|
# @return [String]
|
85
101
|
def failure_message
|
86
|
-
@eval_block ?
|
102
|
+
@eval_block ? actual_error_message : "expected #{expected_error}#{given_error}"
|
87
103
|
end
|
88
104
|
|
89
105
|
# @api private
|
@@ -100,6 +116,12 @@ module RSpec
|
|
100
116
|
|
101
117
|
private
|
102
118
|
|
119
|
+
def actual_error_message
|
120
|
+
return nil unless @actual_error
|
121
|
+
|
122
|
+
@actual_error.respond_to?(:original_message) ? @actual_error.original_message : @actual_error.message
|
123
|
+
end
|
124
|
+
|
103
125
|
def expectation_matched?
|
104
126
|
error_and_message_match? && block_matches?
|
105
127
|
end
|
@@ -128,32 +150,38 @@ module RSpec
|
|
128
150
|
|
129
151
|
def verify_message
|
130
152
|
return true if @expected_message.nil?
|
131
|
-
values_match?(@expected_message,
|
153
|
+
values_match?(@expected_message, actual_error_message.to_s)
|
132
154
|
end
|
133
155
|
|
134
|
-
def
|
156
|
+
def warn_for_negative_false_positives!
|
135
157
|
expression = if expecting_specific_exception? && @expected_message
|
136
158
|
"`expect { }.not_to raise_error(SpecificErrorClass, message)`"
|
137
159
|
elsif expecting_specific_exception?
|
138
160
|
"`expect { }.not_to raise_error(SpecificErrorClass)`"
|
139
161
|
elsif @expected_message
|
140
162
|
"`expect { }.not_to raise_error(message)`"
|
163
|
+
elsif @warn_about_nil_error
|
164
|
+
"`expect { }.not_to raise_error(nil)`"
|
141
165
|
end
|
142
166
|
|
143
167
|
return unless expression
|
144
168
|
|
145
|
-
warn_about_negative_false_positive expression
|
169
|
+
warn_about_negative_false_positive! expression
|
146
170
|
end
|
147
171
|
|
148
172
|
def handle_warning(message)
|
149
173
|
RSpec::Expectations.configuration.false_positives_handler.call(message)
|
150
174
|
end
|
151
175
|
|
152
|
-
def
|
176
|
+
def warn_about_bare_error?
|
153
177
|
@warn_about_bare_error && @block.nil?
|
154
178
|
end
|
155
179
|
|
156
|
-
def
|
180
|
+
def warn_about_nil_error?
|
181
|
+
@warn_about_nil_error
|
182
|
+
end
|
183
|
+
|
184
|
+
def warn_about_bare_error!
|
157
185
|
handle_warning("Using the `raise_error` matcher without providing a specific " \
|
158
186
|
"error or message risks false positives, since `raise_error` " \
|
159
187
|
"will match when Ruby raises a `NoMethodError`, `NameError` or " \
|
@@ -166,11 +194,24 @@ module RSpec
|
|
166
194
|
"_positives = :nothing`")
|
167
195
|
end
|
168
196
|
|
169
|
-
def
|
197
|
+
def warn_about_nil_error!
|
198
|
+
handle_warning("Using the `raise_error` matcher with a `nil` error is probably " \
|
199
|
+
"unintentional, it risks false positives, since `raise_error` " \
|
200
|
+
"will match when Ruby raises a `NoMethodError`, `NameError` or " \
|
201
|
+
"`ArgumentError`, potentially allowing the expectation to pass " \
|
202
|
+
"without even executing the method you are intending to call. " \
|
203
|
+
"#{warning}"\
|
204
|
+
"Instead consider providing a specific error class or message. " \
|
205
|
+
"This message can be suppressed by setting: " \
|
206
|
+
"`RSpec::Expectations.configuration.on_potential_false" \
|
207
|
+
"_positives = :nothing`")
|
208
|
+
end
|
209
|
+
|
210
|
+
def warn_about_negative_false_positive!(expression)
|
170
211
|
handle_warning("Using #{expression} risks false positives, since literally " \
|
171
212
|
"any other error would cause the expectation to pass, " \
|
172
|
-
"including those raised by Ruby (e.g. NoMethodError
|
173
|
-
"and ArgumentError), meaning the code you are intending to test " \
|
213
|
+
"including those raised by Ruby (e.g. `NoMethodError`, `NameError` " \
|
214
|
+
"and `ArgumentError`), meaning the code you are intending to test " \
|
174
215
|
"may not even get reached. Instead consider using " \
|
175
216
|
"`expect { }.not_to raise_error` or `expect { }.to raise_error" \
|
176
217
|
"(DifferentSpecificErrorClass)`. This message can be suppressed by " \
|
@@ -223,8 +264,8 @@ module RSpec
|
|
223
264
|
warning if @actual_error
|
224
265
|
end
|
225
266
|
end
|
226
|
-
# rubocop:enable RescueException
|
227
|
-
# rubocop:enable ClassLength
|
267
|
+
# rubocop:enable Lint/RescueException
|
268
|
+
# rubocop:enable Metrics/ClassLength
|
228
269
|
end
|
229
270
|
end
|
230
271
|
end
|
@@ -11,6 +11,7 @@ module RSpec
|
|
11
11
|
@names = names
|
12
12
|
@expected_arity = nil
|
13
13
|
@expected_keywords = []
|
14
|
+
@ignoring_method_signature_failure = false
|
14
15
|
@unlimited_arguments = nil
|
15
16
|
@arbitrary_keywords = nil
|
16
17
|
end
|
@@ -100,6 +101,12 @@ module RSpec
|
|
100
101
|
"respond to #{pp_names}#{with_arity}"
|
101
102
|
end
|
102
103
|
|
104
|
+
# @api private
|
105
|
+
# Used by other matchers to suppress a check
|
106
|
+
def ignoring_method_signature_failure!
|
107
|
+
@ignoring_method_signature_failure = true
|
108
|
+
end
|
109
|
+
|
103
110
|
private
|
104
111
|
|
105
112
|
def find_failing_method_names(actual, filter_method)
|
@@ -110,24 +117,14 @@ module RSpec
|
|
110
117
|
end
|
111
118
|
|
112
119
|
def matches_arity?(actual, name)
|
113
|
-
|
114
|
-
|
115
|
-
if @
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
expectation.keywords = @expected_keywords
|
123
|
-
expectation.expect_unlimited_arguments = @unlimited_arguments
|
124
|
-
expectation.expect_arbitrary_keywords = @arbitrary_keywords
|
125
|
-
|
126
|
-
return true if expectation.empty?
|
127
|
-
|
128
|
-
signature = Support::MethodSignature.new(Support.method_handle_for(actual, name))
|
129
|
-
|
130
|
-
Support::StrictSignatureVerifier.new(signature).with_expectation(expectation).valid?
|
120
|
+
ArityCheck.new(@expected_arity, @expected_keywords, @arbitrary_keywords, @unlimited_arguments).matches?(actual, name)
|
121
|
+
rescue NameError
|
122
|
+
return true if @ignoring_method_signature_failure
|
123
|
+
raise ArgumentError, "The #{matcher_name} matcher requires that " \
|
124
|
+
"the actual object define the method(s) in " \
|
125
|
+
"order to check arity, but the method " \
|
126
|
+
"`#{name}` is not defined. Remove the arity " \
|
127
|
+
"check or define the method to continue."
|
131
128
|
end
|
132
129
|
|
133
130
|
def with_arity
|
@@ -159,6 +156,44 @@ module RSpec
|
|
159
156
|
def pp_names
|
160
157
|
@names.length == 1 ? "##{@names.first}" : description_of(@names)
|
161
158
|
end
|
159
|
+
|
160
|
+
# @private
|
161
|
+
class ArityCheck
|
162
|
+
def initialize(expected_arity, expected_keywords, arbitrary_keywords, unlimited_arguments)
|
163
|
+
expectation = Support::MethodSignatureExpectation.new
|
164
|
+
|
165
|
+
if expected_arity.is_a?(Range)
|
166
|
+
expectation.min_count = expected_arity.min
|
167
|
+
expectation.max_count = expected_arity.max
|
168
|
+
else
|
169
|
+
expectation.min_count = expected_arity
|
170
|
+
end
|
171
|
+
|
172
|
+
expectation.keywords = expected_keywords
|
173
|
+
expectation.expect_unlimited_arguments = unlimited_arguments
|
174
|
+
expectation.expect_arbitrary_keywords = arbitrary_keywords
|
175
|
+
@expectation = expectation
|
176
|
+
end
|
177
|
+
|
178
|
+
def matches?(actual, name)
|
179
|
+
return true if @expectation.empty?
|
180
|
+
verifier_for(actual, name).with_expectation(@expectation).valid?
|
181
|
+
end
|
182
|
+
|
183
|
+
def verifier_for(actual, name)
|
184
|
+
Support::StrictSignatureVerifier.new(method_signature_for(actual, name))
|
185
|
+
end
|
186
|
+
|
187
|
+
def method_signature_for(actual, name)
|
188
|
+
method_handle = Support.method_handle_for(actual, name)
|
189
|
+
|
190
|
+
if name == :new && method_handle.owner === ::Class && ::Class === actual
|
191
|
+
Support::MethodSignature.new(actual.instance_method(:initialize))
|
192
|
+
else
|
193
|
+
Support::MethodSignature.new(method_handle)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
162
197
|
end
|
163
198
|
end
|
164
199
|
end
|
@@ -13,7 +13,7 @@ module RSpec
|
|
13
13
|
@caught_symbol = @caught_arg = nil
|
14
14
|
end
|
15
15
|
|
16
|
-
# rubocop:disable MethodLength
|
16
|
+
# rubocop:disable Metrics/MethodLength
|
17
17
|
# @private
|
18
18
|
def matches?(given_proc)
|
19
19
|
@block = given_proc
|
@@ -48,7 +48,7 @@ module RSpec
|
|
48
48
|
rescue => other_exception
|
49
49
|
raise
|
50
50
|
ensure
|
51
|
-
# rubocop:disable EnsureReturn
|
51
|
+
# rubocop:disable Lint/EnsureReturn
|
52
52
|
unless other_exception
|
53
53
|
if @expected_symbol.nil?
|
54
54
|
return !!@caught_symbol
|
@@ -60,10 +60,10 @@ module RSpec
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
63
|
-
# rubocop:enable EnsureReturn
|
63
|
+
# rubocop:enable Lint/EnsureReturn
|
64
64
|
end
|
65
65
|
end
|
66
|
-
# rubocop:enable MethodLength
|
66
|
+
# rubocop:enable Metrics/MethodLength
|
67
67
|
|
68
68
|
def does_not_match?(given_proc)
|
69
69
|
!matches?(given_proc) && Proc === given_proc
|
@@ -94,6 +94,12 @@ module RSpec
|
|
94
94
|
true
|
95
95
|
end
|
96
96
|
|
97
|
+
# @api private
|
98
|
+
def supports_value_expectations?
|
99
|
+
false
|
100
|
+
end
|
101
|
+
|
102
|
+
# @api private
|
97
103
|
def expects_call_stack_jump?
|
98
104
|
true
|
99
105
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rspec/matchers/built_in/count_expectation'
|
2
|
+
|
1
3
|
RSpec::Support.require_rspec_support 'method_signature_verifier'
|
2
4
|
|
3
5
|
module RSpec
|
@@ -97,64 +99,12 @@ module RSpec
|
|
97
99
|
# Provides the implementation for `yield_control`.
|
98
100
|
# Not intended to be instantiated directly.
|
99
101
|
class YieldControl < BaseMatcher
|
100
|
-
|
101
|
-
at_least(:once)
|
102
|
-
end
|
103
|
-
|
104
|
-
# @api public
|
105
|
-
# Specifies that the method is expected to yield once.
|
106
|
-
def once
|
107
|
-
exactly(1)
|
108
|
-
self
|
109
|
-
end
|
110
|
-
|
111
|
-
# @api public
|
112
|
-
# Specifies that the method is expected to yield twice.
|
113
|
-
def twice
|
114
|
-
exactly(2)
|
115
|
-
self
|
116
|
-
end
|
117
|
-
|
118
|
-
# @api public
|
119
|
-
# Specifies that the method is expected to yield thrice.
|
120
|
-
def thrice
|
121
|
-
exactly(3)
|
122
|
-
self
|
123
|
-
end
|
124
|
-
|
125
|
-
# @api public
|
126
|
-
# Specifies that the method is expected to yield the given number of times.
|
127
|
-
def exactly(number)
|
128
|
-
set_expected_yields_count(:==, number)
|
129
|
-
self
|
130
|
-
end
|
131
|
-
|
132
|
-
# @api public
|
133
|
-
# Specifies the maximum number of times the method is expected to yield
|
134
|
-
def at_most(number)
|
135
|
-
set_expected_yields_count(:<=, number)
|
136
|
-
self
|
137
|
-
end
|
138
|
-
|
139
|
-
# @api public
|
140
|
-
# Specifies the minimum number of times the method is expected to yield
|
141
|
-
def at_least(number)
|
142
|
-
set_expected_yields_count(:>=, number)
|
143
|
-
self
|
144
|
-
end
|
145
|
-
|
146
|
-
# @api public
|
147
|
-
# No-op. Provides syntactic sugar.
|
148
|
-
def times
|
149
|
-
self
|
150
|
-
end
|
151
|
-
|
102
|
+
include CountExpectation
|
152
103
|
# @private
|
153
104
|
def matches?(block)
|
154
105
|
@probe = YieldProbe.probe(block)
|
155
106
|
return false unless @probe.has_block?
|
156
|
-
|
157
|
-
@probe.num_yields.__send__(@expectation_type, @expected_yields_count)
|
107
|
+
expected_count_matches?(@probe.num_yields)
|
158
108
|
end
|
159
109
|
|
160
110
|
# @private
|
@@ -179,39 +129,17 @@ module RSpec
|
|
179
129
|
true
|
180
130
|
end
|
181
131
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
@expectation_type = relativity
|
186
|
-
@expected_yields_count = case n
|
187
|
-
when Numeric then n
|
188
|
-
when :once then 1
|
189
|
-
when :twice then 2
|
190
|
-
when :thrice then 3
|
191
|
-
end
|
132
|
+
# @private
|
133
|
+
def supports_value_expectations?
|
134
|
+
false
|
192
135
|
end
|
193
136
|
|
137
|
+
private
|
138
|
+
|
194
139
|
def failure_reason
|
195
140
|
return ' but was not a block' unless @probe.has_block?
|
196
|
-
return
|
197
|
-
|
198
|
-
" but yielded #{human_readable_count(@probe.num_yields)}"
|
199
|
-
end
|
200
|
-
|
201
|
-
def human_readable_expectation_type
|
202
|
-
case @expectation_type
|
203
|
-
when :<= then 'at most '
|
204
|
-
when :>= then 'at least '
|
205
|
-
else ''
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
def human_readable_count(count)
|
210
|
-
case count
|
211
|
-
when 1 then 'once'
|
212
|
-
when 2 then 'twice'
|
213
|
-
else "#{count} times"
|
214
|
-
end
|
141
|
+
return "#{count_expectation_description} but did not yield" if @probe.num_yields == 0
|
142
|
+
count_failure_reason('yielded')
|
215
143
|
end
|
216
144
|
end
|
217
145
|
|
@@ -246,6 +174,11 @@ module RSpec
|
|
246
174
|
true
|
247
175
|
end
|
248
176
|
|
177
|
+
# @private
|
178
|
+
def supports_value_expectations?
|
179
|
+
false
|
180
|
+
end
|
181
|
+
|
249
182
|
private
|
250
183
|
|
251
184
|
def positive_failure_reason
|
@@ -308,6 +241,11 @@ module RSpec
|
|
308
241
|
true
|
309
242
|
end
|
310
243
|
|
244
|
+
# @private
|
245
|
+
def supports_value_expectations?
|
246
|
+
false
|
247
|
+
end
|
248
|
+
|
311
249
|
private
|
312
250
|
|
313
251
|
def positive_failure_reason
|
@@ -405,6 +343,11 @@ module RSpec
|
|
405
343
|
true
|
406
344
|
end
|
407
345
|
|
346
|
+
# @private
|
347
|
+
def supports_value_expectations?
|
348
|
+
false
|
349
|
+
end
|
350
|
+
|
408
351
|
private
|
409
352
|
|
410
353
|
def expected_arg_description
|
@@ -16,8 +16,9 @@ module RSpec
|
|
16
16
|
autoload :Be, 'rspec/matchers/built_in/be'
|
17
17
|
autoload :BeComparedTo, 'rspec/matchers/built_in/be'
|
18
18
|
autoload :BeFalsey, 'rspec/matchers/built_in/be'
|
19
|
+
autoload :BeHelpers, 'rspec/matchers/built_in/be'
|
19
20
|
autoload :BeNil, 'rspec/matchers/built_in/be'
|
20
|
-
autoload :BePredicate, 'rspec/matchers/built_in/
|
21
|
+
autoload :BePredicate, 'rspec/matchers/built_in/has'
|
21
22
|
autoload :BeTruthy, 'rspec/matchers/built_in/be'
|
22
23
|
autoload :BeWithin, 'rspec/matchers/built_in/be_within'
|
23
24
|
autoload :Change, 'rspec/matchers/built_in/change'
|