email_build_test.rb 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. require 'test_helper'
  2. class EmailBuildTest < ActiveSupport::TestCase
  3. test 'document complete check' do
  4. html = '<b>test</b>'
  5. result = Channel::EmailBuild.html_complete_check(html)
  6. assert(result.start_with?('<!DOCTYPE'), 'test 1')
  7. assert(result !~ %r{^.+?<!DOCTYPE}, 'test 1')
  8. assert(result.include?('<html>'), 'test 1')
  9. assert(result.include?('font-family'), 'test 1')
  10. assert(result.include?('<b>test</b>'), 'test 1')
  11. html = 'invalid <!DOCTYPE html><html><b>test</b></html>'
  12. result = Channel::EmailBuild.html_complete_check(html)
  13. assert(result !~ %r{^<!DOCTYPE}, 'test 2')
  14. assert(result =~ %r{^.+?<!DOCTYPE}, 'test 2')
  15. assert(result.include?('<html>'), 'test 2')
  16. assert(result !~ %r{font-family}, 'test 2')
  17. assert(result.include?('<b>test</b>'), 'test 2')
  18. # Issue #1230, missing backslashes
  19. # 'Test URL: \\storage\project\100242-Inc'
  20. html = '<b>Test URL</b>: \\\\storage\\project\\100242-Inc'
  21. result = Channel::EmailBuild.html_complete_check(html)
  22. assert(result.include?(html), 'backslashes must be kept')
  23. end
  24. test 'html email + attachment check' do
  25. html = <<~MSG_HTML.chomp
  26. <!DOCTYPE html>
  27. <html>
  28. <head>
  29. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  30. </head>
  31. <body style="font-family:Geneva,Helvetica,Arial,sans-serif; font-size: 12px;">
  32. <div>&gt; Welcome!</div><div>&gt;</div><div>&gt; Thank you for installing Zammad. äöüß</div><div>&gt;</div>
  33. </body>
  34. </html>
  35. MSG_HTML
  36. mail = Channel::EmailBuild.build(
  37. from: 'sender@example.com',
  38. to: 'recipient@example.com',
  39. body: html,
  40. content_type: 'text/html',
  41. attachments: [
  42. {
  43. 'Mime-Type' => 'image/png',
  44. :content => 'xxx',
  45. :filename => 'somename.png'
  46. }
  47. ],
  48. )
  49. text_should = <<~MSG_TEXT.chomp
  50. > Welcome!\r
  51. >\r
  52. > Thank you for installing Zammad. äöüß\r
  53. >\r
  54. MSG_TEXT
  55. assert_equal(text_should, mail.text_part.body.to_s)
  56. html_should = <<~MSG_HTML.chomp
  57. <!DOCTYPE html>\r
  58. <html>\r
  59. <head>\r
  60. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>\r
  61. </head>\r
  62. <body style="font-family:Geneva,Helvetica,Arial,sans-serif; font-size: 12px;">\r
  63. <div>&gt; Welcome!</div><div>&gt;</div><div>&gt; Thank you for installing Zammad. äöüß</div><div>&gt;</div>\r
  64. </body>\r
  65. </html>
  66. MSG_HTML
  67. assert_equal(html_should, mail.html_part.body.to_s)
  68. parser = Channel::EmailParser.new
  69. data = parser.parse(mail.to_s)
  70. # check body
  71. should = '<div>&gt; Welcome!</div><div>&gt;</div><div>&gt; Thank you for installing Zammad. äöüß</div><div>&gt;</div>'
  72. assert_equal(should, data[:body])
  73. assert_equal('text/html', data[:content_type])
  74. # check count of attachments, only 2, because 3 part is text message and is already in body
  75. assert_equal(2, data[:attachments].length)
  76. # check attachments
  77. data[:attachments]&.each do |attachment|
  78. case attachment[:filename]
  79. when 'message.html'
  80. assert_nil(attachment[:preferences]['Content-ID'])
  81. assert_equal(true, attachment[:preferences]['content-alternative'])
  82. assert_equal('text/html', attachment[:preferences]['Mime-Type'])
  83. assert_equal('UTF-8', attachment[:preferences]['Charset'])
  84. when 'somename.png'
  85. assert_nil(attachment[:preferences]['Content-ID'])
  86. assert_nil(attachment[:preferences]['content-alternative'])
  87. assert_equal('image/png', attachment[:preferences]['Mime-Type'])
  88. assert_equal('UTF-8', attachment[:preferences]['Charset'])
  89. else
  90. assert(false, "invalid attachment, should not be there, #{attachment.inspect}")
  91. end
  92. end
  93. end
  94. test 'plain email + attachment check' do
  95. text = <<~MSG_TEXT.chomp
  96. > Welcome!
  97. >
  98. > Thank you for installing Zammad. äöüß
  99. >
  100. MSG_TEXT
  101. mail = Channel::EmailBuild.build(
  102. from: 'sender@example.com',
  103. to: 'recipient@example.com',
  104. body: text,
  105. attachments: [
  106. {
  107. 'Mime-Type' => 'image/png',
  108. :content => 'xxx',
  109. :filename => 'somename.png'
  110. }
  111. ],
  112. )
  113. text_should = <<~MSG_TEXT.chomp
  114. > Welcome!\r
  115. >\r
  116. > Thank you for installing Zammad. äöüß\r
  117. >\r
  118. MSG_TEXT
  119. assert_equal(text_should, mail.text_part.body.to_s)
  120. assert_nil(mail.html_part)
  121. assert_equal('image/png; filename=somename.png', mail.attachments[0].content_type)
  122. parser = Channel::EmailParser.new
  123. data = parser.parse(mail.to_s)
  124. # check body
  125. assert_equal(text, data[:body])
  126. # check count of attachments, 2
  127. assert_equal(1, data[:attachments].length)
  128. # check attachments
  129. data[:attachments]&.each do |attachment|
  130. if attachment[:filename] == 'somename.png'
  131. assert_nil(attachment[:preferences]['Content-ID'])
  132. assert_nil(attachment[:preferences]['content-alternative'])
  133. assert_equal('image/png', attachment[:preferences]['Mime-Type'])
  134. assert_equal('UTF-8', attachment[:preferences]['Charset'])
  135. else
  136. assert(false, "invalid attachment, should not be there, #{attachment.inspect}")
  137. end
  138. end
  139. end
  140. test 'plain email + attachment check 2' do
  141. ticket1 = Ticket.create!(
  142. title: 'some article helper test1',
  143. group: Group.lookup(name: 'Users'),
  144. customer_id: 2,
  145. state: Ticket::State.lookup(name: 'new'),
  146. priority: Ticket::Priority.lookup(name: '2 normal'),
  147. updated_by_id: 1,
  148. created_by_id: 1,
  149. )
  150. assert(ticket1, 'ticket created')
  151. # create inbound article #1
  152. article1 = Ticket::Article.create!(
  153. ticket_id: ticket1.id,
  154. from: 'some_sender@example.com',
  155. to: 'some_recipient@example.com',
  156. subject: 'some subject',
  157. message_id: 'some@id',
  158. content_type: 'text/html',
  159. body: 'some message article helper test1 <div><img style="width: 85.5px; height: 49.5px" src="cid:15.274327094.140938@zammad.example.com">asdasd<img src="cid:15.274327094.140939@zammad.example.com"><br>',
  160. internal: false,
  161. sender: Ticket::Article::Sender.find_by(name: 'Customer'),
  162. type: Ticket::Article::Type.find_by(name: 'email'),
  163. updated_by_id: 1,
  164. created_by_id: 1,
  165. )
  166. store1 = Store.add(
  167. object: 'Ticket::Article',
  168. o_id: article1.id,
  169. data: 'content_file1_normally_should_be_an_ics_calendar_file',
  170. filename: 'schedule.ics',
  171. preferences: {
  172. 'Mime-Type' => 'text/calendar'
  173. },
  174. created_by_id: 1,
  175. )
  176. text = <<~MSG_TEXT.chomp
  177. > Welcome!
  178. >
  179. > Thank you for installing Zammad. äöüß
  180. >
  181. MSG_TEXT
  182. mail = Channel::EmailBuild.build(
  183. from: 'sender@example.com',
  184. to: 'recipient@example.com',
  185. body: text,
  186. attachments: [
  187. store1
  188. ],
  189. )
  190. text_should = <<~MSG_TEXT.chomp
  191. > Welcome!\r
  192. >\r
  193. > Thank you for installing Zammad. äöüß\r
  194. >\r
  195. MSG_TEXT
  196. assert_equal(text_should, mail.text_part.body.to_s)
  197. assert_nil(mail.html_part)
  198. assert_equal('text/calendar; filename=schedule.ics', mail.attachments[0].content_type)
  199. parser = Channel::EmailParser.new
  200. data = parser.parse(mail.to_s)
  201. # check body
  202. assert_equal(text, data[:body])
  203. # check count of attachments, 2
  204. assert_equal(1, data[:attachments].length)
  205. # check attachments
  206. data[:attachments]&.each do |attachment|
  207. if attachment[:filename] == 'schedule.ics'
  208. assert(attachment[:preferences]['Content-ID'])
  209. assert_nil(attachment[:preferences]['content-alternative'])
  210. assert_equal('text/calendar', attachment[:preferences]['Mime-Type'])
  211. assert_equal('UTF-8', attachment[:preferences]['Charset'])
  212. else
  213. assert(false, "invalid attachment, should not be there, #{attachment.inspect}")
  214. end
  215. end
  216. end
  217. test 'plain email + without attachment check' do
  218. text = <<~MSG_TEXT.chomp
  219. > Welcome!
  220. >
  221. > Thank you for installing Zammad. äöüß
  222. >
  223. MSG_TEXT
  224. mail = Channel::EmailBuild.build(
  225. from: 'sender@example.com',
  226. to: 'recipient@example.com',
  227. body: text,
  228. )
  229. text_should = <<~MSG_TEXT.chomp
  230. > Welcome!\r
  231. >\r
  232. > Thank you for installing Zammad. äöüß\r
  233. >\r
  234. MSG_TEXT
  235. assert_equal(text_should, mail.body.to_s)
  236. assert_nil(mail.html_part)
  237. parser = Channel::EmailParser.new
  238. data = parser.parse(mail.to_s)
  239. # check body
  240. assert_equal(text, data[:body])
  241. # check count of attachments, 0
  242. assert_equal(0, data[:attachments].length)
  243. end
  244. test 'email - html email client fixes' do
  245. # https://github.com/martini/zammad/issues/165
  246. html_raw = '<blockquote type="cite">some
  247. text
  248. </blockquote>
  249. 123
  250. <blockquote type="cite">some
  251. text
  252. </blockquote>'
  253. html_with_fixes = Channel::EmailBuild.html_mail_client_fixes(html_raw)
  254. assert_not_equal(html_with_fixes, html_raw)
  255. html_should = '<blockquote type="cite" style="border-left: 2px solid blue; margin: 0 0 16px; padding: 8px 12px 8px 12px;">some
  256. text
  257. </blockquote>
  258. 123
  259. <blockquote type="cite" style="border-left: 2px solid blue; margin: 0 0 16px; padding: 8px 12px 8px 12px;">some
  260. text
  261. </blockquote>'
  262. assert_equal(html_should, html_with_fixes)
  263. html_raw = '<p>some
  264. text
  265. </p>
  266. <p>123</p>'
  267. html_with_fixes = Channel::EmailBuild.html_mail_client_fixes(html_raw)
  268. assert_not_equal(html_with_fixes, html_raw)
  269. html_should = '<p style="margin: 0;">some
  270. text
  271. </p>
  272. <p style="margin: 0;">123</p>'
  273. assert_equal(html_should, html_with_fixes)
  274. html_raw = '<p>sometext</p><hr><p>123</p>'
  275. html_with_fixes = Channel::EmailBuild.html_mail_client_fixes(html_raw)
  276. assert_not_equal(html_with_fixes, html_raw)
  277. html_should = '<p style="margin: 0;">sometext</p><hr style="margin-top: 6px; margin-bottom: 6px; border: 0; border-top: 1px solid #dfdfdf;"><p style="margin: 0;">123</p>'
  278. assert_equal(html_should, html_with_fixes)
  279. end
  280. test 'from checks' do
  281. quoted_in_one_line = Channel::EmailBuild.recipient_line('Somebody @ "Company"', 'some.body@example.com')
  282. assert_equal('"Somebody @ \"Company\"" <some.body@example.com>', quoted_in_one_line)
  283. quoted_in_one_line = Channel::EmailBuild.recipient_line('Somebody', 'some.body@example.com')
  284. assert_equal('Somebody <some.body@example.com>', quoted_in_one_line)
  285. quoted_in_one_line = Channel::EmailBuild.recipient_line('Somebody | Some Org', 'some.body@example.com')
  286. assert_equal('"Somebody | Some Org" <some.body@example.com>', quoted_in_one_line)
  287. quoted_in_one_line = Channel::EmailBuild.recipient_line('Test Master Agent via Support', 'some.body@example.com')
  288. assert_equal('"Test Master Agent via Support" <some.body@example.com>', quoted_in_one_line)
  289. end
  290. # #2362 - Attached text files get prepended on e-mail reply instead of appended
  291. test 'plain email + text attachment' do
  292. ticket1 = Ticket.create!(
  293. title: 'some article text attachment test',
  294. group: Group.lookup(name: 'Users'),
  295. customer_id: 2,
  296. state: Ticket::State.lookup(name: 'new'),
  297. priority: Ticket::Priority.lookup(name: '2 normal'),
  298. updated_by_id: 1,
  299. created_by_id: 1,
  300. )
  301. assert(ticket1, 'ticket created')
  302. article1 = Ticket::Article.create!(
  303. ticket_id: ticket1.id,
  304. from: 'some_sender@example.com',
  305. to: 'some_recipient@example.com',
  306. subject: 'some subject',
  307. message_id: 'some@id',
  308. content_type: 'text/html',
  309. body: 'some message article helper test1 <div><img style="width: 85.5px; height: 49.5px" src="cid:15.274327094.140938@zammad.example.com">asdasd<img src="cid:15.274327094.140939@zammad.example.com"><br>',
  310. internal: false,
  311. sender: Ticket::Article::Sender.find_by(name: 'Customer'),
  312. type: Ticket::Article::Type.find_by(name: 'email'),
  313. updated_by_id: 1,
  314. created_by_id: 1,
  315. )
  316. text = <<~MSG_TEXT.chomp
  317. > Welcome!
  318. >
  319. > Email Content
  320. MSG_TEXT
  321. store1 = Store.add(
  322. object: 'Ticket::Article',
  323. o_id: article1.id,
  324. data: 'Text Content',
  325. filename: 'text_file.txt',
  326. preferences: {
  327. 'Mime-Type' => 'text/plain'
  328. },
  329. created_by_id: 1,
  330. )
  331. mail = Channel::EmailBuild.build(
  332. from: 'sender@example.com',
  333. to: 'recipient@example.com',
  334. body: text,
  335. attachments: [
  336. store1
  337. ],
  338. )
  339. File.write('append_test.eml', mail.to_s)
  340. # Email Content should appear before the Text Content within the raw email
  341. assert_match(%r{Email Content[\s\S]*Text Content}, mail.to_s)
  342. end
  343. end