rspec-expectations 3.2.1 → 3.3.0

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 (43) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Changelog.md +55 -4
  5. data/README.md +1 -1
  6. data/lib/rspec/expectations.rb +13 -1
  7. data/lib/rspec/expectations/configuration.rb +17 -0
  8. data/lib/rspec/expectations/expectation_target.rb +3 -9
  9. data/lib/rspec/expectations/fail_with.rb +1 -3
  10. data/lib/rspec/expectations/failure_aggregator.rb +194 -0
  11. data/lib/rspec/expectations/minitest_integration.rb +13 -0
  12. data/lib/rspec/expectations/version.rb +1 -1
  13. data/lib/rspec/matchers.rb +59 -5
  14. data/lib/rspec/matchers/built_in/base_matcher.rb +56 -7
  15. data/lib/rspec/matchers/built_in/be.rb +25 -15
  16. data/lib/rspec/matchers/built_in/be_between.rb +1 -1
  17. data/lib/rspec/matchers/built_in/be_within.rb +2 -2
  18. data/lib/rspec/matchers/built_in/contain_exactly.rb +12 -8
  19. data/lib/rspec/matchers/built_in/eq.rb +3 -38
  20. data/lib/rspec/matchers/built_in/eql.rb +2 -2
  21. data/lib/rspec/matchers/built_in/equal.rb +3 -3
  22. data/lib/rspec/matchers/built_in/exist.rb +2 -2
  23. data/lib/rspec/matchers/built_in/has.rb +3 -1
  24. data/lib/rspec/matchers/built_in/have_attributes.rb +5 -4
  25. data/lib/rspec/matchers/built_in/include.rb +44 -19
  26. data/lib/rspec/matchers/built_in/match.rb +9 -1
  27. data/lib/rspec/matchers/built_in/operators.rb +14 -5
  28. data/lib/rspec/matchers/built_in/output.rb +9 -2
  29. data/lib/rspec/matchers/built_in/raise_error.rb +64 -22
  30. data/lib/rspec/matchers/built_in/respond_to.rb +2 -3
  31. data/lib/rspec/matchers/built_in/satisfy.rb +7 -9
  32. data/lib/rspec/matchers/built_in/start_or_end_with.rb +3 -1
  33. data/lib/rspec/matchers/built_in/throw_symbol.rb +1 -1
  34. data/lib/rspec/matchers/built_in/yield.rb +7 -5
  35. data/lib/rspec/matchers/composable.rb +5 -4
  36. data/lib/rspec/matchers/dsl.rb +19 -6
  37. data/lib/rspec/matchers/english_phrasing.rb +42 -0
  38. data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +2 -8
  39. data/lib/rspec/matchers/fail_matchers.rb +42 -0
  40. data/lib/rspec/matchers/matcher_delegator.rb +2 -0
  41. metadata +9 -7
  42. metadata.gz.sig +0 -0
  43. data/lib/rspec/matchers/pretty.rb +0 -77
@@ -12,7 +12,6 @@ module RSpec
12
12
  # strongly recommend that you do not base your custom matchers on this
13
13
  # class. If/when this changes, we will announce it and remove this warning.
14
14
  class BaseMatcher
15
- include RSpec::Matchers::Pretty
16
15
  include RSpec::Matchers::Composable
17
16
 
18
17
  # @api private
@@ -53,11 +52,12 @@ module RSpec
53
52
  end
54
53
 
55
54
  # @api private
56
- # Generates a "pretty" description using the logic in {Pretty}.
55
+ # Generates a description using {EnglishPhrasing}.
57
56
  # @return [String]
58
57
  def description
59
- return name_to_sentence unless defined?(@expected)
60
- "#{name_to_sentence}#{to_sentence @expected}"
58
+ desc = EnglishPhrasing.split_words(self.class.matcher_name)
59
+ desc << EnglishPhrasing.list(@expected) if defined?(@expected)
60
+ desc
61
61
  end
62
62
 
63
63
  # @api private
@@ -80,21 +80,70 @@ module RSpec
80
80
  false
81
81
  end
82
82
 
