rspec-expectations 3.0.4 → 3.12.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +1 -1
  5. data/Changelog.md +530 -5
  6. data/{License.txt → LICENSE.md} +5 -4
  7. data/README.md +73 -31
  8. data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
  9. data/lib/rspec/expectations/configuration.rb +96 -1
  10. data/lib/rspec/expectations/expectation_target.rb +82 -38
  11. data/lib/rspec/expectations/fail_with.rb +11 -6
  12. data/lib/rspec/expectations/failure_aggregator.rb +229 -0
  13. data/lib/rspec/expectations/handler.rb +36 -15
  14. data/lib/rspec/expectations/minitest_integration.rb +43 -2
  15. data/lib/rspec/expectations/syntax.rb +5 -5
  16. data/lib/rspec/expectations/version.rb +1 -1
  17. data/lib/rspec/expectations.rb +15 -1
  18. data/lib/rspec/matchers/aliased_matcher.rb +79 -4
  19. data/lib/rspec/matchers/built_in/all.rb +11 -0
  20. data/lib/rspec/matchers/built_in/base_matcher.rb +111 -28
  21. data/lib/rspec/matchers/built_in/be.rb +28 -114
  22. data/lib/rspec/matchers/built_in/be_between.rb +1 -1
  23. data/lib/rspec/matchers/built_in/be_instance_of.rb +5 -1
  24. data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -1
  25. data/lib/rspec/matchers/built_in/be_within.rb +5 -12
  26. data/lib/rspec/matchers/built_in/change.rb +171 -63
  27. data/lib/rspec/matchers/built_in/compound.rb +201 -30
  28. data/lib/rspec/matchers/built_in/contain_exactly.rb +73 -12
  29. data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
  30. data/lib/rspec/matchers/built_in/eq.rb +3 -38
  31. data/lib/rspec/matchers/built_in/eql.rb +2 -2
  32. data/lib/rspec/matchers/built_in/equal.rb +3 -3
  33. data/lib/rspec/matchers/built_in/exist.rb +7 -3
  34. data/lib/rspec/matchers/built_in/has.rb +93 -30
  35. data/lib/rspec/matchers/built_in/have_attributes.rb +114 -0
  36. data/lib/rspec/matchers/built_in/include.rb +133 -25
  37. data/lib/rspec/matchers/built_in/match.rb +79 -2
  38. data/lib/rspec/matchers/built_in/operators.rb +14 -5
  39. data/lib/rspec/matchers/built_in/output.rb +59 -2
  40. data/lib/rspec/matchers/built_in/raise_error.rb +130 -27
  41. data/lib/rspec/matchers/built_in/respond_to.rb +117 -15
  42. data/lib/rspec/matchers/built_in/satisfy.rb +28 -14
  43. data/lib/rspec/matchers/built_in/{start_and_end_with.rb → start_or_end_with.rb} +20 -8
  44. data/lib/rspec/matchers/built_in/throw_symbol.rb +15 -5
  45. data/lib/rspec/matchers/built_in/yield.rb +129 -156
  46. data/lib/rspec/matchers/built_in.rb +5 -3
  47. data/lib/rspec/matchers/composable.rb +24 -36
  48. data/lib/rspec/matchers/dsl.rb +203 -37
  49. data/lib/rspec/matchers/english_phrasing.rb +58 -0
  50. data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +82 -0
  51. data/lib/rspec/matchers/fail_matchers.rb +42 -0
  52. data/lib/rspec/matchers/generated_descriptions.rb +1 -2
  53. data/lib/rspec/matchers/matcher_delegator.rb +3 -4
  54. data/lib/rspec/matchers/matcher_protocol.rb +105 -0
  55. data/lib/rspec/matchers.rb +267 -144
  56. data.tar.gz.sig +0 -0
  57. metadata +71 -49
  58. metadata.gz.sig +0 -0
  59. data/lib/rspec/matchers/pretty.rb +0 -77
data/README.md CHANGED
@@ -1,9 +1,11 @@
1
- # RSpec Expectations [![Build Status](https://secure.travis-ci.org/rspec/rspec-expectations.png?branch=master)](http://travis-ci.org/rspec/rspec-expectations) [![Code Climate](https://codeclimate.com/github/rspec/rspec-expectations.png)](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.
5
5
 
6
- expect(account.balance).to eq(Money.new(37.42, :USD))
6
+ ```ruby
7
+ expect(account.balance).to eq(Money.new(37.42, :USD))
8
+ ```
7
9
 
