errors_test.rb 14.4 KB
Newer Older
1 2
# frozen_string_literal: true

3
require "cases/helper"
A
Akira Matsuda 已提交
4
require "yaml"
5 6 7 8 9 10 11 12

class ErrorsTest < ActiveModel::TestCase
  class Person
    extend ActiveModel::Naming
    def initialize
      @errors = ActiveModel::Errors.new(self)
    end

V
Vasiliy Ermolovich 已提交
13
    attr_accessor :name, :age
14 15 16
    attr_reader   :errors

    def validate!
V
Vipul A M 已提交
17
      errors.add(:name, :blank, message: "cannot be nil") if name == nil
18 19 20 21 22 23 24 25 26 27 28 29 30
    end

    def read_attribute_for_validation(attr)
      send(attr)
    end

    def self.human_attribute_name(attr, options = {})
      attr
    end

    def self.lookup_ancestors
      [self]
    end
31
  end
32

33 34
  def test_delete
    errors = ActiveModel::Errors.new(self)
35
    errors[:foo] << "omg"
36
    errors.delete("foo")
37 38 39
    assert_empty errors[:foo]
  end

40 41
  def test_include?
    errors = ActiveModel::Errors.new(self)
42
    errors[:foo] << "omg"
43
    assert_includes errors, :foo, "errors should include :foo"
44
    assert_includes errors, "foo", "errors should include 'foo' as :foo"
45 46
  end

P
Paweł Kondzior 已提交
47 48
  def test_dup
    errors = ActiveModel::Errors.new(self)
49
    errors[:foo] << "bar"
P
Paweł Kondzior 已提交
50
    errors_dup = errors.dup
51
    errors_dup[:bar] << "omg"
P
Paweł Kondzior 已提交
52 53 54
    assert_not_same errors_dup.messages, errors.messages
  end

55 56
  def test_has_key?
    errors = ActiveModel::Errors.new(self)
57 58
    errors[:foo] << "omg"
    assert_equal true, errors.has_key?(:foo), "errors should have key :foo"
59
    assert_equal true, errors.has_key?("foo"), "errors should have key 'foo' as :foo"
60 61
  end

62 63
  def test_has_no_key
    errors = ActiveModel::Errors.new(self)
64
    assert_equal false, errors.has_key?(:name), "errors should not have key :name"
65 66
  end

G
Garry Shutler 已提交
67 68
  def test_key?
    errors = ActiveModel::Errors.new(self)
69 70
    errors[:foo] << "omg"
    assert_equal true, errors.key?(:foo), "errors should have key :foo"
71
    assert_equal true, errors.key?("foo"), "errors should have key 'foo' as :foo"
G
Garry Shutler 已提交
72 73 74 75
  end

  def test_no_key
    errors = ActiveModel::Errors.new(self)
76
    assert_equal false, errors.key?(:name), "errors should not have key :name"
G
Garry Shutler 已提交
77 78
  end

79
  test "clear errors" do
80 81 82 83 84
    person = Person.new
    person.validate!

    assert_equal 1, person.errors.count
    person.errors.clear
85
    assert_empty person.errors
86 87
  end

88 89
  test "error access is indifferent" do
    errors = ActiveModel::Errors.new(self)
90
    errors[:foo] << "omg"
91 92 93 94

    assert_equal ["omg"], errors["foo"]
  end

95 96
  test "values returns an array of messages" do
    errors = ActiveModel::Errors.new(self)
97 98
    errors.messages[:foo] = "omg"
    errors.messages[:baz] = "zomg"
99 100 101 102

    assert_equal ["omg", "zomg"], errors.values
  end

103 104 105 106 107 108 109 110
  test "values returns an empty array after try to get a message only" do
    errors = ActiveModel::Errors.new(self)
    errors.messages[:foo]
    errors.messages[:baz]

    assert_equal [], errors.values
  end

111 112
  test "keys returns the error keys" do
    errors = ActiveModel::Errors.new(self)
113 114
    errors.messages[:foo] << "omg"
    errors.messages[:baz] << "zomg"
115 116 117 118

    assert_equal [:foo, :baz], errors.keys
  end

