rspec-expectations 3.0.0.beta1 → 3.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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)