8
10
  ## Install
9
11
 
@@ -13,11 +15,34 @@ rspec-core and rspec-mocks):
13
15
 
14
16
  gem install rspec
15
17
 
18
+ Want to run against the `main` branch? You'll need to include the dependent
19
+ RSpec repos as well. Add the following to your `Gemfile`:
20
+
21
+ ```ruby
22
+ %w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|
23
+ gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'main'
24
+ end
25
+ ```
26
+
16
27
  If you want to use rspec-expectations with another tool, like Test::Unit,
17
28
  Minitest, or Cucumber, you can install it directly:
18
29
 
19
30
  gem install rspec-expectations
20
31
 
32
+ ## Contributing
33
+
34
+ Once you've set up the environment, you'll need to cd into the working
35
+ directory of whichever repo you want to work in. From there you can run the
36
+ specs and cucumber features, and make patches.
37
+
38
+ NOTE: You do not need to use rspec-dev to work on a specific RSpec repo. You
39
+ can treat each RSpec repo as an independent project.
40
+
41
+ - [Build details](BUILD_DETAIL.md)
42
+ - [Code of Conduct](CODE_OF_CONDUCT.md)
43
+ - [Detailed contributing guide](CONTRIBUTING.md)
44
+ - [Development setup guide](DEVELOPMENT.md)
45
+
21
46
  ## Basic usage
22
47
 
23
48
  Here's an example using rspec-core:
@@ -52,6 +77,7 @@ the example passes. If not, it fails with a message like:
52
77
  ```ruby
53
78
  expect(actual).to eq(expected) # passes if actual == expected
54
79
  expect(actual).to eql(expected) # passes if actual.eql?(expected)
80
+ expect(actual).not_to eql(not_expected) # passes if not(actual.eql?(expected))
55
81
  ```
56
82
 
57
83
  Note: The new `expect` syntax no longer supports the `==` matcher.
@@ -85,7 +111,7 @@ Note: The new `expect` syntax no longer supports the `=~` matcher.
85
111
 
86
112
  ```ruby
87
113
  expect(actual).to be_an_instance_of(expected) # passes if actual.class == expected
88
- expect(actual).to be_a(expected) # passes if actual.is_a?(expected)
114
+ expect(actual).to be_a(expected) # passes if actual.kind_of?(expected)
89
115
  expect(actual).to be_an(expected) # an alias for be_a
90
116
  expect(actual).to be_a_kind_of(expected) # another alias
91
117
  ```
@@ -93,11 +119,12 @@ expect(actual).to be_a_kind_of(expected) # another alias
93
119
  ### Truthiness
94
120
 
95
121
  ```ruby
96
- expect(actual).to be_truthy # passes if actual is truthy (not nil or false)
97
- expect(actual).to be true # passes if actual == true
98
- expect(actual).to be_falsy # passes if actual is falsy (nil or false)
99
- expect(actual).to be false # passes if actual == false
100
- expect(actual).to be_nil # passes if actual is nil
122
+ expect(actual).to be_truthy # passes if actual is truthy (not nil or false)
123
+ expect(actual).to be true # passes if actual == true
124
+ expect(actual).to be_falsy # passes if actual is falsy (nil or false)
125
+ expect(actual).to be false # passes if actual == false
126
+ expect(actual).to be_nil # passes if actual is nil
127
+ expect(actual).to_not be_nil # passes if actual is not nil
101
128
  ```
102
129
 
103
130
  ### Expecting errors
@@ -125,7 +152,7 @@ expect { |b| 5.tap(&b) }.to yield_control # passes regardless of yielded args
125
152
  expect { |b| yield_if_true(true, &b) }.to yield_with_no_args # passes only if no args are yielded
126
153
 
127
154
  expect { |b| 5.tap(&b) }.to yield_with_args(5)
128
- expect { |b| 5.tap(&b) }.to yield_with_args(Fixnum)
155
+ expect { |b| 5.tap(&b) }.to yield_with_args(Integer)
129
156
  expect { |b| "a string".tap(&b) }.to yield_with_args(/str/)
130
157
 
131
158
  expect { |b| [1, 2, 3].each(&b) }.to yield_successive_args(1, 2, 3)
@@ -148,30 +175,45 @@ expect(1..10).to cover(3)
148
175
  ### Collection membership
