shoulda-matchers 3.0.1 → 3.1.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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +3 -3
- data/CONTRIBUTING.md +60 -28
- data/Gemfile +1 -0
- data/Gemfile.lock +15 -12
- data/NEWS.md +111 -0
- data/README.md +94 -6
- data/Rakefile +10 -8
- data/custom_plan.rb +88 -0
- data/gemfiles/4.0.0.gemfile +1 -0
- data/gemfiles/4.0.0.gemfile.lock +21 -18
- data/gemfiles/4.0.1.gemfile +1 -0
- data/gemfiles/4.0.1.gemfile.lock +21 -18
- data/gemfiles/4.1.gemfile +1 -0
- data/gemfiles/4.1.gemfile.lock +21 -18
- data/gemfiles/4.2.gemfile +1 -0
- data/gemfiles/4.2.gemfile.lock +24 -21
- data/lib/shoulda/matchers/action_controller/permit_matcher.rb +6 -11
- data/lib/shoulda/matchers/active_model.rb +10 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +258 -180
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +236 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +34 -14
- data/lib/shoulda/matchers/active_model/helpers.rb +9 -17
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +13 -6
- data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +13 -2
- data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +19 -35
- data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +13 -2
- data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +12 -2
- data/lib/shoulda/matchers/active_model/qualifiers.rb +12 -0
- data/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
- data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +30 -32
- data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +5 -8
- data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +22 -22
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +27 -16
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +58 -15
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +22 -12
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +165 -87
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +7 -9
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +111 -49
- data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +60 -0
- data/lib/shoulda/matchers/active_model/validator.rb +71 -52
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +19 -5
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +450 -124
- data/lib/shoulda/matchers/util.rb +43 -0
- data/lib/shoulda/matchers/util/word_wrap.rb +59 -31
- data/lib/shoulda/matchers/version.rb +1 -1
- data/script/update_gem_in_all_appraisals +1 -1
- data/script/update_gems_in_all_appraisals +1 -1
- data/spec/acceptance/multiple_libraries_integration_spec.rb +5 -2
- data/spec/acceptance/rails_integration_spec.rb +6 -2
- data/spec/spec_helper.rb +1 -3
- data/spec/support/acceptance/helpers/step_helpers.rb +4 -1
- data/spec/support/tests/current_bundle.rb +21 -7
- data/spec/support/unit/active_record/create_table.rb +54 -0
- data/spec/support/unit/attribute.rb +47 -0
- data/spec/support/unit/capture.rb +6 -0
- data/spec/support/unit/change_value.rb +111 -0
- data/spec/support/unit/create_model_arguments/basic.rb +135 -0
- data/spec/support/unit/create_model_arguments/has_many.rb +15 -0
- data/spec/support/unit/create_model_arguments/uniqueness_matcher.rb +74 -0
- data/spec/support/unit/helpers/active_record_versions.rb +1 -1
- data/spec/support/unit/helpers/class_builder.rb +61 -47
- data/spec/support/unit/helpers/database_helpers.rb +5 -3
- data/spec/support/unit/helpers/model_builder.rb +77 -97
- data/spec/support/unit/helpers/validation_matcher_scenario_helpers.rb +44 -0
- data/spec/support/unit/load_environment.rb +12 -0
- data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +2 -2
- data/spec/support/unit/matchers/fail_with_message_matcher.rb +12 -1
- data/spec/support/unit/model_creation_strategies/active_model.rb +111 -0
- data/spec/support/unit/model_creation_strategies/active_record.rb +77 -0
- data/spec/support/unit/model_creators.rb +19 -0
- data/spec/support/unit/model_creators/active_model.rb +39 -0
- data/spec/support/unit/model_creators/active_record.rb +43 -0
- data/spec/support/unit/model_creators/active_record/has_and_belongs_to_many.rb +95 -0
- data/spec/support/unit/model_creators/active_record/has_many.rb +67 -0
- data/spec/support/unit/model_creators/active_record/uniqueness_matcher.rb +42 -0
- data/spec/support/unit/model_creators/basic.rb +97 -0
- data/spec/support/unit/rails_application.rb +1 -1
- data/spec/support/unit/record_validating_confirmation_builder.rb +3 -7
- data/spec/support/unit/shared_examples/ignoring_interference_by_writer.rb +79 -0
- data/spec/support/unit/validation_matcher_scenario.rb +62 -0
- data/spec/unit/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +4 -0
- data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +575 -140
- data/spec/unit/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +115 -15
- data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +42 -4
- data/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +92 -6
- data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +122 -10
- data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +306 -58
- data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +122 -3
- data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +805 -131
- data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +196 -29
- data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +82 -40
- data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +600 -101
- data/spec/unit/shoulda/matchers/util/word_wrap_spec.rb +88 -33
- data/spec/unit_spec_helper.rb +10 -22
- data/zeus.json +11 -0
- metadata +64 -23
- data/lib/shoulda/matchers/active_model/strict_validator.rb +0 -51
- data/spec/support/unit/shared_examples/numerical_type_submatcher.rb +0 -15
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +0 -288
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +0 -100
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +0 -100
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +0 -100
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module UnitTests
|
4
|
+
class ValidationMatcherScenario
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :matcher
|
8
|
+
|
9
|
+
def initialize(arguments)
|
10
|
+
@arguments = arguments.dup
|
11
|
+
@matcher_proc = @arguments.delete(:matcher_proc)
|
12
|
+
|
13
|
+
@specified_model_creator = @arguments.delete(:model_creator) do
|
14
|
+
raise KeyError.new(<<-MESSAGE)
|
15
|
+
:model_creator is missing. You can either provide it as an option or as
|
16
|
+
a method.
|
17
|
+
MESSAGE
|
18
|
+
end
|
19
|
+
|
20
|
+
@model_creator = model_creator_class.new(@arguments)
|
21
|
+
end
|
22
|
+
|
23
|
+
def record
|
24
|
+
@_record ||= model.new.tap do |record|
|
25
|
+
attribute_default_values_by_name.each do |attribute_name, default_value|
|
26
|
+
record.public_send("#{attribute_name}=", default_value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def model
|
32
|
+
@_model ||= model_creator.call
|
33
|
+
end
|
34
|
+
|
35
|
+
def matcher
|
36
|
+
@_matcher ||= matcher_proc.call(attribute_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
attr_reader(
|
42
|
+
:arguments,
|
43
|
+
:existing_value,
|
44
|
+
:matcher_proc,
|
45
|
+
:model_creator,
|
46
|
+
:specified_model_creator,
|
47
|
+
)
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def_delegators(
|
52
|
+
:model_creator,
|
53
|
+
:attribute_name,
|
54
|
+
:attribute_default_values_by_name,
|
55
|
+
)
|
56
|
+
|
57
|
+
def model_creator_class
|
58
|
+
UnitTests::ModelCreators.retrieve(specified_model_creator) ||
|
59
|
+
specified_model_creator
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -13,21 +13,24 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|
13
13
|
it 'describes itself with multiple values' do
|
14
14
|
matcher = allow_value('foo', 'bar').for(:baz)
|
15
15
|
|
16
|
-
expect(matcher.description).to eq
|
16
|
+
expect(matcher.description).to eq(
|
17
|
+
'allow :baz to be ‹"foo"› or ‹"bar"›'
|
18
|
+
)
|
17
19
|
end
|
18
20
|
|
19
21
|
it 'describes itself with a single value' do
|
20
22
|
matcher = allow_value('foo').for(:baz)
|
21
23
|
|
22
|
-
expect(matcher.description).to eq 'allow baz to be
|
24
|
+
expect(matcher.description).to eq 'allow :baz to be ‹"foo"›'
|
23
25
|
end
|
24
26
|
|
25
27
|
if active_model_3_2?
|
26
28
|
it 'describes itself with a strict validation' do
|
27
29
|
strict_matcher = allow_value('xyz').for(:attr).strict
|
28
30
|
|
29
|
-
expect(strict_matcher.description).
|
30
|
-
to
|
31
|
+
expect(strict_matcher.description).to eq(
|
32
|
+
'allow :attr to be ‹"xyz"›, raising a validation exception on failure'
|
33
|
+
)
|
31
34
|
end
|
32
35
|
end
|
33
36
|
end
|
@@ -47,24 +50,192 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|
47
50
|
end
|
48
51
|
|
49
52
|
context 'an attribute with a validation' do
|
50
|
-
|
51
|
-
|
53
|
+
context 'given one good value' do
|
54
|
+
context 'when used in the positive' do
|
55
|
+
it 'accepts' do
|
56
|
+
expect(validating_format(with: /abc/)).
|
57
|
+
to allow_value('abcde').for(:attr)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when used in the negative' do
|
62
|
+
it 'rejects with an appropriate failure message' do
|
63
|
+
assertion = lambda do
|
64
|
+
expect(validating_format(with: /abc/)).
|
65
|
+
not_to allow_value('abcde').for(:attr)
|
66
|
+
end
|
67
|
+
|
68
|
+
message = <<-MESSAGE
|
69
|
+
After setting :attr to ‹"abcde"›, the matcher expected the Example to be
|
70
|
+
invalid, but it was valid instead.
|
71
|
+
MESSAGE
|
72
|
+
|
73
|
+
expect(&assertion).to fail_with_message(message)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'given several good values' do
|
79
|
+
context 'when used in the positive' do
|
80
|
+
it 'accepts' do
|
81
|
+
expect(validating_format(with: /abc/)).
|
82
|
+
to allow_value('abcde', 'deabc').for(:attr)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when used in the negative' do
|
87
|
+
it 'rejects with an appropriate failure message' do
|
88
|
+
assertion = lambda do
|
89
|
+
expect(validating_format(with: /abc/)).
|
90
|
+
not_to allow_value('abcde', 'deabc').for(:attr)
|
91
|
+
end
|
92
|
+
|
93
|
+
message = <<-MESSAGE
|
94
|
+
After setting :attr to ‹"abcde"›, the matcher expected the Example to be
|
95
|
+
invalid, but it was valid instead.
|
96
|
+
MESSAGE
|
97
|
+
|
98
|
+
expect(&assertion).to fail_with_message(message)
|
99
|
+
end
|
100
|
+
end
|
52
101
|
end
|
53
102
|
|
54
|
-
|
55
|
-
|
103
|
+
context 'given one bad value' do
|
104
|
+
context 'when used in the positive' do
|
105
|
+
it 'rejects with an appropriate failure message' do
|
106
|
+
assertion = lambda do
|
107
|
+
expect(validating_format(with: /abc/)).
|
108
|
+
to allow_value('xyz').for(:attr)
|
109
|
+
end
|
110
|
+
|
111
|
+
message = <<-MESSAGE
|
112
|
+
After setting :attr to ‹"xyz"›, the matcher expected the Example to be
|
113
|
+
valid, but it was invalid instead, producing these validation errors:
|
114
|
+
|
115
|
+
* attr: ["is invalid"]
|
116
|
+
MESSAGE
|
117
|
+
|
118
|
+
expect(&assertion).to fail_with_message(message)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'when used in the negative' do
|
123
|
+
it 'accepts' do
|
124
|
+
expect(validating_format(with: /abc/)).
|
125
|
+
not_to allow_value('xyz').for(:attr)
|
126
|
+
end
|
127
|
+
end
|
56
128
|
end
|
57
129
|
|
58
|
-
|
59
|
-
|
60
|
-
|
130
|
+
context 'given several bad values' do
|
131
|
+
context 'when used in the positive' do
|
132
|
+
it 'rejects with an appropriate failure message' do
|
133
|
+
assertion = lambda do
|
134
|
+
expect(validating_format(with: /abc/)).
|
135
|
+
to allow_value('xyz', 'zyx', nil, []).
|
136
|
+
for(:attr).
|
137
|
+
ignoring_interference_by_writer
|
138
|
+
end
|
139
|
+
|
140
|
+
message = <<-MESSAGE
|
141
|
+
After setting :attr to ‹"xyz"›, the matcher expected the Example to be
|
142
|
+
valid, but it was invalid instead, producing these validation errors:
|
143
|
+
|
144
|
+
* attr: ["is invalid"]
|
145
|
+
MESSAGE
|
146
|
+
|
147
|
+
expect(&assertion).to fail_with_message(message)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'when used in the negative' do
|
152
|
+
it 'accepts' do
|
153
|
+
expect(validating_format(with: /abc/)).
|
154
|
+
not_to allow_value('xyz', 'zyx', nil, []).
|
155
|
+
for(:attr).
|
156
|
+
ignoring_interference_by_writer
|
157
|
+
end
|
158
|
+
end
|
61
159
|
end
|
62
160
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
161
|
+
context 'given good values along with bad values' do
|
162
|
+
context 'when used in the positive' do
|
163
|
+
it 'rejects with an appropriate failure message' do
|
164
|
+
assertion = lambda do
|
165
|
+
expect(validating_format(with: /abc/)).
|
166
|
+
to allow_value('abc', 'xyz').
|
167
|
+
for(:attr).
|
168
|
+
ignoring_interference_by_writer
|
169
|
+
end
|
170
|
+
|
171
|
+
message = <<-MESSAGE
|
172
|
+
After setting :attr to ‹"xyz"›, the matcher expected the Example to be
|
173
|
+
valid, but it was invalid instead, producing these validation errors:
|
174
|
+
|
175
|
+
* attr: ["is invalid"]
|
176
|
+
MESSAGE
|
177
|
+
|
178
|
+
expect(&assertion).to fail_with_message(message)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'when used in the negative' do
|
183
|
+
it 'rejects with an appropriate failure message' do
|
184
|
+
assertion = lambda do
|
185
|
+
expect(validating_format(with: /abc/)).
|
186
|
+
not_to allow_value('abc', 'xyz').
|
187
|
+
for(:attr).
|
188
|
+
ignoring_interference_by_writer
|
189
|
+
end
|
190
|
+
|
191
|
+
message = <<-MESSAGE
|
192
|
+
After setting :attr to ‹"abc"›, the matcher expected the Example to be
|
193
|
+
invalid, but it was valid instead.
|
194
|
+
MESSAGE
|
195
|
+
|
196
|
+
expect(&assertion).to fail_with_message(message)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context 'given bad values along with good values' do
|
202
|
+
context 'when used in the positive' do
|
203
|
+
it 'rejects with an appropriate failure message' do
|
204
|
+
assertion = lambda do
|
205
|
+
expect(validating_format(with: /abc/)).
|
206
|
+
to allow_value('xyz', 'abc').
|
207
|
+
for(:attr).
|
208
|
+
ignoring_interference_by_writer
|
209
|
+
end
|
210
|
+
|
211
|
+
message = <<-MESSAGE
|
212
|
+
After setting :attr to ‹"xyz"›, the matcher expected the Example to be
|
213
|
+
valid, but it was invalid instead, producing these validation errors:
|
214
|
+
|
215
|
+
* attr: ["is invalid"]
|
216
|
+
MESSAGE
|
217
|
+
|
218
|
+
expect(&assertion).to fail_with_message(message)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context 'when used in the negative' do
|
223
|
+
it 'rejects with an appropriate failure message' do
|
224
|
+
assertion = lambda do
|
225
|
+
expect(validating_format(with: /abc/)).
|
226
|
+
not_to allow_value('xyz', 'abc').
|
227
|
+
for(:attr).
|
228
|
+
ignoring_interference_by_writer
|
229
|
+
end
|
230
|
+
|
231
|
+
message = <<-MESSAGE
|
232
|
+
After setting :attr to ‹"abc"›, the matcher expected the Example to be
|
233
|
+
invalid, but it was valid instead.
|
234
|
+
MESSAGE
|
235
|
+
|
236
|
+
expect(&assertion).to fail_with_message(message)
|
237
|
+
end
|
238
|
+
end
|
68
239
|
end
|
69
240
|
end
|
70
241
|
|
@@ -74,27 +245,95 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|
74
245
|
to allow_value('abcde').for(:attr).with_message(/bad/)
|
75
246
|
end
|
76
247
|
|
77
|
-
it 'rejects a bad value' do
|
78
|
-
|
79
|
-
|
248
|
+
it 'rejects a bad value with an appropriate failure message' do
|
249
|
+
message = <<-MESSAGE
|
250
|
+
After setting :attr to ‹"xyz"›, the matcher expected the Example to be
|
251
|
+
valid, but it was invalid instead, producing these validation errors:
|
252
|
+
|
253
|
+
* attr: ["bad value"]
|
254
|
+
MESSAGE
|
255
|
+
|
256
|
+
assertion = lambda do
|
257
|
+
expect(validating_format(with: /abc/, message: 'bad value')).
|
258
|
+
to allow_value('xyz').for(:attr).with_message(/bad/)
|
259
|
+
end
|
260
|
+
|
261
|
+
expect(&assertion).to fail_with_message(message)
|
80
262
|
end
|
81
263
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
264
|
+
context 'when the custom messages do not match' do
|
265
|
+
it 'rejects with an appropriate failure message' do
|
266
|
+
message = <<-MESSAGE
|
267
|
+
After setting :attr to ‹"xyz"›, the matcher expected the Example to be
|
268
|
+
invalid and to produce a validation error matching ‹/different/› on
|
269
|
+
:attr. The record was indeed invalid, but it produced these validation
|
270
|
+
errors instead:
|
271
|
+
|
272
|
+
* attr: ["bad value"]
|
273
|
+
MESSAGE
|
87
274
|
|
88
|
-
|
89
|
-
|
90
|
-
|
275
|
+
assertion = lambda do
|
276
|
+
expect(validating_format(with: /abc/, message: 'bad value')).
|
277
|
+
not_to allow_value('xyz').for(:attr).with_message(/different/)
|
91
278
|
end
|
279
|
+
|
280
|
+
expect(&assertion).to fail_with_message(message)
|
92
281
|
end
|
282
|
+
end
|
93
283
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
284
|
+
context 'when interpolation values are provided along with a custom message' do
|
285
|
+
context 'when the messages match' do
|
286
|
+
it 'accepts' do
|
287
|
+
options = {
|
288
|
+
attribute_name: :attr,
|
289
|
+
attribute_type: :string
|
290
|
+
}
|
291
|
+
|
292
|
+
record = record_with_custom_validation(options) do
|
293
|
+
if self.attr == 'xyz'
|
294
|
+
self.errors.add :attr, :greater_than, count: 2
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
expect(record).
|
299
|
+
not_to allow_value('xyz').
|
300
|
+
for(:attr).
|
301
|
+
with_message(:greater_than, values: { count: 2 })
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
context 'when the messages do not match' do
|
306
|
+
it 'rejects with an appropriate failure message' do
|
307
|
+
options = {
|
308
|
+
attribute_name: :attr,
|
309
|
+
attribute_type: :string
|
310
|
+
}
|
311
|
+
|
312
|
+
record = record_with_custom_validation(options) do
|
313
|
+
if self.attr == 'xyz'
|
314
|
+
self.errors.add :attr, "some other error"
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
assertion = lambda do
|
319
|
+
expect(record).
|
320
|
+
not_to allow_value('xyz').
|
321
|
+
for(:attr).
|
322
|
+
with_message(:greater_than, values: { count: 2 })
|
323
|
+
end
|
324
|
+
|
325
|
+
message = <<-MESSAGE
|
326
|
+
After setting :attr to ‹"xyz"›, the matcher expected the Example to be
|
327
|
+
invalid and to produce the validation error "must be greater than 2" on
|
328
|
+
:attr. The record was indeed invalid, but it produced these validation
|
329
|
+
errors instead:
|
330
|
+
|
331
|
+
* attr: ["some other error"]
|
332
|
+
MESSAGE
|
333
|
+
|
334
|
+
expect(&assertion).to fail_with_message(message)
|
335
|
+
end
|
336
|
+
end
|
98
337
|
end
|
99
338
|
end
|
100
339
|
|
@@ -102,25 +341,62 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|
102
341
|
include UnitTests::AllowValueMatcherHelpers
|
103
342
|
|
104
343
|
context 'when the validation error message was provided directly' do
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
344
|
+
context 'given a valid value' do
|
345
|
+
it 'accepts' do
|
346
|
+
builder = builder_for_record_with_different_error_attribute
|
347
|
+
expect(builder.record).
|
348
|
+
to allow_value(builder.valid_value).
|
349
|
+
for(builder.attribute_to_validate).
|
350
|
+
with_message(
|
351
|
+
builder.message,
|
352
|
+
against: builder.attribute_that_receives_error
|
353
|
+
)
|
354
|
+
end
|
113
355
|
end
|
114
356
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
357
|
+
context 'given an invalid value' do
|
358
|
+
it 'rejects' do
|
359
|
+
builder = builder_for_record_with_different_error_attribute
|
360
|
+
invalid_value = "#{builder.valid_value} (invalid)"
|
361
|
+
|
362
|
+
expect(builder.record).
|
363
|
+
not_to allow_value(invalid_value).
|
364
|
+
for(builder.attribute_to_validate).
|
365
|
+
with_message(
|
366
|
+
builder.message,
|
367
|
+
against: builder.attribute_that_receives_error
|
368
|
+
)
|
369
|
+
end
|
370
|
+
|
371
|
+
context 'if the messages do not match' do
|
372
|
+
it 'technically accepts' do
|
373
|
+
builder = builder_for_record_with_different_error_attribute(
|
374
|
+
message: "a different error"
|
375
|
+
)
|
376
|
+
invalid_value = "#{builder.valid_value} (invalid)"
|
377
|
+
|
378
|
+
assertion = lambda do
|
379
|
+
expect(builder.record).
|
380
|
+
not_to allow_value(invalid_value).
|
381
|
+
for(builder.attribute_to_validate).
|
382
|
+
with_message(
|
383
|
+
"some error",
|
384
|
+
against: builder.attribute_that_receives_error
|
385
|
+
)
|
386
|
+
end
|
387
|
+
|
388
|
+
message = <<-MESSAGE
|
389
|
+
After setting :#{builder.attribute_to_validate} to ‹"#{invalid_value}"›, the
|
390
|
+
matcher expected the #{builder.model.name} to be invalid and to produce the validation
|
391
|
+
error "some error" on :#{builder.attribute_that_receives_error}. The record was
|
392
|
+
indeed invalid, but it produced these validation errors instead:
|
393
|
+
|
394
|
+
* #{builder.attribute_that_receives_error}: ["a different error"]
|
395
|
+
MESSAGE
|
396
|
+
|
397
|
+
expect(&assertion).to fail_with_message(message)
|
398
|
+
end
|
399
|
+
end
|
124
400
|
end
|
125
401
|
end
|
126
402
|
|
@@ -130,7 +406,8 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|
130
406
|
expect(builder.record).
|
131
407
|
to allow_value(builder.valid_value).
|
132
408
|
for(builder.attribute_to_validate).
|
133
|
-
with_message(
|
409
|
+
with_message(
|
410
|
+
builder.validation_message_key,
|
134
411
|
against: builder.attribute_that_receives_error
|
135
412
|
)
|
136
413
|
end
|
@@ -141,7 +418,8 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|
141
418
|
expect(builder.record).
|
142
419
|
not_to allow_value(invalid_value).
|
143
420
|
for(builder.attribute_to_validate).
|
144
|
-
with_message(
|
421
|
+
with_message(
|
422
|
+
builder.validation_message_key,
|
145
423
|
against: builder.attribute_that_receives_error
|
146
424
|
)
|
147
425
|
end
|
@@ -199,11 +477,29 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|
199
477
|
end
|
200
478
|
|
201
479
|
it "does not match given good values along with bad values" do
|
202
|
-
message =
|
480
|
+
message = <<-MESSAGE.strip_heredoc
|
481
|
+
After setting :attr to ‹"12345"›, the matcher expected the Example to be
|
482
|
+
invalid, but it was valid instead.
|
483
|
+
MESSAGE
|
203
484
|
|
204
|
-
|
485
|
+
assertion = lambda do
|
205
486
|
expect(model).not_to allow_value('12345', *bad_values).for(:attr)
|
206
|
-
|
487
|
+
end
|
488
|
+
|
489
|
+
expect(&assertion).to fail_with_message(message)
|
490
|
+
end
|
491
|
+
|
492
|
+
it "does not match given bad values along with good values" do
|
493
|
+
message = <<-MESSAGE.strip_heredoc
|
494
|
+
After setting :attr to ‹"12345"›, the matcher expected the Example to be
|
495
|
+
invalid, but it was valid instead.
|
496
|
+
MESSAGE
|
497
|
+
|
498
|
+
assertion = lambda do
|
499
|
+
expect(model).not_to allow_value(*bad_values, '12345').for(:attr)
|
500
|
+
end
|
501
|
+
|
502
|
+
expect(&assertion).to fail_with_message(message)
|
207
503
|
end
|
208
504
|
end
|
209
505
|
|
@@ -226,161 +522,300 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|
226
522
|
|
227
523
|
if active_model_3_2?
|
228
524
|
context 'an attribute with a strict format validation' do
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
525
|
+
context 'when qualified with strict' do
|
526
|
+
it 'rejects a bad value, providing the correct failure message' do
|
527
|
+
message = <<-MESSAGE.strip_heredoc
|
528
|
+
After setting :attr to ‹"xyz"›, the matcher expected the Example to be
|
529
|
+
valid, but it was invalid instead, raising a validation exception with
|
530
|
+
the message "Attr is invalid".
|
531
|
+
MESSAGE
|
233
532
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
533
|
+
assertion = lambda do
|
534
|
+
expect(validating_format(with: /abc/, strict: true)).
|
535
|
+
to allow_value('xyz').for(:attr).strict
|
536
|
+
end
|
238
537
|
|
239
|
-
|
240
|
-
|
538
|
+
expect(&assertion).to fail_with_message(message)
|
539
|
+
end
|
241
540
|
|
242
|
-
|
541
|
+
context 'qualified with a custom message' do
|
542
|
+
it 'rejects a bad value when the failure messages do not match' do
|
543
|
+
message = <<-MESSAGE.strip_heredoc
|
544
|
+
After setting :attr to ‹"xyz"›, the matcher expected the Example to be
|
545
|
+
invalid and to raise a validation exception with message matching
|
546
|
+
‹/abc/›. The record was indeed invalid, but the exception message was
|
547
|
+
"Attr is invalid" instead.
|
548
|
+
MESSAGE
|
549
|
+
|
550
|
+
assertion = lambda do
|
551
|
+
expect(validating_format(with: /abc/, strict: true)).
|
552
|
+
not_to allow_value('xyz').for(:attr).with_message(/abc/).strict
|
553
|
+
end
|
243
554
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
)
|
555
|
+
expect(&assertion).to fail_with_message(message)
|
556
|
+
end
|
557
|
+
end
|
248
558
|
end
|
249
559
|
end
|
250
560
|
end
|
251
561
|
|
252
562
|
context 'when the attribute interferes with attempts to be set' do
|
253
|
-
context 'when the
|
254
|
-
context '
|
255
|
-
it '
|
256
|
-
model = define_active_model_class 'Example' do
|
257
|
-
|
563
|
+
context 'when the attribute cannot be changed from nil to non-nil' do
|
564
|
+
context 'and the record remains valid' do
|
565
|
+
it 'accepts (and does not raise an AttributeChangedValueError)' do
|
566
|
+
model = define_active_model_class 'Example', accessors: [:name] do
|
567
|
+
def name=(_value)
|
568
|
+
nil
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
expect(model.new).to allow_value('anything').for(:name)
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
context 'and the record becomes invalid' do
|
577
|
+
it 'rejects with an appropriate failure message' do
|
578
|
+
model = define_active_model_class 'Example', accessors: [:name] do
|
579
|
+
validates_presence_of :name
|
258
580
|
|
259
581
|
def name=(_value)
|
260
582
|
nil
|
261
583
|
end
|
262
584
|
end
|
263
585
|
|
264
|
-
assertion =
|
586
|
+
assertion = lambda do
|
265
587
|
expect(model.new).to allow_value('anything').for(:name)
|
266
|
-
|
588
|
+
end
|
267
589
|
|
268
|
-
|
269
|
-
|
270
|
-
|
590
|
+
message = <<-MESSAGE.strip
|
591
|
+
After setting :name to ‹"anything"› -- which was read back as ‹nil› --
|
592
|
+
the matcher expected the Example to be valid, but it was invalid
|
593
|
+
instead, producing these validation errors:
|
594
|
+
|
595
|
+
* name: ["can't be blank"]
|
596
|
+
|
597
|
+
As indicated in the message above, :name seems to be changing certain
|
598
|
+
values as they are set, and this could have something to do with why
|
599
|
+
this test is failing. If you've overridden the writer method for this
|
600
|
+
attribute, then you may need to change it to make this test pass, or do
|
601
|
+
something else entirely.
|
602
|
+
MESSAGE
|
603
|
+
|
604
|
+
expect(&assertion).to fail_with_message(message)
|
271
605
|
end
|
272
606
|
end
|
607
|
+
end
|
273
608
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
609
|
+
context 'when the attribute cannot be changed from non-nil to nil' do
|
610
|
+
context 'and the record remains valid' do
|
611
|
+
it 'accepts (and does not raise an AttributeChangedValueError)' do
|
612
|
+
model = define_active_model_class 'Example', accessors: [:name] do
|
279
613
|
def name=(value)
|
280
|
-
|
614
|
+
if value
|
615
|
+
super(value)
|
616
|
+
end
|
281
617
|
end
|
282
618
|
end
|
283
619
|
|
284
620
|
record = model.new(name: 'some name')
|
285
621
|
|
286
|
-
|
287
|
-
expect(record).to allow_value(nil).for(:name)
|
288
|
-
}
|
289
|
-
|
290
|
-
expect(&assertion).to raise_error(
|
291
|
-
described_class::CouldNotSetAttributeError
|
292
|
-
)
|
622
|
+
expect(record).to allow_value(nil).for(:name)
|
293
623
|
end
|
294
624
|
end
|
295
625
|
|
296
|
-
context '
|
297
|
-
it '
|
298
|
-
model = define_active_model_class 'Example' do
|
299
|
-
|
626
|
+
context 'and the record becomes invalid' do
|
627
|
+
it 'rejects with an appropriate failure message' do
|
628
|
+
model = define_active_model_class 'Example', accessors: [:name] do
|
629
|
+
validates_absence_of :name
|
300
630
|
|
301
|
-
def name=(
|
302
|
-
|
631
|
+
def name=(value)
|
632
|
+
if value
|
633
|
+
super(value)
|
634
|
+
end
|
303
635
|
end
|
304
636
|
end
|
305
637
|
|
306
638
|
record = model.new(name: 'some name')
|
307
639
|
|
308
|
-
assertion =
|
309
|
-
expect(record).to allow_value(
|
310
|
-
|
640
|
+
assertion = lambda do
|
641
|
+
expect(record).to allow_value(nil).for(:name)
|
642
|
+
end
|
311
643
|
|
312
|
-
|
313
|
-
|
314
|
-
|
644
|
+
message = <<-MESSAGE.strip
|
645
|
+
After setting :name to ‹nil› -- which was read back as ‹"some name"› --
|
646
|
+
the matcher expected the Example to be valid, but it was invalid
|
647
|
+
instead, producing these validation errors:
|
648
|
+
|
649
|
+
* name: ["must be blank"]
|
650
|
+
|
651
|
+
As indicated in the message above, :name seems to be changing certain
|
652
|
+
values as they are set, and this could have something to do with why
|
653
|
+
this test is failing. If you've overridden the writer method for this
|
654
|
+
attribute, then you may need to change it to make this test pass, or do
|
655
|
+
something else entirely.
|
656
|
+
MESSAGE
|
657
|
+
|
658
|
+
expect(&assertion).to fail_with_message(message)
|
315
659
|
end
|
316
660
|
end
|
317
661
|
end
|
318
662
|
|
319
|
-
context 'when the
|
320
|
-
context '
|
321
|
-
it 'does not raise an
|
322
|
-
model = define_active_model_class 'Example' do
|
323
|
-
attr_reader :name
|
324
|
-
|
663
|
+
context 'when the attribute cannot be changed from a non-nil value to another non-nil value' do
|
664
|
+
context 'and the record remains valid' do
|
665
|
+
it 'accepts (and does not raise an AttributeChangedValueError)' do
|
666
|
+
model = define_active_model_class 'Example', accessors: [:name] do
|
325
667
|
def name=(_value)
|
326
|
-
|
668
|
+
super('constant name')
|
327
669
|
end
|
328
670
|
end
|
329
671
|
|
330
|
-
|
331
|
-
expect(model.new).
|
332
|
-
to allow_value('anything').
|
333
|
-
for(:name).
|
334
|
-
ignoring_interference_by_writer
|
335
|
-
end
|
672
|
+
record = model.new(name: 'some name')
|
336
673
|
|
337
|
-
expect(
|
674
|
+
expect(record).to allow_value('another name').for(:name)
|
338
675
|
end
|
339
676
|
end
|
340
677
|
|
341
|
-
context '
|
342
|
-
it '
|
343
|
-
model = define_active_model_class 'Example' do
|
344
|
-
|
678
|
+
context 'and the record becomes invalid' do
|
679
|
+
it 'rejects with an appropriate failure message' do
|
680
|
+
model = define_active_model_class 'Example', accessors: [:name] do
|
681
|
+
validates_format_of :name, with: /another name/
|
345
682
|
|
346
683
|
def name=(value)
|
347
|
-
|
684
|
+
super('constant name')
|
348
685
|
end
|
349
686
|
end
|
350
687
|
|
351
688
|
record = model.new(name: 'some name')
|
352
689
|
|
353
690
|
assertion = lambda do
|
354
|
-
expect(record).
|
355
|
-
to allow_value(nil).
|
356
|
-
for(:name).
|
357
|
-
ignoring_interference_by_writer
|
691
|
+
expect(record).to allow_value('another name').for(:name)
|
358
692
|
end
|
359
693
|
|
360
|
-
|
694
|
+
message = <<-MESSAGE.strip
|
695
|
+
After setting :name to ‹"another name"› -- which was read back as
|
696
|
+
‹"constant name"› -- the matcher expected the Example to be valid, but
|
697
|
+
it was invalid instead, producing these validation errors:
|
698
|
+
|
699
|
+
* name: ["is invalid"]
|
700
|
+
|
701
|
+
As indicated in the message above, :name seems to be changing certain
|
702
|
+
values as they are set, and this could have something to do with why
|
703
|
+
this test is failing. If you've overridden the writer method for this
|
704
|
+
attribute, then you may need to change it to make this test pass, or do
|
705
|
+
something else entirely.
|
706
|
+
MESSAGE
|
707
|
+
|
708
|
+
expect(&assertion).to fail_with_message(message)
|
709
|
+
end
|
710
|
+
end
|
711
|
+
end
|
712
|
+
end
|
713
|
+
|
714
|
+
context 'when the attribute does not exist on the model' do
|
715
|
+
context 'when the assertion is positive' do
|
716
|
+
it 'raises an AttributeDoesNotExistError' do
|
717
|
+
model = define_class('Example')
|
718
|
+
|
719
|
+
assertion = lambda do
|
720
|
+
expect(model.new).to allow_value('foo').for(:nonexistent)
|
361
721
|
end
|
722
|
+
|
723
|
+
message = <<-MESSAGE.rstrip
|
724
|
+
The matcher attempted to set :nonexistent on the Example to "foo", but
|
725
|
+
that attribute does not exist.
|
726
|
+
MESSAGE
|
727
|
+
|
728
|
+
expect(&assertion).to raise_error(
|
729
|
+
described_class::AttributeDoesNotExistError,
|
730
|
+
message
|
731
|
+
)
|
362
732
|
end
|
733
|
+
end
|
363
734
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
attr_reader :name
|
735
|
+
context 'when the assertion is negative' do
|
736
|
+
it 'raises an AttributeDoesNotExistError' do
|
737
|
+
model = define_class('Example')
|
368
738
|
|
369
|
-
|
370
|
-
|
371
|
-
|
739
|
+
assertion = lambda do
|
740
|
+
expect(model.new).not_to allow_value('foo').for(:nonexistent)
|
741
|
+
end
|
742
|
+
|
743
|
+
message = <<-MESSAGE.rstrip
|
744
|
+
The matcher attempted to set :nonexistent on the Example to "foo", but
|
745
|
+
that attribute does not exist.
|
746
|
+
MESSAGE
|
747
|
+
|
748
|
+
expect(&assertion).to raise_error(
|
749
|
+
described_class::AttributeDoesNotExistError,
|
750
|
+
message
|
751
|
+
)
|
752
|
+
end
|
753
|
+
end
|
754
|
+
end
|
755
|
+
|
756
|
+
context 'given attributes to preset on the record before validation' do
|
757
|
+
context 'when the assertion is positive' do
|
758
|
+
context 'if any attributes do not exist on the model' do
|
759
|
+
it 'raises an AttributeDoesNotExistError' do
|
760
|
+
model = define_active_model_class('Example', accessors: [:existent])
|
761
|
+
|
762
|
+
allow_value_matcher = allow_value('foo').for(:existent).tap do |matcher|
|
763
|
+
matcher.values_to_preset = { nonexistent: 'some value' }
|
372
764
|
end
|
373
765
|
|
374
|
-
|
766
|
+
assertion = lambda do
|
767
|
+
expect(model.new).to(allow_value_matcher)
|
768
|
+
end
|
769
|
+
|
770
|
+
message = <<-MESSAGE.rstrip
|
771
|
+
The matcher attempted to set :nonexistent on the Example to "some
|
772
|
+
value", but that attribute does not exist.
|
773
|
+
MESSAGE
|
774
|
+
|
775
|
+
expect(&assertion).to raise_error(
|
776
|
+
described_class::AttributeDoesNotExistError,
|
777
|
+
message
|
778
|
+
)
|
779
|
+
end
|
780
|
+
end
|
781
|
+
end
|
782
|
+
|
783
|
+
context 'when the assertion is negative' do
|
784
|
+
context 'if any attributes do not exist on the model' do
|
785
|
+
it 'raises an AttributeDoesNotExistError' do
|
786
|
+
model = define_active_model_class('Example', accessors: [:existent])
|
787
|
+
|
788
|
+
allow_value_matcher = allow_value('foo').for(:existent).tap do |matcher|
|
789
|
+
matcher.values_to_preset = { nonexistent: 'some value' }
|
790
|
+
end
|
375
791
|
|
376
792
|
assertion = lambda do
|
377
|
-
expect(
|
378
|
-
|
379
|
-
|
380
|
-
|
793
|
+
expect(model.new).not_to(allow_value_matcher)
|
794
|
+
end
|
795
|
+
|
796
|
+
message = <<-MESSAGE.rstrip
|
797
|
+
The matcher attempted to set :nonexistent on the Example to "some
|
798
|
+
value", but that attribute does not exist.
|
799
|
+
MESSAGE
|
800
|
+
|
801
|
+
expect(&assertion).to raise_error(
|
802
|
+
described_class::AttributeDoesNotExistError,
|
803
|
+
message
|
804
|
+
)
|
805
|
+
end
|
806
|
+
end
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
if active_record_supports_enum?
|
811
|
+
context 'given an ActiveRecord model' do
|
812
|
+
context 'where the attribute under test is an enum and the given value is a value in that enum' do
|
813
|
+
it 'accepts' do
|
814
|
+
model = define_model('Shipment', status: :integer) do
|
815
|
+
enum status: { pending: 1, shipped: 2, delivered: 3 }
|
381
816
|
end
|
382
817
|
|
383
|
-
expect(
|
818
|
+
expect(model.new).to allow_value(1).for(:status)
|
384
819
|
end
|
385
820
|
end
|
386
821
|
end
|