rspec-expectations 2.14.0 → 3.13.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 (155) 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 +976 -25
  6. data/{License.txt → LICENSE.md} +5 -3
  7. data/README.md +162 -26
  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 +127 -51
  11. data/lib/rspec/expectations/fail_with.rb +17 -57
  12. data/lib/rspec/expectations/failure_aggregator.rb +229 -0
  13. data/lib/rspec/expectations/handler.rb +146 -32
  14. data/lib/rspec/expectations/minitest_integration.rb +58 -0
  15. data/lib/rspec/expectations/syntax.rb +68 -100
  16. data/lib/rspec/expectations/version.rb +1 -1
  17. data/lib/rspec/expectations.rb +58 -23
  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 +191 -20
  21. data/lib/rspec/matchers/built_in/be.rb +114 -114
  22. data/lib/rspec/matchers/built_in/be_between.rb +77 -0
  23. data/lib/rspec/matchers/built_in/be_instance_of.rb +15 -4
  24. data/lib/rspec/matchers/built_in/be_kind_of.rb +10 -1
  25. data/lib/rspec/matchers/built_in/be_within.rb +35 -18
  26. data/lib/rspec/matchers/built_in/change.rb +389 -80
  27. data/lib/rspec/matchers/built_in/compound.rb +290 -0
  28. data/lib/rspec/matchers/built_in/contain_exactly.rb +310 -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 +30 -8
  32. data/lib/rspec/matchers/built_in/eql.rb +23 -8
  33. data/lib/rspec/matchers/built_in/equal.rb +55 -22
  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 +184 -32
  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 +192 -44
  42. data/lib/rspec/matchers/built_in/respond_to.rb +154 -28
  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 +240 -161
  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 +531 -10
  50. data/lib/rspec/matchers/english_phrasing.rb +58 -0
  51. data/lib/rspec/matchers/fail_matchers.rb +42 -0
  52. data/lib/rspec/matchers/generated_descriptions.rb +14 -8
  53. data/lib/rspec/matchers/matcher_delegator.rb +61 -0
  54. data/lib/rspec/matchers/matcher_protocol.rb +105 -0
  55. data/lib/rspec/matchers/multi_matcher_diff.rb +82 -0
  56. data/lib/rspec/matchers.rb +520 -173
  57. data.tar.gz.sig +0 -0
  58. metadata +141 -242
  59. metadata.gz.sig +2 -0
  60. data/features/README.md +0 -48
  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 -175
  64. data/features/built_in_matchers/be_within.feature +0 -48
  65. data/features/built_in_matchers/cover.feature +0 -47
  66. data/features/built_in_matchers/end_with.feature +0 -48
  67. data/features/built_in_matchers/equality.feature +0 -139
  68. data/features/built_in_matchers/exist.feature +0 -45
  69. data/features/built_in_matchers/expect_change.feature +0 -59
  70. data/features/built_in_matchers/expect_error.feature +0 -144
  71. data/features/built_in_matchers/have.feature +0 -109
  72. data/features/built_in_matchers/include.feature +0 -174
  73. data/features/built_in_matchers/match.feature +0 -52
  74. data/features/built_in_matchers/operators.feature +0 -227
  75. data/features/built_in_matchers/predicates.feature +0 -137
  76. data/features/built_in_matchers/respond_to.feature +0 -84
  77. data/features/built_in_matchers/satisfy.feature +0 -33
  78. data/features/built_in_matchers/start_with.feature +0 -48
  79. data/features/built_in_matchers/throw_symbol.feature +0 -91
  80. data/features/built_in_matchers/types.feature +0 -116
  81. data/features/built_in_matchers/yield.feature +0 -161
  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 -368
  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 -14
  92. data/features/syntax_configuration.feature +0 -71
  93. data/features/test_frameworks/test_unit.feature +0 -44
  94. data/lib/rspec/expectations/deprecation.rb +0 -17
  95. data/lib/rspec/expectations/differ.rb +0 -133
  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 -29
  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 -124
  102. data/lib/rspec/matchers/built_in/match_array.rb +0 -51
  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 -108
  106. data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
  107. data/lib/rspec/matchers/matcher.rb +0 -300
  108. data/lib/rspec/matchers/method_missing.rb +0 -12
  109. data/lib/rspec/matchers/operator_matcher.rb +0 -109
  110. data/lib/rspec/matchers/pretty.rb +0 -70
  111. data/lib/rspec/matchers/test_unit_integration.rb +0 -11
  112. data/lib/rspec-expectations.rb +0 -1
  113. data/spec/rspec/expectations/differ_spec.rb +0 -192
  114. data/spec/rspec/expectations/expectation_target_spec.rb +0 -82
  115. data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
  116. data/spec/rspec/expectations/fail_with_spec.rb +0 -114
  117. data/spec/rspec/expectations/handler_spec.rb +0 -227
  118. data/spec/rspec/expectations/syntax_spec.rb +0 -139
  119. data/spec/rspec/matchers/base_matcher_spec.rb +0 -62
  120. data/spec/rspec/matchers/be_close_spec.rb +0 -22
  121. data/spec/rspec/matchers/be_instance_of_spec.rb +0 -63
  122. data/spec/rspec/matchers/be_kind_of_spec.rb +0 -41
  123. data/spec/rspec/matchers/be_spec.rb +0 -516
  124. data/spec/rspec/matchers/be_within_spec.rb +0 -137
  125. data/spec/rspec/matchers/change_spec.rb +0 -553
  126. data/spec/rspec/matchers/configuration_spec.rb +0 -206
  127. data/spec/rspec/matchers/cover_spec.rb +0 -69
  128. data/spec/rspec/matchers/description_generation_spec.rb +0 -190
  129. data/spec/rspec/matchers/dsl_spec.rb +0 -57
  130. data/spec/rspec/matchers/eq_spec.rb +0 -60
  131. data/spec/rspec/matchers/eql_spec.rb +0 -41
  132. data/spec/rspec/matchers/equal_spec.rb +0 -78
  133. data/spec/rspec/matchers/exist_spec.rb +0 -124
  134. data/spec/rspec/matchers/has_spec.rb +0 -122
  135. data/spec/rspec/matchers/have_spec.rb +0 -455
  136. data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
  137. data/spec/rspec/matchers/include_spec.rb +0 -531
  138. data/spec/rspec/matchers/match_array_spec.rb +0 -194
  139. data/spec/rspec/matchers/match_spec.rb +0 -61
  140. data/spec/rspec/matchers/matcher_spec.rb +0 -471
  141. data/spec/rspec/matchers/matchers_spec.rb +0 -37
  142. data/spec/rspec/matchers/method_missing_spec.rb +0 -28
  143. data/spec/rspec/matchers/operator_matcher_spec.rb +0 -223
  144. data/spec/rspec/matchers/raise_error_spec.rb +0 -485
  145. data/spec/rspec/matchers/respond_to_spec.rb +0 -292
  146. data/spec/rspec/matchers/satisfy_spec.rb +0 -44
  147. data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -186
  148. data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
  149. data/spec/rspec/matchers/yield_spec.rb +0 -514
  150. data/spec/spec_helper.rb +0 -54
  151. data/spec/support/classes.rb +0 -56
  152. data/spec/support/in_sub_process.rb +0 -38
  153. data/spec/support/matchers.rb +0 -22
  154. data/spec/support/ruby_version.rb +0 -10
  155. data/spec/support/shared_examples.rb +0 -13
