rspec-expectations 2.11.3 → 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.
Files changed (152) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +1 -1
  5. data/Changelog.md +1026 -21
  6. data/{License.txt → LICENSE.md} +5 -3
  7. data/README.md +174 -78
  8. data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
  9. data/lib/rspec/expectations/configuration.rb +230 -0
  10. data/lib/rspec/expectations/expectation_target.rb +130 -55
  11. data/lib/rspec/expectations/fail_with.rb +17 -33
  12. data/lib/rspec/expectations/failure_aggregator.rb +212 -0
  13. data/lib/rspec/expectations/handler.rb +163 -29
  14. data/lib/rspec/expectations/minitest_integration.rb +58 -0
  15. data/lib/rspec/expectations/syntax.rb +68 -54
  16. data/lib/rspec/expectations/version.rb +1 -1
  17. data/lib/rspec/expectations.rb +59 -24
  18. data/lib/rspec/matchers/aliased_matcher.rb +116 -0
  19. data/lib/rspec/matchers/built_in/all.rb +86 -0
  20. data/lib/rspec/matchers/built_in/base_matcher.rb +150 -20
  21. data/lib/rspec/matchers/built_in/be.rb +115 -109
  22. data/lib/rspec/matchers/built_in/be_between.rb +77 -0
  23. data/lib/rspec/matchers/built_in/be_instance_of.rb +16 -1
  24. data/lib/rspec/matchers/built_in/be_kind_of.rb +10 -1
  25. data/lib/rspec/matchers/built_in/be_within.rb +43 -17
  26. data/lib/rspec/matchers/built_in/change.rb +392 -75
  27. data/lib/rspec/matchers/built_in/compound.rb +290 -0
  28. data/lib/rspec/matchers/built_in/contain_exactly.rb +302 -0
  29. data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
  30. data/lib/rspec/matchers/built_in/cover.rb +3 -0
  31. data/lib/rspec/matchers/built_in/eq.rb +26 -8
  32. data/lib/rspec/matchers/built_in/eql.rb +19 -8
  33. data/lib/rspec/matchers/built_in/equal.rb +56 -19
  34. data/lib/rspec/matchers/built_in/exist.rb +74 -10
  35. data/lib/rspec/matchers/built_in/has.rb +141 -22
  36. data/lib/rspec/matchers/built_in/have_attributes.rb +114 -0
  37. data/lib/rspec/matchers/built_in/include.rb +175 -20
  38. data/lib/rspec/matchers/built_in/match.rb +95 -1
  39. data/lib/rspec/matchers/built_in/operators.rb +128 -0
  40. data/lib/rspec/matchers/built_in/output.rb +207 -0
  41. data/lib/rspec/matchers/built_in/raise_error.rb +212 -38
  42. data/lib/rspec/matchers/built_in/respond_to.rb +155 -29
  43. data/lib/rspec/matchers/built_in/satisfy.rb +39 -9
  44. data/lib/rspec/matchers/built_in/start_or_end_with.rb +94 -0
  45. data/lib/rspec/matchers/built_in/throw_symbol.rb +58 -14
  46. data/lib/rspec/matchers/built_in/yield.rb +252 -98
  47. data/lib/rspec/matchers/built_in.rb +47 -33
  48. data/lib/rspec/matchers/composable.rb +171 -0
  49. data/lib/rspec/matchers/dsl.rb +530 -10
  50. data/lib/rspec/matchers/english_phrasing.rb +58 -0
  51. data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +82 -0
  52. data/lib/rspec/matchers/fail_matchers.rb +42 -0
  53. data/lib/rspec/matchers/generated_descriptions.rb +15 -10
  54. data/lib/rspec/matchers/matcher_delegator.rb +35 -0
  55. data/lib/rspec/matchers/matcher_protocol.rb +105 -0
  56. data/lib/rspec/matchers.rb +604 -252
  57. data.tar.gz.sig +0 -0
  58. metadata +178 -278
  59. metadata.gz.sig +0 -0
  60. data/features/README.md +0 -49
  61. data/features/Upgrade.md +0 -53
  62. data/features/built_in_matchers/README.md +0 -90
  63. data/features/built_in_matchers/be.feature +0 -173
  64. data/features/built_in_matchers/be_within.feature +0 -46
  65. data/features/built_in_matchers/cover.feature +0 -45
  66. data/features/built_in_matchers/end_with.feature +0 -46
  67. data/features/built_in_matchers/equality.feature +0 -145
  68. data/features/built_in_matchers/exist.feature +0 -43
  69. data/features/built_in_matchers/expect_change.feature +0 -59
  70. data/features/built_in_matchers/expect_error.feature +0 -138
  71. data/features/built_in_matchers/have.feature +0 -103
  72. data/features/built_in_matchers/include.feature +0 -121
  73. data/features/built_in_matchers/match.feature +0 -50
  74. data/features/built_in_matchers/operators.feature +0 -221
  75. data/features/built_in_matchers/predicates.feature +0 -128
  76. data/features/built_in_matchers/respond_to.feature +0 -78
  77. data/features/built_in_matchers/satisfy.feature +0 -31
  78. data/features/built_in_matchers/start_with.feature +0 -46
  79. data/features/built_in_matchers/throw_symbol.feature +0 -85
  80. data/features/built_in_matchers/types.feature +0 -114
  81. data/features/built_in_matchers/yield.feature +0 -146
  82. data/features/custom_matchers/access_running_example.feature +0 -53
  83. data/features/custom_matchers/define_diffable_matcher.feature +0 -27
  84. data/features/custom_matchers/define_matcher.feature +0 -340
  85. data/features/custom_matchers/define_matcher_outside_rspec.feature +0 -38
  86. data/features/custom_matchers/define_matcher_with_fluent_interface.feature +0 -24
  87. data/features/customized_message.feature +0 -22
  88. data/features/diffing.feature +0 -85
  89. data/features/implicit_docstrings.feature +0 -52
  90. data/features/step_definitions/additional_cli_steps.rb +0 -22
  91. data/features/support/env.rb +0 -5
  92. data/features/syntax_configuration.feature +0 -68
  93. data/features/test_frameworks/test_unit.feature +0 -46
  94. data/lib/rspec/expectations/deprecation.rb +0 -38
  95. data/lib/rspec/expectations/differ.rb +0 -81
  96. data/lib/rspec/expectations/errors.rb +0 -9
  97. data/lib/rspec/expectations/extensions/array.rb +0 -9
  98. data/lib/rspec/expectations/extensions/object.rb +0 -39
  99. data/lib/rspec/expectations/extensions.rb +0 -2
  100. data/lib/rspec/matchers/be_close.rb +0 -9
  101. data/lib/rspec/matchers/built_in/have.rb +0 -108
  102. data/lib/rspec/matchers/built_in/match_array.rb +0 -45
  103. data/lib/rspec/matchers/built_in/start_and_end_with.rb +0 -48
  104. data/lib/rspec/matchers/compatibility.rb +0 -14
  105. data/lib/rspec/matchers/configuration.rb +0 -66
  106. data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
  107. data/lib/rspec/matchers/matcher.rb +0 -299
  108. data/lib/rspec/matchers/method_missing.rb +0 -12
  109. data/lib/rspec/matchers/operator_matcher.rb +0 -84
  110. data/lib/rspec/matchers/pretty.rb +0 -60
  111. data/lib/rspec-expectations.rb +0 -1
  112. data/spec/rspec/expectations/differ_spec.rb +0 -153
  113. data/spec/rspec/expectations/expectation_target_spec.rb +0 -65
  114. data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
  115. data/spec/rspec/expectations/fail_with_spec.rb +0 -70
  116. data/spec/rspec/expectations/handler_spec.rb +0 -206
  117. data/spec/rspec/matchers/base_matcher_spec.rb +0 -60
  118. data/spec/rspec/matchers/be_close_spec.rb +0 -22
  119. data/spec/rspec/matchers/be_instance_of_spec.rb +0 -40
  120. data/spec/rspec/matchers/be_kind_of_spec.rb +0 -37
  121. data/spec/rspec/matchers/be_spec.rb +0 -452
  122. data/spec/rspec/matchers/be_within_spec.rb +0 -80
  123. data/spec/rspec/matchers/change_spec.rb +0 -528
  124. data/spec/rspec/matchers/configuration_spec.rb +0 -202
  125. data/spec/rspec/matchers/cover_spec.rb +0 -69
  126. data/spec/rspec/matchers/description_generation_spec.rb +0 -176
  127. data/spec/rspec/matchers/dsl_spec.rb +0 -57
  128. data/spec/rspec/matchers/eq_spec.rb +0 -54
  129. data/spec/rspec/matchers/eql_spec.rb +0 -41
  130. data/spec/rspec/matchers/equal_spec.rb +0 -60
  131. data/spec/rspec/matchers/exist_spec.rb +0 -110
  132. data/spec/rspec/matchers/has_spec.rb +0 -118
  133. data/spec/rspec/matchers/have_spec.rb +0 -461
  134. data/spec/rspec/matchers/include_spec.rb +0 -367
  135. data/spec/rspec/matchers/match_array_spec.rb +0 -124
  136. data/spec/rspec/matchers/match_spec.rb +0 -61
  137. data/spec/rspec/matchers/matcher_spec.rb +0 -434
  138. data/spec/rspec/matchers/matchers_spec.rb +0 -31
  139. data/spec/rspec/matchers/method_missing_spec.rb +0 -24
  140. data/spec/rspec/matchers/operator_matcher_spec.rb +0 -221
  141. data/spec/rspec/matchers/raise_error_spec.rb +0 -344
  142. data/spec/rspec/matchers/respond_to_spec.rb +0 -295
  143. data/spec/rspec/matchers/satisfy_spec.rb +0 -44
  144. data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -182
  145. data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
  146. data/spec/rspec/matchers/yield_spec.rb +0 -402
  147. data/spec/spec_helper.rb +0 -27
  148. data/spec/support/classes.rb +0 -56
  149. data/spec/support/in_sub_process.rb +0 -31
  150. data/spec/support/matchers.rb +0 -22
  151. data/spec/support/ruby_version.rb +0 -10
  152. data/spec/support/shared_examples.rb +0 -13