119 120 121 122 123 124 125 126
  test "keys returns an empty array after try to get a message only" do
    errors = ActiveModel::Errors.new(self)
    errors.messages[:foo]
    errors.messages[:baz]

    assert_equal [], errors.keys
  end

127
  test "detecting whether there are errors with empty?, blank?, include?" do
128 129
    person = Person.new
    person.errors[:foo]
130
    assert_empty person.errors
131
    assert_predicate person.errors, :blank?
132
    assert_not_includes person.errors, :foo
133 134
  end

135 136 137 138 139 140 141
  test "include? does not add a key to messages hash" do
    person = Person.new
    person.errors.include?(:foo)

    assert_not person.errors.messages.key?(:foo)
  end

142
  test "adding errors using conditionals with Person#validate!" do
143 144
    person = Person.new
    person.validate!
145 146
    assert_equal ["name cannot be nil"], person.errors.full_messages
    assert_equal ["cannot be nil"], person.errors[:name]
147 148
  end

149
  test "add an error message on a specific attribute" do
150
    person = Person.new
151 152
    person.errors.add(:name, "cannot be blank")
    assert_equal ["cannot be blank"], person.errors[:name]
153 154
  end

155 156 157 158 159 160
  test "add an error message on a specific attribute with a defined type" do
    person = Person.new
    person.errors.add(:name, :blank, message: "cannot be blank")
    assert_equal ["cannot be blank"], person.errors[:name]
  end

161
  test "add an error with a symbol" do
162 163 164 165 166 167
    person = Person.new
    person.errors.add(:name, :blank)
    message = person.errors.generate_message(:name, :blank)
    assert_equal [message], person.errors[:name]
  end

168
  test "add an error with a proc" do
169
    person = Person.new
170
    message = Proc.new { "cannot be blank" }
171
    person.errors.add(:name, message)
172
    assert_equal ["cannot be blank"], person.errors[:name]
173 174
  end

175
  test "added? detects indifferent if a specific error was added to the object" do
176
    person = Person.new
177 178
    person.errors.add(:name, "cannot be blank")
    assert person.errors.added?(:name, "cannot be blank")
179
    assert person.errors.added?("name", "cannot be blank")
180 181
  end

182
  test "added? handles symbol message" do
183 184 185 186 187
    person = Person.new
    person.errors.add(:name, :blank)
    assert person.errors.added?(:name, :blank)
  end

188 189 190 191 192 193
  test "added? returns true when string attribute is used with a symbol message" do
    person = Person.new
    person.errors.add(:name, :blank)
    assert person.errors.added?("name", :blank)
  end

194
  test "added? handles proc messages" do
195
    person = Person.new
196
    message = Proc.new { "cannot be blank" }
197 198 199 200
    person.errors.add(:name, message)
    assert person.errors.added?(:name, message)
  end

201
  test "added? defaults message to :invalid" do
202
    person = Person.new
203
    person.errors.add(:name)
204 205 206
    assert person.errors.added?(:name)
  end

207
  test "added? matches the given message when several errors are present for the same attribute" do
208
    person = Person.new
209
    person.errors.add(:name, "cannot be blank")
210
    person.errors.add(:name, "is invalid")
211
    assert person.errors.added?(:name, "cannot be blank")
212 213
  end

214
  test "added? returns false when no errors are present" do
215
    person = Person.new
216
    assert_not person.errors.added?(:name)
217 218
  end

219
  test "added? returns false when checking a nonexisting error and other errors are present for the given attribute" do
220 221
    person = Person.new
    person.errors.add(:name, "is invalid")
222
    assert_not person.errors.added?(:name, "cannot be blank")
223 224
  end

225 226 227
  test "added? returns false when checking for an error, but not providing message arguments" do
    person = Person.new
    person.errors.add(:name, "cannot be blank")
228
    assert_not person.errors.added?(:name)
229 230
  end

231 232 233 234 235 236 237 238 239 240
  test "added? returns false when checking for an error with an incorrect or missing option" do
    person = Person.new
    person.errors.add :name, :too_long, count: 25

    assert person.errors.added? :name, :too_long, count: 25
    assert_not person.errors.added? :name, :too_long, count: 24
    assert_not person.errors.added? :name, :too_long
    assert_not person.errors.added? :name, "is too long"
  end

