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
@@ -114,7 +114,7 @@ module RSpec
114
114
  end
115
115
 
116
116
  def throw_description(symbol, arg)
117
- symbol_description = symbol.is_a?(String) ? symbol : symbol.inspect
117
+ symbol_description = symbol.is_a?(String) ? symbol : description_of(symbol)
118
118
 
119
119
  arg_description = if arg
120
120
  " with #{description_of arg}"
@@ -78,6 +78,7 @@ module RSpec
78
78
  "matcher. Pass the argument as a block on to the method you are testing."
79
79
  end
80
80
  else
81
+ # :nocov:
81
82
  # On 1.8.7, `lambda { }.arity` and `lambda { |*a| }.arity` both return -1,
82
83
  # so we can't distinguish between accepting no args and an arg splat.
83
84
  # It's OK to skip, this, though; it just provides a nice error message
@@ -86,6 +87,7 @@ module RSpec
86
87
  def assert_valid_expect_block!
87
88
  # nothing to do
88
89
  end
90
+ # :nocov:
89
91
  end
90
92
  end
91
93
 
@@ -247,7 +249,7 @@ module RSpec
247
249
  def positive_failure_reason
248
250
  return "was not a block" unless @probe.has_block?
249
251
  return "did not yield" if @probe.num_yields.zero?
250
- "yielded with arguments: #{@probe.single_yield_args.inspect}"
252
+ "yielded with arguments: #{description_of @probe.single_yield_args}"
251
253
  end
252
254
 
253
255
  def negative_failure_reason
@@ -317,7 +319,7 @@ module RSpec
317
319
  elsif all_args_match?
318
320
  "yielded with expected arguments" \
319
321
  "\nexpected not: #{surface_descriptions_in(@expected).inspect}" +
320
- "\n got: #{@actual.inspect}"
322
+ "\n got: #{actual_formatted}"
321
323
  else
322
324
  "did"
323
325
  end
@@ -332,7 +334,7 @@ module RSpec
332
334
  unless (match = all_args_match?)
333
335
  @positive_args_failure = "yielded with unexpected arguments" \
334
336
  "\nexpected: #{surface_descriptions_in(@expected).inspect}" +
335
- "\n got: #{@actual.inspect}"
337
+ "\n got: #{actual_formatted}"
336
338
  end
337
339
 
338
340
  match
@@ -400,7 +402,7 @@ module RSpec
400
402
 
401
403
  "yielded with unexpected arguments" \
402
404
  "\nexpected: #{surface_descriptions_in(@expected).inspect}" \
403
- "\n got: #{@actual.inspect}"
405
+ "\n got: #{actual_formatted}"
404
406
  end
405
407
 
406
408
  def negative_failure_reason
@@ -408,7 +410,7 @@ module RSpec
408
410
 
409
411
  "yielded with expected arguments" \
410
412
  "\nexpected not: #{surface_descriptions_in(@expected).inspect}" \
411
- "\n got: #{@actual.inspect}"
413
+ "\n got: #{actual_formatted}"
412
414
  end
413
415
  end
414
416
  end
@@ -80,8 +80,7 @@ module RSpec
80
80
  #
81
81
  # @!visibility public
82
82
  def description_of(object)
83
- return object.description if Matchers.is_a_describable_matcher?(object)
84
- object.inspect
83
+ RSpec::Support::ObjectFormatter.format(object)
85
84
  end
86
85
 
87
86
  # Transforms the given data structue (typically a hash or array)
@@ -102,12 +101,12 @@ module RSpec
102
101
  elsif Hash === item
103
102
  Hash[surface_descriptions_in(item.to_a)]
104
103
  elsif Struct === item
105
- item.inspect
104
+ RSpec::Support::ObjectFormatter.format(item)
106
105
  elsif should_enumerate?(item)
107
106
  begin
108
107
  item.map { |subitem| surface_descriptions_in(subitem) }
109
108
  rescue IOError # STDOUT is enumerable but `map` raises an error
110
- item.inspect
109
+ RSpec::Support::ObjectFormatter.format(item)
111
110
  end
112
111
  else
113
112
  item
@@ -149,6 +148,7 @@ module RSpec
149
148
  end
150
149
 
151
150
  if String.ancestors.include?(Enumerable) # 1.8.7
151
+ # :nocov:
152
152
  # Strings are not enumerable on 1.9, and on 1.8 they are an infinitely
153
153
  # nested enumerable: since ruby lacks a character class, it yields
