rspec-expectations 3.9.4 → 3.11.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a7b90c1caeec07d53054eb0536482494c2584e6d7cb0fc9cfd3493ebc6b27a7
4
- data.tar.gz: 2eed511b0562ee702eb71bda98058c61d9ddda6c7b0337adde7acd72ddfb81df
3
+ metadata.gz: e651e80dd869915779a72a9ea43b38d20327f3dbfaf94a4851eff7974d1f3d0a
4
+ data.tar.gz: e1788055c911b97cbad1b3747fe9b9be005a999e2e97e6c361df4c91fa4fda25
5
5
  SHA512:
6
- metadata.gz: 7d0ea4666034e2901450f23fe706d1258f0b2c37ac7edd823477e31eae7c6d56e597a0b728cea0fe5f12ab80ee43ac4ed1a5c203ef34f66d180b40713adedf81
7
- data.tar.gz: 3b42d8706be59eb0dd511497941c34cebaff7476394f3f1ca80326d26c362bf3b37cca520ae0c6002a8efe167ee3fe7707e41e53072ac000c8c2a89f960fa6c0
6
+ metadata.gz: ec1b6b68b80fddae764dbf88312817c35c1e76228c29748e2b89d8d9a69d95384aa4fe9daf7b8a650dd307dd32aca13bae590d8960f0989beea67c54f7f6d7d5
7
+ data.tar.gz: 7a8a129e74ccec6299ab3af1c728cab2754a1cc98911507704c3f52a01c33b321250bfe715deef835b61963e22c66ec878500d10c6fea93d4fda7cc39b101676
checksums.yaml.gz.sig CHANGED
Binary file
data/Changelog.md CHANGED
@@ -1,3 +1,58 @@
1
+ ### Development
2
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.11.0...3-11-maintenance)
3
+
4
+ ### 3.11.0 / 2022-02-09
5
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.10.2...v3.11.0)
6
+
7
+ Enhancements:
8
+
9
+ * Return `true` from `aggregate_failures` when no exception occurs. (Jon Rowe, #1225)
10
+
11
+ Deprecations:
12
+
13
+ * Print a deprecation message when using the implicit block expectation syntax.
14
+ (Phil Pirozhkov, #1139)
15
+
16
+ ### 3.10.2 / 2022-01-14
17
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.10.1...v3.10.2)
18
+
19
+ Bug Fixes:
20
+
21
+ * Fix support for dynamic matchers for expectation target checks (Phil Pirozhkov, #1294)
22
+ * Fix `expect(array).to include(hash).times`, previously this would fail due to
23
+ matching the entire array as a single hash, rather than a member of the hash.
24
+ (Slava Kardakov, #1322)
25
+ * Ensure `raise_error` matches works with the `error_highlight` option from Ruby 3.1.
26
+ (Peter Goldstein, #1339)
27
+
28
+ ### 3.10.1 / 2020-12-27
29
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.10.0...v3.10.1)
30
+
31
+ Bug Fixes:
32
+
33
+ * Allow JRuby 9.2.x.x to generate backtraces normally rather than via our
34
+ backfill workaround. (#1230, Jon Rowe)
35
+
36
+ ### 3.10.0 / 2020-10-30
37
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.9.3...v3.10.0)
38
+
39
+ Enhancements:
40
+
41
+ * Allow `include` matcher to be chained with `once`, `at_least`, etc. for simple cases.
42
+ (Marc-André Lafortune, #1168)
43
+ * Add an explicit warning when `nil` is passed to `raise_error`. (Phil Pirozhkov, #1143)
44
+ * Improve `include` matcher's composability. (Phil Pirozhkov, #1155)
45
+ * Mocks expectations can now set a custom failure message.
46
+ (Benoit Tigeot and Nicolas Zermati, #1156)
47
+ * `aggregate_failures` now shows the backtrace line for each failure. (Fabricio Bedin, #1163)
48
+ * Support multiple combinations of `yield_control` modifiers like `at_least`, `at_most`.
49
+ (Jon Rowe, #1169)
50
+ * Dynamic `have_<n>` matchers now have output consistent with other dynamic matchers.
51
+ (Marc-André Lafortune, #1195)
52
+ * New config option `strict_predicate_matchers` allows predicate matcher to be strict
53
+ (i.e. match for `true` or `false`) instead of the default (match truthy vs `false` or `nil`).
54
+ (Marc-André Lafortune, #1196)
55
+
1
56
  ### 3.9.4 / 2020-10-29
2
57
  [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.9.3...v3.9.4)
3
58
 
@@ -7,7 +62,6 @@ Bug Fixes:
7
62
  were they would act like keywords and be cast to a hash. (Jon Rowe, #1222)
8
63
 
9
64
  ### 3.9.3 / 2020-10-23
10
- [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.9.2...v3.9.3)
11
65
 
12
66
  Bug Fixes:
13
67
 
@@ -22,6 +76,7 @@ Bug Fixes:
22
76
  * Prevent errors from causing false positives when using `be <operator>` comparison, e.g.
23
77
  `expect(1).not_to be < 'a'` will now correctly fail rather than pass. (Jon Rowe, #1208)
24
78
 
79
+
25
80
  ### 3.9.2 / 2020-05-08
26
81
  [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.9.1...v3.9.2)
27
82
 
@@ -85,7 +140,7 @@ Bug Fixes:
85
140
  * Prevent composed `all` matchers from leaking into their siblings leading to duplicate
86
141
  failures. (Jamie English, #1086)
87
142
  * Prevent objects which change their hash on comparison from failing change checks.
88
- (Phil Pirozhkov, #1110)
143
+ (Phil Pirozhkov, #1100)
89
144
  * Issue an `ArgumentError` rather than a `NoMethodError` when `be_an_instance_of` and
90
145
  `be_kind_of` matchers encounter objects not supporting those methods.
91
146
  (Taichi Ishitani, #1107)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # RSpec Expectations [![Build Status](https://secure.travis-ci.org/rspec/rspec-expectations.svg?branch=main)](http://travis-ci.org/rspec/rspec-expectations) [![Code Climate](https://codeclimate.com/github/rspec/rspec-expectations.svg)](https://codeclimate.com/github/rspec/rspec-expectations)
1
+ # RSpec Expectations [![Build Status](https://github.com/rspec/rspec-expectations/workflows/RSpec%20CI/badge.svg)](https://github.com/rspec/rspec-expectations/actions) [![Code Climate](https://codeclimate.com/github/rspec/rspec-expectations.svg)](https://codeclimate.com/github/rspec/rspec-expectations)
2
2
 
3
3
  RSpec::Expectations lets you express expected outcomes on an object in an
4
4
  example.
@@ -28,6 +28,7 @@ module RSpec
28
28
 
29
29
  def initialize
30
30
  @on_potential_false_positives = :warn
31
+ @strict_predicate_matchers = false
31
32
  end
32
33
 
33
34
  # Configures the supported syntax.
@@ -185,6 +186,20 @@ module RSpec
185
186
  @on_potential_false_positives = behavior
186
187
  end
187
188
 
189
+ # Configures RSpec to check predicate matchers to `be(true)` / `be(false)` (strict),
190
+ # or `be_truthy` / `be_falsey` (not strict).
191
+ # Historically, the default was `false`, but `true` is recommended.
192
+ def strict_predicate_matchers=(flag)
193
+ raise ArgumentError, "Pass `true` or `false`" unless flag == true || flag == false
194
+ @strict_predicate_matchers = flag
195
+ end
196
+
197
+ attr_reader :strict_predicate_matchers
198
+
199
+ def strict_predicate_matchers?
200
+ @strict_predicate_matchers
201
+ end
202
+
188
203
  # Indicates what RSpec will do about matcher use which will
189
204
  # potentially cause false positives in tests, generally you want to
190
205
  # avoid such scenarios so this defaults to `true`.
@@ -42,7 +42,7 @@ module RSpec
42
42
  elsif block
43
43
  raise ArgumentError, "You cannot pass both an argument and a block to `expect`."
44
44
  else
45
- new(value)
45
+ ValueExpectationTarget.new(value)
46
46
  end
47
47
  end
48
48
 
@@ -57,7 +57,7 @@ module RSpec
57
57
  # expect { perform }.to raise_error
58
58
  # @param [Matcher]
59
59
  # matcher
60
- # @param [String or Proc] message optional message to display when the expectation fails
60
+ # @param [String, Proc] message optional message to display when the expectation fails
61
61
  # @return [Boolean] true if the expectation succeeds (else raises)
62
62
  # @see RSpec::Matchers
63
63
  def to(matcher=nil, message=nil, &block)
@@ -70,7 +70,7 @@ module RSpec
70
70
  # expect(value).not_to eq(5)
71
71
  # @param [Matcher]
72
72
  # matcher
73
- # @param [String or Proc] message optional message to display when the expectation fails
73
+ # @param [String, Proc] message optional message to display when the expectation fails
74
74
  # @return [Boolean] false if the negative expectation succeeds (else raises)
75
75
  # @see RSpec::Matchers
76
76
  def not_to(matcher=nil, message=nil, &block)
@@ -90,6 +90,44 @@ module RSpec
90
90
  include InstanceMethods
91
91
  end
92
92
 
93
+ # @private
94
+ # Validates the provided matcher to ensure it supports block
95
+ # expectations, in order to avoid user confusion when they
96
+ # use a block thinking the expectation will be on the return
97
+ # value of the block rather than the block itself.
98
+ class ValueExpectationTarget < ExpectationTarget
99
+ def to(matcher=nil, message=nil, &block)
100
+ enforce_value_expectation(matcher)
101
+ super
102
+ end
103
+
104
+ def not_to(matcher=nil, message=nil, &block)
105
+ enforce_value_expectation(matcher)
106
+ super
107
+ end
108
+
109
+ private
110
+
111
+ def enforce_value_expectation(matcher)
112
+ return if supports_value_expectations?(matcher)
113
+
114
+ RSpec.deprecate(
115
+ "expect(value).to #{RSpec::Support::ObjectFormatter.format(matcher)}",
116
+ :message =>
117
+ "The implicit block expectation syntax is deprecated, you should pass " \
118
+ "a block rather than an argument to `expect` to use the provided " \
119
+ "block expectation matcher or the matcher must implement " \
120
+ "`supports_value_expectations?`. e.g `expect { value }.to " \
121
+ "#{RSpec::Support::ObjectFormatter.format(matcher)}` not " \
122
+ "`expect(value).to #{RSpec::Support::ObjectFormatter.format(matcher)}`"
123
+ )
124
+ end
125
+
126
+ def supports_value_expectations?(matcher)
127
+ !matcher.respond_to?(:supports_value_expectations?) || matcher.supports_value_expectations?
128
+ end
129
+ end
130
+
93
131
  # @private
94
132
  # Validates the provided matcher to ensure it supports block
95
133
  # expectations, in order to avoid user confusion when they
@@ -118,9 +156,7 @@ module RSpec
118
156
  end
119
157
 
120
158
  def supports_block_expectations?(matcher)
121
- matcher.supports_block_expectations?
122
- rescue NoMethodError
123
- false
159
+ matcher.respond_to?(:supports_block_expectations?) && matcher.supports_block_expectations?
124
160
  end
125
161
  end
126
162
  end
@@ -52,10 +52,10 @@ module RSpec
52
52
 
53
53
  private
54
54
 
55
- if RSpec::Support::Ruby.jruby?
56
- # On JRuby, `caller` and `raise` produce different backtraces with regards to `.java`
57
- # stack frames. It's important that we use `raise` for JRuby to produce a backtrace
58
- # that has a continuous common section with the raised `MultipleExpectationsNotMetError`,
55
+ if RSpec::Support::Ruby.jruby? && RSpec::Support::Ruby.jruby_version < '9.2.0.0'
56
+ # On JRuby 9.1.x.x and before, `caller` and `raise` produce different backtraces with
57
+ # regards to `.java` stack frames. It's important that we use `raise` for JRuby to produce
58
+ # a backtrace that has a continuous common section with the raised `MultipleExpectationsNotMetError`,
59
59
  # so that rspec-core's truncation logic can work properly on it to list the backtrace
60
60
  # relative to the `aggregate_failures` block.
61
61
  def assign_backtrace(failure)
@@ -80,7 +80,7 @@ module RSpec
80
80
  all_errors = failures + other_errors
81
81
 
82
82
  case all_errors.size
83
- when 0 then return nil
83
+ when 0 then return true
84
84
  when 1 then RSpec::Support.notify_failure all_errors.first
85
85
  else RSpec::Support.notify_failure MultipleExpectationsNotMetError.new(self)
86
86
  end
@@ -150,11 +150,29 @@ module RSpec
150
150
  def enumerated(exceptions, index_offset)
151
151
  exceptions.each_with_index.map do |exception, index|
152
152
  index += index_offset
153
- formatted_message = yield exception
153
+ formatted_message = "#{yield exception}\n#{format_backtrace(exception.backtrace).first}"
154
154
  "#{index_label index}#{indented formatted_message, index}"
155
155
  end
156
156
  end
157
157
 
158
+ def exclusion_patterns
159
+ patterns = %w[/lib\d*/ruby/ bin/ exe/rspec /lib/bundler/ /exe/bundle:]
160
+ patterns << "org/jruby/" if RSpec::Support::Ruby.jruby?
161
+ patterns.map! { |s| Regexp.new(s.gsub('/', File::SEPARATOR)) }
162
+ end
163
+
164
+ def format_backtrace(backtrace)
165
+ backtrace.map { |l| backtrace_line(l) }.compact.tap { |filtered| filtered.concat backtrace if filtered.empty? }
166
+ end
167
+
168
+ def backtrace_line(line)
169
+ return if [Regexp.union(RSpec::CallerFilter::IGNORE_REGEX, *exclusion_patterns)].any? { |p| line =~ p }
170
+
171
+ # It changes the current path that is relative to
172
+ # system root to be relative to the project root.
173
+ line.sub(/(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/, '\\1.\\2'.freeze).sub(/\A([^:]+:\d+)$/, '\\1'.freeze)
174
+ end
175
+
158
176
  def enumerated_failures
159
177
  enumerated(failures, 0, &:message)
160
178
  end
@@ -44,10 +44,16 @@ module RSpec
44
44
 
45
45
  # @private
46
46
  class PositiveExpectationHandler
47
- def self.handle_matcher(actual, initial_matcher, message=nil, &block)
48
- ExpectationHelper.with_matcher(self, initial_matcher, message) do |matcher|
47
+ def self.handle_matcher(actual, initial_matcher, custom_message=nil, &block)
48
+ ExpectationHelper.with_matcher(self, initial_matcher, custom_message) do |matcher|
49
49
  return ::RSpec::Matchers::BuiltIn::PositiveOperatorMatcher.new(actual) unless initial_matcher
50
- matcher.matches?(actual, &block) || ExpectationHelper.handle_failure(matcher, message, :failure_message)
50
+
51
+ match_result = matcher.matches?(actual, &block)
52
+ if custom_message && match_result.respond_to?(:error_generator)
53
+ match_result.error_generator.opts[:message] = custom_message
54
+ end
55
+
56
+ match_result || ExpectationHelper.handle_failure(matcher, custom_message, :failure_message)
51
57
  end
52
58
  end
53
59
 
@@ -66,10 +72,16 @@ module RSpec
66
72
 
67
73
  # @private
68
74
  class NegativeExpectationHandler
69
- def self.handle_matcher(actual, initial_matcher, message=nil, &block)
70
- ExpectationHelper.with_matcher(self, initial_matcher, message) do |matcher|
75
+ def self.handle_matcher(actual, initial_matcher, custom_message=nil, &block)
76
+ ExpectationHelper.with_matcher(self, initial_matcher, custom_message) do |matcher|
71
77
  return ::RSpec::Matchers::BuiltIn::NegativeOperatorMatcher.new(actual) unless initial_matcher
72
- does_not_match?(matcher, actual, &block) || ExpectationHelper.handle_failure(matcher, message, :failure_message_when_negated)
78
+
79
+ negated_match_result = does_not_match?(matcher, actual, &block)
80
+ if custom_message && negated_match_result.respond_to?(:error_generator)
81
+ negated_match_result.error_generator.opts[:message] = custom_message
82
+ end
83
+
84
+ negated_match_result || ExpectationHelper.handle_failure(matcher, custom_message, :failure_message_when_negated)
73
85
  end
74
86
  end
75
87
 
@@ -2,7 +2,7 @@ module RSpec
2
2
  module Expectations
3
3
  # @private
4
4
  module Version
5
- STRING = '3.9.4'
5
+ STRING = '3.11.0'
6
6
  end
7
7
  end
8
8
  end
@@ -78,6 +78,11 @@ module RSpec
78
78
  false
79
79
  end
80
80
 
81
+ # @private
82
+ def supports_value_expectations?
83
+ true
84
+ end
85
+
81
86
  # @api private
82
87
  def expects_call_stack_jump?
83
88
  false
@@ -186,116 +186,6 @@ module RSpec
186
186
  @actual.__send__ @operator, @expected
187
187
  end
188
188
  end
189
-
190
- # @api private
191
- # Provides the implementation of `be_<predicate>`.
192
- # Not intended to be instantiated directly.
193
- class BePredicate < BaseMatcher
194
- include BeHelpers
195
-
196
- def initialize(*args, &block)
197
- @expected = parse_expected(args.shift)
198
- @args = args
199
- @block = block
200
- end
201
- ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
202
-
203
- def matches?(actual, &block)
204
- @actual = actual
205
- @block ||= block
206
- predicate_accessible? && predicate_matches?
207
- end
208
-
209
- def does_not_match?(actual, &block)
210
- @actual = actual
211
- @block ||= block
212
- predicate_accessible? && !predicate_matches?
213
- end
214
-
215
- # @api private
216
- # @return [String]
217
- def failure_message
218
- failure_message_expecting(true)
219
- end
220
-
221
- # @api private
222
- # @return [String]
223
- def failure_message_when_negated
224
- failure_message_expecting(false)
225
- end
226
-
227
- # @api private
228
- # @return [String]
229
- def description
230
- "#{prefix_to_sentence}#{expected_to_sentence}#{args_to_sentence}"
231
- end
232
-
233
- private
234
-
235
- def predicate_accessible?
236
- actual.respond_to?(predicate) || actual.respond_to?(present_tense_predicate)
237
- end
238
-
239
- # support 1.8.7, evaluate once at load time for performance
240
- if String === methods.first
241
- # :nocov:
242
- def private_predicate?
243
- @actual.private_methods.include? predicate.to_s
244
- end
245
- # :nocov:
246
- else
247
- def private_predicate?
248
- @actual.private_methods.include? predicate
249
- end
250
- end
251
-
252
- def predicate_matches?
253
- method_name = actual.respond_to?(predicate) ? predicate : present_tense_predicate
254
- @predicate_matches = actual.__send__(method_name, *@args, &@block)
255
- end
256
-
257
- def predicate
258
- :"#{@expected}?"
259
- end
260
-
261
- def present_tense_predicate
262
- :"#{@expected}s?"
263
- end
264
-
265
- def parse_expected(expected)
266
- @prefix, expected = prefix_and_expected(expected)
267
- expected
268
- end
269
-
270
- def prefix_and_expected(symbol)
271
- Matchers::BE_PREDICATE_REGEX.match(symbol.to_s).captures.compact
272
- end
273
-
274
- def prefix_to_sentence
275
- EnglishPhrasing.split_words(@prefix)
276
- end
277
-
278
- def failure_message_expecting(value)
279
- validity_message ||
280
- "expected `#{actual_formatted}.#{predicate}#{args_to_s}` to return #{value}, got #{description_of @predicate_matches}"
281
- end
282
-
283
- def validity_message
284
- return nil if predicate_accessible?
285
-
286
- msg = "expected #{actual_formatted} to respond to `#{predicate}`".dup
287
-
288
- if private_predicate?
289
- msg << " but `#{predicate}` is a private method"
290
- elsif predicate == :true?
291
- msg << " or perhaps you meant `be true` or `be_truthy`"
292
- elsif predicate == :false?
293
- msg << " or perhaps you meant `be false` or `be_falsey`"
294
- end
295
-
296
- msg
297
- end
298
- end
299
189
  end
300
190
  end
301
191
  end
@@ -77,6 +77,11 @@ module RSpec
77
77
  true
78
78
  end
79
79
 
80
+ # @private
81
+ def supports_value_expectations?
82
+ false
83
+ end
84
+
80
85
  private
81
86
 
82
87
  def initialize(receiver=nil, message=nil, &block)
@@ -158,6 +163,11 @@ module RSpec
158
163
  true
159
164
  end
160
165
 
166
+ # @private
167
+ def supports_value_expectations?
168
+ false
169
+ end
170
+
161
171
  private
162
172
 
163
173
  def failure_reason
@@ -201,6 +211,11 @@ module RSpec
201
211
  true
202
212
  end
203
213
 
214
+ # @private
215
+ def supports_value_expectations?
216
+ false
217
+ end
218
+
204
219
  private
205
220
 
206
221
  def perform_change(event_proc)
@@ -337,6 +352,8 @@ module RSpec
337
352
  class ChangeDetails
338
353
  attr_reader :actual_after
339
354
 
355
+ UNDEFINED = Module.new.freeze
356
+
340
357
  def initialize(matcher_name, receiver=nil, message=nil, &block)
341
358
  if receiver && !message
342
359
  raise(
@@ -351,6 +368,11 @@ module RSpec
351
368
  @receiver = receiver
352
369
  @message = message
353
370
  @value_proc = block
371
+ # TODO: temporary measure to mute warning of access to an initialized
372
+ # instance variable when a deprecated implicit block expectation
373
+ # syntax is used. This may be removed once `fail` is used, and the
374
+ # matcher never issues this warning.
375
+ @actual_after = UNDEFINED
354
376
  end
355
377
 
356
378
  def value_representation
@@ -26,11 +26,19 @@ module RSpec
26
26
  "#{matcher_1.description} #{conjunction} #{matcher_2.description}"
27
27
  end
28
28
 
29
+ # @api private
29
30
  def supports_block_expectations?
30
31
  matcher_supports_block_expectations?(matcher_1) &&
31
32
  matcher_supports_block_expectations?(matcher_2)
32
33
  end
33
34
 
35
+ # @api private
36
+ def supports_value_expectations?
37
+ matcher_supports_value_expectations?(matcher_1) &&
38
+ matcher_supports_value_expectations?(matcher_2)
39
+ end
40
+
41
+ # @api private
34
42
  def expects_call_stack_jump?
35
43
  NestedEvaluator.matcher_expects_call_stack_jump?(matcher_1) ||
36
44
  NestedEvaluator.matcher_expects_call_stack_jump?(matcher_2)
@@ -102,6 +110,12 @@ module RSpec
102
110
  false
103
111
  end
104
112
 
113
+ def matcher_supports_value_expectations?(matcher)
114
+ matcher.supports_value_expectations?
115
+ rescue NoMethodError
116
+ true
117
+ end
118
+
105
119
  def matcher_is_diffable?(matcher)
106
120
  matcher.diffable?
107
121
  rescue NoMethodError