odoo18/addons/website/tests/test_qweb.py

241 lines
9.0 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import http
from odoo.addons.base.tests.common import TransactionCaseWithUserDemo
from odoo.addons.website.tools import MockRequest
from odoo.tests.common import TransactionCase
class TestQweb(TransactionCaseWithUserDemo):
def test_qweb_post_processing_att(self):
website = self.env.ref('website.default_website')
t = self.env['ir.ui.view'].create({
'name': 'test',
'type': 'qweb',
'arch_db': '''<t t-name="attr-escaping">
<img src="http://test.external.img/img.png"/>
<img t-att-src="url"/>
</t>'''
})
result = """
<img src="http://test.external.img/img.png" loading="lazy"/>
<img src="http://test.external.img/img2.png" loading="lazy"/>
"""
rendered = self.env['ir.qweb']._render(t.id, {'url': 'http://test.external.img/img2.png'}, website_id=website.id)
self.assertEqual(rendered.strip(), result.strip())
class TestQwebProcessAtt(TransactionCase):
def setUp(self):
super(TestQwebProcessAtt, self).setUp()
self.website = self.env.ref('website.default_website')
self.env['res.lang']._activate_lang('fr_FR')
self.website.language_ids = self.env.ref('base.lang_en') + self.env.ref('base.lang_fr')
self.website.default_lang_id = self.env.ref('base.lang_en')
self.website.cdn_activated = True
self.website.cdn_url = "http://test.cdn"
self.website.cdn_filters = "\n".join(["^(/[a-z]{2}_[A-Z]{2})?/a$", "^(/[a-z]{2})?/a$", "^/b$"])
def _test_att(self, url, expect, tag='a', attribute='href'):
self.assertEqual(
self.env['ir.qweb']._post_processing_att(tag, {attribute: url}),
expect
)
def test_process_att_no_request(self):
# no request so no URL rewriting
self._test_att('/', {'href': '/'})
self._test_att('/en', {'href': '/en'})
self._test_att('/fr', {'href': '/fr'})
# no URL rewritting for CDN
self._test_att('/a', {'href': '/a'})
def test_process_att_no_website(self):
with MockRequest(self.env):
# no website so URL rewriting
self._test_att('/', {'href': '/'})
self._test_att('/en', {'href': '/en'})
self._test_att('/fr', {'href': '/fr'})
# no URL rewritting for CDN
self._test_att('/a', {'href': '/a'})
def test_process_att_monolang_route(self):
with MockRequest(self.env, website=self.website, multilang=False):
# lang not changed in URL but CDN enabled
self._test_att('/a', {'href': 'http://test.cdn/a'})
self._test_att('/en/a', {'href': 'http://test.cdn/en/a'})
self._test_att('/b', {'href': 'http://test.cdn/b'})
self._test_att('/en/b', {'href': '/en/b'})
def test_process_att_no_request_lang(self):
with MockRequest(self.env, website=self.website):
self._test_att('/', {'href': '/'})
self._test_att('/en/', {'href': '/'})
self._test_att('/fr/', {'href': '/fr/'})
self._test_att('/fr', {'href': '/fr'})
def test_process_att_with_request_lang(self):
with MockRequest(self.env, website=self.website, context={'lang': 'fr_FR'}):
self._test_att('/', {'href': '/fr'})
self._test_att('/en/', {'href': '/'})
self._test_att('/fr/', {'href': '/fr/'})
self._test_att('/fr', {'href': '/fr'})
def test_process_att_matching_cdn_and_lang(self):
with MockRequest(self.env, website=self.website):
# lang prefix is added before CDN
self._test_att('/a', {'href': 'http://test.cdn/a'})
self._test_att('/en/a', {'href': 'http://test.cdn/a'})
self._test_att('/fr/a', {'href': 'http://test.cdn/fr/a'})
self._test_att('/b', {'href': 'http://test.cdn/b'})
self._test_att('/en/b', {'href': 'http://test.cdn/b'})
self._test_att('/fr/b', {'href': '/fr/b'})
def test_process_att_no_route(self):
with MockRequest(self.env, website=self.website, context={'lang': 'fr_FR'}, routing=False):
# default on multilang=True if route is not /{module}/static/
self._test_att('/web/static/hi', {'href': '/web/static/hi'})
self._test_att('/my-page', {'href': '/fr/my-page'})
def test_process_att_url_crap(self):
with MockRequest(self.env, website=self.website):
match = http.root.get_db_router.return_value.bind.return_value.match
# #{fragment} is stripped from URL when testing route
self._test_att('/x#y?z', {'href': '/x#y?z'})
match.assert_called_with('/x', method='POST', query_args=None)
match.reset_calls()
self._test_att('/x?y#z', {'href': '/x?y#z'})
match.assert_called_with('/x', method='POST', query_args='y')
class TestQwebDataSnippet(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env['ir.ui.view'].create({
'name': 'some_html',
'type': 'qweb',
'key': 'website.some_html',
'arch': '''
<t t-name="some_html">
<article>
<span>Hello</span>
<t t-out="0"/>
</article>
</t>
'''
})
cls.env['ir.ui.view'].create({
'name': 's_a',
'type': 'qweb',
'key': 'website.s_a',
'arch': '''
<t t-name="s_a">
<section class="hello">
<t t-call="website.some_html"/>
<t t-out="0"/>
</section>
</t>
'''
})
cls.env['ir.ui.view'].create({
'name': 's_b',
'type': 'qweb',
'key': 'website.s_b',
'arch': '''
<t t-name="s_b">
<section class="foo">
<t t-snippet-call="website.s_a"/>
</section>
</t>
'''
})
cls.env['ir.ui.view'].create({
'name': 's_c',
'type': 'qweb',
'key': 'website.s_c',
'arch': '''
<t t-name="s_c">
<t t-call="website.some_html">
<p>World!</p>
</t>
</t>
'''
})
cls.env['ir.ui.view'].create({
'name': 's_d',
'type': 'qweb',
'key': 'website.s_d',
'arch_db': '''
<t t-name="s_d">
<t t-snippet-call="website.s_a">
<p>World!</p>
</t>
</t>
'''
})
def _normalize_xml(self, html):
return "\n".join(
line.strip() for line in html.strip().splitlines() if line.strip()
)
def _render_snippet(self, snippet):
render_template = self.env['ir.ui.view'].create({
'name': f't-snippet-call_{snippet}',
'type': 'qweb',
'arch': f'''
<t t-snippet-call="{snippet}"/>
'''
})
return self.env['ir.qweb']._render(render_template.id)
def test_t_call_inside_snippet(self):
expected_output = '''
<section class="hello" data-snippet="s_a">
<article>
<span>Hello</span>
</article>
</section>
'''
rendered = self._render_snippet('website.s_a')
self.assertEqual(self._normalize_xml(rendered), self._normalize_xml(expected_output))
def test_t_snippet_call_inside_snippet(self):
expected_output = '''
<section class="foo" data-snippet="s_b">
<section class="hello" data-snippet="s_a">
<article>
<span>Hello</span>
</article>
</section>
</section>
'''
rendered = self._render_snippet('website.s_b')
self.assertEqual(self._normalize_xml(rendered), self._normalize_xml(expected_output))
def test_t_call_as_snippet_root(self):
expected_output = '''
<article data-snippet="s_c">
<span>Hello</span>
<p>World!</p>
</article>
'''
rendered = self._render_snippet('website.s_c')
self.assertEqual(self._normalize_xml(rendered), self._normalize_xml(expected_output))
def test_t_snippet_call_as_snippet_root(self):
expected_output = '''
<section class="hello" data-snippet="s_a">
<article>
<span>Hello</span>
</article>
<p>World!</p>
</section>
'''
rendered = self._render_snippet('website.s_d')
self.assertEqual(self._normalize_xml(rendered), self._normalize_xml(expected_output))