test_email_body_parser.py 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. from bs4 import BeautifulSoup
  2. from nose.tools import raises
  3. from tracim.lib.email_processing.checkers import HtmlMailQuoteChecker
  4. from tracim.lib.email_processing.checkers import HtmlMailSignatureChecker
  5. from tracim.lib.email_processing.parser import ParsedHTMLMail
  6. from tracim.lib.email_processing.models import BodyMailPartType
  7. from tracim.lib.email_processing.models import BodyMailPart
  8. from tracim.lib.email_processing.models import BodyMailParts
  9. from tracim.tests import TestStandard
  10. class TestHtmlMailQuoteChecker(TestStandard):
  11. def test_unit__is_standard_quote_ok(self):
  12. soup = BeautifulSoup('<blockquote></blockquote>', 'html.parser')
  13. main_elem = soup.find()
  14. assert HtmlMailQuoteChecker._is_standard_quote(main_elem) is True
  15. def test_unit__is_standard_quote_no(self):
  16. soup = BeautifulSoup('<a></a>', 'html.parser')
  17. main_elem = soup.find()
  18. assert HtmlMailQuoteChecker._is_standard_quote(main_elem) is False
  19. def test_unit__is_thunderbird_quote_ok(self):
  20. soup = BeautifulSoup('<div class="moz-cite-prefix"></div>',
  21. 'html.parser')
  22. main_elem = soup.find()
  23. assert HtmlMailQuoteChecker._is_thunderbird_quote(main_elem) is True
  24. def test_unit__is_thunderbird_quote_no(self):
  25. soup = BeautifulSoup('<div class="nothing"></div>', 'html.parser')
  26. main_elem = soup.find()
  27. assert HtmlMailQuoteChecker._is_thunderbird_quote(main_elem) is False
  28. def test_unit__is_gmail_quote_ok(self):
  29. html = '<div class="gmail_extra">' + \
  30. '<a></a><div class="gmail_quote"></div>' + \
  31. '</div>'
  32. soup = BeautifulSoup(html, 'html.parser')
  33. main_elem = soup.find()
  34. assert HtmlMailQuoteChecker._is_gmail_quote(main_elem) is True
  35. def test_unit__is_gmail_quote_no(self):
  36. soup = BeautifulSoup('<div class="nothing"></div>', 'html.parser')
  37. main_elem = soup.find()
  38. assert HtmlMailQuoteChecker._is_gmail_quote(main_elem) is False
  39. def test_unit__is_gmail_quote_no_2(self):
  40. html = '<div class="gmail_extra">' + \
  41. '<a></a><div class="gmail_signature"></div>' + \
  42. '</div>'
  43. soup = BeautifulSoup(html, 'html.parser')
  44. main_elem = soup.find()
  45. assert HtmlMailQuoteChecker._is_gmail_quote(main_elem) is False
  46. def test_unit__is_outlook_com_quote_ok(self):
  47. soup = BeautifulSoup('<div id="divRplyFwdMsg"></div>', 'html.parser')
  48. main_elem = soup.find()
  49. assert HtmlMailQuoteChecker._is_outlook_com_quote(main_elem) is True
  50. def test_unit__is_outlook_com_quote_no(self):
  51. soup = BeautifulSoup('<div id="Signature"></div>', 'html.parser')
  52. main_elem = soup.find()
  53. assert HtmlMailQuoteChecker._is_outlook_com_quote(main_elem) is False
  54. # TODO - G.M - 2017-11-24 - Check Yahoo and New roundcube html mail with
  55. # correct mail example
  56. class TestHtmlMailSignatureChecker(TestStandard):
  57. def test_unit__is_thunderbird_signature_ok(self):
  58. soup = BeautifulSoup('<div class="moz-signature"></div>', 'html.parser')
  59. main_elem = soup.find()
  60. assert HtmlMailSignatureChecker._is_thunderbird_signature(main_elem) is True # nopep8
  61. def test_unit__is_thunderbird_signature_no(self):
  62. soup = BeautifulSoup('<div class="other"></div>', 'html.parser')
  63. main_elem = soup.find()
  64. assert HtmlMailSignatureChecker._is_thunderbird_signature(main_elem) is False # nopep8
  65. def test_unit__is_gmail_signature_ok(self):
  66. html = '<div class="gmail_extra">' + \
  67. '<a></a><div class="gmail_quote"></div>' + \
  68. '</div>'
  69. soup = BeautifulSoup(html, 'html.parser')
  70. main_elem = soup.find()
  71. assert HtmlMailSignatureChecker._is_gmail_signature(main_elem) is False
  72. def test_unit__is_gmail_signature_no(self):
  73. soup = BeautifulSoup('<div class="nothing"></div>', 'html.parser')
  74. main_elem = soup.find()
  75. assert HtmlMailSignatureChecker._is_gmail_signature(main_elem) is False
  76. def test_unit__is_gmail_signature_yes(self):
  77. html = '<div class="gmail_extra">' + \
  78. '<a></a><div class="gmail_signature"></div>' + \
  79. '</div>'
  80. soup = BeautifulSoup(html, 'html.parser')
  81. main_elem = soup.find()
  82. assert HtmlMailSignatureChecker._is_gmail_signature(main_elem) is True
  83. def test_unit__is_gmail_signature_yes_2(self):
  84. html = '<div class="gmail_signature">' + \
  85. '</div>'
  86. soup = BeautifulSoup(html, 'html.parser')
  87. main_elem = soup.find()
  88. assert HtmlMailSignatureChecker._is_gmail_signature(main_elem) is True
  89. def test_unit__is_outlook_com_signature_no(self):
  90. soup = BeautifulSoup('<div id="divRplyFwdMsg"></div>', 'html.parser')
  91. main_elem = soup.find()
  92. assert HtmlMailSignatureChecker._is_outlook_com_signature(main_elem) \
  93. is False
  94. def test_unit__is_outlook_com_signature_ok(self):
  95. soup = BeautifulSoup('<div id="Signature"></div>', 'html.parser')
  96. main_elem = soup.find()
  97. assert HtmlMailSignatureChecker._is_outlook_com_signature(main_elem) \
  98. is True
  99. class TestBodyMailsParts(TestStandard):
  100. def test_unit__std_list_methods(self):
  101. mail_parts = BodyMailParts()
  102. assert len(mail_parts) == 0
  103. a = BodyMailPart('a', BodyMailPartType.Main)
  104. mail_parts._list.append(a)
  105. assert len(mail_parts) == 1
  106. assert mail_parts[0] == a
  107. del mail_parts[0]
  108. assert len(mail_parts) == 0
  109. def test_unit__append_same_type(self):
  110. mail_parts = BodyMailParts()
  111. a = BodyMailPart('a', BodyMailPartType.Main)
  112. mail_parts._append(a)
  113. b = BodyMailPart('b', BodyMailPartType.Main)
  114. mail_parts._append(b)
  115. assert len(mail_parts) == 1
  116. assert mail_parts[0].part_type == BodyMailPartType.Main
  117. assert mail_parts[0].text == 'ab'
  118. def test_unit__append_different_type(self):
  119. mail_parts = BodyMailParts()
  120. a = BodyMailPart('a', BodyMailPartType.Main)
  121. mail_parts.append(a)
  122. b = BodyMailPart('b', BodyMailPartType.Quote)
  123. mail_parts._append(b)
  124. assert len(mail_parts) == 2
  125. assert mail_parts[0] == a
  126. assert mail_parts[1] == b
  127. def test_unit__append_follow(self):
  128. mail_parts = BodyMailParts()
  129. mail_parts.follow = True
  130. a = BodyMailPart('a', BodyMailPartType.Main)
  131. mail_parts._append(a)
  132. b = BodyMailPart('b', BodyMailPartType.Quote)
  133. mail_parts._append(b)
  134. assert len(mail_parts) == 1
  135. assert mail_parts[0].part_type == BodyMailPartType.Main
  136. assert mail_parts[0].text == 'ab'
  137. def test_unit__append_dont_follow_when_first(self):
  138. mail_parts = BodyMailParts()
  139. a = BodyMailPart('a', BodyMailPartType.Main)
  140. mail_parts._append(a, follow=True)
  141. assert len(mail_parts) == 1
  142. assert mail_parts[0].part_type == BodyMailPartType.Main
  143. assert mail_parts[0].text == 'a'
  144. @raises(TypeError)
  145. def test_unit__check_value__type_error(self):
  146. mail_parts = BodyMailParts()
  147. mail_parts._check_value('a')
  148. def test_unit__check_value__ok(self):
  149. mail_parts = BodyMailParts()
  150. a = BodyMailPart('a', BodyMailPartType.Main)
  151. mail_parts._check_value(a)
  152. def test_unit__drop_part_type(self):
  153. mail_parts = BodyMailParts()
  154. a = BodyMailPart('a', BodyMailPartType.Main)
  155. mail_parts._list.append(a)
  156. b = BodyMailPart('b', BodyMailPartType.Quote)
  157. mail_parts._list.append(b)
  158. c = BodyMailPart('c', BodyMailPartType.Signature)
  159. mail_parts._list.append(c)
  160. mail_parts.drop_part_type(BodyMailPartType.Quote)
  161. assert len(mail_parts) == 2
  162. assert mail_parts[0].text == 'a'
  163. assert mail_parts[0].part_type == BodyMailPartType.Main
  164. assert len(mail_parts) == 2
  165. assert mail_parts[1].text == 'c'
  166. assert mail_parts[1].part_type == BodyMailPartType.Signature
  167. def test_unit__drop_part_type_verify_no_follow_incidence(self):
  168. mail_parts = BodyMailParts()
  169. a = BodyMailPart('a', BodyMailPartType.Main)
  170. mail_parts._list.append(a)
  171. b = BodyMailPart('b', BodyMailPartType.Quote)
  172. mail_parts._list.append(b)
  173. c = BodyMailPart('c', BodyMailPartType.Signature)
  174. mail_parts._list.append(c)
  175. mail_parts.follow = True
  176. mail_parts.drop_part_type(BodyMailPartType.Quote)
  177. assert len(mail_parts) == 2
  178. assert mail_parts[0].text == 'a'
  179. assert mail_parts[0].part_type == BodyMailPartType.Main
  180. assert len(mail_parts) == 2
  181. assert mail_parts[1].text == 'c'
  182. assert mail_parts[1].part_type == BodyMailPartType.Signature
  183. def test_unit__drop_part_type_consistence(self):
  184. mail_parts = BodyMailParts()
  185. a = BodyMailPart('a', BodyMailPartType.Main)
  186. mail_parts._list.append(a)
  187. b = BodyMailPart('b', BodyMailPartType.Quote)
  188. mail_parts._list.append(b)
  189. c = BodyMailPart('c', BodyMailPartType.Main)
  190. mail_parts._list.append(c)
  191. mail_parts.drop_part_type(BodyMailPartType.Quote)
  192. assert len(mail_parts) == 1
  193. assert mail_parts[0].text == 'ac'
  194. assert mail_parts[0].part_type == BodyMailPartType.Main
  195. def test_unit__get_nb_part_type(self):
  196. mail_parts = BodyMailParts()
  197. assert mail_parts.get_nb_part_type(BodyMailPartType.Main) == 0
  198. assert mail_parts.get_nb_part_type(BodyMailPartType.Quote) == 0
  199. assert mail_parts.get_nb_part_type(BodyMailPartType.Signature) == 0
  200. a = BodyMailPart('a', BodyMailPartType.Main)
  201. mail_parts._list.append(a)
  202. assert mail_parts.get_nb_part_type(BodyMailPartType.Main) == 1
  203. b = BodyMailPart('b', BodyMailPartType.Quote)
  204. mail_parts._list.append(b)
  205. assert mail_parts.get_nb_part_type(BodyMailPartType.Quote) == 1
  206. c = BodyMailPart('c', BodyMailPartType.Signature)
  207. mail_parts._list.append(c)
  208. assert mail_parts.get_nb_part_type(BodyMailPartType.Main) == 1
  209. assert mail_parts.get_nb_part_type(BodyMailPartType.Quote) == 1
  210. assert mail_parts.get_nb_part_type(BodyMailPartType.Signature) == 1
  211. def test_unit__str(self):
  212. mail_parts = BodyMailParts()
  213. a = BodyMailPart('a', BodyMailPartType.Main)
  214. mail_parts._list.append(a)
  215. b = BodyMailPart('b', BodyMailPartType.Quote)
  216. mail_parts._list.append(b)
  217. c = BodyMailPart('c', BodyMailPartType.Signature)
  218. mail_parts._list.append(c)
  219. assert str(mail_parts) == 'abc'
  220. class TestParsedMail(TestStandard):
  221. def test_other__check_gmail_mail_text_only(self):
  222. text_only = '''<div dir="ltr">Voici le texte<br></div>'''
  223. mail = ParsedHTMLMail(text_only)
  224. elements = mail.get_elements()
  225. assert len(elements) == 1
  226. assert elements[0].part_type == BodyMailPartType.Main
  227. def test_other__check_gmail_mail_text_signature(self):
  228. text_and_signature = '''
  229. <div dir="ltr">POF<br clear="all"><div><br>-- <br>
  230. <div class="gmail_signature" data-smartmail="gmail_signature">
  231. <div dir="ltr">Voici Ma signature. En HTML <br><ol>
  232. <li>Plop</li>
  233. <li>Plip</li>
  234. <li>Plop<br>
  235. </li></ol></div></div></div></div>
  236. '''
  237. mail = ParsedHTMLMail(text_and_signature)
  238. elements = mail.get_elements()
  239. assert len(elements) == 2
  240. assert elements[0].part_type == BodyMailPartType.Main
  241. assert elements[1].part_type == BodyMailPartType.Signature
  242. def test_other__check_gmail_mail_text_quote(self):
  243. text_and_quote = '''
  244. <div dir="ltr">Réponse<br>
  245. <div class="gmail_extra"><br>
  246. <div class="gmail_quote">Le 28 novembre 2017 à 10:29, John Doe <span
  247. dir="ltr">&lt;<a href="mailto:bidule@localhost.fr"
  248. target="_blank">bidule@localhost.fr</a>&gt;</span>
  249. a écrit :<br>
  250. <blockquote class="gmail_quote" style="margin:0 0 0
  251. .8ex;border-left:1px #ccc solid;padding-left:1ex">Voici ma réponse<br>
  252. <br><br>
  253. Le 28/11/2017 à 10:05, Foo Bar a écrit&nbsp;:<br>
  254. <blockquote class="gmail_quote" style="margin:0 0 0
  255. .8ex;border-left:1px #ccc solid;padding-left:1ex">
  256. Voici le texte<span class="HOEnZb"><font color="#888888"><br>
  257. </font></span></blockquote>
  258. <span class="HOEnZb"><font color="#888888">
  259. <br>
  260. -- <br>
  261. TEST DE signature<br>
  262. </font></span></blockquote>
  263. </div><br></div></div>
  264. '''
  265. mail = ParsedHTMLMail(text_and_quote)
  266. elements = mail.get_elements()
  267. assert len(elements) == 2
  268. assert elements[0].part_type == BodyMailPartType.Main
  269. assert elements[1].part_type == BodyMailPartType.Quote
  270. def test_other__check_gmail_mail_text_quote_text(self):
  271. text_quote_text = '''
  272. <div dir="ltr">Avant<br>
  273. <div class="gmail_extra"><br>
  274. <div class="gmail_quote">Le 28 novembre 2017 à 10:29, John Doe
  275. <span dir="ltr">&lt;<a href="mailto:bidule@localhost.fr"
  276. target="_blank">bidule@localhost.fr</a>&gt;</span>
  277. a écrit :<br>
  278. <blockquote class="gmail_quote" style="margin:0 0 0
  279. .8ex;border-left:1px #ccc solid;padding-left:1ex">Voici ma
  280. réponse<br>
  281. <br>
  282. <br>
  283. Le 28/11/2017 à 10:05, Foo Bar a écrit&nbsp;:<br>
  284. <blockquote class="gmail_quote" style="margin:0 0 0
  285. .8ex;border-left:1px #ccc solid;padding-left:1ex">
  286. Voici le texte<span class="HOEnZb"><font color="#888888"><br>
  287. </font></span></blockquote>
  288. <span class="HOEnZb"><font color="#888888">
  289. <br>
  290. -- <br>
  291. TEST DE signature<br>
  292. </font></span></blockquote>
  293. </div>
  294. <br>
  295. </div>
  296. <div class="gmail_extra">Aprés<br>
  297. </div>
  298. </div>
  299. '''
  300. mail = ParsedHTMLMail(text_quote_text)
  301. elements = mail.get_elements()
  302. assert len(elements) == 3
  303. assert elements[0].part_type == BodyMailPartType.Main
  304. assert elements[1].part_type == BodyMailPartType.Quote
  305. assert elements[2].part_type == BodyMailPartType.Main
  306. def test_other__check_gmail_mail_text_quote_signature(self):
  307. text_quote_signature = '''
  308. <div dir="ltr">Hey !<br>
  309. </div>
  310. <div class="gmail_extra"><br>
  311. <div class="gmail_quote">Le 28 novembre 2017 à 10:29,
  312. John Doe <span
  313. dir="ltr">&lt;<a href="mailto:bidule@localhost.fr"
  314. target="_blank">bidule@localhost.fr</a>&gt;</span>
  315. a écrit :<br>
  316. <blockquote class="gmail_quote" style="margin:0 0 0
  317. .8ex;border-left:1px #ccc solid;padding-left:1ex">Voici ma
  318. réponse<br>
  319. <br>
  320. <br>
  321. Le 28/11/2017 à 10:05, Foo Bar a écrit&nbsp;:<br>
  322. <blockquote class="gmail_quote" style="margin:0 0 0
  323. .8ex;border-left:1px #ccc solid;padding-left:1ex">
  324. Voici le texte<span class="HOEnZb"><font color="#888888"><br>
  325. </font></span></blockquote>
  326. <span class="HOEnZb"><font color="#888888">
  327. <br>
  328. -- <br>
  329. TEST DE signature<br>
  330. </font></span></blockquote>
  331. </div>
  332. <br>
  333. <br clear="all">
  334. <br>
  335. -- <br>
  336. <div class="gmail_signature" data-smartmail="gmail_signature">
  337. <div dir="ltr">Voici Ma signature. En HTML <br>
  338. <ol>
  339. <li>Plop</li>
  340. <li>Plip</li>
  341. <li>Plop<br>
  342. </li>
  343. </ol>
  344. </div>
  345. </div>
  346. </div>
  347. '''
  348. # INFO - G.M - 2017-11-28 -
  349. # Now Quote + Signature block in Gmail is considered as one Quote
  350. # Block.
  351. mail = ParsedHTMLMail(text_quote_signature)
  352. elements = mail.get_elements()
  353. assert len(elements) == 2
  354. assert elements[0].part_type == BodyMailPartType.Main
  355. assert elements[1].part_type == BodyMailPartType.Quote
  356. def test_other__check_gmail_mail_text_quote_text_signature(self):
  357. text_quote_text_sign = '''
  358. <div dir="ltr">Test<br>
  359. <div class="gmail_extra"><br>
  360. <div class="gmail_quote">Le 28 novembre 2017 à 10:29, John Doe <span
  361. dir="ltr">&lt;<a href="mailto:bidule@localhost.fr"
  362. target="_blank">bidule@localhost.fr</a>&gt;</span>
  363. a écrit :<br>
  364. <blockquote class="gmail_quote" style="margin:0 0 0
  365. .8ex;border-left:1px #ccc solid;padding-left:1ex">Voici ma
  366. réponse<br>
  367. <br>
  368. <br>
  369. Le 28/11/2017 à 10:05, Foo Bar a écrit&nbsp;:<br>
  370. <blockquote class="gmail_quote" style="margin:0 0 0
  371. .8ex;border-left:1px #ccc solid;padding-left:1ex">
  372. Voici le texte<span class="HOEnZb"><font color="#888888"><br>
  373. </font></span></blockquote>
  374. <span class="HOEnZb"><font color="#888888">
  375. <br>
  376. -- <br>
  377. TEST DE signature<br>
  378. </font></span></blockquote>
  379. </div>
  380. <br>
  381. <br>
  382. </div>
  383. <div class="gmail_extra">RE test<br clear="all">
  384. </div>
  385. <div class="gmail_extra"><br>
  386. -- <br>
  387. <div class="gmail_signature" data-smartmail="gmail_signature">
  388. <div dir="ltr">Voici Ma signature. En HTML <br>
  389. <ol>
  390. <li>Plop</li>
  391. <li>Plip</li>
  392. <li>Plop<br>
  393. </li>
  394. </ol>
  395. </div>
  396. </div>
  397. </div>
  398. </div>
  399. '''
  400. mail = ParsedHTMLMail(text_quote_text_sign)
  401. elements = mail.get_elements()
  402. assert len(elements) == 4
  403. assert elements[0].part_type == BodyMailPartType.Main
  404. assert elements[1].part_type == BodyMailPartType.Quote
  405. assert elements[2].part_type == BodyMailPartType.Main
  406. assert elements[3].part_type == BodyMailPartType.Signature
  407. def test_other__check_thunderbird_mail_text_only(self):
  408. text_only = '''Coucou<br><br><br>'''
  409. mail = ParsedHTMLMail(text_only)
  410. elements = mail.get_elements()
  411. assert len(elements) == 1
  412. assert elements[0].part_type == BodyMailPartType.Main
  413. def test_other__check_thunderbird_mail_text_signature(self):
  414. text_and_signature = '''
  415. <p>Test<br>
  416. </p>
  417. <div class="moz-signature">-- <br>
  418. TEST DE signature</div>
  419. '''
  420. mail = ParsedHTMLMail(text_and_signature)
  421. elements = mail.get_elements()
  422. assert len(elements) == 2
  423. assert elements[0].part_type == BodyMailPartType.Main
  424. assert elements[1].part_type == BodyMailPartType.Signature
  425. def test_other__check_thunderbird_mail_text_quote(self):
  426. text_and_quote = '''
  427. <p>Pof<br>
  428. </p>
  429. <br>
  430. <div class="moz-cite-prefix">Le 28/11/2017 à 11:21, John Doe a
  431. écrit&nbsp;:<br>
  432. </div>
  433. <blockquote type="cite"
  434. cite="mid:658592c1-14de-2958-5187-3571edea0aac@localhost.fr">
  435. <meta http-equiv="Context-Type"
  436. content="text/html; charset=utf-8">
  437. <p>Test<br>
  438. </p>
  439. <div class="moz-signature">-- <br>
  440. TEST DE signature</div>
  441. </blockquote>
  442. <br>
  443. '''
  444. mail = ParsedHTMLMail(text_and_quote)
  445. elements = mail.get_elements()
  446. assert len(elements) == 2
  447. assert elements[0].part_type == BodyMailPartType.Main
  448. assert elements[1].part_type == BodyMailPartType.Quote
  449. def test_other__check_thunderbird_mail_text_quote_text(self):
  450. text_quote_text = '''
  451. <p>Pof<br>
  452. </p>
  453. <br>
  454. <div class="moz-cite-prefix">Le 28/11/2017 à 11:54,
  455. Bidule a
  456. écrit&nbsp;:<br>
  457. </div>
  458. <blockquote type="cite"
  459. cite="mid:b541b451-bb31-77a4-45b9-ad89969d7962@localhost.fr">
  460. <meta http-equiv="Context-Type"
  461. content="text/html; charset=utf-8">
  462. <p>Pof<br>
  463. </p>
  464. <br>
  465. <div class="moz-cite-prefix">Le 28/11/2017 à 11:21, John Doe a
  466. écrit&nbsp;:<br>
  467. </div>
  468. <blockquote type="cite"
  469. cite="mid:658592c1-14de-2958-5187-3571edea0aac@localhost.fr">
  470. <p>Test<br>
  471. </p>
  472. <div class="moz-signature">-- <br>
  473. TEST DE signature</div>
  474. </blockquote>
  475. <br>
  476. </blockquote>
  477. Pif<br>
  478. '''
  479. mail = ParsedHTMLMail(text_quote_text)
  480. elements = mail.get_elements()
  481. assert len(elements) == 3
  482. assert elements[0].part_type == BodyMailPartType.Main
  483. assert elements[1].part_type == BodyMailPartType.Quote
  484. assert elements[2].part_type == BodyMailPartType.Main
  485. def test_other__check_thunderbird_mail_text_quote_signature(self):
  486. text_quote_signature = '''
  487. <p>Coucou<br>
  488. </p>
  489. <br>
  490. <div class="moz-cite-prefix">Le 28/11/2017 à 11:22, Bidule a
  491. écrit&nbsp;:<br>
  492. </div>
  493. <blockquote type="cite"
  494. cite="mid:4e6923e2-796d-eccf-84b7-6824da4151ee@localhost.fr">Réponse<br>
  495. <br>
  496. Le 28/11/2017 à 11:21, John Doe a écrit&nbsp;: <br>
  497. <blockquote type="cite"> <br>
  498. Test <br>
  499. <br>
  500. --&nbsp;<br>
  501. TEST DE signature <br>
  502. </blockquote>
  503. <br>
  504. </blockquote>
  505. <br>
  506. <div class="moz-signature">-- <br>
  507. TEST DE signature</div>
  508. '''
  509. mail = ParsedHTMLMail(text_quote_signature)
  510. elements = mail.get_elements()
  511. assert len(elements) == 3
  512. assert elements[0].part_type == BodyMailPartType.Main
  513. assert elements[1].part_type == BodyMailPartType.Quote
  514. assert elements[2].part_type == BodyMailPartType.Signature
  515. def test_other__check_thunderbird_mail_text_quote_text_signature(self):
  516. text_quote_text_sign = '''
  517. <p>Avant<br>
  518. </p>
  519. <br>
  520. <div class="moz-cite-prefix">Le 28/11/2017 à 11:19, Bidule a
  521. écrit&nbsp;:<br>
  522. </div>
  523. <blockquote type="cite"
  524. cite="mid:635df73c-d3c9-f2e9-2304-24ff536bfa16@localhost.fr">Coucou
  525. <br><br>
  526. </blockquote>
  527. Aprés<br>
  528. <br>
  529. <div class="moz-signature">-- <br>
  530. TEST DE signature</div>
  531. '''
  532. mail = ParsedHTMLMail(text_quote_text_sign)
  533. elements = mail.get_elements()
  534. assert len(elements) == 4
  535. assert elements[0].part_type == BodyMailPartType.Main
  536. assert elements[1].part_type == BodyMailPartType.Quote
  537. assert elements[2].part_type == BodyMailPartType.Main
  538. assert elements[3].part_type == BodyMailPartType.Signature
  539. # INFO - G.M - 2017-11-28 - Test for outlook.com webapp html mail
  540. # outlook.com ui doesn't seems to allow complex reply, new message
  541. # and signature are always before quoted one.
  542. def test_other__check_outlook_com_mail_text_only(self):
  543. text_only = '''
  544. <div id="divtagdefaultwrapper"
  545. style="font-size:12pt;color:#000000;
  546. font-family:Calibri,Helvetica,sans-serif;"
  547. dir="ltr">
  548. <p style="margin-top:0;margin-bottom:0">message<br>
  549. </p>
  550. </div>
  551. '''
  552. mail = ParsedHTMLMail(text_only)
  553. elements = mail.get_elements()
  554. assert len(elements) == 1
  555. assert elements[0].part_type == BodyMailPartType.Main
  556. def test_other__check_outlook_com_mail_text_signature(self):
  557. text_and_signature = '''
  558. <div id="divtagdefaultwrapper"
  559. style="font-size:12pt;color:#000000;
  560. font-family:Calibri,Helvetica,sans-serif;"
  561. dir="ltr">
  562. <p style="margin-top:0;margin-bottom:0">Test<br>
  563. </p>
  564. <p style="margin-top:0;margin-bottom:0"><br>
  565. </p>
  566. <div id="Signature">
  567. <div id="divtagdefaultwrapper" style="font-size: 12pt; color:
  568. rgb(0, 0, 0); background-color: rgb(255, 255, 255);
  569. font-family:
  570. Calibri,Arial,Helvetica,sans-serif,&quot;EmojiFont&quot;,&quot;Apple
  571. Color Emoji&quot;,&quot;Segoe UI
  572. Emoji&quot;,NotoColorEmoji,&quot;Segoe UI
  573. Symbol&quot;,&quot;Android Emoji&quot;,EmojiSymbols;">
  574. Envoyé à partir de <a href="http://aka.ms/weboutlook"
  575. id="LPNoLP">Outlook</a></div>
  576. </div>
  577. </div>
  578. '''
  579. mail = ParsedHTMLMail(text_and_signature)
  580. elements = mail.get_elements()
  581. assert len(elements) == 2
  582. assert elements[0].part_type == BodyMailPartType.Main
  583. assert elements[1].part_type == BodyMailPartType.Signature
  584. def test_other__check_outlook_com_mail_text_quote(self):
  585. text_and_quote = '''
  586. <div id="divtagdefaultwrapper"
  587. style="font-size:12pt;color:#000000;font-family:Calibri,Helvetica,sans-serif;"
  588. dir="ltr">
  589. <p style="margin-top:0;margin-bottom:0">Salut !<br>
  590. </p>
  591. </div>
  592. <hr style="display:inline-block;width:98%" tabindex="-1">
  593. <div id="divRplyFwdMsg" dir="ltr"><font style="font-size:11pt"
  594. color="#000000" face="Calibri, sans-serif"><b>De :</b> John Doe<br>
  595. <b>Envoyé :</b> mardi 28 novembre 2017 12:44:59<br>
  596. <b>À :</b> dev.bidule@localhost.fr<br>
  597. <b>Objet :</b> voila</font>
  598. <div>&nbsp;</div>
  599. </div>
  600. <style type="text/css" style="display:none">
  601. <!--
  602. p
  603. &#x09;{margin-top:0;
  604. &#x09;margin-bottom:0}
  605. -->
  606. </style>
  607. <div dir="ltr">
  608. <div id="x_divtagdefaultwrapper" dir="ltr" style="font-size:12pt;
  609. color:#000000; font-family:Calibri,Helvetica,sans-serif">
  610. Contenu
  611. <p style="margin-top:0; margin-bottom:0"><br>
  612. </p>
  613. <div id="x_Signature">
  614. <div id="x_divtagdefaultwrapper" dir="ltr"
  615. style="font-size:12pt; color:rgb(0,0,0);
  616. background-color:rgb(255,255,255);
  617. font-family:Calibri,Arial,Helvetica,sans-serif,&quot;EmojiFont&quot;,&quot;Apple
  618. Color Emoji&quot;,&quot;Segoe UI
  619. Emoji&quot;,NotoColorEmoji,&quot;Segoe UI
  620. Symbol&quot;,&quot;Android Emoji&quot;,EmojiSymbols">
  621. DLMQDNLQNDMLQS<br>
  622. qs<br>
  623. dqsd<br>
  624. d<br>
  625. qsd<br>
  626. </div>
  627. </div>
  628. </div>
  629. </div>
  630. '''
  631. mail = ParsedHTMLMail(text_and_quote)
  632. elements = mail.get_elements()
  633. assert len(elements) == 2
  634. assert elements[0].part_type == BodyMailPartType.Main
  635. assert elements[1].part_type == BodyMailPartType.Quote
  636. def test_other__check_outlook_com_mail_text_signature_quote(self):
  637. text_signature_quote = '''
  638. <div id="divtagdefaultwrapper"
  639. style="font-size:12pt;color:#000000;font-family:Calibri,Helvetica,sans-serif;"
  640. dir="ltr">
  641. <p style="margin-top:0;margin-bottom:0">Salut !<br>
  642. </p>
  643. <p style="margin-top:0;margin-bottom:0"><br>
  644. </p>
  645. <div id="Signature">
  646. <div id="divtagdefaultwrapper" dir="ltr" style="font-size: 12pt;
  647. color: rgb(0, 0, 0); background-color: rgb(255, 255, 255);
  648. font-family:
  649. Calibri,Arial,Helvetica,sans-serif,&quot;EmojiFont&quot;,&quot;Apple
  650. Color Emoji&quot;,&quot;Segoe UI
  651. Emoji&quot;,NotoColorEmoji,&quot;Segoe UI
  652. Symbol&quot;,&quot;Android Emoji&quot;,EmojiSymbols;">
  653. Envoyée depuis Outlook<br>
  654. </div>
  655. </div>
  656. </div>
  657. <hr style="display:inline-block;width:98%" tabindex="-1">
  658. <div id="divRplyFwdMsg" dir="ltr"><font style="font-size:11pt"
  659. color="#000000" face="Calibri, sans-serif"><b>De :</b> John Doe
  660. &lt;dev.bidule@localhost.fr&gt;<br>
  661. <b>Envoyé :</b> mardi 28 novembre 2017 12:51:42<br>
  662. <b>À :</b> John Doe<br>
  663. <b>Objet :</b> Re: Test</font>
  664. <div>&nbsp;</div>
  665. </div>
  666. <div style="background-color:#FFFFFF">
  667. <p>Coucou<br>
  668. </p>
  669. <br>
  670. <div class="x_moz-cite-prefix">Le 28/11/2017 à 12:39, John Doe a
  671. écrit&nbsp;:<br>
  672. </div>
  673. <blockquote type="cite">
  674. <div id="x_divtagdefaultwrapper" dir="ltr">
  675. <p>Test<br>
  676. </p>
  677. <p><br>
  678. </p>
  679. <div id="x_Signature">
  680. <div id="x_divtagdefaultwrapper">Envoyé à partir de <a
  681. href="http://aka.ms/weboutlook" id="LPNoLP">
  682. Outlook</a></div>
  683. </div>
  684. </div>
  685. </blockquote>
  686. <br>
  687. <div class="x_moz-signature">-- <br>
  688. TEST DE signature</div>
  689. </div>
  690. '''
  691. mail = ParsedHTMLMail(text_signature_quote)
  692. elements = mail.get_elements()
  693. assert len(elements) == 3
  694. assert elements[0].part_type == BodyMailPartType.Main
  695. assert elements[1].part_type == BodyMailPartType.Signature
  696. assert elements[2].part_type == BodyMailPartType.Quote