83
+ # @private
84
+ def expected_formatted
85
+ RSpec::Support::ObjectFormatter.format(@expected)
86
+ end
87
+
88
+ # @private
89
+ def actual_formatted
90
+ RSpec::Support::ObjectFormatter.format(@actual)
91
+ end
92
+
93
+ # @private
94
+ def self.matcher_name
95
+ @matcher_name ||= underscore(name.split("::").last)
96
+ end
97
+
98
+ # @private
99
+ # Borrowed from ActiveSupport.
100
+ def self.underscore(camel_cased_word)
101
+ word = camel_cased_word.to_s.dup
102
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
103
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
104
+ word.tr!("-", "_")
105
+ word.downcase!
106
+ word
107
+ end
108
+ private_class_method :underscore
109
+
83
110
  private
84
111
 
85
112
  def assert_ivars(*expected_ivars)
86
113
  return unless (expected_ivars - present_ivars).any?
87
- raise "#{self.class.name} needs to supply#{to_sentence expected_ivars}"
114
+ ivar_list = EnglishPhrasing.list(expected_ivars)
115
+ raise "#{self.class.name} needs to supply#{ivar_list}"
88
116
  end
89
117
 
90
118
  if RUBY_VERSION.to_f < 1.9
119
+ # :nocov:
91
120
  def present_ivars
92
121
  instance_variables.map { |v| v.to_sym }
93
122
  end
123
+ # :nocov:
94
124
  else
95
125
  alias present_ivars instance_variables
96
126
  end
97
127
 
128
+ # @private
129
+ module HashFormatting
130
+ # `{ :a => 5, :b => 2 }.inspect` produces:
131
+ #
132
+ # {:a=>5, :b=>2}
133
+ #
134
+ # ...but it looks much better as:
135
+ #
136
+ # {:a => 5, :b => 2}
137
+ #
138
+ # This is idempotent and safe to run on a string multiple times.
139
+ def improve_hash_formatting(inspect_string)
140
+ inspect_string.gsub(/(\S)=>(\S)/, '\1 => \2')
141
+ end
142
+ module_function :improve_hash_formatting
143
+ end
144
+
145
+ include HashFormatting
146
+
98
147
  # @api private
99
148
  # Provides default implementations of failure messages, based on the `description`.
100
149
  module DefaultFailureMessages
@@ -104,7 +153,7 @@ module RSpec
104
153
  # you often only need to override `description`.
105
154
  # @return [String]
106
155
  def failure_message
107
- "expected #{actual.inspect} to #{description}"
156
+ "expected #{description_of @actual} to #{description}"
108
157
  end
109
158
 
110
159
  # @api private
@@ -113,7 +162,7 @@ module RSpec
113
162
  # you often only need to override `description`.
114
163
  # @return [String]
115
164
  def failure_message_when_negated
116
- "expected #{actual.inspect} not to #{description}"
165
+ "expected #{description_of @actual} not to #{description}"
117
166
  end
118
167
 
119
168
  # @private
@@ -8,13 +8,13 @@ module RSpec
8
8
  # @api private
9
9
  # @return [String]
10
10
  def failure_message
11
- "expected: truthy value\n got: #{actual.inspect}"
11
+ "expected: truthy value\n got: #{actual_formatted}"
12
12
  end
13
13
 
14
14
  # @api private
15
15
  # @return [String]
16
16
  def failure_message_when_negated
17
- "expected: falsey value\n got: #{actual.inspect}"
17
+ "expected: falsey value\n got: #{actual_formatted}"
18
18
  end
19
19
 
20
20
  private
@@ -31,13 +31,13 @@ module RSpec
31
31
  # @api private
32
32
  # @return [String]
33
33
  def failure_message
34
- "expected: falsey value\n got: #{actual.inspect}"
34
+ "expected: falsey value\n got: #{actual_formatted}"
35
35
  end
36
36
 
37
37
  # @api private
38
38
  # @return [String]
39
39
  def failure_message_when_negated
40
- "expected: truthy value\n got: #{actual.inspect}"
40
+ "expected: truthy value\n got: #{actual_formatted}"
41
41
  end
42
42
 
43
43
  private
@@ -54,7 +54,7 @@ module RSpec
54
54
  # @api private
55
55
  # @return [String]
56
56
  def failure_message
57
- "expected: nil\n got: #{actual.inspect}"
57
+ "expected: nil\n got: #{actual_formatted}"
58
58
  end
