# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
require 'rails_helper'
RSpec.describe Channel::EmailBuild, type: :model do
describe '#build' do
let(:html_body) do
<<~MSG_HTML.chomp
> Welcome!
>
> Thank you for installing Zammad. äöüß
>
MSG_HTML
end
let(:plain_text_body) do
<<~MSG_TEXT.chomp
> Welcome!
>
> Thank you for installing Zammad. äöüß
>
MSG_TEXT
end
let(:parser) { Channel::EmailParser.new }
let(:parsed_data) { parser.parse(mail.to_s) }
let(:html_part_attachment) do
parsed_data[:attachments]
.find { |attachment| attachment[:filename] == 'message.html' }
end
let(:file_attachment) do
parsed_data[:attachments]
.find { |attachment| attachment[:filename] == filename }
end
shared_examples 'adding the email html part as an attachment' do
it 'adds the html part as an attachment' do
expect(html_part_attachment).to be_a Hash
end
it 'adds the html part as an attachment' do
expect(html_part_attachment).to include(
'filename' => 'message.html',
'preferences' => include('content-alternative' => true, 'Charset' => 'UTF-8',
'Mime-Type' => 'text/html', 'original-format' => true)
)
end
it 'does not include content-id property in attachment preferences' do
expect(html_part_attachment).not_to include(
'preferences' => include('Content-ID')
)
end
end
shared_examples 'adding a text file as an attachment' do
it 'adds the text file as an attachment' do
expect(file_attachment).to include(
'filename' => filename,
'preferences' => include('Charset' => 'UTF-8', 'Mime-Type' => mime_type,
'Content-Type' => "text/plain; charset=UTF-8; filename=#{filename}")
)
end
it 'does not include content* properties in attachment preferences' do
expect(file_attachment).not_to include(
'preferences' => include('Content-ID', 'content-alternative')
)
end
end
shared_examples 'adding a file as an attachment' do |file_type|
it "adds a #{file_type} as an attachment'" do
expect(file_attachment).to include(
'data' => content, 'filename' => filename,
'preferences' => include('Charset' => 'UTF-8', 'Mime-Type' => mime_type,
'Content-Type' => preferences_content_type)
)
end
it 'does not include content* properties in attachment preferences' do
expect(file_attachment).not_to include(
'preferences' => include('content-alternative', 'Content-ID')
)
end
end
shared_examples 'not adding email content as attachment' do
it 'does not add email content as an attachment' do
expect(html_part_attachment).to be_nil
end
end
context 'with email only' do
let(:mail) do
described_class.build(
from: 'sender@example.com',
to: 'recipient@example.com',
body: mail_body,
content_type: content_type
)
end
let(:expected_text) do
<<~MSG_TEXT.chomp
> Welcome!\r
>\r
> Thank you for installing Zammad. äöüß\r
>\r
MSG_TEXT
end
context 'when email contains only html' do
let(:mail_body) { html_body }
let(:content_type) { 'text/html' }
let(:expected_html) do
<<~MSG_HTML.chomp
\r
\r
\r
\r
\r
\r
> Welcome!
>
> Thank you for installing Zammad. äöüß
>
\r
\r
MSG_HTML
end
let(:expected_body) { '> Welcome!
>
> Thank you for installing Zammad. äöüß
>
' }
it 'builds a mail with a text part' do
expect(mail.text_part.body.to_s).to eq expected_text
end
it 'builds a mail with a html part' do
expect(mail.html_part.body.to_s).to eq expected_html
end
it 'builds a mail that is parsed correctly' do
expect(parsed_data).to include(body: expected_body, content_type: 'text/html')
end
it_behaves_like 'adding the email html part as an attachment'
end
context 'when email contains only plain text' do
let(:mail_body) { plain_text_body }
let(:content_type) { 'text/plain' }
it 'builds a mail with a text part' do
expect(mail.body.to_s).to eq expected_text
end
it 'does not build a html part' do
expect(mail.html_part).to be_nil
end
it 'builds a mail that is parsed correctly' do
expect(parsed_data).to include(body: plain_text_body, content_type: 'text/plain')
end
it 'does not have an attachment' do
expect(parsed_data[:attachments].first).to be_nil
end
it_behaves_like 'not adding email content as attachment'
end
end
context 'with email and attachment' do
let(:mail) do
described_class.build(
from: 'sender@example.com',
to: 'recipient@example.com',
body: mail_body,
content_type: content_type,
attachments: attachments
)
end
let(:filename) { 'somename.txt' }
let(:mime_type) { 'text/plain' }
let(:content) { 'Some text' }
let(:direct_attachment) do
[{
'Mime-Type' => mime_type,
:content => content,
:filename => filename
}]
end
let(:ticket) { create(:ticket, title: 'some article text attachment test', group: group) }
let(:group) { Group.lookup(name: 'Users') }
let(:article) do
create(:ticket_article,
ticket: ticket,
body: 'some message article helper test1 asdasd
')
end
let(:store_attributes) do
{
object: 'Ticket::Article',
o_id: article.id,
data: content,
filename: filename,
preferences: {
'Mime-Type' => mime_type
}
}
end
let(:store) { create(:store, **store_attributes) }
shared_context 'with attachment checks' do
context 'when attachment is a text file' do
it_behaves_like 'adding a text file as an attachment'
end
context 'when attachment is a image file' do
let(:filename) { 'somename.png' }
let(:mime_type) { 'image/png' }
let(:preferences_content_type) { "#{mime_type}; filename=#{filename}" }
let(:content) { 'xxxxxxx' }
it_behaves_like 'adding a file as an attachment', 'image'
end
context 'when attachment is a calendar file' do
let(:filename) { 'schedule.ics' }
let(:mime_type) { 'text/calendar' }
let(:preferences_content_type) { "#{mime_type}; charset=UTF-8; filename=#{filename}" }
let(:content) { 'xxxxxxx' }
it_behaves_like 'adding a file as an attachment', 'calendar'
end
end
context 'with html email' do
let(:mail_body) { html_body }
let(:content_type) { 'text/html' }
context 'with direct attachment' do
let(:attachments) { direct_attachment }
it 'has two attachments' do
expect(parsed_data[:attachments].size).to eq 2
end
it_behaves_like 'adding the email html part as an attachment'
include_context 'with attachment checks'
end
context 'with attachement from store' do
let(:attachments) { [ store ] }
let(:filename) { 'text_file.txt' }
let(:mime_type) { 'text/plain' }
it 'has two attachments' do
expect(parsed_data[:attachments].size).to eq 2
end
it_behaves_like 'adding the email html part as an attachment'
include_context 'with attachment checks'
end
end
context 'with plain text email' do
let(:mail_body) { plain_text_body }
let(:content_type) { 'text/plain' }
context 'with direct attachment' do
let(:attachments) { direct_attachment }
let(:filename) { 'somename.txt' }
let(:mime_type) { 'text/plain' }
it 'has only one attachment' do
expect(parsed_data[:attachments].size).to eq 1
end
it_behaves_like 'not adding email content as attachment'
include_context 'with attachment checks'
end
context 'with attachement from store' do
let(:attachments) { [ store ] }
let(:filename) { 'text_file.txt' }
let(:mime_type) { 'text/plain' }
let(:mail_body) do
<<~MSG_TEXT.chomp
> Welcome!
>
> Email Content
MSG_TEXT
end
let(:content) { 'Text Content' }
it 'has only one attachment' do
expect(parsed_data[:attachments].size).to eq 1
end
# #2362 - Attached text files get prepended on e-mail reply instead of appended
it 'Email Content should appear before the Text Content within the raw email' do
expect(mail.to_s).to match(%r{Email Content[\s\S]*Text Content})
end
it_behaves_like 'not adding email content as attachment'
include_context 'with attachment checks'
end
end
end
end
describe '#recipient_line' do
let(:email) { 'some.body@example.com' }
let(:generated_recipient_line) { described_class.recipient_line(realname, email) }
context 'with quote in the realname' do
let(:realname) { 'Somebody @ "Company"' }
it 'escapes the quotes' do
expected_recipient_line = '"Somebody @ \"Company\""
'
expect(generated_recipient_line).to eq expected_recipient_line
end
end
context 'with a simple realname with no special characters' do
let(:realname) { 'Somebody' }
it 'wraps the realname with quotes and wraps the email with <>' do
expected_recipient_line = 'Somebody '
expect(generated_recipient_line).to eq expected_recipient_line
end
end
context 'with special characters (|) in the realname' do
let(:realname) { 'Somebody | Some Org' }
it 'wraps the email with <>' do
expected_recipient_line = 'Somebody | Some Org '
expect(generated_recipient_line).to eq expected_recipient_line
end
end
context 'with special characters (spaces) in the realname' do
let(:realname) { 'Test Admin Agent via Support' }
it 'wraps the email with <>' do
expected_recipient_line = 'Test Admin Agent via Support '
expect(generated_recipient_line).to eq expected_recipient_line
end
end
end
# https://github.com/zammad/zammad/issues/165
describe '#html_mail_client_fixes' do
let(:generated_html) { described_class.html_mail_client_fixes(html) }
shared_examples 'adding styles to the element' do
it 'adds style to the element' do
expect(generated_html).to eq expected_html
end
it { expect(generated_html).not_to eq html }
end
context 'when html element is a blockquote' do
let(:html) do
<<~HTML.chomp
some
text
123
some
text
HTML
end
let(:expected_html) do
<<~HTML.chomp
some
text
123
some
text
HTML
end
it_behaves_like 'adding styles to the element'
end
context 'when html element is a p' do
let(:html) do
<<~HTML.chomp
some
text
123
HTML
end
let(:expected_html) do
<<~HTML.chomp
some
text
123
HTML
end
it_behaves_like 'adding styles to the element'
end
context 'when html element is a hr' do
let(:html) do
<<~HTML.chomp
sometext
123
HTML
end
let(:expected_html) do
<<~HTML.chomp
sometext
123
HTML
end
it_behaves_like 'adding styles to the element'
context 'when hr is a closing tag' do
let(:html) do
<<~HTML.chomp
sometext
HTML
end
let(:expected_html) do
<<~HTML.chomp
sometext
HTML
end
it_behaves_like 'adding styles to the element'
end
end
context 'when html element does not contian p, hr or blockquote' do
let(:html) do
<<~HTML.chomp
HTML
end
it 'does not add style to the element' do
expect(generated_html).to eq html
end
end
end
describe '#html_complete_check' do
let(:generated_html) { described_class.html_complete_check(html) }
context 'when html element includes an html tag' do
let(:html) { 'test' }
it 'returns the html as it is' do
expect(generated_html).to eq html
end
end
context 'when html does not include an html tag' do
let(:html) { 'test' }
it 'adds DOCTYPE tag to the element' do
expect(generated_html).to start_with ''
end
it 'adds the original element' do
expect(generated_html).to match html
end
end
# Issue #1230, missing backslashes
# 'Test URL: \\storage\project\100242-Inc'
context 'when html includes a backslash' do
let(:html) { 'Test URL: \\\\storage\\project\\100242-Inc' }
it 'keeps the backslashes' do
expect(generated_html).to include html
end
end
context 'with a configured html_email_css_font setting' do
let(:html) { 'test' }
let(:css_font) { "font-family:'Helvetica Neue', sans-serif; font-size: 12px;" }
before { Setting.set('html_email_css_font', css_font) }
it 'includes the configured css font' do
expect(generated_html).to include("#{css_font}\n")
end
end
end
end