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.
Files changed (143) hide show
  1. checksums.yaml +15 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/Changelog.md +369 -25
  4. data/License.txt +1 -0
  5. data/README.md +88 -15
  6. data/lib/rspec/{matchers → expectations}/configuration.rb +44 -14
  7. data/lib/rspec/expectations/expectation_target.rb +67 -33
  8. data/lib/rspec/expectations/fail_with.rb +11 -53
  9. data/lib/rspec/expectations/handler.rb +126 -31
  10. data/lib/rspec/expectations/minitest_integration.rb +16 -0
  11. data/lib/rspec/expectations/syntax.rb +60 -94
  12. data/lib/rspec/expectations/version.rb +1 -1
  13. data/lib/rspec/expectations.rb +45 -23
  14. data/lib/rspec/matchers/aliased_matcher.rb +41 -0
  15. data/lib/rspec/matchers/built_in/all.rb +78 -0
  16. data/lib/rspec/matchers/built_in/base_matcher.rb +63 -15
  17. data/lib/rspec/matchers/built_in/be.rb +170 -81
  18. data/lib/rspec/matchers/built_in/be_between.rb +77 -0
  19. data/lib/rspec/matchers/built_in/be_instance_of.rb +11 -3
  20. data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -0
  21. data/lib/rspec/matchers/built_in/be_within.rb +39 -15
  22. data/lib/rspec/matchers/built_in/change.rb +273 -80
  23. data/lib/rspec/matchers/built_in/compound.rb +122 -0
  24. data/lib/rspec/matchers/built_in/contain_exactly.rb +256 -0
  25. data/lib/rspec/matchers/built_in/cover.rb +3 -0
  26. data/lib/rspec/matchers/built_in/eq.rb +60 -6
  27. data/lib/rspec/matchers/built_in/eql.rb +17 -5
  28. data/lib/rspec/matchers/built_in/equal.rb +55 -21
  29. data/lib/rspec/matchers/built_in/exist.rb +74 -10
  30. data/lib/rspec/matchers/built_in/has.rb +71 -15
  31. data/lib/rspec/matchers/built_in/include.rb +64 -27
  32. data/lib/rspec/matchers/built_in/match.rb +19 -1
  33. data/lib/rspec/matchers/built_in/operators.rb +119 -0
  34. data/lib/rspec/matchers/built_in/output.rb +150 -0
  35. data/lib/rspec/matchers/built_in/raise_error.rb +84 -42
  36. data/lib/rspec/matchers/built_in/respond_to.rb +42 -20
  37. data/lib/rspec/matchers/built_in/satisfy.rb +19 -3
  38. data/lib/rspec/matchers/built_in/start_and_end_with.rb +54 -18
  39. data/lib/rspec/matchers/built_in/throw_symbol.rb +38 -8
  40. data/lib/rspec/matchers/built_in/yield.rb +175 -68
  41. data/lib/rspec/matchers/built_in.rb +45 -33
  42. data/lib/rspec/matchers/composable.rb +179 -0
  43. data/lib/rspec/matchers/dsl.rb +362 -5
  44. data/lib/rspec/matchers/generated_descriptions.rb +13 -6
  45. data/lib/rspec/matchers/matcher_delegator.rb +37 -0
  46. data/lib/rspec/matchers/pretty.rb +27 -20
  47. data/lib/rspec/matchers.rb +361 -136
  48. data.tar.gz.sig +1 -0
  49. metadata +99 -215
  50. metadata.gz.sig +0 -0
  51. data/features/README.md +0 -48
  52. data/features/Upgrade.md +0 -53
  53. data/features/built_in_matchers/README.md +0 -90
  54. data/features/built_in_matchers/be.feature +0 -175
  55. data/features/built_in_matchers/be_within.feature +0 -48
  56. data/features/built_in_matchers/cover.feature +0 -47
  57. data/features/built_in_matchers/end_with.feature +0 -48
  58. data/features/built_in_matchers/equality.feature +0 -139
  59. data/features/built_in_matchers/exist.feature +0 -45
  60. data/features/built_in_matchers/expect_change.feature +0 -59
  61. data/features/built_in_matchers/expect_error.feature +0 -144
  62. data/features/built_in_matchers/have.feature +0 -109
  63. data/features/built_in_matchers/include.feature +0 -174
  64. data/features/built_in_matchers/match.feature +0 -52
  65. data/features/built_in_matchers/operators.feature +0 -227
  66. data/features/built_in_matchers/predicates.feature +0 -137
  67. data/features/built_in_matchers/respond_to.feature +0 -84
  68. data/features/built_in_matchers/satisfy.feature +0 -33
  69. data/features/built_in_matchers/start_with.feature +0 -48
  70. data/features/built_in_matchers/throw_symbol.feature +0 -91
  71. data/features/built_in_matchers/types.feature +0 -116
  72. data/features/built_in_matchers/yield.feature +0 -161
  73. data/features/custom_matchers/access_running_example.feature +0 -53
  74. data/features/custom_matchers/define_diffable_matcher.feature +0 -27
  75. data/features/custom_matchers/define_matcher.feature +0 -368
  76. data/features/custom_matchers/define_matcher_outside_rspec.feature +0 -38
  77. data/features/custom_matchers/define_matcher_with_fluent_interface.feature +0 -24
  78. data/features/customized_message.feature +0 -22
  79. data/features/diffing.feature +0 -85
  80. data/features/implicit_docstrings.feature +0 -52
  81. data/features/step_definitions/additional_cli_steps.rb +0 -22
  82. data/features/support/env.rb +0 -14
  83. data/features/syntax_configuration.feature +0 -71
  84. data/features/test_frameworks/test_unit.feature +0 -44
  85. data/lib/rspec/expectations/deprecation.rb +0 -17
  86. data/lib/rspec/expectations/differ.rb +0 -133
  87. data/lib/rspec/expectations/errors.rb +0 -9
  88. data/lib/rspec/expectations/extensions/array.rb +0 -9
  89. data/lib/rspec/expectations/extensions/object.rb +0 -29
  90. data/lib/rspec/expectations/extensions.rb +0 -2
  91. data/lib/rspec/matchers/be_close.rb +0 -9
  92. data/lib/rspec/matchers/built_in/have.rb +0 -124
  93. data/lib/rspec/matchers/built_in/match_array.rb +0 -51
  94. data/lib/rspec/matchers/compatibility.rb +0 -14
  95. data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
  96. data/lib/rspec/matchers/matcher.rb +0 -300
  97. data/lib/rspec/matchers/method_missing.rb +0 -12
  98. data/lib/rspec/matchers/operator_matcher.rb +0 -109
  99. data/lib/rspec/matchers/test_unit_integration.rb +0 -11
  100. data/lib/rspec-expectations.rb +0 -1
  101. data/spec/rspec/expectations/differ_spec.rb +0 -192
  102. data/spec/rspec/expectations/expectation_target_spec.rb +0 -82
  103. data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
  104. data/spec/rspec/expectations/fail_with_spec.rb +0 -114
  105. data/spec/rspec/expectations/handler_spec.rb +0 -227
  106. data/spec/rspec/expectations/syntax_spec.rb +0 -139
  107. data/spec/rspec/matchers/base_matcher_spec.rb +0 -62
  108. data/spec/rspec/matchers/be_close_spec.rb +0 -22
  109. data/spec/rspec/matchers/be_instance_of_spec.rb +0 -63
  110. data/spec/rspec/matchers/be_kind_of_spec.rb +0 -41
  111. data/spec/rspec/matchers/be_spec.rb +0 -516
  112. data/spec/rspec/matchers/be_within_spec.rb +0 -137
  113. data/spec/rspec/matchers/change_spec.rb +0 -553
  114. data/spec/rspec/matchers/configuration_spec.rb +0 -206
  115. data/spec/rspec/matchers/cover_spec.rb +0 -69
  116. data/spec/rspec/matchers/description_generation_spec.rb +0 -190
  117. data/spec/rspec/matchers/dsl_spec.rb +0 -57
  118. data/spec/rspec/matchers/eq_spec.rb +0 -60
  119. data/spec/rspec/matchers/eql_spec.rb +0 -41
  120. data/spec/rspec/matchers/equal_spec.rb +0 -78
  121. data/spec/rspec/matchers/exist_spec.rb +0 -124
  122. data/spec/rspec/matchers/has_spec.rb +0 -122
  123. data/spec/rspec/matchers/have_spec.rb +0 -455
  124. data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
  125. data/spec/rspec/matchers/include_spec.rb +0 -531
  126. data/spec/rspec/matchers/match_array_spec.rb +0 -194
  127. data/spec/rspec/matchers/match_spec.rb +0 -61
  128. data/spec/rspec/matchers/matcher_spec.rb +0 -471
  129. data/spec/rspec/matchers/matchers_spec.rb +0 -37
  130. data/spec/rspec/matchers/method_missing_spec.rb +0 -28
  131. data/spec/rspec/matchers/operator_matcher_spec.rb +0 -223
  132. data/spec/rspec/matchers/raise_error_spec.rb +0 -485
  133. data/spec/rspec/matchers/respond_to_spec.rb +0 -292
  134. data/spec/rspec/matchers/satisfy_spec.rb +0 -44
  135. data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -186
  136. data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
  137. data/spec/rspec/matchers/yield_spec.rb +0 -514
  138. data/spec/spec_helper.rb +0 -54
  139. data/spec/support/classes.rb +0 -56
  140. data/spec/support/in_sub_process.rb +0 -38
  141. data/spec/support/matchers.rb +0 -22
  142. data/spec/support/ruby_version.rb +0 -10
  143. 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