59
59
 
60
60
  # @api private
@@ -83,15 +83,15 @@ module RSpec
83
83
  end
84
84
 
85
85
  def inspected_args
86
- @args.map { |a| a.inspect }
86
+ @args.map { |a| RSpec::Support::ObjectFormatter.format(a) }
87
87
  end
88
88
 
89
89
  def expected_to_sentence
90
- split_words(@expected)
90
+ EnglishPhrasing.split_words(@expected)
91
91
  end
92
92
 
93
93
  def args_to_sentence
94
- to_sentence(@args)
94
+ EnglishPhrasing.list(@args)
95
95
  end
96
96
  end
97
97
 
@@ -108,13 +108,13 @@ module RSpec
108
108
  # @api private
109
109
  # @return [String]
110
110
  def failure_message
111
- "expected #{@actual.inspect} to evaluate to true"
111
+ "expected #{actual_formatted} to evaluate to true"
112
112
  end
113
113
 
114
114
  # @api private
115
115
  # @return [String]
116
116
  def failure_message_when_negated
117
- "expected #{@actual.inspect} to evaluate to false"
117
+ "expected #{actual_formatted} to evaluate to false"
118
118
  end
119
119
 
120
120
  [:==, :<, :<=, :>=, :>, :===, :=~].each do |operator|
@@ -149,13 +149,13 @@ module RSpec
149
149
  # @api private
150
150
  # @return [String]
151
151
  def failure_message
152
- "expected: #{@operator} #{@expected.inspect}\n got: #{@operator.to_s.gsub(/./, ' ')} #{@actual.inspect}"
152
+ "expected: #{@operator} #{expected_formatted}\n got: #{@operator.to_s.gsub(/./, ' ')} #{actual_formatted}"
153
153
  end
154
154
 
155
155
  # @api private
156
156
  # @return [String]
157
157
  def failure_message_when_negated
158
- message = "`expect(#{@actual.inspect}).not_to be #{@operator} #{@expected.inspect}`"
158
+ message = "`expect(#{actual_formatted}).not_to be #{@operator} #{expected_formatted}`"
159
159
  if [:<, :>, :<=, :>=].include?(@operator)
160
160
  message + " not only FAILED, it is a bit confusing."
161
161
  else
@@ -220,9 +220,11 @@ module RSpec
220
220
 
221
221
  # support 1.8.7, evaluate once at load time for performance
222
222
  if String === methods.first
223
+ # :nocov:
223
224
  def private_predicate?
224
225
  @actual.private_methods.include? predicate.to_s
225
226
  end
227
+ # :nocov:
226
228
  else
227
229
  def private_predicate?
228
230
  @actual.private_methods.include? predicate
@@ -252,19 +254,27 @@ module RSpec
252
254
  end
253
255
 
254
256
  def prefix_to_sentence
255
- split_words(@prefix)
257
+ EnglishPhrasing.split_words(@prefix)
256
258
  end
257
259
 
258
260
  def failure_message_expecting(value)
259
261
  validity_message ||
260
- "expected `#{@actual.inspect}.#{predicate}#{args_to_s}` to return #{value}, got #{@predicate_matches.inspect}"
262
+ "expected `#{actual_formatted}.#{predicate}#{args_to_s}` to return #{value}, got #{description_of @predicate_matches}"
261
263
  end
262
264
 
263
265
  def validity_message
264
266
  return nil if predicate_accessible?
265
267
 
266
268
  msg = "expected #{@actual} to respond to `#{predicate}`"
267
- msg << " but `#{predicate}` is a private method" if private_predicate?
269
+
270
+ if private_predicate?
271
+ msg << " but `#{predicate}` is a private method"
272
+ elsif predicate == :true?
273
+ msg << " or perhaps you meant `be true` or `be_truthy`"
274
+ elsif predicate == :false?
275
+ msg << " or perhaps you meant `be false` or `be_falsey`"
276
+ end
277
+
268
278
  msg
269
279
  end
270
280
  end
@@ -55,7 +55,7 @@ module RSpec
55
55
  # @api private
56
56
  # @return [String]
57
57
  def description
58
- "be between #{@min.inspect} and #{@max.inspect} (#{@mode})"
58
+ "be between #{description_of @min} and #{description_of @max} (#{@mode})"
59
59
  end
