rspec-expectations 3.6.0.beta2 → 3.6.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/Changelog.md +21 -1
- data/README.md +5 -3
- data/lib/rspec/expectations.rb +2 -1
- data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/matchers.rb +60 -73
- data/lib/rspec/matchers/built_in/base_matcher.rb +12 -0
- data/lib/rspec/matchers/built_in/be.rb +1 -1
- data/lib/rspec/matchers/built_in/change.rb +60 -27
- data/lib/rspec/matchers/built_in/raise_error.rb +1 -1
- data/lib/rspec/matchers/built_in/satisfy.rb +27 -4
- data/lib/rspec/matchers/built_in/yield.rb +42 -29
- data/lib/rspec/matchers/dsl.rb +64 -3
- metadata +11 -10
- 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: aa835825e71953a32260e80c58d892f5517ed692
|
4
|
+
data.tar.gz: 3e66073a9dbefb71be893d00692df5162826105f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e967e93899006e4a6c9b419b217f6cac32aee74d60d006724db428e4d574fea2be76499759348808e50f9ba69a7e34d9472aa02eb7b23cca04e56ef9beab6743
|
7
|
+
data.tar.gz: 96f5ea4360e26175df7dfaae04a9f76c0d1d273ccca0e8939722947e59c7d09fd3a0c3e29eb27035101060cd80d540b90cfb6abcb4b7a40d60d5f8ae5a307a16
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/Changelog.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
### 3.6.0 / 2017-05-04
|
2
|
+
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.6.0.beta2...v3.6.0)
|
3
|
+
|
4
|
+
Enhancements:
|
5
|
+
|
6
|
+
* Treat NoMethodError as a failure for comparison matchers. (Jon Rowe, #972)
|
7
|
+
* Allow for scoped aliased and negated matchers--just call
|
8
|
+
`alias_matcher` or `define_negated_matcher` from within an example
|
9
|
+
group. (Markus Reiter, #974)
|
10
|
+
* Improve failure message of `change` matcher with block and `satisfy` matcher
|
11
|
+
by including the block snippet instead of just describing it as `result` or
|
12
|
+
`block` when Ripper is available. (Yuji Nakayama, #987)
|
13
|
+
|
14
|
+
Bug Fixes:
|
15
|
+
|
16
|
+
* Fix `yield_with_args` and `yield_successive_args` matchers so that
|
17
|
+
they compare expected to actual args at the time the args are yielded
|
18
|
+
instead of at the end, in case the method that is yielding mutates the
|
19
|
+
arguments after yielding. (Alyssa Ross, #965)
|
20
|
+
|
1
21
|
### 3.6.0.beta2 / 2016-12-12
|
2
22
|
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.6.0.beta1...v3.6.0.beta2)
|
3
23
|
|
@@ -92,7 +112,7 @@ Bug Fixes:
|
|
92
112
|
|
93
113
|
* Fix failure message from dynamic predicate matchers when the object
|
94
114
|
does not respond to the predicate so that it is inspected rather
|
95
|
-
than relying upon
|
115
|
+
than relying upon its `to_s` -- that way for `nil`, `"nil"` is
|
96
116
|
printed rather than an empty string. (Myron Marston, #841)
|
97
117
|
* Fix SystemStackError raised when diffing an Enumerable object
|
98
118
|
whose `#each` includes the object itself. (Yuji Nakayama, #857)
|
data/README.md
CHANGED
@@ -3,7 +3,9 @@
|
|
3
3
|
RSpec::Expectations lets you express expected outcomes on an object in an
|
4
4
|
example.
|
5
5
|
|
6
|
-
|
6
|
+
```ruby
|
7
|
+
expect(account.balance).to eq(Money.new(37.42, :USD))
|
8
|
+
```
|
7
9
|
|
8
10
|
## Install
|
9
11
|
|
@@ -150,7 +152,7 @@ expect { |b| 5.tap(&b) }.to yield_control # passes regardless of yielded args
|
|
150
152
|
expect { |b| yield_if_true(true, &b) }.to yield_with_no_args # passes only if no args are yielded
|
151
153
|
|
152
154
|
expect { |b| 5.tap(&b) }.to yield_with_args(5)
|
153
|
-
expect { |b| 5.tap(&b) }.to yield_with_args(
|
155
|
+
expect { |b| 5.tap(&b) }.to yield_with_args(Integer)
|
154
156
|
expect { |b| "a string".tap(&b) }.to yield_with_args(/str/)
|
155
157
|
|
156
158
|
expect { |b| [1, 2, 3].each(&b) }.to yield_successive_args(1, 2, 3)
|
@@ -263,7 +265,7 @@ expect(hash).to match(
|
|
263
265
|
:a => {
|
264
266
|
:b => a_collection_containing_exactly(
|
265
267
|
a_string_starting_with("f"),
|
266
|
-
an_instance_of(
|
268
|
+
an_instance_of(Integer)
|
267
269
|
),
|
268
270
|
:c => { :d => (a_value < 3) }
|
269
271
|
}
|
data/lib/rspec/expectations.rb
CHANGED
@@ -76,6 +76,7 @@ module RSpec
|
|
76
76
|
class MultipleExpectationsNotMetError < ExpectationNotMetError
|
77
77
|
end
|
78
78
|
|
79
|
-
autoload :
|
79
|
+
autoload :BlockSnippetExtractor, "rspec/expectations/block_snippet_extractor"
|
80
|
+
autoload :FailureAggregator, "rspec/expectations/failure_aggregator"
|
80
81
|
end
|
81
82
|
end
|
@@ -0,0 +1,253 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Expectations
|
3
|
+
# @private
|
4
|
+
class BlockSnippetExtractor # rubocop:disable Style/ClassLength
|
5
|
+
# rubocop should properly handle `Struct.new {}` as an inner class definition.
|
6
|
+
|
7
|
+
attr_reader :proc, :method_name
|
8
|
+
|
9
|
+
def self.try_extracting_single_line_body_of(proc, method_name)
|
10
|
+
lines = new(proc, method_name).body_content_lines
|
11
|
+
return nil unless lines.count == 1
|
12
|
+
lines.first
|
13
|
+
rescue Error
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(proc, method_name)
|
18
|
+
@proc = proc
|
19
|
+
@method_name = method_name.to_s.freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
# Ideally we should properly handle indentations of multiline snippet,
|
23
|
+
# but it's not implemented yet since because we use result of this method only when it's a
|
24
|
+
# single line and implementing the logic introduces additional complexity.
|
25
|
+
def body_content_lines
|
26
|
+
raw_body_lines.map(&:strip).reject(&:empty?)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def raw_body_lines
|
32
|
+
raw_body_snippet.split("\n")
|
33
|
+
end
|
34
|
+
|
35
|
+
def raw_body_snippet
|
36
|
+
block_token_extractor.body_tokens.map(&:string).join
|
37
|
+
end
|
38
|
+
|
39
|
+
def block_token_extractor
|
40
|
+
@block_token_extractor ||= BlockTokenExtractor.new(method_name, source, beginning_line_number)
|
41
|
+
end
|
42
|
+
|
43
|
+
if RSpec.respond_to?(:world)
|
44
|
+
def source
|
45
|
+
raise TargetNotFoundError unless File.exist?(file_path)
|
46
|
+
RSpec.world.source_from_file(file_path)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
RSpec::Support.require_rspec_support 'source'
|
50
|
+
def source
|
51
|
+
raise TargetNotFoundError unless File.exist?(file_path)
|
52
|
+
@source ||= RSpec::Support::Source.from_file(file_path)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def file_path
|
57
|
+
source_location.first
|
58
|
+
end
|
59
|
+
|
60
|
+
def beginning_line_number
|
61
|
+
source_location.last
|
62
|
+
end
|
63
|
+
|
64
|
+
def source_location
|
65
|
+
proc.source_location || raise(TargetNotFoundError)
|
66
|
+
end
|
67
|
+
|
68
|
+
Error = Class.new(StandardError)
|
69
|
+
TargetNotFoundError = Class.new(Error)
|
70
|
+
AmbiguousTargetError = Class.new(Error)
|
71
|
+
|
72
|
+
# @private
|
73
|
+
# Performs extraction of block body snippet using tokens,
|
74
|
+
# which cannot be done with node information.
|
75
|
+
BlockTokenExtractor = Struct.new(:method_name, :source, :beginning_line_number) do
|
76
|
+
attr_reader :state, :body_tokens
|
77
|
+
|
78
|
+
def initialize(*)
|
79
|
+
super
|
80
|
+
parse!
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def parse!
|
86
|
+
@state = :initial
|
87
|
+
|
88
|
+
catch(:finish) do
|
89
|
+
source.tokens.each do |token|
|
90
|
+
invoke_state_handler(token)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def finish!
|
96
|
+
throw :finish
|
97
|
+
end
|
98
|
+
|
99
|
+
def invoke_state_handler(token)
|
100
|
+
__send__("#{state}_state", token)
|
101
|
+
end
|
102
|
+
|
103
|
+
def initial_state(token)
|
104
|
+
@state = :after_method_call if token.location == block_locator.method_call_location
|
105
|
+
end
|
106
|
+
|
107
|
+
def after_method_call_state(token)
|
108
|
+
@state = :after_opener if handle_opener_token(token)
|
109
|
+
end
|
110
|
+
|
111
|
+
def after_opener_state(token)
|
112
|
+
if handle_closer_token(token)
|
113
|
+
finish_or_find_next_block_if_incorrect!
|
114
|
+
elsif pipe_token?(token)
|
115
|
+
finalize_pending_tokens!
|
116
|
+
@state = :after_beginning_of_args
|
117
|
+
else
|
118
|
+
pending_tokens << token
|
119
|
+
handle_opener_token(token)
|
120
|
+
@state = :after_beginning_of_body unless token.type == :on_sp
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def after_beginning_of_args_state(token)
|
125
|
+
@state = :after_beginning_of_body if pipe_token?(token)
|
126
|
+
end
|
127
|
+
|
128
|
+
def after_beginning_of_body_state(token)
|
129
|
+
if handle_closer_token(token)
|
130
|
+
finish_or_find_next_block_if_incorrect!
|
131
|
+
else
|
132
|
+
pending_tokens << token
|
133
|
+
handle_opener_token(token)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def pending_tokens
|
138
|
+
@pending_tokens ||= []
|
139
|
+
end
|
140
|
+
|
141
|
+
def finalize_pending_tokens!
|
142
|
+
pending_tokens.freeze.tap do
|
143
|
+
@pending_tokens = nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def finish_or_find_next_block_if_incorrect!
|
148
|
+
body_tokens = finalize_pending_tokens!
|
149
|
+
|
150
|
+
if correct_block?(body_tokens)
|
151
|
+
@body_tokens = body_tokens
|
152
|
+
finish!
|
153
|
+
else
|
154
|
+
@state = :after_method_call
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def handle_opener_token(token)
|
159
|
+
opener_token?(token).tap do |boolean|
|
160
|
+
opener_token_stack.push(token) if boolean
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def opener_token?(token)
|
165
|
+
token.type == :on_lbrace || (token.type == :on_kw && token.string == 'do')
|
166
|
+
end
|
167
|
+
|
168
|
+
def handle_closer_token(token)
|
169
|
+
if opener_token_stack.last.closed_by?(token)
|
170
|
+
opener_token_stack.pop
|
171
|
+
opener_token_stack.empty?
|
172
|
+
else
|
173
|
+
false
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def opener_token_stack
|
178
|
+
@opener_token_stack ||= []
|
179
|
+
end
|
180
|
+
|
181
|
+
def pipe_token?(token)
|
182
|
+
token.type == :on_op && token.string == '|'
|
183
|
+
end
|
184
|
+
|
185
|
+
def correct_block?(body_tokens)
|
186
|
+
return true if block_locator.body_content_locations.empty?
|
187
|
+
content_location = block_locator.body_content_locations.first
|
188
|
+
content_location.between?(body_tokens.first.location, body_tokens.last.location)
|
189
|
+
end
|
190
|
+
|
191
|
+
def block_locator
|
192
|
+
@block_locator ||= BlockLocator.new(method_name, source, beginning_line_number)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# @private
|
197
|
+
# Locates target block with node information (semantics), which tokens don't have.
|
198
|
+
BlockLocator = Struct.new(:method_name, :source, :beginning_line_number) do
|
199
|
+
def method_call_location
|
200
|
+
@method_call_location ||= method_ident_node.location
|
201
|
+
end
|
202
|
+
|
203
|
+
def body_content_locations
|
204
|
+
@body_content_locations ||= block_body_node.map(&:location).compact
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def method_ident_node
|
210
|
+
method_call_node = block_wrapper_node.children.first
|
211
|
+
method_call_node.find do |node|
|
212
|
+
method_ident_node?(node)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def block_body_node
|
217
|
+
block_node = block_wrapper_node.children[1]
|
218
|
+
block_node.children.last
|
219
|
+
end
|
220
|
+
|
221
|
+
def block_wrapper_node
|
222
|
+
case candidate_block_wrapper_nodes.size
|
223
|
+
when 1
|
224
|
+
candidate_block_wrapper_nodes.first
|
225
|
+
when 0
|
226
|
+
raise TargetNotFoundError
|
227
|
+
else
|
228
|
+
raise AmbiguousTargetError
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def candidate_block_wrapper_nodes
|
233
|
+
@candidate_block_wrapper_nodes ||= candidate_method_ident_nodes.map do |method_ident_node|
|
234
|
+
block_wrapper_node = method_ident_node.each_ancestor.find { |node| node.type == :method_add_block }
|
235
|
+
next nil unless block_wrapper_node
|
236
|
+
method_call_node = block_wrapper_node.children.first
|
237
|
+
method_call_node.include?(method_ident_node) ? block_wrapper_node : nil
|
238
|
+
end.compact
|
239
|
+
end
|
240
|
+
|
241
|
+
def candidate_method_ident_nodes
|
242
|
+
source.nodes_by_line_number[beginning_line_number].select do |node|
|
243
|
+
method_ident_node?(node)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def method_ident_node?(node)
|
248
|
+
node.type == :@ident && node.args.first == method_name
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
data/lib/rspec/matchers.rb
CHANGED
@@ -41,9 +41,9 @@ module RSpec
|
|
41
41
|
#
|
42
42
|
# expect("a string").to be_an_instance_of(String) # =>"a string".instance_of?(String) # passes
|
43
43
|
#
|
44
|
-
# expect(3).to be_a_kind_of(
|
45
|
-
# expect(3).to be_a_kind_of(Numeric)
|
46
|
-
# expect(3).to be_an_instance_of(
|
44
|
+
# expect(3).to be_a_kind_of(Integer) # => 3.kind_of?(Numeric) | passes
|
45
|
+
# expect(3).to be_a_kind_of(Numeric) # => 3.kind_of?(Numeric) | passes
|
46
|
+
# expect(3).to be_an_instance_of(Integer) # => 3.instance_of?(Integer) | passes
|
47
47
|
# expect(3).not_to be_an_instance_of(Numeric) # => 3.instance_of?(Numeric) | fails
|
48
48
|
#
|
49
49
|
# RSpec will also create custom matchers for predicates like `has_key?`. To
|
@@ -62,6 +62,26 @@ module RSpec
|
|
62
62
|
# RSpec::Matchers.alias_matcher :a_user_who_is_an_admin, :be_an_admin
|
63
63
|
# expect(user_list).to include(a_user_who_is_an_admin)
|
64
64
|
#
|
65
|
+
# ## Alias Matchers
|
66
|
+
#
|
67
|
+
# With {RSpec::Matchers.alias_matcher}, you can easily create an
|
68
|
+
# alternate name for a given matcher.
|
69
|
+
#
|
70
|
+
# The description will also change according to the new name:
|
71
|
+
#
|
72
|
+
# RSpec::Matchers.alias_matcher :a_list_that_sums_to, :sum_to
|
73
|
+
# sum_to(3).description # => "sum to 3"
|
74
|
+
# a_list_that_sums_to(3).description # => "a list that sums to 3"
|
75
|
+
#
|
76
|
+
# or you can specify a custom description like this:
|
77
|
+
#
|
78
|
+
# RSpec::Matchers.alias_matcher :a_list_sorted_by, :be_sorted_by do |description|
|
79
|
+
# description.sub("be sorted by", "a list sorted by")
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# be_sorted_by(:age).description # => "be sorted by age"
|
83
|
+
# a_list_sorted_by(:age).description # => "a list sorted by age"
|
84
|
+
#
|
65
85
|
# ## Custom Matchers
|
66
86
|
#
|
67
87
|
# When you find that none of the stock matchers provide a natural feeling
|
@@ -202,7 +222,34 @@ module RSpec
|
|
202
222
|
# expressions, and also uses the noun-phrase wording in the matcher's `description`,
|
203
223
|
# for readable failure messages. You can alias your custom matchers in similar fashion
|
204
224
|
# using {RSpec::Matchers.alias_matcher}.
|
225
|
+
#
|
226
|
+
# ## Negated Matchers
|
227
|
+
#
|
228
|
+
# Sometimes if you want to test for the opposite using a more descriptive name
|
229
|
+
# instead of using `not_to`, you can use {RSpec::Matchers.define_negated_matcher}:
|
230
|
+
#
|
231
|
+
# RSpec::Matchers.define_negated_matcher :exclude, :include
|
232
|
+
# include(1, 2).description # => "include 1 and 2"
|
233
|
+
# exclude(1, 2).description # => "exclude 1 and 2"
|
234
|
+
#
|
235
|
+
# While the most obvious negated form may be to add a `not_` prefix,
|
236
|
+
# the failure messages you get with that form can be confusing (e.g.
|
237
|
+
# "expected [actual] to not [verb], but did not"). We've found it works
|
238
|
+
# best to find a more positive name for the negated form, such as
|
239
|
+
# `avoid_changing` rather than `not_change`.
|
240
|
+
#
|
205
241
|
module Matchers
|
242
|
+
extend ::RSpec::Matchers::DSL
|
243
|
+
|
244
|
+
# @!method self.alias_matcher(new_name, old_name, options={}, &description_override)
|
245
|
+
# Extended from {RSpec::Matchers::DSL#alias_matcher}.
|
246
|
+
|
247
|
+
# @!method self.define(name, &declarations)
|
248
|
+
# Extended from {RSpec::Matchers::DSL#define}.
|
249
|
+
|
250
|
+
# @!method self.define_negated_matcher(negated_name, base_name, &description_override)
|
251
|
+
# Extended from {RSpec::Matchers::DSL#define_negated_matcher}.
|
252
|
+
|
206
253
|
# @method expect
|
207
254
|
# Supports `expect(actual).to matcher` syntax by wrapping `actual` in an
|
208
255
|
# `ExpectationTarget`.
|
@@ -213,70 +260,6 @@ module RSpec
|
|
213
260
|
# @see ExpectationTarget#to
|
214
261
|
# @see ExpectationTarget#not_to
|
215
262
|
|
216
|
-
# Defines a matcher alias. The returned matcher's `description` will be overriden
|
217
|
-
# to reflect the phrasing of the new name, which will be used in failure messages
|
218
|
-
# when passed as an argument to another matcher in a composed matcher expression.
|
219
|
-
#
|
220
|
-
# @param new_name [Symbol] the new name for the matcher
|
221
|
-
# @param old_name [Symbol] the original name for the matcher
|
222
|
-
# @param options [Hash] options for the aliased matcher
|
223
|
-
# @option options [Class] :klass the ruby class to use as the decorator. (Not normally used).
|
224
|
-
# @yield [String] optional block that, when given, is used to define the overriden
|
225
|
-
# logic. The yielded arg is the original description or failure message. If no
|
226
|
-
# block is provided, a default override is used based on the old and new names.
|
227
|
-
#
|
228
|
-
# @example
|
229
|
-
# RSpec::Matchers.alias_matcher :a_list_that_sums_to, :sum_to
|
230
|
-
# sum_to(3).description # => "sum to 3"
|
231
|
-
# a_list_that_sums_to(3).description # => "a list that sums to 3"
|
232
|
-
#
|
233
|
-
# @example
|
234
|
-
# RSpec::Matchers.alias_matcher :a_list_sorted_by, :be_sorted_by do |description|
|
235
|
-
# description.sub("be sorted by", "a list sorted by")
|
236
|
-
# end
|
237
|
-
#
|
238
|
-
# be_sorted_by(:age).description # => "be sorted by age"
|
239
|
-
# a_list_sorted_by(:age).description # => "a list sorted by age"
|
240
|
-
#
|
241
|
-
# @!macro [attach] alias_matcher
|
242
|
-
# @!parse
|
243
|
-
# alias $1 $2
|
244
|
-
def self.alias_matcher(new_name, old_name, options={}, &description_override)
|
245
|
-
description_override ||= lambda do |old_desc|
|
246
|
-
old_desc.gsub(EnglishPhrasing.split_words(old_name), EnglishPhrasing.split_words(new_name))
|
247
|
-
end
|
248
|
-
klass = options.fetch(:klass) { AliasedMatcher }
|
249
|
-
|
250
|
-
define_method(new_name) do |*args, &block|
|
251
|
-
matcher = __send__(old_name, *args, &block)
|
252
|
-
klass.new(matcher, description_override)
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
# Defines a negated matcher. The returned matcher's `description` and `failure_message`
|
257
|
-
# will be overriden to reflect the phrasing of the new name, and the match logic will
|
258
|
-
# be based on the original matcher but negated.
|
259
|
-
#
|
260
|
-
# @param negated_name [Symbol] the name for the negated matcher
|
261
|
-
# @param base_name [Symbol] the name of the original matcher that will be negated
|
262
|
-
# @yield [String] optional block that, when given, is used to define the overriden
|
263
|
-
# logic. The yielded arg is the original description or failure message. If no
|
264
|
-
# block is provided, a default override is used based on the old and new names.
|
265
|
-
#
|
266
|
-
# @example
|
267
|
-
# RSpec::Matchers.define_negated_matcher :exclude, :include
|
268
|
-
# include(1, 2).description # => "include 1 and 2"
|
269
|
-
# exclude(1, 2).description # => "exclude 1 and 2"
|
270
|
-
#
|
271
|
-
# @note While the most obvious negated form may be to add a `not_` prefix,
|
272
|
-
# the failure messages you get with that form can be confusing (e.g.
|
273
|
-
# "expected [actual] to not [verb], but did not"). We've found it works
|
274
|
-
# best to find a more positive name for the negated form, such as
|
275
|
-
# `avoid_changing` rather than `not_change`.
|
276
|
-
def self.define_negated_matcher(negated_name, base_name, &description_override)
|
277
|
-
alias_matcher(negated_name, base_name, :klass => AliasedNegatedMatcher, &description_override)
|
278
|
-
end
|
279
|
-
|
280
263
|
# Allows multiple expectations in the provided block to fail, and then
|
281
264
|
# aggregates them into a single exception, rather than aborting on the
|
282
265
|
# first expectation failure like normal. This allows you to see all
|
@@ -313,6 +296,10 @@ module RSpec
|
|
313
296
|
Expectations::FailureAggregator.new(label, metadata).aggregate(&block)
|
314
297
|
end
|
315
298
|
|
299
|
+
# @!macro [attach] alias_matcher
|
300
|
+
# @!parse
|
301
|
+
# alias $1 $2
|
302
|
+
|
316
303
|
# Passes if actual is truthy (anything but false or nil)
|
317
304
|
def be_truthy
|
318
305
|
BuiltIn::BeTruthy.new
|
@@ -367,7 +354,7 @@ module RSpec
|
|
367
354
|
# Passes if actual.instance_of?(expected)
|
368
355
|
#
|
369
356
|
# @example
|
370
|
-
# expect(5).to be_an_instance_of(
|
357
|
+
# expect(5).to be_an_instance_of(Integer)
|
371
358
|
# expect(5).not_to be_an_instance_of(Numeric)
|
372
359
|
# expect(5).not_to be_an_instance_of(Float)
|
373
360
|
def be_an_instance_of(expected)
|
@@ -379,7 +366,7 @@ module RSpec
|
|
379
366
|
# Passes if actual.kind_of?(expected)
|
380
367
|
#
|
381
368
|
# @example
|
382
|
-
# expect(5).to be_a_kind_of(
|
369
|
+
# expect(5).to be_a_kind_of(Integer)
|
383
370
|
# expect(5).to be_a_kind_of(Numeric)
|
384
371
|
# expect(5).not_to be_a_kind_of(Float)
|
385
372
|
def be_a_kind_of(expected)
|
@@ -585,7 +572,7 @@ module RSpec
|
|
585
572
|
# information about equality in Ruby.
|
586
573
|
#
|
587
574
|
# @example
|
588
|
-
# expect(5).to equal(5) #
|
575
|
+
# expect(5).to equal(5) # Integers are equal
|
589
576
|
# expect("5").not_to equal("5") # Strings that look the same are not the same object
|
590
577
|
def equal(expected)
|
591
578
|
BuiltIn::Equal.new(expected)
|
@@ -688,7 +675,7 @@ module RSpec
|
|
688
675
|
# :a => {
|
689
676
|
# :b => a_collection_containing_exactly(
|
690
677
|
# a_string_starting_with("f"),
|
691
|
-
# an_instance_of(
|
678
|
+
# an_instance_of(Integer)
|
692
679
|
# ),
|
693
680
|
# :c => { :d => (a_value < 3) }
|
694
681
|
# }
|
@@ -809,7 +796,7 @@ module RSpec
|
|
809
796
|
# @example
|
810
797
|
# expect(5).to satisfy { |n| n > 3 }
|
811
798
|
# expect(5).to satisfy("be greater than 3") { |n| n > 3 }
|
812
|
-
def satisfy(description=
|
799
|
+
def satisfy(description=nil, &block)
|
813
800
|
BuiltIn::Satisfy.new(description, &block)
|
814
801
|
end
|
815
802
|
alias_matcher :an_object_satisfying, :satisfy
|
@@ -905,7 +892,7 @@ module RSpec
|
|
905
892
|
# @example
|
906
893
|
# expect { |b| 5.tap(&b) }.to yield_with_args # because #tap yields an arg
|
907
894
|
# expect { |b| 5.tap(&b) }.to yield_with_args(5) # because 5 == 5
|
908
|
-
# expect { |b| 5.tap(&b) }.to yield_with_args(
|
895
|
+
# expect { |b| 5.tap(&b) }.to yield_with_args(Integer) # because Integer === 5
|
909
896
|
# expect { |b| File.open("f.txt", &b) }.to yield_with_args(/txt/) # because /txt/ === "f.txt"
|
910
897
|
#
|
911
898
|
# expect { |b| User.transaction(&b) }.not_to yield_with_args # because it yields no args
|
@@ -22,6 +22,9 @@ module RSpec
|
|
22
22
|
# @private
|
23
23
|
attr_reader :actual, :expected, :rescued_exception
|
24
24
|
|
25
|
+
# @private
|
26
|
+
attr_writer :matcher_name
|
27
|
+
|
25
28
|
def initialize(expected=UNDEFINED)
|
26
29
|
@expected = expected unless UNDEFINED.equal?(expected)
|
27
30
|
end
|
@@ -95,6 +98,15 @@ module RSpec
|
|
95
98
|
@matcher_name ||= underscore(name.split('::').last)
|
96
99
|
end
|
97
100
|
|
101
|
+
# @private
|
102
|
+
def matcher_name
|
103
|
+
if defined?(@matcher_name)
|
104
|
+
@matcher_name
|
105
|
+
else
|
106
|
+
self.class.matcher_name
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
98
110
|
# @private
|
99
111
|
# Borrowed from ActiveSupport.
|
100
112
|
def self.underscore(camel_cased_word)
|
@@ -8,7 +8,7 @@ module RSpec
|
|
8
8
|
# @api public
|
9
9
|
# Specifies the delta of the expected change.
|
10
10
|
def by(expected_delta)
|
11
|
-
ChangeRelatively.new(
|
11
|
+
ChangeRelatively.new(change_details, expected_delta, :by) do |actual_delta|
|
12
12
|
values_match?(expected_delta, actual_delta)
|
13
13
|
end
|
14
14
|
end
|
@@ -16,7 +16,7 @@ module RSpec
|
|
16
16
|
# @api public
|
17
17
|
# Specifies a minimum delta of the expected change.
|
18
18
|
def by_at_least(minimum)
|
19
|
-
ChangeRelatively.new(
|
19
|
+
ChangeRelatively.new(change_details, minimum, :by_at_least) do |actual_delta|
|
20
20
|
actual_delta >= minimum
|
21
21
|
end
|
22
22
|
end
|
@@ -24,7 +24,7 @@ module RSpec
|
|
24
24
|
# @api public
|
25
25
|
# Specifies a maximum delta of the expected change.
|
26
26
|
def by_at_most(maximum)
|
27
|
-
ChangeRelatively.new(
|
27
|
+
ChangeRelatively.new(change_details, maximum, :by_at_most) do |actual_delta|
|
28
28
|
actual_delta <= maximum
|
29
29
|
end
|
30
30
|
end
|
@@ -32,13 +32,13 @@ module RSpec
|
|
32
32
|
# @api public
|
33
33
|
# Specifies the new value you expect.
|
34
34
|
def to(value)
|
35
|
-
ChangeToValue.new(
|
35
|
+
ChangeToValue.new(change_details, value)
|
36
36
|
end
|
37
37
|
|
38
38
|
# @api public
|
39
39
|
# Specifies the original value.
|
40
40
|
def from(value)
|
41
|
-
ChangeFromValue.new(
|
41
|
+
ChangeFromValue.new(change_details, value)
|
42
42
|
end
|
43
43
|
|
44
44
|
# @private
|
@@ -46,8 +46,8 @@ module RSpec
|
|
46
46
|
@event_proc = event_proc
|
47
47
|
return false unless Proc === event_proc
|
48
48
|
raise_block_syntax_error if block_given?
|
49
|
-
|
50
|
-
|
49
|
+
change_details.perform_change(event_proc)
|
50
|
+
change_details.changed?
|
51
51
|
end
|
52
52
|
|
53
53
|
def does_not_match?(event_proc)
|
@@ -58,21 +58,21 @@ module RSpec
|
|
58
58
|
# @api private
|
59
59
|
# @return [String]
|
60
60
|
def failure_message
|
61
|
-
"expected #{
|
61
|
+
"expected #{change_details.value_representation} to have changed, " \
|
62
62
|
"but #{positive_failure_reason}"
|
63
63
|
end
|
64
64
|
|
65
65
|
# @api private
|
66
66
|
# @return [String]
|
67
67
|
def failure_message_when_negated
|
68
|
-
"expected #{
|
68
|
+
"expected #{change_details.value_representation} not to have changed, " \
|
69
69
|
"but #{negative_failure_reason}"
|
70
70
|
end
|
71
71
|
|
72
72
|
# @api private
|
73
73
|
# @return [String]
|
74
74
|
def description
|
75
|
-
"change #{
|
75
|
+
"change #{change_details.value_representation}"
|
76
76
|
end
|
77
77
|
|
78
78
|
# @private
|
@@ -83,7 +83,13 @@ module RSpec
|
|
83
83
|
private
|
84
84
|
|
85
85
|
def initialize(receiver=nil, message=nil, &block)
|
86
|
-
@
|
86
|
+
@receiver = receiver
|
87
|
+
@message = message
|
88
|
+
@block = block
|
89
|
+
end
|
90
|
+
|
91
|
+
def change_details
|
92
|
+
@change_details ||= ChangeDetails.new(matcher_name, @receiver, @message, &@block)
|
87
93
|
end
|
88
94
|
|
89
95
|
def raise_block_syntax_error
|
@@ -93,13 +99,13 @@ module RSpec
|
|
93
99
|
|
94
100
|
def positive_failure_reason
|
95
101
|
return "was not given a block" unless Proc === @event_proc
|
96
|
-
"is still #{description_of
|
102
|
+
"is still #{description_of change_details.actual_before}"
|
97
103
|
end
|
98
104
|
|
99
105
|
def negative_failure_reason
|
100
106
|
return "was not given a block" unless Proc === @event_proc
|
101
|
-
"did change from #{description_of
|
102
|
-
"to #{description_of
|
107
|
+
"did change from #{description_of change_details.actual_before} " \
|
108
|
+
"to #{description_of change_details.actual_after}"
|
103
109
|
end
|
104
110
|
end
|
105
111
|
|
@@ -115,7 +121,7 @@ module RSpec
|
|
115
121
|
|
116
122
|
# @private
|
117
123
|
def failure_message
|
118
|
-
"expected #{@change_details.
|
124
|
+
"expected #{@change_details.value_representation} to have changed " \
|
119
125
|
"#{@relativity.to_s.tr('_', ' ')} " \
|
120
126
|
"#{description_of @expected_delta}, but #{failure_reason}"
|
121
127
|
end
|
@@ -136,7 +142,7 @@ module RSpec
|
|
136
142
|
|
137
143
|
# @private
|
138
144
|
def description
|
139
|
-
"change #{@change_details.
|
145
|
+
"change #{@change_details.value_representation} " \
|
140
146
|
"#{@relativity.to_s.tr('_', ' ')} #{description_of @expected_delta}"
|
141
147
|
end
|
142
148
|
|
@@ -175,7 +181,7 @@ module RSpec
|
|
175
181
|
|
176
182
|
# @private
|
177
183
|
def description
|
178
|
-
"change #{@change_details.
|
184
|
+
"change #{@change_details.value_representation} #{change_description}"
|
179
185
|
end
|
180
186
|
|
181
187
|
# @private
|
@@ -202,30 +208,30 @@ module RSpec
|
|
202
208
|
end
|
203
209
|
|
204
210
|
def before_value_failure
|
205
|
-
"expected #{@change_details.
|
211
|
+
"expected #{@change_details.value_representation} " \
|
206
212
|
"to have initially been #{description_of @expected_before}, " \
|
207
213
|
"but was #{description_of @change_details.actual_before}"
|
208
214
|
end
|
209
215
|
|
210
216
|
def after_value_failure
|
211
|
-
"expected #{@change_details.
|
217
|
+
"expected #{@change_details.value_representation} " \
|
212
218
|
"to have changed to #{description_of @expected_after}, " \
|
213
219
|
"but is now #{description_of @change_details.actual_after}"
|
214
220
|
end
|
215
221
|
|
216
222
|
def did_not_change_failure
|
217
|
-
"expected #{@change_details.
|
223
|
+
"expected #{@change_details.value_representation} " \
|
218
224
|
"to have changed #{change_description}, but did not change"
|
219
225
|
end
|
220
226
|
|
221
227
|
def did_change_failure
|
222
|
-
"expected #{@change_details.
|
228
|
+
"expected #{@change_details.value_representation} not to have changed, but " \
|
223
229
|
"did change from #{description_of @change_details.actual_before} " \
|
224
230
|
"to #{description_of @change_details.actual_after}"
|
225
231
|
end
|
226
232
|
|
227
233
|
def not_given_a_block_failure
|
228
|
-
"expected #{@change_details.
|
234
|
+
"expected #{@change_details.value_representation} to have changed " \
|
229
235
|
"#{change_description}, but was not given a block"
|
230
236
|
end
|
231
237
|
end
|
@@ -307,9 +313,9 @@ module RSpec
|
|
307
313
|
# @private
|
308
314
|
# Encapsulates the details of the before/after values.
|
309
315
|
class ChangeDetails
|
310
|
-
attr_reader :
|
316
|
+
attr_reader :actual_before, :actual_after
|
311
317
|
|
312
|
-
def initialize(receiver=nil, message=nil, &block)
|
318
|
+
def initialize(matcher_name, receiver=nil, message=nil, &block)
|
313
319
|
if receiver && !message
|
314
320
|
raise(
|
315
321
|
ArgumentError,
|
@@ -318,8 +324,22 @@ module RSpec
|
|
318
324
|
"You passed an object but no message."
|
319
325
|
)
|
320
326
|
end
|
321
|
-
|
322
|
-
@
|
327
|
+
|
328
|
+
@matcher_name = matcher_name
|
329
|
+
@receiver = receiver
|
330
|
+
@message = message
|
331
|
+
@value_proc = block
|
332
|
+
end
|
333
|
+
|
334
|
+
def value_representation
|
335
|
+
@value_representation ||=
|
336
|
+
if @message
|
337
|
+
"##{@message}"
|
338
|
+
elsif (value_block_snippet = extract_value_block_snippet)
|
339
|
+
"`#{value_block_snippet}`"
|
340
|
+
else
|
341
|
+
'result'
|
342
|
+
end
|
323
343
|
end
|
324
344
|
|
325
345
|
def perform_change(event_proc)
|
@@ -339,7 +359,9 @@ module RSpec
|
|
339
359
|
private
|
340
360
|
|
341
361
|
def evaluate_value_proc
|
342
|
-
|
362
|
+
value_proc = @value_proc || lambda { @receiver.__send__(@message) }
|
363
|
+
|
364
|
+
case val = value_proc.call
|
343
365
|
when IO # enumerable, but we don't want to dup it.
|
344
366
|
val
|
345
367
|
when Enumerable, String
|
@@ -348,6 +370,17 @@ module RSpec
|
|
348
370
|
val
|
349
371
|
end
|
350
372
|
end
|
373
|
+
|
374
|
+
if RSpec::Support::RubyFeatures.ripper_supported?
|
375
|
+
def extract_value_block_snippet
|
376
|
+
return nil unless @value_proc
|
377
|
+
Expectations::BlockSnippetExtractor.try_extracting_single_line_body_of(@value_proc, @matcher_name)
|
378
|
+
end
|
379
|
+
else
|
380
|
+
def extract_value_block_snippet
|
381
|
+
nil
|
382
|
+
end
|
383
|
+
end
|
351
384
|
end
|
352
385
|
end
|
353
386
|
end
|
@@ -172,7 +172,7 @@ module RSpec
|
|
172
172
|
"including those raised by Ruby (e.g. NoMethodError, NameError " \
|
173
173
|
"and ArgumentError), meaning the code you are intending to test " \
|
174
174
|
"may not even get reached. Instead consider using " \
|
175
|
-
"`expect {}.not_to raise_error` or `expect { }.to raise_error" \
|
175
|
+
"`expect { }.not_to raise_error` or `expect { }.to raise_error" \
|
176
176
|
"(DifferentSpecificErrorClass)`. This message can be suppressed by " \
|
177
177
|
"setting: `RSpec::Expectations.configuration.on_potential_false" \
|
178
178
|
"_positives = :nothing`")
|
@@ -5,10 +5,7 @@ module RSpec
|
|
5
5
|
# Provides the implementation for `satisfy`.
|
6
6
|
# Not intended to be instantiated directly.
|
7
7
|
class Satisfy < BaseMatcher
|
8
|
-
|
9
|
-
attr_reader :description
|
10
|
-
|
11
|
-
def initialize(description="satisfy block", &block)
|
8
|
+
def initialize(description=nil, &block)
|
12
9
|
@description = description
|
13
10
|
@block = block
|
14
11
|
end
|
@@ -20,6 +17,11 @@ module RSpec
|
|
20
17
|
@block.call(actual)
|
21
18
|
end
|
22
19
|
|
20
|
+
# @private
|
21
|
+
def description
|
22
|
+
@description ||= "satisfy #{block_representation}"
|
23
|
+
end
|
24
|
+
|
23
25
|
# @api private
|
24
26
|
# @return [String]
|
25
27
|
def failure_message
|
@@ -31,6 +33,27 @@ module RSpec
|
|
31
33
|
def failure_message_when_negated
|
32
34
|
"expected #{actual_formatted} not to #{description}"
|
33
35
|
end
|
36
|
+
|
37
|
+
private # rubocop:disable Lint/UselessAccessModifier
|
38
|
+
|
39
|
+
if RSpec::Support::RubyFeatures.ripper_supported?
|
40
|
+
def block_representation
|
41
|
+
if (block_snippet = extract_block_snippet)
|
42
|
+
"expression `#{block_snippet}`"
|
43
|
+
else
|
44
|
+
'block'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def extract_block_snippet
|
49
|
+
return nil unless @block
|
50
|
+
Expectations::BlockSnippetExtractor.try_extracting_single_line_body_of(@block, matcher_name)
|
51
|
+
end
|
52
|
+
else
|
53
|
+
def block_representation
|
54
|
+
'block'
|
55
|
+
end
|
56
|
+
end
|
34
57
|
end
|
35
58
|
end
|
36
59
|
end
|
@@ -8,19 +8,17 @@ module RSpec
|
|
8
8
|
# yield matchers is used. Provides information about
|
9
9
|
# the yield behavior of the object-under-test.
|
10
10
|
class YieldProbe
|
11
|
-
def self.probe(block)
|
12
|
-
probe = new(block)
|
11
|
+
def self.probe(block, &callback)
|
12
|
+
probe = new(block, &callback)
|
13
13
|
return probe unless probe.has_block?
|
14
|
-
probe.
|
15
|
-
block.call(probe)
|
16
|
-
probe.assert_used!
|
17
|
-
probe
|
14
|
+
probe.probe
|
18
15
|
end
|
19
16
|
|
20
17
|
attr_accessor :num_yields, :yielded_args
|
21
18
|
|
22
|
-
def initialize(block)
|
19
|
+
def initialize(block, &callback)
|
23
20
|
@block = block
|
21
|
+
@callback = callback || Proc.new {}
|
24
22
|
@used = false
|
25
23
|
self.num_yields = 0
|
26
24
|
self.yielded_args = []
|
@@ -30,13 +28,22 @@ module RSpec
|
|
30
28
|
Proc === @block
|
31
29
|
end
|
32
30
|
|
31
|
+
def probe
|
32
|
+
assert_valid_expect_block!
|
33
|
+
@block.call(self)
|
34
|
+
assert_used!
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
33
38
|
def to_proc
|
34
39
|
@used = true
|
35
40
|
|
36
41
|
probe = self
|
42
|
+
callback = @callback
|
37
43
|
Proc.new do |*args|
|
38
44
|
probe.num_yields += 1
|
39
45
|
probe.yielded_args << args
|
46
|
+
callback.call(*args)
|
40
47
|
nil # to indicate the block does not return a meaningful value
|
41
48
|
end
|
42
49
|
end
|
@@ -56,12 +63,6 @@ module RSpec
|
|
56
63
|
end
|
57
64
|
end
|
58
65
|
|
59
|
-
def successive_yield_args
|
60
|
-
yielded_args.map do |arg_array|
|
61
|
-
arg_array.size == 1 ? arg_array.first : arg_array
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
66
|
def assert_used!
|
66
67
|
return if @used
|
67
68
|
raise 'You must pass the argument yielded to your expect block on ' \
|
@@ -269,10 +270,15 @@ module RSpec
|
|
269
270
|
|
270
271
|
# @private
|
271
272
|
def matches?(block)
|
272
|
-
@
|
273
|
+
@args_matched_when_yielded = true
|
274
|
+
@probe = YieldProbe.new(block) do
|
275
|
+
@actual = @probe.single_yield_args
|
276
|
+
@actual_formatted = actual_formatted
|
277
|
+
@args_matched_when_yielded &&= args_currently_match?
|
278
|
+
end
|
273
279
|
return false unless @probe.has_block?
|
274
|
-
@
|
275
|
-
@probe.yielded_once?(:yield_with_args) &&
|
280
|
+
@probe.probe
|
281
|
+
@probe.yielded_once?(:yield_with_args) && @args_matched_when_yielded
|
276
282
|
end
|
277
283
|
|
278
284
|
# @private
|
@@ -317,16 +323,16 @@ module RSpec
|
|
317
323
|
def negative_failure_reason
|
318
324
|
if !@probe.has_block?
|
319
325
|
'was not a block'
|
320
|
-
elsif
|
326
|
+
elsif @args_matched_when_yielded && !@expected.empty?
|
321
327
|
'yielded with expected arguments' \
|
322
328
|
"\nexpected not: #{surface_descriptions_in(@expected).inspect}" \
|
323
|
-
"\n got: #{actual_formatted}"
|
329
|
+
"\n got: #{@actual_formatted}"
|
324
330
|
else
|
325
331
|
'did'
|
326
332
|
end
|
327
333
|
end
|
328
334
|
|
329
|
-
def
|
335
|
+
def args_currently_match?
|
330
336
|
if @expected.empty? # expect {...}.to yield_with_args
|
331
337
|
@positive_args_failure = 'yielded with no arguments' if @actual.empty?
|
332
338
|
return !@actual.empty?
|
@@ -335,7 +341,7 @@ module RSpec
|
|
335
341
|
unless (match = all_args_match?)
|
336
342
|
@positive_args_failure = 'yielded with unexpected arguments' \
|
337
343
|
"\nexpected: #{surface_descriptions_in(@expected).inspect}" \
|
338
|
-
"\n got: #{actual_formatted}"
|
344
|
+
"\n got: #{@actual_formatted}"
|
339
345
|
end
|
340
346
|
|
341
347
|
match
|
@@ -356,10 +362,21 @@ module RSpec
|
|
356
362
|
|
357
363
|
# @private
|
358
364
|
def matches?(block)
|
359
|
-
@
|
365
|
+
@actual_formatted = []
|
366
|
+
@actual = []
|
367
|
+
args_matched_when_yielded = true
|
368
|
+
yield_count = 0
|
369
|
+
|
370
|
+
@probe = YieldProbe.probe(block) do |*arg_array|
|
371
|
+
arg_or_args = arg_array.size == 1 ? arg_array.first : arg_array
|
372
|
+
@actual_formatted << RSpec::Support::ObjectFormatter.format(arg_or_args)
|
373
|
+
@actual << arg_or_args
|
374
|
+
args_matched_when_yielded &&= values_match?(@expected[yield_count], arg_or_args)
|
375
|
+
yield_count += 1
|
376
|
+
end
|
377
|
+
|
360
378
|
return false unless @probe.has_block?
|
361
|
-
|
362
|
-
args_match?
|
379
|
+
args_matched_when_yielded && yield_count == @expected.length
|
363
380
|
end
|
364
381
|
|
365
382
|
def does_not_match?(block)
|
@@ -390,10 +407,6 @@ module RSpec
|
|
390
407
|
|
391
408
|
private
|
392
409
|
|
393
|
-
def args_match?
|
394
|
-
values_match?(@expected, @actual)
|
395
|
-
end
|
396
|
-
|
397
410
|
def expected_arg_description
|
398
411
|
@expected.map { |e| description_of e }.join(', ')
|
399
412
|
end
|
@@ -403,7 +416,7 @@ module RSpec
|
|
403
416
|
|
404
417
|
'yielded with unexpected arguments' \
|
405
418
|
"\nexpected: #{surface_descriptions_in(@expected).inspect}" \
|
406
|
-
"\n got: #{actual_formatted}"
|
419
|
+
"\n got: [#{@actual_formatted.join(", ")}]"
|
407
420
|
end
|
408
421
|
|
409
422
|
def negative_failure_reason
|
@@ -411,7 +424,7 @@ module RSpec
|
|
411
424
|
|
412
425
|
'yielded with expected arguments' \
|
413
426
|
"\nexpected not: #{surface_descriptions_in(@expected).inspect}" \
|
414
|
-
"\n got: #{actual_formatted}"
|
427
|
+
"\n got: [#{@actual_formatted.join(", ")}]"
|
415
428
|
end
|
416
429
|
end
|
417
430
|
end
|
data/lib/rspec/matchers/dsl.rb
CHANGED
@@ -2,7 +2,70 @@ module RSpec
|
|
2
2
|
module Matchers
|
3
3
|
# Defines the custom matcher DSL.
|
4
4
|
module DSL
|
5
|
+
# Defines a matcher alias. The returned matcher's `description` will be overriden
|
6
|
+
# to reflect the phrasing of the new name, which will be used in failure messages
|
7
|
+
# when passed as an argument to another matcher in a composed matcher expression.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# RSpec::Matchers.alias_matcher :a_list_that_sums_to, :sum_to
|
11
|
+
# sum_to(3).description # => "sum to 3"
|
12
|
+
# a_list_that_sums_to(3).description # => "a list that sums to 3"
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# RSpec::Matchers.alias_matcher :a_list_sorted_by, :be_sorted_by do |description|
|
16
|
+
# description.sub("be sorted by", "a list sorted by")
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# be_sorted_by(:age).description # => "be sorted by age"
|
20
|
+
# a_list_sorted_by(:age).description # => "a list sorted by age"
|
21
|
+
#
|
22
|
+
# @param new_name [Symbol] the new name for the matcher
|
23
|
+
# @param old_name [Symbol] the original name for the matcher
|
24
|
+
# @param options [Hash] options for the aliased matcher
|
25
|
+
# @option options [Class] :klass the ruby class to use as the decorator. (Not normally used).
|
26
|
+
# @yield [String] optional block that, when given, is used to define the overriden
|
27
|
+
# logic. The yielded arg is the original description or failure message. If no
|
28
|
+
# block is provided, a default override is used based on the old and new names.
|
29
|
+
# @see RSpec::Matchers
|
30
|
+
def alias_matcher(new_name, old_name, options={}, &description_override)
|
31
|
+
description_override ||= lambda do |old_desc|
|
32
|
+
old_desc.gsub(EnglishPhrasing.split_words(old_name), EnglishPhrasing.split_words(new_name))
|
33
|
+
end
|
34
|
+
klass = options.fetch(:klass) { AliasedMatcher }
|
35
|
+
|
36
|
+
define_method(new_name) do |*args, &block|
|
37
|
+
matcher = __send__(old_name, *args, &block)
|
38
|
+
matcher.matcher_name = new_name if matcher.respond_to?(:matcher_name=)
|
39
|
+
klass.new(matcher, description_override)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Defines a negated matcher. The returned matcher's `description` and `failure_message`
|
44
|
+
# will be overriden to reflect the phrasing of the new name, and the match logic will
|
45
|
+
# be based on the original matcher but negated.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# RSpec::Matchers.define_negated_matcher :exclude, :include
|
49
|
+
# include(1, 2).description # => "include 1 and 2"
|
50
|
+
# exclude(1, 2).description # => "exclude 1 and 2"
|
51
|
+
#
|
52
|
+
# @param negated_name [Symbol] the name for the negated matcher
|
53
|
+
# @param base_name [Symbol] the name of the original matcher that will be negated
|
54
|
+
# @yield [String] optional block that, when given, is used to define the overriden
|
55
|
+
# logic. The yielded arg is the original description or failure message. If no
|
56
|
+
# block is provided, a default override is used based on the old and new names.
|
57
|
+
# @see RSpec::Matchers
|
58
|
+
def define_negated_matcher(negated_name, base_name, &description_override)
|
59
|
+
alias_matcher(negated_name, base_name, :klass => AliasedNegatedMatcher, &description_override)
|
60
|
+
end
|
61
|
+
|
5
62
|
# Defines a custom matcher.
|
63
|
+
#
|
64
|
+
# @param name [Symbol] the name for the matcher
|
65
|
+
# @yield [Object] block that is used to define the matcher.
|
66
|
+
# The block is evaluated in the context of your custom matcher class.
|
67
|
+
# When args are passed to your matcher, they will be yielded here,
|
68
|
+
# usually representing the expected value(s).
|
6
69
|
# @see RSpec::Matchers
|
7
70
|
def define(name, &declarations)
|
8
71
|
warn_about_block_args(name, declarations)
|
@@ -266,7 +329,7 @@ module RSpec
|
|
266
329
|
#
|
267
330
|
# This compiles the user block into an actual method, allowing
|
268
331
|
# them to use normal method constructs like `return`
|
269
|
-
# (e.g. for
|
332
|
+
# (e.g. for an early guard statement), while allowing us to define
|
270
333
|
# an override that can provide the wrapped handling
|
271
334
|
# (e.g. assigning `@actual`, rescueing errors, etc) and
|
272
335
|
# can `super` to the user's definition.
|
@@ -462,5 +525,3 @@ module RSpec
|
|
462
525
|
end
|
463
526
|
end
|
464
527
|
end
|
465
|
-
|
466
|
-
RSpec::Matchers.extend RSpec::Matchers::DSL
|
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.6.0
|
4
|
+
version: 3.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steven Baker
|
@@ -45,22 +45,22 @@ cert_chain:
|
|
45
45
|
ZsVDj6a7lH3cNqtWXZxrb2wO38qV5AkYj8SQK7Hj3/Yui9myUX3crr+PdetazSqQ
|
46
46
|
F3MdtaDehhjC
|
47
47
|
-----END CERTIFICATE-----
|
48
|
-
date:
|
48
|
+
date: 2017-05-04 00:00:00.000000000 Z
|
49
49
|
dependencies:
|
50
50
|
- !ruby/object:Gem::Dependency
|
51
51
|
name: rspec-support
|
52
52
|
requirement: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- -
|
54
|
+
- - "~>"
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version: 3.6.0
|
56
|
+
version: 3.6.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.6.0
|
63
|
+
version: 3.6.0
|
64
64
|
- !ruby/object:Gem::Dependency
|
65
65
|
name: diff-lcs
|
66
66
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,6 +150,7 @@ files:
|
|
150
150
|
- LICENSE.md
|
151
151
|
- README.md
|
152
152
|
- lib/rspec/expectations.rb
|
153
|
+
- lib/rspec/expectations/block_snippet_extractor.rb
|
153
154
|
- lib/rspec/expectations/configuration.rb
|
154
155
|
- lib/rspec/expectations/expectation_target.rb
|
155
156
|
- lib/rspec/expectations/fail_with.rb
|
@@ -212,14 +213,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
212
213
|
version: 1.8.7
|
213
214
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
214
215
|
requirements:
|
215
|
-
- - "
|
216
|
+
- - ">="
|
216
217
|
- !ruby/object:Gem::Version
|
217
|
-
version:
|
218
|
+
version: '0'
|
218
219
|
requirements: []
|
219
220
|
rubyforge_project:
|
220
|
-
rubygems_version: 2.
|
221
|
+
rubygems_version: 2.4.5.2
|
221
222
|
signing_key:
|
222
223
|
specification_version: 4
|
223
|
-
summary: rspec-expectations-3.6.0
|
224
|
+
summary: rspec-expectations-3.6.0
|
224
225
|
test_files: []
|
225
226
|
has_rdoc:
|
metadata.gz.sig
CHANGED
Binary file
|