149
176
 
150
177
  ```ruby
151
- expect(actual).to include(expected)
178
+ # exact order, entire collection
179
+ expect(actual).to eq(expected)
180
+
181
+ # exact order, partial collection (based on an exact position)
152
182
  expect(actual).to start_with(expected)
153
183
  expect(actual).to end_with(expected)
154
184
 
155
- expect(actual).to contain_exactly(individual, items)
156
- # ...which is the same as:
157
- expect(actual).to match_array(expected_array)
185
+ # any order, entire collection
186
+ expect(actual).to match_array(expected)
187
+
188
+ # You can also express this by passing the expected elements
189
+ # as individual arguments
190
+ expect(actual).to contain_exactly(expected_element1, expected_element2)
191
+
192
+ # any order, partial collection
193
+ expect(actual).to include(expected)
158
194
  ```
159
195
 
160
196
  #### Examples
161
197
 
162
198
  ```ruby
163
- expect([1, 2, 3]).to include(1)
164
- expect([1, 2, 3]).to include(1, 2)
165
- expect([1, 2, 3]).to start_with(1)
166
- expect([1, 2, 3]).to start_with(1, 2)
167
- expect([1, 2, 3]).to end_with(3)
168
- expect([1, 2, 3]).to end_with(2, 3)
169
- expect({:a => 'b'}).to include(:a => 'b')
170
- expect("this string").to include("is str")
171
- expect("this string").to start_with("this")
172
- expect("this string").to end_with("ring")
173
- expect([1, 2, 3]).to contain_exactly(2, 3, 1)
174
- expect([1, 2, 3]).to match_array([3, 2, 1])
199
+ expect([1, 2, 3]).to eq([1, 2, 3]) # Order dependent equality check
200
+ expect([1, 2, 3]).to include(1) # Exact ordering, partial collection matches
201
+ expect([1, 2, 3]).to include(2, 3) #
202
+ expect([1, 2, 3]).to start_with(1) # As above, but from the start of the collection
203
+ expect([1, 2, 3]).to start_with(1, 2) #
204
+ expect([1, 2, 3]).to end_with(3) # As above but from the end of the collection
205
+ expect([1, 2, 3]).to end_with(2, 3) #
206
+ expect({:a => 'b'}).to include(:a => 'b') # Matching within hashes
207
+ expect("this string").to include("is str") # Matching within strings
208
+ expect("this string").to start_with("this") #
209
+ expect("this string").to end_with("ring") #
210
+ expect([1, 2, 3]).to contain_exactly(2, 3, 1) # Order independent matches
211
+ expect([1, 2, 3]).to match_array([3, 2, 1]) #
212
+
213
+ # Order dependent compound matchers
214
+ expect(
215
+ [{:a => 'hash'},{:a => 'another'}]
216
+ ).to match([a_hash_including(:a => 'hash'), a_hash_including(:a => 'another')])
175
217
  ```
176
218
 
177
219
  ## `should` syntax
@@ -185,7 +227,7 @@ actual.should be > 3
185
227
  [1, 2, 3].should_not include 4
