summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorThibault Delavallée <tde@odoo.com>2021-04-07 10:09:45 +0000
committerThibault Delavallée <tde@odoo.com>2021-04-08 15:06:04 +0000
commit51700491ae3b26d513ddfeda378a60592fca6eab (patch)
tree87eb7fb37552dc3369c97212283a3f02e4449531 /addons
parentd4206d4a4a4cc527351a1c9cea0af0cf53dfd500 (diff)
[MOV] mail, test_mail: merge and improve composer tests
This commits is a bit a followup of odoo/odoo#62061 where new composer tests were added. This commit finalize this work by merging all composer related tests within the same file. It also backports some of tooling update done in odoo/odoo#64459 or odoo/odoo#63677 in order to have coherent naming through 14+ versions. Some additional or deeper tests are added. Notably it now more cleanly uses asserts for created mail.mail and sent emails. Task ID-2500615 COM PR odoo/odoo#68874
Diffstat (limited to 'addons')
-rw-r--r--addons/mail/tests/common.py50
-rw-r--r--addons/test_mail/tests/__init__.py1
-rw-r--r--addons/test_mail/tests/test_mail_composer.py499
-rw-r--r--addons/test_mail/tests/test_message_composer.py345
4 files changed, 538 insertions, 357 deletions
diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py
index 3e1dd62994d..25d1768e4a0 100644
--- a/addons/mail/tests/common.py
+++ b/addons/mail/tests/common.py
@@ -189,7 +189,7 @@ class MockEmail(common.BaseCase):
if all(p in mail.recipient_ids for p in recipients):
break
else:
- raise AssertionError('mail.mail not found for message %s / status %s / recipients %s' % (mail_message, status, recipients.ids))
+ raise AssertionError('mail.mail not found for message %s / status %s / recipients %s / author %s' % (mail_message, status, recipients.ids, author))
return mail
def _find_mail_mail_wemail(self, email_to, status, mail_message=None, author=None):
@@ -218,13 +218,21 @@ class MockEmail(common.BaseCase):
mail = self._find_mail(author, recipients, mail_message)
self.assertEqual(mail.state, 'exception')
- def assertMailMail(self, recipients, status, check_mail_mail=True, mail_message=None, author=None, email_values=None):
+ def assertMailMail(self, recipients, status,
+ check_mail_mail=True, mail_message=None, author=None,
+ content=None, fields_values=None, email_values=None):
if check_mail_mail:
- mail = self._find_mail_mail_wpartners(recipients, status, mail_message=mail_message, author=author)
- self.assertTrue(bool(mail))
+ found_mail = self._find_mail_mail_wpartners(recipients, status, mail_message=mail_message, author=author)
+ self.assertTrue(bool(found_mail))
+ if content:
+ self.assertIn(content, found_mail.body_html)
+ for fname, fvalue in (fields_values or {}).items():
+ self.assertEqual(
+ found_mail[fname], fvalue,
+ 'Mail: expected %s for %s, got %s' % (fvalue, fname, found_mail[fname]))
if status == 'sent':
for recipient in recipients:
- self.assertSentEmail(author, [recipient], **email_values)
+ self.assertSentEmail(email_values['email_from'] if email_values and email_values.get('email_from') else author, [recipient], **(email_values or {}))
def assertMailMailWEmails(self, emails, status, content, fields_values=None):
""" Will check in self._new_mails to find a sent mail.mail. To use with
@@ -296,6 +304,15 @@ class MockEmail(common.BaseCase):
for val in ['reply_to', 'subject', 'references', 'attachments']:
if val in expected:
self.assertEqual(expected[val], sent_mail[val], 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val]))
+ if 'attachments_info' in values:
+ attachments = sent_mail['attachments']
+ for attachment_info in values['attachments_info']:
+ attachment = next(attach for attach in attachments if attach[0] == attachment_info['name'])
+ if attachment_info.get('raw'):
+ self.assertEqual(attachment[1], attachment_info['raw'])
+ if attachment_info.get('type'):
+ self.assertEqual(attachment[2], attachment_info['type'])
+ self.assertEqual(len(values['attachments_info']), len(attachments))
for val in ['body']:
if val in expected:
self.assertHtmlEqual(expected[val], sent_mail[val], 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val]))
@@ -649,6 +666,29 @@ class MailCase(MockEmail):
return bus_notifs
+ def assertNotified(self, message, recipients_info, is_complete=False):
+ """ Lightweight check for notifications (mail.notification).
+
+ :param recipients_info: list notified recipients: [
+ {'partner': res.partner record (may be empty),
+ 'type': notification_type to check,
+ 'is_read': is_read to check,
+ }, {...}]
+ """
+ notifications = self._new_notifs.filtered(lambda notif: notif in message.notification_ids)
+ if is_complete:
+ self.assertEqual(len(notifications), len(recipients_info))
+ for rinfo in recipients_info:
+ recipient_notif = next(
+ (notif
+ for notif in notifications
+ if notif.res_partner_id == rinfo['partner']
+ ), False
+ )
+ self.assertTrue(recipient_notif)
+ self.assertEqual(recipient_notif.is_read, rinfo['is_read'])
+ self.assertEqual(recipient_notif.notification_type, rinfo['type'])
+
def assertTracking(self, message, data):
tracking_values = message.sudo().tracking_value_ids
for field_name, value_type, old_value, new_value in data:
diff --git a/addons/test_mail/tests/__init__.py b/addons/test_mail/tests/__init__.py
index 3c362ee53b9..fb4c72db5cb 100644
--- a/addons/test_mail/tests/__init__.py
+++ b/addons/test_mail/tests/__init__.py
@@ -12,7 +12,6 @@ from . import test_mail_channel_partner
from . import test_mail_gateway
from . import test_mail_template
from . import test_mail_thread_internals
-from . import test_message_composer
from . import test_message_management
from . import test_message_post
from . import test_message_track
diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py
index 589f4105176..e22a875a5a6 100644
--- a/addons/test_mail/tests/test_mail_composer.py
+++ b/addons/test_mail/tests/test_mail_composer.py
@@ -3,7 +3,10 @@
import base64
+from unittest.mock import patch
+
from odoo.addons.mail.tests.common import mail_new_test_user
+from odoo.addons.test_mail.models.test_mail_models import MailTestTicket
from odoo.addons.test_mail.tests.common import TestMailCommon, TestRecipients
from odoo.tests import tagged
from odoo.tests.common import users, Form
@@ -18,9 +21,14 @@ class TestMailComposer(TestMailCommon, TestRecipients):
super(TestMailComposer, cls).setUpClass()
cls._init_mail_gateway()
+ # ensure employee can create partners, necessary for templates
+ cls.user_employee.write({
+ 'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)],
+ })
+
cls.user_employee_2 = mail_new_test_user(
cls.env, login='employee2', groups='base.group_user',
- notification_type='inbox', email='eglantine@example.com',
+ notification_type='email', email='eglantine@example.com',
name='Eglantine Employee', signature='--\nEglantine')
cls.partner_employee_2 = cls.user_employee_2.partner_id
@@ -37,6 +45,7 @@ class TestMailComposer(TestMailCommon, TestRecipients):
'report_type': 'qweb-pdf',
'report_name': 'test_mail.mail_test_ticket_test_template',
})
+ cls.test_record_report = cls.test_report._render_qweb_pdf(cls.test_report.ids)
cls.test_from = '"John Doe" <john@example.com>'
@@ -54,9 +63,10 @@ class TestMailComposer(TestMailCommon, TestRecipients):
'body_html': '<p>TemplateBody ${object.name}</p>',
'partner_to': '${object.customer_id.id if object.customer_id else ""}',
'email_to': '${(object.email_from if not object.customer_id else "") | safe}',
- 'email_from': '${object.user_id.email_formatted | safe}',
+ 'email_from': '${(object.user_id.email_formatted or user.email_formatted) | safe}',
'model_id': cls.env['ir.model']._get('mail.test.ticket').id,
'mail_server_id': cls.mail_server.id,
+ 'auto_delete': True,
})
def _generate_attachments_data(self, count):
@@ -102,7 +112,59 @@ class TestComposerForm(TestMailComposer):
# self.assertEqual(composer_form.record_name, self.test_record.name, 'MailComposer: comment mode should compute record name')
self.assertFalse(composer_form.no_auto_thread)
self.assertEqual(composer_form.composition_mode, 'comment')
- self.assertEqual(composer_form.model, self.test_records._name)
+ self.assertEqual(composer_form.model, self.test_record._name)
+
+ @users('employee')
+ def test_mail_composer_comment_attachments(self):
+ """Tests that all attachments are added to the composer, static attachments
+ are not duplicated and while reports are re-generated, and that intermediary
+ attachments are dropped."""
+ attachment_data = self._generate_attachments_data(2)
+ template_1 = self.template.copy({
+ 'attachment_ids': [(0, 0, a) for a in attachment_data],
+ 'report_name': 'TestReport for ${object.name}.html', # test cursor forces html
+ 'report_template': self.test_report.id,
+ })
+ template_1_attachments = template_1.attachment_ids
+ self.assertEqual(len(template_1_attachments), 2)
+ template_2 = self.template.copy({
+ 'attachment_ids': False,
+ 'report_template': self.test_report.id,
+ })
+
+ # begins without attachments
+ composer_form = Form(self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_record, add_web=True, default_attachment_ids=[])
+ ))
+ self.assertEqual(len(composer_form.attachment_ids), 0)
+
+ # change template: 2 static (attachment_ids) and 1 dynamic (report)
+ composer_form.template_id = template_1
+ self.assertEqual(len(composer_form.attachment_ids), 3)
+ report_attachments = [att for att in composer_form.attachment_ids if att not in template_1_attachments]
+ self.assertEqual(len(report_attachments), 1)
+ tpl_attachments = composer_form.attachment_ids[:] - report_attachments[0]
+ self.assertEqual(tpl_attachments, template_1_attachments)
+
+ # change template: 0 static (attachment_ids) and 1 dynamic (report)
+ composer_form.template_id = template_2
+ self.assertEqual(len(composer_form.attachment_ids), 1)
+ report_attachments = [att for att in composer_form.attachment_ids if att not in template_1_attachments]
+ self.assertEqual(len(report_attachments), 1)
+ tpl_attachments = composer_form.attachment_ids[:] - report_attachments[0]
+ self.assertEqual(tpl_attachments, self.env['ir.attachment'])
+
+ # change back to template 1
+ composer_form.template_id = template_1
+ self.assertEqual(len(composer_form.attachment_ids), 3)
+ report_attachments = [att for att in composer_form.attachment_ids if att not in template_1_attachments]
+ self.assertEqual(len(report_attachments), 1)
+ tpl_attachments = composer_form.attachment_ids[:] - report_attachments[0]
+ self.assertEqual(tpl_attachments, template_1_attachments)
+
+ # reset template
+ composer_form.template_id = self.env['mail.template']
+ self.assertEqual(len(composer_form.attachment_ids), 0)
@users('employee')
def test_mail_composer_mass(self):
@@ -139,6 +201,7 @@ class TestComposerInternals(TestMailComposer):
attachment_data = self._generate_attachments_data(3)
self.template.write({
'attachment_ids': [(0, 0, a) for a in attachment_data],
+ 'report_name': 'TestReport for ${object.name}.html', # test cursor forces html
'report_template': self.test_report.id,
})
attachs = self.env['ir.attachment'].search([('name', 'in', [a['name'] for a in attachment_data])])
@@ -161,6 +224,7 @@ class TestComposerInternals(TestMailComposer):
self.assertIn(attach, composer.attachment_ids)
generated = composer.attachment_ids - attachs
self.assertEqual(len(generated), 1, 'MailComposer: should have 1 additional attachment for report')
+ self.assertEqual(generated.name, 'TestReport for %s.html' % self.test_record.name)
self.assertEqual(generated.res_model, 'mail.compose.message')
self.assertEqual(generated.res_id, 0)
@@ -207,9 +271,9 @@ class TestComposerInternals(TestMailComposer):
self.assertEqual(composer.email_from, self.test_from)
@users('employee')
- def test_mail_composer_content_comment(self):
- """ Test content management (subject, body, server) in comment mode.
- Template update is also tested. """
+ def test_mail_composer_content(self):
+ """ Test content management (subject, body, server) in both comment and
+ mass mailing mode. Template update is also tested. """
for composition_mode in ['comment', 'mass_mail']:
if composition_mode == 'comment':
ctx = self._get_web_context(self.test_record, add_web=False)
@@ -314,3 +378,426 @@ class TestComposerInternals(TestMailComposer):
self.assertEqual(composer.subject, 'My amazing subject')
self.assertEqual(composer.body, '<p>Test Body</p>')
self.assertEqual(composer.mail_server_id.id, False)
+
+ @users('employee')
+ @mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail')
+ def test_mail_composer_parent(self):
+ """ Test specific management in comment mode when having parent_id set:
+ record_name, subject, parent's partners. """
+ parent = self.test_record.message_post(body='Test', partner_ids=(self.partner_1 + self.partner_2).ids)
+
+ composer = self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_record, add_web=False, default_parent_id=parent.id)
+ ).create({
+ 'body': '<p>Test Body</p>',
+ })
+
+ # creation values taken from parent
+ self.assertEqual(composer.subject, 'Re: %s' % self.test_record.name)
+ self.assertEqual(composer.body, '<p>Test Body</p>')
+ self.assertEqual(composer.partner_ids, self.partner_1 + self.partner_2)
+
+ def test_mail_composer_rights_portal(self):
+ portal_user = self._create_portal_user()
+
+ with patch.object(MailTestTicket, 'check_access_rights', return_value=True):
+ self.env['mail.compose.message'].with_user(portal_user).with_context(
+ self._get_web_context(self.test_record)
+ ).create({
+ 'subject': 'Subject',
+ 'body': '<p>Body text</p>',
+ 'partner_ids': []
+ }).send_mail()
+
+ self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text</p>')
+ self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
+
+ self.env['mail.compose.message'].with_user(portal_user).with_context({
+ 'default_composition_mode': 'comment',
+ 'default_parent_id': self.test_record.message_ids.ids[0],
+ }).create({
+ 'subject': 'Subject',
+ 'body': '<p>Body text 2</p>'
+ }).send_mail()
+
+ self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text 2</p>')
+ self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
+
+ @users('employee')
+ def test_mail_composer_save_template(self):
+ self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_record, add_web=False)
+ ).create({
+ 'subject': 'Template Subject',
+ 'body': '<p>Template Body</p>',
+ }).save_as_template()
+
+ # Test: email_template subject, body_html, model
+ template = self.env['mail.template'].search([
+ ('model', '=', self.test_record._name),
+ ('subject', '=', 'Template Subject')
+ ], limit=1)
+ self.assertEqual(template.name, "%s: %s" % (self.env['ir.model']._get(self.test_record._name).name, 'Template Subject'))
+ self.assertEqual(template.body_html, '<p>Template Body</p>', 'email_template incorrect body_html')
+
+
+@tagged('mail_composer')
+class TestComposerResultsComment(TestMailComposer):
+ """ Test global output of composer used in comment mode. Test notably
+ notification and emails generated during this process. """
+
+ @users('employee')
+ @mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail')
+ def test_mail_composer_recipients(self):
+ """ Test partner_ids given to composer are given to the final message. """
+ composer = self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_record)
+ ).create({
+ 'body': '<p>Test Body</p>',
+ 'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
+ })
+ composer.send_mail()
+
+ message = self.test_record.message_ids[0]
+ self.assertEqual(message.body, '<p>Test Body</p>')
+ self.assertEqual(message.author_id, self.user_employee.partner_id)
+ self.assertEqual(message.subject, 'Re: %s' % self.test_record.name)
+ self.assertEqual(message.subtype_id, self.env.ref('mail.mt_comment'))
+ self.assertEqual(message.partner_ids, self.partner_1 | self.partner_2)
+
+ @users('employee')
+ @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
+ def test_mail_composer_wtpl_complete(self):
+ """ Test a posting process using a complex template, holding several
+ additional recipients and attachments.
+
+ This tests notifies: 2 new email_to (+ 1 duplicated), 1 email_cc,
+ test_record followers and partner_admin added in partner_to."""
+ attachment_data = self._generate_attachments_data(2)
+ email_to_1 = 'test.to.1@test.example.com'
+ email_to_2 = 'test.to.2@test.example.com'
+ email_to_3 = 'test.to.1@test.example.com' # duplicate: should not sent twice the email
+ email_cc_1 = 'test.cc.1@test.example.com'
+ self.template.write({
+ 'auto_delete': False, # keep sent emails to check content
+ 'attachment_ids': [(0, 0, a) for a in attachment_data],
+ 'email_to': '%s, %s, %s' % (email_to_1, email_to_2, email_to_3),
+ 'email_cc': email_cc_1,
+ 'partner_to': '%s, ${object.customer_id.id if object.customer_id else ""}' % self.partner_admin.id,
+ 'report_name': 'TestReport for ${object.name}', # test cursor forces html
+ 'report_template': self.test_report.id,
+ })
+ attachs = self.env['ir.attachment'].search([('name', 'in', [a['name'] for a in attachment_data])])
+ self.assertEqual(len(attachs), 2)
+
+ # ensure initial data
+ self.assertEqual(self.test_record.user_id, self.user_employee_2)
+ self.assertEqual(self.test_record.message_partner_ids, self.partner_employee_2)
+
+ # open a composer and run it in comment mode
+ composer_form = Form(self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_record, add_web=True,
+ default_template_id=self.template.id)
+ ))
+ composer = composer_form.save()
+ with self.mock_mail_gateway(mail_unlink_sent=False), self.mock_mail_app():
+ composer.send_mail()
+
+ # check new partners have been created based on emails given
+ new_partners = self.env['res.partner'].search([
+ ('email', 'in', [email_to_1, email_to_2, email_to_3, email_cc_1])
+ ])
+ self.assertEqual(len(new_partners), 3)
+ self.assertEqual(set(new_partners.mapped('email')),
+ set(['test.to.1@test.example.com', 'test.to.2@test.example.com', 'test.cc.1@test.example.com'])
+ )
+
+ # global outgoing: one mail.mail (all customer recipients, then all employee recipients)
+ # and 5 emails, and 1 inbox notification (admin)
+ self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail')
+ self.assertEqual(len(self._mails), 5, 'Should have sent 5 emails, one per recipient')
+
+ # template is sent only to partners (email_to are transformed)
+ message = self.test_record.message_ids[0]
+ self.assertMailMail(self.partner_employee_2, 'sent',
+ check_mail_mail=True,
+ mail_message=message,
+ author=self.partner_employee, # author != email_from (template sets only email_from)
+ email_values={
+ 'body_content': 'TemplateBody %s' % self.test_record.name,
+ 'email_from': self.test_record.user_id.email_formatted, # set by template
+ 'subject': 'TemplateSubject %s' % self.test_record.name,
+ 'attachments_info': [
+ {'name': '00.txt', 'raw': b'Att00', 'type': 'text/plain'},
+ {'name': '01.txt', 'raw': b'Att01', 'type': 'text/plain'},
+ {'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'},
+ ]
+ },
+ fields_values={},
+ )
+ self.assertMailMail(self.test_record.customer_id + new_partners, 'sent',
+ check_mail_mail=True,
+ mail_message=message,
+ author=self.partner_employee, # author != email_from (template sets only email_from)
+ email_values={
+ 'body_content': 'TemplateBody %s' % self.test_record.name,
+ 'email_from': self.test_record.user_id.email_formatted, # set by template
+ 'subject': 'TemplateSubject %s' % self.test_record.name,
+ 'attachments_info': [
+ {'name': '00.txt', 'raw': b'Att00', 'type': 'text/plain'},
+ {'name': '01.txt', 'raw': b'Att01', 'type': 'text/plain'},
+ {'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'},
+ ]
+ },
+ fields_values={},
+ )
+
+ # message is posted and notified admin
+ self.assertEqual(message.subtype_id, self.env.ref('mail.mt_comment'))
+ self.assertNotified(message, [{'partner': self.partner_admin, 'is_read': False, 'type': 'inbox'}])
+ # attachments are copied on message and linked to document
+ self.assertEqual(
+ set(message.attachment_ids.mapped('name')),
+ set(['00.txt', '01.txt', 'TestReport for %s.html' % self.test_record.name])
+ )
+ self.assertEqual(set(message.attachment_ids.mapped('res_model')), set([self.test_record._name]))
+ self.assertEqual(set(message.attachment_ids.mapped('res_id')), set(self.test_record.ids))
+ self.assertTrue(all(attach not in message.attachment_ids for attach in attachs), 'Should have copied attachments')
+
+
+@tagged('mail_composer')
+class TestComposerResultsMass(TestMailComposer):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestComposerResultsMass, cls).setUpClass()
+ # ensure employee can create partners, necessary for templates
+ cls.user_employee.write({
+ 'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)],
+ })
+
+ @users('employee')
+ @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
+ def test_mail_composer_wtpl(self):
+ self.template.auto_delete = False # keep sent emails to check content
+ composer_form = Form(self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_records, add_web=True,
+ default_template_id=self.template.id)
+ ))
+ composer = composer_form.save()
+ with self.mock_mail_gateway(mail_unlink_sent=True):
+ composer.send_mail()
+
+ # global outgoing
+ self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
+ self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
+
+ for record in self.test_records:
+ # message copy is kept
+ message = record.message_ids[0]
+
+ # template is sent directly using customer field, meaning we have recipients
+ self.assertMailMail(record.customer_id, 'sent', check_mail_mail=True,
+ mail_message=message,
+ author=self.partner_employee)
+
+ # message content
+ self.assertEqual(message.subject, 'TemplateSubject %s' % record.name)
+ self.assertEqual(message.body, '<p>TemplateBody %s</p>' % record.name)
+ self.assertEqual(message.author_id, self.user_employee.partner_id)
+ # post-related fields are void
+ self.assertEqual(message.subtype_id, self.env['mail.message.subtype'])
+ self.assertEqual(message.partner_ids, self.env['res.partner'])
+
+ @users('employee')
+ @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
+ def test_mail_composer_wtpl_complete(self):
+ """ Test a composer in mass mode with a quite complete template, containing
+ notably email-based recipients and attachments. """
+ attachment_data = self._generate_attachments_data(2)
+ email_to_1 = 'test.to.1@test.example.com'
+ email_to_2 = 'test.to.2@test.example.com'
+ email_to_3 = 'test.to.1@test.example.com' # duplicate: should not sent twice the email
+ email_cc_1 = 'test.cc.1@test.example.com'
+ self.template.write({
+ 'auto_delete': False, # keep sent emails to check content
+ 'attachment_ids': [(0, 0, a) for a in attachment_data],
+ 'email_to': '%s, %s, %s' % (email_to_1, email_to_2, email_to_3),
+ 'email_cc': email_cc_1,
+ 'partner_to': '%s, ${object.customer_id.id if object.customer_id else ""}' % self.partner_admin.id,
+ 'report_name': 'TestReport for ${object.name}', # test cursor forces html
+ 'report_template': self.test_report.id,
+ })
+ attachs = self.env['ir.attachment'].search([('name', 'in', [a['name'] for a in attachment_data])])
+ self.assertEqual(len(attachs), 2)
+
+ # ensure initial data
+ self.assertEqual(self.test_records.user_id, self.env['res.users'])
+ self.assertEqual(self.test_records.message_partner_ids, self.env['res.partner'])
+
+ # launch composer in mass mode
+ composer_form = Form(self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_records, add_web=True,
+ default_template_id=self.template.id)
+ ))
+ composer = composer_form.save()
+ with self.mock_mail_gateway(mail_unlink_sent=False):
+ composer.send_mail()
+
+ new_partners = self.env['res.partner'].search([
+ ('email', 'in', [email_to_1, email_to_2, email_to_3, email_cc_1])
+ ])
+ self.assertEqual(len(new_partners), 3)
+
+ # global outgoing
+ self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
+ self.assertEqual(len(self._mails), 10, 'Should have sent 5 emails per record')
+
+ # hack to use assertEmails: filtering on from/to only is not sufficient to distinguish emails
+ _mails_records = [
+ [mail for mail in self._mails if '%s-%s' % (record.id, record._name) in mail['message_id']]
+ for record in self.test_records
+ ]
+ _mails_record2 = [mail for mail in self._mails if '%s-%s' % (self.test_records[1].id, self.test_records._name) in mail['message_id']]
+
+ for record, _mails in zip(self.test_records, _mails_records):
+ # message copy is kept
+ message = record.message_ids[0]
+
+ # template is sent only to partners (email_to are transformed)
+ self._mails = _mails
+ self.assertMailMail(record.customer_id + new_partners + self.partner_admin,
+ 'sent',
+ check_mail_mail=True,
+ mail_message=message,
+ author=self.partner_employee,
+ email_values={
+ 'body_content': 'TemplateBody %s' % record.name,
+ 'subject': 'TemplateSubject %s' % record.name,
+ # 'attachments': [
+ # ('00.txt', b'Att00', 'text/plain'),
+ # ('01.txt', b'Att01', 'text/plain'),
+ # ('report.test_mail.mail_test_ticket_test_template.html', b'My second attachment', 'text/plain')
+ # ]
+ }
+ )
+
+ @users('employee')
+ @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
+ def test_mail_composer_wtpl_delete(self):
+ self.template.auto_delete = True
+ composer_form = Form(self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_records, add_web=True,
+ default_template_id=self.template.id)
+ ))
+ composer = composer_form.save()
+ with self.mock_mail_gateway(mail_unlink_sent=True):
+ composer.send_mail()
+
+ # global outgoing
+ self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
+ self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
+ self.assertEqual(self._new_mails.exists(), self.env['mail.mail'], 'Should have deleted mail.mail records')
+
+ for record in self.test_records:
+ # message copy is kept
+ message = record.message_ids[0]
+
+ # template is sent directly using customer field
+ self.assertSentEmail(self.partner_employee, record.customer_id)
+
+ # message content
+ self.assertEqual(message.subject, 'TemplateSubject %s' % record.name)
+ self.assertEqual(message.body, '<p>TemplateBody %s</p>' % record.name)
+ self.assertEqual(message.author_id, self.user_employee.partner_id)
+ # post-related fields are void
+ self.assertEqual(message.subtype_id, self.env['mail.message.subtype'])
+ self.assertEqual(message.partner_ids, self.env['res.partner'])
+
+ @users('employee')
+ @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
+ def test_mail_composer_wtpl_delete_notif(self):
+ self.template.auto_delete = True
+ composer_form = Form(self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_records, add_web=True,
+ default_template_id=self.template.id,
+ default_auto_delete_message=True)
+ ))
+ composer = composer_form.save()
+ with self.mock_mail_gateway(mail_unlink_sent=True):
+ composer.send_mail()
+
+ # global outgoing
+ self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
+ self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
+ self.assertEqual(self._new_mails.exists(), self.env['mail.mail'], 'Should have deleted mail.mail records')
+
+ for record in self.test_records:
+ # message copy is unlinked
+ self.assertEqual(record.message_ids, self.env['mail.message'], 'Should have deleted mail.message records')
+
+ # template is sent directly using customer field
+ self.assertSentEmail(self.partner_employee, record.customer_id)
+
+ @users('employee')
+ @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
+ def test_mail_composer_wtpl_recipients(self):
+ """ Test various combinations of recipients: active_domain, active_id,
+ active_ids, ... to ensure fallback behavior are working. """
+ # 1: active_domain
+ composer_form = Form(self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_records, add_web=True,
+ default_template_id=self.template.id,
+ active_ids=[],
+ default_use_active_domain=True,
+ default_active_domain=[('id', 'in', self.test_records.ids)])
+ ))
+ composer = composer_form.save()
+ with self.mock_mail_gateway(mail_unlink_sent=True):
+ composer.send_mail()
+
+ # global outgoing
+ self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
+ self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
+
+ for record in self.test_records:
+ # template is sent directly using customer field
+ self.assertSentEmail(self.partner_employee, record.customer_id)
+
+ # 2: active_domain not taken into account if use_active_domain is False
+ composer_form = Form(self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_records, add_web=True,
+ default_template_id=self.template.id,
+ default_use_active_domain=False,
+ default_active_domain=[('id', 'in', -1)])
+ ))
+ composer = composer_form.save()
+ with self.mock_mail_gateway(mail_unlink_sent=True):
+ composer.send_mail()
+
+ # global outgoing
+ self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
+ self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
+
+ # 3: fallback on active_id if not active_ids
+ composer_form = Form(self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_records, add_web=True,
+ default_template_id=self.template.id,
+ active_ids=[])
+ ))
+ composer = composer_form.save()
+ with self.mock_mail_gateway(mail_unlink_sent=False):
+ composer.send_mail()
+
+ # global outgoing
+ self.assertEqual(len(self._new_mails), 1, 'Should have created 1 mail.mail per record')
+ self.assertEqual(len(self._mails), 1, 'Should have sent 1 email per record')
+
+ # 3: void is void
+ composer_form = Form(self.env['mail.compose.message'].with_context(
+ default_model='mail.test.ticket',
+ default_template_id=self.template.id
+ ))
+ composer = composer_form.save()
+ with self.mock_mail_gateway(mail_unlink_sent=False), self.assertRaises(ValueError):
+ composer.send_mail()
diff --git a/addons/test_mail/tests/test_message_composer.py b/addons/test_mail/tests/test_message_composer.py
deleted file mode 100644
index ea6aa91803f..00000000000
--- a/addons/test_mail/tests/test_message_composer.py
+++ /dev/null
@@ -1,345 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-import base64
-
-from unittest.mock import patch
-
-from odoo.addons.test_mail.tests.common import TestMailCommon, TestRecipients
-from odoo.addons.test_mail.models.test_mail_models import MailTestSimple
-from odoo.tests import tagged
-from odoo.tools import mute_logger
-
-
-@tagged('mail_composer')
-class TestComposer(TestMailCommon, TestRecipients):
- """ Test Composer integration and results """
-
- @classmethod
- def setUpClass(cls):
- super(TestComposer, cls).setUpClass()
- cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
- cls._reset_mail_context(cls.test_record)
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_composer_comment(self):
- composer = self.env['mail.compose.message'].with_context({
- 'default_composition_mode': 'comment',
- 'default_model': self.test_record._name,
- 'default_res_id': self.test_record.id,
- }).with_user(self.user_employee).create({
- 'body': '<p>Test Body</p>',
- 'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
- })
- composer.send_mail()
-
- message = self.test_record.message_ids[0]
- self.assertEqual(message.body, '<p>Test Body</p>')
- self.assertEqual(message.author_id, self.user_employee.partner_id)
- self.assertEqual(message.subject, 'Re: %s' % self.test_record.name)
- self.assertEqual(message.subtype_id, self.env.ref('mail.mt_comment'))
- self.assertEqual(message.partner_ids, self.partner_1 | self.partner_2)
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_composer_comment_parent(self):
- parent = self.test_record.message_post(body='Test')
-
- self.env['mail.compose.message'].with_context({
- 'default_composition_mode': 'comment',
- 'default_parent_id': parent.id
- }).with_user(self.user_employee).create({
- 'body': '<p>Mega</p>',
- }).send_mail()
-
- message = self.test_record.message_ids[0]
- self.assertEqual(message.body, '<p>Mega</p>')
- self.assertEqual(message.parent_id, parent)
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_composer_mass_mail(self):
- test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2'})
-
- composer = self.env['mail.compose.message'].with_context({
- 'default_composition_mode': 'mass_mail',
- 'default_model': self.test_record._name,
- 'default_res_id': False,
- 'active_ids': [self.test_record.id, test_record_2.id]
- }).with_user(self.user_employee).create({
- 'subject': 'Testing ${object.name}',
- 'body': '<p>${object.name}</p>',
- 'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
- })
- composer.with_context({
- 'default_res_id': -1,
- 'active_ids': [self.test_record.id, test_record_2.id]
- }).send_mail()
-
- # check mail_mail
- mails = self.env['mail.mail'].sudo().search([('subject', 'ilike', 'Testing')])
- for mail in mails:
- self.assertEqual(mail.recipient_ids, self.partner_1 | self.partner_2,
- 'compose wizard: mail_mail mass mailing: mail.mail in mass mail incorrect recipients')
-
- # check message on test_record
- message1 = self.test_record.message_ids[0]
- self.assertEqual(message1.subject, 'Testing %s' % self.test_record.name)
- self.assertEqual(message1.body, '<p>%s</p>' % self.test_record.name)
-
- # check message on test_record_2
- message1 = test_record_2.message_ids[0]
- self.assertEqual(message1.subject, 'Testing %s' % test_record_2.name)
- self.assertEqual(message1.body, '<p>%s</p>' % test_record_2.name)
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_composer_mass_mail_active_domain(self):
- test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2'})
-
- self.env['mail.compose.message'].with_context({
- 'default_composition_mode': 'mass_mail',
- 'default_model': self.test_record._name,
- 'default_use_active_domain': True,
- 'active_ids': [self.test_record.id],
- 'active_domain': [('name', 'in', ['%s' % self.test_record.name, '%s' % test_record_2.name])],
- }).with_user(self.user_employee).create({
- 'subject': 'From Composer Test',
- 'body': '${object.name}',
- }).send_mail()
-
- self.assertEqual(self.test_record.message_ids[0].subject, 'From Composer Test')
- self.assertEqual(test_record_2.message_ids[0].subject, 'From Composer Test')
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_message_compose_mass_mail_no_active_domain(self):
- test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2'})
-
- self.env['mail.compose.message'].with_context({
- 'default_composition_mode': 'mass_mail',
- 'default_model': self.test_record._name,
- 'default_use_active_domain': False,
- 'active_ids': [self.test_record.id],
- 'active_domain': [('name', 'in', ['%s' % self.test_record.name, '%s' % test_record_2.name])],
- }).with_user(self.user_employee).create({
- 'subject': 'From Composer Test',
- 'body': '${object.name}',
- }).send_mail()
-
- self.assertEqual(self.test_record.message_ids[0].subject, 'From Composer Test')
- self.assertFalse(test_record_2.message_ids.ids)
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_message_compose_portal_ok(self):
- portal_user = self._create_portal_user()
-
- with patch.object(MailTestSimple, 'check_access_rights', return_value=True):
- ComposerPortal = self.env['mail.compose.message'].with_user(portal_user)
-
- ComposerPortal.with_context({
- 'default_composition_mode': 'comment',
- 'default_model': self.test_record._name,
- 'default_res_id': self.test_record.id,
- }).create({
- 'subject': 'Subject',
- 'body': '<p>Body text</p>',
- 'partner_ids': []}).send_mail()
-
- self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text</p>')
- self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
-
- ComposerPortal.with_context({
- 'default_composition_mode': 'comment',
- 'default_parent_id': self.test_record.message_ids.ids[0],
- }).create({
- 'subject': 'Subject',
- 'body': '<p>Body text 2</p>'}).send_mail()
-
- self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text 2</p>')
- self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
-
-
-@tagged('mail_composer')
-class TestComposerWTpl(TestMailCommon, TestRecipients):
-
- @classmethod
- def setUpClass(cls):
- super(TestComposerWTpl, cls).setUpClass()
- cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
-
- cls.user_employee.write({
- 'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)],
- })
-
- cls._attachments = [{
- 'name': 'first.txt',
- 'datas': base64.b64encode(b'My first attachment'),
- 'res_model': 'res.partner',
- 'res_id': cls.user_admin.partner_id.id
- }, {
- 'name': 'second.txt',
- 'datas': base64.b64encode(b'My second attachment'),
- 'res_model': 'res.partner',
- 'res_id': cls.user_admin.partner_id.id
- }]
-
- cls.email_1 = 'test1@example.com'
- cls.email_2 = 'test2@example.com'
- cls.email_3 = cls.partner_1.email
- cls._create_template('mail.test.simple', {
- 'attachment_ids': [(0, 0, cls._attachments[0]), (0, 0, cls._attachments[1])],
- 'partner_to': '%s,%s' % (cls.partner_2.id, cls.user_admin.partner_id.id),
- 'email_to': '%s, %s' % (cls.email_1, cls.email_2),
- 'email_cc': '%s' % cls.email_3,
- })
-
- # admin should receive emails
- cls.user_admin.write({'notification_type': 'email'})
- # Force the attachments of the template to be in the natural order.
- cls.email_template.invalidate_cache(['attachment_ids'], ids=cls.email_template.ids)
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_composer_w_template(self):
- composer = self.env['mail.compose.message'].with_user(self.user_employee).with_context({
- 'default_composition_mode': 'comment',
- 'default_model': 'mail.test.simple',
- 'default_res_id': self.test_record.id,
- 'default_template_id': self.email_template.id,
- }).create({'subject': 'Forget me subject', 'body': 'Dummy body'})
-
- # perform onchange and send emails
- values = composer.onchange_template_id(self.email_template.id, 'comment', self.test_record._name, self.test_record.id)['value']
- composer.write(values)
- with self.mock_mail_gateway(mail_unlink_sent=True):
- composer.send_mail()
-
- new_partners = self.env['res.partner'].search([('email', 'in', [self.email_1, self.email_2])])
- self.assertMailMail(
- [self.partner_1, self.partner_2, new_partners[0], new_partners[1], self.partner_admin],
- 'sent',
- author=self.user_employee.partner_id,
- mail_message=False,
- check_mail_mail=False,
- email_values={
- 'subject': 'About %s' % self.test_record.name,
- 'body_content': self.test_record.name,
- 'attachments': [
- ('first.txt', b'My first attachment', 'text/plain'),
- ('second.txt', b'My second attachment', 'text/plain')
- ]
- }
- )
-
- def test_composer_template_onchange_attachments(self):
- """Tests that all attachments are added to the composer,
- static attachments are not duplicated and while reports are re-generated,
- and that intermediary attachments are dropped."""
-
- composer = self.env['mail.compose.message'].with_context(default_attachment_ids=[]).create({})
- report_template = self.env.ref('web.action_report_externalpreview')
- template_1 = self.email_template.copy({
- 'report_template': report_template.id,
- })
- template_2 = self.email_template.copy({
- 'attachment_ids': False,
- 'report_template': report_template.id,
- })
-
- onchange_templates = [template_1, template_2, template_1, False]
- attachments_onchange = [composer.attachment_ids]
- # template_1 has two static attachments and one dynamically generated report,
- # template_2 only has the report, so we should get 3, 1, 3 attachments
- attachment_numbers = [0, 3, 1, 3, 0]
-
- for template in onchange_templates:
- onchange = composer.onchange_template_id(
- template.id if template else False, 'comment', self.test_record._name, self.test_record.id
- )
- composer.update(onchange['value'])
- attachments_onchange.append(composer.attachment_ids)
-
- self.assertEqual(
- [len(attachments) for attachments in attachments_onchange],
- attachment_numbers,
- )
-
- self.assertTrue(
- len(attachments_onchange[1] & attachments_onchange[3]) == 2,
- "The two static attachments on the template should be common to the two onchanges"
- )
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_composer_w_template_mass_mailing(self):
- test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2', 'email_from': 'laurie.poiret@example.com'})
-
- composer = self.env['mail.compose.message'].with_user(self.user_employee).with_context({
- 'default_composition_mode': 'mass_mail',
- # 'default_notify': True,
- 'default_notify': False,
- 'default_model': 'mail.test.simple',
- 'default_res_id': self.test_record.id,
- 'default_template_id': self.email_template.id,
- 'active_ids': [self.test_record.id, test_record_2.id]
- }).create({})
- values = composer.onchange_template_id(self.email_template.id, 'mass_mail', 'mail.test.simple', self.test_record.id)['value']
- composer.write(values)
- with self.mock_mail_gateway(mail_unlink_sent=True):
- composer.send_mail()
-
- new_partners = self.env['res.partner'].search([('email', 'in', [self.email_1, self.email_2])])
- # hack to use assertEmails
- self._mails_record1 = [dict(mail) for mail in self._mails if '%s-%s' % (self.test_record.id, self.test_record._name) in mail['message_id']]
- self._mails_record2 = [dict(mail) for mail in self._mails if '%s-%s' % (test_record_2.id, test_record_2._name) in mail['message_id']]
-
- self._mails = self._mails_record1
- self.assertMailMail(
- [self.partner_1, self.partner_2, new_partners[0], new_partners[1], self.partner_admin],
- 'sent',
- author=self.user_employee.partner_id,
- mail_message=False,
- check_mail_mail=False,
- email_values={
- 'subject': 'About %s' % self.test_record.name,
- 'body_content': self.test_record.name,
- 'attachments': [
- ('first.txt', b'My first attachment', 'text/plain'),
- ('second.txt', b'My second attachment', 'text/plain')
- ]
- }
- )
-
- self._mails = self._mails_record2
- self.assertMailMail(
- [self.partner_1, self.partner_2, new_partners[0], new_partners[1], self.partner_admin],
- 'sent',
- author=self.user_employee.partner_id,
- mail_message=False,
- check_mail_mail=False,
- email_values={
- 'subject': 'About %s' % test_record_2.name,
- 'body_content': test_record_2.name,
- 'attachments': [
- ('first.txt', b'My first attachment', 'text/plain'),
- ('second.txt', b'My second attachment', 'text/plain')
- ]
- }
- )
-
- message_1 = self.test_record.message_ids[0]
- message_2 = test_record_2.message_ids[0]
-
- # messages effectively posted
- self.assertEqual(message_1.subject, 'About %s' % self.test_record.name)
- self.assertEqual(message_2.subject, 'About %s' % test_record_2.name)
- self.assertIn(self.test_record.name, message_1.body)
- self.assertIn(test_record_2.name, message_2.body)
-
- def test_composer_template_save(self):
- self.env['mail.compose.message'].with_context({
- 'default_composition_mode': 'comment',
- 'default_model': 'mail.test.simple',
- 'default_res_id': self.test_record.id,
- }).create({
- 'subject': 'Forget me subject',
- 'body': '<p>Dummy body</p>'
- }).save_as_template()
- # Test: email_template subject, body_html, model
- last_template = self.env['mail.template'].search([('model', '=', 'mail.test.simple'), ('subject', '=', 'Forget me subject')], limit=1)
- self.assertEqual(last_template.body_html, '<p>Dummy body</p>', 'email_template incorrect body_html')