rspec-expectations 2.14.0 → 3.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 +15 -0
- checksums.yaml.gz.sig +0 -0
- data/Changelog.md +369 -25
- data/License.txt +1 -0
- data/README.md +88 -15
- data/lib/rspec/{matchers → expectations}/configuration.rb +44 -14
- data/lib/rspec/expectations/expectation_target.rb +67 -33
- data/lib/rspec/expectations/fail_with.rb +11 -53
- data/lib/rspec/expectations/handler.rb +126 -31
- data/lib/rspec/expectations/minitest_integration.rb +16 -0
- data/lib/rspec/expectations/syntax.rb +60 -94
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/expectations.rb +45 -23
- data/lib/rspec/matchers/aliased_matcher.rb +41 -0
- data/lib/rspec/matchers/built_in/all.rb +78 -0
- data/lib/rspec/matchers/built_in/base_matcher.rb +63 -15
- data/lib/rspec/matchers/built_in/be.rb +170 -81
- data/lib/rspec/matchers/built_in/be_between.rb +77 -0
- data/lib/rspec/matchers/built_in/be_instance_of.rb +11 -3
- data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -0
- data/lib/rspec/matchers/built_in/be_within.rb +39 -15
- data/lib/rspec/matchers/built_in/change.rb +273 -80
- data/lib/rspec/matchers/built_in/compound.rb +122 -0
- data/lib/rspec/matchers/built_in/contain_exactly.rb +256 -0
- data/lib/rspec/matchers/built_in/cover.rb +3 -0
- data/lib/rspec/matchers/built_in/eq.rb +60 -6
- data/lib/rspec/matchers/built_in/eql.rb +17 -5
- data/lib/rspec/matchers/built_in/equal.rb +55 -21
- data/lib/rspec/matchers/built_in/exist.rb +74 -10
- data/lib/rspec/matchers/built_in/has.rb +71 -15
- data/lib/rspec/matchers/built_in/include.rb +64 -27
- data/lib/rspec/matchers/built_in/match.rb +19 -1
- data/lib/rspec/matchers/built_in/operators.rb +119 -0
- data/lib/rspec/matchers/built_in/output.rb +150 -0
- data/lib/rspec/matchers/built_in/raise_error.rb +84 -42
- data/lib/rspec/matchers/built_in/respond_to.rb +42 -20
- data/lib/rspec/matchers/built_in/satisfy.rb +19 -3
- data/lib/rspec/matchers/built_in/start_and_end_with.rb +54 -18
- data/lib/rspec/matchers/built_in/throw_symbol.rb +38 -8
- data/lib/rspec/matchers/built_in/yield.rb +175 -68
- data/lib/rspec/matchers/built_in.rb +45 -33
- data/lib/rspec/matchers/composable.rb +179 -0
- data/lib/rspec/matchers/dsl.rb +362 -5
- data/lib/rspec/matchers/generated_descriptions.rb +13 -6
- data/lib/rspec/matchers/matcher_delegator.rb +37 -0
- data/lib/rspec/matchers/pretty.rb +27 -20
- data/lib/rspec/matchers.rb +361 -136
- data.tar.gz.sig +1 -0
- metadata +99 -215
- metadata.gz.sig +0 -0
- data/features/README.md +0 -48
- data/features/Upgrade.md +0 -53
- data/features/built_in_matchers/README.md +0 -90
- data/features/built_in_matchers/be.feature +0 -175
- data/features/built_in_matchers/be_within.feature +0 -48
- data/features/built_in_matchers/cover.feature +0 -47
- data/features/built_in_matchers/end_with.feature +0 -48
- data/features/built_in_matchers/equality.feature +0 -139
- data/features/built_in_matchers/exist.feature +0 -45
- data/features/built_in_matchers/expect_change.feature +0 -59
- data/features/built_in_matchers/expect_error.feature +0 -144
- data/features/built_in_matchers/have.feature +0 -109
- data/features/built_in_matchers/include.feature +0 -174
- data/features/built_in_matchers/match.feature +0 -52
- data/features/built_in_matchers/operators.feature +0 -227
- data/features/built_in_matchers/predicates.feature +0 -137
- data/features/built_in_matchers/respond_to.feature +0 -84
- data/features/built_in_matchers/satisfy.feature +0 -33
- data/features/built_in_matchers/start_with.feature +0 -48
- data/features/built_in_matchers/throw_symbol.feature +0 -91
- data/features/built_in_matchers/types.feature +0 -116
- data/features/built_in_matchers/yield.feature +0 -161
- data/features/custom_matchers/access_running_example.feature +0 -53
- data/features/custom_matchers/define_diffable_matcher.feature +0 -27
- data/features/custom_matchers/define_matcher.feature +0 -368
- data/features/custom_matchers/define_matcher_outside_rspec.feature +0 -38
- data/features/custom_matchers/define_matcher_with_fluent_interface.feature +0 -24
- data/features/customized_message.feature +0 -22
- data/features/diffing.feature +0 -85
- data/features/implicit_docstrings.feature +0 -52
- data/features/step_definitions/additional_cli_steps.rb +0 -22
- data/features/support/env.rb +0 -14
- data/features/syntax_configuration.feature +0 -71
- data/features/test_frameworks/test_unit.feature +0 -44
- data/lib/rspec/expectations/deprecation.rb +0 -17
- data/lib/rspec/expectations/differ.rb +0 -133
- data/lib/rspec/expectations/errors.rb +0 -9
- data/lib/rspec/expectations/extensions/array.rb +0 -9
- data/lib/rspec/expectations/extensions/object.rb +0 -29
- data/lib/rspec/expectations/extensions.rb +0 -2
- data/lib/rspec/matchers/be_close.rb +0 -9
- data/lib/rspec/matchers/built_in/have.rb +0 -124
- data/lib/rspec/matchers/built_in/match_array.rb +0 -51
- data/lib/rspec/matchers/compatibility.rb +0 -14
- data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
- data/lib/rspec/matchers/matcher.rb +0 -300
- data/lib/rspec/matchers/method_missing.rb +0 -12
- data/lib/rspec/matchers/operator_matcher.rb +0 -109
- data/lib/rspec/matchers/test_unit_integration.rb +0 -11
- data/lib/rspec-expectations.rb +0 -1
- data/spec/rspec/expectations/differ_spec.rb +0 -192
- data/spec/rspec/expectations/expectation_target_spec.rb +0 -82
- data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
- data/spec/rspec/expectations/fail_with_spec.rb +0 -114
- data/spec/rspec/expectations/handler_spec.rb +0 -227
- data/spec/rspec/expectations/syntax_spec.rb +0 -139
- data/spec/rspec/matchers/base_matcher_spec.rb +0 -62
- data/spec/rspec/matchers/be_close_spec.rb +0 -22
- data/spec/rspec/matchers/be_instance_of_spec.rb +0 -63
- data/spec/rspec/matchers/be_kind_of_spec.rb +0 -41
- data/spec/rspec/matchers/be_spec.rb +0 -516
- data/spec/rspec/matchers/be_within_spec.rb +0 -137
- data/spec/rspec/matchers/change_spec.rb +0 -553
- data/spec/rspec/matchers/configuration_spec.rb +0 -206
- data/spec/rspec/matchers/cover_spec.rb +0 -69
- data/spec/rspec/matchers/description_generation_spec.rb +0 -190
- data/spec/rspec/matchers/dsl_spec.rb +0 -57
- data/spec/rspec/matchers/eq_spec.rb +0 -60
- data/spec/rspec/matchers/eql_spec.rb +0 -41
- data/spec/rspec/matchers/equal_spec.rb +0 -78
- data/spec/rspec/matchers/exist_spec.rb +0 -124
- data/spec/rspec/matchers/has_spec.rb +0 -122
- data/spec/rspec/matchers/have_spec.rb +0 -455
- data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
- data/spec/rspec/matchers/include_spec.rb +0 -531
- data/spec/rspec/matchers/match_array_spec.rb +0 -194
- data/spec/rspec/matchers/match_spec.rb +0 -61
- data/spec/rspec/matchers/matcher_spec.rb +0 -471
- data/spec/rspec/matchers/matchers_spec.rb +0 -37
- data/spec/rspec/matchers/method_missing_spec.rb +0 -28
- data/spec/rspec/matchers/operator_matcher_spec.rb +0 -223
- data/spec/rspec/matchers/raise_error_spec.rb +0 -485
- data/spec/rspec/matchers/respond_to_spec.rb +0 -292
- data/spec/rspec/matchers/satisfy_spec.rb +0 -44
- data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -186
- data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
- data/spec/rspec/matchers/yield_spec.rb +0 -514
- data/spec/spec_helper.rb +0 -54
- data/spec/support/classes.rb +0 -56
- data/spec/support/in_sub_process.rb +0 -38
- data/spec/support/matchers.rb +0 -22
- data/spec/support/ruby_version.rb +0 -10
- data/spec/support/shared_examples.rb +0 -13
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
module RSpec
|
|
2
2
|
module Matchers
|
|
3
3
|
module BuiltIn
|
|
4
|
+
# @private
|
|
5
|
+
# Object that is yielded to `expect` when one of the
|
|
6
|
+
# yield matchers is used. Provides information about
|
|
7
|
+
# the yield behavior of the object-under-test.
|
|
4
8
|
class YieldProbe
|
|
5
9
|
def self.probe(block)
|
|
6
|
-
probe = new
|
|
7
|
-
|
|
10
|
+
probe = new(block)
|
|
11
|
+
return probe unless probe.has_block?
|
|
12
|
+
probe.assert_valid_expect_block!
|
|
8
13
|
block.call(probe)
|
|
9
14
|
probe.assert_used!
|
|
10
15
|
probe
|
|
@@ -12,11 +17,16 @@ module RSpec
|
|
|
12
17
|
|
|
13
18
|
attr_accessor :num_yields, :yielded_args
|
|
14
19
|
|
|
15
|
-
def initialize
|
|
20
|
+
def initialize(block)
|
|
21
|
+
@block = block
|
|
16
22
|
@used = false
|
|
17
23
|
self.num_yields, self.yielded_args = 0, []
|
|
18
24
|
end
|
|
19
25
|
|
|
26
|
+
def has_block?
|
|
27
|
+
Proc === @block
|
|
28
|
+
end
|
|
29
|
+
|
|
20
30
|
def to_proc
|
|
21
31
|
@used = true
|
|
22
32
|
|
|
@@ -24,6 +34,7 @@ module RSpec
|
|
|
24
34
|
Proc.new do |*args|
|
|
25
35
|
probe.num_yields += 1
|
|
26
36
|
probe.yielded_args << args
|
|
37
|
+
nil # to indicate the block does not return a meaningful value
|
|
27
38
|
end
|
|
28
39
|
end
|
|
29
40
|
|
|
@@ -57,71 +68,98 @@ module RSpec
|
|
|
57
68
|
"are."
|
|
58
69
|
end
|
|
59
70
|
|
|
60
|
-
def
|
|
61
|
-
return if block.arity == 1
|
|
71
|
+
def assert_valid_expect_block!
|
|
72
|
+
return if @block.arity == 1
|
|
62
73
|
raise "Your expect block must accept an argument to be used with this " +
|
|
63
74
|
"matcher. Pass the argument as a block on to the method you are testing."
|
|
64
75
|
end
|
|
65
76
|
end
|
|
66
77
|
|
|
78
|
+
# @api private
|
|
79
|
+
# Provides the implementation for `yield_control`.
|
|
80
|
+
# Not intended to be instantiated directly.
|
|
67
81
|
class YieldControl < BaseMatcher
|
|
68
82
|
def initialize
|
|
69
83
|
@expectation_type = nil
|
|
70
84
|
@expected_yields_count = nil
|
|
71
85
|
end
|
|
72
86
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if @expectation_type
|
|
77
|
-
probe.num_yields.send(@expectation_type, @expected_yields_count)
|
|
78
|
-
else
|
|
79
|
-
probe.yielded_once?(:yield_control)
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
87
|
+
# @api public
|
|
88
|
+
# Specifies that the method is expected to yield once.
|
|
83
89
|
def once
|
|
84
90
|
exactly(1)
|
|
85
91
|
self
|
|
86
92
|
end
|
|
87
93
|
|
|
94
|
+
# @api public
|
|
95
|
+
# Specifies that the method is expected to yield once.
|
|
88
96
|
def twice
|
|
89
97
|
exactly(2)
|
|
90
98
|
self
|
|
91
99
|
end
|
|
92
100
|
|
|
101
|
+
# @api public
|
|
102
|
+
# Specifies that the method is expected to yield the given number of times.
|
|
93
103
|
def exactly(number)
|
|
94
104
|
set_expected_yields_count(:==, number)
|
|
95
105
|
self
|
|
96
106
|
end
|
|
97
107
|
|
|
108
|
+
# @api public
|
|
109
|
+
# Specifies the maximum number of times the method is expected to yield
|
|
98
110
|
def at_most(number)
|
|
99
111
|
set_expected_yields_count(:<=, number)
|
|
100
112
|
self
|
|
101
113
|
end
|
|
102
114
|
|
|
115
|
+
# @api public
|
|
116
|
+
# Specifies the minimum number of times the method is expected to yield
|
|
103
117
|
def at_least(number)
|
|
104
118
|
set_expected_yields_count(:>=, number)
|
|
105
119
|
self
|
|
106
120
|
end
|
|
107
121
|
|
|
122
|
+
# @api public
|
|
123
|
+
# No-op. Provides syntactic sugar.
|
|
108
124
|
def times
|
|
109
125
|
self
|
|
110
126
|
end
|
|
111
127
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
# @private
|
|
129
|
+
def matches?(block)
|
|
130
|
+
@probe = YieldProbe.probe(block)
|
|
131
|
+
return false unless @probe.has_block?
|
|
132
|
+
|
|
133
|
+
if @expectation_type
|
|
134
|
+
@probe.num_yields.__send__(@expectation_type, @expected_yields_count)
|
|
135
|
+
else
|
|
136
|
+
@probe.yielded_once?(:yield_control)
|
|
115
137
|
end
|
|
116
138
|
end
|
|
117
139
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
end
|
|
140
|
+
# @private
|
|
141
|
+
def does_not_match?(block)
|
|
142
|
+
!matches?(block) && @probe.has_block?
|
|
122
143
|
end
|
|
123
144
|
|
|
124
|
-
private
|
|
145
|
+
# @api private
|
|
146
|
+
# @return [String]
|
|
147
|
+
def failure_message
|
|
148
|
+
'expected given block to yield control' + failure_reason
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# @api private
|
|
152
|
+
# @return [String]
|
|
153
|
+
def failure_message_when_negated
|
|
154
|
+
'expected given block not to yield control' + failure_reason
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# @private
|
|
158
|
+
def supports_block_expectations?
|
|
159
|
+
true
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
125
163
|
|
|
126
164
|
def set_expected_yields_count(relativity, n)
|
|
127
165
|
@expectation_type = relativity
|
|
@@ -132,7 +170,8 @@ module RSpec
|
|
|
132
170
|
end
|
|
133
171
|
end
|
|
134
172
|
|
|
135
|
-
def
|
|
173
|
+
def failure_reason
|
|
174
|
+
return " but was not a block" unless @probe.has_block?
|
|
136
175
|
return '' unless @expected_yields_count
|
|
137
176
|
" #{human_readable_expecation_type}#{human_readable_count}"
|
|
138
177
|
end
|
|
@@ -154,73 +193,115 @@ module RSpec
|
|
|
154
193
|
end
|
|
155
194
|
end
|
|
156
195
|
|
|
196
|
+
# @api private
|
|
197
|
+
# Provides the implementation for `yield_with_no_args`.
|
|
198
|
+
# Not intended to be instantiated directly.
|
|
157
199
|
class YieldWithNoArgs < BaseMatcher
|
|
158
|
-
|
|
200
|
+
# @private
|
|
159
201
|
def matches?(block)
|
|
160
202
|
@probe = YieldProbe.probe(block)
|
|
203
|
+
return false unless @probe.has_block?
|
|
161
204
|
@probe.yielded_once?(:yield_with_no_args) && @probe.single_yield_args.empty?
|
|
162
205
|
end
|
|
163
206
|
|
|
164
|
-
|
|
165
|
-
|
|
207
|
+
# @private
|
|
208
|
+
def does_not_match?(block)
|
|
209
|
+
!matches?(block) && @probe.has_block?
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# @private
|
|
213
|
+
def failure_message
|
|
214
|
+
"expected given block to yield with no arguments, but #{positive_failure_reason}"
|
|
166
215
|
end
|
|
167
216
|
|
|
168
|
-
|
|
169
|
-
|
|
217
|
+
# @private
|
|
218
|
+
def failure_message_when_negated
|
|
219
|
+
"expected given block not to yield with no arguments, but #{negative_failure_reason}"
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# @private
|
|
223
|
+
def supports_block_expectations?
|
|
224
|
+
true
|
|
170
225
|
end
|
|
171
226
|
|
|
172
227
|
private
|
|
173
228
|
|
|
174
|
-
def
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
229
|
+
def positive_failure_reason
|
|
230
|
+
return "was not a block" unless @probe.has_block?
|
|
231
|
+
return "did not yield" if @probe.num_yields.zero?
|
|
232
|
+
"yielded with arguments: #{@probe.single_yield_args.inspect}"
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def negative_failure_reason
|
|
236
|
+
return "was not a block" unless @probe.has_block?
|
|
237
|
+
"did"
|
|
180
238
|
end
|
|
181
239
|
end
|
|
182
240
|
|
|
241
|
+
# @api private
|
|
242
|
+
# Provides the implementation for `yield_with_args`.
|
|
243
|
+
# Not intended to be instantiated directly.
|
|
183
244
|
class YieldWithArgs
|
|
245
|
+
include Composable
|
|
246
|
+
|
|
184
247
|
def initialize(*args)
|
|
185
248
|
@expected = args
|
|
186
249
|
end
|
|
187
250
|
|
|
251
|
+
# @private
|
|
188
252
|
def matches?(block)
|
|
189
253
|
@probe = YieldProbe.probe(block)
|
|
254
|
+
return false unless @probe.has_block?
|
|
190
255
|
@actual = @probe.single_yield_args
|
|
191
256
|
@probe.yielded_once?(:yield_with_args) && args_match?
|
|
192
257
|
end
|
|
193
|
-
alias == matches?
|
|
194
258
|
|
|
195
|
-
|
|
259
|
+
# @private
|
|
260
|
+
def does_not_match?(block)
|
|
261
|
+
!matches?(block) && @probe.has_block?
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# @private
|
|
265
|
+
def failure_message
|
|
196
266
|
"expected given block to yield with arguments, but #{positive_failure_reason}"
|
|
197
267
|
end
|
|
198
268
|
|
|
199
|
-
|
|
269
|
+
# @private
|
|
270
|
+
def failure_message_when_negated
|
|
200
271
|
"expected given block not to yield with arguments, but #{negative_failure_reason}"
|
|
201
272
|
end
|
|
202
273
|
|
|
274
|
+
# @private
|
|
203
275
|
def description
|
|
204
276
|
desc = "yield with args"
|
|
205
|
-
desc << "(
|
|
277
|
+
desc << "(#{expected_arg_description})" unless @expected.empty?
|
|
206
278
|
desc
|
|
207
279
|
end
|
|
208
280
|
|
|
281
|
+
# @private
|
|
282
|
+
def supports_block_expectations?
|
|
283
|
+
true
|
|
284
|
+
end
|
|
285
|
+
|
|
209
286
|
private
|
|
210
287
|
|
|
211
288
|
def positive_failure_reason
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
289
|
+
return "was not a block" unless @probe.has_block?
|
|
290
|
+
return "did not yield" if @probe.num_yields.zero?
|
|
291
|
+
@positive_args_failure
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def expected_arg_description
|
|
295
|
+
@expected.map { |e| description_of e }.join(", ")
|
|
217
296
|
end
|
|
218
297
|
|
|
219
298
|
def negative_failure_reason
|
|
220
|
-
if
|
|
299
|
+
if !@probe.has_block?
|
|
300
|
+
"was not a block"
|
|
301
|
+
elsif all_args_match?
|
|
221
302
|
"yielded with expected arguments" +
|
|
222
|
-
"\nexpected not: #{@expected.inspect}" +
|
|
223
|
-
"\n got: #{@actual.inspect}
|
|
303
|
+
"\nexpected not: #{surface_descriptions_in(@expected).inspect}" +
|
|
304
|
+
"\n got: #{@actual.inspect}"
|
|
224
305
|
else
|
|
225
306
|
"did"
|
|
226
307
|
end
|
|
@@ -234,60 +315,86 @@ module RSpec
|
|
|
234
315
|
|
|
235
316
|
unless match = all_args_match?
|
|
236
317
|
@positive_args_failure = "yielded with unexpected arguments" +
|
|
237
|
-
"\nexpected: #{@expected.inspect}" +
|
|
238
|
-
"\n got: #{@actual.inspect}
|
|
318
|
+
"\nexpected: #{surface_descriptions_in(@expected).inspect}" +
|
|
319
|
+
"\n got: #{@actual.inspect}"
|
|
239
320
|
end
|
|
240
321
|
|
|
241
322
|
match
|
|
242
323
|
end
|
|
243
324
|
|
|
244
325
|
def all_args_match?
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
@expected.zip(@actual).all? do |expected, actual|
|
|
248
|
-
expected === actual || actual == expected
|
|
249
|
-
end
|
|
326
|
+
values_match?(@expected, @actual)
|
|
250
327
|
end
|
|
251
328
|
end
|
|
252
329
|
|
|
330
|
+
# @api private
|
|
331
|
+
# Provides the implementation for `yield_successive_args`.
|
|
332
|
+
# Not intended to be instantiated directly.
|
|
253
333
|
class YieldSuccessiveArgs
|
|
334
|
+
include Composable
|
|
335
|
+
|
|
254
336
|
def initialize(*args)
|
|
255
337
|
@expected = args
|
|
256
338
|
end
|
|
257
339
|
|
|
340
|
+
# @private
|
|
258
341
|
def matches?(block)
|
|
259
342
|
@probe = YieldProbe.probe(block)
|
|
343
|
+
return false unless @probe.has_block?
|
|
260
344
|
@actual = @probe.successive_yield_args
|
|
261
345
|
args_match?
|
|
262
346
|
end
|
|
263
|
-
alias == matches?
|
|
264
347
|
|
|
265
|
-
def
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
348
|
+
def does_not_match?(block)
|
|
349
|
+
!matches?(block) && @probe.has_block?
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# @private
|
|
353
|
+
def failure_message
|
|
354
|
+
"expected given block to yield successively with arguments, but #{positive_failure_reason}"
|
|
269
355
|
end
|
|
270
356
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
"\n got: #{@actual.inspect} (compared using === and ==)"
|
|
357
|
+
# @private
|
|
358
|
+
def failure_message_when_negated
|
|
359
|
+
"expected given block not to yield successively with arguments, but #{negative_failure_reason}"
|
|
275
360
|
end
|
|
276
361
|
|
|
362
|
+
# @private
|
|
277
363
|
def description
|
|
278
364
|
desc = "yield successive args"
|
|
279
|
-
desc << "(
|
|
365
|
+
desc << "(#{expected_arg_description})"
|
|
280
366
|
desc
|
|
281
367
|
end
|
|
282
368
|
|
|
369
|
+
# @private
|
|
370
|
+
def supports_block_expectations?
|
|
371
|
+
true
|
|
372
|
+
end
|
|
373
|
+
|
|
283
374
|
private
|
|
284
375
|
|
|
285
376
|
def args_match?
|
|
286
|
-
|
|
377
|
+
values_match?(@expected, @actual)
|
|
378
|
+
end
|
|
287
379
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
380
|
+
def expected_arg_description
|
|
381
|
+
@expected.map { |e| description_of e }.join(", ")
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def positive_failure_reason
|
|
385
|
+
return "was not a block" unless @probe.has_block?
|
|
386
|
+
|
|
387
|
+
"yielded with unexpected arguments" +
|
|
388
|
+
"\nexpected: #{surface_descriptions_in(@expected).inspect}" +
|
|
389
|
+
"\n got: #{@actual.inspect}"
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def negative_failure_reason
|
|
393
|
+
return "was not a block" unless @probe.has_block?
|
|
394
|
+
|
|
395
|
+
"yielded with expected arguments" +
|
|
396
|
+
"\nexpected not: #{surface_descriptions_in(@expected).inspect}" +
|
|
397
|
+
"\n got: #{@actual.inspect}"
|
|
291
398
|
end
|
|
292
399
|
end
|
|
293
400
|
end
|
|
@@ -1,39 +1,51 @@
|
|
|
1
|
+
RSpec::Support.require_rspec_matchers "built_in/base_matcher"
|
|
2
|
+
|
|
1
3
|
module RSpec
|
|
2
4
|
module Matchers
|
|
5
|
+
# Container module for all built-in matchers. The matcher classes are here
|
|
6
|
+
# (rather than directly under `RSpec::Matchers`) in order to prevent name
|
|
7
|
+
# collisions, since `RSpec::Matchers` gets included into the user's namespace.
|
|
8
|
+
#
|
|
9
|
+
# Autoloading is used to delay when the matcher classes get loaded, allowing
|
|
10
|
+
# rspec-matchers to boot faster, and avoiding loading matchers the user is
|
|
11
|
+
# not using.
|
|
3
12
|
module BuiltIn
|
|
4
|
-
|
|
5
|
-
autoload :BeAnInstanceOf,
|
|
6
|
-
autoload :
|
|
7
|
-
autoload :
|
|
8
|
-
autoload :
|
|
9
|
-
autoload :
|
|
10
|
-
autoload :
|
|
11
|
-
autoload :BePredicate,
|
|
12
|
-
autoload :
|
|
13
|
-
autoload :BeWithin,
|
|
14
|
-
autoload :Change,
|
|
15
|
-
autoload :
|
|
16
|
-
autoload :
|
|
17
|
-
autoload :
|
|
18
|
-
autoload :
|
|
19
|
-
autoload :
|
|
20
|
-
autoload :
|
|
21
|
-
autoload :
|
|
22
|
-
autoload :
|
|
23
|
-
autoload :
|
|
24
|
-
autoload :
|
|
25
|
-
autoload :
|
|
26
|
-
autoload :
|
|
27
|
-
autoload :
|
|
28
|
-
autoload :
|
|
29
|
-
autoload :
|
|
30
|
-
autoload :
|
|
31
|
-
autoload :
|
|
32
|
-
autoload :
|
|
33
|
-
autoload :
|
|
34
|
-
autoload :
|
|
13
|
+
autoload :BeAKindOf, 'rspec/matchers/built_in/be_kind_of'
|
|
14
|
+
autoload :BeAnInstanceOf, 'rspec/matchers/built_in/be_instance_of'
|
|
15
|
+
autoload :BeBetween, 'rspec/matchers/built_in/be_between'
|
|
16
|
+
autoload :Be, 'rspec/matchers/built_in/be'
|
|
17
|
+
autoload :BeComparedTo, 'rspec/matchers/built_in/be'
|
|
18
|
+
autoload :BeFalsey, 'rspec/matchers/built_in/be'
|
|
19
|
+
autoload :BeNil, 'rspec/matchers/built_in/be'
|
|
20
|
+
autoload :BePredicate, 'rspec/matchers/built_in/be'
|
|
21
|
+
autoload :BeTruthy, 'rspec/matchers/built_in/be'
|
|
22
|
+
autoload :BeWithin, 'rspec/matchers/built_in/be_within'
|
|
23
|
+
autoload :Change, 'rspec/matchers/built_in/change'
|
|
24
|
+
autoload :Compound, 'rspec/matchers/built_in/compound'
|
|
25
|
+
autoload :ContainExactly, 'rspec/matchers/built_in/contain_exactly'
|
|
26
|
+
autoload :Cover, 'rspec/matchers/built_in/cover'
|
|
27
|
+
autoload :EndWith, 'rspec/matchers/built_in/start_and_end_with'
|
|
28
|
+
autoload :Eq, 'rspec/matchers/built_in/eq'
|
|
29
|
+
autoload :Eql, 'rspec/matchers/built_in/eql'
|
|
30
|
+
autoload :Equal, 'rspec/matchers/built_in/equal'
|
|
31
|
+
autoload :Exist, 'rspec/matchers/built_in/exist'
|
|
32
|
+
autoload :Has, 'rspec/matchers/built_in/has'
|
|
33
|
+
autoload :Include, 'rspec/matchers/built_in/include'
|
|
34
|
+
autoload :All, 'rspec/matchers/built_in/all'
|
|
35
|
+
autoload :Match, 'rspec/matchers/built_in/match'
|
|
36
|
+
autoload :NegativeOperatorMatcher, 'rspec/matchers/built_in/operators'
|
|
37
|
+
autoload :OperatorMatcher, 'rspec/matchers/built_in/operators'
|
|
38
|
+
autoload :Output, 'rspec/matchers/built_in/output'
|
|
39
|
+
autoload :PositiveOperatorMatcher, 'rspec/matchers/built_in/operators'
|
|
40
|
+
autoload :RaiseError, 'rspec/matchers/built_in/raise_error'
|
|
41
|
+
autoload :RespondTo, 'rspec/matchers/built_in/respond_to'
|
|
42
|
+
autoload :Satisfy, 'rspec/matchers/built_in/satisfy'
|
|
43
|
+
autoload :StartWith, 'rspec/matchers/built_in/start_and_end_with'
|
|
44
|
+
autoload :ThrowSymbol, 'rspec/matchers/built_in/throw_symbol'
|
|
45
|
+
autoload :YieldControl, 'rspec/matchers/built_in/yield'
|
|
46
|
+
autoload :YieldSuccessiveArgs, 'rspec/matchers/built_in/yield'
|
|
47
|
+
autoload :YieldWithArgs, 'rspec/matchers/built_in/yield'
|
|
48
|
+
autoload :YieldWithNoArgs, 'rspec/matchers/built_in/yield'
|
|
35
49
|
end
|
|
36
50
|
end
|
|
37
51
|
end
|
|
38
|
-
|
|
39
|
-
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
RSpec::Support.require_rspec_support "fuzzy_matcher"
|
|
2
|
+
|
|
3
|
+
module RSpec
|
|
4
|
+
module Matchers
|
|
5
|
+
# Mixin designed to support the composable matcher features
|
|
6
|
+
# of RSpec 3+. Mix it into your custom matcher classes to
|
|
7
|
+
# allow them to be used in a composable fashion.
|
|
8
|
+
#
|
|
9
|
+
# @api public
|
|
10
|
+
module Composable
|
|
11
|
+
# Creates a compound `and` expectation. The matcher will
|
|
12
|
+
# only pass if both sub-matchers pass.
|
|
13
|
+
# This can be chained together to form an arbitrarily long
|
|
14
|
+
# chain of matchers.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# expect(alphabet).to start_with("a").and end_with("z")
|
|
18
|
+
# expect(alphabet).to start_with("a") & end_with("z")
|
|
19
|
+
#
|
|
20
|
+
# @note The negative form (`expect(...).not_to matcher.and other`)
|
|
21
|
+
# is not supported at this time.
|
|
22
|
+
def and(matcher)
|
|
23
|
+
BuiltIn::Compound::And.new self, matcher
|
|
24
|
+
end
|
|
25
|
+
alias & and
|
|
26
|
+
|
|
27
|
+
# Creates a compound `or` expectation. The matcher will
|
|
28
|
+
# pass if either sub-matcher passes.
|
|
29
|
+
# This can be chained together to form an arbitrarily long
|
|
30
|
+
# chain of matchers.
|
|
31
|
+
#
|
|
32
|
+
# @example
|
|
33
|
+
# expect(stoplight.color).to eq("red").or eq("green").or eq("yellow")
|
|
34
|
+
# expect(stoplight.color).to eq("red") | eq("green") | eq("yellow")
|
|
35
|
+
#
|
|
36
|
+
# @note The negative form (`expect(...).not_to matcher.or other`)
|
|
37
|
+
# is not supported at this time.
|
|
38
|
+
def or(matcher)
|
|
39
|
+
BuiltIn::Compound::Or.new self, matcher
|
|
40
|
+
end
|
|
41
|
+
alias | or
|
|
42
|
+
|
|
43
|
+
# Delegates to `#matches?`. Allows matchers to be used in composable
|
|
44
|
+
# fashion and also supports using matchers in case statements.
|
|
45
|
+
def ===(value)
|
|
46
|
+
matches?(value)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# This provides a generic way to fuzzy-match an expected value against
|
|
52
|
+
# an actual value. It understands nested data structures (e.g. hashes
|
|
53
|
+
# and arrays) and is able to match against a matcher being used as
|
|
54
|
+
# the expected value or within the expected value at any level of
|
|
55
|
+
# nesting.
|
|
56
|
+
#
|
|
57
|
+
# Within a custom matcher you are encouraged to use this whenever your
|
|
58
|
+
# matcher needs to match two values, unless it needs more precise semantics.
|
|
59
|
+
# For example, the `eq` matcher _does not_ use this as it is meant to
|
|
60
|
+
# use `==` (and only `==`) for matching.
|
|
61
|
+
#
|
|
62
|
+
# @param expected [Object] what is expected
|
|
63
|
+
# @param actual [Object] the actual value
|
|
64
|
+
#
|
|
65
|
+
# @!visibility public
|
|
66
|
+
def values_match?(expected, actual)
|
|
67
|
+
expected = with_matchers_cloned(expected)
|
|
68
|
+
Support::FuzzyMatcher.values_match?(expected, actual)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Returns the description of the given object in a way that is
|
|
72
|
+
# aware of composed matchers. If the object is a matcher with
|
|
73
|
+
# a `description` method, returns the description; otherwise
|
|
74
|
+
# returns `object.inspect`.
|
|
75
|
+
#
|
|
76
|
+
# You are encouraged to use this in your custom matcher's
|
|
77
|
+
# `description`, `failure_message` or
|
|
78
|
+
# `failure_message_when_negated` implementation if you are
|
|
79
|
+
# supporting matcher arguments.
|
|
80
|
+
#
|
|
81
|
+
# @!visibility public
|
|
82
|
+
def description_of(object)
|
|
83
|
+
return object.description if Matchers.is_a_describable_matcher?(object)
|
|
84
|
+
object.inspect
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Transforms the given data structue (typically a hash or array)
|
|
88
|
+
# into a new data structure that, when `#inspect` is called on it,
|
|
89
|
+
# will provide descriptions of any contained matchers rather than
|
|
90
|
+
# the normal `#inspect` output.
|
|
91
|
+
#
|
|
92
|
+
# You are encouraged to use this in your custom matcher's
|
|
93
|
+
# `description`, `failure_message` or
|
|
94
|
+
# `failure_message_when_negated` implementation if you are
|
|
95
|
+
# supporting any arguments which may be a data structure
|
|
96
|
+
# containing matchers.
|
|
97
|
+
#
|
|
98
|
+
# @!visibility public
|
|
99
|
+
def surface_descriptions_in(item)
|
|
100
|
+
if Matchers.is_a_describable_matcher?(item)
|
|
101
|
+
DescribableItem.new(item)
|
|
102
|
+
elsif Hash === item
|
|
103
|
+
Hash[ surface_descriptions_in(item.to_a) ]
|
|
104
|
+
elsif enumerable?(item)
|
|
105
|
+
begin
|
|
106
|
+
item.map { |subitem| surface_descriptions_in(subitem) }
|
|
107
|
+
rescue IOError # STDOUT is enumerable but `map` raises an error
|
|
108
|
+
item.inspect
|
|
109
|
+
end
|
|
110
|
+
else
|
|
111
|
+
item
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @private
|
|
116
|
+
# Historically, a single matcher instance was only checked
|
|
117
|
+
# against a single value. Given that the matcher was only
|
|
118
|
+
# used once, it's been common to memoize some intermediate
|
|
119
|
+
# calculation that is derived from the `actual` value in
|
|
120
|
+
# order to reuse that intermediate result in the failure
|
|
121
|
+
# message.
|
|
122
|
+
#
|
|
123
|
+
# This can cause a problem when using such a matcher as an
|
|
124
|
+
# argument to another matcher in a composed matcher expression,
|
|
125
|
+
# since the matcher instance may be checked against multiple
|
|
126
|
+
# values and produce invalid results due to the memoization.
|
|
127
|
+
#
|
|
128
|
+
# To deal with this, we clone any matchers in `expected` via
|
|
129
|
+
# this method when using `values_match?`, so that any memoization
|
|
130
|
+
# does not "leak" between checks.
|
|
131
|
+
def with_matchers_cloned(object)
|
|
132
|
+
if Matchers.is_a_matcher?(object)
|
|
133
|
+
object.clone
|
|
134
|
+
elsif Hash === object
|
|
135
|
+
Hash[ with_matchers_cloned(object.to_a) ]
|
|
136
|
+
elsif enumerable?(object)
|
|
137
|
+
begin
|
|
138
|
+
object.map { |subobject| with_matchers_cloned(subobject) }
|
|
139
|
+
rescue IOError # STDOUT is enumerable but `map` raises an error
|
|
140
|
+
object
|
|
141
|
+
end
|
|
142
|
+
else
|
|
143
|
+
object
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
if String.ancestors.include?(Enumerable) # 1.8.7
|
|
148
|
+
# Strings are not enumerable on 1.9, and on 1.8 they are an infinitely
|
|
149
|
+
# nested enumerable: since ruby lacks a character class, it yields
|
|
150
|
+
# 1-character strings, which are themselves enumerable, composed of a
|
|
151
|
+
# a single 1-character string, which is an enumerable, etc.
|
|
152
|
+
#
|
|
153
|
+
# @api private
|
|
154
|
+
def enumerable?(item)
|
|
155
|
+
return false if String === item
|
|
156
|
+
Enumerable === item
|
|
157
|
+
end
|
|
158
|
+
else
|
|
159
|
+
# @api private
|
|
160
|
+
def enumerable?(item)
|
|
161
|
+
Enumerable === item
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
module_function :surface_descriptions_in, :enumerable?
|
|
165
|
+
|
|
166
|
+
# Wraps an item in order to surface its `description` via `inspect`.
|
|
167
|
+
# @api private
|
|
168
|
+
DescribableItem = Struct.new(:item) do
|
|
169
|
+
def inspect
|
|
170
|
+
"(#{item.description})"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def pretty_print(pp)
|
|
174
|
+
pp.text "(#{item.description})"
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|