# frozen_string_literal: true
require "abstract_unit"
require "set"
require "action_dispatch"
require "active_support/time"
require "mailers/base_mailer"
require "mailers/proc_mailer"
require "mailers/asset_mailer"
class BaseTest < ActiveSupport::TestCase
include Rails::Dom::Testing::Assertions::DomAssertions
setup do
@original_delivery_method = ActionMailer::Base.delivery_method
ActionMailer::Base.delivery_method = :test
@original_asset_host = ActionMailer::Base.asset_host
@original_assets_dir = ActionMailer::Base.assets_dir
end
teardown do
ActionMailer::Base.asset_host = @original_asset_host
ActionMailer::Base.assets_dir = @original_assets_dir
BaseMailer.deliveries.clear
ActionMailer::Base.delivery_method = @original_delivery_method
end
test "method call to mail does not raise error" do
assert_nothing_raised { BaseMailer.welcome }
end
# Basic mail usage without block
test "mail() should set the headers of the mail message" do
email = BaseMailer.welcome
assert_equal(["system@test.lindsaar.net"], email.to)
assert_equal(["jose@test.plataformatec.com"], email.from)
assert_equal("The first email on new API!", email.subject)
end
test "mail() with from overwrites the class level default" do
email = BaseMailer.welcome(from: "someone@example.com",
to: "another@example.org")
assert_equal(["someone@example.com"], email.from)
assert_equal(["another@example.org"], email.to)
end
test "mail() with bcc, cc, content_type, charset, mime_version, reply_to and date" do
time = Time.now.beginning_of_day.to_datetime
email = BaseMailer.welcome(bcc: "bcc@test.lindsaar.net",
cc: "cc@test.lindsaar.net",
content_type: "multipart/mixed",
charset: "iso-8559-1",
mime_version: "2.0",
reply_to: "reply-to@test.lindsaar.net",
date: time)
assert_equal(["bcc@test.lindsaar.net"], email.bcc)
assert_equal(["cc@test.lindsaar.net"], email.cc)
assert_equal("multipart/mixed; charset=iso-8559-1", email.content_type)
assert_equal("iso-8559-1", email.charset)
assert_equal("2.0", email.mime_version)
assert_equal(["reply-to@test.lindsaar.net"], email.reply_to)
assert_equal(time, email.date)
end
test "mail() renders the template using the method being processed" do
email = BaseMailer.welcome
assert_equal("Welcome", email.body.encoded)
end
test "can pass in :body to the mail method hash" do
email = BaseMailer.welcome(body: "Hello there")
assert_equal("text/plain", email.mime_type)
assert_equal("Hello there", email.body.encoded)
end
test "should set template content type if mail has only one part" do
mail = BaseMailer.html_only
assert_equal("text/html", mail.mime_type)
mail = BaseMailer.plain_text_only
assert_equal("text/plain", mail.mime_type)
end
# Custom headers
test "custom headers" do
email = BaseMailer.welcome
assert_equal("Not SPAM", email["X-SPAM"].decoded)
end
test "can pass random headers in as a hash to mail" do
hash = { "X-Special-Domain-Specific-Header" => "SecretValue",
"In-Reply-To" => "<1234@mikel.me.com>" }
mail = BaseMailer.welcome(hash)
assert_equal("SecretValue", mail["X-Special-Domain-Specific-Header"].decoded)
assert_equal("<1234@mikel.me.com>", mail["In-Reply-To"].decoded)
end
test "can pass random headers in as a hash to headers" do
hash = { "X-Special-Domain-Specific-Header" => "SecretValue",
"In-Reply-To" => "<1234@mikel.me.com>" }
mail = BaseMailer.welcome_with_headers(hash)
assert_equal("SecretValue", mail["X-Special-Domain-Specific-Header"].decoded)
assert_equal("<1234@mikel.me.com>", mail["In-Reply-To"].decoded)
end
# Attachments
test "attachment with content" do
email = BaseMailer.attachment_with_content
assert_equal(1, email.attachments.length)
assert_equal("invoice.pdf", email.attachments[0].filename)
assert_equal("This is test File content", email.attachments["invoice.pdf"].decoded)
end
test "attachment gets content type from filename" do
email = BaseMailer.attachment_with_content
assert_equal("invoice.pdf", email.attachments[0].filename)
assert_equal("application/pdf", email.attachments[0].mime_type)
end
test "attachment with hash" do
email = BaseMailer.attachment_with_hash
assert_equal(1, email.attachments.length)
assert_equal("invoice.jpg", email.attachments[0].filename)
expected = +"\312\213\254\232)b"
expected.force_encoding(Encoding::BINARY)
assert_equal expected, email.attachments["invoice.jpg"].decoded
end
test "attachment with hash using default mail encoding" do
email = BaseMailer.attachment_with_hash_default_encoding
assert_equal(1, email.attachments.length)
assert_equal("invoice.jpg", email.attachments[0].filename)
expected = +"\312\213\254\232)b"
expected.force_encoding(Encoding::BINARY)
assert_equal expected, email.attachments["invoice.jpg"].decoded
end
test "sets mime type to multipart/mixed when attachment is included" do
email = BaseMailer.attachment_with_content
assert_equal(1, email.attachments.length)
assert_equal("multipart/mixed", email.mime_type)
end
test "set mime type to text/html when attachment is included and body is set" do
email = BaseMailer.attachment_with_content(body: "Hello there", content_type: "text/html")
assert_equal("text/html", email.mime_type)
end
test "adds the rendered template as part" do
email = BaseMailer.attachment_with_content
assert_equal(2, email.parts.length)
assert_equal("multipart/mixed", email.mime_type)
assert_equal("text/html", email.parts[0].mime_type)
assert_equal("Attachment with content", email.parts[0].decoded)
assert_equal("application/pdf", email.parts[1].mime_type)
assert_equal("This is test File content", email.parts[1].decoded)
end
test "adds the given :body as part" do
email = BaseMailer.attachment_with_content(body: "I'm the eggman")
assert_equal(2, email.parts.length)
assert_equal("multipart/mixed", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("I'm the eggman", email.parts[0].decoded)
assert_equal("application/pdf", email.parts[1].mime_type)
assert_equal("This is test File content", email.parts[1].decoded)
end
test "can embed an inline attachment" do
email = BaseMailer.inline_attachment
# Need to call #encoded to force the JIT sort on parts
email.encoded
assert_equal(2, email.parts.length)
assert_equal("multipart/related", email.mime_type)
assert_equal("multipart/alternative", email.parts[0].mime_type)
assert_equal("text/plain", email.parts[0].parts[0].mime_type)
assert_equal("text/html", email.parts[0].parts[1].mime_type)
assert_equal("logo.png", email.parts[1].filename)
end
# Defaults values
test "uses default charset from class" do
with_default BaseMailer, charset: "US-ASCII" do
email = BaseMailer.welcome
assert_equal("US-ASCII", email.charset)
email = BaseMailer.welcome(charset: "iso-8559-1")
assert_equal("iso-8559-1", email.charset)
end
end
test "uses default content type from class" do
with_default BaseMailer, content_type: "text/html" do
email = BaseMailer.welcome
assert_equal("text/html", email.mime_type)
email = BaseMailer.welcome(content_type: "text/plain")
assert_equal("text/plain", email.mime_type)
end
end
test "uses default mime version from class" do
with_default BaseMailer, mime_version: "2.0" do
email = BaseMailer.welcome
assert_equal("2.0", email.mime_version)
email = BaseMailer.welcome(mime_version: "1.0")
assert_equal("1.0", email.mime_version)
end
end
test "uses random default headers from class" do
with_default BaseMailer, "X-Custom" => "Custom" do
email = BaseMailer.welcome
assert_equal("Custom", email["X-Custom"].decoded)
end
end
test "subject gets default from I18n" do
with_default BaseMailer, subject: nil do
email = BaseMailer.welcome(subject: nil)
assert_equal "Welcome", email.subject
with_translation "en", base_mailer: { welcome: { subject: "New Subject!" } } do
email = BaseMailer.welcome(subject: nil)
assert_equal "New Subject!", email.subject
end
end
end
test "default subject can have interpolations" do
with_translation "en", base_mailer: { with_subject_interpolations: { subject: "Will the real %{rapper_or_impersonator} please stand up?" } } do
email = BaseMailer.with_subject_interpolations
assert_equal "Will the real Slim Shady please stand up?", email.subject
end
end
test "translations are scoped properly" do
with_translation "en", base_mailer: { email_with_translations: { greet_user: "Hello %{name}!" } } do
email = BaseMailer.email_with_translations
assert_equal "Hello lifo!", email.body.encoded
end
end
test "adding attachments after mail was called raises exception" do
class LateAttachmentMailer < ActionMailer::Base
def welcome
mail body: "yay", from: "welcome@example.com", to: "to@example.com"
attachments["invoice.pdf"] = "This is test File content"
end
end
e = assert_raises(RuntimeError) { LateAttachmentMailer.welcome.message }
assert_match(/Can't add attachments after `mail` was called./, e.message)
end
test "adding inline attachments after mail was called raises exception" do
class LateInlineAttachmentMailer < ActionMailer::Base
def welcome
mail body: "yay", from: "welcome@example.com", to: "to@example.com"
attachments.inline["invoice.pdf"] = "This is test File content"
end
end
e = assert_raises(RuntimeError) { LateInlineAttachmentMailer.welcome.message }
assert_match(/Can't add attachments after `mail` was called./, e.message)
end
test "accessing inline attachments after mail was called works" do
class LateInlineAttachmentMailer < ActionMailer::Base
def welcome
mail body: "yay", from: "welcome@example.com", to: "to@example.com"
attachments.inline["invoice.pdf"]
end
end
assert_nothing_raised { LateInlineAttachmentMailer.welcome.message }
end
test "adding inline attachments while rendering mail works" do
class LateInlineAttachmentMailer < ActionMailer::Base
def on_render
mail from: "welcome@example.com", to: "to@example.com"
end
end
mail = LateInlineAttachmentMailer.on_render
assert_nothing_raised { mail.message }
assert_equal ["image/jpeg; filename=controller_attachments.jpg",
"image/jpeg; filename=attachments.jpg"], mail.attachments.inline.map { |a| a["Content-Type"].to_s }
end
test "accessing attachments works after mail was called" do
class LateAttachmentAccessorMailer < ActionMailer::Base
def welcome
attachments["invoice.pdf"] = "This is test File content"
mail body: "yay", from: "welcome@example.com", to: "to@example.com"
unless attachments.map(&:filename) == ["invoice.pdf"]
raise Minitest::Assertion, "Should allow access to attachments"
end
end
end
assert_nothing_raised { LateAttachmentAccessorMailer.welcome.message }
end
# Implicit multipart
test "implicit multipart" do
email = BaseMailer.implicit_multipart
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("TEXT Implicit Multipart", email.parts[0].body.encoded)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("HTML Implicit Multipart", email.parts[1].body.encoded)
end
test "implicit multipart formats" do
email = BaseMailer.implicit_multipart_formats
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("Implicit Multipart [:text]", email.parts[0].body.encoded)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("Implicit Multipart [:html]", email.parts[1].body.encoded)
end
test "implicit multipart with sort order" do
order = ["text/html", "text/plain"]
with_default BaseMailer, parts_order: order do
email = BaseMailer.implicit_multipart
assert_equal("text/html", email.parts[0].mime_type)
assert_equal("text/plain", email.parts[1].mime_type)
email = BaseMailer.implicit_multipart(parts_order: order.reverse)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("text/html", email.parts[1].mime_type)
end
end
test "implicit multipart with attachments creates nested parts" do
email = BaseMailer.implicit_multipart(attachments: true)
assert_equal(%w[ application/pdf multipart/alternative ], email.parts.map(&:mime_type).sort)
multipart = email.parts.detect { |p| p.mime_type == "multipart/alternative" }
assert_equal("text/plain", multipart.parts[0].mime_type)
assert_equal("TEXT Implicit Multipart", multipart.parts[0].body.encoded)
assert_equal("text/html", multipart.parts[1].mime_type)
assert_equal("HTML Implicit Multipart", multipart.parts[1].body.encoded)
end
test "implicit multipart with attachments and sort order" do
order = ["text/html", "text/plain"]
with_default BaseMailer, parts_order: order do
email = BaseMailer.implicit_multipart(attachments: true)
assert_equal(%w[ application/pdf multipart/alternative ], email.parts.map(&:mime_type).sort)
multipart = email.parts.detect { |p| p.mime_type == "multipart/alternative" }
assert_equal(%w[ text/html text/plain ], multipart.parts.map(&:mime_type).sort)
end
end
test "implicit multipart with default locale" do
email = BaseMailer.implicit_with_locale
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("Implicit with locale TEXT", email.parts[0].body.encoded)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("Implicit with locale EN HTML", email.parts[1].body.encoded)
end
test "implicit multipart with other locale" do
swap I18n, locale: :pl do
email = BaseMailer.implicit_with_locale
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("Implicit with locale PL TEXT", email.parts[0].body.encoded)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("Implicit with locale EN HTML", email.parts[1].body.encoded)
end
end
test "implicit multipart with fallback locale" do
fallback_backend = Class.new(I18n::Backend::Simple) do
include I18n::Backend::Fallbacks
end
begin
backend = I18n.backend
I18n.backend = fallback_backend.new
I18n.fallbacks[:"de-AT"] = [:de]
swap I18n, locale: "de-AT" do
email = BaseMailer.implicit_with_locale
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("Implicit with locale DE-AT TEXT", email.parts[0].body.encoded)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("Implicit with locale DE HTML", email.parts[1].body.encoded)
end
ensure
I18n.backend = backend
end
end
test "implicit multipart with several view paths uses the first one with template" do
old = BaseMailer.view_paths
begin
BaseMailer.view_paths = [File.join(FIXTURE_LOAD_PATH, "another.path")] + old.dup
email = BaseMailer.welcome
assert_equal("Welcome from another path", email.body.encoded)
ensure
BaseMailer.view_paths = old
end
end
test "implicit multipart with inexistent templates uses the next view path" do
old = BaseMailer.view_paths
begin
BaseMailer.view_paths = [File.join(FIXTURE_LOAD_PATH, "unknown")] + old.dup
email = BaseMailer.welcome
assert_equal("Welcome", email.body.encoded)
ensure
BaseMailer.view_paths = old
end
end
# Explicit multipart
test "explicit multipart" do
email = BaseMailer.explicit_multipart
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("TEXT Explicit Multipart", email.parts[0].body.encoded)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("HTML Explicit Multipart", email.parts[1].body.encoded)
end
test "explicit multipart have a boundary" do
mail = BaseMailer.explicit_multipart
assert_not_nil(mail.content_type_parameters[:boundary])
end
test "explicit multipart with attachments creates nested parts" do
email = BaseMailer.explicit_multipart(attachments: true)
assert_equal(%w[ application/pdf multipart/alternative ], email.parts.map(&:mime_type).sort)
multipart = email.parts.detect { |p| p.mime_type == "multipart/alternative" }
assert_equal("text/plain", multipart.parts[0].mime_type)
assert_equal("TEXT Explicit Multipart", multipart.parts[0].body.encoded)
assert_equal("text/html", multipart.parts[1].mime_type)
assert_equal("HTML Explicit Multipart", multipart.parts[1].body.encoded)
end
test "explicit multipart with templates" do
email = BaseMailer.explicit_multipart_templates
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("TEXT Explicit Multipart Templates", email.parts[0].body.encoded)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("HTML Explicit Multipart Templates", email.parts[1].body.encoded)
end
test "explicit multipart with format.any" do
email = BaseMailer.explicit_multipart_with_any
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("Format with any!", email.parts[0].body.encoded)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("Format with any!", email.parts[1].body.encoded)
end
test "explicit without specifying format with format.any" do
error = assert_raises(ArgumentError) do
BaseMailer.explicit_without_specifying_format_with_any.parts
end
assert_equal "You have to supply at least one format", error.message
end
test "explicit multipart with format(Hash)" do
email = BaseMailer.explicit_multipart_with_options(true)
email.ready_to_send!
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("base64", email.parts[0].content_transfer_encoding)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("7bit", email.parts[1].content_transfer_encoding)
end
test "explicit multipart with one part is rendered as body and options are merged" do
email = BaseMailer.explicit_multipart_with_options
assert_equal(0, email.parts.size)
assert_equal("text/plain", email.mime_type)
assert_equal("base64", email.content_transfer_encoding)
end
test "explicit multipart with one template has the expected format" do
email = BaseMailer.explicit_multipart_with_one_template
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("[:text]", email.parts[0].body.encoded)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("[:html]", email.parts[1].body.encoded)
end
test "explicit multipart with sort order" do
order = ["text/html", "text/plain"]
with_default BaseMailer, parts_order: order do
email = BaseMailer.explicit_multipart
assert_equal("text/html", email.parts[0].mime_type)
assert_equal("text/plain", email.parts[1].mime_type)
email = BaseMailer.explicit_multipart(parts_order: order.reverse)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("text/html", email.parts[1].mime_type)
end
end
# Class level API with method missing
test "should respond to action methods" do
assert_respond_to BaseMailer, :welcome
assert_respond_to BaseMailer, :implicit_multipart
assert_not_respond_to BaseMailer, :mail
assert_not_respond_to BaseMailer, :headers
end
test "calling just the action should return the generated mail object" do
email = BaseMailer.welcome
assert_equal(0, BaseMailer.deliveries.length)
assert_equal("The first email on new API!", email.subject)
end
test "calling deliver on the action should deliver the mail object" do
assert_called(BaseMailer, :deliver_mail) do
mail = BaseMailer.welcome.deliver_now
assert_equal "The first email on new API!", mail.subject
end
end
test "calling deliver on the action should increment the deliveries collection if using the test mailer" do
BaseMailer.welcome.deliver_now
assert_equal(1, BaseMailer.deliveries.length)
end
test "calling deliver, ActionMailer should yield back to mail to let it call :do_delivery on itself" do
mail = Mail::Message.new
assert_called(mail, :do_delivery) do
assert_called(BaseMailer, :welcome, returns: mail) do
BaseMailer.welcome.deliver
end
end
end
# Rendering
test "you can specify a different template for implicit render" do
mail = BaseMailer.implicit_different_template("implicit_multipart").deliver_now
assert_equal("HTML Implicit Multipart", mail.html_part.body.decoded)
assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded)
end
test "you can specify a different template for multipart render" do
mail = BaseMailer.implicit_different_template_with_block("explicit_multipart_templates").deliver
assert_equal("HTML Explicit Multipart Templates", mail.html_part.body.decoded)
assert_equal("TEXT Explicit Multipart Templates", mail.text_part.body.decoded)
end
test "should raise if missing template in implicit render" do
assert_raises ActionView::MissingTemplate do
BaseMailer.implicit_different_template("missing_template").deliver_now
end
assert_equal(0, BaseMailer.deliveries.length)
end
test "you can specify a different template for explicit render" do
mail = BaseMailer.explicit_different_template("explicit_multipart_templates").deliver_now
assert_equal("HTML Explicit Multipart Templates", mail.html_part.body.decoded)
assert_equal("TEXT Explicit Multipart Templates", mail.text_part.body.decoded)
end
test "you can specify a different layout" do
mail = BaseMailer.different_layout("different_layout").deliver_now
assert_equal("HTML -- HTML", mail.html_part.body.decoded)
assert_equal("PLAIN -- PLAIN", mail.text_part.body.decoded)
end
test "you can specify the template path for implicit lookup" do
mail = BaseMailer.welcome_from_another_path("another.path/base_mailer").deliver_now
assert_equal("Welcome from another path", mail.body.encoded)
mail = BaseMailer.welcome_from_another_path(["unknown/invalid", "another.path/base_mailer"]).deliver_now
assert_equal("Welcome from another path", mail.body.encoded)
end
test "assets tags should use ActionMailer's asset_host settings" do
ActionMailer::Base.config.asset_host = "http://global.com"
ActionMailer::Base.config.assets_dir = "global/"
mail = AssetMailer.welcome
assert_dom_equal(%{}, mail.body.to_s.strip)
end
test "assets tags should use a Mailer's asset_host settings when available" do
ActionMailer::Base.config.asset_host = "http://global.com"
ActionMailer::Base.config.assets_dir = "global/"
TempAssetMailer = Class.new(AssetMailer) do
self.mailer_name = "asset_mailer"
self.asset_host = "http://local.com"
end
mail = TempAssetMailer.welcome
assert_dom_equal(%{}, mail.body.to_s.strip)
end
test "the view is not rendered when mail was never called" do
mail = BaseMailer.without_mail_call
assert_equal("", mail.body.to_s.strip)
mail.deliver_now
end
test "the return value of mailer methods is not relevant" do
mail = BaseMailer.with_nil_as_return_value
assert_equal("Welcome", mail.body.to_s.strip)
mail.deliver_now
end
# Before and After hooks
class MyObserver
def self.delivered_email(mail)
end
end
class MySecondObserver
def self.delivered_email(mail)
end
end
test "you can register and unregister an observer to the mail object that gets informed on email delivery" do
mail_side_effects do
ActionMailer::Base.register_observer(MyObserver)
mail = BaseMailer.welcome
assert_called_with(MyObserver, :delivered_email, [mail]) do
mail.deliver_now
end
ActionMailer::Base.unregister_observer(MyObserver)
assert_not_called(MyObserver, :delivered_email, returns: mail) do
mail.deliver_now
end
end
end
test "you can register and unregister an observer using its stringified name to the mail object that gets informed on email delivery" do
mail_side_effects do
ActionMailer::Base.register_observer("BaseTest::MyObserver")
mail = BaseMailer.welcome
assert_called_with(MyObserver, :delivered_email, [mail]) do
mail.deliver_now
end
ActionMailer::Base.unregister_observer("BaseTest::MyObserver")
assert_not_called(MyObserver, :delivered_email, returns: mail) do
mail.deliver_now
end
end
end
test "you can register and unregister an observer using its symbolized underscored name to the mail object that gets informed on email delivery" do
mail_side_effects do
ActionMailer::Base.register_observer(:"base_test/my_observer")
mail = BaseMailer.welcome
assert_called_with(MyObserver, :delivered_email, [mail]) do
mail.deliver_now
end
ActionMailer::Base.unregister_observer(:"base_test/my_observer")
assert_not_called(MyObserver, :delivered_email, returns: mail) do
mail.deliver_now
end
end
end
test "you can register and unregister multiple observers to the mail object that both get informed on email delivery" do
mail_side_effects do
ActionMailer::Base.register_observers("BaseTest::MyObserver", MySecondObserver)
mail = BaseMailer.welcome
assert_called_with(MyObserver, :delivered_email, [mail]) do
assert_called_with(MySecondObserver, :delivered_email, [mail]) do
mail.deliver_now
end
end
ActionMailer::Base.unregister_observers("BaseTest::MyObserver", MySecondObserver)
assert_not_called(MyObserver, :delivered_email, returns: mail) do
mail.deliver_now
end
assert_not_called(MySecondObserver, :delivered_email, returns: mail) do
mail.deliver_now
end
end
end
class MyInterceptor
def self.delivering_email(mail); end
def self.previewing_email(mail); end
end
class MySecondInterceptor
def self.delivering_email(mail); end
def self.previewing_email(mail); end
end
test "you can register and unregister an interceptor to the mail object that gets passed the mail object before delivery" do
mail_side_effects do
ActionMailer::Base.register_interceptor(MyInterceptor)
mail = BaseMailer.welcome
assert_called_with(MyInterceptor, :delivering_email, [mail]) do
mail.deliver_now
end
ActionMailer::Base.unregister_interceptor(MyInterceptor)
assert_not_called(MyInterceptor, :delivering_email, returns: mail) do
mail.deliver_now
end
end
end
test "you can register and unregister an interceptor using its stringified name to the mail object that gets passed the mail object before delivery" do
mail_side_effects do
ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor")
mail = BaseMailer.welcome
assert_called_with(MyInterceptor, :delivering_email, [mail]) do
mail.deliver_now
end
ActionMailer::Base.unregister_interceptor("BaseTest::MyInterceptor")
assert_not_called(MyInterceptor, :delivering_email, returns: mail) do
mail.deliver_now
end
end
end
test "you can register and unregister an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before delivery" do
mail_side_effects do
ActionMailer::Base.register_interceptor(:"base_test/my_interceptor")
mail = BaseMailer.welcome
assert_called_with(MyInterceptor, :delivering_email, [mail]) do
mail.deliver_now
end
ActionMailer::Base.unregister_interceptor(:"base_test/my_interceptor")
assert_not_called(MyInterceptor, :delivering_email, returns: mail) do
mail.deliver_now
end
end
end
test "you can register and unregister multiple interceptors to the mail object that both get passed the mail object before delivery" do
mail_side_effects do
ActionMailer::Base.register_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
mail = BaseMailer.welcome
assert_called_with(MyInterceptor, :delivering_email, [mail]) do
assert_called_with(MySecondInterceptor, :delivering_email, [mail]) do
mail.deliver_now
end
end
ActionMailer::Base.unregister_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
assert_not_called(MyInterceptor, :delivering_email, returns: mail) do
mail.deliver_now
end
assert_not_called(MySecondInterceptor, :delivering_email, returns: mail) do
mail.deliver_now
end
end
end
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
mail1 = ProcMailer.welcome["X-Proc-Method"]
yesterday = 1.day.ago
Time.stub(:now, yesterday) do
mail2 = ProcMailer.welcome["X-Proc-Method"]
assert(mail1.to_s.to_i > mail2.to_s.to_i)
end
end
test "default values which have to_proc (e.g. symbols) should not be considered procs" do
assert(ProcMailer.welcome["x-has-to-proc"].to_s == "symbol")
end
test "proc default values can have arity of 1 where arg is a mailer instance" do
assert_equal(ProcMailer.welcome["X-Lambda-Arity-1-arg"].to_s, "complex_value")
assert_equal(ProcMailer.welcome["X-Lambda-Arity-1-self"].to_s, "complex_value")
end
test "proc default values with fixed arity of 0 can be called" do
assert_equal("0", ProcMailer.welcome["X-Lambda-Arity-0"].to_s)
end
test "we can call other defined methods on the class as needed" do
mail = ProcMailer.welcome
assert_equal("Thanks for signing up this afternoon", mail.subject)
end
test "modifying the mail message with a before_action" do
class BeforeActionMailer < ActionMailer::Base
before_action :add_special_header!
def welcome ; mail ; end
private
def add_special_header!
headers("X-Special-Header" => "Wow, so special")
end
end
assert_equal("Wow, so special", BeforeActionMailer.welcome["X-Special-Header"].to_s)
end
test "modifying the mail message with an after_action" do
class AfterActionMailer < ActionMailer::Base
after_action :add_special_header!
def welcome ; mail ; end
private
def add_special_header!
headers("X-Special-Header" => "Testing")
end
end
assert_equal("Testing", AfterActionMailer.welcome["X-Special-Header"].to_s)
end
test "adding an inline attachment using a before_action" do
class DefaultInlineAttachmentMailer < ActionMailer::Base
before_action :add_inline_attachment!
def welcome ; mail ; end
private
def add_inline_attachment!
attachments.inline["footer.jpg"] = "hey there"
end
end
mail = DefaultInlineAttachmentMailer.welcome
assert_equal("image/jpeg; filename=footer.jpg", mail.attachments.inline.first["Content-Type"].to_s)
end
test "action methods should be refreshed after defining new method" do
class FooMailer < ActionMailer::Base
# This triggers action_methods.
respond_to?(:foo)
def notify
end
end
assert_equal Set.new(["notify"]), FooMailer.action_methods
end
test "mailer can be anonymous" do
mailer = Class.new(ActionMailer::Base) do
def welcome
mail
end
end
assert_equal "anonymous", mailer.mailer_name
assert_equal "Welcome", mailer.welcome.subject
assert_equal "Anonymous mailer body", mailer.welcome.body.encoded.strip
end
test "default_from can be set" do
class DefaultFromMailer < ActionMailer::Base
default to: "system@test.lindsaar.net"
self.default_options = { from: "robert.pankowecki@gmail.com" }
def welcome
mail(subject: "subject", body: "hello world")
end
end
assert_equal ["robert.pankowecki@gmail.com"], DefaultFromMailer.welcome.from
end
test "mail() without arguments serves as getter for the current mail message" do
class MailerWithCallback < ActionMailer::Base
after_action :a_callback
def welcome
headers("X-Special-Header" => "special indeed!")
mail subject: "subject", body: "hello world", to: ["joe@example.com"]
end
def a_callback
mail.to << "jane@example.com"
end
end
mail = MailerWithCallback.welcome
assert_equal "subject", mail.subject
assert_equal ["joe@example.com", "jane@example.com"], mail.to
assert_equal "hello world", mail.body.encoded.strip
assert_equal "special indeed!", mail["X-Special-Header"].to_s
end
test "notification for process" do
events = []
ActiveSupport::Notifications.subscribe("process.action_mailer") do |*args|
events << ActiveSupport::Notifications::Event.new(*args)
end
BaseMailer.welcome(body: "Hello there").deliver_now
assert_equal 1, events.length
assert_equal "process.action_mailer", events[0].name
assert_equal "BaseMailer", events[0].payload[:mailer]
assert_equal :welcome, events[0].payload[:action]
assert_equal [{ body: "Hello there" }], events[0].payload[:args]
ensure
ActiveSupport::Notifications.unsubscribe "process.action_mailer"
end
test "notification for deliver" do
events = []
ActiveSupport::Notifications.subscribe("deliver.action_mailer") do |*args|
events << ActiveSupport::Notifications::Event.new(*args)
end
BaseMailer.welcome(body: "Hello there").deliver_now
assert_equal 1, events.length
assert_equal "deliver.action_mailer", events[0].name
assert_not_nil events[0].payload[:message_id]
ensure
ActiveSupport::Notifications.unsubscribe "deliver.action_mailer"
end
private
# Execute the block setting the given values and restoring old values after
# the block is executed.
def swap(klass, new_values)
old_values = {}
new_values.each do |key, value|
old_values[key] = klass.send key
klass.send :"#{key}=", value
end
yield
ensure
old_values.each do |key, value|
klass.send :"#{key}=", value
end
end
def with_default(klass, new_values)
old = klass.default_params
klass.default(new_values)
yield
ensure
klass.default_params = old
end
def mail_side_effects
old_observers = Mail.class_variable_get(:@@delivery_notification_observers)
old_delivery_interceptors = Mail.class_variable_get(:@@delivery_interceptors)
yield
ensure
Mail.class_variable_set(:@@delivery_notification_observers, old_observers)
Mail.class_variable_set(:@@delivery_interceptors, old_delivery_interceptors)
end
def with_translation(locale, data)
I18n.backend.store_translations(locale, data)
yield
ensure
I18n.backend.reload!
end
end
class BasePreviewInterceptorsTest < ActiveSupport::TestCase
teardown do
ActionMailer::Base.preview_interceptors.clear
end
class BaseMailerPreview < ActionMailer::Preview
def welcome
BaseMailer.welcome
end
end
class MyInterceptor
def self.delivering_email(mail); end
def self.previewing_email(mail); end
end
class MySecondInterceptor
def self.delivering_email(mail); end
def self.previewing_email(mail); end
end
test "you can register and unregister a preview interceptor to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor(MyInterceptor)
mail = BaseMailer.welcome
stub_any_instance(BaseMailerPreview) do |instance|
instance.stub(:welcome, mail) do
assert_called_with(MyInterceptor, :previewing_email, [mail]) do
BaseMailerPreview.call(:welcome)
end
end
end
ActionMailer::Base.unregister_preview_interceptor(MyInterceptor)
assert_not_called(MyInterceptor, :previewing_email, returns: mail) do
BaseMailerPreview.call(:welcome)
end
end
test "you can register and unregister a preview interceptor using its stringified name to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor")
mail = BaseMailer.welcome
stub_any_instance(BaseMailerPreview) do |instance|
instance.stub(:welcome, mail) do
assert_called_with(MyInterceptor, :previewing_email, [mail]) do
BaseMailerPreview.call(:welcome)
end
end
end
ActionMailer::Base.unregister_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor")
assert_not_called(MyInterceptor, :previewing_email, returns: mail) do
BaseMailerPreview.call(:welcome)
end
end
test "you can register and unregister a preview interceptor using its symbolized underscored name to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor(:"base_preview_interceptors_test/my_interceptor")
mail = BaseMailer.welcome
stub_any_instance(BaseMailerPreview) do |instance|
instance.stub(:welcome, mail) do
assert_called_with(MyInterceptor, :previewing_email, [mail]) do
BaseMailerPreview.call(:welcome)
end
end
end
ActionMailer::Base.unregister_preview_interceptor(:"base_preview_interceptors_test/my_interceptor")
assert_not_called(MyInterceptor, :previewing_email, returns: mail) do
BaseMailerPreview.call(:welcome)
end
end
test "you can register and unregister multiple preview interceptors to the mail object that both get passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptors("BasePreviewInterceptorsTest::MyInterceptor", MySecondInterceptor)
mail = BaseMailer.welcome
stub_any_instance(BaseMailerPreview) do |instance|
instance.stub(:welcome, mail) do
assert_called_with(MyInterceptor, :previewing_email, [mail]) do
assert_called_with(MySecondInterceptor, :previewing_email, [mail]) do
BaseMailerPreview.call(:welcome)
end
end
end
end
ActionMailer::Base.unregister_preview_interceptors("BasePreviewInterceptorsTest::MyInterceptor", MySecondInterceptor)
assert_not_called(MyInterceptor, :previewing_email, returns: mail) do
BaseMailerPreview.call(:welcome)
end
assert_not_called(MySecondInterceptor, :previewing_email, returns: mail) do
BaseMailerPreview.call(:welcome)
end
end
end
class BasePreviewTest < ActiveSupport::TestCase
class BaseMailerPreview < ActionMailer::Preview
def welcome
BaseMailer.welcome(params)
end
end
test "has access to params" do
params = { name: "World" }
message = BaseMailerPreview.call(:welcome, params)
assert_equal "World", message["name"].decoded
end
end