@@ -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).not_to eq(expected)
36
- # @return [ExpectationTarget]
37
- # @see ExpectationTarget#to
38
- # @see ExpectationTarget#not_to
7
+ module_function
39
8
 
40
9
  # @api private
41
10
  # Determines where we add `should` and `should_not`.
@@ -43,122 +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
-
117
- # @api private
118
- # Generates a positive expectation expression.
119
- def positive_expression(target_expression, matcher_expression)
120
- expression_generator.positive_expression(target_expression, matcher_expression)
121
- end
122
-
123
- # @api private
124
- # Generates a negative expectation expression.
125
- def negative_expression(target_expression, matcher_expression)
126
- expression_generator.negative_expression(target_expression, matcher_expression)
127
- end
128
-
129
- # @api private
130
- # Selects which expression generator to use based on the configured syntax.
131
- def expression_generator
132
- if expect_enabled?
133
- ExpectExpressionGenerator
134
- else
135
- ShouldExpressionGenerator
136
- end
137
- end
138
-
139
- # @api private
140
- # Generates expectation expressions for the `should` syntax.
141
- module ShouldExpressionGenerator
142
- def self.positive_expression(target_expression, matcher_expression)
143
- "#{target_expression}.should #{matcher_expression}"
144
- end
145
-
146
- def self.negative_expression(target_expression, matcher_expression)
147
- "#{target_expression}.should_not #{matcher_expression}"
148
- end
149
- end
150
-
151
- # @api private
152
- # Generates expectation expressions for the `expect` syntax.
153
- module ExpectExpressionGenerator
154
- def self.positive_expression(target_expression, matcher_expression)
155
- "expect(#{target_expression}).to #{matcher_expression}"
156
- end
157
-
158
- def self.negative_expression(target_expression, matcher_expression)
159
- "expect(#{target_expression}).not_to #{matcher_expression}"
160
- end
161
- end
162
99
  end
