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,114 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
module BuiltIn
|
4
|
+
# @api private
|
5
|
+
# Provides the implementation for `have_attributes`.
|
6
|
+
# Not intended to be instantiated directly.
|
7
|
+
class HaveAttributes < BaseMatcher
|
8
|
+
# @private
|
9
|
+
attr_reader :respond_to_failed
|
10
|
+
|
11
|
+
def initialize(expected)
|
12
|
+
@expected = expected
|
13
|
+
@values = {}
|
14
|
+
@respond_to_failed = false
|
15
|
+
@negated = false
|
16
|
+
end
|
17
|
+
|
18
|
+
# @private
|
19
|
+
def actual
|
20
|
+
@values
|
21
|
+
end
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
# @return [Boolean]
|
25
|
+
def matches?(actual)
|
26
|
+
@actual = actual
|
27
|
+
@negated = false
|
28
|
+
return false unless respond_to_attributes?
|
29
|
+
perform_match(:all?)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @api private
|
33
|
+
# @return [Boolean]
|
34
|
+
def does_not_match?(actual)
|
35
|
+
@actual = actual
|
36
|
+
@negated = true
|
37
|
+
return false unless respond_to_attributes?
|
38
|
+
perform_match(:none?)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @api private
|
42
|
+
# @return [String]
|
43
|
+
def description
|
44
|
+
described_items = surface_descriptions_in(expected)
|
45
|
+
improve_hash_formatting "have attributes #{RSpec::Support::ObjectFormatter.format(described_items)}"
|
46
|
+
end
|
47
|
+
|
48
|
+
# @api private
|
49
|
+
# @return [Boolean]
|
50
|
+
def diffable?
|
51
|
+
!@respond_to_failed && !@negated
|
52
|
+
end
|
53
|
+
|
54
|
+
# @api private
|
55
|
+
# @return [String]
|
56
|
+
def failure_message
|
57
|
+
respond_to_failure_message_or do
|
58
|
+
"expected #{actual_formatted} to #{description} but had attributes #{ formatted_values }"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @api private
|
63
|
+
# @return [String]
|
64
|
+
def failure_message_when_negated
|
65
|
+
respond_to_failure_message_or { "expected #{actual_formatted} not to #{description}" }
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def cache_all_values
|
71
|
+
@values = {}
|
72
|
+
expected.each do |attribute_key, _attribute_value|
|
73
|
+
actual_value = @actual.__send__(attribute_key)
|
74
|
+
@values[attribute_key] = actual_value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def perform_match(predicate)
|
79
|
+
cache_all_values
|
80
|
+
expected.__send__(predicate) do |attribute_key, attribute_value|
|
81
|
+
actual_has_attribute?(attribute_key, attribute_value)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def actual_has_attribute?(attribute_key, attribute_value)
|
86
|
+
values_match?(attribute_value, @values.fetch(attribute_key))
|
87
|
+
end
|
88
|
+
|
89
|
+
def respond_to_attributes?
|
90
|
+
matches = respond_to_matcher.matches?(@actual)
|
91
|
+
@respond_to_failed = !matches
|
92
|
+
matches
|
93
|
+
end
|
94
|
+
|
95
|
+
def respond_to_matcher
|
96
|
+
@respond_to_matcher ||= RespondTo.new(*expected.keys).with(0).arguments.tap { |m| m.ignoring_method_signature_failure! }
|
97
|
+
end
|
98
|
+
|
99
|
+
def respond_to_failure_message_or
|
100
|
+
if respond_to_failed
|
101
|
+
respond_to_matcher.failure_message
|
102
|
+
else
|
103
|
+
improve_hash_formatting(yield)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def formatted_values
|
108
|
+
values = RSpec::Support::ObjectFormatter.format(@values)
|
109
|
+
improve_hash_formatting(values)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -1,59 +1,211 @@
|
|
1
|
+
require 'rspec/matchers/built_in/count_expectation'
|
2
|
+
|
1
3
|
module RSpec
|
2
4
|
module Matchers
|
3
5
|
module BuiltIn
|
4
|
-
|
5
|
-
|
6
|
-
|
6
|
+
# @api private
|
7
|
+
# Provides the implementation for `include`.
|
8
|
+
# Not intended to be instantiated directly.
|
9
|
+
class Include < BaseMatcher # rubocop:disable Metrics/ClassLength
|
10
|
+
include CountExpectation
|
11
|
+
# @private
|
12
|
+
attr_reader :expecteds
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
def initialize(*expecteds)
|
16
|
+
@expecteds = expecteds
|
7
17
|
end
|
8
18
|
|
19
|
+
# @api private
|
20
|
+
# @return [Boolean]
|
9
21
|
def matches?(actual)
|
10
|
-
|
11
|
-
|
22
|
+
check_actual?(actual) &&
|
23
|
+
if check_expected_count?
|
24
|
+
expected_count_matches?(count_inclusions)
|
25
|
+
else
|
26
|
+
perform_match { |v| v }
|
27
|
+
end
|
12
28
|
end
|
13
29
|
|
30
|
+
# @api private
|
31
|
+
# @return [Boolean]
|
14
32
|
def does_not_match?(actual)
|
15
|
-
|
16
|
-
|
33
|
+
check_actual?(actual) &&
|
34
|
+
if check_expected_count?
|
35
|
+
!expected_count_matches?(count_inclusions)
|
36
|
+
else
|
37
|
+
perform_match { |v| !v }
|
38
|
+
end
|
17
39
|
end
|
18
40
|
|
41
|
+
# @api private
|
42
|
+
# @return [String]
|
19
43
|
def description
|
20
|
-
"include#{
|
44
|
+
improve_hash_formatting("include#{readable_list_of(expecteds)}#{count_expectation_description}")
|
45
|
+
end
|
46
|
+
|
47
|
+
# @api private
|
48
|
+
# @return [String]
|
49
|
+
def failure_message
|
50
|
+
format_failure_message("to") { super }
|
21
51
|
end
|
22
52
|
|
53
|
+
# @api private
|
54
|
+
# @return [String]
|
55
|
+
def failure_message_when_negated
|
56
|
+
format_failure_message("not to") { super }
|
57
|
+
end
|
58
|
+
|
59
|
+
# @api private
|
60
|
+
# @return [Boolean]
|
23
61
|
def diffable?
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
62
|
+
!diff_would_wrongly_highlight_matched_item?
|
63
|
+
end
|
64
|
+
|
65
|
+
# @api private
|
66
|
+
# @return [Array, Hash]
|
67
|
+
def expected
|
68
|
+
if expecteds.one? && Hash === expecteds.first
|
69
|
+
expecteds.first
|
70
|
+
else
|
71
|
+
expecteds
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def check_actual?(actual)
|
78
|
+
actual = actual.to_hash if convert_to_hash?(actual)
|
79
|
+
@actual = actual
|
80
|
+
@actual.respond_to?(:include?)
|
81
|
+
end
|
82
|
+
|
83
|
+
def check_expected_count?
|
84
|
+
case
|
85
|
+
when !has_expected_count?
|
86
|
+
return false
|
87
|
+
when expecteds.size != 1
|
88
|
+
raise NotImplementedError, 'Count constraint supported only when testing for a single value being included'
|
89
|
+
when actual.is_a?(Hash)
|
90
|
+
raise NotImplementedError, 'Count constraint on hash keys not implemented'
|
91
|
+
end
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
def format_failure_message(preposition)
|
96
|
+
msg = if actual.respond_to?(:include?)
|
97
|
+
"expected #{description_of @actual} #{preposition}" \
|
98
|
+
" include#{readable_list_of @divergent_items}" \
|
99
|
+
"#{count_failure_reason('it is included') if has_expected_count?}"
|
100
|
+
else
|
101
|
+
"#{yield}, but it does not respond to `include?`"
|
102
|
+
end
|
103
|
+
improve_hash_formatting(msg)
|
104
|
+
end
|
105
|
+
|
106
|
+
def readable_list_of(items)
|
107
|
+
described_items = surface_descriptions_in(items)
|
108
|
+
if described_items.all? { |item| item.is_a?(Hash) }
|
109
|
+
" #{described_items.inject(:merge).inspect}"
|
110
|
+
else
|
111
|
+
EnglishPhrasing.list(described_items)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def perform_match(&block)
|
116
|
+
@divergent_items = excluded_from_actual(&block)
|
117
|
+
@divergent_items.empty?
|
118
|
+
end
|
119
|
+
|
120
|
+
def excluded_from_actual
|
121
|
+
return [] unless @actual.respond_to?(:include?)
|
122
|
+
|
123
|
+
expecteds.inject([]) do |memo, expected_item|
|
124
|
+
if comparing_hash_to_a_subset?(expected_item)
|
125
|
+
expected_item.each do |(key, value)|
|
126
|
+
memo << { key => value } unless yield actual_hash_includes?(key, value)
|
127
|
+
end
|
128
|
+
elsif comparing_hash_keys?(expected_item)
|
129
|
+
memo << expected_item unless yield actual_hash_has_key?(expected_item)
|
41
130
|
else
|
42
|
-
|
131
|
+
memo << expected_item unless yield actual_collection_includes?(expected_item)
|
43
132
|
end
|
133
|
+
memo
|
44
134
|
end
|
45
135
|
end
|
46
136
|
|
47
|
-
def
|
48
|
-
actual.is_a?(Hash) &&
|
137
|
+
def comparing_hash_to_a_subset?(expected_item)
|
138
|
+
actual.is_a?(Hash) && expected_item.is_a?(Hash)
|
139
|
+
end
|
140
|
+
|
141
|
+
def actual_hash_includes?(expected_key, expected_value)
|
142
|
+
actual_value =
|
143
|
+
actual.fetch(expected_key) do
|
144
|
+
actual.find(Proc.new { return false }) { |actual_key, _| values_match?(expected_key, actual_key) }[1]
|
145
|
+
end
|
146
|
+
values_match?(expected_value, actual_value)
|
147
|
+
end
|
148
|
+
|
149
|
+
def comparing_hash_keys?(expected_item)
|
150
|
+
actual.is_a?(Hash) && !expected_item.is_a?(Hash)
|
151
|
+
end
|
152
|
+
|
153
|
+
def actual_hash_has_key?(expected_key)
|
154
|
+
# We check `key?` first for perf:
|
155
|
+
# `key?` is O(1), but `any?` is O(N).
|
156
|
+
|
157
|
+
has_exact_key =
|
158
|
+
begin
|
159
|
+
actual.key?(expected_key)
|
160
|
+
rescue
|
161
|
+
false
|
162
|
+
end
|
163
|
+
|
164
|
+
has_exact_key || actual.keys.any? { |key| values_match?(expected_key, key) }
|
49
165
|
end
|
50
166
|
|
51
|
-
def
|
52
|
-
|
167
|
+
def actual_collection_includes?(expected_item)
|
168
|
+
return true if actual.include?(expected_item)
|
169
|
+
|
170
|
+
# String lacks an `any?` method...
|
171
|
+
return false unless actual.respond_to?(:any?)
|
172
|
+
|
173
|
+
actual.any? { |value| values_match?(expected_item, value) }
|
174
|
+
end
|
175
|
+
|
176
|
+
if RUBY_VERSION < '1.9'
|
177
|
+
def count_enumerable(expected_item)
|
178
|
+
actual.select { |value| values_match?(expected_item, value) }.size
|
179
|
+
end
|
180
|
+
else
|
181
|
+
def count_enumerable(expected_item)
|
182
|
+
actual.count { |value| values_match?(expected_item, value) }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def count_inclusions
|
187
|
+
@divergent_items = expected
|
188
|
+
case actual
|
189
|
+
when String
|
190
|
+
actual.scan(expected.first).length
|
191
|
+
when Enumerable
|
192
|
+
count_enumerable(Hash === expected ? expected : expected.first)
|
193
|
+
else
|
194
|
+
raise NotImplementedError, 'Count constraints are implemented for Enumerable and String values only'
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def diff_would_wrongly_highlight_matched_item?
|
199
|
+
return false unless actual.is_a?(String) && expected.is_a?(Array)
|
200
|
+
|
201
|
+
lines = actual.split("\n")
|
202
|
+
expected.any? do |str|
|
203
|
+
actual.include?(str) && lines.none? { |line| line == str }
|
204
|
+
end
|
53
205
|
end
|
54
206
|
|
55
|
-
def
|
56
|
-
|
207
|
+
def convert_to_hash?(obj)
|
208
|
+
!obj.respond_to?(:include?) && obj.respond_to?(:to_hash)
|
57
209
|
end
|
58
210
|
end
|
59
211
|
end
|
@@ -1,11 +1,105 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Matchers
|
3
3
|
module BuiltIn
|
4
|
+
# @api private
|
5
|
+
# Provides the implementation for `match`.
|
6
|
+
# Not intended to be instantiated directly.
|
4
7
|
class Match < BaseMatcher
|
8
|
+
def initialize(expected)
|
9
|
+
super(expected)
|
10
|
+
|
11
|
+
@expected_captures = nil
|
12
|
+
end
|
13
|
+
# @api private
|
14
|
+
# @return [String]
|
15
|
+
def description
|
16
|
+
if @expected_captures && @expected.match(actual)
|
17
|
+
"match #{surface_descriptions_in(expected).inspect} with captures #{surface_descriptions_in(@expected_captures).inspect}"
|
18
|
+
else
|
19
|
+
"match #{surface_descriptions_in(expected).inspect}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
# @return [Boolean]
|
25
|
+
def diffable?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
# Used to specify the captures we match against
|
30
|
+
# @return [self]
|
31
|
+
def with_captures(*captures)
|
32
|
+
@expected_captures = captures
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
5
37
|
|
6
38
|
def match(expected, actual)
|
7
|
-
actual
|
39
|
+
return match_captures(expected, actual) if @expected_captures
|
40
|
+
return true if values_match?(expected, actual)
|
41
|
+
return false unless can_safely_call_match?(expected, actual)
|
42
|
+
actual.match(expected)
|
43
|
+
end
|
44
|
+
|
45
|
+
def can_safely_call_match?(expected, actual)
|
46
|
+
return false unless actual.respond_to?(:match)
|
47
|
+
|
48
|
+
!(RSpec::Matchers.is_a_matcher?(expected) &&
|
49
|
+
(String === actual || Regexp === actual))
|
8
50
|
end
|
51
|
+
|
52
|
+
def match_captures(expected, actual)
|
53
|
+
match = actual.match(expected)
|
54
|
+
if match
|
55
|
+
match = ReliableMatchData.new(match)
|
56
|
+
if match.names.empty?
|
57
|
+
values_match?(@expected_captures, match.captures)
|
58
|
+
else
|
59
|
+
expected_matcher = @expected_captures.last
|
60
|
+
values_match?(expected_matcher, Hash[match.names.zip(match.captures)]) ||
|
61
|
+
values_match?(expected_matcher, Hash[match.names.map(&:to_sym).zip(match.captures)]) ||
|
62
|
+
values_match?(@expected_captures, match.captures)
|
63
|
+
end
|
64
|
+
else
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# @api private
|
71
|
+
# Used to wrap match data and make it reliable for 1.8.7
|
72
|
+
class ReliableMatchData
|
73
|
+
def initialize(match_data)
|
74
|
+
@match_data = match_data
|
75
|
+
end
|
76
|
+
|
77
|
+
if RUBY_VERSION == "1.8.7"
|
78
|
+
# @api private
|
79
|
+
# Returns match data names for named captures
|
80
|
+
# @return Array
|
81
|
+
def names
|
82
|
+
[]
|
83
|
+
end
|
84
|
+
else
|
85
|
+
# @api private
|
86
|
+
# Returns match data names for named captures
|
87
|
+
# @return Array
|
88
|
+
def names
|
89
|
+
match_data.names
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# @api private
|
94
|
+
# returns an array of captures from the match data
|
95
|
+
# @return Array
|
96
|
+
def captures
|
97
|
+
match_data.captures
|
98
|
+
end
|
99
|
+
|
100
|
+
protected
|
101
|
+
|
102
|
+
attr_reader :match_data
|
9
103
|
end
|
10
104
|
end
|
11
105
|
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'rspec/support'
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Matchers
|
5
|
+
module BuiltIn
|
6
|
+
# @api private
|
7
|
+
# Provides the implementation for operator matchers.
|
8
|
+
# Not intended to be instantiated directly.
|
9
|
+
# Only available for use with `should`.
|
10
|
+
class OperatorMatcher
|
11
|
+
class << self
|
12
|
+
# @private
|
13
|
+
def registry
|
14
|
+
@registry ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# @private
|
18
|
+
def register(klass, operator, matcher)
|
19
|
+
registry[klass] ||= {}
|
20
|
+
registry[klass][operator] = matcher
|
21
|
+
end
|
22
|
+
|
23
|
+
# @private
|
24
|
+
def unregister(klass, operator)
|
25
|
+
registry[klass] && registry[klass].delete(operator)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @private
|
29
|
+
def get(klass, operator)
|
30
|
+
klass.ancestors.each do |ancestor|
|
31
|
+
matcher = registry[ancestor] && registry[ancestor][operator]
|
32
|
+
return matcher if matcher
|
33
|
+
end
|
34
|
+
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
register Enumerable, '=~', BuiltIn::ContainExactly
|
40
|
+
|
41
|
+
def initialize(actual)
|
42
|
+
@actual = actual
|
43
|
+
end
|
44
|
+
|
45
|
+
# @private
|
46
|
+
def self.use_custom_matcher_or_delegate(operator)
|
47
|
+
define_method(operator) do |expected|
|
48
|
+
if !has_non_generic_implementation_of?(operator) && (matcher = OperatorMatcher.get(@actual.class, operator))
|
49
|
+
@actual.__send__(::RSpec::Matchers.last_expectation_handler.should_method, matcher.new(expected))
|
50
|
+
else
|
51
|
+
eval_match(@actual, operator, expected)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
negative_operator = operator.sub(/^=/, '!')
|
56
|
+
if negative_operator != operator && respond_to?(negative_operator)
|
57
|
+
define_method(negative_operator) do |_expected|
|
58
|
+
opposite_should = ::RSpec::Matchers.last_expectation_handler.opposite_should_method
|
59
|
+
raise "RSpec does not support `#{::RSpec::Matchers.last_expectation_handler.should_method} #{negative_operator} expected`. " \
|
60
|
+
"Use `#{opposite_should} #{operator} expected` instead."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
['==', '===', '=~', '>', '>=', '<', '<='].each do |operator|
|
66
|
+
use_custom_matcher_or_delegate operator
|
67
|
+
end
|
68
|
+
|
69
|
+
# @private
|
70
|
+
def fail_with_message(message)
|
71
|
+
RSpec::Expectations.fail_with(message, @expected, @actual)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @api private
|
75
|
+
# @return [String]
|
76
|
+
def description
|
77
|
+
"#{@operator} #{RSpec::Support::ObjectFormatter.format(@expected)}"
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def has_non_generic_implementation_of?(op)
|
83
|
+
Support.method_handle_for(@actual, op).owner != ::Kernel
|
84
|
+
rescue NameError
|
85
|
+
false
|
86
|
+
end
|
87
|
+
|
88
|
+
def eval_match(actual, operator, expected)
|
89
|
+
::RSpec::Matchers.last_matcher = self
|
90
|
+
@operator, @expected = operator, expected
|
91
|
+
__delegate_operator(actual, operator, expected)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# @private
|
96
|
+
# Handles operator matcher for `should`.
|
97
|
+
class PositiveOperatorMatcher < OperatorMatcher
|
98
|
+
def __delegate_operator(actual, operator, expected)
|
99
|
+
if actual.__send__(operator, expected)
|
100
|
+
true
|
101
|
+
else
|
102
|
+
expected_formatted = RSpec::Support::ObjectFormatter.format(expected)
|
103
|
+
actual_formatted = RSpec::Support::ObjectFormatter.format(actual)
|
104
|
+
|
105
|
+
if ['==', '===', '=~'].include?(operator)
|
106
|
+
fail_with_message("expected: #{expected_formatted}\n got: #{actual_formatted} (using #{operator})")
|
107
|
+
else
|
108
|
+
fail_with_message("expected: #{operator} #{expected_formatted}\n got: #{operator.gsub(/./, ' ')} #{actual_formatted}")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# @private
|
115
|
+
# Handles operator matcher for `should_not`.
|
116
|
+
class NegativeOperatorMatcher < OperatorMatcher
|
117
|
+
def __delegate_operator(actual, operator, expected)
|
118
|
+
return false unless actual.__send__(operator, expected)
|
119
|
+
|
120
|
+
expected_formatted = RSpec::Support::ObjectFormatter.format(expected)
|
121
|
+
actual_formatted = RSpec::Support::ObjectFormatter.format(actual)
|
122
|
+
|
123
|
+
fail_with_message("expected not: #{operator} #{expected_formatted}\n got: #{operator.gsub(/./, ' ')} #{actual_formatted}")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|