rspec-expectations 2.11.3 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +1 -1
- data/Changelog.md +1026 -21
- data/{License.txt → LICENSE.md} +5 -3
- data/README.md +174 -78
- 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 +130 -55
- data/lib/rspec/expectations/fail_with.rb +17 -33
- data/lib/rspec/expectations/failure_aggregator.rb +212 -0
- data/lib/rspec/expectations/handler.rb +163 -29
- data/lib/rspec/expectations/minitest_integration.rb +58 -0
- data/lib/rspec/expectations/syntax.rb +68 -54
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/expectations.rb +59 -24
- 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 +150 -20
- data/lib/rspec/matchers/built_in/be.rb +115 -109
- data/lib/rspec/matchers/built_in/be_between.rb +77 -0
- data/lib/rspec/matchers/built_in/be_instance_of.rb +16 -1
- data/lib/rspec/matchers/built_in/be_kind_of.rb +10 -1
- data/lib/rspec/matchers/built_in/be_within.rb +43 -17
- data/lib/rspec/matchers/built_in/change.rb +392 -75
- data/lib/rspec/matchers/built_in/compound.rb +290 -0
- data/lib/rspec/matchers/built_in/contain_exactly.rb +302 -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 +26 -8
- data/lib/rspec/matchers/built_in/eql.rb +19 -8
- data/lib/rspec/matchers/built_in/equal.rb +56 -19
- 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 +175 -20
- 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 +212 -38
- data/lib/rspec/matchers/built_in/respond_to.rb +155 -29
- 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 +252 -98
- data/lib/rspec/matchers/built_in.rb +47 -33
- data/lib/rspec/matchers/composable.rb +171 -0
- data/lib/rspec/matchers/dsl.rb +530 -10
- data/lib/rspec/matchers/english_phrasing.rb +58 -0
- data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +82 -0
- data/lib/rspec/matchers/fail_matchers.rb +42 -0
- data/lib/rspec/matchers/generated_descriptions.rb +15 -10
- data/lib/rspec/matchers/matcher_delegator.rb +35 -0
- data/lib/rspec/matchers/matcher_protocol.rb +105 -0
- data/lib/rspec/matchers.rb +604 -252
- data.tar.gz.sig +0 -0
- metadata +178 -278
- metadata.gz.sig +0 -0
- data/features/README.md +0 -49
- data/features/Upgrade.md +0 -53
- data/features/built_in_matchers/README.md +0 -90
- data/features/built_in_matchers/be.feature +0 -173
- data/features/built_in_matchers/be_within.feature +0 -46
- data/features/built_in_matchers/cover.feature +0 -45
- data/features/built_in_matchers/end_with.feature +0 -46
- data/features/built_in_matchers/equality.feature +0 -145
- data/features/built_in_matchers/exist.feature +0 -43
- data/features/built_in_matchers/expect_change.feature +0 -59
- data/features/built_in_matchers/expect_error.feature +0 -138
- data/features/built_in_matchers/have.feature +0 -103
- data/features/built_in_matchers/include.feature +0 -121
- data/features/built_in_matchers/match.feature +0 -50
- data/features/built_in_matchers/operators.feature +0 -221
- data/features/built_in_matchers/predicates.feature +0 -128
- data/features/built_in_matchers/respond_to.feature +0 -78
- data/features/built_in_matchers/satisfy.feature +0 -31
- data/features/built_in_matchers/start_with.feature +0 -46
- data/features/built_in_matchers/throw_symbol.feature +0 -85
- data/features/built_in_matchers/types.feature +0 -114
- data/features/built_in_matchers/yield.feature +0 -146
- 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 -340
- 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 -5
- data/features/syntax_configuration.feature +0 -68
- data/features/test_frameworks/test_unit.feature +0 -46
- data/lib/rspec/expectations/deprecation.rb +0 -38
- data/lib/rspec/expectations/differ.rb +0 -81
- 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 -39
- 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 -108
- data/lib/rspec/matchers/built_in/match_array.rb +0 -45
- 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 -66
- data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
- data/lib/rspec/matchers/matcher.rb +0 -299
- data/lib/rspec/matchers/method_missing.rb +0 -12
- data/lib/rspec/matchers/operator_matcher.rb +0 -84
- data/lib/rspec/matchers/pretty.rb +0 -60
- data/lib/rspec-expectations.rb +0 -1
- data/spec/rspec/expectations/differ_spec.rb +0 -153
- data/spec/rspec/expectations/expectation_target_spec.rb +0 -65
- data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
- data/spec/rspec/expectations/fail_with_spec.rb +0 -70
- data/spec/rspec/expectations/handler_spec.rb +0 -206
- data/spec/rspec/matchers/base_matcher_spec.rb +0 -60
- data/spec/rspec/matchers/be_close_spec.rb +0 -22
- data/spec/rspec/matchers/be_instance_of_spec.rb +0 -40
- data/spec/rspec/matchers/be_kind_of_spec.rb +0 -37
- data/spec/rspec/matchers/be_spec.rb +0 -452
- data/spec/rspec/matchers/be_within_spec.rb +0 -80
- data/spec/rspec/matchers/change_spec.rb +0 -528
- data/spec/rspec/matchers/configuration_spec.rb +0 -202
- data/spec/rspec/matchers/cover_spec.rb +0 -69
- data/spec/rspec/matchers/description_generation_spec.rb +0 -176
- data/spec/rspec/matchers/dsl_spec.rb +0 -57
- data/spec/rspec/matchers/eq_spec.rb +0 -54
- data/spec/rspec/matchers/eql_spec.rb +0 -41
- data/spec/rspec/matchers/equal_spec.rb +0 -60
- data/spec/rspec/matchers/exist_spec.rb +0 -110
- data/spec/rspec/matchers/has_spec.rb +0 -118
- data/spec/rspec/matchers/have_spec.rb +0 -461
- data/spec/rspec/matchers/include_spec.rb +0 -367
- data/spec/rspec/matchers/match_array_spec.rb +0 -124
- data/spec/rspec/matchers/match_spec.rb +0 -61
- data/spec/rspec/matchers/matcher_spec.rb +0 -434
- data/spec/rspec/matchers/matchers_spec.rb +0 -31
- data/spec/rspec/matchers/method_missing_spec.rb +0 -24
- data/spec/rspec/matchers/operator_matcher_spec.rb +0 -221
- data/spec/rspec/matchers/raise_error_spec.rb +0 -344
- data/spec/rspec/matchers/respond_to_spec.rb +0 -295
- data/spec/rspec/matchers/satisfy_spec.rb +0 -44
- data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -182
- data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
- data/spec/rspec/matchers/yield_spec.rb +0 -402
- data/spec/spec_helper.rb +0 -27
- data/spec/support/classes.rb +0 -56
- data/spec/support/in_sub_process.rb +0 -31
- data/spec/support/matchers.rb +0 -22
- data/spec/support/ruby_version.rb +0 -10
- data/spec/support/shared_examples.rb +0 -13
@@ -1,131 +1,448 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Matchers
|
3
3
|
module BuiltIn
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
# @api private
|
5
|
+
# Provides the implementation for `change`.
|
6
|
+
# Not intended to be instantiated directly.
|
7
|
+
class Change < BaseMatcher
|
8
|
+
# @api public
|
9
|
+
# Specifies the delta of the expected change.
|
10
|
+
def by(expected_delta)
|
11
|
+
ChangeRelatively.new(change_details, expected_delta, :by) do |actual_delta|
|
12
|
+
values_match?(expected_delta, actual_delta)
|
13
|
+
end
|
10
14
|
end
|
11
15
|
|
16
|
+
# @api public
|
17
|
+
# Specifies a minimum delta of the expected change.
|
18
|
+
def by_at_least(minimum)
|
19
|
+
ChangeRelatively.new(change_details, minimum, :by_at_least) do |actual_delta|
|
20
|
+
actual_delta >= minimum
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @api public
|
25
|
+
# Specifies a maximum delta of the expected change.
|
26
|
+
def by_at_most(maximum)
|
27
|
+
ChangeRelatively.new(change_details, maximum, :by_at_most) do |actual_delta|
|
28
|
+
actual_delta <= maximum
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# @api public
|
33
|
+
# Specifies the new value you expect.
|
34
|
+
def to(value)
|
35
|
+
ChangeToValue.new(change_details, value)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @api public
|
39
|
+
# Specifies the original value.
|
40
|
+
def from(value)
|
41
|
+
ChangeFromValue.new(change_details, value)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @private
|
12
45
|
def matches?(event_proc)
|
13
46
|
raise_block_syntax_error if block_given?
|
47
|
+
perform_change(event_proc) && change_details.changed?
|
48
|
+
end
|
49
|
+
|
50
|
+
def does_not_match?(event_proc)
|
51
|
+
raise_block_syntax_error if block_given?
|
52
|
+
perform_change(event_proc) && !change_details.changed?
|
53
|
+
end
|
14
54
|
|
15
|
-
|
16
|
-
|
17
|
-
|
55
|
+
# @api private
|
56
|
+
# @return [String]
|
57
|
+
def failure_message
|
58
|
+
"expected #{change_details.value_representation} to have changed, " \
|
59
|
+
"but #{positive_failure_reason}"
|
60
|
+
end
|
18
61
|
|
19
|
-
|
62
|
+
# @api private
|
63
|
+
# @return [String]
|
64
|
+
def failure_message_when_negated
|
65
|
+
"expected #{change_details.value_representation} not to have changed, " \
|
66
|
+
"but #{negative_failure_reason}"
|
20
67
|
end
|
21
|
-
alias == matches?
|
22
68
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
69
|
+
# @api private
|
70
|
+
# @return [String]
|
71
|
+
def description
|
72
|
+
"change #{change_details.value_representation}"
|
27
73
|
end
|
28
74
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
val.dup
|
33
|
-
else
|
34
|
-
val
|
35
|
-
end
|
75
|
+
# @private
|
76
|
+
def supports_block_expectations?
|
77
|
+
true
|
36
78
|
end
|
37
79
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
80
|
+
# @private
|
81
|
+
def supports_value_expectations?
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def initialize(receiver=nil, message=nil, &block)
|
88
|
+
@receiver = receiver
|
89
|
+
@message = message
|
90
|
+
@block = block
|
91
|
+
end
|
92
|
+
|
93
|
+
def change_details
|
94
|
+
@change_details ||= ChangeDetails.new(matcher_name, @receiver, @message, &@block)
|
95
|
+
end
|
96
|
+
|
97
|
+
def perform_change(event_proc)
|
98
|
+
@event_proc = event_proc
|
99
|
+
change_details.perform_change(event_proc) do |actual_before|
|
100
|
+
# pre-compute values derived from the `before` value before the
|
101
|
+
# mutation is applied, in case the specified mutation is mutation
|
102
|
+
# of a single object (rather than a changing what object a method
|
103
|
+
# returns). We need to cache these values before the `before` value
|
104
|
+
# they are based on potentially gets mutated.
|
105
|
+
@actual_before_description = description_of(actual_before)
|
51
106
|
end
|
52
107
|
end
|
53
108
|
|
54
|
-
def
|
55
|
-
|
109
|
+
def raise_block_syntax_error
|
110
|
+
raise SyntaxError, "Block not received by the `change` matcher. " \
|
111
|
+
"Perhaps you want to use `{ ... }` instead of do/end?"
|
56
112
|
end
|
57
113
|
|
58
|
-
def
|
59
|
-
"
|
114
|
+
def positive_failure_reason
|
115
|
+
return "was not given a block" unless Proc === @event_proc
|
116
|
+
"is still #{@actual_before_description}"
|
60
117
|
end
|
61
118
|
|
62
|
-
def
|
119
|
+
def negative_failure_reason
|
120
|
+
return "was not given a block" unless Proc === @event_proc
|
121
|
+
"did change from #{@actual_before_description} " \
|
122
|
+
"to #{description_of change_details.actual_after}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Used to specify a relative change.
|
127
|
+
# @api private
|
128
|
+
class ChangeRelatively < BaseMatcher
|
129
|
+
def initialize(change_details, expected_delta, relativity, &comparer)
|
130
|
+
@change_details = change_details
|
63
131
|
@expected_delta = expected_delta
|
64
|
-
|
132
|
+
@relativity = relativity
|
133
|
+
@comparer = comparer
|
65
134
|
end
|
66
135
|
|
67
|
-
|
68
|
-
|
69
|
-
|
136
|
+
# @private
|
137
|
+
def failure_message
|
138
|
+
"expected #{@change_details.value_representation} to have changed " \
|
139
|
+
"#{@relativity.to_s.tr('_', ' ')} " \
|
140
|
+
"#{description_of @expected_delta}, but #{failure_reason}"
|
70
141
|
end
|
71
142
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
143
|
+
# @private
|
144
|
+
def matches?(event_proc)
|
145
|
+
@event_proc = event_proc
|
146
|
+
@change_details.perform_change(event_proc) && @comparer.call(@change_details.actual_delta)
|
147
|
+
end
|
148
|
+
|
149
|
+
# @private
|
150
|
+
def does_not_match?(_event_proc)
|
151
|
+
raise NotImplementedError, "`expect { }.not_to change " \
|
152
|
+
"{ }.#{@relativity}()` is not supported"
|
153
|
+
end
|
154
|
+
|
155
|
+
# @private
|
156
|
+
def description
|
157
|
+
"change #{@change_details.value_representation} " \
|
158
|
+
"#{@relativity.to_s.tr('_', ' ')} #{description_of @expected_delta}"
|
159
|
+
end
|
160
|
+
|
161
|
+
# @private
|
162
|
+
def supports_block_expectations?
|
163
|
+
true
|
164
|
+
end
|
165
|
+
|
166
|
+
# @private
|
167
|
+
def supports_value_expectations?
|
168
|
+
false
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
def failure_reason
|
174
|
+
return "was not given a block" unless Proc === @event_proc
|
175
|
+
"was changed by #{description_of @change_details.actual_delta}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# @api private
|
180
|
+
# Base class for specifying a change from and/or to specific values.
|
181
|
+
class SpecificValuesChange < BaseMatcher
|
182
|
+
# @private
|
183
|
+
MATCH_ANYTHING = ::Object.ancestors.last
|
184
|
+
|
185
|
+
def initialize(change_details, from, to)
|
186
|
+
@change_details = change_details
|
187
|
+
@expected_before = from
|
188
|
+
@expected_after = to
|
189
|
+
end
|
190
|
+
|
191
|
+
# @private
|
192
|
+
def matches?(event_proc)
|
193
|
+
perform_change(event_proc) && @change_details.changed? && @matches_before && matches_after?
|
194
|
+
end
|
195
|
+
|
196
|
+
# @private
|
197
|
+
def description
|
198
|
+
"change #{@change_details.value_representation} #{change_description}"
|
199
|
+
end
|
200
|
+
|
201
|
+
# @private
|
202
|
+
def failure_message
|
203
|
+
return not_given_a_block_failure unless Proc === @event_proc
|
204
|
+
return before_value_failure unless @matches_before
|
205
|
+
return did_not_change_failure unless @change_details.changed?
|
206
|
+
after_value_failure
|
207
|
+
end
|
208
|
+
|
209
|
+
# @private
|
210
|
+
def supports_block_expectations?
|
211
|
+
true
|
212
|
+
end
|
213
|
+
|
214
|
+
# @private
|
215
|
+
def supports_value_expectations?
|
216
|
+
false
|
217
|
+
end
|
218
|
+
|
219
|
+
private
|
220
|
+
|
221
|
+
def perform_change(event_proc)
|
222
|
+
@event_proc = event_proc
|
223
|
+
@change_details.perform_change(event_proc) do |actual_before|
|
224
|
+
# pre-compute values derived from the `before` value before the
|
225
|
+
# mutation is applied, in case the specified mutation is mutation
|
226
|
+
# of a single object (rather than a changing what object a method
|
227
|
+
# returns). We need to cache these values before the `before` value
|
228
|
+
# they are based on potentially gets mutated.
|
229
|
+
@matches_before = values_match?(@expected_before, actual_before)
|
230
|
+
@actual_before_description = description_of(actual_before)
|
231
|
+
end
|
232
|
+
end
|
76
233
|
|
77
|
-
def
|
78
|
-
@
|
79
|
-
|
234
|
+
def matches_after?
|
235
|
+
values_match?(@expected_after, @change_details.actual_after)
|
236
|
+
end
|
237
|
+
|
238
|
+
def before_value_failure
|
239
|
+
"expected #{@change_details.value_representation} " \
|
240
|
+
"to have initially been #{description_of @expected_before}, " \
|
241
|
+
"but was #{@actual_before_description}"
|
242
|
+
end
|
243
|
+
|
244
|
+
def after_value_failure
|
245
|
+
"expected #{@change_details.value_representation} " \
|
246
|
+
"to have changed to #{description_of @expected_after}, " \
|
247
|
+
"but is now #{description_of @change_details.actual_after}"
|
248
|
+
end
|
249
|
+
|
250
|
+
def did_not_change_failure
|
251
|
+
"expected #{@change_details.value_representation} " \
|
252
|
+
"to have changed #{change_description}, but did not change"
|
253
|
+
end
|
254
|
+
|
255
|
+
def did_change_failure
|
256
|
+
"expected #{@change_details.value_representation} not to have changed, but " \
|
257
|
+
"did change from #{@actual_before_description} " \
|
258
|
+
"to #{description_of @change_details.actual_after}"
|
259
|
+
end
|
260
|
+
|
261
|
+
def not_given_a_block_failure
|
262
|
+
"expected #{@change_details.value_representation} to have changed " \
|
263
|
+
"#{change_description}, but was not given a block"
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# @api private
|
268
|
+
# Used to specify a change from a specific value
|
269
|
+
# (and, optionally, to a specific value).
|
270
|
+
class ChangeFromValue < SpecificValuesChange
|
271
|
+
def initialize(change_details, expected_before)
|
272
|
+
@description_suffix = nil
|
273
|
+
super(change_details, expected_before, MATCH_ANYTHING)
|
274
|
+
end
|
275
|
+
|
276
|
+
# @api public
|
277
|
+
# Specifies the new value you expect.
|
278
|
+
def to(value)
|
279
|
+
@expected_after = value
|
280
|
+
@description_suffix = " to #{description_of value}"
|
80
281
|
self
|
81
282
|
end
|
82
283
|
|
83
|
-
|
84
|
-
|
85
|
-
@
|
284
|
+
# @private
|
285
|
+
def does_not_match?(event_proc)
|
286
|
+
if @description_suffix
|
287
|
+
raise NotImplementedError, "`expect { }.not_to change { }.to()` " \
|
288
|
+
"is not supported"
|
289
|
+
end
|
290
|
+
|
291
|
+
perform_change(event_proc) && !@change_details.changed? && @matches_before
|
292
|
+
end
|
293
|
+
|
294
|
+
# @private
|
295
|
+
def failure_message_when_negated
|
296
|
+
return not_given_a_block_failure unless Proc === @event_proc
|
297
|
+
return before_value_failure unless @matches_before
|
298
|
+
did_change_failure
|
299
|
+
end
|
300
|
+
|
301
|
+
private
|
302
|
+
|
303
|
+
def change_description
|
304
|
+
"from #{description_of @expected_before}#{@description_suffix}"
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
# @api private
|
309
|
+
# Used to specify a change to a specific value
|
310
|
+
# (and, optionally, from a specific value).
|
311
|
+
class ChangeToValue < SpecificValuesChange
|
312
|
+
def initialize(change_details, expected_after)
|
313
|
+
@description_suffix = nil
|
314
|
+
super(change_details, MATCH_ANYTHING, expected_after)
|
315
|
+
end
|
316
|
+
|
317
|
+
# @api public
|
318
|
+
# Specifies the original value.
|
319
|
+
def from(value)
|
320
|
+
@expected_before = value
|
321
|
+
@description_suffix = " from #{description_of value}"
|
86
322
|
self
|
87
323
|
end
|
88
324
|
|
89
|
-
|
90
|
-
|
325
|
+
# @private
|
326
|
+
def does_not_match?(_event_proc)
|
327
|
+
raise NotImplementedError, "`expect { }.not_to change { }.to()` " \
|
328
|
+
"is not supported"
|
91
329
|
end
|
92
330
|
|
93
|
-
|
331
|
+
private
|
94
332
|
|
95
|
-
def
|
96
|
-
|
333
|
+
def change_description
|
334
|
+
"to #{description_of @expected_after}#{@description_suffix}"
|
97
335
|
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# @private
|
339
|
+
# Encapsulates the details of the before/after values.
|
340
|
+
#
|
341
|
+
# Note that this class exposes the `actual_after` value, to allow the
|
342
|
+
# matchers above to derive failure messages, etc from the value on demand
|
343
|
+
# as needed, but it intentionally does _not_ expose the `actual_before`
|
344
|
+
# value. Some usages of the `change` matcher mutate a specific object
|
345
|
+
# returned by the value proc, which means that failure message snippets,
|
346
|
+
# etc, which are derived from the `before` value may not be accurate if
|
347
|
+
# they are lazily computed as needed. We must pre-compute them before
|
348
|
+
# applying the change in the `expect` block. To ensure that all `change`
|
349
|
+
# matchers do that properly, we do not expose the `actual_before` value.
|
350
|
+
# Instead, matchers must pass a block to `perform_change`, which yields
|
351
|
+
# the `actual_before` value before applying the change.
|
352
|
+
class ChangeDetails
|
353
|
+
attr_reader :actual_after
|
354
|
+
|
355
|
+
UNDEFINED = Module.new.freeze
|
356
|
+
|
357
|
+
def initialize(matcher_name, receiver=nil, message=nil, &block)
|
358
|
+
if receiver && !message
|
359
|
+
raise(
|
360
|
+
ArgumentError,
|
361
|
+
"`change` requires either an object and message " \
|
362
|
+
"(`change(obj, :msg)`) or a block (`change { }`). " \
|
363
|
+
"You passed an object but no message."
|
364
|
+
)
|
365
|
+
end
|
98
366
|
|
99
|
-
|
100
|
-
@
|
367
|
+
@matcher_name = matcher_name
|
368
|
+
@receiver = receiver
|
369
|
+
@message = message
|
370
|
+
@value_proc = block
|
371
|
+
# TODO: temporary measure to mute warning of access to an initialized
|
372
|
+
# instance variable when a deprecated implicit block expectation
|
373
|
+
# syntax is used. This may be removed once `fail` is used, and the
|
374
|
+
# matcher never issues this warning.
|
375
|
+
@actual_after = UNDEFINED
|
101
376
|
end
|
102
377
|
|
103
|
-
def
|
104
|
-
@
|
378
|
+
def value_representation
|
379
|
+
@value_representation ||=
|
380
|
+
if @message
|
381
|
+
"`#{message_notation(@receiver, @message)}`"
|
382
|
+
elsif (value_block_snippet = extract_value_block_snippet)
|
383
|
+
"`#{value_block_snippet}`"
|
384
|
+
else
|
385
|
+
'result'
|
386
|
+
end
|
105
387
|
end
|
106
388
|
|
107
|
-
def
|
108
|
-
@
|
389
|
+
def perform_change(event_proc)
|
390
|
+
@actual_before = evaluate_value_proc
|
391
|
+
@before_hash = @actual_before.hash
|
392
|
+
yield @actual_before if block_given?
|
393
|
+
|
394
|
+
return false unless Proc === event_proc
|
395
|
+
event_proc.call
|
396
|
+
|
397
|
+
@actual_after = evaluate_value_proc
|
398
|
+
@actual_hash = @actual_after.hash
|
399
|
+
true
|
109
400
|
end
|
110
401
|
|
111
|
-
def
|
112
|
-
|
402
|
+
def changed?
|
403
|
+
# Consider it changed if either:
|
404
|
+
#
|
405
|
+
# - The before/after values are unequal
|
406
|
+
# - The before/after values have different hash values
|
407
|
+
#
|
408
|
+
# The latter case specifically handles the case when the value proc
|
409
|
+
# returns the exact same object, but it has been mutated.
|
410
|
+
#
|
411
|
+
# Note that it is not sufficient to only check the hashes; it is
|
412
|
+
# possible for two values to be unequal (and of different classes)
|
413
|
+
# but to return the same hash value. Also, some objects may change
|
414
|
+
# their hash after being compared with `==`/`!=`.
|
415
|
+
@actual_before != @actual_after || @before_hash != @actual_hash
|
113
416
|
end
|
114
417
|
|
115
|
-
def
|
116
|
-
@
|
418
|
+
def actual_delta
|
419
|
+
@actual_after - @actual_before
|
117
420
|
end
|
118
421
|
|
119
|
-
|
120
|
-
|
422
|
+
private
|
423
|
+
|
424
|
+
def evaluate_value_proc
|
425
|
+
@value_proc ? @value_proc.call : @receiver.__send__(@message)
|
121
426
|
end
|
122
427
|
|
123
|
-
def
|
124
|
-
|
428
|
+
def message_notation(receiver, message)
|
429
|
+
case receiver
|
430
|
+
when Module
|
431
|
+
"#{receiver}.#{message}"
|
432
|
+
else
|
433
|
+
"#{Support.class_of(receiver)}##{message}"
|
434
|
+
end
|
125
435
|
end
|
126
436
|
|
127
|
-
|
128
|
-
|
437
|
+
if RSpec::Support::RubyFeatures.ripper_supported?
|
438
|
+
def extract_value_block_snippet
|
439
|
+
return nil unless @value_proc
|
440
|
+
Expectations::BlockSnippetExtractor.try_extracting_single_line_body_of(@value_proc, @matcher_name)
|
441
|
+
end
|
442
|
+
else
|
443
|
+
def extract_value_block_snippet
|
444
|
+
nil
|
445
|
+
end
|
129
446
|
end
|
130
447
|
end
|
131
448
|
end
|