163
100
  end
164
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.14.0'
5
+ STRING = '3.13.0'
6
6
  end
7
7
  end
8
8
  end
@@ -1,40 +1,55 @@
1
- require 'rspec/expectations/extensions'
1
+ require 'rspec/support'
2
+ RSpec::Support.require_rspec_support "caller_filter"
3
+ RSpec::Support.require_rspec_support "warnings"
4
+ RSpec::Support.require_rspec_support "object_formatter"
5
+
2
6
  require 'rspec/matchers'
3
- require 'rspec/expectations/expectation_target'
4
- require 'rspec/matchers/configuration'
5
- require 'rspec/expectations/fail_with'
6
- require 'rspec/expectations/errors'
7
- require 'rspec/expectations/deprecation'
8
- require 'rspec/expectations/handler'
9
- require 'rspec/expectations/version'
10
- require 'rspec/expectations/differ'
7
+
8
+ RSpec::Support.define_optimized_require_for_rspec(:expectations) { |f| require_relative(f) }
9
+
10
+ %w[
11
+ expectation_target
12
+ configuration
13
+ fail_with
14
+ handler
15
+ version
16
+ ].each { |file| RSpec::Support.require_rspec_expectations(file) }
11
17
 
12
18
  module RSpec
13
- # RSpec::Expectations adds two instance methods to every object:
19
+ # RSpec::Expectations provides a simple, readable API to express
20
+ # the expected outcomes in a code example. To express an expected
21
+ # outcome, wrap an object or block in `expect`, call `to` or `to_not`
22
+ # (aliased as `not_to`) and pass it a matcher object:
14
23
  #
15
- # should(matcher=nil)
16
- # should_not(matcher=nil)
24
+ # expect(order.total).to eq(Money.new(5.55, :USD))
25
+ # expect(list).to include(user)
26
+ # expect(message).not_to match(/foo/)
27
+ # expect { do_something }.to raise_error
17
28
  #
18
- # Both methods take an optional matcher object (See
19
- # [RSpec::Matchers](../RSpec/Matchers)). When `should` is invoked with a
20
- # matcher, it turns around and calls `matcher.matches?(self)`. For example,
29
+ # The last form (the block form) is needed to match against ruby constructs
30
+ # that are not objects, but can only be observed when executing a block
31
+ # of code. This includes raising errors, throwing symbols, yielding,
32
+ # and changing values.
33
+ #
34
+ # When `expect(...).to` is invoked with a matcher, it turns around
35
+ # and calls `matcher.matches?(<object wrapped by expect>)`. For example,
21
36
  # in the expression:
22
37
  #
23
- # order.total.should eq(Money.new(5.55, :USD))
38
+ # expect(order.total).to eq(Money.new(5.55, :USD))
24
39
  #
25
- # the `should` method invokes the equivalent of `eq.matches?(order.total)`. If
26
- # `matches?` returns true, the expectation is met and execution continues. If
27
- # `false`, then the spec fails with the message returned by
28
- # `eq.failure_message_for_should`.
40
+ # ...`eq(Money.new(5.55, :USD))` returns a matcher object, and it results
41
+ # in the equivalent of `eq.matches?(order.total)`. If `matches?` returns
42
+ # `true`, the expectation is met and execution continues. If `false`, then
43
+ # the spec fails with the message returned by `eq.failure_message`.
29
44
  #
30
45
  # Given the expression:
31
46
  #
32
- # order.entries.should_not include(entry)
47
+ # expect(order.entries).not_to include(entry)
33
48
  #
