rspec-expectations 3.0.0.beta1 → 3.0.0.beta2
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.
- data.tar.gz.sig +2 -2
- data/.yardopts +1 -0
- data/Changelog.md +138 -0
- data/README.md +75 -8
- data/features/README.md +2 -2
- data/features/built_in_matchers/README.md +12 -9
- data/features/built_in_matchers/comparisons.feature +2 -2
- data/features/built_in_matchers/contain_exactly.feature +46 -0
- data/features/built_in_matchers/expect_change.feature +2 -2
- data/features/built_in_matchers/include.feature +0 -48
- data/features/built_in_matchers/output.feature +70 -0
- data/features/composing_matchers.feature +250 -0
- data/features/compound_expectations.feature +45 -0
- data/features/custom_matchers/access_running_example.feature +1 -1
- data/features/custom_matchers/define_matcher.feature +6 -6
- data/features/custom_matchers/define_matcher_outside_rspec.feature +4 -8
- data/features/test_frameworks/{test_unit.feature → minitest.feature} +11 -11
- data/lib/rspec/expectations.rb +31 -42
- data/lib/rspec/expectations/diff_presenter.rb +141 -0
- data/lib/rspec/expectations/differ.rb +22 -132
- data/lib/rspec/expectations/encoded_string.rb +56 -0
- data/lib/rspec/expectations/expectation_target.rb +0 -30
- data/lib/rspec/expectations/fail_with.rb +2 -2
- data/lib/rspec/expectations/handler.rb +128 -31
- data/lib/rspec/expectations/minitest_integration.rb +16 -0
- data/lib/rspec/expectations/syntax.rb +4 -58
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/matchers.rb +298 -60
- data/lib/rspec/matchers/aliased_matcher.rb +35 -0
- data/lib/rspec/matchers/built_in.rb +37 -33
- data/lib/rspec/matchers/built_in/base_matcher.rb +25 -15
- data/lib/rspec/matchers/built_in/be.rb +23 -31
- data/lib/rspec/matchers/built_in/be_between.rb +55 -0
- data/lib/rspec/matchers/built_in/be_within.rb +15 -11
- data/lib/rspec/matchers/built_in/change.rb +198 -81
- data/lib/rspec/matchers/built_in/compound.rb +106 -0
- data/lib/rspec/matchers/built_in/contain_exactly.rb +245 -0
- data/lib/rspec/matchers/built_in/eq.rb +43 -4
- data/lib/rspec/matchers/built_in/eql.rb +2 -2
- data/lib/rspec/matchers/built_in/equal.rb +35 -18
- data/lib/rspec/matchers/built_in/has.rb +16 -15
- data/lib/rspec/matchers/built_in/include.rb +45 -23
- data/lib/rspec/matchers/built_in/match.rb +6 -3
- data/lib/rspec/matchers/built_in/operators.rb +103 -0
- data/lib/rspec/matchers/built_in/output.rb +108 -0
- data/lib/rspec/matchers/built_in/raise_error.rb +9 -15
- data/lib/rspec/matchers/built_in/respond_to.rb +5 -4
- data/lib/rspec/matchers/built_in/satisfy.rb +4 -3
- data/lib/rspec/matchers/built_in/start_and_end_with.rb +37 -16
- data/lib/rspec/matchers/built_in/throw_symbol.rb +6 -5
- data/lib/rspec/matchers/built_in/yield.rb +31 -29
- data/lib/rspec/matchers/composable.rb +138 -0
- data/lib/rspec/matchers/dsl.rb +330 -0
- data/lib/rspec/matchers/generated_descriptions.rb +6 -6
- data/lib/rspec/matchers/matcher_delegator.rb +33 -0
- data/lib/rspec/matchers/pretty.rb +13 -2
- data/spec/rspec/expectations/{differ_spec.rb → diff_presenter_spec.rb} +56 -36
- data/spec/rspec/expectations/encoded_string_spec.rb +74 -0
- data/spec/rspec/expectations/extensions/kernel_spec.rb +11 -11
- data/spec/rspec/expectations/fail_with_spec.rb +8 -8
- data/spec/rspec/expectations/handler_spec.rb +27 -49
- data/spec/rspec/expectations/minitest_integration_spec.rb +27 -0
- data/spec/rspec/expectations/syntax_spec.rb +17 -67
- data/spec/rspec/expectations_spec.rb +7 -52
- data/spec/rspec/matchers/aliased_matcher_spec.rb +48 -0
- data/spec/rspec/matchers/aliases_spec.rb +449 -0
- data/spec/rspec/matchers/{base_matcher_spec.rb → built_in/base_matcher_spec.rb} +24 -3
- data/spec/rspec/matchers/built_in/be_between_spec.rb +159 -0
- data/spec/rspec/matchers/{be_instance_of_spec.rb → built_in/be_instance_of_spec.rb} +0 -0
- data/spec/rspec/matchers/{be_kind_of_spec.rb → built_in/be_kind_of_spec.rb} +0 -0
- data/spec/rspec/matchers/{be_spec.rb → built_in/be_spec.rb} +76 -32
- data/spec/rspec/matchers/{be_within_spec.rb → built_in/be_within_spec.rb} +6 -2
- data/spec/rspec/matchers/{change_spec.rb → built_in/change_spec.rb} +310 -69
- data/spec/rspec/matchers/built_in/compound_spec.rb +292 -0
- data/spec/rspec/matchers/built_in/contain_exactly_spec.rb +441 -0
- data/spec/rspec/matchers/{cover_spec.rb → built_in/cover_spec.rb} +0 -0
- data/spec/rspec/matchers/built_in/eq_spec.rb +156 -0
- data/spec/rspec/matchers/{eql_spec.rb → built_in/eql_spec.rb} +2 -2
- data/spec/rspec/matchers/built_in/equal_spec.rb +106 -0
- data/spec/rspec/matchers/{exist_spec.rb → built_in/exist_spec.rb} +1 -1
- data/spec/rspec/matchers/{has_spec.rb → built_in/has_spec.rb} +39 -0
- data/spec/rspec/matchers/{include_spec.rb → built_in/include_spec.rb} +118 -109
- data/spec/rspec/matchers/{match_spec.rb → built_in/match_spec.rb} +30 -2
- data/spec/rspec/matchers/{operator_matcher_spec.rb → built_in/operators_spec.rb} +26 -26
- data/spec/rspec/matchers/built_in/output_spec.rb +165 -0
- data/spec/rspec/matchers/{raise_error_spec.rb → built_in/raise_error_spec.rb} +81 -11
- data/spec/rspec/matchers/{respond_to_spec.rb → built_in/respond_to_spec.rb} +0 -0
- data/spec/rspec/matchers/{satisfy_spec.rb → built_in/satisfy_spec.rb} +0 -0
- data/spec/rspec/matchers/{start_with_end_with_spec.rb → built_in/start_and_end_with_spec.rb} +82 -15
- data/spec/rspec/matchers/{throw_symbol_spec.rb → built_in/throw_symbol_spec.rb} +29 -10
- data/spec/rspec/matchers/{yield_spec.rb → built_in/yield_spec.rb} +90 -0
- data/spec/rspec/matchers/configuration_spec.rb +7 -39
- data/spec/rspec/matchers/description_generation_spec.rb +22 -6
- data/spec/rspec/matchers/dsl_spec.rb +838 -0
- data/spec/rspec/matchers/legacy_spec.rb +101 -0
- data/spec/rspec/matchers_spec.rb +74 -0
- data/spec/spec_helper.rb +35 -21
- data/spec/support/shared_examples.rb +26 -4
- metadata +172 -116
- metadata.gz.sig +3 -4
- checksums.yaml +0 -15
- checksums.yaml.gz.sig +0 -0
- data/features/built_in_matchers/match_array.feature +0 -37
- data/lib/rspec/expectations/errors.rb +0 -9
- data/lib/rspec/expectations/extensions.rb +0 -1
- data/lib/rspec/expectations/extensions/object.rb +0 -29
- data/lib/rspec/matchers/built_in/match_array.rb +0 -51
- data/lib/rspec/matchers/compatibility.rb +0 -14
- data/lib/rspec/matchers/matcher.rb +0 -301
- data/lib/rspec/matchers/method_missing.rb +0 -12
- data/lib/rspec/matchers/operator_matcher.rb +0 -99
- data/lib/rspec/matchers/test_unit_integration.rb +0 -11
- data/spec/rspec/matchers/eq_spec.rb +0 -60
- data/spec/rspec/matchers/equal_spec.rb +0 -78
- data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
- data/spec/rspec/matchers/match_array_spec.rb +0 -194
- data/spec/rspec/matchers/matcher_spec.rb +0 -706
- data/spec/rspec/matchers/matchers_spec.rb +0 -36
- data/spec/rspec/matchers/method_missing_spec.rb +0 -28
- data/spec/support/classes.rb +0 -56
- data/spec/support/in_sub_process.rb +0 -37
- data/spec/support/ruby_version.rb +0 -10
data/lib/rspec/matchers/dsl.rb
CHANGED
@@ -16,6 +16,336 @@ module RSpec
|
|
16
16
|
if RSpec.respond_to?(:configure)
|
17
17
|
RSpec.configure {|c| c.extend self}
|
18
18
|
end
|
19
|
+
|
20
|
+
# Contains the methods that are available from within the
|
21
|
+
# `RSpec::Matchers.define` DSL for creating custom matchers.
|
22
|
+
module Macros
|
23
|
+
# Stores the block that is used to determine whether this matcher passes
|
24
|
+
# or fails. The block should return a boolean value. When the matcher is
|
25
|
+
# passed to `expect(...).to` and the block returns `true`, then the expectation
|
26
|
+
# passes. Similarly, when the matcher is passed to `expect(...).not_to` and the
|
27
|
+
# block returns `false`, then the expectation passes.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
#
|
31
|
+
# RSpec::Matchers.define :be_even do
|
32
|
+
# match do |actual|
|
33
|
+
# actual.even?
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# expect(4).to be_even # passes
|
38
|
+
# expect(3).not_to be_even # passes
|
39
|
+
# expect(3).to be_even # fails
|
40
|
+
# expect(4).not_to be_even # fails
|
41
|
+
#
|
42
|
+
# @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
|
43
|
+
def match(&match_block)
|
44
|
+
define_user_override(:matches?, match_block) do |actual|
|
45
|
+
begin
|
46
|
+
@actual = actual
|
47
|
+
super(*actual_arg_for(match_block))
|
48
|
+
rescue RSpec::Expectations::ExpectationNotMetError
|
49
|
+
false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Use this to define the block for a negative expectation (`expect(...).not_to`)
|
55
|
+
# when the positive and negative forms require different handling. This
|
56
|
+
# is rarely necessary, but can be helpful, for example, when specifying
|
57
|
+
# asynchronous processes that require different timeouts.
|
58
|
+
#
|
59
|
+
# @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
|
60
|
+
def match_when_negated(&match_block)
|
61
|
+
define_user_override(:does_not_match?, match_block) do |actual|
|
62
|
+
@actual = actual
|
63
|
+
super(*actual_arg_for(match_block))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Use this instead of `match` when the block will raise an exception
|
68
|
+
# rather than returning false to indicate a failure.
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
#
|
72
|
+
# RSpec::Matchers.define :accept_as_valid do |candidate_address|
|
73
|
+
# match_unless_raises ValidationException do |validator|
|
74
|
+
# validator.validate(candidate_address)
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# expect(email_validator).to accept_as_valid("person@company.com")
|
79
|
+
#
|
80
|
+
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
|
81
|
+
def match_unless_raises(expected_exception=Exception, &match_block)
|
82
|
+
define_user_override(:matches?, match_block) do |actual|
|
83
|
+
@actual = actual
|
84
|
+
begin
|
85
|
+
super(*actual_arg_for(match_block))
|
86
|
+
rescue expected_exception => @rescued_exception
|
87
|
+
false
|
88
|
+
else
|
89
|
+
true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Customizes the failure messsage to use when this matcher is
|
95
|
+
# asked to positively match. Only use this when the message
|
96
|
+
# generated by default doesn't suit your needs.
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
#
|
100
|
+
# RSpec::Matchers.define :have_strength do |expected|
|
101
|
+
# match { your_match_logic }
|
102
|
+
#
|
103
|
+
# failure_message do |actual|
|
104
|
+
# "Expected strength of #{expected}, but had #{actual.strength}"
|
105
|
+
# end
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
|
109
|
+
def failure_message(&definition)
|
110
|
+
define_user_override(__method__, definition)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Customize the failure messsage to use when this matcher is asked
|
114
|
+
# to negatively match. Only use this when the message generated by
|
115
|
+
# default doesn't suit your needs.
|
116
|
+
#
|
117
|
+
# @example
|
118
|
+
#
|
119
|
+
# RSpec::Matchers.define :have_strength do |expected|
|
120
|
+
# match { your_match_logic }
|
121
|
+
#
|
122
|
+
# failure_message_when_negated do |actual|
|
123
|
+
# "Expected not to have strength of #{expected}, but did"
|
124
|
+
# end
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
|
128
|
+
def failure_message_when_negated(&definition)
|
129
|
+
define_user_override(__method__, definition)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Customize the description to use for one-liners. Only use this when
|
133
|
+
# the description generated by default doesn't suit your needs.
|
134
|
+
#
|
135
|
+
# @example
|
136
|
+
#
|
137
|
+
# RSpec::Matchers.define :qualify_for do |expected|
|
138
|
+
# match { your_match_logic }
|
139
|
+
#
|
140
|
+
# description do
|
141
|
+
# "qualify for #{expected}"
|
142
|
+
# end
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
|
146
|
+
def description(&definition)
|
147
|
+
define_user_override(__method__, definition)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Tells the matcher to diff the actual and expected values in the failure
|
151
|
+
# message.
|
152
|
+
def diffable
|
153
|
+
define_method(:diffable?) { true }
|
154
|
+
end
|
155
|
+
|
156
|
+
# Convenience for defining methods on this matcher to create a fluent
|
157
|
+
# interface. The trick about fluent interfaces is that each method must
|
158
|
+
# return self in order to chain methods together. `chain` handles that
|
159
|
+
# for you.
|
160
|
+
#
|
161
|
+
# @example
|
162
|
+
#
|
163
|
+
# RSpec::Matchers.define :have_errors_on do |key|
|
164
|
+
# chain :with do |message|
|
165
|
+
# @message = message
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# match do |actual|
|
169
|
+
# actual.errors[key] == @message
|
170
|
+
# end
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
# expect(minor).to have_errors_on(:age).with("Not old enough to participate")
|
174
|
+
def chain(name, &definition)
|
175
|
+
define_user_override(name, definition) do |*args, &block|
|
176
|
+
super(*args, &block)
|
177
|
+
self
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
# Does the following:
|
184
|
+
#
|
185
|
+
# - Defines the named method usign a user-provided block
|
186
|
+
# in @user_method_defs, which is included as an ancestor
|
187
|
+
# in the singleton class in which we eval the `define` block.
|
188
|
+
# - Defines an overriden definition for the same method
|
189
|
+
# usign the provided `our_def` block.
|
190
|
+
# - Provides a default `our_def` block for the common case
|
191
|
+
# of needing to call the user's definition with `@actual`
|
192
|
+
# as an arg, but only if their block's arity can handle it.
|
193
|
+
#
|
194
|
+
# This compiles the user block into an actual method, allowing
|
195
|
+
# them to use normal method constructs like `return`
|
196
|
+
# (e.g. for a early guard statement), while allowing us to define
|
197
|
+
# an override that can provide the wrapped handling
|
198
|
+
# (e.g. assigning `@actual`, rescueing errors, etc) and
|
199
|
+
# can `super` to the user's definition.
|
200
|
+
def define_user_override(method_name, user_def, &our_def)
|
201
|
+
@user_method_defs.__send__(:define_method, method_name, &user_def)
|
202
|
+
our_def ||= lambda { super(*actual_arg_for(user_def)) }
|
203
|
+
define_method(method_name, &our_def)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Defines deprecated macro methods from RSpec 2 for backwards compatibility.
|
207
|
+
# @deprecated Use the methods from {Macros} instead.
|
208
|
+
module Deprecated
|
209
|
+
# @deprecated Use {Macros#match} instead.
|
210
|
+
def match_for_should(&definition)
|
211
|
+
RSpec.deprecate("`match_for_should`", :replacement => "`match`")
|
212
|
+
match(&definition)
|
213
|
+
end
|
214
|
+
|
215
|
+
# @deprecated Use {Macros#match_when_negated} instead.
|
216
|
+
def match_for_should_not(&definition)
|
217
|
+
RSpec.deprecate("`match_for_should_not`", :replacement => "`match_when_negated`")
|
218
|
+
match_when_negated(&definition)
|
219
|
+
end
|
220
|
+
|
221
|
+
# @deprecated Use {Macros#failure_message} instead.
|
222
|
+
def failure_message_for_should(&definition)
|
223
|
+
RSpec.deprecate("`failure_message_for_should`", :replacement => "`failure_message`")
|
224
|
+
failure_message(&definition)
|
225
|
+
end
|
226
|
+
|
227
|
+
# @deprecated Use {Macros#failure_message_when_negated} instead.
|
228
|
+
def failure_message_for_should_not(&definition)
|
229
|
+
RSpec.deprecate("`failure_message_for_should_not`", :replacement => "`failure_message_when_negated`")
|
230
|
+
failure_message_when_negated(&definition)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Defines default implementations of the matcher
|
236
|
+
# protocol methods for custom matchers. You can
|
237
|
+
# override any of these using the {RSpec::Matchers::DSL::Macros Macros} methods
|
238
|
+
# from within an `RSpec::Matchers.define` block.
|
239
|
+
module DefaultImplementations
|
240
|
+
# @api private
|
241
|
+
# Used internally by objects returns by `should` and `should_not`.
|
242
|
+
def diffable?
|
243
|
+
false
|
244
|
+
end
|
245
|
+
|
246
|
+
# The default description.
|
247
|
+
def description
|
248
|
+
"#{name_to_sentence}#{expected_to_sentence}"
|
249
|
+
end
|
250
|
+
|
251
|
+
# The default failure message for positive expectations.
|
252
|
+
def failure_message
|
253
|
+
"expected #{actual.inspect} to #{name_to_sentence}#{expected_to_sentence}"
|
254
|
+
end
|
255
|
+
|
256
|
+
# The default failure message for negative expectations.
|
257
|
+
def failure_message_when_negated
|
258
|
+
"expected #{actual.inspect} not to #{name_to_sentence}#{expected_to_sentence}"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# The class used for custom matchers. The block passed to
|
263
|
+
# `RSpec::Matchers.define` will be evaluated in the context
|
264
|
+
# of the singleton class of an instance, and will have the
|
265
|
+
# {RSpec::Matchers::DSL::Macros Macros} methods available.
|
266
|
+
class Matcher
|
267
|
+
# Provides default implementations for the matcher protocol methods.
|
268
|
+
include DefaultImplementations
|
269
|
+
|
270
|
+
# Allows expectation expressions to be used in the match block.
|
271
|
+
include RSpec::Matchers
|
272
|
+
|
273
|
+
# Converts matcher name and expected args to an English expresion.
|
274
|
+
include RSpec::Matchers::Pretty
|
275
|
+
|
276
|
+
# Supports the matcher composability features of RSpec 3+.
|
277
|
+
include Composable
|
278
|
+
|
279
|
+
# Makes the macro methods available to an `RSpec::Matchers.define` block.
|
280
|
+
extend Macros
|
281
|
+
extend Macros::Deprecated
|
282
|
+
|
283
|
+
attr_reader :expected_as_array, :actual, :rescued_exception
|
284
|
+
attr_accessor :matcher_execution_context
|
285
|
+
|
286
|
+
# @api private
|
287
|
+
def initialize(name, declarations, *expected)
|
288
|
+
@name = name
|
289
|
+
@actual = nil
|
290
|
+
@expected_as_array = expected
|
291
|
+
|
292
|
+
class << self
|
293
|
+
# See `Macros#define_user_override` above, for an explanation.
|
294
|
+
include(@user_method_defs = Module.new)
|
295
|
+
self
|
296
|
+
end.class_exec(*expected, &declarations)
|
297
|
+
end
|
298
|
+
|
299
|
+
def expected
|
300
|
+
if expected_as_array.size == 1
|
301
|
+
expected_as_array[0]
|
302
|
+
else
|
303
|
+
expected_as_array
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
# Adds the name (rather than a cryptic hex number)
|
308
|
+
# so we can identify an instance of
|
309
|
+
# the matcher in error messages (e.g. for `NoMethodError`)
|
310
|
+
def inspect
|
311
|
+
"#<#{self.class.name} #{name}>"
|
312
|
+
end
|
313
|
+
|
314
|
+
if RUBY_VERSION.to_f >= 1.9
|
315
|
+
# Indicates that this matcher responds to messages
|
316
|
+
# from the `matcher_execution_context` as well.
|
317
|
+
# Also, supports getting a method object for such methods.
|
318
|
+
def respond_to_missing?(method, include_private=false)
|
319
|
+
super || matcher_execution_context.respond_to?(method, include_private)
|
320
|
+
end
|
321
|
+
else # for 1.8.7
|
322
|
+
# Indicates that this matcher responds to messages
|
323
|
+
# from the `matcher_execution_context` as well.
|
324
|
+
def respond_to?(method, include_private=false)
|
325
|
+
super || matcher_execution_context.respond_to?(method, include_private)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
private
|
330
|
+
|
331
|
+
def actual_arg_for(block)
|
332
|
+
block.arity.zero? ? [] : [@actual]
|
333
|
+
end
|
334
|
+
|
335
|
+
# Takes care of forwarding unhandled messages to the
|
336
|
+
# `matcher_execution_context` (typically the current
|
337
|
+
# running `RSpec::Core::Example`). This is needed by
|
338
|
+
# rspec-rails so that it can define matchers that wrap
|
339
|
+
# Rails' test helper methods, but it's also a useful
|
340
|
+
# feature in its own right.
|
341
|
+
def method_missing(method, *args, &block)
|
342
|
+
if matcher_execution_context.respond_to?(method)
|
343
|
+
matcher_execution_context.__send__ method, *args, &block
|
344
|
+
else
|
345
|
+
super(method, *args, &block)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
19
349
|
end
|
20
350
|
end
|
21
351
|
end
|
@@ -1,17 +1,17 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Matchers
|
3
3
|
class << self
|
4
|
-
attr_accessor :last_matcher, :
|
4
|
+
attr_accessor :last_matcher, :last_expectation_handler
|
5
5
|
end
|
6
6
|
|
7
7
|
def self.clear_generated_description
|
8
8
|
self.last_matcher = nil
|
9
|
-
self.
|
9
|
+
self.last_expectation_handler = nil
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.generated_description
|
13
|
-
return nil if
|
14
|
-
"#{
|
13
|
+
return nil if last_expectation_handler.nil?
|
14
|
+
"#{last_expectation_handler.verb} #{last_description}"
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
@@ -20,11 +20,11 @@ module RSpec
|
|
20
20
|
last_matcher.respond_to?(:description) ? last_matcher.description : <<-MESSAGE
|
21
21
|
When you call a matcher in an example without a String, like this:
|
22
22
|
|
23
|
-
specify { object.
|
23
|
+
specify { expect(object).to matcher }
|
24
24
|
|
25
25
|
or this:
|
26
26
|
|
27
|
-
it {
|
27
|
+
it { is_expected.to matcher }
|
28
28
|
|
29
29
|
RSpec expects the matcher to have a #description method. You should either
|
30
30
|
add a String to the example this matcher is being used in, or give it a
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
# Provides the necessary plumbing to wrap a matcher with a decorator.
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
class MatcherDelegator
|
7
|
+
attr_reader :base_matcher
|
8
|
+
|
9
|
+
def initialize(base_matcher)
|
10
|
+
@base_matcher = base_matcher
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(*args, &block)
|
14
|
+
base_matcher.__send__(*args, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
if ::RUBY_VERSION.to_f > 1.8
|
18
|
+
def respond_to_missing?(name, include_all=false)
|
19
|
+
super || base_matcher.respond_to?(name, include_all)
|
20
|
+
end
|
21
|
+
else
|
22
|
+
def respond_to?(name, include_all=false)
|
23
|
+
super || base_matcher.respond_to?(name, include_all)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# So `===` is delegated via `method_missing`.
|
28
|
+
undef ===
|
29
|
+
undef ==
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -4,9 +4,10 @@ module RSpec
|
|
4
4
|
def split_words(sym)
|
5
5
|
sym.to_s.gsub(/_/,' ')
|
6
6
|
end
|
7
|
+
module_function :split_words
|
7
8
|
|
8
9
|
def to_sentence(words)
|
9
|
-
return "" unless words
|
10
|
+
return " #{words.inspect}" unless words
|
10
11
|
words = Array(words).map { |w| to_word(w) }
|
11
12
|
case words.length
|
12
13
|
when 0
|
@@ -43,7 +44,7 @@ module RSpec
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def expected_to_sentence
|
46
|
-
to_sentence(
|
47
|
+
to_sentence(expected) if defined?(expected)
|
47
48
|
end
|
48
49
|
|
49
50
|
def name
|
@@ -65,6 +66,16 @@ module RSpec
|
|
65
66
|
def is_matcher_with_description?(object)
|
66
67
|
RSpec::Matchers.is_a_matcher?(object) && object.respond_to?(:description)
|
67
68
|
end
|
69
|
+
|
70
|
+
# `{ :a => 5, :b => 2 }.inspect` produces:
|
71
|
+
# {:a=>5, :b=>2}
|
72
|
+
# ...but it looks much better as:
|
73
|
+
# {:a => 5, :b => 2}
|
74
|
+
#
|
75
|
+
# This is idempotent and safe to run on a string multiple times.
|
76
|
+
def improve_hash_formatting(inspect_string)
|
77
|
+
inspect_string.gsub(/(\S)=>(\S)/, '\1 => \2')
|
78
|
+
end
|
68
79
|
end
|
69
80
|
end
|
70
81
|
end
|
@@ -4,21 +4,19 @@ require 'ostruct'
|
|
4
4
|
|
5
5
|
module RSpec
|
6
6
|
module Expectations
|
7
|
-
describe
|
7
|
+
describe DiffPresenter do
|
8
|
+
let(:differ) { RSpec::Expectations::DiffPresenter.new }
|
8
9
|
context "without --color" do
|
9
10
|
|
10
|
-
before { RSpec::Matchers.configuration.
|
11
|
-
|
12
|
-
let(:differ) { RSpec::Expectations::Differ.new }
|
13
|
-
|
14
|
-
# color disabled context
|
11
|
+
before { allow(RSpec::Matchers.configuration).to receive_messages(:color? => false) }
|
15
12
|
|
16
13
|
describe '#diff_as_string' do
|
17
|
-
subject { differ.diff_as_string(@
|
14
|
+
subject { differ.diff_as_string(@actual, @expected) }
|
15
|
+
|
18
16
|
it "outputs unified diff of two strings" do
|
19
|
-
@expected="foo\nbar\
|
20
|
-
@actual="foo\nzap\
|
21
|
-
expect(subject).to
|
17
|
+
@expected = "foo\nzap\nbar\nthis\nis\nsoo\nvery\nvery\nequal\ninsert\na\nanother\nline\n"
|
18
|
+
@actual = "foo\nbar\nzap\nthis\nis\nsoo\nvery\nvery\nequal\ninsert\na\nline\n"
|
19
|
+
expect(subject).to eq(<<-'EOD')
|
22
20
|
|
23
21
|
|
24
22
|
@@ -1,6 +1,6 @@
|
@@ -38,10 +36,11 @@ module RSpec
|
|
38
36
|
EOD
|
39
37
|
|
40
38
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@
|
39
|
+
|
40
|
+
if String.method_defined?(:encoding)
|
41
|
+
it 'copes with encoded strings' do
|
42
|
+
@expected = "Tu avec carte {count} item has".encode('UTF-16LE')
|
43
|
+
@actual = "Tu avec carté {count} itém has".encode('UTF-16LE')
|
45
44
|
expect(subject).to eql(<<-EOD.encode('UTF-16LE'))
|
46
45
|
|
47
46
|
@@ -1,2 +1,2 @@
|
@@ -49,20 +48,27 @@ EOD
|
|
49
48
|
+Tu avec carté {count} itém has
|
50
49
|
EOD
|
51
50
|
end
|
52
|
-
|
53
|
-
@expected="Tu avec carté {count} itém has".encode('UTF-16LE')
|
54
|
-
@actual="Tu avec carte {count} item has".encode('UTF-16LE')
|
55
|
-
expect(subject).to eql 'Could not produce a diff because of the encoding of the string (UTF-16LE)'
|
56
|
-
end
|
51
|
+
|
57
52
|
it 'handles differently encoded strings that are compatible' do
|
58
|
-
@expected = "
|
59
|
-
@actual = "
|
53
|
+
@expected = "abc".encode('us-ascii')
|
54
|
+
@actual = "강인철".encode('UTF-8')
|
60
55
|
expect(subject).to eql "\n@@ -1,2 +1,2 @@\n-abc\n+강인철\n"
|
61
56
|
end
|
62
|
-
|
63
|
-
|
64
|
-
@
|
65
|
-
|
57
|
+
|
58
|
+
it 'uses the default external encoding when the two strings have incompatible encodings' do
|
59
|
+
@expected = "Tu avec carte {count} item has"
|
60
|
+
@actual = "Tu avec carté {count} itém has".encode('UTF-16LE')
|
61
|
+
expect(subject).to eq("\n@@ -1,2 +1,2 @@\n-Tu avec carte {count} item has\n+Tu avec carté {count} itém has\n")
|
62
|
+
expect(subject.encoding).to eq(Encoding.default_external)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'handles any encoding error that occurs with a helpful error message' do
|
66
|
+
expect(Differ).to receive(:new).and_raise(Encoding::CompatibilityError)
|
67
|
+
@expected = "Tu avec carte {count} item has".encode('us-ascii')
|
68
|
+
@actual = "Tu avec carté {count} itém has"
|
69
|
+
expect(subject).to match(/Could not produce a diff/)
|
70
|
+
expect(subject).to match(/actual string \(UTF-8\)/)
|
71
|
+
expect(subject).to match(/expected string \(US-ASCII\)/)
|
66
72
|
end
|
67
73
|
end
|
68
74
|
end
|
@@ -135,7 +141,7 @@ EOD
|
|
135
141
|
+:delta => "charlie",
|
136
142
|
:foo => "bar",
|
137
143
|
:metasyntactic => "variable",
|
138
|
-
:width => "quite wide"
|
144
|
+
:width => "quite wide",
|
139
145
|
EOD
|
140
146
|
|
141
147
|
diff = differ.diff_as_object(expected,actual)
|
@@ -145,8 +151,8 @@ EOD
|
|
145
151
|
it 'outputs unified diff message of two hashes with differing encoding' do
|
146
152
|
expected_diff = %Q{
|
147
153
|
@@ -1,2 +1,2 @@
|
148
|
-
-"a" => "a"
|
149
|
-
#{ (RUBY_VERSION.to_f > 1.8) ? %Q{+"ö" => "ö"} : '+"\303\266" => "\303\266"' }
|
154
|
+
-"a" => "a",
|
155
|
+
#{ (RUBY_VERSION.to_f > 1.8) ? %Q{+"ö" => "ö"} : '+"\303\266" => "\303\266"' },
|
150
156
|
}
|
151
157
|
|
152
158
|
diff = differ.diff_as_object({'ö' => 'ö'}, {'a' => 'a'})
|
@@ -156,8 +162,8 @@ EOD
|
|
156
162
|
it 'outputs unified diff message of two hashes with encoding different to key encoding' do
|
157
163
|
expected_diff = %Q{
|
158
164
|
@@ -1,2 +1,2 @@
|
159
|
-
-:a => "a"
|
160
|
-
#{ (RUBY_VERSION.to_f > 1.8) ? %Q{+\"한글\" => \"한글2\"} : '+"\355\225\234\352\270\200" => "\355\225\234\352\270\2002"' }
|
165
|
+
-:a => "a",
|
166
|
+
#{ (RUBY_VERSION.to_f > 1.8) ? %Q{+\"한글\" => \"한글2\"} : '+"\355\225\234\352\270\200" => "\355\225\234\352\270\2002"' },
|
161
167
|
}
|
162
168
|
|
163
169
|
diff = differ.diff_as_object({ "한글" => "한글2"}, { :a => "a"})
|
@@ -167,8 +173,8 @@ EOD
|
|
167
173
|
it "outputs unified diff message of two hashes with object keys" do
|
168
174
|
expected_diff = %Q{
|
169
175
|
@@ -1,2 +1,2 @@
|
170
|
-
-["a", "c"] => "b"
|
171
|
-
+["d", "c"] => "b"
|
176
|
+
-["a", "c"] => "b",
|
177
|
+
+["d", "c"] => "b",
|
172
178
|
}
|
173
179
|
|
174
180
|
diff = differ.diff_as_object({ ['d','c'] => 'b'}, { ['a','c'] => 'b' })
|
@@ -205,18 +211,32 @@ EOD
|
|
205
211
|
diff = differ.diff_as_object(expected,actual)
|
206
212
|
expect(diff).to eq expected_diff
|
207
213
|
end
|
214
|
+
|
215
|
+
it "uses matcher descriptions in place of matchers in diffs" do
|
216
|
+
expected = [a_string_matching(/foo/), a_string_matching(/bar/)]
|
217
|
+
actual = ["poo", "car"]
|
218
|
+
|
219
|
+
expected_diff = dedent(<<-EOS)
|
220
|
+
|
|
221
|
+
|@@ -1,2 +1,2 @@
|
222
|
+
|-["poo", "car"]
|
223
|
+
|+[(a string matching /foo/), (a string matching /bar/)]
|
224
|
+
|
|
225
|
+
EOS
|
226
|
+
|
227
|
+
diff = differ.diff_as_object(expected,actual)
|
228
|
+
expect(diff).to eq expected_diff
|
229
|
+
end
|
208
230
|
end
|
209
231
|
end
|
210
232
|
|
211
233
|
context "with --color" do
|
212
|
-
before { RSpec::Matchers.configuration.
|
213
|
-
|
214
|
-
let(:differ) { RSpec::Expectations::Differ.new }
|
234
|
+
before { allow(RSpec::Matchers.configuration).to receive_messages(:color? => true) }
|
215
235
|
|
216
236
|
it "outputs colored diffs" do
|
217
237
|
expected = "foo bar baz"
|
218
238
|
actual = "foo bang baz"
|
219
|
-
expected_diff = "\n\e[34m@@ -1,2 +1,2 @@\n\e[0m\e[31m-foo bang baz\n\e[0m\e[32m+foo bar baz\n\e[0m"
|
239
|
+
expected_diff = "\e[0m\n\e[0m\e[34m@@ -1,2 +1,2 @@\n\e[0m\e[31m-foo bang baz\n\e[0m\e[32m+foo bar baz\n\e[0m"
|
220
240
|
|
221
241
|
|
222
242
|
diff = differ.diff_as_string(expected,actual)
|