- assert_valid_expect_block!(block)
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 self.assert_valid_expect_block!(block)
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
- def matches?(block)
74
- probe = YieldProbe.probe(block)
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
- def failure_message_for_should
113
- 'expected given block to yield control'.tap do |failure_message|
114
- failure_message << relativity_failure_message
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
- def failure_message_for_should_not
119
- 'expected given block not to yield control'.tap do |failure_message|
120
- failure_message << relativity_failure_message
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 relativity_failure_message
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
- def failure_message_for_should
165
- "expected given block to yield with no arguments, but #{failure_reason}"
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
- def failure_message_for_should_not
169
- "expected given block not to yield with no arguments, but did"
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 failure_reason
175
- if @probe.num_yields.zero?
176
- "did not yield"
177
- else
178
- "yielded with arguments: #{@probe.single_yield_args.inspect}"
179
- end
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
- def failure_message_for_should
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
- def failure_message_for_should_not
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 << "(" + @expected.map { |e| e.inspect }.join(", ") + ")" unless @expected.empty?
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
- if @probe.num_yields.zero?
213
- "did not yield"
214
- else
215
- @positive_args_failure
216
- end
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 all_args_match?
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} (compared using === and ==)"
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} (compared using === and ==)"
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
- return false if @expected.size != @actual.size
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 failure_message_for_should
266
- "expected given block to yield successively with arguments, but yielded with unexpected arguments" +
267
- "\nexpected: #{@expected.inspect}" +
268
- "\n got: #{@actual.inspect} (compared using === and ==)"
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
- def failure_message_for_should_not
272
- "expected given block not to yield successively with arguments, but yielded with expected arguments" +
273
- "\nexpected not: #{@expected.inspect}" +
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 << "(" + @expected.map { |e| e.inspect }.join(", ") + ")"
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
- return false if @expected.size != @actual.size
377
+ values_match?(@expected, @actual)
378
+ end
287
379
 
