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
@@ -0,0 +1,169 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
module BuiltIn
|
4
|
+
# @api private
|
5
|
+
# Abstract class to implement `once`, `at_least` and other
|
6
|
+
# count constraints.
|
7
|
+
module CountExpectation
|
8
|
+
# @api public
|
9
|
+
# Specifies that the method is expected to match once.
|
10
|
+
def once
|
11
|
+
exactly(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @api public
|
15
|
+
# Specifies that the method is expected to match twice.
|
16
|
+
def twice
|
17
|
+
exactly(2)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @api public
|
21
|
+
# Specifies that the method is expected to match thrice.
|
22
|
+
def thrice
|
23
|
+
exactly(3)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @api public
|
27
|
+
# Specifies that the method is expected to match the given number of times.
|
28
|
+
def exactly(number)
|
29
|
+
set_expected_count(:==, number)
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api public
|
34
|
+
# Specifies the maximum number of times the method is expected to match
|
35
|
+
def at_most(number)
|
36
|
+
set_expected_count(:<=, number)
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# @api public
|
41
|
+
# Specifies the minimum number of times the method is expected to match
|
42
|
+
def at_least(number)
|
43
|
+
set_expected_count(:>=, number)
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# @api public
|
48
|
+
# No-op. Provides syntactic sugar.
|
49
|
+
def times
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
# @api private
|
55
|
+
attr_reader :count_expectation_type, :expected_count
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
if RUBY_VERSION.to_f > 1.8
|
60
|
+
def cover?(count, number)
|
61
|
+
count.cover?(number)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
def cover?(count, number)
|
65
|
+
number >= count.first && number <= count.last
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def expected_count_matches?(actual_count)
|
70
|
+
@actual_count = actual_count
|
71
|
+
return @actual_count > 0 unless count_expectation_type
|
72
|
+
return cover?(expected_count, actual_count) if count_expectation_type == :<=>
|
73
|
+
|
74
|
+
@actual_count.__send__(count_expectation_type, expected_count)
|
75
|
+
end
|
76
|
+
|
77
|
+
def has_expected_count?
|
78
|
+
!!count_expectation_type
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_expected_count(relativity, n)
|
82
|
+
raise_unsupported_count_expectation if unsupported_count_expectation?(relativity)
|
83
|
+
|
84
|
+
count = count_constraint_to_number(n)
|
85
|
+
|
86
|
+
if count_expectation_type == :<= && relativity == :>=
|
87
|
+
raise_impossible_count_expectation(count) if count > expected_count
|
88
|
+
@count_expectation_type = :<=>
|
89
|
+
@expected_count = count..expected_count
|
90
|
+
elsif count_expectation_type == :>= && relativity == :<=
|
91
|
+
raise_impossible_count_expectation(count) if count < expected_count
|
92
|
+
@count_expectation_type = :<=>
|
93
|
+
@expected_count = expected_count..count
|
94
|
+
else
|
95
|
+
@count_expectation_type = relativity
|
96
|
+
@expected_count = count
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def raise_impossible_count_expectation(count)
|
101
|
+
text =
|
102
|
+
case count_expectation_type
|
103
|
+
when :<= then "at_least(#{count}).at_most(#{expected_count})"
|
104
|
+
when :>= then "at_least(#{expected_count}).at_most(#{count})"
|
105
|
+
end
|
106
|
+
raise ArgumentError, "The constraint #{text} is not possible"
|
107
|
+
end
|
108
|
+
|
109
|
+
def raise_unsupported_count_expectation
|
110
|
+
text =
|
111
|
+
case count_expectation_type
|
112
|
+
when :<= then "at_least"
|
113
|
+
when :>= then "at_most"
|
114
|
+
when :<=> then "at_least/at_most combination"
|
115
|
+
else "count"
|
116
|
+
end
|
117
|
+
raise ArgumentError, "Multiple #{text} constraints are not supported"
|
118
|
+
end
|
119
|
+
|
120
|
+
def count_constraint_to_number(n)
|
121
|
+
case n
|
122
|
+
when Numeric then n
|
123
|
+
when :once then 1
|
124
|
+
when :twice then 2
|
125
|
+
when :thrice then 3
|
126
|
+
else
|
127
|
+
raise ArgumentError, "Expected a number, :once, :twice or :thrice," \
|
128
|
+
" but got #{n}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def unsupported_count_expectation?(relativity)
|
133
|
+
return true if count_expectation_type == :==
|
134
|
+
return true if count_expectation_type == :<=>
|
135
|
+
(count_expectation_type == :<= && relativity == :<=) ||
|
136
|
+
(count_expectation_type == :>= && relativity == :>=)
|
137
|
+
end
|
138
|
+
|
139
|
+
def count_expectation_description
|
140
|
+
"#{human_readable_expectation_type}#{human_readable_count(expected_count)}"
|
141
|
+
end
|
142
|
+
|
143
|
+
def count_failure_reason(action)
|
144
|
+
"#{count_expectation_description}" \
|
145
|
+
" but #{action}#{human_readable_count(@actual_count)}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def human_readable_expectation_type
|
149
|
+
case count_expectation_type
|
150
|
+
when :<= then ' at most'
|
151
|
+
when :>= then ' at least'
|
152
|
+
when :<=> then ' between'
|
153
|
+
else ''
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def human_readable_count(count)
|
158
|
+
case count
|
159
|
+
when Range then " #{count.first} and #{count.last} times"
|
160
|
+
when nil then ''
|
161
|
+
when 1 then ' once'
|
162
|
+
when 2 then ' twice'
|
163
|
+
else " #{count} times"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -1,22 +1,44 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Matchers
|
3
3
|
module BuiltIn
|
4
|
+
# @api private
|
5
|
+
# Provides the implementation for `eq`.
|
6
|
+
# Not intended to be instantiated directly.
|
4
7
|
class Eq < BaseMatcher
|
5
|
-
|
6
|
-
|
8
|
+
# @api private
|
9
|
+
# @return [String]
|
10
|
+
def failure_message
|
11
|
+
if string_encoding_differs?
|
12
|
+
"\nexpected: #{format_encoding(expected)} #{expected_formatted}\n got: #{format_encoding(actual)} #{actual_formatted}\n\n(compared using ==)\n"
|
13
|
+
else
|
14
|
+
"\nexpected: #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using ==)\n"
|
15
|
+
end
|
7
16
|
end
|
8
17
|
|
9
|
-
|
10
|
-
|
18
|
+
# @api private
|
19
|
+
# @return [String]
|
20
|
+
def failure_message_when_negated
|
21
|
+
"\nexpected: value != #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using ==)\n"
|
11
22
|
end
|
12
23
|
|
13
|
-
|
14
|
-
|
24
|
+
# @api private
|
25
|
+
# @return [String]
|
26
|
+
def description
|
27
|
+
"eq #{expected_formatted}"
|
15
28
|
end
|
16
29
|
|
17
|
-
|
30
|
+
# @api private
|
31
|
+
# @return [Boolean]
|
32
|
+
def diffable?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def match(expected, actual)
|
39
|
+
actual == expected
|
40
|
+
end
|
18
41
|
end
|
19
42
|
end
|
20
43
|
end
|
21
44
|
end
|
22
|
-
|
@@ -1,22 +1,37 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Matchers
|
3
3
|
module BuiltIn
|
4
|
+
# @api private
|
5
|
+
# Provides the implementation for `eql`.
|
6
|
+
# Not intended to be instantiated directly.
|
4
7
|
class Eql < BaseMatcher
|
5
|
-
|
6
|
-
|
8
|
+
# @api private
|
9
|
+
# @return [String]
|
10
|
+
def failure_message
|
11
|
+
if string_encoding_differs?
|
12
|
+
"\nexpected: #{format_encoding(expected)} #{expected_formatted}\n got: #{format_encoding(actual)} #{actual_formatted}\n\n(compared using eql?)\n"
|
13
|
+
else
|
14
|
+
"\nexpected: #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using eql?)\n"
|
15
|
+
end
|
7
16
|
end
|
8
17
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def failure_message_for_should_not
|
14
|
-
"\nexpected: value != #{expected.inspect}\n got: #{actual.inspect}\n\n(compared using eql?)\n"
|
18
|
+
# @api private
|
19
|
+
# @return [String]
|
20
|
+
def failure_message_when_negated
|
21
|
+
"\nexpected: value != #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using eql?)\n"
|
15
22
|
end
|
16
23
|
|
24
|
+
# @api private
|
25
|
+
# @return [Boolean]
|
17
26
|
def diffable?
|
18
27
|
true
|
19
28
|
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def match(expected, actual)
|
33
|
+
actual.eql? expected
|
34
|
+
end
|
20
35
|
end
|
21
36
|
end
|
22
37
|
end
|
@@ -1,46 +1,79 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Matchers
|
3
3
|
module BuiltIn
|
4
|
+
# @api private
|
5
|
+
# Provides the implementation for `equal`.
|
6
|
+
# Not intended to be instantiated directly.
|
4
7
|
class Equal < BaseMatcher
|
5
|
-
|
6
|
-
|
8
|
+
# @api private
|
9
|
+
# @return [String]
|
10
|
+
def failure_message
|
11
|
+
if expected_is_a_literal_singleton?
|
12
|
+
simple_failure_message
|
13
|
+
else
|
14
|
+
detailed_failure_message
|
15
|
+
end
|
7
16
|
end
|
8
17
|
|
9
|
-
|
10
|
-
|
18
|
+
# @api private
|
19
|
+
# @return [String]
|
20
|
+
def failure_message_when_negated
|
21
|
+
<<-MESSAGE
|
11
22
|
|
12
|
-
expected #{inspect_object(
|
13
|
-
|
23
|
+
expected not #{inspect_object(actual)}
|
24
|
+
got #{inspect_object(expected)}
|
14
25
|
|
15
|
-
Compared using equal?, which compares object identity
|
16
|
-
but expected and actual are not the same object. Use
|
17
|
-
`#{eq_expression}` if you don't care about
|
18
|
-
object identity in this example.
|
26
|
+
Compared using equal?, which compares object identity.
|
19
27
|
|
20
28
|
MESSAGE
|
21
29
|
end
|
22
30
|
|
23
|
-
|
24
|
-
|
31
|
+
# @api private
|
32
|
+
# @return [Boolean]
|
33
|
+
def diffable?
|
34
|
+
!expected_is_a_literal_singleton?
|
35
|
+
end
|
25
36
|
|
26
|
-
|
27
|
-
got #{inspect_object(expected)}
|
37
|
+
private
|
28
38
|
|
29
|
-
|
39
|
+
def match(expected, actual)
|
40
|
+
actual.equal? expected
|
41
|
+
end
|
30
42
|
|
31
|
-
|
43
|
+
LITERAL_SINGLETONS = [true, false, nil]
|
44
|
+
|
45
|
+
def expected_is_a_literal_singleton?
|
46
|
+
LITERAL_SINGLETONS.include?(expected)
|
47
|
+
end
|
48
|
+
|
49
|
+
def actual_inspected
|
50
|
+
if LITERAL_SINGLETONS.include?(actual)
|
51
|
+
actual_formatted
|
52
|
+
else
|
53
|
+
inspect_object(actual)
|
54
|
+
end
|
32
55
|
end
|
33
56
|
|
34
|
-
def
|
57
|
+
def simple_failure_message
|
58
|
+
"\nexpected #{expected_formatted}\n got #{actual_inspected}\n"
|
59
|
+
end
|
35
60
|
|
36
|
-
|
61
|
+
def detailed_failure_message
|
62
|
+
<<-MESSAGE
|
37
63
|
|
38
|
-
|
39
|
-
|
64
|
+
expected #{inspect_object(expected)}
|
65
|
+
got #{inspect_object(actual)}
|
66
|
+
|
67
|
+
Compared using equal?, which compares object identity,
|
68
|
+
but expected and actual are not the same object. Use
|
69
|
+
`expect(actual).to eq(expected)` if you don't care about
|
70
|
+
object identity in this example.
|
71
|
+
|
72
|
+
MESSAGE
|
40
73
|
end
|
41
74
|
|
42
|
-
def
|
43
|
-
|
75
|
+
def inspect_object(o)
|
76
|
+
"#<#{o.class}:#{o.object_id}> => #{RSpec::Support::ObjectFormatter.format(o)}"
|
44
77
|
end
|
45
78
|
end
|
46
79
|
end
|
@@ -1,23 +1,87 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Matchers
|
3
3
|
module BuiltIn
|
4
|
+
# @api private
|
5
|
+
# Provides the implementation for `exist`.
|
6
|
+
# Not intended to be instantiated directly.
|
4
7
|
class Exist < BaseMatcher
|
5
8
|
def initialize(*expected)
|
6
9
|
@expected = expected
|
7
10
|
end
|
8
11
|
|
12
|
+
# @api private
|
13
|
+
# @return [Boolean]
|
9
14
|
def matches?(actual)
|
10
15
|
@actual = actual
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
@test = ExistenceTest.new @actual, @expected
|
17
|
+
@test.valid_test? && @test.actual_exists?
|
18
|
+
end
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
# @return [Boolean]
|
22
|
+
def does_not_match?(actual)
|
23
|
+
@actual = actual
|
24
|
+
@test = ExistenceTest.new @actual, @expected
|
25
|
+
@test.valid_test? && !@test.actual_exists?
|
26
|
+
end
|
27
|
+
|
28
|
+
# @api private
|
29
|
+
# @return [String]
|
30
|
+
def failure_message
|
31
|
+
"expected #{actual_formatted} to exist#{@test.validity_message}"
|
32
|
+
end
|
33
|
+
|
34
|
+
# @api private
|
35
|
+
# @return [String]
|
36
|
+
def failure_message_when_negated
|
37
|
+
"expected #{actual_formatted} not to exist#{@test.validity_message}"
|
38
|
+
end
|
39
|
+
|
40
|
+
# @api private
|
41
|
+
# Simple class for memoizing actual/expected for this matcher
|
42
|
+
# and examining the match
|
43
|
+
class ExistenceTest < Struct.new(:actual, :expected)
|
44
|
+
# @api private
|
45
|
+
# @return [Boolean]
|
46
|
+
def valid_test?
|
47
|
+
uniq_truthy_values.size == 1
|
48
|
+
end
|
49
|
+
|
50
|
+
# @api private
|
51
|
+
# @return [Boolean]
|
52
|
+
def actual_exists?
|
53
|
+
existence_values.first
|
54
|
+
end
|
55
|
+
|
56
|
+
# @api private
|
57
|
+
# @return [String]
|
58
|
+
def validity_message
|
59
|
+
case uniq_truthy_values.size
|
60
|
+
when 0
|
61
|
+
" but it does not respond to either `exist?` or `exists?`"
|
62
|
+
when 2
|
63
|
+
" but `exist?` and `exists?` returned different values:\n\n"\
|
64
|
+
" exist?: #{existence_values.first}\n"\
|
65
|
+
"exists?: #{existence_values.last}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def uniq_truthy_values
|
72
|
+
@uniq_truthy_values ||= existence_values.map { |v| !!v }.uniq
|
73
|
+
end
|
74
|
+
|
75
|
+
def existence_values
|
76
|
+
@existence_values ||= predicates.map { |p| actual.__send__(p, *expected) }
|
77
|
+
end
|
78
|
+
|
79
|
+
def predicates
|
80
|
+
@predicates ||= [:exist?, :exists?].select { |p| actual.respond_to?(p) && !deprecated(p, actual) }
|
81
|
+
end
|
82
|
+
|
83
|
+
def deprecated(predicate, actual)
|
84
|
+
predicate == :exists? && (File == actual || FileTest == actual || Dir == actual)
|
21
85
|
end
|
22
86
|
end
|
23
87
|
end
|
@@ -1,46 +1,165 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Matchers
|
3
3
|
module BuiltIn
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
# @api private
|
5
|
+
# Provides the implementation for dynamic predicate matchers.
|
6
|
+
# Not intended to be inherited directly.
|
7
|
+
class DynamicPredicate < BaseMatcher
|
8
|
+
include BeHelpers
|
9
|
+
|
10
|
+
def initialize(method_name, *args, &block)
|
11
|
+
@method_name, @args, @block = method_name, args, block
|
12
|
+
end
|
13
|
+
ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
|
14
|
+
|
15
|
+
# @private
|
16
|
+
def matches?(actual, &block)
|
17
|
+
@actual = actual
|
18
|
+
@block ||= block
|
19
|
+
predicate_accessible? && predicate_matches?
|
7
20
|
end
|
8
21
|
|
9
|
-
|
10
|
-
|
22
|
+
# @private
|
23
|
+
def does_not_match?(actual, &block)
|
24
|
+
@actual = actual
|
25
|
+
@block ||= block
|
26
|
+
predicate_accessible? && predicate_matches?(false)
|
11
27
|
end
|
12
|
-
alias == matches?
|
13
28
|
|
14
|
-
|
15
|
-
|
29
|
+
# @api private
|
30
|
+
# @return [String]
|
31
|
+
def failure_message
|
32
|
+
failure_message_expecting(true)
|
16
33
|
end
|
17
34
|
|
18
|
-
|
19
|
-
|
35
|
+
# @api private
|
36
|
+
# @return [String]
|
37
|
+
def failure_message_when_negated
|
38
|
+
failure_message_expecting(false)
|
20
39
|
end
|
21
40
|
|
41
|
+
# @api private
|
42
|
+
# @return [String]
|
22
43
|
def description
|
23
|
-
|
44
|
+
"#{method_description}#{args_to_sentence}"
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def predicate_accessible?
|
50
|
+
@actual.respond_to? predicate
|
51
|
+
end
|
52
|
+
|
53
|
+
# support 1.8.7, evaluate once at load time for performance
|
54
|
+
if String === methods.first
|
55
|
+
# :nocov:
|
56
|
+
def private_predicate?
|
57
|
+
@actual.private_methods.include? predicate.to_s
|
58
|
+
end
|
59
|
+
# :nocov:
|
60
|
+
else
|
61
|
+
def private_predicate?
|
62
|
+
@actual.private_methods.include? predicate
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def predicate_result
|
67
|
+
@predicate_result = actual.__send__(predicate_method_name, *@args, &@block)
|
68
|
+
end
|
69
|
+
|
70
|
+
def predicate_method_name
|
71
|
+
predicate
|
72
|
+
end
|
73
|
+
|
74
|
+
def predicate_matches?(value=true)
|
75
|
+
if RSpec::Expectations.configuration.strict_predicate_matchers?
|
76
|
+
value == predicate_result
|
77
|
+
else
|
78
|
+
value == !!predicate_result
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def root
|
83
|
+
# On 1.9, there appears to be a bug where String#match can return `false`
|
84
|
+
# rather than the match data object. Changing to Regex#match appears to
|
85
|
+
# work around this bug. For an example of this bug, see:
|
86
|
+
# https://travis-ci.org/rspec/rspec-expectations/jobs/27549635
|
87
|
+
self.class::REGEX.match(@method_name.to_s).captures.first
|
24
88
|
end
|
25
89
|
|
26
|
-
|
90
|
+
def method_description
|
91
|
+
EnglishPhrasing.split_words(@method_name)
|
92
|
+
end
|
93
|
+
|
94
|
+
def failure_message_expecting(value)
|
95
|
+
validity_message ||
|
96
|
+
"expected `#{actual_formatted}.#{predicate}#{args_to_s}` to #{expectation_of value}, got #{description_of @predicate_result}"
|
97
|
+
end
|
98
|
+
|
99
|
+
def expectation_of(value)
|
100
|
+
if RSpec::Expectations.configuration.strict_predicate_matchers?
|
101
|
+
"return #{value}"
|
102
|
+
elsif value
|
103
|
+
"be truthy"
|
104
|
+
else
|
105
|
+
"be falsey"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def validity_message
|
110
|
+
return nil if predicate_accessible?
|
111
|
+
|
112
|
+
"expected #{actual_formatted} to respond to `#{predicate}`#{failure_to_respond_explanation}"
|
113
|
+
end
|
114
|
+
|
115
|
+
def failure_to_respond_explanation
|
116
|
+
if private_predicate?
|
117
|
+
" but `#{predicate}` is a private method"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# @api private
|
123
|
+
# Provides the implementation for `has_<predicate>`.
|
124
|
+
# Not intended to be instantiated directly.
|
125
|
+
class Has < DynamicPredicate
|
126
|
+
# :nodoc:
|
127
|
+
REGEX = Matchers::HAS_REGEX
|
128
|
+
private
|
129
|
+
def predicate
|
130
|
+
@predicate ||= :"has_#{root}?"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# @api private
|
135
|
+
# Provides the implementation of `be_<predicate>`.
|
136
|
+
# Not intended to be instantiated directly.
|
137
|
+
class BePredicate < DynamicPredicate
|
138
|
+
# :nodoc:
|
139
|
+
REGEX = Matchers::BE_PREDICATE_REGEX
|
140
|
+
private
|
141
|
+
def predicate
|
142
|
+
@predicate ||= :"#{root}?"
|
143
|
+
end
|
27
144
|
|
28
|
-
def
|
29
|
-
|
145
|
+
def predicate_method_name
|
146
|
+
actual.respond_to?(predicate) ? predicate : present_tense_predicate
|
30
147
|
end
|
31
148
|
|
32
|
-
def
|
33
|
-
|
149
|
+
def failure_to_respond_explanation
|
150
|
+
super || if predicate == :true?
|
151
|
+
" or perhaps you meant `be true` or `be_truthy`"
|
152
|
+
elsif predicate == :false?
|
153
|
+
" or perhaps you meant `be false` or `be_falsey`"
|
154
|
+
end
|
34
155
|
end
|
35
156
|
|
36
|
-
def
|
37
|
-
|
38
|
-
@args.map { |arg| arg.inspect }.join(', ')
|
157
|
+
def predicate_accessible?
|
158
|
+
super || actual.respond_to?(present_tense_predicate)
|
39
159
|
end
|
40
160
|
|
41
|
-
def
|
42
|
-
|
43
|
-
"(#{desc})" if desc
|
161
|
+
def present_tense_predicate
|
162
|
+
:"#{root}s?"
|
44
163
|
end
|
45
164
|
end
|
46
165
|
end
|