diff --git a/test/test_mailer_message.rb b/test/test_mailer_message.rb new file mode 100644 index 0000000..a790f9c --- /dev/null +++ b/test/test_mailer_message.rb @@ -0,0 +1,99 @@ +require 'minitest/autorun' +require 'prism' + +class MailerMessageTest < Minitest::Test + def setup + @message = Prism::Mailer::Message.new + @message.from = 'sender@example.com' + @message.to = 'receiver@example.com' + @message.subject = 'Test subject' + end + + def test_requires_a_from_address + @message.from = nil + @message.text_body = 'body' + assert_raises(ArgumentError) { @message.to_s } + end + + def test_requires_at_least_one_recipient + @message.to = [] + @message.text_body = 'body' + assert_raises(ArgumentError) { @message.to_s } + end + + def test_requires_a_body + assert_raises(ArgumentError) { @message.to_s } + end + + def test_to_cc_bcc_coerce_a_single_address_into_an_array + @message.to = 'a@example.com' + @message.cc = 'b@example.com' + @message.bcc = 'c@example.com' + assert_equal ['a@example.com'], @message.to + assert_equal %w[a@example.com b@example.com c@example.com], @message.all_recipients + end + + def test_plain_text_only_message_has_a_single_text_content_type + @message.text_body = 'hello world' + raw = @message.to_s + assert_includes raw, "Content-Type: text/plain; charset=UTF-8\r\n\r\nhello world" + refute_includes raw, 'multipart' + end + + def test_html_only_message_has_a_single_html_content_type + @message.html_body = '
hello
' + raw = @message.to_s + assert_includes raw, "Content-Type: text/html; charset=UTF-8\r\n\r\nhello
" + refute_includes raw, 'multipart' + end + + def test_text_and_html_together_build_a_multipart_alternative_part + @message.text_body = 'plain body' + @message.html_body = 'html body
' + raw = @message.to_s + assert_match(/Content-Type: multipart\/alternative; boundary="([^"]+)"/, raw) + boundary = raw[/boundary="([^"]+)"/, 1] + assert_includes raw, "--#{boundary}\r\nContent-Type: text/plain" + assert_includes raw, "--#{boundary}\r\nContent-Type: text/html" + assert_includes raw, "--#{boundary}--" + end + + def test_attachment_wraps_the_body_in_multipart_mixed_and_base64_encodes_content + @message.text_body = 'see attached' + @message.add_attachment('data.bin', "\xFF\x00\xFE binary".b, mime_type: 'application/octet-stream') + raw = @message.to_s + assert_match(/Content-Type: multipart\/mixed; boundary="([^"]+)"/, raw) + assert_includes raw, 'Content-Disposition: attachment; filename="data.bin"' + assert_includes raw, 'Content-Transfer-Encoding: base64' + encoded = raw[/Content-Disposition: attachment; filename="data\.bin"\r\n\r\n(.+?)\r\n--/m, 1] + assert_equal "\xFF\x00\xFE binary".b, encoded.unpack1('m0') + end + + def test_headers_writer_adds_a_custom_header_line + @message.text_body = 'body' + @message['X-Custom'] = 'yes' + assert_includes @message.to_s, "X-Custom: yes\r\n" + end + + def test_reply_to_and_cc_appear_in_headers + @message.text_body = 'body' + @message.cc = 'cc@example.com' + @message.reply_to = 'reply@example.com' + raw = @message.to_s + assert_includes raw, "Cc: cc@example.com\r\n" + assert_includes raw, "Reply-To: reply@example.com\r\n" + end + + def test_non_ascii_subject_is_mime_encoded + @message.subject = "Zaproszenie na przyj\u0119cie" + @message.text_body = 'body' + subject_line = @message.to_s.lines.find { |l| l.start_with?('Subject:') } + assert_match(/Subject: =\?UTF-8\?B\?.+\?=/, subject_line) + end + + def test_ascii_subject_is_left_untouched + @message.text_body = 'body' + subject_line = @message.to_s.lines.find { |l| l.start_with?('Subject:') } + assert_equal 'Subject: Test subject', subject_line.chomp("\n") + end +end diff --git a/test/test_mailer_preview.rb b/test/test_mailer_preview.rb new file mode 100644 index 0000000..7c37667 --- /dev/null +++ b/test/test_mailer_preview.rb @@ -0,0 +1,70 @@ +require 'minitest/autorun' +require 'prism' +require 'tmpdir' +require 'fileutils' + +class MailerPreviewTest < Minitest::Test + def setup + @dir = Dir.mktmpdir('prism-mailer-preview') + FileUtils.mkdir_p(File.join(@dir, 'preview_user')) + File.write(File.join(@dir, 'preview_user/welcome.text.erb'), "Hi <%= name %>\n") + + mailer_dir = @dir + @mailer_class = Class.new(Prism::Mailer::Base) do + self.template_roots = [mailer_dir] + + def welcome(name) + @name = name + mail(to: 'someone@example.com', subject: 'Welcome!') + end + end + Object.const_set(:PreviewUserMailer, @mailer_class) + + mailer_class = @mailer_class + @preview_class = Class.new(Prism::Mailer::Preview) do + define_method(:welcome) { mailer_class.welcome('Fixture Fred') } + end + Object.const_set(:UserMailerPreview, @preview_class) + end + + def teardown + Prism::Mailer::Preview.registry.delete(@preview_class) + %i[UserMailerPreview PreviewUserMailer].each do |name| + Object.send(:remove_const, name) if Object.const_defined?(name) + end + FileUtils.remove_entry(@dir) + end + + def test_subclassing_registers_the_preview + assert_includes Prism::Mailer::Preview.registry, UserMailerPreview + end + + def test_preview_name_strips_the_preview_suffix_and_underscores + assert_equal 'user_mailer', UserMailerPreview.preview_name + end + + def test_emails_lists_the_defined_preview_actions + assert_equal ['welcome'], UserMailerPreview.emails + end + + def test_call_renders_the_action_and_returns_its_delivery + delivery = UserMailerPreview.call(:welcome) + assert_equal "Hi Fixture Fred\n", delivery.message.text_body + end + + def test_call_never_delivers_the_message + Prism::Mailer.deliveries.clear + UserMailerPreview.call(:welcome) + assert_empty Prism::Mailer.deliveries + end + + def test_call_raises_for_an_unknown_email + error = assert_raises(ArgumentError) { UserMailerPreview.call(:does_not_exist) } + assert_match(/unknown preview/, error.message) + end + + def test_find_looks_up_a_registered_preview_by_name + assert_equal UserMailerPreview, Prism::Mailer::Preview.find('user_mailer') + assert_nil Prism::Mailer::Preview.find('nonexistent') + end +end