186
228
  ```
187
229
 
188
- See [detailed information on the `should` syntax and its usage.](https://github.com/rspec/rspec-expectations/blob/master/Should.md)
230
+ See [detailed information on the `should` syntax and its usage.](https://github.com/rspec/rspec-expectations/blob/main/Should.md)
189
231
 
190
232
  ## Compound Matcher Expressions
191
233
 
@@ -238,7 +280,7 @@ expect(hash).to match(
238
280
  :a => {
239
281
  :b => a_collection_containing_exactly(
240
282
  a_string_starting_with("f"),
241
- an_instance_of(Fixnum)
283
+ an_instance_of(Integer)
242
284
  ),
243
285
  :c => { :d => (a_value < 3) }
244
286
  }
@@ -272,7 +314,7 @@ end
272
314
 
273
315
  ## Also see
274
316
 
275
- * [http://github.com/rspec/rspec](http://github.com/rspec/rspec)
276
- * [http://github.com/rspec/rspec-core](http://github.com/rspec/rspec-core)
277
- * [http://github.com/rspec/rspec-mocks](http://github.com/rspec/rspec-mocks)
278
- * [http://github.com/rspec/rspec-collection_matchers](https://github.com/rspec/rspec-collection_matchers)
317
+ * [https://github.com/rspec/rspec](https://github.com/rspec/rspec)
318
+ * [https://github.com/rspec/rspec-core](https://github.com/rspec/rspec-core)
319
+ * [https://github.com/rspec/rspec-mocks](https://github.com/rspec/rspec-mocks)
320
+ * [https://github.com/rspec/rspec-rails](https://github.com/rspec/rspec-rails)
@@ -0,0 +1,253 @@
1
+ module RSpec
2
+ module Expectations
3
+ # @private
4
+ class BlockSnippetExtractor # rubocop:disable Metrics/ClassLength
5
+ # rubocop should properly handle `Struct.new {}` as an inner class definition.
6
+
7
+ attr_reader :proc, :method_name
8
+
9
+ def self.try_extracting_single_line_body_of(proc, method_name)
10
+ lines = new(proc, method_name).body_content_lines
11
+ return nil unless lines.count == 1
12
+ lines.first
13
+ rescue Error
14
+ nil
15
+ end
16
+
17
+ def initialize(proc, method_name)
18
+ @proc = proc
19
+ @method_name = method_name.to_s.freeze
20
+ end
21
+
22
+ # Ideally we should properly handle indentations of multiline snippet,
23
+ # but it's not implemented yet since because we use result of this method only when it's a
24
+ # single line and implementing the logic introduces additional complexity.
25
+ def body_content_lines
26
+ raw_body_lines.map(&:strip).reject(&:empty?)
27
+ end
28
+
29
+ private
30
+
31
+ def raw_body_lines
32
+ raw_body_snippet.split("\n")
33
+ end
34
+
35
+ def raw_body_snippet
36
+ block_token_extractor.body_tokens.map(&:string).join
37
+ end
38
+
39
+ def block_token_extractor
40
+ @block_token_extractor ||= BlockTokenExtractor.new(method_name, source, beginning_line_number)
41
+ end
42
+
43
+ if RSpec.respond_to?(:world)
44
+ def source
45
+ raise TargetNotFoundError unless File.exist?(file_path)
46
+ RSpec.world.source_from_file(file_path)
47
+ end
48
+ else
49
+ RSpec::Support.require_rspec_support 'source'
50
+ def source
51
+ raise TargetNotFoundError unless File.exist?(file_path)
52
+ @source ||= RSpec::Support::Source.from_file(file_path)
53
+ end
54
+ end
55
+
56
+ def file_path
57
+ source_location.first
58
+ end
59
+
60
+ def beginning_line_number
61
+ source_location.last
62
+ end
63
+
64
+ def source_location
65
+ proc.source_location || raise(TargetNotFoundError)
66
+ end
67
+
68
+ Error = Class.new(StandardError)
69
+ TargetNotFoundError = Class.new(Error)
70
+ AmbiguousTargetError = Class.new(Error)
71
+
72
+ # @private
73
+ # Performs extraction of block body snippet using tokens,
74
+ # which cannot be done with node information.
75
+ BlockTokenExtractor = Struct.new(:method_name, :source, :beginning_line_number) do
76
+ attr_reader :state, :body_tokens
77
+
78
+ def initialize(*)
79
+ super
80
+ parse!
81
+ end
82
+
83
+ private
84
+
85
+ def parse!
86
+ @state = :initial
87
+
88
+ catch(:finish) do
89
+ source.tokens.each do |token|
90
+ invoke_state_handler(token)
91
+ end
92
+ end
93
+ end
94
+
95
+ def finish!
96
+ throw :finish
97
+ end
98
+
99
+ def invoke_state_handler(token)
100
+ __send__("#{state}_state", token)
101
+ end
102
+
103
+ def initial_state(token)
104
+ @state = :after_method_call if token.location == block_locator.method_call_location
105
+ end
106
+
107
+ def after_method_call_state(token)
108
+ @state = :after_opener if handle_opener_token(token)
109
+ end
110
+
111
+ def after_opener_state(token)
112
+ if handle_closer_token(token)
113
+ finish_or_find_next_block_if_incorrect!
114
+ elsif pipe_token?(token)
115
+ finalize_pending_tokens!
116
+ @state = :after_beginning_of_args
117
+ else
118
+ pending_tokens << token
119
+ handle_opener_token(token)
120
+ @state = :after_beginning_of_body unless token.type == :on_sp
121
+ end
122
+ end
123
+
124
+ def after_beginning_of_args_state(token)
125
+ @state = :after_beginning_of_body if pipe_token?(token)
126
+ end
127
+
128
+ def after_beginning_of_body_state(token)
129
+ if handle_closer_token(token)
130
+ finish_or_find_next_block_if_incorrect!
131
+ else
132
+ pending_tokens << token
133
+ handle_opener_token(token)
134
+ end
135
+ end
136
+
137
+ def pending_tokens
138
+ @pending_tokens ||= []
139
+ end
140
+
141
+ def finalize_pending_tokens!
142
+ pending_tokens.freeze.tap do
143
+ @pending_tokens = nil
144
+ end
145
+ end
146
+
147
+ def finish_or_find_next_block_if_incorrect!
148
+ body_tokens = finalize_pending_tokens!
149
+
150
+ if correct_block?(body_tokens)
151
+ @body_tokens = body_tokens
152
+ finish!
153
+ else
154
+ @state = :after_method_call
155
+ end
156
+ end
157
+
158
+ def handle_opener_token(token)
159
+ opener_token?(token).tap do |boolean|
160
+ opener_token_stack.push(token) if boolean
161
+ end
162
+ end
163
+
164
+ def opener_token?(token)
165
+ token.type == :on_lbrace || (token.type == :on_kw && token.string == 'do')
166
+ end
167
+
168
+ def handle_closer_token(token)
169
+ if opener_token_stack.last.closed_by?(token)
170
+ opener_token_stack.pop
171
+ opener_token_stack.empty?
172
+ else
173
+ false
174
+ end
175
+ end
176
+
177
+ def opener_token_stack
178
+ @opener_token_stack ||= []
179
+ end
180
+
181
+ def pipe_token?(token)
182
+ token.type == :on_op && token.string == '|'
183
+ end
184
+
185
+ def correct_block?(body_tokens)
186
+ return true if block_locator.body_content_locations.empty?
187
+ content_location = block_locator.body_content_locations.first
188
+ content_location.between?(body_tokens.first.location, body_tokens.last.location)
189
+ end
190
+
191
+ def block_locator
192
+ @block_locator ||= BlockLocator.new(method_name, source, beginning_line_number)
193
+ end
194
+ end
195
+
196
+ # @private
197
+ # Locates target block with node information (semantics), which tokens don't have.
198
+ BlockLocator = Struct.new(:method_name, :source, :beginning_line_number) do
199
+ def method_call_location
200
+ @method_call_location ||= method_ident_node.location
201
+ end
202
+
203
+ def body_content_locations
204
+ @body_content_locations ||= block_body_node.map(&:location).compact
205
+ end
206
+
207
+ private
208
+
209
+ def method_ident_node
210
+ method_call_node = block_wrapper_node.children.first
211
+ method_call_node.find do |node|
212
+ method_ident_node?(node)
213
+ end
214
+ end
215
+
216
+ def block_body_node
217
+ block_node = block_wrapper_node.children[1]
218
+ block_node.children.last
219
+ end
220
+
221
+ def block_wrapper_node
222
+ case candidate_block_wrapper_nodes.size
223
+ when 1
224
+ candidate_block_wrapper_nodes.first
225
+ when 0
226
+ raise TargetNotFoundError
227
+ else
228
+ raise AmbiguousTargetError
229
+ end
230
+ end
231
+
232
+ def candidate_block_wrapper_nodes
233
+ @candidate_block_wrapper_nodes ||= candidate_method_ident_nodes.map do |method_ident_node|
234
+ block_wrapper_node = method_ident_node.each_ancestor.find { |node| node.type == :method_add_block }
235
+ next nil unless block_wrapper_node
236
+ method_call_node = block_wrapper_node.children.first
237
+ method_call_node.include?(method_ident_node) ? block_wrapper_node : nil
238
+ end.compact
239
+ end
240
+
241
+ def candidate_method_ident_nodes
242
+ source.nodes_by_line_number[beginning_line_number].select do |node|
243
+ method_ident_node?(node)
244
+ end
245
+ end
246
+
247
+ def method_ident_node?(node)
248
+ node.type == :@ident && node.args.first == method_name
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end
@@ -18,6 +18,19 @@ module RSpec
18
18
  #
19
19
  # RSpec::Expectations.configuration
20
20
  class Configuration
21
+ # @private
22
+ FALSE_POSITIVE_BEHAVIOURS =
23
+ {
24
+ :warn => lambda { |message| RSpec.warning message },
25
+ :raise => lambda { |message| raise ArgumentError, message },
26
+ :nothing => lambda { |_| true },
27
+ }
28
+
29
+ def initialize
30
+ @on_potential_false_positives = :warn
31
+ @strict_predicate_matchers = false
32
+ end
33
+
21
34
  # Configures the supported syntax.
22
35
  # @param [Array<Symbol>, Symbol] values the syntaxes to enable
23
36
  # @example
@@ -44,6 +57,20 @@ module RSpec
44
57
  end
45
58
  end
46
59
 
60
+ # Configures the maximum character length that RSpec will print while
61
+ # formatting an object. You can set length to nil to prevent RSpec from
62
+ # doing truncation.
63
+ # @param [Fixnum] length the number of characters to limit the formatted output to.
64
+ # @example
65
+ # RSpec.configure do |rspec|
66
+ # rspec.expect_with :rspec do |c|
67
+ # c.max_formatted_output_length = 200
68
+ # end
69
+ # end
70
+ def max_formatted_output_length=(length)
71
+ RSpec::Support::ObjectFormatter.default_instance.max_formatted_output_length = length
72
+ end
73
+
47
74
  # The list of configured syntaxes.
48
75
  # @return [Array<Symbol>] the list of configured syntaxes.
49
76
  # @example
@@ -71,7 +98,7 @@ module RSpec
71
98
  # Delegates to rspec-core's color option if rspec-core
72
99
  # is loaded; otherwise you can set it here.
73
100
  def color?
74
- @color
101
+ defined?(@color) && @color
75
102
  end
76
103
  end
77
104
 
@@ -107,6 +134,18 @@ module RSpec
107
134
  end
108
135
  end
109
136
 
137
+ # Sets if custom matcher descriptions and failure messages
138
+ # should include clauses from methods defined using `chain`.
139
+ # @param value [Boolean]
140
+ attr_writer :include_chain_clauses_in_custom_matcher_descriptions
141
+
142
+ # Indicates whether or not custom matcher descriptions and failure messages
143
+ # should include clauses from methods defined using `chain`. It is
144
+ # false by default for backwards compatibility.
145
+ def include_chain_clauses_in_custom_matcher_descriptions?
146
+ @include_chain_clauses_in_custom_matcher_descriptions ||= false
147
+ end
148
+
110
149
  # @private
111
150
  def reset_syntaxes_to_default
112
151
  self.syntax = [:should, :expect]
@@ -121,6 +160,62 @@ module RSpec
121
160
  backtrace
122
161
  end
123
162
  end
163
+
164
+ # Configures whether RSpec will warn about matcher use which will
165
+ # potentially cause false positives in tests.
166
+ #
167
+ # @param [Boolean] boolean
168
+ def warn_about_potential_false_positives=(boolean)
169
+ if boolean
170
+ self.on_potential_false_positives = :warn
171
+ elsif warn_about_potential_false_positives?
172
+ self.on_potential_false_positives = :nothing
173
+ else
174
+ # no-op, handler is something else
175
+ end
176
+ end
177
+ #
178
+ # Configures what RSpec will do about matcher use which will
179
+ # potentially cause false positives in tests.
180
+ #
181
+ # @param [Symbol] behavior can be set to :warn, :raise or :nothing
182
+ def on_potential_false_positives=(behavior)
183
+ unless FALSE_POSITIVE_BEHAVIOURS.key?(behavior)
184
+ raise ArgumentError, "Supported values are: #{FALSE_POSITIVE_BEHAVIOURS.keys}"
185
+ end
186
+ @on_potential_false_positives = behavior
187
+ end
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
+
203
+ # Indicates what RSpec will do about matcher use which will
204
+ # potentially cause false positives in tests, generally you want to
205
+ # avoid such scenarios so this defaults to `true`.
206
+ attr_reader :on_potential_false_positives
207
+
208
+ # Indicates whether RSpec will warn about matcher use which will
209
+ # potentially cause false positives in tests, generally you want to
210
+ # avoid such scenarios so this defaults to `true`.
211
+ def warn_about_potential_false_positives?
212
+ on_potential_false_positives == :warn
213
+ end
214
+
215
+ # @private
216
+ def false_positives_handler
217
+ FALSE_POSITIVE_BEHAVIOURS.fetch(@on_potential_false_positives)
218
+ end
124
219
  end
125
220
 
126
221
  # The configuration object.