@@ -0,0 +1,212 @@
1
+ module RSpec
2
+ module Expectations
3
+ # @private
4
+ class FailureAggregator
5
+ attr_reader :block_label, :metadata
6
+
7
+ def aggregate
8
+ RSpec::Support.with_failure_notifier(self) do
9
+ begin
10
+ yield
11
+ rescue ExpectationNotMetError => e
12
+ # Normally, expectation failures will be notified via the `call` method, below,
13
+ # but since the failure notifier uses a thread local variable, failing expectations
14
+ # in another thread will still raise. We handle that here and categorize it as part
15
+ # of `failures` rather than letting it fall through and be categorized as part of
16
+ # `other_errors`.
17
+ failures << e
18
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
19
+ # While it is normally a bad practice to rescue `Exception`, it's important we do
20
+ # so here. It's low risk (`notify_aggregated_failures` below will re-raise the exception,
21
+ # or raise a `MultipleExpectationsNotMetError` that includes the exception), and it's
22
+ # essential that the user is notified of expectation failures that may have already
23
+ # occurred in the `aggregate_failures` block. Those expectation failures may provide
24
+ # important diagnostics for understanding why this exception occurred, and if we simply
25
+ # allowed this exception to be raised as-is, it would (wrongly) suggest to the user
26
+ # that the expectation passed when it did not, which would be quite confusing.
27
+ other_errors << e
28
+ end
29
+ end
30
+
31
+ notify_aggregated_failures
32
+ end
33
+
34
+ def failures
35
+ @failures ||= []
36
+ end
37
+
38
+ def other_errors
39
+ @other_errors ||= []
40
+ end
41
+
42
+ # This method is defined to satisfy the callable interface
43
+ # expected by `RSpec::Support.with_failure_notifier`.
44
+ def call(failure, options)
45
+ source_id = options[:source_id]
46
+ return if source_id && @seen_source_ids.key?(source_id)
47
+
48
+ @seen_source_ids[source_id] = true
49
+ assign_backtrace(failure) unless failure.backtrace
50
+ failures << failure
51
+ end
52
+
53
+ private
54
+
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
+ # so that rspec-core's truncation logic can work properly on it to list the backtrace
60
+ # relative to the `aggregate_failures` block.
61
+ def assign_backtrace(failure)
62
+ raise failure
63
+ rescue failure.class => e
64
+ failure.set_backtrace(e.backtrace)
65
+ end
66
+ else
67
+ # Using `caller` performs better (and is simpler) than `raise` on most Rubies.
68
+ def assign_backtrace(failure)
69
+ failure.set_backtrace(caller)
70
+ end
71
+ end
72
+
73
+ def initialize(block_label, metadata)
74
+ @block_label = block_label
75
+ @metadata = metadata
76
+ @seen_source_ids = {} # don't want to load stdlib set
77
+ end
78
+
79
+ def notify_aggregated_failures
80
+ all_errors = failures + other_errors
81
+
82
+ case all_errors.size
83
+ when 0 then return true
84
+ when 1 then RSpec::Support.notify_failure all_errors.first
85
+ else RSpec::Support.notify_failure MultipleExpectationsNotMetError.new(self)
86
+ end
87
+ end
88
+ end
89
+
90
+ # Exception raised from `aggregate_failures` when multiple expectations fail.
91
+ class MultipleExpectationsNotMetError
92
+ # @return [String] The fully formatted exception message.
93
+ def message
94
+ @message ||= (["#{summary}:"] + enumerated_failures + enumerated_errors).join("\n\n")
95
+ end
96
+
97
+ # @return [Array<RSpec::Expectations::ExpectationNotMetError>] The list of expectation failures.
98
+ def failures
99
+ @failure_aggregator.failures
100
+ end
101
+
102
+ # @return [Array<Exception>] The list of other exceptions.
103
+ def other_errors
104
+ @failure_aggregator.other_errors
105
+ end
106
+
107
+ # @return [Array<Exception>] The list of expectation failures and other exceptions, combined.
108
+ attr_reader :all_exceptions
109
+
110
+ # @return [String] The user-assigned label for the aggregation block.
111
+ def aggregation_block_label
112
+ @failure_aggregator.block_label
113
+ end
114
+
115
+ # @return [Hash] The metadata hash passed to `aggregate_failures`.
116
+ def aggregation_metadata
117
+ @failure_aggregator.metadata
118
+ end
119
+
120
+ # @return [String] A summary of the failure, including the block label and a count of failures.
121
+ def summary
122
+ "Got #{exception_count_description} from failure aggregation " \
123
+ "block#{block_description}"
124
+ end
125
+
126
+ # return [String] A description of the failure/error counts.
127
+ def exception_count_description
128
+ failure_count = pluralize("failure", failures.size)
129
+ return failure_count if other_errors.empty?
130
+ error_count = pluralize("other error", other_errors.size)
131
+ "#{failure_count} and #{error_count}"
132
+ end
133
+
134
+ private
135
+
136
+ def initialize(failure_aggregator)
137
+ @failure_aggregator = failure_aggregator
138
+ @all_exceptions = failures + other_errors
139
+ end
140
+
141
+ def block_description
142
+ return "" unless aggregation_block_label
143
+ " #{aggregation_block_label.inspect}"
144
+ end
145
+
146
+ def pluralize(noun, count)
147
+ "#{count} #{noun}#{'s' unless count == 1}"
148
+ end
149
+
150
+ def enumerated(exceptions, index_offset)
151
+ exceptions.each_with_index.map do |exception, index|
152
+ index += index_offset
153
+ formatted_message = "#{yield exception}\n#{format_backtrace(exception.backtrace).first}"
154
+ "#{index_label index}#{indented formatted_message, index}"
155
+ end
156
+ end
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
+
176
+ def enumerated_failures
177
+ enumerated(failures, 0, &:message)
178
+ end
179
+
180
+ def enumerated_errors
181
+ enumerated(other_errors, failures.size) do |error|
182
+ "#{error.class}: #{error.message}"
183
+ end
184
+ end
185
+
186
+ def indented(failure_message, index)
187
+ line_1, *rest = failure_message.strip.lines.to_a
188
+ first_line_indentation = ' ' * (longest_index_label_width - width_of_label(index))
189
+
190
+ first_line_indentation + line_1 + rest.map do |line|
191
+ line =~ /\S/ ? indentation + line : line
192
+ end.join
193
+ end
194
+
195
+ def indentation
196
+ @indentation ||= ' ' * longest_index_label_width
197
+ end
198
+
199
+ def longest_index_label_width
200
+ @longest_index_label_width ||= width_of_label(failures.size)
201
+ end
202
+
203
+ def width_of_label(index)
204
+ index_label(index).chars.count
205
+ end
206
+
207
+ def index_label(index)
208
+ " #{index + 1}) "
209
+ end
210
+ end
211
+ end
212
+ end
@@ -1,18 +1,39 @@
1
1
  module RSpec