60
60
 
61
61
  private
@@ -38,13 +38,13 @@ module RSpec
38
38
  # @api private
39
39
  # @return [String]
40
40
  def failure_message
41
- "expected #{@actual.inspect} to #{description}#{not_numeric_clause}"
41
+ "expected #{actual_formatted} to #{description}#{not_numeric_clause}"
42
42
  end
43
43
 
44
44
  # @api private
45
45
  # @return [String]
46
46
  def failure_message_when_negated
47
- "expected #{@actual.inspect} not to #{description}"
47
+ "expected #{actual_formatted} not to #{description}"
48
48
  end
49
49
 
50
50
  # @api private
@@ -9,27 +9,29 @@ module RSpec
9
9
  # @return [String]
10
10
  def failure_message
11
11
  if Array === actual
12
- message = "expected collection contained: #{safe_sort(surface_descriptions_in expected).inspect}\n"
13
- message += "actual collection contained: #{safe_sort(actual).inspect}\n"
14
- message += "the missing elements were: #{safe_sort(surface_descriptions_in missing_items).inspect}\n" unless missing_items.empty?
15
- message += "the extra elements were: #{safe_sort(extra_items).inspect}\n" unless extra_items.empty?
12
+ message = "expected collection contained: #{description_of(safe_sort(surface_descriptions_in expected))}\n"
13
+ message += "actual collection contained: #{description_of(safe_sort(actual))}\n"
14
+ message += "the missing elements were: #{description_of(safe_sort(surface_descriptions_in missing_items))}\n" unless missing_items.empty?
15
+ message += "the extra elements were: #{description_of(safe_sort(extra_items))}\n" unless extra_items.empty?
16
16
  message
17
17
  else
18
18
  "expected a collection that can be converted to an array with " \
19
- "`#to_ary` or `#to_a`, but got #{actual.inspect}"
19
+ "`#to_ary` or `#to_a`, but got #{actual_formatted}"
20
20
  end
21
21
  end
22
22
 
23
23
  # @api private
24
24
  # @return [String]
25
25
  def failure_message_when_negated
26
- "expected #{actual.inspect} not to contain exactly#{to_sentence(surface_descriptions_in expected)}"
26
+ list = EnglishPhrasing.list(surface_descriptions_in(expected))
27
+ "expected #{actual_formatted} not to contain exactly#{list}"
27
28
  end
28
29
 
29
30
  # @api private
30
31
  # @return [String]
31
32
  def description
32
- "contain exactly#{to_sentence(surface_descriptions_in expected)}"
33
+ list = EnglishPhrasing.list(surface_descriptions_in(expected))
34
+ "contain exactly#{list}"
33
35
  end
34
36
 
35
37
  private
@@ -58,7 +60,9 @@ module RSpec
58
60
  end
59
61
 
60
62
  def safe_sort(array)
61
- array.sort rescue array
63
+ array.sort
64
+ rescue Exception
65
+ array
62
66
  end
63
67
 
64
68
  def missing_items
@@ -8,19 +8,19 @@ module RSpec
8
8
  # @api private
9
9
  # @return [String]
10
10
  def failure_message
11
- "\nexpected: #{format_object(expected)}\n got: #{format_object(actual)}\n\n(compared using ==)\n"
11
+ "\nexpected: #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using ==)\n"
12
12
  end
13
13
 
14
14
  # @api private
15
15
  # @return [String]
16
16
  def failure_message_when_negated
17
- "\nexpected: value != #{format_object(expected)}\n got: #{format_object(actual)}\n\n(compared using ==)\n"
17
+ "\nexpected: value != #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using ==)\n"
18
18
  end
19
19
 
20
20
  # @api private
21
21
  # @return [String]
22
22
  def description
23
- "#{name_to_sentence} #{@expected.inspect}"
23
+ "eq #{expected_formatted}"
24
24
  end
25
25
 
26
26
  # @api private
@@ -34,41 +34,6 @@ module RSpec
34
34
  def match(expected, actual)
35
35
  actual == expected
36
36
  end