34
- # the `should_not` method invokes the equivalent of
49
+ # ...the `not_to` method (also available as `to_not`) invokes the equivalent of
35
50
  # `include.matches?(order.entries)`, but it interprets `false` as success, and
36
51
  # `true` as a failure, using the message generated by
37
- # `eq.failure_message_for_should_not`.
52
+ # `include.failure_message_when_negated`.
38
53
  #
39
54
  # rspec-expectations ships with a standard set of useful matchers, and writing
40
55
  # your own matchers is quite simple.
@@ -43,5 +58,25 @@ module RSpec
43
58
  # built-in matchers that ship with rspec-expectations, and how to write your
44
59
  # own custom matchers.
45
60
  module Expectations
61
+ # Exception raised when an expectation fails.
62
+ #
63
+ # @note We subclass Exception so that in a stub implementation if
64
+ # the user sets an expectation, it can't be caught in their
65
+ # code by a bare `rescue`.
66
+ # @api public
67
+ class ExpectationNotMetError < Exception
68
+ end
69
+
70
+ # Exception raised from `aggregate_failures` when multiple expectations fail.
71
+ #
72
+ # @note The constant is defined here but the extensive logic of this class
73
+ # is lazily defined when `FailureAggregator` is autoloaded, since we do
74
+ # not need to waste time defining that functionality unless
75
+ # `aggregate_failures` is used.
76
+ class MultipleExpectationsNotMetError < ExpectationNotMetError
77
+ end
78
+
79
+ autoload :BlockSnippetExtractor, "rspec/expectations/block_snippet_extractor"
80
+ autoload :FailureAggregator, "rspec/expectations/failure_aggregator"
46
81
  end
47
82
  end