2
2
  module Expectations
3
- class PositiveExpectationHandler
4
- def self.handle_matcher(actual, matcher, message=nil, &block)
5
- ::RSpec::Matchers.last_should = :should
3
+ # @private
4
+ module ExpectationHelper
5
+ def self.check_message(msg)
6
+ unless msg.nil? || msg.respond_to?(:to_str) || msg.respond_to?(:call)
7
+ ::Kernel.warn [
8
+ "WARNING: ignoring the provided expectation message argument (",
9
+ msg.inspect,
10
+ ") since it is not a string or a proc."
11
+ ].join
12
+ end
13
+ end
14
+
15
+ # Returns an RSpec-3+ compatible matcher, wrapping a legacy one
16
+ # in an adapter if necessary.
17
+ #
18
+ # @private
19
+ def self.modern_matcher_from(matcher)
20
+ LegacyMatcherAdapter::RSpec2.wrap(matcher) ||
21
+ LegacyMatcherAdapter::RSpec1.wrap(matcher) || matcher
22
+ end
23
+
24
+ def self.with_matcher(handler, matcher, message)
25
+ check_message(message)
26
+ matcher = modern_matcher_from(matcher)
27
+ yield matcher
28
+ ensure
29
+ ::RSpec::Matchers.last_expectation_handler = handler
6
30
  ::RSpec::Matchers.last_matcher = matcher
