Files
odoo_source/addons/mail/tests/test_mail_render.py
T
Thibault Delavallée 69dfaf4ca5 [IMP] mail: experimentally support QWeb rendering
Purpose of this commit is to prepare ground for future improvements by already
supporting QWeb rendering in ``mail.render.mixin``.

We temporarily support new field parameters allowing to tune the rendering
directly from field. Notaby choosing engine (jinja or qweb) can be done
from field directly. This is considered as experimental, used mainly to
prepare QWeb support.

Task ID-2534550 (Template usage improvement)
Task ID-2477164 (Composer mixin)
Prepares Task ID-27033 (QWeb in templates)
COM PR odoo/odoo#70889
ENT PR odoo/enterprise#18352
2021-06-01 09:24:26 +00:00

322 lines
12 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.mail.tests import common
from odoo.tests import tagged, users
@tagged('mail_render')
class TestMailRender(common.MailCommon):
@classmethod
def setUpClass(cls):
super(TestMailRender, cls).setUpClass()
# activate multi language support
cls.env['res.lang']._activate_lang('fr_FR')
cls.user_admin.write({'lang': 'en_US'})
# test records
cls.render_object = cls.env['res.partner'].create({
'name': 'TestRecord',
'lang': 'en_US',
})
cls.render_object_fr = cls.env['res.partner'].create({
'name': 'Element de Test',
'lang': 'fr_FR',
})
# some jinja templates
cls.base_jinja_bits = [
'<p>Hello</p>',
'<p>Hello ${object.name}</p>',
"""<p>
% set english = object.lang == 'en_US'
% if english
<span>English Speaker</span>
% else
<span>Other Speaker</span>
% endif
</p>"""
]
cls.base_jinja_bits_fr = [
'<p>Bonjour</p>',
'<p>Bonjour ${object.name}</p>',
"""<p>
% set english = object.lang == 'en_US'
% if english
<span>Narrateur Anglais</span>
% else
<span>Autre Narrateur</span>
% endif
</p>"""
]
# some qweb templates, their views and their xml ids
cls.base_qweb_bits = [
'<p>Hello</p>',
'<p>Hello <t t-esc="object.name"/></p>',
"""<p>
<span t-if="object.lang == 'en_US'">English Speaker</span>
<span t-else="">Other Speaker</span>
</p>"""
]
cls.base_qweb_templates = cls.env['ir.ui.view'].create([
{'name': 'TestRender%d' % index,
'type': 'qweb',
'arch': qweb_content,
} for index, qweb_content in enumerate(cls.base_qweb_bits)
])
cls.base_qweb_templates_data = cls.env['ir.model.data'].create([
{'name': template.name, 'module': 'mail',
'model': template._name, 'res_id': template.id,
} for template in cls.base_qweb_templates
])
cls.base_qweb_templates_xmlids = [
model_data.complete_name
for model_data in cls.base_qweb_templates_data
]
# render result
cls.base_rendered = [
'<p>Hello</p>',
'<p>Hello %s</p>' % cls.render_object.name,
"""<p>
<span>English Speaker</span>
</p>"""
]
cls.base_rendered_fr = [
'<p>Bonjour</p>',
'<p>Bonjour %s</p>' % cls.render_object_fr.name,
"""<p>
<span>Autre Narrateur</span>
</p>"""
]
# link to mail template
cls.test_template_jinja = cls.env['mail.template'].create({
'name': 'Test Template',
'subject': cls.base_jinja_bits[0],
'body_html': cls.base_jinja_bits[1],
'model_id': cls.env['ir.model']._get('res.partner').id,
'lang': '${object.lang}'
})
# some translations
cls.env['ir.translation'].create({
'type': 'model',
'name': 'mail.template,subject',
'lang': 'fr_FR',
'res_id': cls.test_template_jinja.id,
'src': cls.test_template_jinja.subject,
'value': cls.base_jinja_bits_fr[0],
})
cls.env['ir.translation'].create({
'type': 'model',
'name': 'mail.template,body_html',
'lang': 'fr_FR',
'res_id': cls.test_template_jinja.id,
'src': cls.test_template_jinja.body_html,
'value': cls.base_jinja_bits_fr[1],
})
cls.env['ir.model.data'].create({
'name': 'test_template_xmlid',
'module': 'mail',
'model': cls.test_template_jinja._name,
'res_id': cls.test_template_jinja.id,
})
@users('employee')
def test_evaluation_context(self):
""" Test evaluation context and various ways of tweaking it. """
partner = self.env['res.partner'].browse(self.render_object.ids)
MailRenderMixin = self.env['mail.render.mixin']
custom_ctx = {'custom_ctx': 'Custom Context Value'}
add_context = {
'custom_value': 'Custom Render Value'
}
srces = [
'<b>I am ${user.name}</b>',
'<span>Datetime is ${format_datetime(datetime.datetime(2021, 6, 1), dt_format="MM - d - YYY")}</span>',
'<span>Context ${ctx.get("custom_ctx")}, value ${custom_value}</span>',
]
results = [
'<b>I am %s</b>' % self.env.user.name,
'<span>Datetime is 06 - 1 - 2021</span>',
'<span>Context Custom Context Value, value Custom Render Value</span>'
]
for src, expected in zip(srces, results):
for engine in ['jinja']:
result = MailRenderMixin.with_context(**custom_ctx)._render_template(
src, partner._name, partner.ids,
engine=engine, add_context=add_context
)[partner.id]
self.assertEqual(expected, result)
@users('employee')
def test_render_mail_template_jinja(self):
template = self.env['mail.template'].browse(self.test_template_jinja.ids)
partner = self.env['res.partner'].browse(self.render_object.ids)
for fname, expected in zip(['subject', 'body_html'], self.base_rendered):
rendered = template._render_field(
fname,
partner.ids,
compute_lang=True
)[partner.id]
self.assertEqual(rendered, expected)
partner = self.env['res.partner'].browse(self.render_object_fr.ids)
for fname, expected in zip(['subject', 'body_html'], self.base_rendered_fr):
rendered = template._render_field(
fname,
partner.ids,
compute_lang=True
)[partner.id]
self.assertEqual(rendered, expected)
@users('employee')
def test_render_template_jinja(self):
partner = self.env['res.partner'].browse(self.render_object.ids)
for source, expected in zip(self.base_jinja_bits, self.base_rendered):
rendered = self.env['mail.render.mixin']._render_template(
source,
partner._name,
partner.ids,
engine='jinja',
)[partner.id]
self.assertEqual(rendered, expected)
@users('employee')
def test_render_template_qweb_view(self):
partner = self.env['res.partner'].browse(self.render_object.ids)
for source, expected in zip(self.base_qweb_templates_xmlids, self.base_rendered):
rendered = self.env['mail.render.mixin']._render_template(
source,
partner._name,
partner.ids,
engine='qweb_view',
)[partner.id].decode()
self.assertEqual(rendered, expected)
@users('employee')
def test_template_rendering_impersonate(self):
""" Test that the use of SUDO do not change the current user. """
partner = self.env['res.partner'].browse(self.render_object.ids)
src = '${user.name} - ${object.name}'
expected = '%s - %s' % (self.env.user.name, partner.name)
result = self.env['mail.render.mixin'].sudo()._render_template_jinja(
src, partner._name, partner.ids
)[partner.id]
self.assertIn(expected, result)
@users('employee')
def test_template_rendering_function_call(self):
"""Test the case when the template call a custom function.
This function should not be called when the template is not rendered.
"""
partner = self.env['res.partner'].browse(self.render_object.ids)
def cust_function():
# Can not use "MagicMock" in a Jinja sand-boxed environment
# so create our own function
cust_function.call = True
return 'return value'
cust_function.call = False
src = """<h1>This is a test</h1>
<p>${cust_function()}</p>"""
expected = """<h1>This is a test</h1>
<p>return value</p>"""
context = {'cust_function': cust_function}
result = self.env['mail.render.mixin']._render_template_jinja(
src, partner._name, partner.ids,
add_context=context
)[partner.id]
self.assertEqual(expected, result)
self.assertTrue(cust_function.call)
@users('employee')
def test_template_rendering_various(self):
""" Test static rendering """
partner = self.env['res.partner'].browse(self.render_object.ids)
MailRenderMixin = self.env['mail.render.mixin']
# static string
src = 'This is a string'
expected = 'This is a string'
for engine in ['jinja']:
result = MailRenderMixin._render_template(
src, partner._name, partner.ids, engine=engine,
)[partner.id]
self.assertEqual(expected, result)
# code string
src = 'This is a string with a number ${13+13}'
expected = 'This is a string with a number 26'
for engine in ['jinja']:
result = MailRenderMixin._render_template(
src, partner._name, partner.ids, engine=engine,
)[partner.id]
self.assertEqual(expected, result)
# block string
src = "This is a string with a block ${'hidden' if False else 'displayed'}"
expected = 'This is a string with a block displayed'
for engine in ['jinja']:
result = MailRenderMixin._render_template(
src, partner._name, partner.ids, engine=engine,
)[partner.id]
self.assertEqual(expected, result)
# static xml
src = '<p class="text-muted"><span>This is a string</span></p>'
expected = '<p class="text-muted"><span>This is a string</span></p>'
for engine in ['jinja', 'qweb']:
result = MailRenderMixin._render_template(
src, partner._name, partner.ids, engine=engine,
)[partner.id]
self.assertEqual(expected, result if engine == 'jinja' else result.decode()) # tde: checkme
# code xml
srces = [
'<p class="text-muted"><span>This is a string with a number ${13+13}</span></p>',
'<p class="text-muted"><span>This is a string with a number <t t-out="13+13"/></span></p>',
]
expected = '<p class="text-muted"><span>This is a string with a number 26</span></p>'
for engine, src in zip(['jinja', 'qweb'], srces):
result = MailRenderMixin._render_template(
src, partner._name, partner.ids, engine=engine,
)[partner.id]
self.assertEqual(expected, result if engine == 'jinja' else result.decode()) # tde: checkme
# condition block xml
src = """<b>Test</b>
% if False:
<b>Code not executed</b>
% endif"""
expected = "<b>Test</b>\n" # TDE: to check
for engine in ['jinja']:
result = MailRenderMixin._render_template(
src, partner._name, partner.ids, engine=engine,
)[partner.id]
self.assertEqual(expected, result)
# complete block xml
src = """<p>
% set line_statement_variable = 3
<span>We have ${line_statement_variable} cookies in stock</span>
<span>We have <% set block_variable = 4 %>${block_variable} cookies in stock</span>
</p>"""
expected = """<p>
<span>We have 3 cookies in stock</span>
<span>We have 4 cookies in stock</span>
</p>"""
for engine in ['jinja']:
result = MailRenderMixin._render_template(
src, partner._name, partner.ids, engine=engine,
)[partner.id]
self.assertEqual(result, expected)