154
154
  # 1-character strings, which are themselves enumerable, composed of a
@@ -159,6 +159,7 @@ module RSpec
159
159
  return false if String === item
160
160
  Enumerable === item && !(Range === item)
161
161
  end
162
+ # :nocov:
162
163
  else
163
164
  # @api private
164
165
  def should_enumerate?(item)
@@ -24,9 +24,11 @@ module RSpec
24
24
  end
25
25
  end
26
26
  else
27
+ # :nocov:
27
28
  def warn_about_block_args(*)
28
29
  # There's no way to detect block params on 1.8 since the method reflection APIs don't expose it
29
30
  end
31
+ # :nocov:
30
32
  end
31
33
 
32
34
  RSpec.configure { |c| c.extend self } if RSpec.respond_to?(:configure)
@@ -58,13 +60,18 @@ module RSpec
58
60
  define_user_override(:matches?, match_block) do |actual|
59
61
  begin
60
62
  @actual = actual
61
- super(*actual_arg_for(match_block))
63
+ RSpec::Support.with_failure_notifier(RAISE_NOTIFIER) do
64
+ super(*actual_arg_for(match_block))
65
+ end
62
66
  rescue RSpec::Expectations::ExpectationNotMetError
63
67
  false
64
68
  end
65
69
  end
66
70
  end
67
71
 
72
+ # @private
73
+ RAISE_NOTIFIER = Proc.new { |err, _opts| raise err }
74
+
68
75
  # Use this to define the block for a negative expectation (`expect(...).not_to`)
69
76
  # when the positive and negative forms require different handling. This
70
77
  # is rarely necessary, but can be helpful, for example, when specifying
@@ -300,7 +307,9 @@ module RSpec
300
307
 
301
308
  # The default description.
302
309
  def description
303
- "#{name_to_sentence}#{to_sentence expected}#{chained_method_clause_sentences}"
310
+ english_name = EnglishPhrasing.split_words(name)
311
+ expected_list = EnglishPhrasing.list(expected)
312
+ "#{english_name}#{expected_list}#{chained_method_clause_sentences}"
304
313
  end
305
314
 
306
315
  # Matchers do not support block expectations by default. You
@@ -320,7 +329,9 @@ module RSpec
320
329
  return '' unless Expectations.configuration.include_chain_clauses_in_custom_matcher_descriptions?
321
330
 
322
331
  @chained_method_clauses.map do |(method_name, method_args)|
323
- " #{split_words(method_name)}#{to_sentence(method_args)}"
332
+ english_name = EnglishPhrasing.split_words(method_name)
333
+ arg_list = EnglishPhrasing.list(method_args)
334
+ " #{english_name}#{arg_list}"
324
335
  end.join
325
336
  end
326
337
  end
@@ -336,9 +347,6 @@ module RSpec
336
347
  # Allows expectation expressions to be used in the match block.
337
348
  include RSpec::Matchers
338
349
 
339
- # Converts matcher name and expected args to an English expresion.
340
- include RSpec::Matchers::Pretty
341
-
342
350
  # Supports the matcher composability features of RSpec 3+.
343
351
  include Composable
344
352
 
@@ -357,6 +365,9 @@ module RSpec
357
365
  # The block parameter used in the expectation
358
366
  attr_reader :block_arg
359
367
 
368
+ # The name of the matcher.
369
+ attr_reader :name
370
+
360
371
  # @api private
361
372
  def initialize(name, declarations, matcher_execution_context, *expected, &block_arg)
362
373
  @name = name
@@ -406,11 +417,13 @@ module RSpec
406
417
  super || @matcher_execution_context.respond_to?(method, include_private)
407
418
  end
408
419
  else # for 1.8.7
420
+ # :nocov:
409
421
  # Indicates that this matcher responds to messages
410
422
  # from the `@matcher_execution_context` as well.
411
423
  def respond_to?(method, include_private=false)
412
424
  super || @matcher_execution_context.respond_to?(method, include_private)
413
425
  end
426
+ # :nocov:
414
427
  end
415
428
 
416
429
  private