7
- return ::RSpec::Matchers::BuiltIn::PositiveOperatorMatcher.new(actual) if matcher.nil?
8
-
9
- match = matcher.matches?(actual, &block)
10
- return match if match
11
-
12
- message ||= matcher.respond_to?(:failure_message_for_should) ?
13
- matcher.failure_message_for_should :
14
- matcher.failure_message
15
-
31
+ end
32
+
33
+ def self.handle_failure(matcher, message, failure_message_method)
34
+ message = message.call if message.respond_to?(:call)
35
+ message ||= matcher.__send__(failure_message_method)
36
+
16
37
  if matcher.respond_to?(:diffable?) && matcher.diffable?
17
38
  ::RSpec::Expectations.fail_with message, matcher.expected, matcher.actual
18
39
  else
@@ -21,28 +42,141 @@ module RSpec
21
42
  end
22
43
  end
23
44
 
45
+ # @private
46
+ class PositiveExpectationHandler
47
+ def self.handle_matcher(actual, initial_matcher, custom_message=nil, &block)
48
+ ExpectationHelper.with_matcher(self, initial_matcher, custom_message) do |matcher|
49
+ return ::RSpec::Matchers::BuiltIn::PositiveOperatorMatcher.new(actual) unless initial_matcher
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)
57
+ end
58
+ end
59
+
60
+ def self.verb
61
+ 'is expected to'
62
+ end
63
+
64
+ def self.should_method
65
+ :should
66
+ end
67
+
68
+ def self.opposite_should_method
69
+ :should_not
70
+ end
71
+ end
72
+
73
+ # @private
24
74
  class NegativeExpectationHandler