288
- @expected.zip(@actual).all? do |expected, actual|
289
- expected === actual || actual == expected
290
- end
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
- require 'rspec/matchers/built_in/base_matcher'
5
- autoload :BeAnInstanceOf, 'rspec/matchers/built_in/be_instance_of'
6
- autoload :Be, 'rspec/matchers/built_in/be'
7
- autoload :BeTrue, 'rspec/matchers/built_in/be'
8
- autoload :BeFalse, 'rspec/matchers/built_in/be'
9
- autoload :BeNil, 'rspec/matchers/built_in/be'
10
- autoload :BeComparedTo, 'rspec/matchers/built_in/be'
11
- autoload :BePredicate, 'rspec/matchers/built_in/be'
12
- autoload :BeAKindOf, 'rspec/matchers/built_in/be_kind_of'
13
- autoload :BeWithin, 'rspec/matchers/built_in/be_within'
14
- autoload :Change, 'rspec/matchers/built_in/change'
15
- autoload :Cover, 'rspec/matchers/built_in/cover' if (1..2).respond_to?(:cover?)
16
- autoload :Eq, 'rspec/matchers/built_in/eq'
17
- autoload :Eql, 'rspec/matchers/built_in/eql'
18
- autoload :Equal, 'rspec/matchers/built_in/equal'
19
- autoload :Exist, 'rspec/matchers/built_in/exist'
20
- autoload :Has, 'rspec/matchers/built_in/has'
21
- autoload :Have, 'rspec/matchers/built_in/have'
22
- autoload :Include, 'rspec/matchers/built_in/include'
23
- autoload :Match, 'rspec/matchers/built_in/match'
24
- autoload :MatchArray, 'rspec/matchers/built_in/match_array'
25
- autoload :RaiseError, 'rspec/matchers/built_in/raise_error'
26
- autoload :RespondTo, 'rspec/matchers/built_in/respond_to'
27
- autoload :StartWith, 'rspec/matchers/built_in/start_and_end_with'
28
- autoload :EndWith, 'rspec/matchers/built_in/start_and_end_with'
29
- autoload :Satisfy, 'rspec/matchers/built_in/satisfy'
30
- autoload :ThrowSymbol, 'rspec/matchers/built_in/throw_symbol'
31
- autoload :YieldControl, 'rspec/matchers/built_in/yield'
32
- autoload :YieldWithArgs, 'rspec/matchers/built_in/yield'
33
- autoload :YieldWithNoArgs, 'rspec/matchers/built_in/yield'
34
- autoload :YieldSuccessiveArgs, 'rspec/matchers/built_in/yield'
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