rspec-expectations 3.0.4 → 3.12.3
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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +1 -1
- data/Changelog.md +530 -5
- data/{License.txt → LICENSE.md} +5 -4
- data/README.md +73 -31
- data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
- data/lib/rspec/expectations/configuration.rb +96 -1
- data/lib/rspec/expectations/expectation_target.rb +82 -38
- data/lib/rspec/expectations/fail_with.rb +11 -6
- data/lib/rspec/expectations/failure_aggregator.rb +229 -0
- data/lib/rspec/expectations/handler.rb +36 -15
- data/lib/rspec/expectations/minitest_integration.rb +43 -2
- data/lib/rspec/expectations/syntax.rb +5 -5
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/expectations.rb +15 -1
- data/lib/rspec/matchers/aliased_matcher.rb +79 -4
- data/lib/rspec/matchers/built_in/all.rb +11 -0
- data/lib/rspec/matchers/built_in/base_matcher.rb +111 -28
- data/lib/rspec/matchers/built_in/be.rb +28 -114
- data/lib/rspec/matchers/built_in/be_between.rb +1 -1
- data/lib/rspec/matchers/built_in/be_instance_of.rb +5 -1
- data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -1
- data/lib/rspec/matchers/built_in/be_within.rb +5 -12
- data/lib/rspec/matchers/built_in/change.rb +171 -63
- data/lib/rspec/matchers/built_in/compound.rb +201 -30
- data/lib/rspec/matchers/built_in/contain_exactly.rb +73 -12
- data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
- data/lib/rspec/matchers/built_in/eq.rb +3 -38
- data/lib/rspec/matchers/built_in/eql.rb +2 -2
- data/lib/rspec/matchers/built_in/equal.rb +3 -3
- data/lib/rspec/matchers/built_in/exist.rb +7 -3
- data/lib/rspec/matchers/built_in/has.rb +93 -30
- data/lib/rspec/matchers/built_in/have_attributes.rb +114 -0
- data/lib/rspec/matchers/built_in/include.rb +133 -25
- data/lib/rspec/matchers/built_in/match.rb +79 -2
- data/lib/rspec/matchers/built_in/operators.rb +14 -5
- data/lib/rspec/matchers/built_in/output.rb +59 -2
- data/lib/rspec/matchers/built_in/raise_error.rb +130 -27
- data/lib/rspec/matchers/built_in/respond_to.rb +117 -15
- data/lib/rspec/matchers/built_in/satisfy.rb +28 -14
- data/lib/rspec/matchers/built_in/{start_and_end_with.rb → start_or_end_with.rb} +20 -8
- data/lib/rspec/matchers/built_in/throw_symbol.rb +15 -5
- data/lib/rspec/matchers/built_in/yield.rb +129 -156
- data/lib/rspec/matchers/built_in.rb +5 -3
- data/lib/rspec/matchers/composable.rb +24 -36
- data/lib/rspec/matchers/dsl.rb +203 -37
- data/lib/rspec/matchers/english_phrasing.rb +58 -0
- data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +82 -0
- data/lib/rspec/matchers/fail_matchers.rb +42 -0
- data/lib/rspec/matchers/generated_descriptions.rb +1 -2
- data/lib/rspec/matchers/matcher_delegator.rb +3 -4
- data/lib/rspec/matchers/matcher_protocol.rb +105 -0
- data/lib/rspec/matchers.rb +267 -144
- data.tar.gz.sig +0 -0
- metadata +71 -49
- metadata.gz.sig +0 -0
- data/lib/rspec/matchers/pretty.rb +0 -77
@@ -80,11 +80,10 @@ module RSpec
|
|
80
80
|
#
|
81
81
|
# @!visibility public
|
82
82
|
def description_of(object)
|
83
|
-
|
84
|
-
object.inspect
|
83
|
+
RSpec::Support::ObjectFormatter.format(object)
|
85
84
|
end
|
86
85
|
|
87
|
-
# Transforms the given data
|
86
|
+
# Transforms the given data structure (typically a hash or array)
|
88
87
|
# into a new data structure that, when `#inspect` is called on it,
|
89
88
|
# will provide descriptions of any contained matchers rather than
|
90
89
|
# the normal `#inspect` output.
|
@@ -101,14 +100,10 @@ module RSpec
|
|
101
100
|
DescribableItem.new(item)
|
102
101
|
elsif Hash === item
|
103
102
|
Hash[surface_descriptions_in(item.to_a)]
|
104
|
-
elsif Struct === item
|
105
|
-
item
|
106
|
-
elsif
|
107
|
-
|
108
|
-
item.map { |subitem| surface_descriptions_in(subitem) }
|
109
|
-
rescue IOError # STDOUT is enumerable but `map` raises an error
|
110
|
-
item.inspect
|
111
|
-
end
|
103
|
+
elsif Struct === item || unreadable_io?(item)
|
104
|
+
RSpec::Support::ObjectFormatter.format(item)
|
105
|
+
elsif should_enumerate?(item)
|
106
|
+
item.map { |subitem| surface_descriptions_in(subitem) }
|
112
107
|
else
|
113
108
|
item
|
114
109
|
end
|
@@ -135,45 +130,38 @@ module RSpec
|
|
135
130
|
object.clone
|
136
131
|
elsif Hash === object
|
137
132
|
Hash[with_matchers_cloned(object.to_a)]
|
138
|
-
elsif
|
139
|
-
object
|
140
|
-
elsif enumerable?(object)
|
141
|
-
begin
|
142
|
-
object.map { |subobject| with_matchers_cloned(subobject) }
|
143
|
-
rescue IOError # STDOUT is enumerable but `map` raises an error
|
144
|
-
object
|
145
|
-
end
|
133
|
+
elsif should_enumerate?(object)
|
134
|
+
object.map { |subobject| with_matchers_cloned(subobject) }
|
146
135
|
else
|
147
136
|
object
|
148
137
|
end
|
149
138
|
end
|
150
139
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
def enumerable?(item)
|
165
|
-
Enumerable === item
|
166
|
-
end
|
140
|
+
# @api private
|
141
|
+
# We should enumerate arrays as long as they are not recursive.
|
142
|
+
def should_enumerate?(item)
|
143
|
+
Array === item && item.none? { |subitem| subitem.equal?(item) }
|
144
|
+
end
|
145
|
+
|
146
|
+
# @api private
|
147
|
+
def unreadable_io?(object)
|
148
|
+
return false unless IO === object
|
149
|
+
object.each {} # STDOUT is enumerable but raises an error
|
150
|
+
false
|
151
|
+
rescue IOError
|
152
|
+
true
|
167
153
|
end
|
168
|
-
module_function :surface_descriptions_in, :
|
154
|
+
module_function :surface_descriptions_in, :should_enumerate?, :unreadable_io?
|
169
155
|
|
170
156
|
# Wraps an item in order to surface its `description` via `inspect`.
|
171
157
|
# @api private
|
172
158
|
DescribableItem = Struct.new(:item) do
|
159
|
+
# Inspectable version of the item description
|
173
160
|
def inspect
|
174
161
|
"(#{item.description})"
|
175
162
|
end
|
176
163
|
|
164
|
+
# A pretty printed version of the item description.
|
177
165
|
def pretty_print(pp)
|
178
166
|
pp.text "(#{item.description})"
|
179
167
|
end
|
data/lib/rspec/matchers/dsl.rb
CHANGED
@@ -1,16 +1,102 @@
|
|
1
|
+
RSpec::Support.require_rspec_support "with_keywords_when_needed"
|
2
|
+
|
1
3
|
module RSpec
|
2
4
|
module Matchers
|
3
5
|
# Defines the custom matcher DSL.
|
4
6
|
module DSL
|
7
|
+
# Defines a matcher alias. The returned matcher's `description` will be overridden
|
8
|
+
# to reflect the phrasing of the new name, which will be used in failure messages
|
9
|
+
# when passed as an argument to another matcher in a composed matcher expression.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# RSpec::Matchers.alias_matcher :a_list_that_sums_to, :sum_to
|
13
|
+
# sum_to(3).description # => "sum to 3"
|
14
|
+
# a_list_that_sums_to(3).description # => "a list that sums to 3"
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# RSpec::Matchers.alias_matcher :a_list_sorted_by, :be_sorted_by do |description|
|
18
|
+
# description.sub("be sorted by", "a list sorted by")
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# be_sorted_by(:age).description # => "be sorted by age"
|
22
|
+
# a_list_sorted_by(:age).description # => "a list sorted by age"
|
23
|
+
#
|
24
|
+
# @param new_name [Symbol] the new name for the matcher
|
25
|
+
# @param old_name [Symbol] the original name for the matcher
|
26
|
+
# @param options [Hash] options for the aliased matcher
|
27
|
+
# @option options [Class] :klass the ruby class to use as the decorator. (Not normally used).
|
28
|
+
# @yield [String] optional block that, when given, is used to define the overridden
|
29
|
+
# logic. The yielded arg is the original description or failure message. If no
|
30
|
+
# block is provided, a default override is used based on the old and new names.
|
31
|
+
# @see RSpec::Matchers
|
32
|
+
def alias_matcher(new_name, old_name, options={}, &description_override)
|
33
|
+
description_override ||= lambda do |old_desc|
|
34
|
+
old_desc.gsub(EnglishPhrasing.split_words(old_name), EnglishPhrasing.split_words(new_name))
|
35
|
+
end
|
36
|
+
klass = options.fetch(:klass) { AliasedMatcher }
|
37
|
+
|
38
|
+
define_method(new_name) do |*args, &block|
|
39
|
+
matcher = __send__(old_name, *args, &block)
|
40
|
+
matcher.matcher_name = new_name if matcher.respond_to?(:matcher_name=)
|
41
|
+
klass.new(matcher, description_override)
|
42
|
+
end
|
43
|
+
ruby2_keywords new_name if respond_to?(:ruby2_keywords, true)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Defines a negated matcher. The returned matcher's `description` and `failure_message`
|
47
|
+
# will be overridden to reflect the phrasing of the new name, and the match logic will
|
48
|
+
# be based on the original matcher but negated.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# RSpec::Matchers.define_negated_matcher :exclude, :include
|
52
|
+
# include(1, 2).description # => "include 1 and 2"
|
53
|
+
# exclude(1, 2).description # => "exclude 1 and 2"
|
54
|
+
#
|
55
|
+
# @param negated_name [Symbol] the name for the negated matcher
|
56
|
+
# @param base_name [Symbol] the name of the original matcher that will be negated
|
57
|
+
# @yield [String] optional block that, when given, is used to define the overridden
|
58
|
+
# logic. The yielded arg is the original description or failure message. If no
|
59
|
+
# block is provided, a default override is used based on the old and new names.
|
60
|
+
# @see RSpec::Matchers
|
61
|
+
def define_negated_matcher(negated_name, base_name, &description_override)
|
62
|
+
alias_matcher(negated_name, base_name, :klass => AliasedNegatedMatcher, &description_override)
|
63
|
+
end
|
64
|
+
|
5
65
|
# Defines a custom matcher.
|
66
|
+
#
|
67
|
+
# @param name [Symbol] the name for the matcher
|
68
|
+
# @yield [Object] block that is used to define the matcher.
|
69
|
+
# The block is evaluated in the context of your custom matcher class.
|
70
|
+
# When args are passed to your matcher, they will be yielded here,
|
71
|
+
# usually representing the expected value(s).
|
6
72
|
# @see RSpec::Matchers
|
7
73
|
def define(name, &declarations)
|
8
|
-
|
9
|
-
|
74
|
+
warn_about_block_args(name, declarations)
|
75
|
+
define_method name do |*expected, &block_arg|
|
76
|
+
RSpec::Matchers::DSL::Matcher.new(name, declarations, self, *expected, &block_arg)
|
10
77
|
end
|
11
78
|
end
|
12
79
|
alias_method :matcher, :define
|
13
80
|
|
81
|
+
private
|
82
|
+
|
83
|
+
if Proc.method_defined?(:parameters)
|
84
|
+
def warn_about_block_args(name, declarations)
|
85
|
+
declarations.parameters.each do |type, arg_name|
|
86
|
+
next unless type == :block
|
87
|
+
RSpec.warning("Your `#{name}` custom matcher receives a block argument (`#{arg_name}`), " \
|
88
|
+
"but due to limitations in ruby, RSpec cannot provide the block. Instead, " \
|
89
|
+
"use the `block_arg` method to access the block")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else
|
93
|
+
# :nocov:
|
94
|
+
def warn_about_block_args(*)
|
95
|
+
# There's no way to detect block params on 1.8 since the method reflection APIs don't expose it
|
96
|
+
end
|
97
|
+
# :nocov:
|
98
|
+
end
|
99
|
+
|
14
100
|
RSpec.configure { |c| c.extend self } if RSpec.respond_to?(:configure)
|
15
101
|
|
16
102
|
# Contains the methods that are available from within the
|
@@ -35,28 +121,53 @@ module RSpec
|
|
35
121
|
# expect(3).to be_even # fails
|
36
122
|
# expect(4).not_to be_even # fails
|
37
123
|
#
|
124
|
+
# By default the match block will swallow expectation errors (e.g.
|
125
|
+
# caused by using an expectation such as `expect(1).to eq 2`), if you
|
126
|
+
# wish to allow these to bubble up, pass in the option
|
127
|
+
# `:notify_expectation_failures => true`.
|
128
|
+
#
|
129
|
+
# @param [Hash] options for defining the behavior of the match block.
|
38
130
|
# @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
|
39
|
-
def match(&match_block)
|
131
|
+
def match(options={}, &match_block)
|
40
132
|
define_user_override(:matches?, match_block) do |actual|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
133
|
+
@actual = actual
|
134
|
+
RSpec::Support.with_failure_notifier(RAISE_NOTIFIER) do
|
135
|
+
begin
|
136
|
+
super(*actual_arg_for(match_block))
|
137
|
+
rescue RSpec::Expectations::ExpectationNotMetError
|
138
|
+
raise if options[:notify_expectation_failures]
|
139
|
+
false
|
140
|
+
end
|
46
141
|
end
|
47
142
|
end
|
48
143
|
end
|
49
144
|
|
145
|
+
# @private
|
146
|
+
RAISE_NOTIFIER = Proc.new { |err, _opts| raise err }
|
147
|
+
|
50
148
|
# Use this to define the block for a negative expectation (`expect(...).not_to`)
|
51
149
|
# when the positive and negative forms require different handling. This
|
52
150
|
# is rarely necessary, but can be helpful, for example, when specifying
|
53
151
|
# asynchronous processes that require different timeouts.
|
54
152
|
#
|
153
|
+
# By default the match block will swallow expectation errors (e.g.
|
154
|
+
# caused by using an expectation such as `expect(1).to eq 2`), if you
|
155
|
+
# wish to allow these to bubble up, pass in the option
|
156
|
+
# `:notify_expectation_failures => true`.
|
157
|
+
#
|
158
|
+
# @param [Hash] options for defining the behavior of the match block.
|
55
159
|
# @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
|
56
|
-
def match_when_negated(&match_block)
|
160
|
+
def match_when_negated(options={}, &match_block)
|
57
161
|
define_user_override(:does_not_match?, match_block) do |actual|
|
58
|
-
|
59
|
-
|
162
|
+
begin
|
163
|
+
@actual = actual
|
164
|
+
RSpec::Support.with_failure_notifier(RAISE_NOTIFIER) do
|
165
|
+
super(*actual_arg_for(match_block))
|
166
|
+
end
|
167
|
+
rescue RSpec::Expectations::ExpectationNotMetError
|
168
|
+
raise if options[:notify_expectation_failures]
|
169
|
+
false
|
170
|
+
end
|
60
171
|
end
|
61
172
|
end
|
62
173
|
|
@@ -87,7 +198,7 @@ module RSpec
|
|
87
198
|
end
|
88
199
|
end
|
89
200
|
|
90
|
-
# Customizes the failure
|
201
|
+
# Customizes the failure message to use when this matcher is
|
91
202
|
# asked to positively match. Only use this when the message
|
92
203
|
# generated by default doesn't suit your needs.
|
93
204
|
#
|
@@ -106,7 +217,7 @@ module RSpec
|
|
106
217
|
define_user_override(__method__, definition)
|
107
218
|
end
|
108
219
|
|
109
|
-
# Customize the failure
|
220
|
+
# Customize the failure message to use when this matcher is asked
|
110
221
|
# to negatively match. Only use this when the message generated by
|
111
222
|
# default doesn't suit your needs.
|
112
223
|
#
|
@@ -160,7 +271,16 @@ module RSpec
|
|
160
271
|
# Convenience for defining methods on this matcher to create a fluent
|
161
272
|
# interface. The trick about fluent interfaces is that each method must
|
162
273
|
# return self in order to chain methods together. `chain` handles that
|
163
|
-
# for you.
|
274
|
+
# for you. If the method is invoked and the
|
275
|
+
# `include_chain_clauses_in_custom_matcher_descriptions` config option
|
276
|
+
# hash been enabled, the chained method name and args will be added to the
|
277
|
+
# default description and failure message.
|
278
|
+
#
|
279
|
+
# In the common case where you just want the chained method to store some
|
280
|
+
# value(s) for later use (e.g. in `match`), you can provide one or more
|
281
|
+
# attribute names instead of a block; the chained method will store its
|
282
|
+
# arguments in instance variables with those names, and the values will
|
283
|
+
# be exposed via getters.
|
164
284
|
#
|
165
285
|
# @example
|
166
286
|
#
|
@@ -175,21 +295,43 @@ module RSpec
|
|
175
295
|
# end
|
176
296
|
#
|
177
297
|
# expect(minor).to have_errors_on(:age).with("Not old enough to participate")
|
178
|
-
def chain(
|
179
|
-
|
298
|
+
def chain(method_name, *attr_names, &definition)
|
299
|
+
unless block_given? ^ attr_names.any?
|
300
|
+
raise ArgumentError, "You must pass either a block or some attribute names (but not both) to `chain`."
|
301
|
+
end
|
302
|
+
|
303
|
+
definition = assign_attributes(attr_names) if attr_names.any?
|
304
|
+
|
305
|
+
define_user_override(method_name, definition) do |*args, &block|
|
180
306
|
super(*args, &block)
|
307
|
+
@chained_method_clauses.push([method_name, args])
|
181
308
|
self
|
182
309
|
end
|
183
310
|
end
|
184
311
|
|
312
|
+
def assign_attributes(attr_names)
|
313
|
+
attr_reader(*attr_names)
|
314
|
+
private(*attr_names)
|
315
|
+
|
316
|
+
lambda do |*attr_values|
|
317
|
+
attr_names.zip(attr_values) do |attr_name, attr_value|
|
318
|
+
instance_variable_set(:"@#{attr_name}", attr_value)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
# assign_attributes isn't defined in the private section below because
|
324
|
+
# that makes MRI 1.9.2 emit a warning about private attributes.
|
325
|
+
private :assign_attributes
|
326
|
+
|
185
327
|
private
|
186
328
|
|
187
329
|
# Does the following:
|
188
330
|
#
|
189
|
-
# - Defines the named method
|
331
|
+
# - Defines the named method using a user-provided block
|
190
332
|
# in @user_method_defs, which is included as an ancestor
|
191
333
|
# in the singleton class in which we eval the `define` block.
|
192
|
-
# - Defines an
|
334
|
+
# - Defines an overridden definition for the same method
|
193
335
|
# usign the provided `our_def` block.
|
194
336
|
# - Provides a default `our_def` block for the common case
|
195
337
|
# of needing to call the user's definition with `@actual`
|
@@ -197,7 +339,7 @@ module RSpec
|
|
197
339
|
#
|
198
340
|
# This compiles the user block into an actual method, allowing
|
199
341
|
# them to use normal method constructs like `return`
|
200
|
-
# (e.g. for
|
342
|
+
# (e.g. for an early guard statement), while allowing us to define
|
201
343
|
# an override that can provide the wrapped handling
|
202
344
|
# (e.g. assigning `@actual`, rescueing errors, etc) and
|
203
345
|
# can `super` to the user's definition.
|
@@ -241,6 +383,8 @@ module RSpec
|
|
241
383
|
# override any of these using the {RSpec::Matchers::DSL::Macros Macros} methods
|
242
384
|
# from within an `RSpec::Matchers.define` block.
|
243
385
|
module DefaultImplementations
|
386
|
+
include BuiltIn::BaseMatcher::DefaultFailureMessages
|
387
|
+
|
244
388
|
# @api private
|
245
389
|
# Used internally by objects returns by `should` and `should_not`.
|
246
390
|
def diffable?
|
@@ -249,24 +393,37 @@ module RSpec
|
|
249
393
|
|
250
394
|
# The default description.
|
251
395
|
def description
|
252
|
-
|
396
|
+
english_name = EnglishPhrasing.split_words(name)
|
397
|
+
expected_list = EnglishPhrasing.list(expected)
|
398
|
+
"#{english_name}#{expected_list}#{chained_method_clause_sentences}"
|
253
399
|
end
|
254
400
|
|
255
|
-
#
|
256
|
-
|
257
|
-
|
401
|
+
# Matchers do not support block expectations by default. You
|
402
|
+
# must opt-in.
|
403
|
+
def supports_block_expectations?
|
404
|
+
false
|
258
405
|
end
|
259
406
|
|
260
|
-
|
261
|
-
|
262
|
-
"expected #{actual.inspect} not to #{name_to_sentence}#{to_sentence expected}"
|
407
|
+
def supports_value_expectations?
|
408
|
+
true
|
263
409
|
end
|
264
410
|
|
265
|
-
#
|
266
|
-
|
267
|
-
def supports_block_expectations?
|
411
|
+
# Most matchers do not expect call stack jumps.
|
412
|
+
def expects_call_stack_jump?
|
268
413
|
false
|
269
414
|
end
|
415
|
+
|
416
|
+
private
|
417
|
+
|
418
|
+
def chained_method_clause_sentences
|
419
|
+
return '' unless Expectations.configuration.include_chain_clauses_in_custom_matcher_descriptions?
|
420
|
+
|
421
|
+
@chained_method_clauses.map do |(method_name, method_args)|
|
422
|
+
english_name = EnglishPhrasing.split_words(method_name)
|
423
|
+
arg_list = EnglishPhrasing.list(method_args)
|
424
|
+
" #{english_name}#{arg_list}"
|
425
|
+
end.join
|
426
|
+
end
|
270
427
|
end
|
271
428
|
|
272
429
|
# The class used for custom matchers. The block passed to
|
@@ -280,9 +437,6 @@ module RSpec
|
|
280
437
|
# Allows expectation expressions to be used in the match block.
|
281
438
|
include RSpec::Matchers
|
282
439
|
|
283
|
-
# Converts matcher name and expected args to an English expresion.
|
284
|
-
include RSpec::Matchers::Pretty
|
285
|
-
|
286
440
|
# Supports the matcher composability features of RSpec 3+.
|
287
441
|
include Composable
|
288
442
|
|
@@ -298,18 +452,27 @@ module RSpec
|
|
298
452
|
# Could be useful to extract details for a failure message.
|
299
453
|
attr_reader :rescued_exception
|
300
454
|
|
455
|
+
# The block parameter used in the expectation
|
456
|
+
attr_reader :block_arg
|
457
|
+
|
458
|
+
# The name of the matcher.
|
459
|
+
attr_reader :name
|
460
|
+
|
301
461
|
# @api private
|
302
|
-
def initialize(name, declarations, matcher_execution_context, *expected)
|
462
|
+
def initialize(name, declarations, matcher_execution_context, *expected, &block_arg)
|
303
463
|
@name = name
|
304
464
|
@actual = nil
|
305
465
|
@expected_as_array = expected
|
306
466
|
@matcher_execution_context = matcher_execution_context
|
467
|
+
@chained_method_clauses = []
|
468
|
+
@block_arg = block_arg
|
307
469
|
|
308
|
-
class << self
|
470
|
+
klass = class << self
|
309
471
|
# See `Macros#define_user_override` above, for an explanation.
|
310
472
|
include(@user_method_defs = Module.new)
|
311
473
|
self
|
312
|
-
end
|
474
|
+
end
|
475
|
+
RSpec::Support::WithKeywordsWhenNeeded.class_exec(klass, *expected, &declarations)
|
313
476
|
end
|
314
477
|
|
315
478
|
# Provides the expected value. This will return an array if
|
@@ -345,11 +508,13 @@ module RSpec
|
|
345
508
|
super || @matcher_execution_context.respond_to?(method, include_private)
|
346
509
|
end
|
347
510
|
else # for 1.8.7
|
511
|
+
# :nocov:
|
348
512
|
# Indicates that this matcher responds to messages
|
349
513
|
# from the `@matcher_execution_context` as well.
|
350
514
|
def respond_to?(method, include_private=false)
|
351
515
|
super || @matcher_execution_context.respond_to?(method, include_private)
|
352
516
|
end
|
517
|
+
# :nocov:
|
353
518
|
end
|
354
519
|
|
355
520
|
private
|
@@ -371,9 +536,10 @@ module RSpec
|
|
371
536
|
super(method, *args, &block)
|
372
537
|
end
|
373
538
|
end
|
539
|
+
# The method_missing method should be refactored to pass kw args in RSpec 4
|
540
|
+
# then this can be removed
|
541
|
+
ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
|
374
542
|
end
|
375
543
|
end
|
376
544
|
end
|
377
545
|
end
|
378
|
-
|
379
|
-
RSpec::Matchers.extend RSpec::Matchers::DSL
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
# Facilitates converting ruby objects to English phrases.
|
4
|
+
module EnglishPhrasing
|
5
|
+
# Converts a symbol into an English expression.
|
6
|
+
#
|
7
|
+
# split_words(:banana_creme_pie) #=> "banana creme pie"
|
8
|
+
#
|
9
|
+
def self.split_words(sym)
|
10
|
+
sym.to_s.tr('_', ' ')
|
11
|
+
end
|
12
|
+
|
13
|
+
# @note The returned string has a leading space except
|
14
|
+
# when given an empty list.
|
15
|
+
#
|
16
|
+
# Converts an object (often a collection of objects)
|
17
|
+
# into an English list.
|
18
|
+
#
|
19
|
+
# list(['banana', 'kiwi', 'mango'])
|
20
|
+
# #=> " \"banana\", \"kiwi\", and \"mango\""
|
21
|
+
#
|
22
|
+
# Given an empty collection, returns the empty string.
|
23
|
+
#
|
24
|
+
# list([]) #=> ""
|
25
|
+
#
|
26
|
+
def self.list(obj)
|
27
|
+
return " #{RSpec::Support::ObjectFormatter.format(obj)}" if !obj || Struct === obj || Hash === obj
|
28
|
+
items = Array(obj).map { |w| RSpec::Support::ObjectFormatter.format(w) }
|
29
|
+
case items.length
|
30
|
+
when 0
|
31
|
+
""
|
32
|
+
when 1
|
33
|
+
" #{items[0]}"
|
34
|
+
when 2
|
35
|
+
" #{items[0]} and #{items[1]}"
|
36
|
+
else
|
37
|
+
" #{items[0...-1].join(', ')}, and #{items[-1]}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if RUBY_VERSION == '1.8.7'
|
42
|
+
# Not sure why, but on travis on 1.8.7 we have gotten these warnings:
|
43
|
+
# lib/rspec/matchers/english_phrasing.rb:28: warning: default `to_a' will be obsolete
|
44
|
+
# So it appears that `Array` can trigger that (e.g. by calling `to_a` on the passed object?)
|
45
|
+
# So here we replace `Kernel#Array` with our own warning-free implementation for 1.8.7.
|
46
|
+
# @private
|
47
|
+
# rubocop:disable Naming/MethodName
|
48
|
+
def self.Array(obj)
|
49
|
+
case obj
|
50
|
+
when Array then obj
|
51
|
+
else [obj]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
# rubocop:enable Naming/MethodName
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
# @api private
|
4
|
+
# Handles list of expected values when there is a need to render
|
5
|
+
# multiple diffs. Also can handle one value.
|
6
|
+
class ExpectedsForMultipleDiffs
|
7
|
+
# @private
|
8
|
+
# Default diff label when there is only one matcher in diff
|
9
|
+
# output
|
10
|
+
DEFAULT_DIFF_LABEL = "Diff:".freeze
|
11
|
+
|
12
|
+
# @private
|
13
|
+
# Maximum readable matcher description length
|
14
|
+
DESCRIPTION_MAX_LENGTH = 65
|
15
|
+
|
16
|
+
def initialize(expected_list)
|
17
|
+
@expected_list = expected_list
|
18
|
+
end
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
# Wraps provided expected value in instance of
|
22
|
+
# ExpectedForMultipleDiffs. If provided value is already an
|
23
|
+
# ExpectedForMultipleDiffs then it just returns it.
|
24
|
+
# @param [Any] expected value to be wrapped
|
25
|
+
# @return [RSpec::Matchers::ExpectedsForMultipleDiffs]
|
26
|
+
def self.from(expected)
|
27
|
+
return expected if self === expected
|
28
|
+
new([[expected, DEFAULT_DIFF_LABEL]])
|
29
|
+
end
|
30
|
+
|
31
|
+
# @api private
|
32
|
+
# Wraps provided matcher list in instance of
|
33
|
+
# ExpectedForMultipleDiffs.
|
34
|
+
# @param [Array<Any>] matchers list of matchers to wrap
|
35
|
+
# @return [RSpec::Matchers::ExpectedsForMultipleDiffs]
|
36
|
+
def self.for_many_matchers(matchers)
|
37
|
+
new(matchers.map { |m| [m.expected, diff_label_for(m)] })
|
38
|
+
end
|
39
|
+
|
40
|
+
# @api private
|
41
|
+
# Returns message with diff(s) appended for provided differ
|
42
|
+
# factory and actual value if there are any
|
43
|
+
# @param [String] message original failure message
|
44
|
+
# @param [Proc] differ
|
45
|
+
# @param [Any] actual value
|
46
|
+
# @return [String]
|
47
|
+
def message_with_diff(message, differ, actual)
|
48
|
+
diff = diffs(differ, actual)
|
49
|
+
message = "#{message}\n#{diff}" unless diff.empty?
|
50
|
+
message
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
class << self
|
56
|
+
private
|
57
|
+
|
58
|
+
def diff_label_for(matcher)
|
59
|
+
"Diff for (#{truncated(RSpec::Support::ObjectFormatter.format(matcher))}):"
|
60
|
+
end
|
61
|
+
|
62
|
+
def truncated(description)
|
63
|
+
return description if description.length <= DESCRIPTION_MAX_LENGTH
|
64
|
+
description[0...DESCRIPTION_MAX_LENGTH - 3] << "..."
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def diffs(differ, actual)
|
69
|
+
@expected_list.map do |(expected, diff_label)|
|
70
|
+
diff = differ.diff(actual, expected)
|
71
|
+
next if diff.strip.empty?
|
72
|
+
if diff == "\e[0m\n\e[0m"
|
73
|
+
"#{diff_label}\n" \
|
74
|
+
" <The diff is empty, are your objects producing identical `#inspect` output?>"
|
75
|
+
else
|
76
|
+
"#{diff_label}#{diff}"
|
77
|
+
end
|
78
|
+
end.compact.join("\n")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rspec/expectations'
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Matchers
|
5
|
+
# Matchers for testing RSpec matchers. Include them with:
|
6
|
+
#
|
7
|
+
# require 'rspec/matchers/fail_matchers'
|
8
|
+
# RSpec.configure do |config|
|
9
|
+
# config.include RSpec::Matchers::FailMatchers
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
module FailMatchers
|
13
|
+
# Matches if an expectation fails
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# expect { some_expectation }.to fail
|
17
|
+
def fail(&block)
|
18
|
+
raise_error(RSpec::Expectations::ExpectationNotMetError, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Matches if an expectation fails with the provided message
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# expect { some_expectation }.to fail_with("some failure message")
|
25
|
+
# expect { some_expectation }.to fail_with(/some failure message/)
|
26
|
+
def fail_with(message)
|
27
|
+
raise_error(RSpec::Expectations::ExpectationNotMetError, message)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Matches if an expectation fails including the provided message
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# expect { some_expectation }.to fail_including("portion of some failure message")
|
34
|
+
def fail_including(*snippets)
|
35
|
+
raise_error(
|
36
|
+
RSpec::Expectations::ExpectationNotMetError,
|
37
|
+
a_string_including(*snippets)
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -21,8 +21,7 @@ module RSpec
|
|
21
21
|
"#{last_expectation_handler.verb} #{last_description}"
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
# @private
|
26
25
|
def self.last_description
|
27
26
|
last_matcher.respond_to?(:description) ? last_matcher.description : <<-MESSAGE
|
28
27
|
When you call a matcher in an example without a String, like this:
|