activerecord 2.3.3 → 2.3.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +8 -1
- data/Rakefile +32 -15
- data/examples/performance.rb +162 -0
- data/lib/active_record/associations.rb +37 -5
- data/lib/active_record/associations/association_collection.rb +1 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +16 -0
- data/lib/active_record/associations/has_many_association.rb +1 -0
- data/lib/active_record/associations/has_many_through_association.rb +13 -3
- data/lib/active_record/associations/has_one_through_association.rb +8 -2
- data/lib/active_record/autosave_association.rb +4 -3
- data/lib/active_record/base.rb +18 -10
- data/lib/active_record/calculations.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +16 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/abstract_adapter.rb +7 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +17 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -13
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +12 -0
- data/lib/active_record/dirty.rb +1 -1
- data/lib/active_record/fixtures.rb +9 -7
- data/lib/active_record/i18n_interpolation_deprecation.rb +1 -1
- data/lib/active_record/locale/en.yml +4 -0
- data/lib/active_record/named_scope.rb +1 -6
- data/lib/active_record/reflection.rb +1 -1
- data/lib/active_record/schema_dumper.rb +1 -2
- data/lib/active_record/serializers/json_serializer.rb +5 -3
- data/lib/active_record/serializers/xml_serializer.rb +6 -2
- data/lib/active_record/validations.rb +148 -79
- data/lib/active_record/version.rb +1 -1
- data/test/cases/adapter_test.rb +12 -0
- data/test/cases/associations/belongs_to_associations_test.rb +0 -18
- data/test/cases/associations/eager_load_nested_include_test.rb +5 -5
- data/test/cases/associations/habtm_join_table_test.rb +56 -0
- data/test/cases/associations/has_many_associations_test.rb +56 -2
- data/test/cases/associations/has_many_through_associations_test.rb +46 -1
- data/test/cases/associations/has_one_through_associations_test.rb +10 -0
- data/test/cases/associations/join_model_test.rb +4 -4
- data/test/cases/base_test.rb +49 -4
- data/test/cases/calculations_test.rb +6 -0
- data/test/cases/column_definition_test.rb +34 -0
- data/test/cases/dirty_test.rb +10 -0
- data/test/cases/finder_test.rb +15 -50
- data/test/cases/fixtures_test.rb +1 -1
- data/test/cases/i18n_test.rb +5 -0
- data/test/cases/method_scoping_test.rb +1 -1
- data/test/cases/migration_test.rb +39 -11
- data/test/cases/modules_test.rb +42 -0
- data/test/cases/named_scope_test.rb +6 -4
- data/test/cases/pk_test.rb +18 -0
- data/test/cases/reflection_test.rb +2 -2
- data/test/cases/schema_dumper_test.rb +19 -1
- data/test/cases/validations_i18n_test.rb +656 -624
- data/test/cases/validations_test.rb +12 -2
- data/test/cases/xml_serialization_test.rb +20 -0
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/fixtures/posts.yml +3 -0
- data/test/models/author.rb +1 -0
- data/test/models/comment.rb +5 -1
- data/test/models/company.rb +2 -0
- data/test/models/company_in_module.rb +1 -1
- data/test/models/contract.rb +5 -0
- data/test/models/organization.rb +2 -0
- data/test/models/topic.rb +0 -2
- data/test/schema/postgresql_specific_schema.rb +13 -2
- data/test/schema/schema.rb +4 -0
- metadata +12 -54
- data/test/debug.log +0 -415
@@ -1,32 +1,20 @@
|
|
1
1
|
require "cases/helper"
|
2
2
|
require 'models/topic'
|
3
3
|
require 'models/reply'
|
4
|
+
require 'models/person'
|
4
5
|
|
5
|
-
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
I18n.
|
11
|
-
I18n.backend = I18n::Backend::Simple.new
|
12
|
-
I18n.backend.store_translations('en', :activerecord => {:errors => {:messages => {:custom => nil}}})
|
13
|
-
end
|
14
|
-
|
15
|
-
def teardown
|
16
|
-
reset_callbacks Topic
|
17
|
-
I18n.load_path.replace @old_load_path
|
18
|
-
I18n.backend = @old_backend
|
19
|
-
end
|
20
|
-
|
21
|
-
def unique_topic
|
22
|
-
@unique ||= Topic.create :title => 'unique!'
|
6
|
+
module ActiveRecordValidationsI18nTestHelper
|
7
|
+
def store_translations(*args)
|
8
|
+
data = args.extract_options!
|
9
|
+
locale = args.shift || 'en'
|
10
|
+
I18n.backend.send(:init_translations)
|
11
|
+
I18n.backend.store_translations(locale, :activerecord => data)
|
23
12
|
end
|
24
13
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
topic
|
14
|
+
def delete_translation(key)
|
15
|
+
I18n.backend.instance_eval do
|
16
|
+
keys = I18n.send(:normalize_translation_keys, 'en', key, nil)
|
17
|
+
keys.inject(translations) { |result, k| keys.last == k ? result.delete(k.to_sym) : result[k.to_sym] }
|
30
18
|
end
|
31
19
|
end
|
32
20
|
|
@@ -37,879 +25,923 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase
|
|
37
25
|
model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
38
26
|
end
|
39
27
|
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# DEPRECATIONS
|
40
31
|
|
41
|
-
|
32
|
+
class ActiveRecordValidationsI18nDeprecationsTests < ActiveSupport::TestCase
|
33
|
+
test "default_error_messages is deprecated and can be removed in Rails 3 / ActiveModel" do
|
42
34
|
assert_deprecated('ActiveRecord::Errors.default_error_messages') do
|
43
35
|
ActiveRecord::Errors.default_error_messages
|
44
36
|
end
|
45
37
|
end
|
46
38
|
|
47
|
-
|
39
|
+
test "%s interpolation syntax in error messages still works" do
|
48
40
|
ActiveSupport::Deprecation.silence do
|
49
41
|
result = I18n.t :does_not_exist, :default => "%s interpolation syntax is deprecated", :value => 'this'
|
50
42
|
assert_equal result, "this interpolation syntax is deprecated"
|
51
43
|
end
|
52
44
|
end
|
53
45
|
|
54
|
-
|
46
|
+
test "%s interpolation syntax in error messages is deprecated" do
|
55
47
|
assert_deprecated('using %s in messages') do
|
56
48
|
I18n.t :does_not_exist, :default => "%s interpolation syntax is deprected", :value => 'this'
|
57
49
|
end
|
58
50
|
end
|
59
51
|
|
60
|
-
|
52
|
+
test "%d interpolation syntax in error messages still works" do
|
61
53
|
ActiveSupport::Deprecation.silence do
|
62
54
|
result = I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprecated", :count => 2
|
63
55
|
assert_equal result, "2 interpolation syntaxes are deprecated"
|
64
56
|
end
|
65
57
|
end
|
66
58
|
|
67
|
-
|
59
|
+
test "%d interpolation syntax in error messages is deprecated" do
|
68
60
|
assert_deprecated('using %d in messages') do
|
69
61
|
I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprected", :count => 2
|
70
62
|
end
|
71
63
|
end
|
64
|
+
end
|
72
65
|
|
73
|
-
# ActiveRecord::Errors
|
74
|
-
def test_errors_generate_message_translates_custom_model_attribute_key
|
75
|
-
|
76
|
-
I18n.expects(:translate).with(
|
77
|
-
:topic,
|
78
|
-
{ :count => 1,
|
79
|
-
:default => ['Topic'],
|
80
|
-
:scope => [:activerecord, :models]
|
81
|
-
}
|
82
|
-
).returns('Topic')
|
83
|
-
|
84
|
-
I18n.expects(:translate).with(
|
85
|
-
:"topic.title",
|
86
|
-
{ :count => 1,
|
87
|
-
:default => ['Title'],
|
88
|
-
:scope => [:activerecord, :attributes]
|
89
|
-
}
|
90
|
-
).returns('Title')
|
91
|
-
|
92
|
-
I18n.expects(:translate).with(
|
93
|
-
:"models.topic.attributes.title.invalid",
|
94
|
-
:value => nil,
|
95
|
-
:scope => [:activerecord, :errors],
|
96
|
-
:default => [
|
97
|
-
:"models.topic.invalid",
|
98
|
-
'default from class def error 1',
|
99
|
-
:"messages.invalid"],
|
100
|
-
:attribute => "Title",
|
101
|
-
:model => "Topic"
|
102
|
-
).returns('default from class def error 1')
|
103
|
-
|
104
|
-
@topic.errors.generate_message :title, :invalid, :default => 'default from class def error 1'
|
105
|
-
end
|
106
|
-
|
107
|
-
def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti
|
108
|
-
|
109
|
-
I18n.expects(:translate).with(
|
110
|
-
:reply,
|
111
|
-
{ :count => 1,
|
112
|
-
:default => [:topic, 'Reply'],
|
113
|
-
:scope => [:activerecord, :models]
|
114
|
-
}
|
115
|
-
).returns('Reply')
|
116
|
-
|
117
|
-
I18n.expects(:translate).with(
|
118
|
-
:"reply.title",
|
119
|
-
{ :count => 1,
|
120
|
-
:default => [:'topic.title', 'Title'],
|
121
|
-
:scope => [:activerecord, :attributes]
|
122
|
-
}
|
123
|
-
).returns('Title')
|
124
66
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
:"models.topic.attributes.title.invalid",
|
132
|
-
:"models.topic.invalid",
|
133
|
-
'default from class def',
|
134
|
-
:"messages.invalid"],
|
135
|
-
:model => 'Reply',
|
136
|
-
:attribute => 'Title'
|
137
|
-
).returns("default from class def")
|
67
|
+
# ACTIVERECORD VALIDATIONS
|
68
|
+
#
|
69
|
+
# For each validation:
|
70
|
+
#
|
71
|
+
# * test expect that it adds an error with the appropriate arguments
|
72
|
+
# * test that it looks up the correct default message
|
138
73
|
|
139
|
-
|
74
|
+
class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase
|
75
|
+
include ActiveRecordValidationsI18nTestHelper
|
140
76
|
|
77
|
+
def setup
|
78
|
+
reset_callbacks(Topic)
|
79
|
+
@topic = Topic.new
|
80
|
+
@reply = Reply.new
|
81
|
+
@old_load_path, @old_backend = I18n.load_path, I18n.backend
|
82
|
+
I18n.load_path.clear
|
83
|
+
I18n.backend = I18n::Backend::Simple.new
|
84
|
+
I18n.backend.store_translations('en', :activerecord => {:errors => {:messages => {:custom => nil}}})
|
141
85
|
end
|
142
86
|
|
143
|
-
def
|
144
|
-
|
145
|
-
|
87
|
+
def teardown
|
88
|
+
reset_callbacks(Topic)
|
89
|
+
I18n.load_path.replace(@old_load_path)
|
90
|
+
I18n.backend = @old_backend
|
146
91
|
end
|
147
92
|
|
148
|
-
def
|
149
|
-
|
150
|
-
|
93
|
+
def expect_error_added(model, attribute, type, options)
|
94
|
+
model.errors.expects(:add).with(attribute, type, options)
|
95
|
+
yield
|
96
|
+
model.valid?
|
151
97
|
end
|
152
98
|
|
153
|
-
def
|
154
|
-
|
155
|
-
|
99
|
+
def assert_message_translations(model, attribute, type, &block)
|
100
|
+
assert_default_message_translation(model, attribute, type, &block)
|
101
|
+
reset_callbacks(model.class)
|
102
|
+
model.errors.clear
|
103
|
+
assert_custom_message_translation(model, attribute, type, &block)
|
156
104
|
end
|
157
105
|
|
158
|
-
def
|
159
|
-
|
160
|
-
|
106
|
+
def assert_custom_message_translation(model, attribute, type)
|
107
|
+
store_translations(:errors => { :models => { model.class.name.underscore => { :attributes => { attribute => { type => 'custom message' } } } } })
|
108
|
+
yield
|
109
|
+
model.valid?
|
110
|
+
assert_equal 'custom message', model.errors.on(attribute)
|
161
111
|
end
|
162
112
|
|
163
|
-
def
|
164
|
-
|
165
|
-
|
166
|
-
|
113
|
+
def assert_default_message_translation(model, attribute, type)
|
114
|
+
store_translations(:errors => { :messages => { type => 'default message' } })
|
115
|
+
yield
|
116
|
+
model.valid?
|
117
|
+
assert_equal 'default message', model.errors.on(attribute)
|
167
118
|
end
|
168
119
|
|
169
|
-
|
170
|
-
|
171
|
-
def test_validates_confirmation_of_generates_message
|
172
|
-
Topic.validates_confirmation_of :title
|
173
|
-
@topic.title_confirmation = 'foo'
|
174
|
-
@topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => nil})
|
175
|
-
@topic.valid?
|
120
|
+
def unique_topic
|
121
|
+
@unique ||= Topic.create(:title => 'unique!')
|
176
122
|
end
|
177
123
|
|
178
|
-
def
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
124
|
+
def replied_topic
|
125
|
+
@replied_topic ||= begin
|
126
|
+
topic = Topic.create(:title => "topic")
|
127
|
+
topic.replies << Reply.new
|
128
|
+
topic
|
129
|
+
end
|
183
130
|
end
|
184
131
|
|
185
|
-
#
|
132
|
+
# validates_confirmation_of
|
186
133
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
def test_validates_acceptance_of_generates_message_with_custom_default_message
|
194
|
-
Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false
|
195
|
-
@topic.errors.expects(:generate_message).with(:title, :accepted, {:default => 'custom'})
|
196
|
-
@topic.valid?
|
134
|
+
test "#validates_confirmation_of given no custom message" do
|
135
|
+
expect_error_added(@topic, :title, :confirmation, :default => nil) do
|
136
|
+
Topic.validates_confirmation_of :title
|
137
|
+
@topic.title = 'title'
|
138
|
+
@topic.title_confirmation = 'foo'
|
139
|
+
end
|
197
140
|
end
|
198
141
|
|
199
|
-
#
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
@topic.valid?
|
142
|
+
test "#validates_confirmation_of given a custom message" do
|
143
|
+
expect_error_added(@topic, :title, :confirmation, :default => 'custom') do
|
144
|
+
Topic.validates_confirmation_of :title, :message => 'custom'
|
145
|
+
@topic.title_confirmation = 'foo'
|
146
|
+
end
|
205
147
|
end
|
206
148
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
149
|
+
test "#validates_confirmation_of finds the correct message translations" do
|
150
|
+
assert_message_translations(@topic, :title, :confirmation) do
|
151
|
+
Topic.validates_confirmation_of :title
|
152
|
+
@topic.title_confirmation = 'foo'
|
153
|
+
end
|
211
154
|
end
|
212
155
|
|
213
|
-
|
214
|
-
Topic.validates_length_of :title, :within => 3..5
|
215
|
-
@topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil})
|
216
|
-
@topic.valid?
|
217
|
-
end
|
156
|
+
# validates_acceptance_of
|
218
157
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
158
|
+
test "#validates_acceptance_of given no custom message" do
|
159
|
+
expect_error_added(@topic, :title, :accepted, :default => nil) do
|
160
|
+
Topic.validates_acceptance_of :title, :allow_nil => false
|
161
|
+
end
|
223
162
|
end
|
224
163
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
@topic.valid?
|
164
|
+
test "#validates_acceptance_of given a custom message" do
|
165
|
+
expect_error_added(@topic, :title, :accepted, :default => 'custom') do
|
166
|
+
Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false
|
167
|
+
end
|
230
168
|
end
|
231
169
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
@topic.valid?
|
170
|
+
test "#validates_acceptance_of finds the correct message translations" do
|
171
|
+
assert_message_translations(@topic, :title, :accepted) do
|
172
|
+
Topic.validates_acceptance_of :title, :allow_nil => false
|
173
|
+
end
|
237
174
|
end
|
238
175
|
|
239
|
-
#
|
240
|
-
|
241
|
-
def test_validates_length_of_within_generates_message_with_title_too_short
|
242
|
-
Topic.validates_length_of :title, :within => 3..5
|
243
|
-
@topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil})
|
244
|
-
@topic.valid?
|
245
|
-
end
|
176
|
+
# validates_presence_of
|
246
177
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
178
|
+
test "#validates_presence_of given no custom message" do
|
179
|
+
expect_error_added(@topic, :title, :blank, :default => nil) do
|
180
|
+
Topic.validates_presence_of :title
|
181
|
+
end
|
251
182
|
end
|
252
183
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
@topic.valid?
|
184
|
+
test "#validates_presence_of given a custom message" do
|
185
|
+
expect_error_added(@topic, :title, :blank, :default => 'custom') do
|
186
|
+
Topic.validates_presence_of :title, :message => 'custom'
|
187
|
+
end
|
258
188
|
end
|
259
189
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
@topic.valid?
|
190
|
+
test "#validates_presence_of finds the correct message translations" do
|
191
|
+
assert_message_translations(@topic, :title, :blank) do
|
192
|
+
Topic.validates_presence_of :title
|
193
|
+
end
|
265
194
|
end
|
266
195
|
|
267
|
-
# validates_length_of :
|
196
|
+
# validates_length_of :too_short
|
268
197
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
198
|
+
test "#validates_length_of (:too_short) and no custom message" do
|
199
|
+
expect_error_added(@topic, :title, :too_short, :default => nil, :count => 3) do
|
200
|
+
Topic.validates_length_of :title, :within => 3..5
|
201
|
+
end
|
273
202
|
end
|
274
203
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
204
|
+
test "#validates_length_of (:too_short) and a custom message" do
|
205
|
+
expect_error_added(@topic, :title, :too_short, :default => 'custom', :count => 3) do
|
206
|
+
Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom'
|
207
|
+
end
|
279
208
|
end
|
280
209
|
|
281
|
-
#
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
@topic.title = unique_topic.title
|
286
|
-
@topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil, :value => 'unique!'})
|
287
|
-
@topic.valid?
|
210
|
+
test "#validates_length_of (:too_short) finds the correct message translations" do
|
211
|
+
assert_message_translations(@topic, :title, :too_short) do
|
212
|
+
Topic.validates_length_of :title, :within => 3..5
|
213
|
+
end
|
288
214
|
end
|
289
215
|
|
290
|
-
|
291
|
-
Topic.validates_uniqueness_of :title, :message => 'custom'
|
292
|
-
@topic.title = unique_topic.title
|
293
|
-
@topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom', :value => 'unique!'})
|
294
|
-
@topic.valid?
|
295
|
-
end
|
216
|
+
# validates_length_of :too_long
|
296
217
|
|
297
|
-
#
|
218
|
+
test "#validates_length_of (:too_long) and no custom message" do
|
219
|
+
expect_error_added(@topic, :title, :too_long, :default => nil, :count => 5) do
|
220
|
+
Topic.validates_length_of :title, :within => 3..5
|
221
|
+
@topic.title = 'this title is too long'
|
222
|
+
end
|
223
|
+
end
|
298
224
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
225
|
+
test "#validates_length_of (:too_long) and a custom message" do
|
226
|
+
expect_error_added(@topic, :title, :too_long, :default => 'custom', :count => 5) do
|
227
|
+
Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom'
|
228
|
+
@topic.title = 'this title is too long'
|
229
|
+
end
|
304
230
|
end
|
305
231
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
232
|
+
test "#validates_length_of (:too_long) finds the correct message translations" do
|
233
|
+
assert_message_translations(@topic, :title, :too_long) do
|
234
|
+
Topic.validates_length_of :title, :within => 3..5
|
235
|
+
@topic.title = 'this title is too long'
|
236
|
+
end
|
311
237
|
end
|
312
238
|
|
313
|
-
#
|
239
|
+
# validates_length_of :is
|
314
240
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
241
|
+
test "#validates_length_of (:is) and no custom message" do
|
242
|
+
expect_error_added(@topic, :title, :wrong_length, :default => nil, :count => 5) do
|
243
|
+
Topic.validates_length_of :title, :is => 5
|
244
|
+
@topic.title = 'this title has the wrong length'
|
245
|
+
end
|
320
246
|
end
|
321
247
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
248
|
+
test "#validates_length_of (:is) and a custom message" do
|
249
|
+
expect_error_added(@topic, :title, :wrong_length, :default => 'custom', :count => 5) do
|
250
|
+
Topic.validates_length_of :title, :is => 5, :wrong_length => 'custom'
|
251
|
+
@topic.title = 'this title has the wrong length'
|
252
|
+
end
|
327
253
|
end
|
328
254
|
|
329
|
-
#
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
@topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => nil})
|
335
|
-
@topic.valid?
|
255
|
+
test "#validates_length_of (:is) finds the correct message translations" do
|
256
|
+
assert_message_translations(@topic, :title, :wrong_length) do
|
257
|
+
Topic.validates_length_of :title, :is => 5
|
258
|
+
@topic.title = 'this title has the wrong length'
|
259
|
+
end
|
336
260
|
end
|
337
261
|
|
338
|
-
|
339
|
-
Topic.validates_exclusion_of :title, :in => %w(a b c), :message => 'custom'
|
340
|
-
@topic.title = 'a'
|
341
|
-
@topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => 'custom'})
|
342
|
-
@topic.valid?
|
343
|
-
end
|
262
|
+
# validates_uniqueness_of
|
344
263
|
|
345
|
-
#
|
264
|
+
test "#validates_uniqueness_of and no custom message" do
|
265
|
+
expect_error_added(@topic, :title, :taken, :default => nil, :value => 'unique!') do
|
266
|
+
Topic.validates_uniqueness_of :title
|
267
|
+
@topic.title = unique_topic.title
|
268
|
+
end
|
269
|
+
end
|
346
270
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
271
|
+
test "#validates_uniqueness_of and a custom message" do
|
272
|
+
expect_error_added(@topic, :title, :taken, :default => 'custom', :value => 'unique!') do
|
273
|
+
Topic.validates_uniqueness_of :title, :message => 'custom'
|
274
|
+
@topic.title = unique_topic.title
|
275
|
+
end
|
352
276
|
end
|
353
277
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
278
|
+
test "#validates_uniqueness_of finds the correct message translations" do
|
279
|
+
assert_message_translations(@topic, :title, :taken) do
|
280
|
+
Topic.validates_uniqueness_of :title
|
281
|
+
@topic.title = unique_topic.title
|
282
|
+
end
|
359
283
|
end
|
360
284
|
|
361
|
-
#
|
285
|
+
# validates_format_of
|
362
286
|
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
287
|
+
test "#validates_format_of and no custom message" do
|
288
|
+
expect_error_added(@topic, :title, :invalid, :default => nil, :value => '72x') do
|
289
|
+
Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
|
290
|
+
@topic.title = '72x'
|
291
|
+
end
|
368
292
|
end
|
369
293
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
294
|
+
test "#validates_format_of and a custom message" do
|
295
|
+
expect_error_added(@topic, :title, :invalid, :default => 'custom', :value => '72x') do
|
296
|
+
Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/, :message => 'custom'
|
297
|
+
@topic.title = '72x'
|
298
|
+
end
|
375
299
|
end
|
376
300
|
|
377
|
-
#
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
@topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => nil})
|
383
|
-
@topic.valid?
|
301
|
+
test "#validates_format_of finds the correct message translations" do
|
302
|
+
assert_message_translations(@topic, :title, :invalid) do
|
303
|
+
Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
|
304
|
+
@topic.title = '72x'
|
305
|
+
end
|
384
306
|
end
|
385
307
|
|
386
|
-
|
387
|
-
Topic.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom'
|
388
|
-
@topic.title = 0
|
389
|
-
@topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => 'custom'})
|
390
|
-
@topic.valid?
|
391
|
-
end
|
308
|
+
# validates_inclusion_of
|
392
309
|
|
393
|
-
#
|
310
|
+
test "#validates_inclusion_of and no custom message" do
|
311
|
+
list = %w(a b c)
|
312
|
+
expect_error_added(@topic, :title, :inclusion, :default => nil, :value => 'z') do
|
313
|
+
Topic.validates_inclusion_of :title, :in => list
|
314
|
+
@topic.title = 'z'
|
315
|
+
end
|
316
|
+
end
|
394
317
|
|
395
|
-
|
396
|
-
|
397
|
-
@topic
|
398
|
-
|
399
|
-
|
318
|
+
test "#validates_inclusion_of and a custom message" do
|
319
|
+
list = %w(a b c)
|
320
|
+
expect_error_added(@topic, :title, :inclusion, :default => 'custom', :value => 'z') do
|
321
|
+
Topic.validates_inclusion_of :title, :in => list, :message => 'custom'
|
322
|
+
@topic.title = 'z'
|
323
|
+
end
|
400
324
|
end
|
401
325
|
|
402
|
-
|
403
|
-
|
404
|
-
@topic
|
405
|
-
|
406
|
-
|
326
|
+
test "#validates_inclusion_of finds the correct message translations" do
|
327
|
+
list = %w(a b c)
|
328
|
+
assert_message_translations(@topic, :title, :inclusion) do
|
329
|
+
Topic.validates_inclusion_of :title, :in => list
|
330
|
+
@topic.title = 'z'
|
331
|
+
end
|
407
332
|
end
|
408
333
|
|
409
|
-
#
|
334
|
+
# validates_exclusion_of
|
410
335
|
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
336
|
+
test "#validates_exclusion_of and no custom message" do
|
337
|
+
list = %w(a b c)
|
338
|
+
expect_error_added(@topic, :title, :exclusion, :default => nil, :value => 'a') do
|
339
|
+
Topic.validates_exclusion_of :title, :in => list
|
340
|
+
@topic.title = 'a'
|
341
|
+
end
|
415
342
|
end
|
416
343
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
344
|
+
test "#validates_exclusion_of and a custom message" do
|
345
|
+
list = %w(a b c)
|
346
|
+
expect_error_added(@topic, :title, :exclusion, :default => 'custom', :value => 'a') do
|
347
|
+
Topic.validates_exclusion_of :title, :in => list, :message => 'custom'
|
348
|
+
@topic.title = 'a'
|
349
|
+
end
|
421
350
|
end
|
422
351
|
|
423
|
-
#
|
352
|
+
test "#validates_exclusion_of finds the correct message translations" do
|
353
|
+
list = %w(a b c)
|
354
|
+
assert_message_translations(@topic, :title, :exclusion) do
|
355
|
+
Topic.validates_exclusion_of :title, :in => list
|
356
|
+
@topic.title = 'a'
|
357
|
+
end
|
358
|
+
end
|
424
359
|
|
425
|
-
|
426
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:confirmation => 'custom message'}}}}}}
|
427
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}}
|
360
|
+
# validates_numericality_of :not_a_number, without :only_integer
|
428
361
|
|
429
|
-
|
430
|
-
@topic
|
431
|
-
|
432
|
-
|
362
|
+
test "#validates_numericality_of (:not_a_number, w/o :only_integer) no custom message" do
|
363
|
+
expect_error_added(@topic, :title, :not_a_number, :default => nil, :value => 'a') do
|
364
|
+
Topic.validates_numericality_of :title
|
365
|
+
@topic.title = 'a'
|
366
|
+
end
|
433
367
|
end
|
434
368
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
@topic.valid?
|
441
|
-
assert_equal 'global message', @topic.errors.on(:title)
|
369
|
+
test "#validates_numericality_of (:not_a_number, w/o :only_integer) and a custom message" do
|
370
|
+
expect_error_added(@topic, :title, :not_a_number, :default => 'custom', :value => 'a') do
|
371
|
+
Topic.validates_numericality_of :title, :message => 'custom'
|
372
|
+
@topic.title = 'a'
|
373
|
+
end
|
442
374
|
end
|
443
375
|
|
444
|
-
#
|
376
|
+
test "#validates_numericality_of (:not_a_number, w/o :only_integer) finds the correct message translations" do
|
377
|
+
assert_message_translations(@topic, :title, :not_a_number) do
|
378
|
+
Topic.validates_numericality_of :title
|
379
|
+
@topic.title = 'a'
|
380
|
+
end
|
381
|
+
end
|
445
382
|
|
446
|
-
|
447
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:accepted => 'custom message'}}}}}}
|
448
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}}
|
383
|
+
# validates_numericality_of :not_a_number, with :only_integer
|
449
384
|
|
450
|
-
|
451
|
-
@topic
|
452
|
-
|
385
|
+
test "#validates_numericality_of (:not_a_number, with :only_integer) no custom message" do
|
386
|
+
expect_error_added(@topic, :title, :not_a_number, :default => nil, :value => 'a') do
|
387
|
+
Topic.validates_numericality_of :title, :only_integer => true
|
388
|
+
@topic.title = 'a'
|
389
|
+
end
|
453
390
|
end
|
454
391
|
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
assert_equal 'global message', @topic.errors.on(:title)
|
392
|
+
test "#validates_numericality_of (:not_a_number, with :only_integer) and a custom message" do
|
393
|
+
expect_error_added(@topic, :title, :not_a_number, :default => 'custom', :value => 'a') do
|
394
|
+
Topic.validates_numericality_of :title, :only_integer => true, :message => 'custom'
|
395
|
+
@topic.title = 'a'
|
396
|
+
end
|
461
397
|
end
|
462
398
|
|
463
|
-
#
|
399
|
+
test "#validates_numericality_of (:not_a_number, with :only_integer) finds the correct message translations" do
|
400
|
+
assert_message_translations(@topic, :title, :not_a_number) do
|
401
|
+
Topic.validates_numericality_of :title, :only_integer => true
|
402
|
+
@topic.title = 'a'
|
403
|
+
end
|
404
|
+
end
|
464
405
|
|
465
|
-
|
466
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:blank => 'custom message'}}}}}}
|
467
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:blank => 'global message'}}}
|
406
|
+
# validates_numericality_of :odd
|
468
407
|
|
469
|
-
|
470
|
-
@topic
|
471
|
-
|
408
|
+
test "#validates_numericality_of (:odd) no custom message" do
|
409
|
+
expect_error_added(@topic, :title, :odd, :default => nil, :value => 0) do
|
410
|
+
Topic.validates_numericality_of :title, :only_integer => true, :odd => true
|
411
|
+
@topic.title = 0
|
412
|
+
end
|
472
413
|
end
|
473
414
|
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
assert_equal 'global message', @topic.errors.on(:title)
|
415
|
+
test "#validates_numericality_of (:odd) and a custom message" do
|
416
|
+
expect_error_added(@topic, :title, :odd, :default => 'custom', :value => 0) do
|
417
|
+
Topic.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom'
|
418
|
+
@topic.title = 0
|
419
|
+
end
|
480
420
|
end
|
481
421
|
|
482
|
-
#
|
422
|
+
test "#validates_numericality_of (:odd) finds the correct message translations" do
|
423
|
+
assert_message_translations(@topic, :title, :odd) do
|
424
|
+
Topic.validates_numericality_of :title, :only_integer => true, :odd => true
|
425
|
+
@topic.title = 0
|
426
|
+
end
|
427
|
+
end
|
483
428
|
|
484
|
-
|
485
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:too_short => 'custom message'}}}}}}
|
486
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}}
|
429
|
+
# validates_numericality_of :even
|
487
430
|
|
488
|
-
|
489
|
-
@topic
|
490
|
-
|
431
|
+
test "#validates_numericality_of (:even) no custom message" do
|
432
|
+
expect_error_added(@topic, :title, :even, :default => nil, :value => 1) do
|
433
|
+
Topic.validates_numericality_of :title, :only_integer => true, :even => true
|
434
|
+
@topic.title = 1
|
435
|
+
end
|
491
436
|
end
|
492
437
|
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
assert_equal 'global message', @topic.errors.on(:title)
|
438
|
+
test "#validates_numericality_of (:even) and a custom message" do
|
439
|
+
expect_error_added(@topic, :title, :even, :default => 'custom', :value => 1) do
|
440
|
+
Topic.validates_numericality_of :title, :only_integer => true, :even => true, :message => 'custom'
|
441
|
+
@topic.title = 1
|
442
|
+
end
|
499
443
|
end
|
500
444
|
|
501
|
-
#
|
445
|
+
test "#validates_numericality_of (:even) finds the correct message translations" do
|
446
|
+
assert_message_translations(@topic, :title, :even) do
|
447
|
+
Topic.validates_numericality_of :title, :only_integer => true, :even => true
|
448
|
+
@topic.title = 1
|
449
|
+
end
|
450
|
+
end
|
502
451
|
|
503
|
-
|
504
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}}
|
505
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
|
452
|
+
# validates_numericality_of :less_than
|
506
453
|
|
507
|
-
|
508
|
-
@topic
|
509
|
-
|
454
|
+
test "#validates_numericality_of (:less_than) no custom message" do
|
455
|
+
expect_error_added(@topic, :title, :less_than, :default => nil, :value => 1, :count => 0) do
|
456
|
+
Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
|
457
|
+
@topic.title = 1
|
458
|
+
end
|
510
459
|
end
|
511
460
|
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
assert_equal 'global message', @topic.errors.on(:title)
|
461
|
+
test "#validates_numericality_of (:less_than) and a custom message" do
|
462
|
+
expect_error_added(@topic, :title, :less_than, :default => 'custom', :value => 1, :count => 0) do
|
463
|
+
Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom'
|
464
|
+
@topic.title = 1
|
465
|
+
end
|
518
466
|
end
|
519
467
|
|
520
|
-
#
|
468
|
+
test "#validates_numericality_of (:less_than) finds the correct message translations" do
|
469
|
+
assert_message_translations(@topic, :title, :less_than) do
|
470
|
+
Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
|
471
|
+
@topic.title = 1
|
472
|
+
end
|
473
|
+
end
|
521
474
|
|
522
|
-
|
523
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}}
|
524
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
|
475
|
+
# validates_associated
|
525
476
|
|
526
|
-
|
527
|
-
|
528
|
-
|
477
|
+
test "#validates_associated no custom message" do
|
478
|
+
expect_error_added(replied_topic, :replies, :invalid, :default => nil, :value => replied_topic.replies) do
|
479
|
+
Topic.validates_associated :replies
|
480
|
+
end
|
529
481
|
end
|
530
482
|
|
531
|
-
|
532
|
-
|
483
|
+
test "#validates_associated and a custom message" do
|
484
|
+
expect_error_added(replied_topic, :replies, :invalid, :default => 'custom', :value => replied_topic.replies) do
|
485
|
+
Topic.validates_associated :replies, :message => 'custom'
|
486
|
+
end
|
487
|
+
end
|
533
488
|
|
534
|
-
|
535
|
-
|
536
|
-
|
489
|
+
test "#validates_associated finds the correct message translations" do
|
490
|
+
assert_message_translations(replied_topic, :replies, :invalid) do
|
491
|
+
Topic.validates_associated :replies
|
492
|
+
end
|
537
493
|
end
|
494
|
+
end
|
538
495
|
|
539
496
|
|
540
|
-
|
497
|
+
# ACTIVERECORD ERROR
|
498
|
+
#
|
499
|
+
# * test that it passes given interpolation arguments, the human model name and human attribute name
|
500
|
+
# * test that it looks messages up with the the correct keys
|
501
|
+
# * test that it looks up the correct default messages
|
541
502
|
|
542
|
-
|
543
|
-
|
544
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
|
503
|
+
class ActiveRecordErrorI18nTests < ActiveSupport::TestCase
|
504
|
+
include ActiveRecordValidationsI18nTestHelper
|
545
505
|
|
546
|
-
|
547
|
-
@
|
548
|
-
|
506
|
+
def setup
|
507
|
+
@reply = Reply.new
|
508
|
+
@old_backend, I18n.backend = I18n.backend, I18n::Backend::Simple.new
|
549
509
|
end
|
550
510
|
|
551
|
-
def
|
552
|
-
I18n.backend
|
553
|
-
|
554
|
-
Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
|
555
|
-
@topic.valid?
|
556
|
-
assert_equal 'global message', @topic.errors.on(:title)
|
511
|
+
def teardown
|
512
|
+
I18n.backend = @old_backend
|
513
|
+
I18n.locale = nil
|
557
514
|
end
|
558
515
|
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:inclusion => 'custom message'}}}}}}
|
563
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}}
|
516
|
+
def assert_error_message(message, *args)
|
517
|
+
assert_equal message, ActiveRecord::Error.new(@reply, *args).message
|
518
|
+
end
|
564
519
|
|
565
|
-
|
566
|
-
@
|
567
|
-
assert_equal 'custom message', @topic.errors.on(:title)
|
520
|
+
def assert_full_message(message, *args)
|
521
|
+
assert_equal message, ActiveRecord::Error.new(@reply, *args).full_message
|
568
522
|
end
|
569
523
|
|
570
|
-
|
571
|
-
|
524
|
+
test "#generate_message passes the model attribute value for interpolation" do
|
525
|
+
store_translations(:errors => { :messages => { :foo => "You fooed: {{value}}." } })
|
526
|
+
@reply.title = "da title"
|
527
|
+
assert_error_message 'You fooed: da title.', :title, :foo
|
528
|
+
end
|
572
529
|
|
573
|
-
|
574
|
-
|
575
|
-
|
530
|
+
test "#generate_message passes the human_name of the model for interpolation" do
|
531
|
+
store_translations(
|
532
|
+
:errors => { :messages => { :foo => "You fooed: {{model}}." } },
|
533
|
+
:models => { :topic => 'da topic' }
|
534
|
+
)
|
535
|
+
assert_error_message 'You fooed: da topic.', :title, :foo
|
576
536
|
end
|
577
537
|
|
578
|
-
#
|
538
|
+
test "#generate_message passes the human_name of the attribute for interpolation" do
|
539
|
+
store_translations(
|
540
|
+
:errors => { :messages => { :foo => "You fooed: {{attribute}}." } },
|
541
|
+
:attributes => { :topic => { :title => 'da topic title' } }
|
542
|
+
)
|
543
|
+
assert_error_message 'You fooed: da topic title.', :title, :foo
|
544
|
+
end
|
579
545
|
|
580
|
-
|
581
|
-
|
582
|
-
|
546
|
+
# generate_message will look up the key for the error message (e.g. :blank) in these namespaces:
|
547
|
+
#
|
548
|
+
# activerecord.errors.models.reply.attributes.title
|
549
|
+
# activerecord.errors.models.reply
|
550
|
+
# activerecord.errors.models.topic.attributes.title
|
551
|
+
# activerecord.errors.models.topic
|
552
|
+
# [default from class level :validates_foo statement if this is a String]
|
553
|
+
# activerecord.errors.messages
|
583
554
|
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
555
|
+
test "#generate_message key fallbacks (given a String as key)" do
|
556
|
+
store_translations(
|
557
|
+
:errors => {
|
558
|
+
:models => {
|
559
|
+
:reply => {
|
560
|
+
:attributes => { :title => { :custom => 'activerecord.errors.models.reply.attributes.title.custom' } },
|
561
|
+
:custom => 'activerecord.errors.models.reply.custom'
|
562
|
+
},
|
563
|
+
:topic => {
|
564
|
+
:attributes => { :title => { :custom => 'activerecord.errors.models.topic.attributes.title.custom' } },
|
565
|
+
:custom => 'activerecord.errors.models.topic.custom'
|
566
|
+
}
|
567
|
+
},
|
568
|
+
:messages => {
|
569
|
+
:custom => 'activerecord.errors.messages.custom',
|
570
|
+
:kaputt => 'activerecord.errors.messages.kaputt'
|
571
|
+
}
|
572
|
+
}
|
573
|
+
)
|
589
574
|
|
590
|
-
|
591
|
-
|
575
|
+
assert_error_message 'activerecord.errors.models.reply.attributes.title.custom', :title, :kaputt, :message => 'custom'
|
576
|
+
delete_translation :'activerecord.errors.models.reply.attributes.title.custom'
|
592
577
|
|
593
|
-
|
594
|
-
|
595
|
-
@topic.valid?
|
596
|
-
assert_equal 'global message', @topic.errors.on(:title)
|
597
|
-
end
|
578
|
+
assert_error_message 'activerecord.errors.models.reply.custom', :title, :kaputt, :message => 'custom'
|
579
|
+
delete_translation :'activerecord.errors.models.reply.custom'
|
598
580
|
|
599
|
-
|
581
|
+
assert_error_message 'activerecord.errors.models.topic.attributes.title.custom', :title, :kaputt, :message => 'custom'
|
582
|
+
delete_translation :'activerecord.errors.models.topic.attributes.title.custom'
|
600
583
|
|
601
|
-
|
602
|
-
|
603
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
|
584
|
+
assert_error_message 'activerecord.errors.models.topic.custom', :title, :kaputt, :message => 'custom'
|
585
|
+
delete_translation :'activerecord.errors.models.topic.custom'
|
604
586
|
|
605
|
-
|
606
|
-
|
607
|
-
@topic.valid?
|
608
|
-
assert_equal 'custom message', @topic.errors.on(:title)
|
609
|
-
end
|
587
|
+
assert_error_message 'activerecord.errors.messages.custom', :title, :kaputt, :message => 'custom'
|
588
|
+
delete_translation :'activerecord.errors.messages.custom'
|
610
589
|
|
611
|
-
|
612
|
-
|
590
|
+
# Implementing this would clash with the AR default behaviour of using validates_foo :message => 'foo'
|
591
|
+
# as an untranslated string. I.e. at this point we can either fall back to the given string from the
|
592
|
+
# class-level macro (validates_*) or fall back to the default message for this validation type.
|
593
|
+
# assert_error_message 'activerecord.errors.messages.kaputt', :title, :kaputt, :message => 'custom'
|
613
594
|
|
614
|
-
|
615
|
-
@topic.title = 'a'
|
616
|
-
@topic.valid?
|
617
|
-
assert_equal 'global message', @topic.errors.on(:title)
|
595
|
+
assert_error_message 'custom', :title, :kaputt, :message => 'custom'
|
618
596
|
end
|
619
597
|
|
620
|
-
#
|
598
|
+
test "#generate_message key fallbacks (given a Symbol as key)" do
|
599
|
+
store_translations(
|
600
|
+
:errors => {
|
601
|
+
:models => {
|
602
|
+
:reply => {
|
603
|
+
:attributes => { :title => { :kaputt => 'activerecord.errors.models.reply.attributes.title.kaputt' } },
|
604
|
+
:kaputt => 'activerecord.errors.models.reply.kaputt'
|
605
|
+
},
|
606
|
+
:topic => {
|
607
|
+
:attributes => { :title => { :kaputt => 'activerecord.errors.models.topic.attributes.title.kaputt' } },
|
608
|
+
:kaputt => 'activerecord.errors.models.topic.kaputt'
|
609
|
+
}
|
610
|
+
},
|
611
|
+
:messages => {
|
612
|
+
:kaputt => 'activerecord.errors.messages.kaputt'
|
613
|
+
}
|
614
|
+
}
|
615
|
+
)
|
616
|
+
|
617
|
+
assert_error_message 'activerecord.errors.models.reply.attributes.title.kaputt', :title, :kaputt
|
618
|
+
delete_translation :'activerecord.errors.models.reply.attributes.title.kaputt'
|
621
619
|
|
622
|
-
|
623
|
-
|
624
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
|
620
|
+
assert_error_message 'activerecord.errors.models.reply.kaputt', :title, :kaputt
|
621
|
+
delete_translation :'activerecord.errors.models.reply.kaputt'
|
625
622
|
|
626
|
-
|
627
|
-
|
628
|
-
@topic.valid?
|
629
|
-
assert_equal 'custom message', @topic.errors.on(:title)
|
630
|
-
end
|
623
|
+
assert_error_message 'activerecord.errors.models.topic.attributes.title.kaputt', :title, :kaputt
|
624
|
+
delete_translation :'activerecord.errors.models.topic.attributes.title.kaputt'
|
631
625
|
|
632
|
-
|
633
|
-
|
626
|
+
assert_error_message 'activerecord.errors.models.topic.kaputt', :title, :kaputt
|
627
|
+
delete_translation :'activerecord.errors.models.topic.kaputt'
|
634
628
|
|
635
|
-
|
636
|
-
@topic.title = 'a'
|
637
|
-
@topic.valid?
|
638
|
-
assert_equal 'global message', @topic.errors.on(:title)
|
629
|
+
assert_error_message 'activerecord.errors.messages.kaputt', :title, :kaputt
|
639
630
|
end
|
640
631
|
|
641
|
-
#
|
632
|
+
# full_messages
|
642
633
|
|
643
|
-
|
644
|
-
|
645
|
-
|
634
|
+
test "#full_message with no format present" do
|
635
|
+
store_translations(:errors => { :messages => { :kaputt => 'is kaputt' } })
|
636
|
+
assert_full_message 'Title is kaputt', :title, :kaputt
|
637
|
+
end
|
646
638
|
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
assert_equal 'custom message', @topic.errors.on(:title)
|
639
|
+
test "#full_message with a format present" do
|
640
|
+
store_translations(:errors => { :messages => { :kaputt => 'is kaputt' }, :full_messages => { :format => '{{attribute}}: {{message}}' } })
|
641
|
+
assert_full_message 'Title: is kaputt', :title, :kaputt
|
651
642
|
end
|
652
643
|
|
653
|
-
|
654
|
-
|
644
|
+
test "#full_message with a type specific format present" do
|
645
|
+
store_translations(:errors => { :messages => { :kaputt => 'is kaputt' }, :full_messages => { :kaputt => '{{attribute}} {{message}}!' } })
|
646
|
+
assert_full_message 'Title is kaputt!', :title, :kaputt
|
647
|
+
end
|
655
648
|
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
assert_equal 'global message', @topic.errors.on(:title)
|
649
|
+
test "#full_message with class-level specified custom message" do
|
650
|
+
store_translations(:errors => { :messages => { :broken => 'is kaputt' }, :full_messages => { :broken => '{{attribute}} {{message}}?!' } })
|
651
|
+
assert_full_message 'Title is kaputt?!', :title, :kaputt, :message => :broken
|
660
652
|
end
|
661
653
|
|
662
|
-
#
|
654
|
+
# switch locales
|
663
655
|
|
664
|
-
|
665
|
-
|
666
|
-
|
656
|
+
test "#message allows to switch locales" do
|
657
|
+
store_translations(:en, :errors => { :messages => { :kaputt => 'is kaputt' } })
|
658
|
+
store_translations(:de, :errors => { :messages => { :kaputt => 'ist kaputt' } })
|
667
659
|
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
660
|
+
assert_error_message 'is kaputt', :title, :kaputt
|
661
|
+
I18n.locale = :de
|
662
|
+
assert_error_message 'ist kaputt', :title, :kaputt
|
663
|
+
I18n.locale = :en
|
664
|
+
assert_error_message 'is kaputt', :title, :kaputt
|
672
665
|
end
|
673
666
|
|
674
|
-
|
675
|
-
|
667
|
+
test "#full_message allows to switch locales" do
|
668
|
+
store_translations(:en, :errors => { :messages => { :kaputt => 'is kaputt' } }, :attributes => { :topic => { :title => 'The title' } })
|
669
|
+
store_translations(:de, :errors => { :messages => { :kaputt => 'ist kaputt' } }, :attributes => { :topic => { :title => 'Der Titel' } })
|
676
670
|
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
671
|
+
assert_full_message 'The title is kaputt', :title, :kaputt
|
672
|
+
I18n.locale = :de
|
673
|
+
assert_full_message 'Der Titel ist kaputt', :title, :kaputt
|
674
|
+
I18n.locale = :en
|
675
|
+
assert_full_message 'The title is kaputt', :title, :kaputt
|
681
676
|
end
|
677
|
+
end
|
682
678
|
|
679
|
+
# ACTIVERECORD DEFAULT ERROR MESSAGES
|
680
|
+
#
|
681
|
+
# * test that Error generates the default error messages
|
683
682
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:replies => {:invalid => 'custom message'}}}}}}
|
688
|
-
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
|
689
|
-
|
690
|
-
Topic.validates_associated :replies
|
691
|
-
replied_topic.valid?
|
692
|
-
assert_equal 'custom message', replied_topic.errors.on(:replies)
|
683
|
+
class ActiveRecordDefaultErrorMessagesI18nTests < ActiveSupport::TestCase
|
684
|
+
def assert_default_error_message(message, *args)
|
685
|
+
assert_equal message, error_message(*args)
|
693
686
|
end
|
694
687
|
|
695
|
-
def
|
696
|
-
|
688
|
+
def error_message(*args)
|
689
|
+
ActiveRecord::Error.new(Topic.new, :title, *args).message
|
690
|
+
end
|
697
691
|
|
698
|
-
|
699
|
-
|
700
|
-
|
692
|
+
# used by: validates_inclusion_of
|
693
|
+
test "default error message: inclusion" do
|
694
|
+
assert_default_error_message 'is not included in the list', :inclusion, :value => 'title'
|
701
695
|
end
|
702
696
|
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
@topic.title = nil
|
707
|
-
@topic.valid?
|
708
|
-
assert_equal "I am a custom error", @topic.errors.on(:title)
|
697
|
+
# used by: validates_exclusion_of
|
698
|
+
test "default error message: exclusion" do
|
699
|
+
assert_default_error_message 'is reserved', :exclusion, :value => 'title'
|
709
700
|
end
|
710
701
|
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
@topic.title = nil
|
715
|
-
@topic.valid?
|
716
|
-
assert_equal "I am a custom error", @topic.errors.on(:title)
|
702
|
+
# used by: validates_associated and validates_format_of
|
703
|
+
test "default error message: invalid" do
|
704
|
+
assert_default_error_message 'is invalid', :invalid, :value => 'title'
|
717
705
|
end
|
718
706
|
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
@topic.title = nil
|
723
|
-
@topic.valid?
|
724
|
-
assert_equal "I am a custom error", @topic.errors.on(:title)
|
707
|
+
# used by: validates_confirmation_of
|
708
|
+
test "default error message: confirmation" do
|
709
|
+
assert_default_error_message "doesn't match confirmation", :confirmation, :default => nil
|
725
710
|
end
|
726
711
|
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
@topic.valid?
|
731
|
-
assert_equal "I am a custom error", @topic.errors.on(:title)
|
712
|
+
# used by: validates_acceptance_of
|
713
|
+
test "default error message: accepted" do
|
714
|
+
assert_default_error_message "must be accepted", :accepted
|
732
715
|
end
|
733
716
|
|
734
|
-
|
717
|
+
# used by: add_on_empty
|
718
|
+
test "default error message: empty" do
|
719
|
+
assert_default_error_message "can't be empty", :empty
|
720
|
+
end
|
735
721
|
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
@topic = Topic.new
|
740
|
-
I18n.backend.store_translations :'en', {
|
741
|
-
:activerecord => {
|
742
|
-
:errors => {
|
743
|
-
:messages => {
|
744
|
-
:inclusion => "is not included in the list",
|
745
|
-
:exclusion => "is reserved",
|
746
|
-
:invalid => "is invalid",
|
747
|
-
:confirmation => "doesn't match confirmation",
|
748
|
-
:accepted => "must be accepted",
|
749
|
-
:empty => "can't be empty",
|
750
|
-
:blank => "can't be blank",
|
751
|
-
:too_long => "is too long (maximum is {{count}} characters)",
|
752
|
-
:too_short => "is too short (minimum is {{count}} characters)",
|
753
|
-
:wrong_length => "is the wrong length (should be {{count}} characters)",
|
754
|
-
:taken => "has already been taken",
|
755
|
-
:not_a_number => "is not a number",
|
756
|
-
:greater_than => "must be greater than {{count}}",
|
757
|
-
:greater_than_or_equal_to => "must be greater than or equal to {{count}}",
|
758
|
-
:equal_to => "must be equal to {{count}}",
|
759
|
-
:less_than => "must be less than {{count}}",
|
760
|
-
:less_than_or_equal_to => "must be less than or equal to {{count}}",
|
761
|
-
:odd => "must be odd",
|
762
|
-
:even => "must be even"
|
763
|
-
}
|
764
|
-
}
|
765
|
-
}
|
766
|
-
}
|
722
|
+
# used by: add_on_blank
|
723
|
+
test "default error message: blank" do
|
724
|
+
assert_default_error_message "can't be blank", :blank
|
767
725
|
end
|
768
726
|
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
773
|
-
model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
774
|
-
end
|
727
|
+
# used by: validates_length_of
|
728
|
+
test "default error message: too_long" do
|
729
|
+
assert_default_error_message "is too long (maximum is 10 characters)", :too_long, :count => 10
|
775
730
|
end
|
776
731
|
|
777
|
-
#
|
778
|
-
|
779
|
-
|
732
|
+
# used by: validates_length_of
|
733
|
+
test "default error message: too_short" do
|
734
|
+
assert_default_error_message "is too short (minimum is 10 characters)", :too_short, :count => 10
|
780
735
|
end
|
781
736
|
|
782
|
-
|
783
|
-
|
737
|
+
# used by: validates_length_of
|
738
|
+
test "default error message: wrong_length" do
|
739
|
+
assert_default_error_message "is the wrong length (should be 10 characters)", :wrong_length, :count => 10
|
784
740
|
end
|
785
741
|
|
786
|
-
#
|
787
|
-
|
788
|
-
|
742
|
+
# used by: validates_uniqueness_of
|
743
|
+
test "default error message: taken" do
|
744
|
+
assert_default_error_message "has already been taken", :taken, :value => 'title'
|
789
745
|
end
|
790
746
|
|
791
|
-
|
792
|
-
|
747
|
+
# used by: validates_numericality_of
|
748
|
+
test "default error message: not_a_number" do
|
749
|
+
assert_default_error_message "is not a number", :not_a_number, :value => 'title'
|
793
750
|
end
|
794
751
|
|
795
|
-
#
|
796
|
-
|
797
|
-
|
798
|
-
assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title')
|
752
|
+
# used by: validates_numericality_of
|
753
|
+
test "default error message: greater_than" do
|
754
|
+
assert_default_error_message "must be greater than 10", :greater_than, :value => 'title', :count => 10
|
799
755
|
end
|
800
756
|
|
801
|
-
|
802
|
-
|
757
|
+
# used by: validates_numericality_of
|
758
|
+
test "default error message: greater_than_or_equal_to" do
|
759
|
+
assert_default_error_message "must be greater than or equal to 10", :greater_than_or_equal_to, :value => 'title', :count => 10
|
803
760
|
end
|
804
761
|
|
805
|
-
#
|
806
|
-
|
807
|
-
|
762
|
+
# used by: validates_numericality_of
|
763
|
+
test "default error message: equal_to" do
|
764
|
+
assert_default_error_message "must be equal to 10", :equal_to, :value => 'title', :count => 10
|
808
765
|
end
|
809
766
|
|
810
|
-
|
811
|
-
|
767
|
+
# used by: validates_numericality_of
|
768
|
+
test "default error message: less_than" do
|
769
|
+
assert_default_error_message "must be less than 10", :less_than, :value => 'title', :count => 10
|
812
770
|
end
|
813
771
|
|
814
|
-
#
|
815
|
-
|
816
|
-
|
772
|
+
# used by: validates_numericality_of
|
773
|
+
test "default error message: less_than_or_equal_to" do
|
774
|
+
assert_default_error_message "must be less than or equal to 10", :less_than_or_equal_to, :value => 'title', :count => 10
|
817
775
|
end
|
818
776
|
|
819
|
-
|
820
|
-
|
777
|
+
# used by: validates_numericality_of
|
778
|
+
test "default error message: odd" do
|
779
|
+
assert_default_error_message "must be odd", :odd, :value => 'title', :count => 10
|
821
780
|
end
|
822
781
|
|
823
|
-
#
|
824
|
-
|
825
|
-
|
782
|
+
# used by: validates_numericality_of
|
783
|
+
test "default error message: even" do
|
784
|
+
assert_default_error_message "must be even", :even, :value => 'title', :count => 10
|
826
785
|
end
|
827
786
|
|
828
|
-
|
829
|
-
assert_equal 'custom message',
|
787
|
+
test "custom message string interpolation" do
|
788
|
+
assert_equal 'custom message title', error_message(:invalid, :default => 'custom message {{value}}', :value => 'title')
|
830
789
|
end
|
790
|
+
end
|
791
|
+
|
792
|
+
# ACTIVERECORD VALIDATION ERROR MESSAGES - FULL STACK
|
793
|
+
#
|
794
|
+
# * test a few combinations full stack to ensure the tests above are correct
|
831
795
|
|
832
|
-
|
833
|
-
|
834
|
-
|
796
|
+
class I18nPerson < Person
|
797
|
+
end
|
798
|
+
|
799
|
+
class ActiveRecordValidationsI18nFullStackTests < ActiveSupport::TestCase
|
800
|
+
include ActiveRecordValidationsI18nTestHelper
|
801
|
+
|
802
|
+
def setup
|
803
|
+
reset_callbacks(I18nPerson)
|
804
|
+
@old_backend, I18n.backend = I18n.backend, I18n::Backend::Simple.new
|
805
|
+
@person = I18nPerson.new
|
835
806
|
end
|
836
807
|
|
837
|
-
def
|
838
|
-
|
808
|
+
def teardown
|
809
|
+
reset_callbacks(I18nPerson)
|
810
|
+
I18n.backend = @old_backend
|
839
811
|
end
|
840
812
|
|
841
|
-
|
842
|
-
|
843
|
-
|
813
|
+
def assert_name_invalid(message)
|
814
|
+
yield
|
815
|
+
@person.valid?
|
816
|
+
assert_equal message, @person.errors.on(:name)
|
844
817
|
end
|
845
818
|
|
846
|
-
|
847
|
-
|
819
|
+
# Symbols as class-level validation messages
|
820
|
+
|
821
|
+
test "Symbol as class level validation message translated per attribute (translation on child class)" do
|
822
|
+
assert_name_invalid("is broken") do
|
823
|
+
store_translations :errors => {:models => {:i18n_person => {:attributes => {:name => {:broken => "is broken"}}}}}
|
824
|
+
I18nPerson.validates_presence_of :name, :message => :broken
|
825
|
+
end
|
848
826
|
end
|
849
827
|
|
850
|
-
|
851
|
-
|
852
|
-
|
828
|
+
test "Symbol as class level validation message translated per attribute (translation on base class)" do
|
829
|
+
assert_name_invalid("is broken") do
|
830
|
+
store_translations :errors => {:models => {:person => {:attributes => {:name => {:broken => "is broken"}}}}}
|
831
|
+
I18nPerson.validates_presence_of :name, :message => :broken
|
832
|
+
end
|
853
833
|
end
|
854
834
|
|
855
|
-
|
856
|
-
|
835
|
+
test "Symbol as class level validation message translated per model (translation on child class)" do
|
836
|
+
assert_name_invalid("is broken") do
|
837
|
+
store_translations :errors => {:models => {:i18n_person => {:broken => "is broken"}}}
|
838
|
+
I18nPerson.validates_presence_of :name, :message => :broken
|
839
|
+
end
|
857
840
|
end
|
858
841
|
|
859
|
-
|
860
|
-
|
861
|
-
|
842
|
+
test "Symbol as class level validation message translated per model (translation on base class)" do
|
843
|
+
assert_name_invalid("is broken") do
|
844
|
+
store_translations :errors => {:models => {:person => {:broken => "is broken"}}}
|
845
|
+
I18nPerson.validates_presence_of :name, :message => :broken
|
846
|
+
end
|
862
847
|
end
|
863
848
|
|
864
|
-
|
865
|
-
|
849
|
+
test "Symbol as class level validation message translated as error message" do
|
850
|
+
assert_name_invalid("is broken") do
|
851
|
+
store_translations :errors => {:messages => {:broken => "is broken"}}
|
852
|
+
I18nPerson.validates_presence_of :name, :message => :broken
|
853
|
+
end
|
866
854
|
end
|
867
855
|
|
868
|
-
#
|
869
|
-
|
870
|
-
|
856
|
+
# Strings as class-level validation messages
|
857
|
+
|
858
|
+
test "String as class level validation message translated per attribute (translation on child class)" do
|
859
|
+
assert_name_invalid("is broken") do
|
860
|
+
store_translations :errors => {:models => {:i18n_person => {:attributes => {:name => {"is broken" => "is broken"}}}}}
|
861
|
+
I18nPerson.validates_presence_of :name, :message => "is broken"
|
862
|
+
end
|
871
863
|
end
|
872
864
|
|
873
|
-
|
874
|
-
|
865
|
+
test "String as class level validation message translated per attribute (translation on base class)" do
|
866
|
+
assert_name_invalid("is broken") do
|
867
|
+
store_translations :errors => {:models => {:person => {:attributes => {:name => {"is broken" => "is broken"}}}}}
|
868
|
+
I18nPerson.validates_presence_of :name, :message => "is broken"
|
869
|
+
end
|
875
870
|
end
|
876
871
|
|
877
|
-
|
878
|
-
|
879
|
-
|
872
|
+
test "String as class level validation message translated per model (translation on child class)" do
|
873
|
+
assert_name_invalid("is broken") do
|
874
|
+
store_translations :errors => {:models => {:i18n_person => {"is broken" => "is broken"}}}
|
875
|
+
I18nPerson.validates_presence_of :name, :message => "is broken"
|
876
|
+
end
|
880
877
|
end
|
881
878
|
|
882
|
-
|
883
|
-
|
879
|
+
test "String as class level validation message translated per model (translation on base class)" do
|
880
|
+
assert_name_invalid("is broken") do
|
881
|
+
store_translations :errors => {:models => {:person => {"is broken" => "is broken"}}}
|
882
|
+
I18nPerson.validates_presence_of :name, :message => "is broken"
|
883
|
+
end
|
884
884
|
end
|
885
885
|
|
886
|
-
|
887
|
-
|
888
|
-
|
886
|
+
test "String as class level validation message translated as error message" do
|
887
|
+
assert_name_invalid("is broken") do
|
888
|
+
store_translations :errors => {:messages => {"is broken" => "is broken"}}
|
889
|
+
I18nPerson.validates_presence_of :name, :message => "is broken"
|
890
|
+
end
|
889
891
|
end
|
890
892
|
|
891
|
-
|
892
|
-
|
893
|
+
test "String as class level validation message not translated (uses message as default)" do
|
894
|
+
assert_name_invalid("is broken!") do
|
895
|
+
I18nPerson.validates_presence_of :name, :message => "is broken!"
|
896
|
+
end
|
893
897
|
end
|
898
|
+
end
|
899
|
+
|
900
|
+
class ActiveRecordValidationsI18nFullMessagesFullStackTests < ActiveSupport::TestCase
|
901
|
+
include ActiveRecordValidationsI18nTestHelper
|
894
902
|
|
895
|
-
def
|
896
|
-
|
903
|
+
def setup
|
904
|
+
reset_callbacks(I18nPerson)
|
905
|
+
@old_backend, I18n.backend = I18n.backend, I18n::Backend::Simple.new
|
906
|
+
@person = I18nPerson.new
|
897
907
|
end
|
898
908
|
|
899
|
-
def
|
900
|
-
|
909
|
+
def teardown
|
910
|
+
reset_callbacks(I18nPerson)
|
911
|
+
I18n.backend = @old_backend
|
901
912
|
end
|
902
913
|
|
903
|
-
def
|
904
|
-
|
914
|
+
def assert_full_message(message)
|
915
|
+
yield
|
916
|
+
@person.valid?
|
917
|
+
assert_equal message, @person.errors.full_messages.join
|
905
918
|
end
|
906
919
|
|
907
|
-
|
908
|
-
|
920
|
+
test "full_message format stored per custom error message key" do
|
921
|
+
assert_full_message("Name is broken!") do
|
922
|
+
store_translations :errors => { :messages => { :broken => 'is broken' }, :full_messages => { :broken => '{{attribute}} {{message}}!' } }
|
923
|
+
I18nPerson.validates_presence_of :name, :message => :broken
|
924
|
+
end
|
909
925
|
end
|
910
926
|
|
911
|
-
|
912
|
-
|
927
|
+
test "full_message format stored per error type" do
|
928
|
+
assert_full_message("Name can't be blank!") do
|
929
|
+
store_translations :errors => { :full_messages => { :blank => '{{attribute}} {{message}}!' } }
|
930
|
+
I18nPerson.validates_presence_of :name
|
931
|
+
end
|
913
932
|
end
|
933
|
+
# ActiveRecord#RecordInvalid exception
|
914
934
|
|
935
|
+
test "full_message format stored as default" do
|
936
|
+
assert_full_message("Name: can't be blank") do
|
937
|
+
store_translations :errors => { :full_messages => { :format => '{{attribute}}: {{message}}' } }
|
938
|
+
I18nPerson.validates_presence_of :name
|
939
|
+
end
|
940
|
+
end
|
941
|
+
test "RecordInvalid exception can be localized" do
|
942
|
+
topic = Topic.new
|
943
|
+
topic.errors.add(:title, :invalid)
|
944
|
+
topic.errors.add(:title, :blank)
|
945
|
+
assert_equal "Validation failed: Title is invalid, Title can't be blank", ActiveRecord::RecordInvalid.new(topic).message
|
946
|
+
end
|
915
947
|
end
|