PURPOSE Purpose of this commit is to backport some improvements done in mail testing tools done in newer versions. Some of them are required for incoming tests to be added, other just to try to ease writing tests across versions. SPECIFICATIONS Partial backport of odoo/odoo@94208cb8b4 Improve finding outgoing mails and emails when batch methods (mass mailing) create similar emails, that can be distinguished notably by the subject in addition to from / to. Partial backport of odoo/odoo@c98f259736 Add a method to check MailMail, based on a given record. When having duplicates to differentiate in a given mailing, having just recipients is not sufficient as multiple emails may match a given recipients list. Checking model / res_id is another method for finding emails. Partial backport of odoo/odoo@a3e404e17e Add some information and values propagation in some custom asserts in mail test tooling. Partial backport of odoo/odoo@b915617569 Allow to return <mail.mail> records and found outgoing emails when using asserts. It eases doing checks in some specific tests e.g. checking notification layout usage in emails. Indeed this is quite low level and does not really deserves its own assert tooling methods. Partial backport of odoo/odoo@bbf4783ac6 Improve checks and asserts done when asserting content of produced mailing content (mails, traces, ...). Task-3438381 (TestMail: backport tools) Prepares Task-2612945 (Mail: Defensive email formatting) X-original-commit: 2bc28c804a92f206133c352c046dcdd4971fd4a6 Part-of: odoo/odoo#129839
630 lines
31 KiB
Python
630 lines
31 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import base64
|
|
import re
|
|
from ast import literal_eval
|
|
from datetime import datetime
|
|
from unittest.mock import patch
|
|
|
|
from freezegun import freeze_time
|
|
from psycopg2 import IntegrityError
|
|
from unittest.mock import patch
|
|
|
|
from odoo.addons.base.tests.test_ir_cron import CronMixinCase
|
|
from odoo.addons.mass_mailing.tests.common import MassMailCommon
|
|
from odoo.exceptions import ValidationError
|
|
from odoo.sql_db import Cursor
|
|
from odoo.tests.common import users, Form, tagged
|
|
from odoo.tools import mute_logger
|
|
|
|
BASE_64_STRING = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII='
|
|
|
|
|
|
@tagged('mass_mailing')
|
|
class TestMassMailValues(MassMailCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestMassMailValues, cls).setUpClass()
|
|
cls._create_mailing_list()
|
|
|
|
@users('user_marketing')
|
|
def test_mailing_body_cropped_vml_image(self):
|
|
""" Testing mail mailing responsive bg-image cropping for Outlook.
|
|
|
|
Outlook needs background images to be converted to VML but there is no
|
|
way to emulate `background-size: cover` that works for Windows Mail as
|
|
well. We therefore need to crop the image in the VML version to mimick
|
|
the style of other email clients.
|
|
"""
|
|
attachment = {}
|
|
def patched_get_image(self, url, session):
|
|
return base64.b64decode(BASE_64_STRING)
|
|
original_images_to_urls = self.env['mailing.mailing']._create_attachments_from_inline_images
|
|
def patched_images_to_urls(self, b64images):
|
|
urls = original_images_to_urls(b64images)
|
|
if len(urls) == 1:
|
|
(attachment_id, attachment_token) = re.search(r'/web/image/(?P<id>[0-9]+)\?access_token=(?P<token>.*)', urls[0]).groups()
|
|
attachment['id'] = attachment_id
|
|
attachment['token'] = attachment_token
|
|
return urls
|
|
else:
|
|
return []
|
|
with patch("odoo.addons.mass_mailing.models.mailing.MassMailing._get_image_by_url",
|
|
new=patched_get_image), \
|
|
patch("odoo.addons.mass_mailing.models.mailing.MassMailing._create_attachments_from_inline_images",
|
|
new=patched_images_to_urls):
|
|
mailing = self.env['mailing.mailing'].create({
|
|
'name': 'Test',
|
|
'subject': 'Test',
|
|
'state': 'draft',
|
|
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
|
'body_html': """
|
|
<html>
|
|
<!--[if mso]>
|
|
<v:image src="https://www.example.com/image" style="width:100px;height:100px;"/>
|
|
<![endif]-->
|
|
</html>
|
|
""",
|
|
})
|
|
self.assertEqual(str(mailing.body_html), f"""
|
|
<html>
|
|
<!--[if mso]>
|
|
<v:image src="/web/image/{attachment['id']}?access_token={attachment['token']}" style="width:100px;height:100px;"/>
|
|
<![endif]-->
|
|
</html>
|
|
""".strip())
|
|
|
|
@users('user_marketing')
|
|
def test_mailing_body_inline_image(self):
|
|
""" Testing mail mailing base64 image conversion to attachment.
|
|
|
|
This test ensures that the base64 images are correctly converted to
|
|
attachments, even when they appear in MSO conditional comments.
|
|
"""
|
|
attachments = []
|
|
original_images_to_urls = self.env['mailing.mailing']._create_attachments_from_inline_images
|
|
def patched_images_to_urls(self, b64images):
|
|
urls = original_images_to_urls(b64images)
|
|
for url in urls:
|
|
(attachment_id, attachment_token) = re.search(r'/web/image/(?P<id>[0-9]+)\?access_token=(?P<token>.*)', url).groups()
|
|
attachments.append({
|
|
'id': attachment_id,
|
|
'token': attachment_token,
|
|
})
|
|
return urls
|
|
with patch("odoo.addons.mass_mailing.models.mailing.MassMailing._create_attachments_from_inline_images",
|
|
new=patched_images_to_urls):
|
|
mailing = self.env['mailing.mailing'].create({
|
|
'name': 'Test',
|
|
'subject': 'Test',
|
|
'state': 'draft',
|
|
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
|
'body_html': f"""
|
|
<html><body>
|
|
<img src="data:image/png;base64,{BASE_64_STRING}0">
|
|
<img src="data:image/jpg;base64,{BASE_64_STRING}1">
|
|
<div style='color: red; background-image:url("data:image/jpg;base64,{BASE_64_STRING}2"); display: block;'/>
|
|
<div style="color: red; background-image:url('data:image/jpg;base64,{BASE_64_STRING}3'); display: block;"/>
|
|
<div style="color: red; background-image:url("data:image/jpg;base64,{BASE_64_STRING}4"); display: block;"/>
|
|
<div style="color: red; background-image:url("data:image/jpg;base64,{BASE_64_STRING}5"); display: block;"/>
|
|
<div style="color: red; background-image:url(data:image/jpg;base64,{BASE_64_STRING}6); display: block;"/>
|
|
<div style="color: red; background-image: url(data:image/jpg;base64,{BASE_64_STRING}7); background: url('data:image/jpg;base64,{BASE_64_STRING}8'); display: block;"/>
|
|
<!--[if mso]>
|
|
<img src="data:image/png;base64,{BASE_64_STRING}9">Fake url, in text: img src="data:image/png;base64,{BASE_64_STRING}"
|
|
Fake url, in text: img src="data:image/png;base64,{BASE_64_STRING}"
|
|
<img src="data:image/jpg;base64,{BASE_64_STRING}10">
|
|
<div style='color: red; background-image:url("data:image/jpg;base64,{BASE_64_STRING}11"); display: block;'>Fake url, in text: style="background-image:url('data:image/png;base64,{BASE_64_STRING}');"
|
|
Fake url, in text: style="background-image:url('data:image/png;base64,{BASE_64_STRING}');"</div>
|
|
<div style="color: red; background-image:url('data:image/jpg;base64,{BASE_64_STRING}12'); display: block;"/>
|
|
<div style="color: red; background-image:url("data:image/jpg;base64,{BASE_64_STRING}13"); display: block;"/>
|
|
<div style="color: red; background-image:url("data:image/jpg;base64,{BASE_64_STRING}14"); display: block;"/>
|
|
<div style="color: red; background-image:url(data:image/jpg;base64,{BASE_64_STRING}15); display: block;"/>
|
|
<div style="color: red; background-image: url(data:image/jpg;base64,{BASE_64_STRING}16); background: url('data:image/jpg;base64,{BASE_64_STRING}17'); display: block;"/>
|
|
<![endif]-->
|
|
</body></html>
|
|
""",
|
|
})
|
|
self.assertEqual(len(attachments), 18)
|
|
self.assertEqual(str(mailing.body_html), f"""
|
|
<html><body>
|
|
<img src="/web/image/{attachments[0]['id']}?access_token={attachments[0]['token']}">
|
|
<img src="/web/image/{attachments[1]['id']}?access_token={attachments[1]['token']}">
|
|
<div style='color: red; background-image:url("/web/image/{attachments[2]['id']}?access_token={attachments[2]['token']}"); display: block;'></div>
|
|
<div style="color: red; background-image:url('/web/image/{attachments[3]['id']}?access_token={attachments[3]['token']}'); display: block;"></div>
|
|
<div style='color: red; background-image:url("/web/image/{attachments[4]['id']}?access_token={attachments[4]['token']}"); display: block;'></div>
|
|
<div style='color: red; background-image:url("/web/image/{attachments[5]['id']}?access_token={attachments[5]['token']}"); display: block;'></div>
|
|
<div style="color: red; background-image:url(/web/image/{attachments[6]['id']}?access_token={attachments[6]['token']}); display: block;"></div>
|
|
<div style="color: red; background-image: url(/web/image/{attachments[7]['id']}?access_token={attachments[7]['token']}); background: url('/web/image/{attachments[8]['id']}?access_token={attachments[8]['token']}'); display: block;"></div>
|
|
<!--[if mso]>
|
|
<img src="/web/image/{attachments[9]['id']}?access_token={attachments[9]['token']}">Fake url, in text: img src="data:image/png;base64,{BASE_64_STRING}"
|
|
Fake url, in text: img src="data:image/png;base64,{BASE_64_STRING}"
|
|
<img src="/web/image/{attachments[10]['id']}?access_token={attachments[10]['token']}">
|
|
<div style='color: red; background-image:url("/web/image/{attachments[11]['id']}?access_token={attachments[11]['token']}"); display: block;'>Fake url, in text: style="background-image:url('data:image/png;base64,{BASE_64_STRING}');"
|
|
Fake url, in text: style="background-image:url('data:image/png;base64,{BASE_64_STRING}');"</div>
|
|
<div style="color: red; background-image:url('/web/image/{attachments[12]['id']}?access_token={attachments[12]['token']}'); display: block;"/>
|
|
<div style="color: red; background-image:url("/web/image/{attachments[13]['id']}?access_token={attachments[13]['token']}"); display: block;"/>
|
|
<div style="color: red; background-image:url("/web/image/{attachments[14]['id']}?access_token={attachments[14]['token']}"); display: block;"/>
|
|
<div style="color: red; background-image:url(/web/image/{attachments[15]['id']}?access_token={attachments[15]['token']}); display: block;"/>
|
|
<div style="color: red; background-image: url(/web/image/{attachments[16]['id']}?access_token={attachments[16]['token']}); background: url('/web/image/{attachments[17]['id']}?access_token={attachments[17]['token']}'); display: block;"/>
|
|
<![endif]-->
|
|
</body></html>
|
|
""".strip())
|
|
|
|
@users('user_marketing')
|
|
def test_mailing_body_responsive(self):
|
|
""" Testing mail mailing responsive mail body
|
|
|
|
Reference: https://litmus.com/community/learning/24-how-to-code-a-responsive-email-from-scratch
|
|
https://www.campaignmonitor.com/css/link-element/link-in-head/
|
|
|
|
This template is meant to put inline CSS into an email's head
|
|
"""
|
|
recipient = self.env['res.partner'].create({
|
|
'name': 'Mass Mail Partner',
|
|
'email': 'Customer <test.customer@example.com>',
|
|
})
|
|
mailing = self.env['mailing.mailing'].create({
|
|
'name': 'Test',
|
|
'subject': 'Test',
|
|
'state': 'draft',
|
|
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
|
})
|
|
|
|
composer = self.env['mail.compose.message'].with_user(self.user_marketing).with_context({
|
|
'default_composition_mode': 'mass_mail',
|
|
'default_model': 'res.partner',
|
|
'default_res_ids': recipient.ids,
|
|
}).create({
|
|
'subject': 'Mass Mail Responsive',
|
|
'body': 'I am Responsive body',
|
|
'mass_mailing_id': mailing.id
|
|
})
|
|
|
|
mail_values = composer._prepare_mail_values([recipient.id])
|
|
body_html = mail_values[recipient.id]['body_html']
|
|
|
|
self.assertIn('<!DOCTYPE html>', body_html)
|
|
self.assertIn('<head>', body_html)
|
|
self.assertIn('viewport', body_html)
|
|
# This is important: we need inline css, and not <link/>
|
|
self.assertIn('@media', body_html)
|
|
self.assertIn('I am Responsive body', body_html)
|
|
|
|
@users('user_marketing')
|
|
def test_mailing_computed_fields(self):
|
|
# Create on res.partner, with default values for computed fields
|
|
mailing = self.env['mailing.mailing'].create({
|
|
'name': 'TestMailing',
|
|
'subject': 'Test',
|
|
'mailing_type': 'mail',
|
|
'body_html': '<p>Hello <t t-out="object.name"/></p>',
|
|
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
|
})
|
|
self.assertEqual(mailing.user_id, self.user_marketing)
|
|
self.assertEqual(mailing.medium_id, self.env.ref('utm.utm_medium_email'))
|
|
self.assertEqual(mailing.mailing_model_name, 'res.partner')
|
|
self.assertEqual(mailing.mailing_model_real, 'res.partner')
|
|
self.assertEqual(mailing.reply_to_mode, 'new')
|
|
self.assertEqual(mailing.reply_to, self.user_marketing.email_formatted)
|
|
# default for partner: remove blacklisted
|
|
self.assertEqual(literal_eval(mailing.mailing_domain), [('is_blacklisted', '=', False)])
|
|
# update domain
|
|
mailing.write({
|
|
'mailing_domain': [('email', 'ilike', 'test.example.com')]
|
|
})
|
|
self.assertEqual(literal_eval(mailing.mailing_domain), [('email', 'ilike', 'test.example.com')])
|
|
|
|
# reset mailing model -> reset domain; set reply_to -> keep it
|
|
mailing.write({
|
|
'mailing_model_id': self.env['ir.model']._get('mailing.list').id,
|
|
'reply_to': self.email_reply_to,
|
|
})
|
|
self.assertEqual(mailing.mailing_model_name, 'mailing.list')
|
|
self.assertEqual(mailing.mailing_model_real, 'mailing.contact')
|
|
self.assertEqual(mailing.reply_to_mode, 'new')
|
|
self.assertEqual(mailing.reply_to, self.email_reply_to)
|
|
# default for mailing list: depends upon contact_list_ids
|
|
self.assertEqual(literal_eval(mailing.mailing_domain), [('list_ids', 'in', [])])
|
|
mailing.write({
|
|
'contact_list_ids': [(4, self.mailing_list_1.id), (4, self.mailing_list_2.id)]
|
|
})
|
|
self.assertEqual(literal_eval(mailing.mailing_domain), [('list_ids', 'in', (self.mailing_list_1 | self.mailing_list_2).ids)])
|
|
|
|
# reset mailing model -> reset domain and reply to mode
|
|
mailing.write({
|
|
'mailing_model_id': self.env['ir.model']._get('discuss.channel').id,
|
|
})
|
|
self.assertEqual(mailing.mailing_model_name, 'discuss.channel')
|
|
self.assertEqual(mailing.mailing_model_real, 'discuss.channel')
|
|
self.assertEqual(mailing.reply_to_mode, 'update')
|
|
self.assertFalse(mailing.reply_to)
|
|
|
|
@users('user_marketing')
|
|
def test_mailing_computed_fields_domain_w_filter(self):
|
|
""" Test domain update, involving mailing.filters added in 15.1. """
|
|
# Create on res.partner, with default values for computed fields
|
|
mailing = self.env['mailing.mailing'].create({
|
|
'name': 'TestMailing',
|
|
'subject': 'Test',
|
|
'mailing_type': 'mail',
|
|
'body_html': '<p>Hello <t t-out="object.name"/></p>',
|
|
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
|
})
|
|
# default for partner: remove blacklisted
|
|
self.assertEqual(literal_eval(mailing.mailing_domain), [('is_blacklisted', '=', False)])
|
|
|
|
# prepare initial data
|
|
filter_1, filter_2, filter_3 = self.env['mailing.filter'].create([
|
|
{'name': 'General channel',
|
|
'mailing_domain' : [('name', '=', 'general')],
|
|
'mailing_model_id': self.env['ir.model']._get('discuss.channel').id,
|
|
},
|
|
{'name': 'LLN City',
|
|
'mailing_domain' : [('city', 'ilike', 'LLN')],
|
|
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
|
},
|
|
{'name': 'Email based',
|
|
'mailing_domain' : [('email', 'ilike', 'info@odoo.com')],
|
|
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
|
}
|
|
])
|
|
|
|
# check that adding mailing_filter_id updates domain correctly
|
|
mailing.mailing_filter_id = filter_2
|
|
self.assertEqual(literal_eval(mailing.mailing_domain), literal_eval(filter_2.mailing_domain))
|
|
|
|
# cannot set a filter linked to another model
|
|
with self.assertRaises(ValidationError):
|
|
mailing.mailing_filter_id = filter_1
|
|
|
|
# resetting model should reset domain, even if filter was chosen previously
|
|
mailing.mailing_model_id = self.env['ir.model']._get('discuss.channel').id
|
|
self.assertEqual(literal_eval(mailing.mailing_domain), [])
|
|
|
|
# changing the filter should update the mailing domain correctly
|
|
mailing.mailing_filter_id = filter_1
|
|
self.assertEqual(literal_eval(mailing.mailing_domain), literal_eval(filter_1.mailing_domain))
|
|
|
|
# changing the domain should not empty the mailing_filter_id
|
|
mailing.mailing_domain = "[('email', 'ilike', 'info_be@odoo.com')]"
|
|
self.assertEqual(mailing.mailing_filter_id, filter_1, "Filter should not be unset even if domain is changed")
|
|
|
|
# deleting the filter record should not delete the domain on mailing
|
|
mailing.mailing_model_id = self.env['ir.model']._get('res.partner').id
|
|
mailing.mailing_filter_id = filter_3
|
|
filter_3_domain = filter_3.mailing_domain
|
|
self.assertEqual(literal_eval(mailing.mailing_domain), literal_eval(filter_3_domain))
|
|
filter_3.unlink() # delete the filter record
|
|
self.assertFalse(mailing.mailing_filter_id, "Should unset filter if it is deleted")
|
|
self.assertEqual(literal_eval(mailing.mailing_domain), literal_eval(filter_3_domain), "Should still have the same domain")
|
|
|
|
@users('user_marketing')
|
|
def test_mailing_computed_fields_default(self):
|
|
mailing = self.env['mailing.mailing'].with_context(
|
|
default_mailing_domain=repr([('email', 'ilike', 'test.example.com')])
|
|
).create({
|
|
'name': 'TestMailing',
|
|
'subject': 'Test',
|
|
'mailing_type': 'mail',
|
|
'body_html': '<p>Hello <t t-out="object.name"/></p>',
|
|
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
|
})
|
|
self.assertEqual(literal_eval(mailing.mailing_domain), [('email', 'ilike', 'test.example.com')])
|
|
|
|
@users('user_marketing')
|
|
def test_mailing_computed_fields_form(self):
|
|
mailing_form = Form(self.env['mailing.mailing'].with_context(
|
|
default_mailing_domain="[('email', 'ilike', 'test.example.com')]",
|
|
default_mailing_model_id=self.env['ir.model']._get('res.partner').id,
|
|
))
|
|
self.assertEqual(
|
|
literal_eval(mailing_form.mailing_domain),
|
|
[('email', 'ilike', 'test.example.com')],
|
|
)
|
|
self.assertEqual(mailing_form.mailing_model_real, 'res.partner')
|
|
|
|
@mute_logger('odoo.sql_db')
|
|
@users('user_marketing')
|
|
def test_mailing_trace_values(self):
|
|
recipient = self.partner_employee
|
|
|
|
# both void and 0 are invalid, document should have an id != 0
|
|
with self.assertRaises(IntegrityError):
|
|
self.env['mailing.trace'].create({
|
|
'model': recipient._name,
|
|
})
|
|
with self.assertRaises(IntegrityError):
|
|
self.env['mailing.trace'].create({
|
|
'model': recipient._name,
|
|
'res_id': 0,
|
|
})
|
|
with self.assertRaises(IntegrityError):
|
|
self.env['mailing.trace'].create({
|
|
'res_id': 3,
|
|
})
|
|
|
|
activity = self.env['mailing.trace'].create({
|
|
'model': recipient._name,
|
|
'res_id': recipient.id,
|
|
})
|
|
with self.assertRaises(IntegrityError):
|
|
activity.write({'model': False})
|
|
self.env.flush_all()
|
|
with self.assertRaises(IntegrityError):
|
|
activity.write({'res_id': False})
|
|
self.env.flush_all()
|
|
with self.assertRaises(IntegrityError):
|
|
activity.write({'res_id': 0})
|
|
self.env.flush_all()
|
|
|
|
@freeze_time('2022-01-02')
|
|
@patch.object(Cursor, 'now', lambda *args, **kwargs: datetime(2022, 1, 2))
|
|
@users('user_marketing')
|
|
def test_mailing_unique_name(self):
|
|
"""Test that the names are generated and unique for each mailing.
|
|
|
|
If the name is missing, it's generated from the subject. Then we should ensure
|
|
that this generated name is unique.
|
|
"""
|
|
mailing_0 = self.env['mailing.mailing'].create({'subject': 'First subject'})
|
|
|
|
mailing_1, mailing_2, mailing_3, mailing_4, mailing_5, mailing_6 = self.env['mailing.mailing'].create([{
|
|
'subject': 'First subject',
|
|
}, {
|
|
'subject': 'First subject',
|
|
}, {
|
|
'subject': 'First subject',
|
|
'source_id': self.env['utm.source'].create({'name': 'Custom Source'}).id,
|
|
}, {
|
|
'subject': 'First subject',
|
|
'name': 'Mailing',
|
|
}, {
|
|
'subject': 'Second subject',
|
|
'name': 'Mailing',
|
|
}, {
|
|
'subject': 'Second subject',
|
|
}])
|
|
|
|
self.assertEqual(mailing_0.name, 'First subject (Mass Mailing created on 2022-01-02)')
|
|
self.assertEqual(mailing_1.name, 'First subject (Mass Mailing created on 2022-01-02) [2]')
|
|
self.assertEqual(mailing_2.name, 'First subject (Mass Mailing created on 2022-01-02) [3]')
|
|
self.assertEqual(mailing_3.name, 'Custom Source')
|
|
self.assertEqual(mailing_4.name, 'Mailing')
|
|
self.assertEqual(mailing_5.name, 'Mailing [2]')
|
|
self.assertEqual(mailing_6.name, 'Second subject (Mass Mailing created on 2022-01-02)')
|
|
|
|
mailing_0.subject = 'First subject'
|
|
self.assertEqual(mailing_0.name, 'First subject (Mass Mailing created on 2022-01-02) [4]',
|
|
msg='The name must have been re-generated')
|
|
|
|
mailing_0.name = 'Second subject (Mass Mailing created on 2022-01-02)'
|
|
self.assertEqual(mailing_0.name, 'Second subject (Mass Mailing created on 2022-01-02) [2]',
|
|
msg='The name must be unique')
|
|
|
|
|
|
@tagged('mass_mailing')
|
|
class TestMassMailFeatures(MassMailCommon, CronMixinCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestMassMailFeatures, cls).setUpClass()
|
|
cls._create_mailing_list()
|
|
|
|
@users('user_marketing')
|
|
@mute_logger('odoo.addons.mail.models.mail_mail')
|
|
def test_mailing_cron_trigger(self):
|
|
""" Technical test to ensure the cron is triggered at the correct
|
|
time """
|
|
|
|
cron_id = self.env.ref('mass_mailing.ir_cron_mass_mailing_queue').id
|
|
partner = self.env['res.partner'].create({
|
|
'name': 'Jean-Alphonce',
|
|
'email': 'jeanalph@example.com',
|
|
})
|
|
common_mailing_values = {
|
|
'name': 'Knock knock',
|
|
'subject': "Who's there?",
|
|
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
|
'mailing_domain': [('id', '=', partner.id)],
|
|
'body_html': 'The marketing mailing test.',
|
|
'schedule_type': 'scheduled',
|
|
}
|
|
|
|
now = datetime(2021, 2, 5, 16, 43, 20)
|
|
then = datetime(2021, 2, 7, 12, 0, 0)
|
|
|
|
with freeze_time(now):
|
|
for (test, truth) in [(False, now), (then, then)]:
|
|
with self.subTest(schedule_date=test):
|
|
with self.capture_triggers(cron_id) as capt:
|
|
mailing = self.env['mailing.mailing'].create({
|
|
**common_mailing_values,
|
|
'schedule_date': test,
|
|
})
|
|
mailing.action_put_in_queue()
|
|
capt.records.ensure_one()
|
|
self.assertLessEqual(capt.records.call_at, truth)
|
|
|
|
@users('user_marketing')
|
|
@mute_logger('odoo.addons.mail.models.mail_mail')
|
|
def test_mailing_deletion(self):
|
|
""" Test deletion in various use case, depending on reply-to """
|
|
# 1- Keep archives and reply-to set to 'answer = new thread'
|
|
mailing = self.env['mailing.mailing'].create({
|
|
'name': 'TestSource',
|
|
'subject': 'TestDeletion',
|
|
'body_html': "<div>Hello {object.name}</div>",
|
|
'mailing_model_id': self.env['ir.model']._get('mailing.list').id,
|
|
'contact_list_ids': [(6, 0, self.mailing_list_1.ids)],
|
|
'keep_archives': True,
|
|
'reply_to_mode': 'new',
|
|
'reply_to': self.email_reply_to,
|
|
})
|
|
self.assertEqual(self.mailing_list_1.contact_ids.message_ids, self.env['mail.message'])
|
|
|
|
with self.mock_mail_gateway(mail_unlink_sent=True):
|
|
mailing.action_send_mail()
|
|
|
|
self.assertEqual(len(self._mails), 3)
|
|
self.assertEqual(len(self._new_mails.exists()), 3)
|
|
self.assertEqual(len(self.mailing_list_1.contact_ids.message_ids), 3)
|
|
|
|
# 2- Keep archives and reply-to set to 'answer = update thread'
|
|
self.mailing_list_1.contact_ids.message_ids.unlink()
|
|
mailing = mailing.copy()
|
|
mailing.write({
|
|
'reply_to_mode': 'update',
|
|
})
|
|
self.assertEqual(self.mailing_list_1.contact_ids.message_ids, self.env['mail.message'])
|
|
|
|
with self.mock_mail_gateway(mail_unlink_sent=True):
|
|
mailing.action_send_mail()
|
|
|
|
self.assertEqual(len(self._mails), 3)
|
|
self.assertEqual(len(self._new_mails.exists()), 3)
|
|
self.assertEqual(len(self.mailing_list_1.contact_ids.message_ids), 3)
|
|
|
|
# 3- Remove archives and reply-to set to 'answer = new thread'
|
|
self.mailing_list_1.contact_ids.message_ids.unlink()
|
|
mailing = mailing.copy()
|
|
mailing.write({
|
|
'keep_archives': False,
|
|
'reply_to_mode': 'new',
|
|
'reply_to': self.email_reply_to,
|
|
})
|
|
self.assertEqual(self.mailing_list_1.contact_ids.message_ids, self.env['mail.message'])
|
|
|
|
with self.mock_mail_gateway(mail_unlink_sent=True):
|
|
mailing.action_send_mail()
|
|
|
|
self.assertEqual(len(self._mails), 3)
|
|
self.assertEqual(len(self._new_mails.exists()), 0)
|
|
self.assertEqual(self.mailing_list_1.contact_ids.message_ids, self.env['mail.message'])
|
|
|
|
# 4- Remove archives and reply-to set to 'answer = update thread'
|
|
# Imply keeping mail.message for gateway reply)
|
|
self.mailing_list_1.contact_ids.message_ids.unlink()
|
|
mailing = mailing.copy()
|
|
mailing.write({
|
|
'keep_archives': False,
|
|
'reply_to_mode': 'update',
|
|
})
|
|
self.assertEqual(self.mailing_list_1.contact_ids.message_ids, self.env['mail.message'])
|
|
|
|
with self.mock_mail_gateway(mail_unlink_sent=True):
|
|
mailing.action_send_mail()
|
|
|
|
self.assertEqual(len(self._mails), 3)
|
|
self.assertEqual(len(self._new_mails.exists()), 0)
|
|
self.assertEqual(len(self.mailing_list_1.contact_ids.message_ids), 3)
|
|
|
|
@users('user_marketing')
|
|
@mute_logger('odoo.addons.mail.models.mail_mail')
|
|
def test_mailing_on_res_partner(self):
|
|
""" Test mailing on res.partner model: ensure default recipients are
|
|
correctly computed """
|
|
partner_a = self.env['res.partner'].create({
|
|
'name': 'test email 1',
|
|
'email': 'test1@example.com',
|
|
})
|
|
partner_b = self.env['res.partner'].create({
|
|
'name': 'test email 2',
|
|
'email': 'test2@example.com',
|
|
})
|
|
self.env['mail.blacklist'].create({'email': 'Test2@example.com',})
|
|
|
|
mailing = self.env['mailing.mailing'].create({
|
|
'name': 'One',
|
|
'subject': 'One',
|
|
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
|
'mailing_domain': [('id', 'in', (partner_a | partner_b).ids)],
|
|
'body_html': 'This is mass mail marketing demo'
|
|
})
|
|
mailing.action_put_in_queue()
|
|
with self.mock_mail_gateway(mail_unlink_sent=False):
|
|
mailing._process_mass_mailing_queue()
|
|
|
|
self.assertMailTraces(
|
|
[{'partner': partner_a},
|
|
{'partner': partner_b, 'trace_status': 'cancel', 'failure_type': 'mail_bl'}],
|
|
mailing, partner_a + partner_b, check_mail=True
|
|
)
|
|
|
|
@users('user_marketing')
|
|
@mute_logger('odoo.addons.mail.models.mail_mail')
|
|
def test_mailing_shortener(self):
|
|
mailing = self.env['mailing.mailing'].create({
|
|
'name': 'TestSource',
|
|
'subject': 'TestShortener',
|
|
'body_html': """<div>
|
|
Hi,
|
|
<t t-set="url" t-value="'www.odoo.com'"/>
|
|
<t t-set="httpurl" t-value="'https://www.odoo.eu'"/>
|
|
Website0: <a id="url0" t-attf-href="https://www.odoo.tz/my/{{object.name}}">https://www.odoo.tz/my/<t t-esc="object.name"/></a>
|
|
Website1: <a id="url1" href="https://www.odoo.be">https://www.odoo.be</a>
|
|
Website2: <a id="url2" t-attf-href="https://{{url}}">https://<t t-esc="url"/></a>
|
|
Website3: <a id="url3" t-att-href="httpurl"><t t-esc="httpurl"/></a>
|
|
External1: <a id="url4" href="https://www.example.com/foo/bar?baz=qux">Youpie</a>
|
|
Email: <a id="url5" href="mailto:test@odoo.com">test@odoo.com</a></div>""",
|
|
'mailing_model_id': self.env['ir.model']._get('mailing.list').id,
|
|
'reply_to_mode': 'new',
|
|
'reply_to': self.email_reply_to,
|
|
'contact_list_ids': [(6, 0, self.mailing_list_1.ids)],
|
|
'keep_archives': True,
|
|
})
|
|
|
|
mailing.action_put_in_queue()
|
|
|
|
with self.mock_mail_gateway(mail_unlink_sent=False):
|
|
mailing._process_mass_mailing_queue()
|
|
|
|
self.assertMailTraces(
|
|
[{'email': 'fleurus@example.com'},
|
|
{'email': 'gorramts@example.com'},
|
|
{'email': 'ybrant@example.com'}],
|
|
mailing, self.mailing_list_1.contact_ids, check_mail=True
|
|
)
|
|
|
|
for contact in self.mailing_list_1.contact_ids:
|
|
new_mail = self._find_mail_mail_wrecord(contact)
|
|
for link_info in [('url0', 'https://www.odoo.tz/my/%s' % contact.name, True),
|
|
('url1', 'https://www.odoo.be', True),
|
|
('url2', 'https://www.odoo.com', True),
|
|
('url3', 'https://www.odoo.eu', True),
|
|
('url4', 'https://www.example.com/foo/bar?baz=qux', True),
|
|
('url5', 'mailto:test@odoo.com', False)]:
|
|
# TDE FIXME: why going to mail message id ? mail.body_html seems to fail, check
|
|
link_params = {'utm_medium': 'Email', 'utm_source': mailing.name}
|
|
if link_info[0] == 'url4':
|
|
link_params['baz'] = 'qux'
|
|
self.assertLinkShortenedHtml(
|
|
new_mail.mail_message_id.body,
|
|
link_info,
|
|
link_params=link_params,
|
|
)
|
|
|
|
class TestMailingScheduleDateWizard(MassMailCommon):
|
|
|
|
@mute_logger('odoo.addons.mail.models.mail_mail')
|
|
@users('user_marketing')
|
|
def test_mailing_schedule_date(self):
|
|
mailing = self.env['mailing.mailing'].create({
|
|
'name': 'mailing',
|
|
'subject': 'some subject'
|
|
})
|
|
# create a schedule date wizard
|
|
wizard_form = Form(
|
|
self.env['mailing.mailing.schedule.date'].with_context(default_mass_mailing_id=mailing.id))
|
|
|
|
# set a schedule date
|
|
wizard_form.schedule_date = datetime(2021, 4, 30, 9, 0)
|
|
wizard = wizard_form.save()
|
|
wizard.action_schedule_date()
|
|
|
|
# assert that the schedule_date and schedule_type fields are correct and that the mailing is put in queue
|
|
self.assertEqual(mailing.schedule_date, datetime(2021, 4, 30, 9, 0))
|
|
self.assertEqual(mailing.schedule_type, 'scheduled')
|
|
self.assertEqual(mailing.state, 'in_queue')
|