A
Alessandro Rodi 已提交
241 242 243 244
  test "added? returns false when checking for an error by symbol and a different error with same message is present" do
    I18n.backend.store_translations("en", errors: { attributes: { name: { wrong: "is wrong", used: "is wrong" } } })
    person = Person.new
    person.errors.add(:name, :wrong)
245
    assert_not person.errors.added?(:name, :used)
A
Alessandro Rodi 已提交
246 247
  end

248
  test "size calculates the number of error messages" do
249
    person = Person.new
250
    person.errors.add(:name, "cannot be blank")
251 252 253
    assert_equal 1, person.errors.size
  end

254 255 256 257 258 259
  test "count calculates the number of error messages" do
    person = Person.new
    person.errors.add(:name, "cannot be blank")
    assert_equal 1, person.errors.count
  end

260
  test "to_a returns the list of errors with complete messages containing the attribute names" do
261
    person = Person.new
262 263 264
    person.errors.add(:name, "cannot be blank")
    person.errors.add(:name, "cannot be nil")
    assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.to_a
265 266
  end

267
  test "to_hash returns the error messages hash" do
268
    person = Person.new
269 270
    person.errors.add(:name, "cannot be blank")
    assert_equal({ name: ["cannot be blank"] }, person.errors.to_hash)
271
  end
272

273 274 275 276 277 278 279 280 281 282
  test "to_hash returns a hash without default proc" do
    person = Person.new
    assert_nil person.errors.to_hash.default_proc
  end

  test "as_json returns a hash without default proc" do
    person = Person.new
    assert_nil person.errors.as_json.default_proc
  end

283
  test "full_messages creates a list of error messages with the attribute name included" do
284
    person = Person.new
285 286 287
    person.errors.add(:name, "cannot be blank")
    person.errors.add(:name, "cannot be nil")
    assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.full_messages
288
  end
289

290
  test "full_messages_for contains all the error messages for the given attribute indifferent" do
291
    person = Person.new
292 293 294
    person.errors.add(:name, "cannot be blank")
    person.errors.add(:name, "cannot be nil")
    assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.full_messages_for(:name)
295 296
  end

297
  test "full_messages_for does not contain error messages from other attributes" do
298
    person = Person.new
299 300 301
    person.errors.add(:name, "cannot be blank")
    person.errors.add(:email, "cannot be blank")
    assert_equal ["name cannot be blank"], person.errors.full_messages_for(:name)
302
    assert_equal ["name cannot be blank"], person.errors.full_messages_for("name")
303 304
  end

305
  test "full_messages_for returns an empty list in case there are no errors for the given attribute" do
306
    person = Person.new
307
    person.errors.add(:name, "cannot be blank")
308 309
    assert_equal [], person.errors.full_messages_for(:email)
  end
310

311
  test "full_message returns the given message when attribute is :base" do
312 313 314 315
    person = Person.new
    assert_equal "press the button", person.errors.full_message(:base, "press the button")
  end

316
  test "full_message returns the given message with the attribute name included" do
317
    person = Person.new
318 319
    assert_equal "name cannot be blank", person.errors.full_message(:name, "cannot be blank")
    assert_equal "name_test cannot be blank", person.errors.full_message(:name_test, "cannot be blank")
320 321
  end

322
  test "as_json creates a json formatted representation of the errors hash" do
L
Lawrence Pit 已提交
323
    person = Person.new
324 325
    person.validate!

326
    assert_equal({ name: ["cannot be nil"] }, person.errors.as_json)
L
Lawrence Pit 已提交
327 328
  end

329
  test "as_json with :full_messages option creates a json formatted representation of the errors containing complete messages" do
330
    person = Person.new
331 332
    person.validate!

333
    assert_equal({ name: ["name cannot be nil"] }, person.errors.as_json(full_messages: true))
334 335
  end

336
  test "generate_message works without i18n_scope" do
337
    person = Person.new
D
Daniel Colson 已提交
338
    assert_not_respond_to Person, :i18n_scope
339 340 341 342
    assert_nothing_raised {
      person.errors.generate_message(:name, :blank)
    }
  end
