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.
Files changed (122) hide show
  1. data.tar.gz.sig +2 -2
  2. data/.yardopts +1 -0
  3. data/Changelog.md +138 -0
  4. data/README.md +75 -8
  5. data/features/README.md +2 -2
  6. data/features/built_in_matchers/README.md +12 -9
  7. data/features/built_in_matchers/comparisons.feature +2 -2
  8. data/features/built_in_matchers/contain_exactly.feature +46 -0
  9. data/features/built_in_matchers/expect_change.feature +2 -2
  10. data/features/built_in_matchers/include.feature +0 -48
  11. data/features/built_in_matchers/output.feature +70 -0
  12. data/features/composing_matchers.feature +250 -0
  13. data/features/compound_expectations.feature +45 -0
  14. data/features/custom_matchers/access_running_example.feature +1 -1
  15. data/features/custom_matchers/define_matcher.feature +6 -6
  16. data/features/custom_matchers/define_matcher_outside_rspec.feature +4 -8
  17. data/features/test_frameworks/{test_unit.feature → minitest.feature} +11 -11
  18. data/lib/rspec/expectations.rb +31 -42
  19. data/lib/rspec/expectations/diff_presenter.rb +141 -0
  20. data/lib/rspec/expectations/differ.rb +22 -132
  21. data/lib/rspec/expectations/encoded_string.rb +56 -0
  22. data/lib/rspec/expectations/expectation_target.rb +0 -30
  23. data/lib/rspec/expectations/fail_with.rb +2 -2
  24. data/lib/rspec/expectations/handler.rb +128 -31
  25. data/lib/rspec/expectations/minitest_integration.rb +16 -0
  26. data/lib/rspec/expectations/syntax.rb +4 -58
  27. data/lib/rspec/expectations/version.rb +1 -1
  28. data/lib/rspec/matchers.rb +298 -60
  29. data/lib/rspec/matchers/aliased_matcher.rb +35 -0
  30. data/lib/rspec/matchers/built_in.rb +37 -33
  31. data/lib/rspec/matchers/built_in/base_matcher.rb +25 -15
  32. data/lib/rspec/matchers/built_in/be.rb +23 -31
  33. data/lib/rspec/matchers/built_in/be_between.rb +55 -0
  34. data/lib/rspec/matchers/built_in/be_within.rb +15 -11
  35. data/lib/rspec/matchers/built_in/change.rb +198 -81
  36. data/lib/rspec/matchers/built_in/compound.rb +106 -0
  37. data/lib/rspec/matchers/built_in/contain_exactly.rb +245 -0
  38. data/lib/rspec/matchers/built_in/eq.rb +43 -4
  39. data/lib/rspec/matchers/built_in/eql.rb +2 -2
  40. data/lib/rspec/matchers/built_in/equal.rb +35 -18
  41. data/lib/rspec/matchers/built_in/has.rb +16 -15
  42. data/lib/rspec/matchers/built_in/include.rb +45 -23
  43. data/lib/rspec/matchers/built_in/match.rb +6 -3
  44. data/lib/rspec/matchers/built_in/operators.rb +103 -0
  45. data/lib/rspec/matchers/built_in/output.rb +108 -0
  46. data/lib/rspec/matchers/built_in/raise_error.rb +9 -15
  47. data/lib/rspec/matchers/built_in/respond_to.rb +5 -4
  48. data/lib/rspec/matchers/built_in/satisfy.rb +4 -3
  49. data/lib/rspec/matchers/built_in/start_and_end_with.rb +37 -16
  50. data/lib/rspec/matchers/built_in/throw_symbol.rb +6 -5
  51. data/lib/rspec/matchers/built_in/yield.rb +31 -29
  52. data/lib/rspec/matchers/composable.rb +138 -0
  53. data/lib/rspec/matchers/dsl.rb +330 -0
  54. data/lib/rspec/matchers/generated_descriptions.rb +6 -6
  55. data/lib/rspec/matchers/matcher_delegator.rb +33 -0
  56. data/lib/rspec/matchers/pretty.rb +13 -2
  57. data/spec/rspec/expectations/{differ_spec.rb → diff_presenter_spec.rb} +56 -36
  58. data/spec/rspec/expectations/encoded_string_spec.rb +74 -0
  59. data/spec/rspec/expectations/extensions/kernel_spec.rb +11 -11
  60. data/spec/rspec/expectations/fail_with_spec.rb +8 -8
  61. data/spec/rspec/expectations/handler_spec.rb +27 -49
  62. data/spec/rspec/expectations/minitest_integration_spec.rb +27 -0
  63. data/spec/rspec/expectations/syntax_spec.rb +17 -67
  64. data/spec/rspec/expectations_spec.rb +7 -52
  65. data/spec/rspec/matchers/aliased_matcher_spec.rb +48 -0
  66. data/spec/rspec/matchers/aliases_spec.rb +449 -0
  67. data/spec/rspec/matchers/{base_matcher_spec.rb → built_in/base_matcher_spec.rb} +24 -3
  68. data/spec/rspec/matchers/built_in/be_between_spec.rb +159 -0
  69. data/spec/rspec/matchers/{be_instance_of_spec.rb → built_in/be_instance_of_spec.rb} +0 -0
  70. data/spec/rspec/matchers/{be_kind_of_spec.rb → built_in/be_kind_of_spec.rb} +0 -0
  71. data/spec/rspec/matchers/{be_spec.rb → built_in/be_spec.rb} +76 -32
  72. data/spec/rspec/matchers/{be_within_spec.rb → built_in/be_within_spec.rb} +6 -2
  73. data/spec/rspec/matchers/{change_spec.rb → built_in/change_spec.rb} +310 -69
  74. data/spec/rspec/matchers/built_in/compound_spec.rb +292 -0
  75. data/spec/rspec/matchers/built_in/contain_exactly_spec.rb +441 -0
  76. data/spec/rspec/matchers/{cover_spec.rb → built_in/cover_spec.rb} +0 -0
  77. data/spec/rspec/matchers/built_in/eq_spec.rb +156 -0
  78. data/spec/rspec/matchers/{eql_spec.rb → built_in/eql_spec.rb} +2 -2
  79. data/spec/rspec/matchers/built_in/equal_spec.rb +106 -0
  80. data/spec/rspec/matchers/{exist_spec.rb → built_in/exist_spec.rb} +1 -1
  81. data/spec/rspec/matchers/{has_spec.rb → built_in/has_spec.rb} +39 -0
  82. data/spec/rspec/matchers/{include_spec.rb → built_in/include_spec.rb} +118 -109
  83. data/spec/rspec/matchers/{match_spec.rb → built_in/match_spec.rb} +30 -2
  84. data/spec/rspec/matchers/{operator_matcher_spec.rb → built_in/operators_spec.rb} +26 -26
  85. data/spec/rspec/matchers/built_in/output_spec.rb +165 -0
  86. data/spec/rspec/matchers/{raise_error_spec.rb → built_in/raise_error_spec.rb} +81 -11
  87. data/spec/rspec/matchers/{respond_to_spec.rb → built_in/respond_to_spec.rb} +0 -0
  88. data/spec/rspec/matchers/{satisfy_spec.rb → built_in/satisfy_spec.rb} +0 -0
  89. data/spec/rspec/matchers/{start_with_end_with_spec.rb → built_in/start_and_end_with_spec.rb} +82 -15
  90. data/spec/rspec/matchers/{throw_symbol_spec.rb → built_in/throw_symbol_spec.rb} +29 -10
  91. data/spec/rspec/matchers/{yield_spec.rb → built_in/yield_spec.rb} +90 -0
  92. data/spec/rspec/matchers/configuration_spec.rb +7 -39
  93. data/spec/rspec/matchers/description_generation_spec.rb +22 -6
  94. data/spec/rspec/matchers/dsl_spec.rb +838 -0
  95. data/spec/rspec/matchers/legacy_spec.rb +101 -0
  96. data/spec/rspec/matchers_spec.rb +74 -0
  97. data/spec/spec_helper.rb +35 -21
  98. data/spec/support/shared_examples.rb +26 -4
  99. metadata +172 -116
  100. metadata.gz.sig +3 -4
  101. checksums.yaml +0 -15
  102. checksums.yaml.gz.sig +0 -0
  103. data/features/built_in_matchers/match_array.feature +0 -37
  104. data/lib/rspec/expectations/errors.rb +0 -9
  105. data/lib/rspec/expectations/extensions.rb +0 -1
  106. data/lib/rspec/expectations/extensions/object.rb +0 -29
  107. data/lib/rspec/matchers/built_in/match_array.rb +0 -51
  108. data/lib/rspec/matchers/compatibility.rb +0 -14
  109. data/lib/rspec/matchers/matcher.rb +0 -301
  110. data/lib/rspec/matchers/method_missing.rb +0 -12
  111. data/lib/rspec/matchers/operator_matcher.rb +0 -99
  112. data/lib/rspec/matchers/test_unit_integration.rb +0 -11
  113. data/spec/rspec/matchers/eq_spec.rb +0 -60
  114. data/spec/rspec/matchers/equal_spec.rb +0 -78
  115. data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
  116. data/spec/rspec/matchers/match_array_spec.rb +0 -194
  117. data/spec/rspec/matchers/matcher_spec.rb +0 -706
  118. data/spec/rspec/matchers/matchers_spec.rb +0 -36
  119. data/spec/rspec/matchers/method_missing_spec.rb +0 -28
  120. data/spec/support/classes.rb +0 -56
  121. data/spec/support/in_sub_process.rb +0 -37
  122. data/spec/support/ruby_version.rb +0 -10