37
-
38
- def format_object(object)
39
- if Time === object
40
- format_time(object)
41
- elsif defined?(DateTime) && DateTime === object
42
- format_date_time(object)
43
- elsif defined?(BigDecimal) && BigDecimal === object
44
- "#{object.to_s 'F'} (#{object.inspect})"
45
- else
46
- object.inspect
47
- end
48
- end
49
-
50
- TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
51
-
52
- if Time.method_defined?(:nsec)
53
- def format_time(time)
54
- time.strftime("#{TIME_FORMAT}.#{"%09d" % time.nsec} %z")
55
- end
56
- else # for 1.8.7
57
- def format_time(time)
58
- time.strftime("#{TIME_FORMAT}.#{"%06d" % time.usec} %z")
59
- end
60
- end
61
-
62
- DATE_TIME_FORMAT = "%a, %d %b %Y %H:%M:%S.%N %z"
63
- # ActiveSupport sometimes overrides inspect. If `ActiveSupport` is
64
- # defined use a custom format string that includes more time precision.
65
- def format_date_time(date_time)
66
- if defined?(ActiveSupport)
67
- date_time.strftime(DATE_TIME_FORMAT)
68
- else
69
- date_time.inspect
70
- end
71
- end
72
37
  end
73
38
  end
74
39
  end
@@ -8,13 +8,13 @@ module RSpec
8
8
  # @api private
9
9
  # @return [String]
10
10
  def failure_message
11
- "\nexpected: #{expected.inspect}\n got: #{actual.inspect}\n\n(compared using eql?)\n"
11
+ "\nexpected: #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using eql?)\n"
12
12
  end
13
13
 
14
14
  # @api private
15
15
  # @return [String]
16
16
  def failure_message_when_negated
17
- "\nexpected: value != #{expected.inspect}\n got: #{actual.inspect}\n\n(compared using eql?)\n"
17
+ "\nexpected: value != #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using eql?)\n"
18
18
  end
19
19
 
20
20
  # @api private
@@ -48,14 +48,14 @@ MESSAGE
48
48
 
49
49
  def actual_inspected
50
50
  if LITERAL_SINGLETONS.include?(actual)
51
- actual.inspect
51
+ actual_formatted
52
52
  else
53
53
  inspect_object(actual)
54
54
  end
55
55
  end
56
56
 
57
57
  def simple_failure_message
58
- "\nexpected #{expected.inspect}\n got #{actual_inspected}\n"
58
+ "\nexpected #{expected_formatted}\n got #{actual_inspected}\n"
59
59
  end
60
60
 
61
61
  def detailed_failure_message
@@ -73,7 +73,7 @@ MESSAGE
73
73
  end
74
74
 
75
75
  def inspect_object(o)
76
- "#<#{o.class}:#{o.object_id}> => #{o.inspect}"
76
+ "#<#{o.class}:#{o.object_id}> => #{RSpec::Support::ObjectFormatter.format(o)}"
77
77
  end
78
78
  end
79
79
  end
@@ -28,13 +28,13 @@ module RSpec
28
28
  # @api private
29
29
  # @return [String]
30
30
  def failure_message
31
- "expected #{@actual.inspect} to exist#{@test.validity_message}"
31
+ "expected #{actual_formatted} to exist#{@test.validity_message}"
32
32
  end
33
33
 
34
34
  # @api private
35
35
  # @return [String]
36
36
  def failure_message_when_negated
37
- "expected #{@actual.inspect} not to exist#{@test.validity_message}"
37
+ "expected #{actual_formatted} not to exist#{@test.validity_message}"
38
38
  end
39
39
 
40
40
  # @api private
@@ -49,9 +49,11 @@ module RSpec
49
49
 
50
50
  # support 1.8.7, evaluate once at load time for performance
51
51
  if String === methods.first
52
+ # :nocov:
52
53
  def private_predicate?
53
54
  @actual.private_methods.include? predicate.to_s
54
55
  end
56
+ # :nocov:
55
57
  else
56
58
  def private_predicate?
57
59
  @actual.private_methods.include? predicate
@@ -80,7 +82,7 @@ module RSpec
80
82
 
81
83
  def args_description
82
84
  return nil if @args.empty?
83
- @args.map { |arg| arg.inspect }.join(', ')
85
+ @args.map { |arg| RSpec::Support::ObjectFormatter.format(arg) }.join(', ')
84
86
  end
85
87
 
86
88
  def failure_message_args_description