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.

test_email_body.py 5.7 KiB

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