25
- def self.handle_matcher(actual, matcher, message=nil, &block)
26
- ::RSpec::Matchers.last_should = :should_not
27
- ::RSpec::Matchers.last_matcher = matcher
28
- return ::RSpec::Matchers::BuiltIn::NegativeOperatorMatcher.new(actual) if matcher.nil?
29
-
30
- match = matcher.respond_to?(:does_not_match?) ?
31
- !matcher.does_not_match?(actual, &block) :
32
- matcher.matches?(actual, &block)
33
- return match unless match
34
-
35
- message ||= matcher.respond_to?(:failure_message_for_should_not) ?
36
- matcher.failure_message_for_should_not :
37
- matcher.negative_failure_message
75
+ def self.handle_matcher(actual, initial_matcher, custom_message=nil, &block)
76
+ ExpectationHelper.with_matcher(self, initial_matcher, custom_message) do |matcher|
77
+ return ::RSpec::Matchers::BuiltIn::NegativeOperatorMatcher.new(actual) unless initial_matcher
38
78
 
39
- if matcher.respond_to?(:diffable?) && matcher.diffable?
40
- ::RSpec::Expectations.fail_with message, matcher.expected, matcher.actual
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)
85
+ end
86
+ end
87
+
88
+ def self.does_not_match?(matcher, actual, &block)
89
+ if matcher.respond_to?(:does_not_match?)
90
+ matcher.does_not_match?(actual, &block)
41
91
  else
42
- ::RSpec::Expectations.fail_with message
92
+ !matcher.matches?(actual, &block)
43
93
  end
44
94
  end
95
+
96
+ def self.verb
97
+ 'is expected not to'
98
+ end
99
+
100
+ def self.should_method
101
+ :should_not
102
+ end
103
+
104
+ def self.opposite_should_method
105
+ :should
106
+ end
45
107
  end