@@ -0,0 +1,116 @@
1
+ module RSpec
2
+ module Matchers
3
+ # Decorator that wraps a matcher and overrides `description`
4
+ # using the provided block in order to support an alias
5
+ # of a matcher. This is intended for use when composing
6
+ # matchers, so that you can use an expression like
7
+ # `include( a_value_within(0.1).of(3) )` rather than
8
+ # `include( be_within(0.1).of(3) )`, and have the corresponding
9
+ # description read naturally.
10
+ #
11
+ # @api private
12
+ class AliasedMatcher < MatcherDelegator
13
+ def initialize(base_matcher, description_block)
14
+ @description_block = description_block
15
+ super(base_matcher)
16
+ end
17
+
18
+ # Forward messages on to the wrapped matcher.
19
+ # Since many matchers provide a fluent interface
20
+ # (e.g. `a_value_within(0.1).of(3)`), we need to wrap
21
+ # the returned value if it responds to `description`,
22
+ # so that our override can be applied when it is eventually
23
+ # used.
24
+ def method_missing(*)
25
+ return_val = super
26
+ return return_val unless RSpec::Matchers.is_a_matcher?(return_val)
27
+ self.class.new(return_val, @description_block)
28
+ end
29
+
30
+ # Provides the description of the aliased matcher. Aliased matchers
31
+ # are designed to behave identically to the original matcher except
32
+ # for the description and failure messages. The description is different
33
+ # to reflect the aliased name.
34
+ #
35
+ # @api private
36
+ def description
37
+ @description_block.call(super)
38
+ end
39
+
40
+ # Provides the failure_message of the aliased matcher. Aliased matchers
41
+ # are designed to behave identically to the original matcher except
42
+ # for the description and failure messages. The failure_message is different
43
+ # to reflect the aliased name.
44
+ #
45
+ # @api private
46
+ def failure_message
47
+ @description_block.call(super)
48
+ end
49
+
50
+ # Provides the failure_message_when_negated of the aliased matcher. Aliased matchers
51
+ # are designed to behave identically to the original matcher except
52
+ # for the description and failure messages. The failure_message_when_negated is different
53
+ # to reflect the aliased name.
54
+ #
55
+ # @api private
56
+ def failure_message_when_negated
57
+ @description_block.call(super)
58
+ end
59
+ end
60
+
61
+ # Decorator used for matchers that have special implementations of
62
+ # operators like `==` and `===`.
63
+ # @private
64
+ class AliasedMatcherWithOperatorSupport < AliasedMatcher
65
+ # We undef these so that they get delegated via `method_missing`.
66
+ undef ==
67
+ undef ===
68
+ end
69
+
70
+ # @private
71
+ class AliasedNegatedMatcher < AliasedMatcher
72
+ def matches?(*args, &block)
73
+ if @base_matcher.respond_to?(:does_not_match?)
74
+ @base_matcher.does_not_match?(*args, &block)
75
+ else
76
+ !super
77
+ end
78
+ end
79
+
80
+ def does_not_match?(*args, &block)
81
+ @base_matcher.matches?(*args, &block)
82
+ end
83
+
84
+ def failure_message
85
+ optimal_failure_message(__method__, :failure_message_when_negated)
86
+ end
87
+
88
+ def failure_message_when_negated
89
+ optimal_failure_message(__method__, :failure_message)
90
+ end
91
+
92
+ private
93
+
94
+ DefaultFailureMessages = BuiltIn::BaseMatcher::DefaultFailureMessages
95
+
96
+ # For a matcher that uses the default failure messages, we prefer to
97
+ # use the override provided by the `description_block`, because it
98
+ # includes the phrasing that the user has expressed a preference for
99
+ # by going through the effort of defining a negated matcher.
100
+ #
101
+ # However, if the override didn't actually change anything, then we
102
+ # should return the opposite failure message instead -- the overridden
103
+ # message is going to be confusing if we return it as-is, as it represents
104
+ # the non-negated failure message for a negated match (or vice versa).
105
+ def optimal_failure_message(same, inverted)
106
+ if DefaultFailureMessages.has_default_failure_messages?(@base_matcher)
107
+ base_message = @base_matcher.__send__(same)
108
+ overridden = @description_block.call(base_message)
109
+ return overridden if overridden != base_message
110
+ end
111
+
112
+ @base_matcher.__send__(inverted)
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,86 @@
1
+ module RSpec
2
+ module Matchers
3
+ module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `all`.
6
+ # Not intended to be instantiated directly.
7
+ class All < BaseMatcher
8
+ # @private
9
+ attr_reader :matcher, :failed_objects
10
+
11
+ def initialize(matcher)
12
+ @matcher = matcher
13
+ @failed_objects = {}
14
+ end
15
+
16
+ # @private
17
+ def does_not_match?(_actual)
18
+ raise NotImplementedError, '`expect().not_to all( matcher )` is not supported.'
19
+ end
20
+
21
+ # @api private
22
+ # @return [String]
23
+ def failure_message
24
+ unless iterable?
25
+ return "#{improve_hash_formatting(super)}, but was not iterable"
26
+ end
27
+
28
+ all_messages = [improve_hash_formatting(super)]
29
+ failed_objects.each do |index, matcher_failure_message|
30
+ all_messages << failure_message_for_item(index, matcher_failure_message)
31
+ end
32
+ all_messages.join("\n\n")
33
+ end
34
+
35
+ # @api private
36
+ # @return [String]
37
+ def description
38
+ improve_hash_formatting "all #{description_of matcher}"
39
+ end
40
+
41
+ private
42
+
43
+ def match(_expected, _actual)
44
+ return false unless iterable?
45
+
46
+ index_failed_objects
47
+ failed_objects.empty?
48
+ end
49
+
50
+ def index_failed_objects
51
+ actual.each_with_index do |actual_item, index|
52
+ cloned_matcher = matcher.clone
53
+ matches = cloned_matcher.matches?(actual_item)
54
+ failed_objects[index] = cloned_matcher.failure_message unless matches
55
+ end
56
+ end
57
+
58
+ def failure_message_for_item(index, failure_message)
59
+ failure_message = indent_multiline_message(add_new_line_if_needed(failure_message))
60
+ indent_multiline_message("object at index #{index} failed to match:#{failure_message}")
61
+ end
62
+
63
+ def add_new_line_if_needed(message)
64
+ message.start_with?("\n") ? message : "\n#{message}"
65
+ end
66
+
67
+ def indent_multiline_message(message)
68
+ message = message.sub(/\n+\z/, '')
69
+ message.lines.map do |line|
70
+ line =~ /\S/ ? ' ' + line : line
71
+ end.join
72
+ end
73
+
74
+ def initialize_copy(other)
75
+ @matcher = @matcher.clone
76
+ @failed_objects = @failed_objects.clone
77
+ super
78
+ end
79
+
80
+ def iterable?
81
+ @actual.respond_to?(:each_with_index)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end