V
Vasiliy Ermolovich 已提交
343

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
  test "details returns added error detail" do
    person = Person.new
    person.errors.add(:name, :invalid)
    assert_equal({ name: [{ error: :invalid }] }, person.errors.details)
  end

  test "details returns added error detail with custom option" do
    person = Person.new
    person.errors.add(:name, :greater_than, count: 5)
    assert_equal({ name: [{ error: :greater_than, count: 5 }] }, person.errors.details)
  end

  test "details do not include message option" do
    person = Person.new
    person.errors.add(:name, :invalid, message: "is bad")
    assert_equal({ name: [{ error: :invalid }] }, person.errors.details)
  end

  test "dup duplicates details" do
    errors = ActiveModel::Errors.new(Person.new)
    errors.add(:name, :invalid)
    errors_dup = errors.dup
    errors_dup.add(:name, :taken)
367
    assert_not_equal errors_dup.details, errors.details
368 369 370 371 372 373 374 375 376
  end

  test "delete removes details on given attribute" do
    errors = ActiveModel::Errors.new(Person.new)
    errors.add(:name, :invalid)
    errors.delete(:name)
    assert_empty errors.details[:name]
  end

377 378 379
  test "delete returns the deleted messages" do
    errors = ActiveModel::Errors.new(Person.new)
    errors.add(:name, :invalid)
380
    assert_equal ["is invalid"], errors.delete(:name)
381 382
  end

383 384 385 386 387 388
  test "clear removes details" do
    person = Person.new
    person.errors.add(:name, :invalid)

    assert_equal 1, person.errors.details.count
    person.errors.clear
389
    assert_empty person.errors.details
390
  end
391 392 393 394 395 396 397 398 399 400

  test "copy errors" do
    errors = ActiveModel::Errors.new(Person.new)
    errors.add(:name, :invalid)
    person = Person.new
    person.errors.copy!(errors)

    assert_equal [:name], person.errors.messages.keys
    assert_equal [:name], person.errors.details.keys
  end
401

J
Jahfer Husain 已提交
402 403 404 405 406 407 408 409 410 411 412 413
  test "merge errors" do
    errors = ActiveModel::Errors.new(Person.new)
    errors.add(:name, :invalid)

    person = Person.new
    person.errors.add(:name, :blank)
    person.errors.merge!(errors)

    assert_equal({ name: ["can't be blank", "is invalid"] }, person.errors.messages)
    assert_equal({ name: [{ error: :blank }, { error: :invalid }] }, person.errors.details)
  end

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
  test "slice! removes all errors except the given keys" do
    person = Person.new
    person.errors.add(:name, "cannot be nil")
    person.errors.add(:age, "cannot be nil")
    person.errors.add(:gender, "cannot be nil")
    person.errors.add(:city, "cannot be nil")

    person.errors.slice!(:age, "gender")

    assert_equal [:age, :gender], person.errors.keys
  end

  test "slice! returns the deleted errors" do
    person = Person.new
    person.errors.add(:name, "cannot be nil")
    person.errors.add(:age, "cannot be nil")
    person.errors.add(:gender, "cannot be nil")
    person.errors.add(:city, "cannot be nil")

    removed_errors = person.errors.slice!(:age, "gender")

    assert_equal({ name: ["cannot be nil"], city: ["cannot be nil"] }, removed_errors)
  end

438 439 440 441 442 443 444 445
  test "errors are marshalable" do
    errors = ActiveModel::Errors.new(Person.new)
    errors.add(:name, :invalid)
    serialized = Marshal.load(Marshal.dump(errors))

    assert_equal errors.messages, serialized.messages
    assert_equal errors.details, serialized.details
  end
446 447

  test "errors are backward compatible with the Rails 4.2 format" do
448
    yaml = <<~CODE
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
    --- !ruby/object:ActiveModel::Errors
    base: &1 !ruby/object:ErrorsTest::Person
      errors: !ruby/object:ActiveModel::Errors
        base: *1
        messages: {}
    messages: {}
    CODE

    errors = YAML.load(yaml)
    errors.add(:name, :invalid)
    assert_equal({ name: ["is invalid"] }, errors.messages)
    assert_equal({ name: [{ error: :invalid }] }, errors.details)

    errors.clear
    assert_equal({}, errors.messages)
    assert_equal({}, errors.details)
  end
466
end