108
+
109
+ # Wraps a matcher written against one of the legacy protocols in
110
+ # order to present the current protocol.
111
+ #
112
+ # @private
113
+ class LegacyMatcherAdapter < Matchers::MatcherDelegator
114
+ def initialize(matcher)
115
+ super
116
+ ::RSpec.warn_deprecation(<<-EOS.gsub(/^\s+\|/, ''), :type => "legacy_matcher")
117
+ |#{matcher.class.name || matcher.inspect} implements a legacy RSpec matcher
118
+ |protocol. For the current protocol you should expose the failure messages
119
+ |via the `failure_message` and `failure_message_when_negated` methods.
120
+ |(Used from #{CallerFilter.first_non_rspec_line})
121
+ EOS
122
+ end
123
+
124
+ def self.wrap(matcher)
125
+ new(matcher) if interface_matches?(matcher)
126
+ end
127
+
128
+ # Starting in RSpec 1.2 (and continuing through all 2.x releases),
129
+ # the failure message protocol was:
130
+ # * `failure_message_for_should`
131
+ # * `failure_message_for_should_not`
132
+ # @private
133
+ class RSpec2 < self
134
+ def failure_message
135
+ base_matcher.failure_message_for_should
136
+ end
137
+
138
+ def failure_message_when_negated
139
+ base_matcher.failure_message_for_should_not
140
+ end
141
+
142
+ def self.interface_matches?(matcher)
143
+ (
144
+ !matcher.respond_to?(:failure_message) &&
145
+ matcher.respond_to?(:failure_message_for_should)
146
+ ) || (
147
+ !matcher.respond_to?(:failure_message_when_negated) &&
148
+ matcher.respond_to?(:failure_message_for_should_not)
149
+ )
150
+ end
151
+ end
152
+
153
+ # Before RSpec 1.2, the failure message protocol was:
154
+ # * `failure_message`
155
+ # * `negative_failure_message`
156
+ # @private
157
+ class RSpec1 < self
158
+ def failure_message
159
+ base_matcher.failure_message
160
+ end
161
+
162
+ def failure_message_when_negated
163
+ base_matcher.negative_failure_message
164
+ end
165
+
166
+ # Note: `failure_message` is part of the RSpec 3 protocol
167
+ # (paired with `failure_message_when_negated`), so we don't check
168
+ # for `failure_message` here.
169
+ def self.interface_matches?(matcher)
170
+ !matcher.respond_to?(:failure_message_when_negated) &&
171
+ matcher.respond_to?(:negative_failure_message)
172
+ end
173
+ end
174
+ end
175
+
176
+ # RSpec 3.0 was released with the class name misspelled. For SemVer compatibility,
177
+ # we will provide this misspelled alias until 4.0.
178
+ # @deprecated Use LegacyMatcherAdapter instead.
179
+ # @private
180
+ LegacyMacherAdapter = LegacyMatcherAdapter
46
181
  end
47
182
  end
48
-
@@ -0,0 +1,58 @@
1
+ require 'rspec/expectations'
2
+
3
+ Minitest::Test.class_eval do
4
+ include ::RSpec::Matchers
5
+
6
+ # This `expect` will only be called if the user is using Minitest < 5.6
7
+ # or if they are _not_ using Minitest::Spec on 5.6+. Minitest::Spec on 5.6+
8
+ # defines its own `expect` and will have the assertions incremented via our
9
+ # definitions of `to`/`not_to`/`to_not` below.
10
+ def expect(*a, &b)
11
+ self.assertions += 1
12
+ super
13
+ end
14
+
15
+ # Convert a `MultipleExpectationsNotMetError` to a `Minitest::Assertion` error so
16
+ # it gets counted in minitest's summary stats as a failure rather than an error.
17
+ # It would be nice to make `MultipleExpectationsNotMetError` subclass
18
+ # `Minitest::Assertion`, but Minitest's implementation does not treat subclasses
19
+ # the same, so this is the best we can do.
20
+ def aggregate_failures(*args, &block)
21
+ super
22
+ rescue RSpec::Expectations::MultipleExpectationsNotMetError => e
23
+ assertion_failed = Minitest::Assertion.new(e.message)
24
+ assertion_failed.set_backtrace e.backtrace
25
+ raise assertion_failed
26
+ end
27
+ end
28
+
29
+ # Older versions of Minitest (e.g. before 5.6) do not define
30
+ # `Minitest::Expectation`.
31
+ if defined?(::Minitest::Expectation)
32
+ Minitest::Expectation.class_eval do
33
+ include RSpec::Expectations::ExpectationTarget::InstanceMethods
34
+
35
+ def to(*args)
36
+ ctx.assertions += 1
37
+ super
38
+ end
39
+
40
+ def not_to(*args)
41
+ ctx.assertions += 1
42
+ super
43
+ end
44
+
45
+ def to_not(*args)
46
+ ctx.assertions += 1
47
+ super
48
+ end
49
+ end
50
+ end
51
+
52
+ module RSpec
53
+ module Expectations
54
+ remove_const :ExpectationNotMetError
55
+ # Exception raised when an expectation fails.
56
+ const_set :ExpectationNotMetError, ::Minitest::Assertion
57
+ end
58
+ end
@@ -4,38 +4,7 @@ module RSpec
4
4
  # Provides methods for enabling and disabling the available
5
5
  # syntaxes provided by rspec-expectations.
6
6
  module Syntax
7
- extend self
8
-
9
- # @method should
10
- # Passes if `matcher` returns true. Available on every `Object`.
11
- # @example
12
- # actual.should eq expected
13
- # actual.should match /expression/
14
- # @param [Matcher]
15
- # matcher
16
- # @param [String] message optional message to display when the expectation fails
17
- # @return [Boolean] true if the expectation succeeds (else raises)
18
- # @see RSpec::Matchers
19
-
20
- # @method should_not
21
- # Passes if `matcher` returns false. Available on every `Object`.
22
- # @example
23
- # actual.should_not eq expected
24
- # @param [Matcher]
25
- # matcher
26
- # @param [String] message optional message to display when the expectation fails
27
- # @return [Boolean] false if the negative expectation succeeds (else raises)
28
- # @see RSpec::Matchers
29
-
30
- # @method expect
31
- # Supports `expect(actual).to matcher` syntax by wrapping `actual` in an
32
- # `ExpectationTarget`.
33
- # @example
34
- # expect(actual).to eq(expected)
35
- # expect(actual).to_not eq(expected)
36
- # @return [ExpectationTarget]
37
- # @see ExpectationTarget#to
38
- # @see ExpectationTarget#to_not
7
+ module_function
39
8
 
40
9
  # @api private
41
10
  # Determines where we add `should` and `should_not`.
@@ -43,76 +12,121 @@ module RSpec
43
12
  @default_should_host ||= ::Object.ancestors.last
44
13
  end
45
14
 
15
+ # @api private
16
+ # Instructs rspec-expectations to warn on first usage of `should` or `should_not`.
17
+ # Enabled by default. This is largely here to facilitate testing.
18
+ def warn_about_should!
19
+ @warn_about_should = true
20
+ end
21
+
22
+ # @api private
23
+ # Generates a deprecation warning for the given method if no warning
24
+ # has already been issued.
25
+ def warn_about_should_unless_configured(method_name)
26
+ return unless @warn_about_should
27
+
28
+ RSpec.deprecate(
29
+ "Using `#{method_name}` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax",
30
+ :replacement => "the new `:expect` syntax or explicitly enable `:should` with `config.expect_with(:rspec) { |c| c.syntax = :should }`"
31
+ )
32
+
33
+ @warn_about_should = false
34
+ end
35
+
46
36
  # @api private
47
37
  # Enables the `should` syntax.
48
- def enable_should(syntax_host = default_should_host)
38
+ def enable_should(syntax_host=default_should_host)
39
+ @warn_about_should = false if syntax_host == default_should_host
49
40
  return if should_enabled?(syntax_host)
50
41
 
51
- syntax_host.module_eval do
42
+ syntax_host.module_exec do
52
43
  def should(matcher=nil, message=nil, &block)
44
+ ::RSpec::Expectations::Syntax.warn_about_should_unless_configured(::Kernel.__method__)
53
45
  ::RSpec::Expectations::PositiveExpectationHandler.handle_matcher(self, matcher, message, &block)
54
46
  end
55
47
 
56
48
  def should_not(matcher=nil, message=nil, &block)
49
+ ::RSpec::Expectations::Syntax.warn_about_should_unless_configured(::Kernel.__method__)
57
50
  ::RSpec::Expectations::NegativeExpectationHandler.handle_matcher(self, matcher, message, &block)
58
51
  end
59
52
  end
60
-
61
- ::RSpec::Expectations::ExpectationTarget.enable_deprecated_should if expect_enabled?
62
53
  end
63
54
 
64
55
  # @api private
65
56
  # Disables the `should` syntax.
66
- def disable_should(syntax_host = default_should_host)
57
+ def disable_should(syntax_host=default_should_host)
67
58
  return unless should_enabled?(syntax_host)
68
59
 
69
- syntax_host.module_eval do
60
+ syntax_host.module_exec do
70
61
  undef should
71
62
  undef should_not
72
63
  end
73
-
74
- ::RSpec::Expectations::ExpectationTarget.disable_deprecated_should
75
64
  end
76
65
 
77
66
  # @api private
78
67
  # Enables the `expect` syntax.
79
- def enable_expect(syntax_host = ::RSpec::Matchers)
68
+ def enable_expect(syntax_host=::RSpec::Matchers)
80
69
  return if expect_enabled?(syntax_host)
81
70
 
82
- syntax_host.module_eval do
83
- def expect(*target, &target_block)
84
- target << target_block if block_given?
85
- raise ArgumentError.new("You must pass an argument or a block to #expect but not both.") unless target.size == 1
86
- ::RSpec::Expectations::ExpectationTarget.new(target.first)
71
+ syntax_host.module_exec do
72
+ def expect(value=::RSpec::Expectations::ExpectationTarget::UndefinedValue, &block)
73
+ ::RSpec::Expectations::ExpectationTarget.for(value, block)
87
74
  end
88
75
  end
89
-
90
- ::RSpec::Expectations::ExpectationTarget.enable_deprecated_should if should_enabled?
91
76
  end
92
77
 
93
78
  # @api private
94
79
  # Disables the `expect` syntax.
95
- def disable_expect(syntax_host = ::RSpec::Matchers)
80
+ def disable_expect(syntax_host=::RSpec::Matchers)
96
81
  return unless expect_enabled?(syntax_host)
97
82
 
98
- syntax_host.module_eval do
83
+ syntax_host.module_exec do
99
84
  undef expect
100
85
  end
101
-
102
- ::RSpec::Expectations::ExpectationTarget.disable_deprecated_should
103
86
  end
104
87
 
105
88
  # @api private
106
89
  # Indicates whether or not the `should` syntax is enabled.
107
- def should_enabled?(syntax_host = default_should_host)
90
+ def should_enabled?(syntax_host=default_should_host)
108
91
  syntax_host.method_defined?(:should)
109
92
  end
110
93
 
111
94
  # @api private
112
95
  # Indicates whether or not the `expect` syntax is enabled.
113
- def expect_enabled?(syntax_host = ::RSpec::Matchers)
96
+ def expect_enabled?(syntax_host=::RSpec::Matchers)
114
97
  syntax_host.method_defined?(:expect)
115
98
  end
116
99
  end
117
100
  end
118
101
  end
102
+
103
+ if defined?(BasicObject)
104
+ # The legacy `:should` syntax adds the following methods directly to
105
+ # `BasicObject` so that they are available off of any object. Note, however,
106
+ # that this syntax does not always play nice with delegate/proxy objects.
107
+ # We recommend you use the non-monkeypatching `:expect` syntax instead.
108
+ class BasicObject
109
+ # @method should(matcher, message)
110
+ # Passes if `matcher` returns true. Available on every `Object`.
111
+ # @example
112
+ # actual.should eq expected
113
+ # actual.should match /expression/
114
+ # @param [Matcher]
115
+ # matcher
116
+ # @param [String] message optional message to display when the expectation fails
117
+ # @return [Boolean] true if the expectation succeeds (else raises)
118
+ # @note This is only available when you have enabled the `:should` syntax.
119
+ # @see RSpec::Matchers
120
+
121
+ # @method should_not(matcher, message)
122
+ # Passes if `matcher` returns false. Available on every `Object`.
123
+ # @example
124
+ # actual.should_not eq expected
125
+ # @param [Matcher]
126
+ # matcher
127
+ # @param [String] message optional message to display when the expectation fails
128
+ # @return [Boolean] false if the negative expectation succeeds (else raises)
129
+ # @note This is only available when you have enabled the `:should` syntax.
130
+ # @see RSpec::Matchers
131
+ end
132
+ end
@@ -2,7 +2,7 @@ module RSpec
2
2
  module Expectations
3
3
  # @private
4
4
  module Version
5
- STRING = '2.11.3'
5
+ STRING = '3.11.0'
6
6
  end
7
7
  end
8
8
  end