diff --git a/cms_form/tests/common.py b/cms_form/tests/common.py index 08e613ec..b2126e47 100644 --- a/cms_form/tests/common.py +++ b/cms_form/tests/common.py @@ -4,7 +4,7 @@ from lxml import html from odoo_test_helper import FakeModelLoader -from odoo.tests.common import HttpCase, TransactionCase +from odoo.tests.common import HttpCase, TransactionCase, tagged from .utils import fake_request, fake_session, session_store @@ -79,12 +79,10 @@ def assert_match_inputs(self, node, expected): self.assertEqual(len(self.find_input_name(node, name)), 1) +@tagged("post_install", "-at_install") class FormTestCase(TransactionCase, FakeModelMixin): """Form test cases.""" - at_install = False - post_install = True - @classmethod def setUpClass(cls): super().setUpClass() @@ -115,6 +113,7 @@ class FormRenderTestCase(FormTestCase, HTMLRenderMixin): """Form test cases where you test HTML rendering.""" +@tagged("post_install", "-at_install") class FormHttpTestCase(HttpCase, FakeModelMixin, HTMLRenderMixin): """Form test cases where you test HTML rendering and HTTP requests.""" diff --git a/cms_form/tests/fake_models/fake_fields_form.py b/cms_form/tests/fake_models/fake_fields_form.py index 4f90be4a..1df280f4 100644 --- a/cms_form/tests/fake_models/fake_fields_form.py +++ b/cms_form/tests/fake_models/fake_fields_form.py @@ -8,6 +8,7 @@ class FakeFloatWidget(models.AbstractModel): _name = "cms.form.test_fields.widget.float" _inherit = "cms.form.widget.float" + _description = _name class FakeFieldsForm(models.AbstractModel): @@ -51,6 +52,7 @@ class FakeFieldsForm2(models.AbstractModel): _name = "cms.form.test_fields2" _inherit = "cms.form.test_fields" + _description = _name a_float_with_another_widget = fields.Float( form_widget={"model": FakeFloatWidget._name} diff --git a/cms_form/tests/test_controllers.py b/cms_form/tests/test_controllers.py index 7e7acfe2..5b52206b 100644 --- a/cms_form/tests/test_controllers.py +++ b/cms_form/tests/test_controllers.py @@ -5,6 +5,8 @@ import os import unittest +from odoo.addons.website.tools import MockRequest + from ..controllers import main from .common import FormHttpTestCase, FormTestCase from .utils import fake_request, mock_request @@ -26,6 +28,8 @@ def setUp(self): self.form_controller = main.CMSFormController() self.form_search_controller = main.CMSSearchFormController() self.form_wiz_controller = main.CMSWizardFormController() + # do not rely on demo data (eg: `base.res_partner_12`) being loaded + self.test_partner = self.env["res.partner"].create({"name": "Test Partner"}) def test_get_template(self): with mock_request(self.env): @@ -56,7 +60,7 @@ def test_get_render_values(self): }, ) # get a main obj - partner = self.env.ref("base.res_partner_12") + partner = self.test_partner form = self.form_controller.get_form("res.partner", model_id=partner.id) self.assertEqual( self.form_controller.get_render_values(form), @@ -129,7 +133,7 @@ def test_redirect_after_success(self): method="POST", ) with mock_request(self.env, httprequest=req.httprequest): - partner = self.env.ref("base.res_partner_12") + partner = self.test_partner response = self.form_controller.make_response( "res.partner", model_id=partner.id ) @@ -143,6 +147,25 @@ def test_redirect_after_success(self): @unittest.skipIf(os.getenv("SKIP_HTTP_CASE"), "HTTP case disabled.") class TestControllersRender(FormHttpTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Warm up frontend QWeb rendering (`web.frontend_layout` / + # `portal.portal_layout`, which pulls in `portal.language_selector`) + # once here, before any real HTTP request is made. + # Compiling/preparing the frontend qweb env for the very first time + # races with `ir.qweb._prepare_frontend_environment`: `languages` can + # end up unset, causing a sporadic 500 on the first HTTP request + # (confirmed on real OCA CI, not just locally). + # We use `website`'s bare `MockRequest` here rather than our own + # `mock_request` helper: the latter deliberately empties + # `registry._init_modules` for its own purposes, which breaks asset + # bundle lookups needed to actually render this template. + with MockRequest(cls.env): + cls.env["ir.qweb"].with_context(lang="en_US")._render( + "portal.portal_layout", {} + ) + def setUp(self): super().setUp() self.authenticate("admin", "admin") @@ -193,7 +216,8 @@ def test_default_create_rendering(self): self._check_rendering(dom, "cms.form.res.partner", "res.partner", "create") def test_default_edit_rendering(self): - partner = self.env.ref("base.res_partner_1") + # do not rely on demo data (eg: `base.res_partner_1`) being loaded + partner = self.env["res.partner"].create({"name": "Test Partner"}) dom = self.html_get("/cms/edit/res.partner/{}".format(partner.id)) self._check_rendering(dom, "cms.form.res.partner", "res.partner", "edit") diff --git a/cms_form/tests/test_form_base.py b/cms_form/tests/test_form_base.py index de320761..93303580 100644 --- a/cms_form/tests/test_form_base.py +++ b/cms_form/tests/test_form_base.py @@ -6,6 +6,7 @@ from werkzeug.wrappers import Request from odoo import http +from odoo.tests.common import new_test_user from .common import FormTestCase from .utils import fake_request @@ -183,7 +184,8 @@ def test_fields_binary(self): def test_fields_protected(self): group = self.env.ref("base.group_system") - user = self.env.ref("base.user_demo") + # plain internal user, not relying on demo data being loaded + user = new_test_user(self.env, login="test_fields_protected_user") # user does not have the group self.assertNotIn(group, user.groups_id) form = self.get_form("cms.form.protected.fields", sudo_uid=user.id) diff --git a/cms_form/tests/test_form_render.py b/cms_form/tests/test_form_render.py index 45705b8e..f0a4c969 100644 --- a/cms_form/tests/test_form_render.py +++ b/cms_form/tests/test_form_render.py @@ -1,6 +1,8 @@ # Copyright 2017 Simone Orsi # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from odoo.tests.common import new_test_user + from .common import FormRenderTestCase @@ -100,7 +102,8 @@ def test_render_form_fieldsets_get(self): def test_render_form_fieldsets_protected_field(self): """No field, no fieldset.""" - user = self.env.ref("base.user_demo") + # plain internal user, not relying on demo data being loaded + user = new_test_user(self.env, login="test_render_fieldsets_protected_user") form = self.get_form("cms.form.test_fieldsets", sudo_uid=user.id) html = form.form_render() node = self.to_xml_node(html)[0] diff --git a/cms_form/tests/utils.py b/cms_form/tests/utils.py index cccd6479..d3b8ecd9 100644 --- a/cms_form/tests/utils.py +++ b/cms_form/tests/utils.py @@ -77,9 +77,17 @@ def mock_request( for k, v in httprequest_attrs.items(): setattr(mocked_request.httprequest, k, v) mocked_request.make_response = lambda data, **kw: data + # `MockRequest` exposes the real (shared, process-wide) registry, not a copy. + # Mutating `_init_modules` here used to leak into every other test running + # afterwards (breaking asset bundling and HTTP routing map generation for + # the rest of the test run), so make sure to restore it once done. + original_init_modules = mocked_request.registry._init_modules mocked_request.registry._init_modules = set() mocked_request.session.touch = lambda: True - yield mocked_request + try: + yield mocked_request + finally: + mocked_request.registry._init_modules = original_init_modules class FakeSessionStore(SessionStore): diff --git a/cms_form/tests/widgets/test_widget_x2many.py b/cms_form/tests/widgets/test_widget_x2many.py index 3c1ab691..6ef13bf7 100644 --- a/cms_form/tests/widgets/test_widget_x2many.py +++ b/cms_form/tests/widgets/test_widget_x2many.py @@ -80,7 +80,11 @@ def test_widget_x2many_base_load(self): ) def test_widget_x2many_base_load_from_record(self): - categs = self.env["res.partner.category"].search([], limit=3) + # Do not rely on demo data or other modules' data being present: + # create dedicated categories so the test is self-contained. + categs = self.env["res.partner.category"].create( + [{"name": f"Fake category {i}"} for i in range(3)] + ) partner = self.partners[0] form = fake_form( self.env,