summaryrefslogtreecommitdiff
path: root/addons/test_website/tests
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/test_website/tests
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/test_website/tests')
-rw-r--r--addons/test_website/tests/__init__.py13
-rw-r--r--addons/test_website/tests/test_controller_args.py31
-rw-r--r--addons/test_website/tests/test_custom_snippet.py13
-rw-r--r--addons/test_website/tests/test_error.py10
-rw-r--r--addons/test_website/tests/test_is_multilang.py71
-rw-r--r--addons/test_website/tests/test_multi_company.py16
-rw-r--r--addons/test_website/tests/test_performance.py10
-rw-r--r--addons/test_website/tests/test_redirect.py162
-rw-r--r--addons/test_website/tests/test_reset_views.py111
-rw-r--r--addons/test_website/tests/test_session.py9
-rw-r--r--addons/test_website/tests/test_views_during_module_operation.py83
11 files changed, 529 insertions, 0 deletions
diff --git a/addons/test_website/tests/__init__.py b/addons/test_website/tests/__init__.py
new file mode 100644
index 00000000..38da4577
--- /dev/null
+++ b/addons/test_website/tests/__init__.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import test_controller_args
+from . import test_custom_snippet
+from . import test_error
+from . import test_is_multilang
+from . import test_multi_company
+from . import test_performance
+from . import test_redirect
+from . import test_reset_views
+from . import test_session
+from . import test_views_during_module_operation
diff --git a/addons/test_website/tests/test_controller_args.py b/addons/test_website/tests/test_controller_args.py
new file mode 100644
index 00000000..fc21c22a
--- /dev/null
+++ b/addons/test_website/tests/test_controller_args.py
@@ -0,0 +1,31 @@
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import odoo.tests
+
+
+@odoo.tests.common.tagged('post_install', '-at_install')
+class TestWebsiteControllerArgs(odoo.tests.HttpCase):
+
+ def test_crawl_args(self):
+ req = self.url_open('/ignore_args/converter/valueA/?b=valueB&c=valueC')
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': 'valueA', 'b': 'valueB', 'kw': {'c': 'valueC'}})
+
+ req = self.url_open('/ignore_args/converter/valueA/nokw?b=valueB&c=valueC')
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': 'valueA', 'b': 'valueB'})
+
+ req = self.url_open('/ignore_args/converteronly/valueA/?b=valueB&c=valueC')
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': 'valueA', 'kw': None})
+
+ req = self.url_open('/ignore_args/none?a=valueA&b=valueB')
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': None, 'kw': None})
+
+ req = self.url_open('/ignore_args/a?a=valueA&b=valueB')
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': 'valueA', 'kw': None})
+
+ req = self.url_open('/ignore_args/kw?a=valueA&b=valueB')
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': 'valueA', 'kw': {'b': 'valueB'}})
diff --git a/addons/test_website/tests/test_custom_snippet.py b/addons/test_website/tests/test_custom_snippet.py
new file mode 100644
index 00000000..c79ed227
--- /dev/null
+++ b/addons/test_website/tests/test_custom_snippet.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import odoo.tests
+from odoo.tools import mute_logger
+
+
+@odoo.tests.common.tagged('post_install', '-at_install')
+class TestCustomSnippet(odoo.tests.HttpCase):
+
+ @mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
+ def test_01_run_tour(self):
+ self.start_tour("/", 'test_custom_snippet', login="admin")
diff --git a/addons/test_website/tests/test_error.py b/addons/test_website/tests/test_error.py
new file mode 100644
index 00000000..f4c9334e
--- /dev/null
+++ b/addons/test_website/tests/test_error.py
@@ -0,0 +1,10 @@
+import odoo.tests
+from odoo.tools import mute_logger
+
+
+@odoo.tests.common.tagged('post_install', '-at_install')
+class TestWebsiteError(odoo.tests.HttpCase):
+
+ @mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
+ def test_01_run_test(self):
+ self.start_tour("/test_error_view", 'test_error_website')
diff --git a/addons/test_website/tests/test_is_multilang.py b/addons/test_website/tests/test_is_multilang.py
new file mode 100644
index 00000000..1c7d6180
--- /dev/null
+++ b/addons/test_website/tests/test_is_multilang.py
@@ -0,0 +1,71 @@
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import odoo.tests
+import lxml
+
+
+@odoo.tests.common.tagged('post_install', '-at_install')
+class TestIsMultiLang(odoo.tests.HttpCase):
+
+ def test_01_is_multilang_url(self):
+ website = self.env['website'].search([], limit=1)
+ fr = self.env.ref('base.lang_fr').sudo()
+ en = self.env.ref('base.lang_en').sudo()
+
+ fr.active = True
+ fr_prefix = "/" + fr.iso_code
+
+ website.default_lang_id = en
+ website.language_ids = en + fr
+
+ for data in [None, {'post': True}]: # GET / POST
+ body = lxml.html.fromstring(self.url_open('/fr/multi_url', data=data).content)
+
+ self.assertEqual(fr_prefix + '/get', body.find('./a[@id="get"]').get('href'))
+ self.assertEqual(fr_prefix + '/post', body.find('./form[@id="post"]').get('action'))
+ self.assertEqual(fr_prefix + '/get_post', body.find('./a[@id="get_post"]').get('href'))
+ self.assertEqual('/get_post_nomultilang', body.find('./a[@id="get_post_nomultilang"]').get('href'))
+
+ def test_02_url_lang_code_underscore(self):
+ website = self.env['website'].browse(1)
+ it = self.env.ref('base.lang_it').sudo()
+ en = self.env.ref('base.lang_en').sudo()
+ be = self.env.ref('base.lang_fr_BE').sudo()
+ country1 = self.env['res.country'].create({'name': "My Super Country"})
+
+ it.active = True
+ be.active = True
+ website.domain = 'http://127.0.0.1:8069' # for _is_canonical_url
+ website.default_lang_id = en
+ website.language_ids = en + it + be
+ params = {
+ 'src': country1.name,
+ 'value': country1.name + ' Italia',
+ 'type': 'model',
+ 'name': 'res.country,name',
+ 'res_id': country1.id,
+ 'lang': it.code,
+ 'state': 'translated',
+ }
+ self.env['ir.translation'].create(params)
+ params.update({
+ 'value': country1.name + ' Belgium',
+ 'lang': be.code,
+ })
+ self.env['ir.translation'].create(params)
+ r = self.url_open('/test_lang_url/%s' % country1.id)
+ self.assertEqual(r.status_code, 200)
+ self.assertTrue(r.url.endswith('/test_lang_url/my-super-country-%s' % country1.id))
+
+ r = self.url_open('/%s/test_lang_url/%s' % (it.url_code, country1.id))
+ self.assertEqual(r.status_code, 200)
+ self.assertTrue(r.url.endswith('/%s/test_lang_url/my-super-country-italia-%s' % (it.url_code, country1.id)))
+
+ body = lxml.html.fromstring(r.content)
+ # Note: this test is indirectly testing the `ref=canonical` tag is correctly set,
+ # as it is required in order for `rel=alternate` tags to be inserted in the DOM
+ it_href = body.find('./head/link[@rel="alternate"][@hreflang="it"]').get('href')
+ fr_href = body.find('./head/link[@rel="alternate"][@hreflang="fr"]').get('href')
+ en_href = body.find('./head/link[@rel="alternate"][@hreflang="en"]').get('href')
+ self.assertTrue(it_href.endswith('/%s/test_lang_url/my-super-country-italia-%s' % (it.url_code, country1.id)))
+ self.assertTrue(fr_href.endswith('/%s/test_lang_url/my-super-country-belgium-%s' % (be.url_code, country1.id)))
+ self.assertTrue(en_href.endswith('/test_lang_url/my-super-country-%s' % country1.id))
diff --git a/addons/test_website/tests/test_multi_company.py b/addons/test_website/tests/test_multi_company.py
new file mode 100644
index 00000000..1395466a
--- /dev/null
+++ b/addons/test_website/tests/test_multi_company.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.tests.common import HttpCase, tagged
+
+
+@tagged('post_install', '-at_install')
+class TestMultiCompany(HttpCase):
+
+ def test_company_in_context(self):
+ """ Test website company is set in context """
+ website = self.env.ref('website.default_website')
+ company = self.env['res.company'].create({'name': "Adaa"})
+ website.company_id = company
+ response = self.url_open('/multi_company_website')
+ self.assertEqual(response.json()[0], company.id)
diff --git a/addons/test_website/tests/test_performance.py b/addons/test_website/tests/test_performance.py
new file mode 100644
index 00000000..cd06dfda
--- /dev/null
+++ b/addons/test_website/tests/test_performance.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.website.tests.test_performance import UtilPerf
+
+
+class TestPerformance(UtilPerf):
+ def test_10_perf_sql_website_controller_minimalist(self):
+ url = '/empty_controller_test'
+ self.assertEqual(self._get_url_hot_query(url), 3)
diff --git a/addons/test_website/tests/test_redirect.py b/addons/test_website/tests/test_redirect.py
new file mode 100644
index 00000000..38eebcde
--- /dev/null
+++ b/addons/test_website/tests/test_redirect.py
@@ -0,0 +1,162 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import odoo
+from odoo.tests import HttpCase, tagged
+from odoo.tests.common import HOST
+from odoo.tools import mute_logger
+from odoo.addons.http_routing.models.ir_http import slug
+
+from unittest.mock import patch
+
+
+@tagged('-at_install', 'post_install')
+class TestRedirect(HttpCase):
+
+ def setUp(self):
+ super(TestRedirect, self).setUp()
+
+ self.user_portal = self.env['res.users'].with_context({'no_reset_password': True}).create({
+ 'name': 'Test Website Portal User',
+ 'login': 'portal_user',
+ 'password': 'portal_user',
+ 'email': 'portal_user@mail.com',
+ 'groups_id': [(6, 0, [self.env.ref('base.group_portal').id])]
+ })
+
+ self.base_url = "http://%s:%s" % (HOST, odoo.tools.config['http_port'])
+
+ def test_01_redirect_308_model_converter(self):
+
+ self.env['website.rewrite'].create({
+ 'name': 'Test Website Redirect',
+ 'redirect_type': '308',
+ 'url_from': '/test_website/country/<model("res.country"):country>',
+ 'url_to': '/redirected/country/<model("res.country"):country>',
+ })
+ country_ad = self.env.ref('base.ad')
+
+ """ Ensure 308 redirect with model converter works fine, including:
+ - Correct & working redirect as public user
+ - Correct & working redirect as logged in user
+ - Correct replace of url_for() URLs in DOM
+ """
+ url = '/test_website/country/' + slug(country_ad)
+ redirect_url = url.replace('test_website', 'redirected')
+
+ # [Public User] Open the original url and check redirect OK
+ r = self.url_open(url)
+ self.assertEqual(r.status_code, 200)
+ self.assertTrue(r.url.endswith(redirect_url), "Ensure URL got redirected")
+ self.assertTrue(country_ad.name in r.text, "Ensure the controller returned the expected value")
+ self.assertTrue(redirect_url in r.text, "Ensure the url_for has replaced the href URL in the DOM")
+
+ # [Logged In User] Open the original url and check redirect OK
+ self.authenticate("portal_user", "portal_user")
+ r = self.url_open(url)
+ self.assertEqual(r.status_code, 200)
+ self.assertTrue(r.url.endswith(redirect_url), "Ensure URL got redirected (2)")
+ self.assertTrue('Logged In' in r.text, "Ensure logged in")
+ self.assertTrue(country_ad.name in r.text, "Ensure the controller returned the expected value (2)")
+ self.assertTrue(redirect_url in r.text, "Ensure the url_for has replaced the href URL in the DOM")
+
+ @mute_logger('odoo.addons.http_routing.models.ir_http') # mute 403 warning
+ def test_02_redirect_308_RequestUID(self):
+ self.env['website.rewrite'].create({
+ 'name': 'Test Website Redirect',
+ 'redirect_type': '308',
+ 'url_from': '/test_website/200/<model("test.model"):rec>',
+ 'url_to': '/test_website/308/<model("test.model"):rec>',
+ })
+
+ rec_published = self.env['test.model'].create({'name': 'name', 'website_published': True})
+ rec_unpublished = self.env['test.model'].create({'name': 'name', 'website_published': False})
+
+ WebsiteHttp = odoo.addons.website.models.ir_http.Http
+
+ def _get_error_html(env, code, value):
+ return str(code).split('_')[-1], "CUSTOM %s" % code
+
+ with patch.object(WebsiteHttp, '_get_error_html', _get_error_html):
+ # Patch will avoid to display real 404 page and regenerate assets each time and unlink old one.
+ # And it allow to be sur that exception id handled by handle_exception and return a "managed error" page.
+
+ # published
+ resp = self.url_open("/test_website/200/name-%s" % rec_published.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 308)
+ self.assertEqual(resp.headers.get('Location'), self.base_url + "/test_website/308/name-%s" % rec_published.id)
+
+ resp = self.url_open("/test_website/308/name-%s" % rec_published.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 200)
+
+ resp = self.url_open("/test_website/200/xx-%s" % rec_published.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 308)
+ self.assertEqual(resp.headers.get('Location'), self.base_url + "/test_website/308/xx-%s" % rec_published.id)
+
+ resp = self.url_open("/test_website/308/xx-%s" % rec_published.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 301)
+ self.assertEqual(resp.headers.get('Location'), self.base_url + "/test_website/308/name-%s" % rec_published.id)
+
+ resp = self.url_open("/test_website/200/xx-%s" % rec_published.id, allow_redirects=True)
+ self.assertEqual(resp.status_code, 200)
+ self.assertEqual(resp.url, self.base_url + "/test_website/308/name-%s" % rec_published.id)
+
+ # unexisting
+ resp = self.url_open("/test_website/200/name-100", allow_redirects=False)
+ self.assertEqual(resp.status_code, 308)
+ self.assertEqual(resp.headers.get('Location'), self.base_url + "/test_website/308/name-100")
+
+ resp = self.url_open("/test_website/308/name-100", allow_redirects=False)
+ self.assertEqual(resp.status_code, 404)
+ self.assertEqual(resp.text, "CUSTOM 404")
+
+ resp = self.url_open("/test_website/200/xx-100", allow_redirects=False)
+ self.assertEqual(resp.status_code, 308)
+ self.assertEqual(resp.headers.get('Location'), self.base_url + "/test_website/308/xx-100")
+
+ resp = self.url_open("/test_website/308/xx-100", allow_redirects=False)
+ self.assertEqual(resp.status_code, 404)
+ self.assertEqual(resp.text, "CUSTOM 404")
+
+ # unpublish
+ resp = self.url_open("/test_website/200/name-%s" % rec_unpublished.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 308)
+ self.assertEqual(resp.headers.get('Location'), self.base_url + "/test_website/308/name-%s" % rec_unpublished.id)
+
+ resp = self.url_open("/test_website/308/name-%s" % rec_unpublished.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 403)
+ self.assertEqual(resp.text, "CUSTOM 403")
+
+ resp = self.url_open("/test_website/200/xx-%s" % rec_unpublished.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 308)
+ self.assertEqual(resp.headers.get('Location'), self.base_url + "/test_website/308/xx-%s" % rec_unpublished.id)
+
+ resp = self.url_open("/test_website/308/xx-%s" % rec_unpublished.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 403)
+ self.assertEqual(resp.text, "CUSTOM 403")
+
+ # with seo_name as slug
+ rec_published.seo_name = "seo_name"
+ rec_unpublished.seo_name = "seo_name"
+
+ resp = self.url_open("/test_website/200/seo-name-%s" % rec_published.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 308)
+ self.assertEqual(resp.headers.get('Location'), self.base_url + "/test_website/308/seo-name-%s" % rec_published.id)
+
+ resp = self.url_open("/test_website/308/seo-name-%s" % rec_published.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 200)
+
+ resp = self.url_open("/test_website/200/xx-%s" % rec_unpublished.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 308)
+ self.assertEqual(resp.headers.get('Location'), self.base_url + "/test_website/308/xx-%s" % rec_unpublished.id)
+
+ resp = self.url_open("/test_website/308/xx-%s" % rec_unpublished.id, allow_redirects=False)
+ self.assertEqual(resp.status_code, 403)
+ self.assertEqual(resp.text, "CUSTOM 403")
+
+ resp = self.url_open("/test_website/200/xx-100", allow_redirects=False)
+ self.assertEqual(resp.status_code, 308)
+ self.assertEqual(resp.headers.get('Location'), self.base_url + "/test_website/308/xx-100")
+
+ resp = self.url_open("/test_website/308/xx-100", allow_redirects=False)
+ self.assertEqual(resp.status_code, 404)
+ self.assertEqual(resp.text, "CUSTOM 404")
diff --git a/addons/test_website/tests/test_reset_views.py b/addons/test_website/tests/test_reset_views.py
new file mode 100644
index 00000000..a11e8282
--- /dev/null
+++ b/addons/test_website/tests/test_reset_views.py
@@ -0,0 +1,111 @@
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import re
+
+import odoo.tests
+from odoo.tools import mute_logger
+
+
+def break_view(view, fr='<p>placeholder</p>', to='<p t-field="not.exist"/>'):
+ view.arch = view.arch.replace(fr, to)
+
+
+@odoo.tests.common.tagged('post_install', '-at_install')
+class TestWebsiteResetViews(odoo.tests.HttpCase):
+
+ def fix_it(self, page, mode='soft'):
+ self.authenticate("admin", "admin")
+ resp = self.url_open(page)
+ self.assertEqual(resp.status_code, 500, "Waiting 500")
+ self.assertTrue('<button data-mode="soft" class="reset_templates_button' in resp.text)
+ data = {'view_id': self.find_template(resp), 'redirect': page, 'mode': mode}
+ resp = self.url_open('/website/reset_template', data)
+ self.assertEqual(resp.status_code, 200, "Waiting 200")
+
+ def find_template(self, response):
+ find = re.search(r'<input.*type="hidden".*name="view_id".*value="([0-9]+)?"', response.text)
+ return find and find.group(1)
+
+ def setUp(self):
+ super(TestWebsiteResetViews, self).setUp()
+ self.Website = self.env['website']
+ self.View = self.env['ir.ui.view']
+ self.test_view = self.Website.viewref('test_website.test_view')
+
+ @mute_logger('odoo.addons.http_routing.models.ir_http')
+ def test_01_reset_specific_page_view(self):
+ self.test_page_view = self.Website.viewref('test_website.test_page_view')
+ total_views = self.View.search_count([('type', '=', 'qweb')])
+ # Trigger COW then break the QWEB XML on it
+ break_view(self.test_page_view.with_context(website_id=1))
+ self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
+ self.fix_it('/test_page_view')
+
+ @mute_logger('odoo.addons.http_routing.models.ir_http')
+ def test_02_reset_specific_view_controller(self):
+ total_views = self.View.search_count([('type', '=', 'qweb')])
+ # Trigger COW then break the QWEB XML on it
+ # `t-att-data="not.exist"` will test the case where exception.html contains branding
+ break_view(self.test_view.with_context(website_id=1), to='<p t-att-data="not.exist" />')
+ self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
+ self.fix_it('/test_view')
+
+ @mute_logger('odoo.addons.http_routing.models.ir_http')
+ def test_03_reset_specific_view_controller_t_called(self):
+ self.test_view_to_be_t_called = self.Website.viewref('test_website.test_view_to_be_t_called')
+
+ total_views = self.View.search_count([('type', '=', 'qweb')])
+ # Trigger COW then break the QWEB XML on it
+ break_view(self.test_view_to_be_t_called.with_context(website_id=1))
+ break_view(self.test_view, to='<t t-call="test_website.test_view_to_be_t_called"/>')
+ self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
+ self.fix_it('/test_view')
+
+ @mute_logger('odoo.addons.http_routing.models.ir_http')
+ def test_04_reset_specific_view_controller_inherit(self):
+ self.test_view_child_broken = self.Website.viewref('test_website.test_view_child_broken')
+
+ # Activate and break the inherited view
+ self.test_view_child_broken.active = True
+ break_view(self.test_view_child_broken.with_context(website_id=1, load_all_views=True))
+
+ self.fix_it('/test_view')
+
+ # This test work in real life, but not in test mode since we cannot rollback savepoint.
+ # @mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.addons.website.models.ir_ui_view')
+ # def test_05_reset_specific_view_controller_broken_request(self):
+ # total_views = self.View.search_count([('type', '=', 'qweb')])
+ # # Trigger COW then break the QWEB XML on it
+ # break_view(self.test_view.with_context(website_id=1), to='<t t-esc="request.env[\'website\'].browse(\'a\').name" />')
+ # self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view (1)")
+ # self.fix_it('/test_view')
+
+ # also mute ir.ui.view as `get_view_id()` will raise "Could not find view object with xml_id 'not.exist'""
+ @mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.addons.website.models.ir_ui_view')
+ def test_06_reset_specific_view_controller_inexisting_template(self):
+ total_views = self.View.search_count([('type', '=', 'qweb')])
+ # Trigger COW then break the QWEB XML on it
+ break_view(self.test_view.with_context(website_id=1), to='<t t-call="not.exist"/>')
+ self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view (2)")
+ self.fix_it('/test_view')
+
+ @mute_logger('odoo.addons.http_routing.models.ir_http')
+ def test_07_reset_page_view_complete_flow(self):
+ self.start_tour("/", 'test_reset_page_view_complete_flow_part1', login="admin")
+ self.fix_it('/test_page_view')
+ self.start_tour("/", 'test_reset_page_view_complete_flow_part2', login="admin")
+ self.fix_it('/test_page_view')
+
+ @mute_logger('odoo.addons.http_routing.models.ir_http')
+ def test_08_reset_specific_page_view_hard_mode(self):
+ self.test_page_view = self.Website.viewref('test_website.test_page_view')
+ total_views = self.View.search_count([('type', '=', 'qweb')])
+ # Trigger COW then break the QWEB XML on it
+ break_view(self.test_page_view.with_context(website_id=1))
+ # Break it again to have a previous arch different than file arch
+ break_view(self.test_page_view.with_context(website_id=1))
+ self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
+ with self.assertRaises(AssertionError):
+ # soft reset should not be able to reset the view as previous
+ # version is also broken
+ self.fix_it('/test_page_view')
+ self.fix_it('/test_page_view', 'hard')
diff --git a/addons/test_website/tests/test_session.py b/addons/test_website/tests/test_session.py
new file mode 100644
index 00000000..22975ebd
--- /dev/null
+++ b/addons/test_website/tests/test_session.py
@@ -0,0 +1,9 @@
+import odoo.tests
+from odoo.tools import mute_logger
+
+
+@odoo.tests.common.tagged('post_install', '-at_install')
+class TestWebsiteSession(odoo.tests.HttpCase):
+
+ def test_01_run_test(self):
+ self.start_tour('/', 'test_json_auth')
diff --git a/addons/test_website/tests/test_views_during_module_operation.py b/addons/test_website/tests/test_views_during_module_operation.py
new file mode 100644
index 00000000..c078afc0
--- /dev/null
+++ b/addons/test_website/tests/test_views_during_module_operation.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.tests import standalone
+
+
+@standalone('cow_views')
+def test_01_cow_views_unlink_on_module_update(env):
+ """ Ensure COW views are correctly removed during module update.
+ Not removing the view could lead to traceback:
+ - Having a view A
+ - Having a view B that inherits from a view C
+ - View B t-call view A
+ - COW view B
+ - Delete view A and B from module datas and update it
+ - Rendering view C will crash since it will render child view B that
+ t-call unexisting view A
+ """
+
+ View = env['ir.ui.view']
+ Imd = env['ir.model.data']
+
+ update_module_base_view = env.ref('test_website.update_module_base_view')
+ update_module_view_to_be_t_called = View.create({
+ 'name': 'View to be t-called',
+ 'type': 'qweb',
+ 'arch': '<div>I will be t-called</div>',
+ 'key': 'test_website.update_module_view_to_be_t_called',
+ })
+ update_module_child_view = View.create({
+ 'name': 'Child View',
+ 'mode': 'extension',
+ 'inherit_id': update_module_base_view.id,
+ 'arch': '''
+ <div position="inside">
+ <t t-call="test_website.update_module_view_to_be_t_called"/>
+ </div>
+ ''',
+ 'key': 'test_website.update_module_child_view',
+ })
+
+ # Create IMD so when updating the module the views will be removed (not found in file)
+ Imd.create({
+ 'module': 'test_website',
+ 'name': 'update_module_view_to_be_t_called',
+ 'model': 'ir.ui.view',
+ 'res_id': update_module_view_to_be_t_called.id,
+ })
+ Imd.create({
+ 'module': 'test_website',
+ 'name': 'update_module_child_view',
+ 'model': 'ir.ui.view',
+ 'res_id': update_module_child_view.id,
+ })
+
+ # Trigger COW on child view
+ update_module_child_view.with_context(website_id=1).write({'name': 'Child View (W1)'})
+
+ # Ensure views are correctly setup
+ msg = "View '%s' does not exist!"
+ assert View.search_count([
+ ('type', '=', 'qweb'),
+ ('key', '=', update_module_child_view.key)
+ ]) == 2, msg % update_module_child_view.key
+ assert bool(env.ref(update_module_view_to_be_t_called.key)),\
+ msg % update_module_view_to_be_t_called.key
+ assert bool(env.ref(update_module_base_view.key)), msg % update_module_base_view.key
+
+ # Upgrade the module
+ test_website_module = env['ir.module.module'].search([('name', '=', 'test_website')])
+ test_website_module.button_immediate_upgrade()
+ env.reset() # clear the set of environments
+ env = env() # get an environment that refers to the new registry
+
+ # Ensure generic views got removed
+ view = env.ref('test_website.update_module_view_to_be_t_called', raise_if_not_found=False)
+ assert not view, "Generic view did not get removed!"
+
+ # Ensure specific COW views got removed
+ assert not env['ir.ui.view'].search_count([
+ ('type', '=', 'qweb'),
+ ('key', '=', 'test_website.update_module_child_view'),
+ ]), "Specific COW views did not get removed!"