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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +1 -1
- data/Changelog.md +976 -25
- data/{License.txt → LICENSE.md} +5 -3
- data/README.md +162 -26
- data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
- data/lib/rspec/expectations/configuration.rb +230 -0
- data/lib/rspec/expectations/expectation_target.rb +127 -51
- data/lib/rspec/expectations/fail_with.rb +17 -57
- data/lib/rspec/expectations/failure_aggregator.rb +229 -0
- data/lib/rspec/expectations/handler.rb +146 -32
- data/lib/rspec/expectations/minitest_integration.rb +58 -0
- data/lib/rspec/expectations/syntax.rb +68 -100
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/expectations.rb +58 -23
- data/lib/rspec/matchers/aliased_matcher.rb +116 -0
- data/lib/rspec/matchers/built_in/all.rb +86 -0
- data/lib/rspec/matchers/built_in/base_matcher.rb +191 -20
- data/lib/rspec/matchers/built_in/be.rb +114 -114
- data/lib/rspec/matchers/built_in/be_between.rb +77 -0
- data/lib/rspec/matchers/built_in/be_instance_of.rb +15 -4
- data/lib/rspec/matchers/built_in/be_kind_of.rb +10 -1
- data/lib/rspec/matchers/built_in/be_within.rb +35 -18
- data/lib/rspec/matchers/built_in/change.rb +389 -80
- data/lib/rspec/matchers/built_in/compound.rb +290 -0
- data/lib/rspec/matchers/built_in/contain_exactly.rb +310 -0
- data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
- data/lib/rspec/matchers/built_in/cover.rb +3 -0
- data/lib/rspec/matchers/built_in/eq.rb +30 -8
- data/lib/rspec/matchers/built_in/eql.rb +23 -8
- data/lib/rspec/matchers/built_in/equal.rb +55 -22
- data/lib/rspec/matchers/built_in/exist.rb +74 -10
- data/lib/rspec/matchers/built_in/has.rb +141 -22
- data/lib/rspec/matchers/built_in/have_attributes.rb +114 -0
- data/lib/rspec/matchers/built_in/include.rb +184 -32
- data/lib/rspec/matchers/built_in/match.rb +95 -1
- data/lib/rspec/matchers/built_in/operators.rb +128 -0
- data/lib/rspec/matchers/built_in/output.rb +207 -0
- data/lib/rspec/matchers/built_in/raise_error.rb +192 -44
- data/lib/rspec/matchers/built_in/respond_to.rb +154 -28
- data/lib/rspec/matchers/built_in/satisfy.rb +39 -9
- data/lib/rspec/matchers/built_in/start_or_end_with.rb +94 -0
- data/lib/rspec/matchers/built_in/throw_symbol.rb +58 -14
- data/lib/rspec/matchers/built_in/yield.rb +240 -161
- data/lib/rspec/matchers/built_in.rb +47 -33
- data/lib/rspec/matchers/composable.rb +171 -0
- data/lib/rspec/matchers/dsl.rb +531 -10
- data/lib/rspec/matchers/english_phrasing.rb +58 -0
- data/lib/rspec/matchers/fail_matchers.rb +42 -0
- data/lib/rspec/matchers/generated_descriptions.rb +14 -8
- data/lib/rspec/matchers/matcher_delegator.rb +61 -0
- data/lib/rspec/matchers/matcher_protocol.rb +105 -0
- data/lib/rspec/matchers/multi_matcher_diff.rb +82 -0
- data/lib/rspec/matchers.rb +520 -173
- data.tar.gz.sig +0 -0
- metadata +141 -242
- metadata.gz.sig +2 -0
- data/features/README.md +0 -48
- data/features/Upgrade.md +0 -53
- data/features/built_in_matchers/README.md +0 -90
- data/features/built_in_matchers/be.feature +0 -175
- data/features/built_in_matchers/be_within.feature +0 -48
- data/features/built_in_matchers/cover.feature +0 -47
- data/features/built_in_matchers/end_with.feature +0 -48
- data/features/built_in_matchers/equality.feature +0 -139
- data/features/built_in_matchers/exist.feature +0 -45
- data/features/built_in_matchers/expect_change.feature +0 -59
- data/features/built_in_matchers/expect_error.feature +0 -144
- data/features/built_in_matchers/have.feature +0 -109
- data/features/built_in_matchers/include.feature +0 -174
- data/features/built_in_matchers/match.feature +0 -52
- data/features/built_in_matchers/operators.feature +0 -227
- data/features/built_in_matchers/predicates.feature +0 -137
- data/features/built_in_matchers/respond_to.feature +0 -84
- data/features/built_in_matchers/satisfy.feature +0 -33
- data/features/built_in_matchers/start_with.feature +0 -48
- data/features/built_in_matchers/throw_symbol.feature +0 -91
- data/features/built_in_matchers/types.feature +0 -116
- data/features/built_in_matchers/yield.feature +0 -161
- data/features/custom_matchers/access_running_example.feature +0 -53
- data/features/custom_matchers/define_diffable_matcher.feature +0 -27
- data/features/custom_matchers/define_matcher.feature +0 -368
- data/features/custom_matchers/define_matcher_outside_rspec.feature +0 -38
- data/features/custom_matchers/define_matcher_with_fluent_interface.feature +0 -24
- data/features/customized_message.feature +0 -22
- data/features/diffing.feature +0 -85
- data/features/implicit_docstrings.feature +0 -52
- data/features/step_definitions/additional_cli_steps.rb +0 -22
- data/features/support/env.rb +0 -14
- data/features/syntax_configuration.feature +0 -71
- data/features/test_frameworks/test_unit.feature +0 -44
- data/lib/rspec/expectations/deprecation.rb +0 -17
- data/lib/rspec/expectations/differ.rb +0 -133
- data/lib/rspec/expectations/errors.rb +0 -9
- data/lib/rspec/expectations/extensions/array.rb +0 -9
- data/lib/rspec/expectations/extensions/object.rb +0 -29
- data/lib/rspec/expectations/extensions.rb +0 -2
- data/lib/rspec/matchers/be_close.rb +0 -9
- data/lib/rspec/matchers/built_in/have.rb +0 -124
- data/lib/rspec/matchers/built_in/match_array.rb +0 -51
- data/lib/rspec/matchers/built_in/start_and_end_with.rb +0 -48
- data/lib/rspec/matchers/compatibility.rb +0 -14
- data/lib/rspec/matchers/configuration.rb +0 -108
- data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
- data/lib/rspec/matchers/matcher.rb +0 -300
- data/lib/rspec/matchers/method_missing.rb +0 -12
- data/lib/rspec/matchers/operator_matcher.rb +0 -109
- data/lib/rspec/matchers/pretty.rb +0 -70
- data/lib/rspec/matchers/test_unit_integration.rb +0 -11
- data/lib/rspec-expectations.rb +0 -1
- data/spec/rspec/expectations/differ_spec.rb +0 -192
- data/spec/rspec/expectations/expectation_target_spec.rb +0 -82
- data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
- data/spec/rspec/expectations/fail_with_spec.rb +0 -114
- data/spec/rspec/expectations/handler_spec.rb +0 -227
- data/spec/rspec/expectations/syntax_spec.rb +0 -139
- data/spec/rspec/matchers/base_matcher_spec.rb +0 -62
- data/spec/rspec/matchers/be_close_spec.rb +0 -22
- data/spec/rspec/matchers/be_instance_of_spec.rb +0 -63
- data/spec/rspec/matchers/be_kind_of_spec.rb +0 -41
- data/spec/rspec/matchers/be_spec.rb +0 -516
- data/spec/rspec/matchers/be_within_spec.rb +0 -137
- data/spec/rspec/matchers/change_spec.rb +0 -553
- data/spec/rspec/matchers/configuration_spec.rb +0 -206
- data/spec/rspec/matchers/cover_spec.rb +0 -69
- data/spec/rspec/matchers/description_generation_spec.rb +0 -190
- data/spec/rspec/matchers/dsl_spec.rb +0 -57
- data/spec/rspec/matchers/eq_spec.rb +0 -60
- data/spec/rspec/matchers/eql_spec.rb +0 -41
- data/spec/rspec/matchers/equal_spec.rb +0 -78
- data/spec/rspec/matchers/exist_spec.rb +0 -124
- data/spec/rspec/matchers/has_spec.rb +0 -122
- data/spec/rspec/matchers/have_spec.rb +0 -455
- data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
- data/spec/rspec/matchers/include_spec.rb +0 -531
- data/spec/rspec/matchers/match_array_spec.rb +0 -194
- data/spec/rspec/matchers/match_spec.rb +0 -61
- data/spec/rspec/matchers/matcher_spec.rb +0 -471
- data/spec/rspec/matchers/matchers_spec.rb +0 -37
- data/spec/rspec/matchers/method_missing_spec.rb +0 -28
- data/spec/rspec/matchers/operator_matcher_spec.rb +0 -223
- data/spec/rspec/matchers/raise_error_spec.rb +0 -485
- data/spec/rspec/matchers/respond_to_spec.rb +0 -292
- data/spec/rspec/matchers/satisfy_spec.rb +0 -44
- data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -186
- data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
- data/spec/rspec/matchers/yield_spec.rb +0 -514
- data/spec/spec_helper.rb +0 -54
- data/spec/support/classes.rb +0 -56
- data/spec/support/in_sub_process.rb +0 -38
- data/spec/support/matchers.rb +0 -22
- data/spec/support/ruby_version.rb +0 -10
- 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
|
-
|
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
|
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.
|
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
|
57
|
+
def disable_should(syntax_host=default_should_host)
|
67
58
|
return unless should_enabled?(syntax_host)
|
68
59
|
|
69
|
-
syntax_host.
|
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
|
68
|
+
def enable_expect(syntax_host=::RSpec::Matchers)
|
80
69
|
return if expect_enabled?(syntax_host)
|
81
70
|
|
82
|
-
syntax_host.
|
83
|
-
def expect(
|
84
|
-
|
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
|
80
|
+
def disable_expect(syntax_host=::RSpec::Matchers)
|
96
81
|
return unless expect_enabled?(syntax_host)
|
97
82
|
|
98
|
-
syntax_host.
|
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
|
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
|
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
|
data/lib/rspec/expectations.rb
CHANGED
@@ -1,40 +1,55 @@
|
|
1
|
-
require 'rspec/
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
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
|
-
#
|
16
|
-
#
|
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
|
-
#
|
19
|
-
#
|
20
|
-
#
|
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.
|
38
|
+
# expect(order.total).to eq(Money.new(5.55, :USD))
|
24
39
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# `
|
28
|
-
# `eq.
|
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.
|
47
|
+
# expect(order.entries).not_to include(entry)
|
33
48
|
#
|
34
|
-
# the `
|
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
|
-
# `
|
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
|