rspec-expectations 3.6.0.beta2 → 3.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|