@@ -0,0 +1,42 @@
1
+ module RSpec
2
+ module Matchers
3
+ # Facilitates converting ruby objects to English phrases.
4
+ module EnglishPhrasing
5
+ # Converts a symbol into an English expression.
6
+ #
7
+ # split_words(:banana_creme_pie) #=> "banana creme pie"
8
+ #
9
+ def self.split_words(sym)
10
+ sym.to_s.gsub(/_/, ' ')
11
+ end
12
+
13
+ # @note The returned string has a leading space except
14
+ # when given an empty list.
15
+ #
16
+ # Converts an object (often a collection of objects)
17
+ # into an English list.
18
+ #
19
+ # list(['banana', 'kiwi', 'mango'])
20
+ # #=> " \"banana\", \"kiwi\", and \"mango\""
21
+ #
22
+ # Given an empty collection, returns the empty string.
23
+ #
24
+ # list([]) #=> ""
25
+ #
26
+ def self.list(obj)
27
+ return " #{RSpec::Support::ObjectFormatter.format(obj)}" if !obj || Struct === obj
28
+ items = Array(obj).map { |w| RSpec::Support::ObjectFormatter.format(w) }
29
+ case items.length
30
+ when 0
31
+ ""
32
+ when 1
33
+ " #{items[0]}"
34
+ when 2
35
+ " #{items[0]} and #{items[1]}"
36
+ else
37
+ " #{items[0...-1].join(', ')}, and #{items[-1]}"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -53,13 +53,7 @@ module RSpec
53
53
  private
54
54
 
55
55
  def self.diff_label_for(matcher)
56
- "Diff for (#{truncated(description_for(matcher))}):"
57
- end
58
-
59
- def self.description_for(matcher)
60
- matcher.description
61
- rescue NoMethodError
62
- matcher.inspect
56
+ "Diff for (#{truncated(RSpec::Support::ObjectFormatter.format(matcher))}):"
63
57
  end
64
58
 
65
59
  def self.truncated(description)
@@ -70,7 +64,7 @@ module RSpec
70
64
  def diffs(differ, actual)
71
65
  @expected_list.map do |(expected, diff_label)|
72
66
  diff = differ.diff(actual, expected)
73
- next if diff.empty?
67
+ next if diff.strip.empty?
74
68
  "#{diff_label}#{diff}"
75
69
  end.compact.join("\n")
76
70
  end
