rspec-expectations 3.3.1 → 3.4.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +1 -1
- data/Changelog.md +26 -0
- data/{License.txt → LICENSE.md} +5 -4
- data/README.md +18 -4
- data/lib/rspec/expectations/failure_aggregator.rb +1 -1
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/matchers.rb +28 -0
- data/lib/rspec/matchers/built_in/base_matcher.rb +6 -6
- data/lib/rspec/matchers/built_in/be.rb +1 -1
- data/lib/rspec/matchers/built_in/change.rb +30 -13
- data/lib/rspec/matchers/built_in/compound.rb +4 -25
- data/lib/rspec/matchers/built_in/contain_exactly.rb +40 -7
- data/lib/rspec/matchers/built_in/match.rb +70 -1
- data/lib/rspec/matchers/built_in/raise_error.rb +17 -5
- data/lib/rspec/matchers/built_in/yield.rb +40 -39
- data/lib/rspec/matchers/composable.rb +16 -15
- metadata +8 -8
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ecfc9ad6573cbd51308c24b96e60534b430370e8
|
|
4
|
+
data.tar.gz: ee7aec158266ec8c5902def9e5777d0f47a4eb18
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 747ae15f2866456d52996626be3ce0f131beac9c92ab447d865c052e9c6211b0eacac9f1c80d2140ef8185e6c1d3f45e88ac49d48c0b23346df325f00f84678c
|
|
7
|
+
data.tar.gz: 55853e5d95bf9019031a1652cf6d8b553d65ebea517b309514275faab04b66a2a713d07ab6021eb96df3d537a106d39bde239933722f2f76f236ae7518aa84a3
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data.tar.gz.sig
CHANGED
|
Binary file
|
data/.document
CHANGED
data/.yardopts
CHANGED
data/Changelog.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
### 3.4.0 / 2015-11-11
|
|
2
|
+
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.3.1...v3.4.0)
|
|
3
|
+
|
|
4
|
+
Enhancements:
|
|
5
|
+
|
|
6
|
+
* Warn when `RSpec::Matchers` is included in a superclass after it has
|
|
7
|
+
already been included in a subclass on MRI 1.9, since that situation
|
|
8
|
+
can cause uses of `super` to trigger infinite recursion. (Myron Marston, #816)
|
|
9
|
+
* Stop rescuing `NoMemoryError`, `SignalExcepetion`, `Interrupt` and
|
|
10
|
+
`SystemExit`. It is dangerous to interfere with these. (Myron Marston, #845)
|
|
11
|
+
* Add `#with_captures` to the
|
|
12
|
+
[match matcher](https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/match-matcher)
|
|
13
|
+
which allows a user to specify expected captures when matching a regex
|
|
14
|
+
against a string. (Sam Phippen, #848)
|
|
15
|
+
* Always print compound failure messages in the multi-line form. Trying
|
|
16
|
+
to print it all on a single line didn't read very well. (Myron Marston, #859)
|
|
17
|
+
|
|
18
|
+
Bug Fixes:
|
|
19
|
+
|
|
20
|
+
* Fix failure message from dynamic predicate matchers when the object
|
|
21
|
+
does not respond to the predicate so that it is inspected rather
|
|
22
|
+
than relying upon it's `to_s` -- that way for `nil`, `"nil"` is
|
|
23
|
+
printed rather than an empty string. (Myron Marston, #841)
|
|
24
|
+
* Fix SystemStackError raised when diffing an Enumerable object
|
|
25
|
+
whose `#each` includes the object itself. (Yuji Nakayama, #857)
|
|
26
|
+
|
|
1
27
|
### 3.3.1 / 2015-07-15
|
|
2
28
|
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.3.0...v3.3.1)
|
|
3
29
|
|
data/{License.txt → LICENSE.md}
RENAMED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
=====================
|
|
2
3
|
|
|
3
|
-
Copyright
|
|
4
|
-
Copyright
|
|
5
|
-
Copyright
|
|
4
|
+
* Copyright © 2012 David Chelimsky, Myron Marston
|
|
5
|
+
* Copyright © 2006 David Chelimsky, The RSpec Development Team
|
|
6
|
+
* Copyright © 2005 Steven Baker
|
|
6
7
|
|
|
7
8
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
8
9
|
a copy of this software and associated documentation files (the
|
data/README.md
CHANGED
|
@@ -27,6 +27,20 @@ Minitest, or Cucumber, you can install it directly:
|
|
|
27
27
|
|
|
28
28
|
gem install rspec-expectations
|
|
29
29
|
|
|
30
|
+
## Contributing
|
|
31
|
+
|
|
32
|
+
Once you've set up the environment, you'll need to cd into the working
|
|
33
|
+
directory of whichever repo you want to work in. From there you can run the
|
|
34
|
+
specs and cucumber features, and make patches.
|
|
35
|
+
|
|
36
|
+
NOTE: You do not need to use rspec-dev to work on a specific RSpec repo. You
|
|
37
|
+
can treat each RSpec repo as an independent project.
|
|
38
|
+
|
|
39
|
+
- [Build details](BUILD_DETAIL.md)
|
|
40
|
+
- [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
41
|
+
- [Detailed contributing guide](CONTRIBUTING.md)
|
|
42
|
+
- [Development setup guide](DEVELOPMENT.md)
|
|
43
|
+
|
|
30
44
|
## Basic usage
|
|
31
45
|
|
|
32
46
|
Here's an example using rspec-core:
|
|
@@ -283,7 +297,7 @@ end
|
|
|
283
297
|
|
|
284
298
|
## Also see
|
|
285
299
|
|
|
286
|
-
* [
|
|
287
|
-
* [
|
|
288
|
-
* [
|
|
289
|
-
* [
|
|
300
|
+
* [https://github.com/rspec/rspec](https://github.com/rspec/rspec)
|
|
301
|
+
* [https://github.com/rspec/rspec-core](https://github.com/rspec/rspec-core)
|
|
302
|
+
* [https://github.com/rspec/rspec-mocks](https://github.com/rspec/rspec-mocks)
|
|
303
|
+
* [https://github.com/rspec/rspec-rails](https://github.com/rspec/rspec-rails)
|
|
@@ -15,7 +15,7 @@ module RSpec
|
|
|
15
15
|
# of `failures` rather than letting it fall through and be categorized as part of
|
|
16
16
|
# `other_errors`.
|
|
17
17
|
failures << e
|
|
18
|
-
rescue
|
|
18
|
+
rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
|
|
19
19
|
# While it is normally a bad practice to rescue `Exception`, it's important we do
|
|
20
20
|
# so here. It's low risk (`notify_aggregated_failures` below will re-raise the exception,
|
|
21
21
|
# or raise a `MultipleExpectationsNotMetError` that includes the exception), and it's
|
data/lib/rspec/matchers.rb
CHANGED
|
@@ -1005,5 +1005,33 @@ module RSpec
|
|
|
1005
1005
|
def self.is_a_describable_matcher?(obj)
|
|
1006
1006
|
is_a_matcher?(obj) && obj.respond_to?(:description)
|
|
1007
1007
|
end
|
|
1008
|
+
|
|
1009
|
+
if RSpec::Support::Ruby.mri? && RUBY_VERSION[0, 3] == '1.9'
|
|
1010
|
+
# @api private
|
|
1011
|
+
# Note that `included` doesn't work for this because it is triggered
|
|
1012
|
+
# _after_ `RSpec::Matchers` is an ancestor of the inclusion host, rather
|
|
1013
|
+
# than _before_, like `append_features`. It's important we check this before
|
|
1014
|
+
# in order to find the cases where it was already previously included.
|
|
1015
|
+
def self.append_features(mod)
|
|
1016
|
+
return super if mod < self # `mod < self` indicates a re-inclusion.
|
|
1017
|
+
|
|
1018
|
+
subclasses = ObjectSpace.each_object(Class).select { |c| c < mod && c < self }
|
|
1019
|
+
return super unless subclasses.any?
|
|
1020
|
+
|
|
1021
|
+
subclasses.reject! { |s| subclasses.any? { |s2| s < s2 } } # Filter to the root ancestor.
|
|
1022
|
+
subclasses = subclasses.map { |s| "`#{s}`" }.join(", ")
|
|
1023
|
+
|
|
1024
|
+
RSpec.warning "`#{self}` has been included in a superclass (`#{mod}`) " \
|
|
1025
|
+
"after previously being included in subclasses (#{subclasses}), " \
|
|
1026
|
+
"which can trigger infinite recursion from `super` due to an MRI 1.9 bug " \
|
|
1027
|
+
"(https://redmine.ruby-lang.org/issues/3351). To work around this, " \
|
|
1028
|
+
"either upgrade to MRI 2.0+, include a dup of the module (e.g. " \
|
|
1029
|
+
"`include #{self}.dup`), or find a way to include `#{self}` in `#{mod}` " \
|
|
1030
|
+
"before it is included in subclasses (#{subclasses}). See " \
|
|
1031
|
+
"https://github.com/rspec/rspec-expectations/issues/814 for more info"
|
|
1032
|
+
|
|
1033
|
+
super
|
|
1034
|
+
end
|
|
1035
|
+
end
|
|
1008
1036
|
end
|
|
1009
1037
|
end
|
|
@@ -8,8 +8,8 @@ module RSpec
|
|
|
8
8
|
#
|
|
9
9
|
# ### Warning:
|
|
10
10
|
#
|
|
11
|
-
# This class is for internal use, and subject to change without notice.
|
|
12
|
-
# strongly recommend that you do not base your custom matchers on this
|
|
11
|
+
# This class is for internal use, and subject to change without notice.
|
|
12
|
+
# We strongly recommend that you do not base your custom matchers on this
|
|
13
13
|
# class. If/when this changes, we will announce it and remove this warning.
|
|
14
14
|
class BaseMatcher
|
|
15
15
|
include RSpec::Matchers::Composable
|
|
@@ -92,7 +92,7 @@ module RSpec
|
|
|
92
92
|
|
|
93
93
|
# @private
|
|
94
94
|
def self.matcher_name
|
|
95
|
-
@matcher_name ||= underscore(name.split(
|
|
95
|
+
@matcher_name ||= underscore(name.split('::').last)
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
# @private
|
|
@@ -101,7 +101,7 @@ module RSpec
|
|
|
101
101
|
word = camel_cased_word.to_s.dup
|
|
102
102
|
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
103
103
|
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
|
104
|
-
word.tr!(
|
|
104
|
+
word.tr!('-', '_')
|
|
105
105
|
word.downcase!
|
|
106
106
|
word
|
|
107
107
|
end
|
|
@@ -118,7 +118,7 @@ module RSpec
|
|
|
118
118
|
if RUBY_VERSION.to_f < 1.9
|
|
119
119
|
# :nocov:
|
|
120
120
|
def present_ivars
|
|
121
|
-
instance_variables.map
|
|
121
|
+
instance_variables.map(&:to_sym)
|
|
122
122
|
end
|
|
123
123
|
# :nocov:
|
|
124
124
|
else
|
|
@@ -168,7 +168,7 @@ module RSpec
|
|
|
168
168
|
# @private
|
|
169
169
|
def self.has_default_failure_messages?(matcher)
|
|
170
170
|
matcher.method(:failure_message).owner == self &&
|
|
171
|
-
|
|
171
|
+
matcher.method(:failure_message_when_negated).owner == self
|
|
172
172
|
rescue NameError
|
|
173
173
|
false
|
|
174
174
|
end
|
|
@@ -267,7 +267,7 @@ module RSpec
|
|
|
267
267
|
def validity_message
|
|
268
268
|
return nil if predicate_accessible?
|
|
269
269
|
|
|
270
|
-
msg = "expected #{
|
|
270
|
+
msg = "expected #{actual_formatted} to respond to `#{predicate}`"
|
|
271
271
|
|
|
272
272
|
if private_predicate?
|
|
273
273
|
msg << " but `#{predicate}` is a private method"
|
|
@@ -58,13 +58,15 @@ module RSpec
|
|
|
58
58
|
# @api private
|
|
59
59
|
# @return [String]
|
|
60
60
|
def failure_message
|
|
61
|
-
"expected #{@change_details.message} to have changed,
|
|
61
|
+
"expected #{@change_details.message} to have changed, " \
|
|
62
|
+
"but #{positive_failure_reason}"
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
# @api private
|
|
65
66
|
# @return [String]
|
|
66
67
|
def failure_message_when_negated
|
|
67
|
-
"expected #{@change_details.message} not to have changed,
|
|
68
|
+
"expected #{@change_details.message} not to have changed, " \
|
|
69
|
+
"but #{negative_failure_reason}"
|
|
68
70
|
end
|
|
69
71
|
|
|
70
72
|
# @api private
|
|
@@ -96,7 +98,8 @@ module RSpec
|
|
|
96
98
|
|
|
97
99
|
def negative_failure_reason
|
|
98
100
|
return "was not given a block" unless Proc === @event_proc
|
|
99
|
-
"did change from #{description_of @change_details.actual_before}
|
|
101
|
+
"did change from #{description_of @change_details.actual_before} " \
|
|
102
|
+
"to #{description_of @change_details.actual_after}"
|
|
100
103
|
end
|
|
101
104
|
end
|
|
102
105
|
|
|
@@ -112,7 +115,9 @@ module RSpec
|
|
|
112
115
|
|
|
113
116
|
# @private
|
|
114
117
|
def failure_message
|
|
115
|
-
"expected #{@change_details.message} to have changed
|
|
118
|
+
"expected #{@change_details.message} to have changed " \
|
|
119
|
+
"#{@relativity.to_s.tr('_', ' ')} " \
|
|
120
|
+
"#{description_of @expected_delta}, but #{failure_reason}"
|
|
116
121
|
end
|
|
117
122
|
|
|
118
123
|
# @private
|
|
@@ -125,12 +130,14 @@ module RSpec
|
|
|
125
130
|
|
|
126
131
|
# @private
|
|
127
132
|
def does_not_match?(_event_proc)
|
|
128
|
-
raise NotImplementedError, "`expect { }.not_to change
|
|
133
|
+
raise NotImplementedError, "`expect { }.not_to change " \
|
|
134
|
+
"{ }.#{@relativity}()` is not supported"
|
|
129
135
|
end
|
|
130
136
|
|
|
131
137
|
# @private
|
|
132
138
|
def description
|
|
133
|
-
"change #{@change_details.message}
|
|
139
|
+
"change #{@change_details.message} " \
|
|
140
|
+
"#{@relativity.to_s.tr('_', ' ')} #{description_of @expected_delta}"
|
|
134
141
|
end
|
|
135
142
|
|
|
136
143
|
# @private
|
|
@@ -195,23 +202,31 @@ module RSpec
|
|
|
195
202
|
end
|
|
196
203
|
|
|
197
204
|
def before_value_failure
|
|
198
|
-
"expected #{@change_details.message}
|
|
205
|
+
"expected #{@change_details.message} " \
|
|
206
|
+
"to have initially been #{description_of @expected_before}, " \
|
|
207
|
+
"but was #{description_of @change_details.actual_before}"
|
|
199
208
|
end
|
|
200
209
|
|
|
201
210
|
def after_value_failure
|
|
202
|
-
"expected #{@change_details.message}
|
|
211
|
+
"expected #{@change_details.message} " \
|
|
212
|
+
"to have changed to #{description_of @expected_after}, " \
|
|
213
|
+
"but is now #{description_of @change_details.actual_after}"
|
|
203
214
|
end
|
|
204
215
|
|
|
205
216
|
def did_not_change_failure
|
|
206
|
-
"expected #{@change_details.message}
|
|
217
|
+
"expected #{@change_details.message} " \
|
|
218
|
+
"to have changed #{change_description}, but did not change"
|
|
207
219
|
end
|
|
208
220
|
|
|
209
221
|
def did_change_failure
|
|
210
|
-
"expected #{@change_details.message} not to have changed, but
|
|
222
|
+
"expected #{@change_details.message} not to have changed, but " \
|
|
223
|
+
"did change from #{description_of @change_details.actual_before} " \
|
|
224
|
+
"to #{description_of @change_details.actual_after}"
|
|
211
225
|
end
|
|
212
226
|
|
|
213
227
|
def not_given_a_block_failure
|
|
214
|
-
"expected #{@change_details.message} to have changed
|
|
228
|
+
"expected #{@change_details.message} to have changed " \
|
|
229
|
+
"#{change_description}, but was not given a block"
|
|
215
230
|
end
|
|
216
231
|
end
|
|
217
232
|
|
|
@@ -235,7 +250,8 @@ module RSpec
|
|
|
235
250
|
# @private
|
|
236
251
|
def does_not_match?(event_proc)
|
|
237
252
|
if @description_suffix
|
|
238
|
-
raise NotImplementedError, "`expect { }.not_to change { }.to()`
|
|
253
|
+
raise NotImplementedError, "`expect { }.not_to change { }.to()` " \
|
|
254
|
+
"is not supported"
|
|
239
255
|
end
|
|
240
256
|
|
|
241
257
|
@event_proc = event_proc
|
|
@@ -277,7 +293,8 @@ module RSpec
|
|
|
277
293
|
|
|
278
294
|
# @private
|
|
279
295
|
def does_not_match?(_event_proc)
|
|
280
|
-
raise NotImplementedError, "`expect { }.not_to change { }.to()`
|
|
296
|
+
raise NotImplementedError, "`expect { }.not_to change { }.to()` " \
|
|
297
|
+
"is not supported"
|
|
281
298
|
end
|
|
282
299
|
|
|
283
300
|
private
|
|
@@ -24,7 +24,7 @@ module RSpec
|
|
|
24
24
|
# @api private
|
|
25
25
|
# @return [String]
|
|
26
26
|
def description
|
|
27
|
-
|
|
27
|
+
"#{matcher_1.description} #{conjunction} #{matcher_2.description}"
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def supports_block_expectations?
|
|
@@ -84,30 +84,9 @@ module RSpec
|
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def compound_failure_message
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if multiline?(message_1) || multiline?(message_2)
|
|
91
|
-
multiline_message(message_1, message_2)
|
|
92
|
-
else
|
|
93
|
-
singleline_message(message_1, message_2)
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def multiline_message(message_1, message_2)
|
|
98
|
-
[
|
|
99
|
-
indent_multiline_message(message_1.sub(/\n+\z/, '')),
|
|
100
|
-
"...#{conjunction}:",
|
|
101
|
-
indent_multiline_message(message_2.sub(/\A\n+/, ''))
|
|
102
|
-
].join("\n\n")
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def multiline?(message)
|
|
106
|
-
message.lines.count > 1
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def singleline_message(message_1, message_2)
|
|
110
|
-
[message_1, conjunction, message_2].join(' ')
|
|
87
|
+
"#{indent_multiline_message(matcher_1.failure_message.sub(/\n+\z/, ''))}" \
|
|
88
|
+
"\n\n...#{conjunction}:" \
|
|
89
|
+
"\n\n#{indent_multiline_message(matcher_2.failure_message.sub(/\A\n+/, ''))}"
|
|
111
90
|
end
|
|
112
91
|
|
|
113
92
|
def matcher_1_matches?
|
|
@@ -9,11 +9,7 @@ module RSpec
|
|
|
9
9
|
# @return [String]
|
|
10
10
|
def failure_message
|
|
11
11
|
if Array === actual
|
|
12
|
-
|
|
13
|
-
message += "actual collection contained: #{description_of(safe_sort(actual))}\n"
|
|
14
|
-
message += "the missing elements were: #{description_of(safe_sort(surface_descriptions_in missing_items))}\n" unless missing_items.empty?
|
|
15
|
-
message += "the extra elements were: #{description_of(safe_sort(extra_items))}\n" unless extra_items.empty?
|
|
16
|
-
message
|
|
12
|
+
generate_failure_message
|
|
17
13
|
else
|
|
18
14
|
"expected a collection that can be converted to an array with " \
|
|
19
15
|
"`#to_ary` or `#to_a`, but got #{actual_formatted}"
|
|
@@ -36,6 +32,43 @@ module RSpec
|
|
|
36
32
|
|
|
37
33
|
private
|
|
38
34
|
|
|
35
|
+
def generate_failure_message
|
|
36
|
+
message = expected_collection_line
|
|
37
|
+
message += actual_collection_line
|
|
38
|
+
message += missing_elements_line unless missing_items.empty?
|
|
39
|
+
message += extra_elements_line unless extra_items.empty?
|
|
40
|
+
message
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def expected_collection_line
|
|
44
|
+
message_line('expected collection contained', expected, true)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def actual_collection_line
|
|
48
|
+
message_line('actual collection contained', actual)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def missing_elements_line
|
|
52
|
+
message_line('the missing elements were', missing_items, true)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def extra_elements_line
|
|
56
|
+
message_line('the extra elements were', extra_items)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def describe_collection(collection, surface_descriptions=false)
|
|
60
|
+
if surface_descriptions
|
|
61
|
+
"#{description_of(safe_sort(surface_descriptions_in collection))}\n"
|
|
62
|
+
else
|
|
63
|
+
"#{description_of(safe_sort(collection))}\n"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def message_line(prefix, collection, surface_descriptions=false)
|
|
68
|
+
"%-32s%s" % [prefix + ':',
|
|
69
|
+
describe_collection(collection, surface_descriptions)]
|
|
70
|
+
end
|
|
71
|
+
|
|
39
72
|
def match(_expected, _actual)
|
|
40
73
|
return false unless convert_actual_to_an_array
|
|
41
74
|
match_when_sorted? || (extra_items.empty? && missing_items.empty?)
|
|
@@ -61,7 +94,7 @@ module RSpec
|
|
|
61
94
|
|
|
62
95
|
def safe_sort(array)
|
|
63
96
|
array.sort
|
|
64
|
-
rescue
|
|
97
|
+
rescue Support::AllExceptionsExceptOnesWeMustNotRescue
|
|
65
98
|
array
|
|
66
99
|
end
|
|
67
100
|
|
|
@@ -231,7 +264,7 @@ module RSpec
|
|
|
231
264
|
|
|
232
265
|
modified_expecteds.delete(expected_index)
|
|
233
266
|
|
|
234
|
-
modified_actuals
|
|
267
|
+
modified_actuals = apply_pairing_to(
|
|
235
268
|
solution.indeterminate_actual_indexes,
|
|
236
269
|
actual_to_expected_matched_indexes, expected_index)
|
|
237
270
|
|
|
@@ -5,10 +5,19 @@ module RSpec
|
|
|
5
5
|
# Provides the implementation for `match`.
|
|
6
6
|
# Not intended to be instantiated directly.
|
|
7
7
|
class Match < BaseMatcher
|
|
8
|
+
def initialize(expected)
|
|
9
|
+
super(expected)
|
|
10
|
+
|
|
11
|
+
@expected_captures = nil
|
|
12
|
+
end
|
|
8
13
|
# @api private
|
|
9
14
|
# @return [String]
|
|
10
15
|
def description
|
|
11
|
-
|
|
16
|
+
if @expected_captures && @expected.match(actual)
|
|
17
|
+
"match #{surface_descriptions_in(expected).inspect} with captures #{surface_descriptions_in(@expected_captures).inspect}"
|
|
18
|
+
else
|
|
19
|
+
"match #{surface_descriptions_in(expected).inspect}"
|
|
20
|
+
end
|
|
12
21
|
end
|
|
13
22
|
|
|
14
23
|
# @api private
|
|
@@ -17,9 +26,17 @@ module RSpec
|
|
|
17
26
|
true
|
|
18
27
|
end
|
|
19
28
|
|
|
29
|
+
# Used to specify the captures we match against
|
|
30
|
+
# @return [self]
|
|
31
|
+
def with_captures(*captures)
|
|
32
|
+
@expected_captures = captures
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
20
36
|
private
|
|
21
37
|
|
|
22
38
|
def match(expected, actual)
|
|
39
|
+
return match_captures(expected, actual) if @expected_captures
|
|
23
40
|
return true if values_match?(expected, actual)
|
|
24
41
|
return false unless can_safely_call_match?(expected, actual)
|
|
25
42
|
actual.match(expected)
|
|
@@ -31,6 +48,58 @@ module RSpec
|
|
|
31
48
|
!(RSpec::Matchers.is_a_matcher?(expected) &&
|
|
32
49
|
(String === actual || Regexp === actual))
|
|
33
50
|
end
|
|
51
|
+
|
|
52
|
+
def match_captures(expected, actual)
|
|
53
|
+
match = actual.match(expected)
|
|
54
|
+
if match
|
|
55
|
+
match = ReliableMatchData.new(match)
|
|
56
|
+
if match.names.empty?
|
|
57
|
+
values_match?(@expected_captures, match.captures)
|
|
58
|
+
else
|
|
59
|
+
expected_matcher = @expected_captures.last
|
|
60
|
+
values_match?(expected_matcher, Hash[match.names.zip(match.captures)]) ||
|
|
61
|
+
values_match?(expected_matcher, Hash[match.names.map(&:to_sym).zip(match.captures)]) ||
|
|
62
|
+
values_match?(@expected_captures, match.captures)
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
false
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @api private
|
|
71
|
+
# Used to wrap match data and make it reliable for 1.8.7
|
|
72
|
+
class ReliableMatchData
|
|
73
|
+
def initialize(match_data)
|
|
74
|
+
@match_data = match_data
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
if RUBY_VERSION == "1.8.7"
|
|
78
|
+
# @api private
|
|
79
|
+
# Returns match data names for named captures
|
|
80
|
+
# @return Array
|
|
81
|
+
def names
|
|
82
|
+
[]
|
|
83
|
+
end
|
|
84
|
+
else
|
|
85
|
+
# @api private
|
|
86
|
+
# Returns match data names for named captures
|
|
87
|
+
# @return Array
|
|
88
|
+
def names
|
|
89
|
+
match_data.names
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# @api private
|
|
94
|
+
# returns an array of captures from the match data
|
|
95
|
+
# @return Array
|
|
96
|
+
def captures
|
|
97
|
+
match_data.captures
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
protected
|
|
101
|
+
|
|
102
|
+
attr_reader :match_data
|
|
34
103
|
end
|
|
35
104
|
end
|
|
36
105
|
end
|
|
@@ -5,6 +5,7 @@ module RSpec
|
|
|
5
5
|
# Provides the implementation for `raise_error`.
|
|
6
6
|
# Not intended to be instantiated directly.
|
|
7
7
|
# rubocop:disable ClassLength
|
|
8
|
+
# rubocop:disable RescueException
|
|
8
9
|
class RaiseError
|
|
9
10
|
include Composable
|
|
10
11
|
|
|
@@ -15,11 +16,14 @@ module RSpec
|
|
|
15
16
|
|
|
16
17
|
case expected_error_or_message
|
|
17
18
|
when nil
|
|
18
|
-
@expected_error
|
|
19
|
+
@expected_error = Exception
|
|
20
|
+
@expected_message = expected_message
|
|
19
21
|
when String
|
|
20
|
-
@expected_error
|
|
22
|
+
@expected_error = Exception
|
|
23
|
+
@expected_message = expected_error_or_message
|
|
21
24
|
else
|
|
22
|
-
@expected_error
|
|
25
|
+
@expected_error = expected_error_or_message
|
|
26
|
+
@expected_message = expected_message
|
|
23
27
|
end
|
|
24
28
|
end
|
|
25
29
|
|
|
@@ -42,7 +46,6 @@ module RSpec
|
|
|
42
46
|
@eval_block = false
|
|
43
47
|
@eval_block_passed = false
|
|
44
48
|
|
|
45
|
-
warn_about_bare_error if warning_about_bare_error && !negative_expectation
|
|
46
49
|
return false unless Proc === given_proc
|
|
47
50
|
|
|
48
51
|
begin
|
|
@@ -55,6 +58,7 @@ module RSpec
|
|
|
55
58
|
end
|
|
56
59
|
end
|
|
57
60
|
|
|
61
|
+
warn_about_bare_error if warning_about_bare_error && !negative_expectation
|
|
58
62
|
eval_block if !negative_expectation && ready_to_eval_block?
|
|
59
63
|
|
|
60
64
|
expectation_matched?
|
|
@@ -156,6 +160,7 @@ module RSpec
|
|
|
156
160
|
"will match when Ruby raises a `NoMethodError`, `NameError` or " \
|
|
157
161
|
"`ArgumentError`, potentially allowing the expectation to pass " \
|
|
158
162
|
"without even executing the method you are intending to call. " \
|
|
163
|
+
"#{warning}"\
|
|
159
164
|
"Instead consider providing a specific error class or message. " \
|
|
160
165
|
"This message can be supressed by setting: " \
|
|
161
166
|
"`RSpec::Expectations.configuration.warn_about_potential_false_positives = false`")
|
|
@@ -207,9 +212,16 @@ module RSpec
|
|
|
207
212
|
end
|
|
208
213
|
|
|
209
214
|
def raise_message_already_set
|
|
210
|
-
raise "`expect { }.to raise_error(message).with_message(message)` is not valid.
|
|
215
|
+
raise "`expect { }.to raise_error(message).with_message(message)` is not valid. " \
|
|
216
|
+
'The matcher only allows the expected message to be specified once'
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def warning
|
|
220
|
+
warning = "Actual error raised was #{description_of(@actual_error)}. "
|
|
221
|
+
warning if @actual_error
|
|
211
222
|
end
|
|
212
223
|
end
|
|
224
|
+
# rubocop:enable RescueException
|
|
213
225
|
# rubocop:enable ClassLength
|
|
214
226
|
end
|
|
215
227
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
RSpec::Support.require_rspec_support
|
|
1
|
+
RSpec::Support.require_rspec_support 'method_signature_verifier'
|
|
2
2
|
|
|
3
3
|
module RSpec
|
|
4
4
|
module Matchers
|
|
@@ -22,7 +22,8 @@ module RSpec
|
|
|
22
22
|
def initialize(block)
|
|
23
23
|
@block = block
|
|
24
24
|
@used = false
|
|
25
|
-
self.num_yields
|
|
25
|
+
self.num_yields = 0
|
|
26
|
+
self.yielded_args = []
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def has_block?
|
|
@@ -50,8 +51,8 @@ module RSpec
|
|
|
50
51
|
when 0 then false
|
|
51
52
|
else
|
|
52
53
|
raise "The #{matcher_name} matcher is not designed to be used with a " \
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
'method that yields multiple times. Use the yield_successive_args ' \
|
|
55
|
+
'matcher for that case.'
|
|
55
56
|
end
|
|
56
57
|
end
|
|
57
58
|
|
|
@@ -63,19 +64,19 @@ module RSpec
|
|
|
63
64
|
|
|
64
65
|
def assert_used!
|
|
65
66
|
return if @used
|
|
66
|
-
raise
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
raise 'You must pass the argument yielded to your expect block on ' \
|
|
68
|
+
'to the method-under-test as a block. It acts as a probe that ' \
|
|
69
|
+
'allows the matcher to detect whether or not the method-under-test ' \
|
|
70
|
+
'yields, and, if so, how many times, and what the yielded arguments ' \
|
|
71
|
+
'are.'
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
if RUBY_VERSION.to_f > 1.8
|
|
74
75
|
def assert_valid_expect_block!
|
|
75
76
|
block_signature = RSpec::Support::BlockSignature.new(@block)
|
|
76
77
|
return if RSpec::Support::StrictSignatureVerifier.new(block_signature, [self]).valid?
|
|
77
|
-
raise
|
|
78
|
-
|
|
78
|
+
raise 'Your expect block must accept an argument to be used with this ' \
|
|
79
|
+
'matcher. Pass the argument as a block on to the method you are testing.'
|
|
79
80
|
end
|
|
80
81
|
else
|
|
81
82
|
# :nocov:
|
|
@@ -190,7 +191,7 @@ module RSpec
|
|
|
190
191
|
end
|
|
191
192
|
|
|
192
193
|
def failure_reason
|
|
193
|
-
return
|
|
194
|
+
return ' but was not a block' unless @probe.has_block?
|
|
194
195
|
return '' unless @expected_yields_count
|
|
195
196
|
" #{human_readable_expectation_type}#{human_readable_count(@expected_yields_count)}" \
|
|
196
197
|
" but yielded #{human_readable_count(@probe.num_yields)}"
|
|
@@ -206,8 +207,8 @@ module RSpec
|
|
|
206
207
|
|
|
207
208
|
def human_readable_count(count)
|
|
208
209
|
case count
|
|
209
|
-
when 1 then
|
|
210
|
-
when 2 then
|
|
210
|
+
when 1 then 'once'
|
|
211
|
+
when 2 then 'twice'
|
|
211
212
|
else "#{count} times"
|
|
212
213
|
end
|
|
213
214
|
end
|
|
@@ -247,14 +248,14 @@ module RSpec
|
|
|
247
248
|
private
|
|
248
249
|
|
|
249
250
|
def positive_failure_reason
|
|
250
|
-
return
|
|
251
|
-
return
|
|
251
|
+
return 'was not a block' unless @probe.has_block?
|
|
252
|
+
return 'did not yield' if @probe.num_yields.zero?
|
|
252
253
|
"yielded with arguments: #{description_of @probe.single_yield_args}"
|
|
253
254
|
end
|
|
254
255
|
|
|
255
256
|
def negative_failure_reason
|
|
256
|
-
return
|
|
257
|
-
|
|
257
|
+
return 'was not a block' unless @probe.has_block?
|
|
258
|
+
'did'
|
|
258
259
|
end
|
|
259
260
|
end
|
|
260
261
|
|
|
@@ -291,7 +292,7 @@ module RSpec
|
|
|
291
292
|
|
|
292
293
|
# @private
|
|
293
294
|
def description
|
|
294
|
-
desc =
|
|
295
|
+
desc = 'yield with args'
|
|
295
296
|
desc << "(#{expected_arg_description})" unless @expected.empty?
|
|
296
297
|
desc
|
|
297
298
|
end
|
|
@@ -304,36 +305,36 @@ module RSpec
|
|
|
304
305
|
private
|
|
305
306
|
|
|
306
307
|
def positive_failure_reason
|
|
307
|
-
return
|
|
308
|
-
return
|
|
308
|
+
return 'was not a block' unless @probe.has_block?
|
|
309
|
+
return 'did not yield' if @probe.num_yields.zero?
|
|
309
310
|
@positive_args_failure
|
|
310
311
|
end
|
|
311
312
|
|
|
312
313
|
def expected_arg_description
|
|
313
|
-
@expected.map { |e| description_of e }.join(
|
|
314
|
+
@expected.map { |e| description_of e }.join(', ')
|
|
314
315
|
end
|
|
315
316
|
|
|
316
317
|
def negative_failure_reason
|
|
317
318
|
if !@probe.has_block?
|
|
318
|
-
|
|
319
|
+
'was not a block'
|
|
319
320
|
elsif all_args_match?
|
|
320
|
-
|
|
321
|
-
"\nexpected not: #{surface_descriptions_in(@expected).inspect}"
|
|
321
|
+
'yielded with expected arguments' \
|
|
322
|
+
"\nexpected not: #{surface_descriptions_in(@expected).inspect}" \
|
|
322
323
|
"\n got: #{actual_formatted}"
|
|
323
324
|
else
|
|
324
|
-
|
|
325
|
+
'did'
|
|
325
326
|
end
|
|
326
327
|
end
|
|
327
328
|
|
|
328
329
|
def args_match?
|
|
329
330
|
if @expected.empty? # expect {...}.to yield_with_args
|
|
330
|
-
@positive_args_failure =
|
|
331
|
+
@positive_args_failure = 'yielded with no arguments' if @actual.empty?
|
|
331
332
|
return !@actual.empty?
|
|
332
333
|
end
|
|
333
334
|
|
|
334
335
|
unless (match = all_args_match?)
|
|
335
|
-
@positive_args_failure =
|
|
336
|
-
"\nexpected: #{surface_descriptions_in(@expected).inspect}"
|
|
336
|
+
@positive_args_failure = 'yielded with unexpected arguments' \
|
|
337
|
+
"\nexpected: #{surface_descriptions_in(@expected).inspect}" \
|
|
337
338
|
"\n got: #{actual_formatted}"
|
|
338
339
|
end
|
|
339
340
|
|
|
@@ -367,19 +368,19 @@ module RSpec
|
|
|
367
368
|
|
|
368
369
|
# @private
|
|
369
370
|
def failure_message
|
|
370
|
-
|
|
371
|
+
'expected given block to yield successively with arguments, ' \
|
|
372
|
+
"but #{positive_failure_reason}"
|
|
371
373
|
end
|
|
372
374
|
|
|
373
375
|
# @private
|
|
374
376
|
def failure_message_when_negated
|
|
375
|
-
|
|
377
|
+
'expected given block not to yield successively with arguments, ' \
|
|
378
|
+
"but #{negative_failure_reason}"
|
|
376
379
|
end
|
|
377
380
|
|
|
378
381
|
# @private
|
|
379
382
|
def description
|
|
380
|
-
|
|
381
|
-
desc << "(#{expected_arg_description})"
|
|
382
|
-
desc
|
|
383
|
+
"yield successive args(#{expected_arg_description})"
|
|
383
384
|
end
|
|
384
385
|
|
|
385
386
|
# @private
|
|
@@ -394,21 +395,21 @@ module RSpec
|
|
|
394
395
|
end
|
|
395
396
|
|
|
396
397
|
def expected_arg_description
|
|
397
|
-
@expected.map { |e| description_of e }.join(
|
|
398
|
+
@expected.map { |e| description_of e }.join(', ')
|
|
398
399
|
end
|
|
399
400
|
|
|
400
401
|
def positive_failure_reason
|
|
401
|
-
return
|
|
402
|
+
return 'was not a block' unless @probe.has_block?
|
|
402
403
|
|
|
403
|
-
|
|
404
|
+
'yielded with unexpected arguments' \
|
|
404
405
|
"\nexpected: #{surface_descriptions_in(@expected).inspect}" \
|
|
405
406
|
"\n got: #{actual_formatted}"
|
|
406
407
|
end
|
|
407
408
|
|
|
408
409
|
def negative_failure_reason
|
|
409
|
-
return
|
|
410
|
+
return 'was not a block' unless @probe.has_block?
|
|
410
411
|
|
|
411
|
-
|
|
412
|
+
'yielded with expected arguments' \
|
|
412
413
|
"\nexpected not: #{surface_descriptions_in(@expected).inspect}" \
|
|
413
414
|
"\n got: #{actual_formatted}"
|
|
414
415
|
end
|
|
@@ -100,14 +100,10 @@ module RSpec
|
|
|
100
100
|
DescribableItem.new(item)
|
|
101
101
|
elsif Hash === item
|
|
102
102
|
Hash[surface_descriptions_in(item.to_a)]
|
|
103
|
-
elsif Struct === item
|
|
103
|
+
elsif Struct === item || unreadable_io?(item)
|
|
104
104
|
RSpec::Support::ObjectFormatter.format(item)
|
|
105
105
|
elsif should_enumerate?(item)
|
|
106
|
-
|
|
107
|
-
item.map { |subitem| surface_descriptions_in(subitem) }
|
|
108
|
-
rescue IOError # STDOUT is enumerable but `map` raises an error
|
|
109
|
-
RSpec::Support::ObjectFormatter.format(item)
|
|
110
|
-
end
|
|
106
|
+
item.map { |subitem| surface_descriptions_in(subitem) }
|
|
111
107
|
else
|
|
112
108
|
item
|
|
113
109
|
end
|
|
@@ -134,14 +130,10 @@ module RSpec
|
|
|
134
130
|
object.clone
|
|
135
131
|
elsif Hash === object
|
|
136
132
|
Hash[with_matchers_cloned(object.to_a)]
|
|
137
|
-
elsif Struct === object
|
|
133
|
+
elsif Struct === object || unreadable_io?(object)
|
|
138
134
|
object
|
|
139
135
|
elsif should_enumerate?(object)
|
|
140
|
-
|
|
141
|
-
object.map { |subobject| with_matchers_cloned(subobject) }
|
|
142
|
-
rescue IOError # STDOUT is enumerable but `map` raises an error
|
|
143
|
-
object
|
|
144
|
-
end
|
|
136
|
+
object.map { |subobject| with_matchers_cloned(subobject) }
|
|
145
137
|
else
|
|
146
138
|
object
|
|
147
139
|
end
|
|
@@ -157,16 +149,25 @@ module RSpec
|
|
|
157
149
|
# @api private
|
|
158
150
|
def should_enumerate?(item)
|
|
159
151
|
return false if String === item
|
|
160
|
-
Enumerable === item && !(Range === item)
|
|
152
|
+
Enumerable === item && !(Range === item) && item.none? { |subitem| subitem.equal?(item) }
|
|
161
153
|
end
|
|
162
154
|
# :nocov:
|
|
163
155
|
else
|
|
164
156
|
# @api private
|
|
165
157
|
def should_enumerate?(item)
|
|
166
|
-
Enumerable === item && !(Range === item)
|
|
158
|
+
Enumerable === item && !(Range === item) && item.none? { |subitem| subitem.equal?(item) }
|
|
167
159
|
end
|
|
168
160
|
end
|
|
169
|
-
|
|
161
|
+
|
|
162
|
+
# @api private
|
|
163
|
+
def unreadable_io?(object)
|
|
164
|
+
return false unless IO === object
|
|
165
|
+
object.each {} # STDOUT is enumerable but raises an error
|
|
166
|
+
false
|
|
167
|
+
rescue IOError
|
|
168
|
+
true
|
|
169
|
+
end
|
|
170
|
+
module_function :surface_descriptions_in, :should_enumerate?, :unreadable_io?
|
|
170
171
|
|
|
171
172
|
# Wraps an item in order to surface its `description` via `inspect`.
|
|
172
173
|
# @api private
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rspec-expectations
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steven Baker
|
|
@@ -45,7 +45,7 @@ cert_chain:
|
|
|
45
45
|
ZsVDj6a7lH3cNqtWXZxrb2wO38qV5AkYj8SQK7Hj3/Yui9myUX3crr+PdetazSqQ
|
|
46
46
|
F3MdtaDehhjC
|
|
47
47
|
-----END CERTIFICATE-----
|
|
48
|
-
date: 2015-
|
|
48
|
+
date: 2015-11-12 00:00:00.000000000 Z
|
|
49
49
|
dependencies:
|
|
50
50
|
- !ruby/object:Gem::Dependency
|
|
51
51
|
name: rspec-support
|
|
@@ -53,14 +53,14 @@ dependencies:
|
|
|
53
53
|
requirements:
|
|
54
54
|
- - "~>"
|
|
55
55
|
- !ruby/object:Gem::Version
|
|
56
|
-
version: 3.
|
|
56
|
+
version: 3.4.0
|
|
57
57
|
type: :runtime
|
|
58
58
|
prerelease: false
|
|
59
59
|
version_requirements: !ruby/object:Gem::Requirement
|
|
60
60
|
requirements:
|
|
61
61
|
- - "~>"
|
|
62
62
|
- !ruby/object:Gem::Version
|
|
63
|
-
version: 3.
|
|
63
|
+
version: 3.4.0
|
|
64
64
|
- !ruby/object:Gem::Dependency
|
|
65
65
|
name: diff-lcs
|
|
66
66
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -115,14 +115,14 @@ dependencies:
|
|
|
115
115
|
requirements:
|
|
116
116
|
- - "~>"
|
|
117
117
|
- !ruby/object:Gem::Version
|
|
118
|
-
version:
|
|
118
|
+
version: 0.6.2
|
|
119
119
|
type: :development
|
|
120
120
|
prerelease: false
|
|
121
121
|
version_requirements: !ruby/object:Gem::Requirement
|
|
122
122
|
requirements:
|
|
123
123
|
- - "~>"
|
|
124
124
|
- !ruby/object:Gem::Version
|
|
125
|
-
version:
|
|
125
|
+
version: 0.6.2
|
|
126
126
|
- !ruby/object:Gem::Dependency
|
|
127
127
|
name: minitest
|
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -147,7 +147,7 @@ files:
|
|
|
147
147
|
- ".document"
|
|
148
148
|
- ".yardopts"
|
|
149
149
|
- Changelog.md
|
|
150
|
-
-
|
|
150
|
+
- LICENSE.md
|
|
151
151
|
- README.md
|
|
152
152
|
- lib/rspec/expectations.rb
|
|
153
153
|
- lib/rspec/expectations/configuration.rb
|
|
@@ -220,6 +220,6 @@ rubyforge_project:
|
|
|
220
220
|
rubygems_version: 2.2.2
|
|
221
221
|
signing_key:
|
|
222
222
|
specification_version: 4
|
|
223
|
-
summary: rspec-expectations-3.
|
|
223
|
+
summary: rspec-expectations-3.4.0
|
|
224
224
|
test_files: []
|
|
225
225
|
has_rdoc:
|
metadata.gz.sig
CHANGED
|
Binary file
|