# frozen_string_literal: true
require "cases/helper"
require "active_model/error"
class ErrorTest < ActiveModel::TestCase
class Person
extend ActiveModel::Naming
def initialize
@errors = ActiveModel::Errors.new(self)
end
attr_accessor :name, :age
attr_reader :errors
def read_attribute_for_validation(attr)
send(attr)
end
def self.human_attribute_name(attr, options = {})
attr
end
def self.lookup_ancestors
[self]
end
end
def test_initialize
base = Person.new
error = ActiveModel::Error.new(base, :name, :too_long, foo: :bar)
assert_equal base, error.base
assert_equal :name, error.attribute
assert_equal :too_long, error.type
assert_equal({ foo: :bar }, error.options)
end
test "initialize without type" do
error = ActiveModel::Error.new(Person.new, :name)
assert_equal :invalid, error.type
assert_equal({}, error.options)
end
test "initialize without type but with options" do
options = { message: "bar" }
error = ActiveModel::Error.new(Person.new, :name, options)
assert_equal(options, error.options)
end
# match?
test "match? handles mixed condition" do
subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2)
assert_not subject.match?(:mineral, :too_coarse)
assert subject.match?(:mineral, :not_enough)
assert subject.match?(:mineral, :not_enough, count: 2)
assert_not subject.match?(:mineral, :not_enough, count: 1)
end
test "match? handles attribute match" do
subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2)
assert_not subject.match?(:foo)
assert subject.match?(:mineral)
end
test "match? handles error type match" do
subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2)
assert_not subject.match?(:mineral, :too_coarse)
assert subject.match?(:mineral, :not_enough)
end
test "match? handles extra options match" do
subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2)
assert_not subject.match?(:mineral, :not_enough, count: 1)
assert subject.match?(:mineral, :not_enough, count: 2)
end
# message
test "message with type as a symbol" do
error = ActiveModel::Error.new(Person.new, :name, :blank)
assert_equal "can't be blank", error.message
end
test "message with custom interpolation" do
subject = ActiveModel::Error.new(Person.new, :name, :inclusion, message: "custom message %{value}", value: "name")
assert_equal "custom message name", subject.message
end
test "message returns plural interpolation" do
subject = ActiveModel::Error.new(Person.new, :name, :too_long, count: 10)
assert_equal "is too long (maximum is 10 characters)", subject.message
end
test "message returns singular interpolation" do
subject = ActiveModel::Error.new(Person.new, :name, :too_long, count: 1)
assert_equal "is too long (maximum is 1 character)", subject.message
end
test "message returns count interpolation" do
subject = ActiveModel::Error.new(Person.new, :name, :too_long, message: "custom message %{count}", count: 10)
assert_equal "custom message 10", subject.message
end
test "message handles lambda in messages and option values, and i18n interpolation" do
subject = ActiveModel::Error.new(Person.new, :name, :invalid,
foo: "foo",
bar: "bar",
baz: Proc.new { "baz" },
message: Proc.new { |model, options|
"%{attribute} %{foo} #{options[:bar]} %{baz}"
}
)
assert_equal "name foo bar baz", subject.message
end
test "generate_message works without i18n_scope" do
person = Person.new
error = ActiveModel::Error.new(person, :name, :blank)
assert_not_respond_to Person, :i18n_scope
assert_nothing_raised {
error.message
}
end
test "message with type as custom message" do
error = ActiveModel::Error.new(Person.new, :name, message: "cannot be blank")
assert_equal "cannot be blank", error.message
end
test "message with options[:message] as custom message" do
error = ActiveModel::Error.new(Person.new, :name, :blank, message: "cannot be blank")
assert_equal "cannot be blank", error.message
end
test "message renders lazily using current locale" do
error = nil
I18n.backend.store_translations(:pl, errors: { messages: { invalid: "jest nieprawidłowe" } })
I18n.with_locale(:en) { error = ActiveModel::Error.new(Person.new, :name, :invalid) }
I18n.with_locale(:pl) {
assert_equal "jest nieprawidłowe", error.message
}
end
test "message uses current locale" do
I18n.backend.store_translations(:en, errors: { messages: { inadequate: "Inadequate %{attribute} found!" } })
error = ActiveModel::Error.new(Person.new, :name, :inadequate)
assert_equal "Inadequate name found!", error.message
end
# full_message
test "full_message returns the given message when attribute is :base" do
error = ActiveModel::Error.new(Person.new, :base, message: "press the button")
assert_equal "press the button", error.full_message
end
test "full_message returns the given message with the attribute name included" do
error = ActiveModel::Error.new(Person.new, :name, :blank)
assert_equal "name can't be blank", error.full_message
end
test "full_message uses default format" do
error = ActiveModel::Error.new(Person.new, :name, message: "can't be blank")
# Use a locale without errors.format
I18n.with_locale(:unknown) {
assert_equal "name can't be blank", error.full_message
}
end
test "equality by base attribute, type and options" do
person = Person.new
e1 = ActiveModel::Error.new(person, :name, foo: :bar)
e2 = ActiveModel::Error.new(person, :name, foo: :bar)
e2.instance_variable_set(:@_humanized_attribute, "Name")
assert_equal(e1, e2)
end
test "inequality" do
person = Person.new
error = ActiveModel::Error.new(person, :name, foo: :bar)
assert error != ActiveModel::Error.new(person, :name, foo: :baz)
assert error != ActiveModel::Error.new(person, :name)
assert error != ActiveModel::Error.new(person, :title, foo: :bar)
assert error != ActiveModel::Error.new(Person.new, :name, foo: :bar)
end
test "comparing against different class would not raise error" do
person = Person.new
error = ActiveModel::Error.new(person, :name, foo: :bar)
assert error != person
end
end