@@ -0,0 +1,42 @@
1
+ require 'rspec/expectations'
2
+
3
+ module RSpec
4
+ module Matchers
5
+ # Matchers for testing RSpec matchers. Include them with:
6
+ #
7
+ # require 'rspec/matchers/fail_matchers'
8
+ # RSpec.configure do |config|
9
+ # config.include RSpec::Matchers::FailMatchers
10
+ # end
11
+ #
12
+ module FailMatchers
13
+ # Matches if an expectation fails
14
+ #
15
+ # @example
16
+ # expect { some_expectation }.to fail
17
+ def fail(&block)
18
+ raise_error(RSpec::Expectations::ExpectationNotMetError, &block)
19
+ end
20
+
21
+ # Matches if an expectation fails with the provided message
22
+ #
23
+ # @example
24
+ # expect { some_expectation }.to fail_with("some failure message")
25
+ # expect { some_expectation }.to fail_with(/some failure message/)
26
+ def fail_with(message)
27
+ raise_error(RSpec::Expectations::ExpectationNotMetError, message)
28
+ end
29
+
30
+ # Matches if an expectation fails including the provided message
31
+ #
32
+ # @example
33
+ # expect { some_expectation }.to fail_including("portion of some failure message")
34
+ def fail_including(*snippets)
35
+ raise_error(
36
+ RSpec::Expectations::ExpectationNotMetError,
37
+ a_string_including(*snippets)
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
@@ -19,9 +19,11 @@ module RSpec
19
19
  super || base_matcher.respond_to?(name, include_all)
20
20
  end
21
21
  else
22
+ # :nocov:
22
23
  def respond_to?(name, include_all=false)
23
24
  super || base_matcher.respond_to?(name, include_all)
24
25
  end
26
+ # :nocov:
25
27
  end
26
28
 
27
29
  def initialize_copy(other)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-expectations
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Baker
@@ -45,7 +45,7 @@ cert_chain:
45
45
  ZsVDj6a7lH3cNqtWXZxrb2wO38qV5AkYj8SQK7Hj3/Yui9myUX3crr+PdetazSqQ
46
46
  F3MdtaDehhjC
47
47
  -----END CERTIFICATE-----
48
- date: 2015-04-06 00:00:00.000000000 Z
48
+ date: 2015-06-12 00:00:00.000000000 Z
49
49
  dependencies:
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: rspec-support
@@ -53,14 +53,14 @@ dependencies:
53
53
  requirements:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
- version: 3.2.0
56
+ version: 3.3.0
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: 3.2.0
63
+ version: 3.3.0
64
64
  - !ruby/object:Gem::Dependency
65
65
  name: diff-lcs
66
66
  requirement: !ruby/object:Gem::Requirement
@@ -153,6 +153,7 @@ files:
153
153
  - lib/rspec/expectations/configuration.rb
154
154
  - lib/rspec/expectations/expectation_target.rb
155
155
  - lib/rspec/expectations/fail_with.rb
156
+ - lib/rspec/expectations/failure_aggregator.rb
156
157
  - lib/rspec/expectations/handler.rb
157
158
  - lib/rspec/expectations/minitest_integration.rb
158
159
  - lib/rspec/expectations/syntax.rb
@@ -189,11 +190,12 @@ files:
189
190
  - lib/rspec/matchers/built_in/yield.rb
190
191
  - lib/rspec/matchers/composable.rb
191
192
  - lib/rspec/matchers/dsl.rb
193
+ - lib/rspec/matchers/english_phrasing.rb
192
194
  - lib/rspec/matchers/expecteds_for_multiple_diffs.rb
195
+ - lib/rspec/matchers/fail_matchers.rb
193
196
  - lib/rspec/matchers/generated_descriptions.rb
194
197
  - lib/rspec/matchers/matcher_delegator.rb
195
198
  - lib/rspec/matchers/matcher_protocol.rb
196
- - lib/rspec/matchers/pretty.rb
197
199
  homepage: http://github.com/rspec/rspec-expectations
198
200
  licenses:
199
201
  - MIT
@@ -214,10 +216,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
216
  - !ruby/object:Gem::Version
215
217
  version: '0'
216
218
  requirements: []
217
- rubyforge_project: rspec
219
+ rubyforge_project:
218
220
  rubygems_version: 2.2.2
219
221
  signing_key:
220
222
  specification_version: 4
221
- summary: rspec-expectations-3.2.1
223
+ summary: rspec-expectations-3.3.0
222
224
  test_files: []
223
225
  has_rdoc:
metadata.gz.sig CHANGED
Binary file
@@ -1,77 +0,0 @@
1
- module RSpec
2
- module Matchers
3
- # @api private
4
- # Contains logic to facilitate converting ruby symbols and
5
- # objects to english phrases.
6
- module Pretty
7
- # @api private
8
- # Converts a symbol into an english expression.
9
- def split_words(sym)
10
- sym.to_s.gsub(/_/, ' ')
11
- end
12
- module_function :split_words
13
-
14
- # @api private
15
- # Converts a collection of objects into an english expression.
16
- def to_sentence(words)
17
- return " #{words.inspect}" if !words || Struct === words
18
- words = Array(words).map { |w| to_word(w) }
19
- case words.length
20
- when 0
21
- ""
22
- when 1
23
- " #{words[0]}"
24
- when 2
25
- " #{words[0]} and #{words[1]}"
26
- else
27
- " #{words[0...-1].join(', ')}, and #{words[-1]}"
28
- end
29
- end
30
-
31
- # @api private
32
- # Converts the given item to string suitable for use in a list expression.
33
- def to_word(item)
34
- is_matcher_with_description?(item) ? item.description : item.inspect
35
- end
36
-
37
- # @private
38
- # Provides an English expression for the matcher name.
39
- def name_to_sentence
40
- split_words(name)
41
- end
42
-
43
- # @api private
44
- # Provides a name for the matcher.
45
- def name
46
- defined?(@name) ? @name : underscore(self.class.name.split("::").last)
47
- end
48
-
49
- # @private
50
- # Borrowed from ActiveSupport
51
- def underscore(camel_cased_word)
52
- word = camel_cased_word.to_s.dup
53
- word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
54
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
55
- word.tr!("-", "_")
56
- word.downcase!
57
- word
58
- end
59
-
60
- private
61
-
62
- def is_matcher_with_description?(object)
63
- RSpec::Matchers.is_a_matcher?(object) && object.respond_to?(:description)
64
- end
65
-
66
- # `{ :a => 5, :b => 2 }.inspect` produces:
67
- # {:a=>5, :b=>2}
68
- # ...but it looks much better as:
69
- # {:a => 5, :b => 2}
70
- #
71
- # This is idempotent and safe to run on a string multiple times.
72
- def improve_hash_formatting(inspect_string)
73
- inspect_string.gsub(/(\S)=>(\S)/, '\1 => \2')
74
- end
75
- end
76
- end
77
- end