You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

181 line
5.4 KiB

  1. # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: MIT. See LICENSE
  3. import unittest, os, base64
  4. from frappe import safe_decode
  5. from frappe.email.receive import Email
  6. from frappe.email.email_body import (replace_filename_with_cid,
  7. get_email, inline_style_in_html, get_header)
  8. from frappe.email.doctype.email_queue.email_queue import SendMailContext, QueueBuilder
  9. class TestEmailBody(unittest.TestCase):
  10. def setUp(self):
  11. email_html = '''
  12. <div>
  13. <h3>Hey John Doe!</h3>
  14. <p>This is embedded image you asked for</p>
  15. <img embed="assets/frappe/images/frappe-favicon.svg" />
  16. </div>
  17. '''
  18. email_text = '''
  19. Hey John Doe!
  20. This is the text version of this email
  21. '''
  22. img_path = os.path.abspath('assets/frappe/images/frappe-favicon.svg')
  23. with open(img_path, 'rb') as f:
  24. img_content = f.read()
  25. img_base64 = base64.b64encode(img_content).decode()
  26. # email body keeps 76 characters on one line
  27. self.img_base64 = fixed_column_width(img_base64, 76)
  28. self.email_string = get_email(
  29. recipients=['test@example.com'],
  30. sender='me@example.com',
  31. subject='Test Subject',
  32. content=email_html,
  33. text_content=email_text
  34. ).as_string().replace("\r\n", "\n")
  35. def test_prepare_message_returns_already_encoded_string(self):
  36. uni_chr1 = chr(40960)
  37. uni_chr2 = chr(1972)
  38. queue_doc = QueueBuilder(
  39. recipients=['test@example.com'],
  40. sender='me@example.com',
  41. subject='Test Subject',
  42. message='<h1>' + uni_chr1 + 'abcd' + uni_chr2 + '</h1>',
  43. text_content='whatever').process()[0]
  44. mail_ctx = SendMailContext(queue_doc = queue_doc)
  45. result = mail_ctx.build_message(recipient_email = 'test@test.com')
  46. self.assertTrue(b"<h1>=EA=80=80abcd=DE=B4</h1>" in result)
  47. def test_prepare_message_returns_cr_lf(self):
  48. queue_doc = QueueBuilder(
  49. recipients=['test@example.com'],
  50. sender='me@example.com',
  51. subject='Test Subject',
  52. message='<h1>\n this is a test of newlines\n' + '</h1>',
  53. text_content='whatever').process()[0]
  54. mail_ctx = SendMailContext(queue_doc = queue_doc)
  55. result = safe_decode(mail_ctx.build_message(recipient_email='test@test.com'))
  56. self.assertTrue(result.count('\n') == result.count("\r"))
  57. def test_image(self):
  58. img_signature = '''
  59. Content-Type: image/svg+xml
  60. MIME-Version: 1.0
  61. Content-Transfer-Encoding: base64
  62. Content-Disposition: inline; filename="frappe-favicon.svg"
  63. '''
  64. self.assertTrue(img_signature in self.email_string)
  65. self.assertTrue(self.img_base64 in self.email_string)
  66. def test_text_content(self):
  67. text_content = '''
  68. Content-Type: text/plain; charset="utf-8"
  69. MIME-Version: 1.0
  70. Content-Transfer-Encoding: quoted-printable
  71. Hey John Doe!
  72. This is the text version of this email
  73. '''
  74. self.assertTrue(text_content in self.email_string)
  75. def test_email_content(self):
  76. html_head = '''
  77. Content-Type: text/html; charset="utf-8"
  78. MIME-Version: 1.0
  79. Content-Transfer-Encoding: quoted-printable
  80. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.=
  81. w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  82. <html xmlns=3D"http://www.w3.org/1999/xhtml">
  83. '''
  84. html = '''<h3>Hey John Doe!</h3>'''
  85. self.assertTrue(html_head in self.email_string)
  86. self.assertTrue(html in self.email_string)
  87. def test_replace_filename_with_cid(self):
  88. original_message = '''
  89. <div>
  90. <img embed="assets/frappe/images/frappe-favicon.svg" alt="test" />
  91. <img embed="notexists.jpg" />
  92. </div>
  93. '''
  94. message, inline_images = replace_filename_with_cid(original_message)
  95. processed_message = '''
  96. <div>
  97. <img src="cid:{0}" alt="test" />
  98. <img />
  99. </div>
  100. '''.format(inline_images[0].get('content_id'))
  101. self.assertEqual(message, processed_message)
  102. def test_inline_styling(self):
  103. html = '''
  104. <h3>Hi John</h3>
  105. <p>This is a test email</p>
  106. '''
  107. transformed_html = '''
  108. <h3>Hi John</h3>
  109. <p style="margin:1em 0 !important">This is a test email</p>
  110. '''
  111. self.assertTrue(transformed_html in inline_style_in_html(html))
  112. def test_email_header(self):
  113. email_html = '''
  114. <h3>Hey John Doe!</h3>
  115. <p>This is embedded image you asked for</p>
  116. '''
  117. email_string = get_email(
  118. recipients=['test@example.com'],
  119. sender='me@example.com',
  120. subject='Test Subject',
  121. content=email_html,
  122. header=['Email Title', 'green']
  123. ).as_string().replace("\r\n", "\n")
  124. # REDESIGN-TODO: Add style for indicators in email
  125. self.assertTrue('''<span class=3D"indicator indicator-green"></span>''' in email_string)
  126. self.assertTrue('<span>Email Title</span>' in email_string)
  127. def test_get_email_header(self):
  128. html = get_header(['This is test', 'orange'])
  129. self.assertTrue('<span class="indicator indicator-orange"></span>' in html)
  130. self.assertTrue('<span>This is test</span>' in html)
  131. html = get_header(['This is another test'])
  132. self.assertTrue('<span>This is another test</span>' in html)
  133. html = get_header('This is string')
  134. self.assertTrue('<span>This is string</span>' in html)
  135. def test_8bit_utf_8_decoding(self):
  136. text_content_bytes = b"\xed\x95\x9c\xea\xb8\x80\xe1\xa5\xa1\xe2\x95\xa5\xe0\xba\xaa\xe0\xa4\x8f"
  137. text_content = text_content_bytes.decode('utf-8')
  138. content_bytes = b"""MIME-Version: 1.0
  139. Content-Type: text/plain; charset=utf-8
  140. Content-Disposition: inline
  141. Content-Transfer-Encoding: 8bit
  142. From: test1_@erpnext.com
  143. Reply-To: test2_@erpnext.com
  144. """ + text_content_bytes
  145. mail = Email(content_bytes)
  146. self.assertEqual(mail.text_content, text_content)
  147. def fixed_column_width(string, chunk_size):
  148. parts = [string[0 + i:chunk_size + i] for i in range(0, len(string), chunk_size)]
  149. return '\n'.join(parts)