@@ -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, :last_should
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.last_should = nil
9
+ self.last_expectation_handler = nil
10
10
  end
11
11
 
12
12
  def self.generated_description
13
- return nil if last_should.nil?
14
- "#{last_should.to_s.gsub('_',' ')} #{last_description}"
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.should matcher }
23
+ specify { expect(object).to matcher }
24
24
 
25
25
  or this:
26
26
 
27
- it { should matcher }
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(@expected) if defined?(@expected)
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 Differ do
7
+ describe DiffPresenter do
8
+ let(:differ) { RSpec::Expectations::DiffPresenter.new }
8
9
  context "without --color" do
9
10
 
10
- before { RSpec::Matchers.configuration.stub(:color? => false) }
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(@expected, @actual) }
14
+ subject { differ.diff_as_string(@actual, @expected) }
15
+
18
16
  it "outputs unified diff of two strings" do
19
- @expected="foo\nbar\nzap\nthis\nis\nsoo\nvery\nvery\nequal\ninsert\na\nline\n"
20
- @actual="foo\nzap\nbar\nthis\nis\nsoo\nvery\nvery\nequal\ninsert\na\nanother\nline\n"
21
- expect(subject).to eql(<<-'EOD')
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
- if RUBY_VERSION.to_f > 1.9
42
- it 'copes with encoded strings', :pending => (Diff::LCS::VERSION < '1.2.2') do
43
- @expected="Tu avec carté {count} itém has".encode('UTF-16LE')
44
- @actual="Tu avec carte {count} item has".encode('UTF-16LE')
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
- it 'copes with encoded strings', :pending => (Diff::LCS::VERSION >= '1.2.2') do
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 = "강인철".encode('UTF-8')
59
- @actual = "abc".encode('us-ascii')
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
- it 'outputs a message when encountering differently encoded strings' do
63
- @expected="Tu avec carté {count} itém has".encode('UTF-16LE')
64
- @actual="Tu avec carte {count} item has"
65
- expect(subject).to eql 'Could not produce a diff because the encoding of the actual string (UTF-8) differs from the encoding of the expected string (UTF-16LE)'
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.stub(:color? => true) }
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)