+
-
+
@@ -103,7 +124,9 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-
+
@@ -126,7 +149,8 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-
-
-
-
-
+
+
+
-
+
-
+
-
+
-
+
@@ -206,21 +233,31 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
name="CMS search form wrapper"
inherit_id="cms_form.form_wrapper"
primary="True"
+ priority="100"
>
-
+
-
+
-
+
+
+
+
+
+
+
+
+
@@ -233,8 +270,9 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
@@ -276,6 +315,11 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
+
+
+
-
+
+
+
+
+
+
@@ -309,6 +358,7 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
name="CMS wizard form buttons"
inherit_id="cms_form.base_form_buttons"
primary="True"
+ priority="100"
>
@@ -317,13 +367,15 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
type="submit"
name="wiz_submit"
value="next"
- class="btn btn-primary btn-next pull-right"
+ class="btn btn-primary btn-next float-end"
>Next
@@ -331,7 +383,7 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
type="submit"
name="wiz_submit"
value="prev"
- class="btn btn-primary btn-prev pull-left"
+ class="btn btn-primary btn-prev float-start"
>Prev
diff --git a/cms_form/templates/portal.xml b/cms_form/templates/portal.xml
new file mode 100644
index 00000000..91d25d1d
--- /dev/null
+++ b/cms_form/templates/portal.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cms_form/templates/widgets.xml b/cms_form/templates/widgets.xml
index 64d77b82..56d0df3a 100644
--- a/cms_form/templates/widgets.xml
+++ b/cms_form/templates/widgets.xml
@@ -5,24 +5,38 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-->
+
+
+
+ widget.html_input_min
+ widget.html_input_max
+
+
@@ -31,9 +45,10 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
@@ -101,7 +118,7 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
@@ -151,16 +170,17 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
+ t-att-readonly="widget.html_readonly or None"
+ >
+
+
@@ -190,8 +225,9 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
capture="camera"
accept="image/*"
t-att-id="widget.w_fname"
- t-att-name="widget.w_fname"
+ t-att-name="widget.html_fname"
class="form-control"
+ t-att-readonly="widget.html_readonly or None"
/>
@@ -232,4 +268,15 @@ License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
+
+
+
diff --git a/cms_form/tests/common.py b/cms_form/tests/common.py
index 44a176a2..08e613ec 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, SavepointCase
+from odoo.tests.common import HttpCase, TransactionCase
from .utils import fake_request, fake_session, session_store
@@ -79,7 +79,7 @@ def assert_match_inputs(self, node, expected):
self.assertEqual(len(self.find_input_name(node, name)), 1)
-class FormTestCase(SavepointCase, FakeModelMixin):
+class FormTestCase(TransactionCase, FakeModelMixin):
"""Form test cases."""
at_install = False
@@ -118,6 +118,7 @@ class FormRenderTestCase(FormTestCase, HTMLRenderMixin):
class FormHttpTestCase(HttpCase, FakeModelMixin, HTMLRenderMixin):
"""Form test cases where you test HTML rendering and HTTP requests."""
+ # FIXME: use setupClass
def setUp(self):
# HttpCase has no ENV on setUpClass we have to setup fake models here
super().setUp()
@@ -130,7 +131,13 @@ def tearDown(self):
def html_get(self, url):
resp = self.url_open(url, timeout=30)
- return html.document_fromstring(resp.content)
+ return self.parse_html(resp.content)
+
+ def parse_html(self, content, fragment=False):
+ parser = html.document_fromstring
+ if fragment:
+ parser = html.fragment_fromstring
+ return parser(content)
def get_form(self, form_model, **kw):
return get_form(self.env, form_model, **kw)
diff --git a/cms_form/tests/fake_models/fake_fields_form.py b/cms_form/tests/fake_models/fake_fields_form.py
index 1e3a2ee8..4f90be4a 100644
--- a/cms_form/tests/fake_models/fake_fields_form.py
+++ b/cms_form/tests/fake_models/fake_fields_form.py
@@ -4,6 +4,12 @@
from odoo import fields, models
+class FakeFloatWidget(models.AbstractModel):
+
+ _name = "cms.form.test_fields.widget.float"
+ _inherit = "cms.form.widget.float"
+
+
class FakeFieldsForm(models.AbstractModel):
"""A test model form."""
@@ -19,8 +25,8 @@ class FakeFieldsForm(models.AbstractModel):
a_one2many = fields.Char()
a_many2many = fields.Char()
- def _form_fields(self):
- _fields = super()._form_fields()
+ def _form_fields_get(self):
+ _fields = super()._form_fields_get()
# fake fields' types
_fields["a_many2one"]["type"] = "many2one"
_fields["a_many2one"]["relation"] = "res.partner"
@@ -38,3 +44,14 @@ def _form_validate_a_float(self, value, **request_values):
def _form_validate_char(self, value, **request_values):
"""Specific validator for all `char` fields."""
return not len(value) > 8, "Text length must be greater than 8!"
+
+
+class FakeFieldsForm2(models.AbstractModel):
+ """A test model form."""
+
+ _name = "cms.form.test_fields2"
+ _inherit = "cms.form.test_fields"
+
+ a_float_with_another_widget = fields.Float(
+ form_widget={"model": FakeFloatWidget._name}
+ )
diff --git a/cms_form/tests/fake_models/fake_fields_form_fieldsets.py b/cms_form/tests/fake_models/fake_fields_form_fieldsets.py
index 52cdd859..f039205a 100644
--- a/cms_form/tests/fake_models/fake_fields_form_fieldsets.py
+++ b/cms_form/tests/fake_models/fake_fields_form_fieldsets.py
@@ -3,6 +3,8 @@
from odoo import fields, models
+from odoo.addons.cms_form.models.fields import Serialized # pylint: disable=W8150
+
class FakeFieldsFormWithFieldsets(models.AbstractModel):
"""A test model form."""
@@ -10,20 +12,22 @@ class FakeFieldsFormWithFieldsets(models.AbstractModel):
_name = "cms.form.test_fieldsets"
_inherit = "cms.form.test_fields"
_description = "CMS Form test fieldsets form"
- _form_fieldsets = [
- {"id": "main", "fields": ["a_char"]},
- {
- "id": "numbers",
- "title": "Number fields",
- "description": "Only number fields here",
- "fields": ["a_number", "a_float"],
- "css_extra_klass": "best_fieldset",
- },
- {
- "id": "relations",
- "title": "Only relations here",
- "fields": ["a_many2one", "a_many2many", "a_one2many"],
- },
- {"id": "protected", "fields": ["ihaveagroup"]},
- ]
- ihaveagroup = fields.Char(groups="website.group_website_designer")
+ form_fieldsets = Serialized(
+ default=[
+ {"id": "main", "fields": ["a_char"]},
+ {
+ "id": "numbers",
+ "title": "Number fields",
+ "description": "Only number fields here",
+ "fields": ["a_number", "a_float"],
+ "css_extra_klass": "best_fieldset",
+ },
+ {
+ "id": "relations",
+ "title": "Only relations here",
+ "fields": ["a_many2one", "a_many2many", "a_one2many"],
+ },
+ {"id": "protected", "fields": ["ihaveagroup"]},
+ ]
+ )
+ ihaveagroup = fields.Char(groups="base.group_system")
diff --git a/cms_form/tests/fake_models/fake_nonpub_model_form.py b/cms_form/tests/fake_models/fake_nonpub_model_form.py
index 3fa276f2..6a606f52 100644
--- a/cms_form/tests/fake_models/fake_nonpub_model_form.py
+++ b/cms_form/tests/fake_models/fake_nonpub_model_form.py
@@ -3,9 +3,10 @@
from odoo import fields, models
+from odoo.addons.cms_form.models.fields import Serialized # pylint: disable=W8150
-# `AbstractModel` or `TransientModel` needed to make ACL check happy`
-class FakeNonPubModel(models.TransientModel):
+
+class FakeNonPubModel(models.Model):
_name = "fake.non.publishable"
_description = "CMS Form fake non publishable model"
name = fields.Char()
@@ -15,5 +16,6 @@ class FakeNonPubModelForm(models.AbstractModel):
_name = "cms.form.fake.non.publishable"
_inherit = "cms.form"
_description = "CMS Form fake non publishable model form"
- _form_model = "fake.non.publishable"
- _form_model_fields = ("name",)
+
+ form_model_name = fields.Char(default="fake.non.publishable")
+ form_model_fields = Serialized(default=("name",))
diff --git a/cms_form/tests/fake_models/fake_partner_channel_form.py b/cms_form/tests/fake_models/fake_partner_channel_form.py
index 56ace6a7..4bacd47f 100644
--- a/cms_form/tests/fake_models/fake_partner_channel_form.py
+++ b/cms_form/tests/fake_models/fake_partner_channel_form.py
@@ -1,15 +1,24 @@
# Copyright 2017 Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-from odoo import models
+from odoo import fields, models
-class FakePartnerChannelForm(models.AbstractModel):
+class FakePartnerRelModel(models.Model):
+ _name = "fake.partner.related"
+ _description = _name
+ _rec_name = "partner_id"
+
+ foo = fields.Char()
+ partner_id = fields.Many2one("res.partner")
+
+
+class FakePartnerRelatedForm(models.AbstractModel):
"""A test model form."""
- _name = "cms.form.mail.channel.partner"
+ _name = "cms.form.rel.partner"
_inherit = "cms.form"
- _description = "CMS Form test partner channel form"
+ _description = "CMS Form test partner form"
# This model has `_rec_name = 'partner_id'` and allows us
# to test a specific case for form_title computation
- _form_model = "mail.channel.partner"
+ form_model_name = fields.Char(default=FakePartnerRelModel._name)
diff --git a/cms_form/tests/fake_models/fake_partner_form.py b/cms_form/tests/fake_models/fake_partner_form.py
index 577d0907..4943763a 100644
--- a/cms_form/tests/fake_models/fake_partner_form.py
+++ b/cms_form/tests/fake_models/fake_partner_form.py
@@ -3,6 +3,8 @@
from odoo import fields, models
+from odoo.addons.cms_form.models.fields import Serialized # pylint: disable=W8150
+
class FakePartnerForm(models.AbstractModel):
"""A test model form."""
@@ -10,9 +12,10 @@ class FakePartnerForm(models.AbstractModel):
_name = "cms.form.res.partner"
_inherit = "cms.form"
_description = "CMS Form test partner form"
- _form_model = "res.partner"
- _form_model_fields = ("name", "country_id")
- _form_required_fields = ("name", "country_id")
+
+ form_model_name = fields.Char(default="res.partner")
+ form_model_fields = Serialized(default=("name", "country_id"))
+ form_required_fields = Serialized(default=("name", "country_id"))
custom = fields.Char(default=lambda self: "I am your default")
diff --git a/cms_form/tests/fake_models/fake_partner_form_protected_fields.py b/cms_form/tests/fake_models/fake_partner_form_protected_fields.py
index 6ff132db..b63b96ab 100644
--- a/cms_form/tests/fake_models/fake_partner_form_protected_fields.py
+++ b/cms_form/tests/fake_models/fake_partner_form_protected_fields.py
@@ -14,4 +14,4 @@ class FakePartnerFormProtectedFields(models.AbstractModel):
_form_fields_order = ["ihaveagroup", "nogroup"]
nogroup = fields.Char()
- ihaveagroup = fields.Char(groups="website.group_website_designer")
+ ihaveagroup = fields.Char(groups="base.group_system")
diff --git a/cms_form/tests/fake_models/fake_pub_model_form.py b/cms_form/tests/fake_models/fake_pub_model_form.py
index 77c0b71a..15c3c7d8 100644
--- a/cms_form/tests/fake_models/fake_pub_model_form.py
+++ b/cms_form/tests/fake_models/fake_pub_model_form.py
@@ -3,24 +3,27 @@
from odoo import fields, models
+from odoo.addons.cms_form.models.fields import Serialized # pylint: disable=W8150
-# `AbstractModel` or `TransientModel` needed to make ACL check happy`
-class FakePubModel(models.TransientModel):
+
+class FakePubModel(models.Model):
_name = "fake.publishable"
_inherit = [
- "website.published.mixin",
+ "cms.info.mixin",
]
_description = "CMS Form fake publishable model form"
+
name = fields.Char()
- def _compute_website_url(self):
+ def _compute_cms_view_url(self):
for item in self:
- item.website_url = "/publishable/%d" % item.id
+ item.url = "/publishable/%d" % item.id
class FakePubModelForm(models.AbstractModel):
_name = "cms.form.fake.publishable"
_inherit = "cms.form"
_description = "CMS Form fake publishable form"
- _form_model = "fake.publishable"
- _form_model_fields = ("name",)
+
+ form_model_name = fields.Char(default="fake.publishable")
+ form_model_fields = Serialized(default=("name",))
diff --git a/cms_form/tests/fake_models/fake_search_partner_form.py b/cms_form/tests/fake_models/fake_search_partner_form.py
index 9d390ce7..97f0b321 100644
--- a/cms_form/tests/fake_models/fake_search_partner_form.py
+++ b/cms_form/tests/fake_models/fake_search_partner_form.py
@@ -1,7 +1,11 @@
# Copyright 2017 Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-from odoo import models
+from odoo import fields, models
+
+from odoo.addons.cms_form.models.fields import Serialized # pylint: disable=W8150
+
+TEST_RECORD_IDS = []
class FakeSearchPartnerForm(models.AbstractModel):
@@ -10,22 +14,30 @@ class FakeSearchPartnerForm(models.AbstractModel):
_name = "cms.form.search.res.partner"
_inherit = "cms.form.search"
_description = "CMS Form test partner search form"
- _form_model = "res.partner"
- _form_model_fields = (
- "name",
- "country_id",
- )
+
+ form_model_name = fields.Char(default="res.partner")
+ form_model_fields = Serialized(default=("name", "country_id"))
def form_search_domain(self, search_values):
"""Force domain to include only test-created records."""
domain = super().form_search_domain(search_values)
# we use this attr in tests to limit search results
# to test records' scope
- include_only_ids = getattr(self, "test_record_ids", [])
+ include_only_ids = self._get_test_record_ids()
if include_only_ids:
domain.append(("id", "in", include_only_ids))
return domain
+ @classmethod
+ def _get_test_record_ids(cls):
+ global TEST_RECORD_IDS
+ return TEST_RECORD_IDS
+
+ @classmethod
+ def _set_test_record_ids(cls, ids):
+ global TEST_RECORD_IDS
+ TEST_RECORD_IDS = ids
+
class FakeSearchPartnerFormMulti(models.AbstractModel):
"""A test model search form w/ multiple values for country."""
@@ -33,10 +45,9 @@ class FakeSearchPartnerFormMulti(models.AbstractModel):
_name = "cms.form.search.res.partner.multicountry"
_inherit = "cms.form.search.res.partner"
_description = "CMS Form test partner search multi form"
- _form_search_fields_multi = ("country_id",)
- @property
- def form_widgets(self):
- res = super().form_widgets
- res.update({"country_id": "cms.form.widget.many2one.multi"})
- return res
+ form_search_fields_multi = Serialized(default=("country_id",))
+ country_id = fields.Many2one(
+ comodel_name="res.country",
+ form_widget={"model": "cms.form.widget.many2one.multi"},
+ )
diff --git a/cms_form/tests/fake_models/fake_wizard_form.py b/cms_form/tests/fake_models/fake_wizard_form.py
index 2456f0e8..3ac6f021 100644
--- a/cms_form/tests/fake_models/fake_wizard_form.py
+++ b/cms_form/tests/fake_models/fake_wizard_form.py
@@ -3,6 +3,8 @@
from odoo import fields, models
+from odoo.addons.cms_form.models.fields import Serialized # pylint: disable=W8150
+
class FakeWiz(models.AbstractModel):
"""A wizard form."""
@@ -35,8 +37,8 @@ class FakeWizStep1Country(models.AbstractModel):
_name = "fake.wiz.step1.country"
_inherit = "fake.wiz"
_description = "CMS Form test wizard form step 1"
- _form_model = "res.country"
- _form_model_fields = ("name",)
+ form_model_name = fields.Char(default="res.country")
+ form_model_fields = Serialized(default=("name",))
class FakeWizStep2Partner(models.AbstractModel):
@@ -44,12 +46,13 @@ class FakeWizStep2Partner(models.AbstractModel):
_name = "fake.wiz.step2.partner"
_inherit = "fake.wiz"
_description = "CMS Form test wizard form step 2"
- _form_model = "res.partner"
- _form_model_fields = (
- "name",
- "to_be_stored",
+ form_model_name = fields.Char(default="res.partner")
+ form_model_fields = Serialized(default=("name", "to_be_stored"))
+ form_step_stored_fields = Serialized(
+ default=[
+ "to_be_stored",
+ ]
)
- _wiz_step_stored_fields = ("to_be_stored",)
to_be_stored = fields.Char()
@@ -59,8 +62,8 @@ class FakeWizStep3Partner(models.AbstractModel):
_name = "fake.wiz.step3.partner"
_inherit = "fake.wiz"
_description = "CMS Form test wizard form step 3"
- _form_model = "res.partner"
- _form_model_fields = ("name",)
+ form_model_name = fields.Char(default="res.partner")
+ form_model_fields = Serialized(default=("name",))
ALL_WIZ_KLASSES = [
diff --git a/cms_form/tests/test_controllers.py b/cms_form/tests/test_controllers.py
index 2e94873e..7e7acfe2 100644
--- a/cms_form/tests/test_controllers.py
+++ b/cms_form/tests/test_controllers.py
@@ -1,21 +1,18 @@
# Copyright 2017 Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
+import json
import os
import unittest
-from contextlib import contextmanager
-
-import mock
from ..controllers import main
-from .common import FormHttpTestCase
-from .utils import fake_request
+from .common import FormHttpTestCase, FormTestCase
+from .utils import fake_request, mock_request
IMPORT = "odoo.addons.cms_form.controllers.main"
-@unittest.skipIf(os.getenv("SKIP_HTTP_CASE"), "HTTP case disabled.")
-class TestControllers(FormHttpTestCase):
+class TestControllersAPI(FormTestCase):
@staticmethod
def _get_test_models():
from .fake_models.fake_partner_form import FakePartnerForm
@@ -29,26 +26,14 @@ def setUp(self):
self.form_controller = main.CMSFormController()
self.form_search_controller = main.CMSSearchFormController()
self.form_wiz_controller = main.CMSWizardFormController()
- self.authenticate("admin", "admin")
-
- @contextmanager
- def mock_assets(self, req=None):
- """Mocks some stuff like request."""
- with mock.patch("%s.request" % IMPORT) as request:
- faked = req or fake_request()
- request.session = self.session
- request.env = self.env
- request.httprequest = faked.httprequest
- yield {
- "request": request,
- }
def test_get_template(self):
- with self.mock_assets():
+ with mock_request(self.env):
form = self.form_controller.get_form("res.partner")
# default
self.assertEqual(
- self.form_controller.get_template(form), "cms_form.form_wrapper",
+ self.form_controller.get_template(form),
+ "cms_form.portal_form_wrapper",
)
# custom on form
form.form_wrapper_template = "foo.baz"
@@ -59,7 +44,7 @@ def test_get_template(self):
self.form_controller.get_template(form)
def test_get_render_values(self):
- with self.mock_assets():
+ with mock_request(self.env):
form = self.form_controller.get_form("res.partner")
# default, no main object
self.assertEqual(
@@ -95,63 +80,81 @@ def test_get_render_values(self):
)
def test_get_no_form(self):
- with self.mock_assets():
+ with mock_request(self.env):
# we do not have a specific form for res.groups
# and cms form is not enabled on partner model
with self.assertRaises(NotImplementedError):
self.form_controller.get_form("res.groups")
def test_get_form_no_model_no_main_object(self):
- with self.mock_assets():
+ with mock_request(self.env):
form = self.form_controller.get_form(
None, form_model_key=self.FakePartnerForm._name
)
self.assertEqual(form.main_object, self.env[self.FakePartnerForm._name])
def test_get_default_form(self):
- with self.mock_assets():
+ with mock_request(self.env):
# we have one for res.partner
form = self.form_controller.get_form("res.partner")
self.assertTrue(
isinstance(form, self.env["cms.form.res.partner"].__class__)
)
- self.assertEqual(form._form_model, "res.partner")
+ self.assertEqual(form.form_model_name, "res.partner")
self.assertEqual(form.form_mode, "create")
def test_get_specific_form(self):
- with self.mock_assets():
+ with mock_request(self.env):
# we have a specific form here
form = self.form_search_controller.get_form("res.partner")
self.assertTrue(
isinstance(form, self.env["cms.form.search.res.partner"].__class__)
)
- self.assertEqual(form._form_model, "res.partner")
+ self.assertEqual(form.form_model_name, "res.partner")
self.assertEqual(form.form_mode, "search")
def test_get_wizard_form(self):
- with self.mock_assets():
+ with mock_request(self.env):
# we have a specific form here
form = self.form_wiz_controller.get_form("res.partner")
self.assertTrue(
isinstance(form, self.env["cms.form.res.partner"].__class__)
)
- self.assertEqual(form._form_model, "res.partner")
+ self.assertEqual(form.form_model_name, "res.partner")
self.assertEqual(form.form_mode, "create")
def test_redirect_after_success(self):
- req = fake_request(form_data={"name": "John"}, method="POST",)
- with self.mock_assets(req=req):
+ req = fake_request(
+ form_data={"name": "John"},
+ method="POST",
+ )
+ with mock_request(self.env, httprequest=req.httprequest):
partner = self.env.ref("base.res_partner_12")
response = self.form_controller.make_response(
"res.partner", model_id=partner.id
)
self.assertEqual(response.status_code, 303)
- if "website_url" in partner:
+ if "url" in partner:
# website_partner installed
- self.assertEqual(response.location, partner.website_url)
+ self.assertEqual(response.location, partner.url)
else:
self.assertEqual(response.location, "/")
+
+@unittest.skipIf(os.getenv("SKIP_HTTP_CASE"), "HTTP case disabled.")
+class TestControllersRender(FormHttpTestCase):
+ def setUp(self):
+ super().setUp()
+ self.authenticate("admin", "admin")
+
+ @staticmethod
+ def _get_test_models():
+ from .fake_models.fake_partner_form import FakePartnerForm
+ from .fake_models.fake_search_partner_form import FakeSearchPartnerForm
+ from .fake_models.fake_wizard_form import ALL_WIZ_KLASSES
+
+ return ALL_WIZ_KLASSES + [FakePartnerForm, FakeSearchPartnerForm]
+
def _check_rendering(self, dom, form_model, model, mode, extra_klass=""):
"""Check default markup for form and form wrapper."""
# test wrapper klass
@@ -223,3 +226,33 @@ def test_default_wiz_rendering(self):
"wizard",
extra_klass="fake_wiz",
)
+ # TODO: check more (paging etc)
+
+ def test_render_only_form(self):
+ url = self.base_url() + "/cms/render/form/cms.form.res.partner"
+ resp = self.url_open(
+ url,
+ data=json.dumps({"widget_params": {}}),
+ headers={"Content-Type": "application/json"},
+ timeout=30,
+ )
+ form = resp.json()["result"]["form"]
+ dom = self.parse_html(form, fragment=True)
+ self.assertTrue(dom.tag == "form")
+ data_form = json.loads(dom.attrib["data-form"])
+ self.assertEqual(data_form["model"], "res.partner")
+
+ def test_render_only_form_widget(self):
+ url = self.base_url() + "/cms/render/form/cms.form.res.partner"
+ resp = self.url_open(
+ url,
+ data=json.dumps({"widget_params": {"custom": {}}}),
+ headers={"Content-Type": "application/json"},
+ timeout=30,
+ )
+ by_widget = resp.json()["result"]["by_widget"]
+ dom = self.parse_html(by_widget["custom"], fragment=True)
+ self.assertTrue(dom.tag == "input")
+ self.assertEqual(dom.attrib["id"], "custom")
+ self.assertEqual(dom.attrib["name"], "custom")
+ self.assertEqual(dom.attrib["value"], "oh yeah!")
diff --git a/cms_form/tests/test_form_base.py b/cms_form/tests/test_form_base.py
index 2d039dfe..de320761 100644
--- a/cms_form/tests/test_form_base.py
+++ b/cms_form/tests/test_form_base.py
@@ -1,7 +1,8 @@
# Copyright 2017 Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-import mock
+from unittest import mock
+
from werkzeug.wrappers import Request
from odoo import http
@@ -13,59 +14,75 @@
class TestFormBase(FormTestCase):
@staticmethod
def _get_test_models():
- from .fake_models.fake_fields_form import FakeFieldsForm
+ from .fake_models.fake_fields_form import (
+ FakeFieldsForm,
+ FakeFieldsForm2,
+ FakeFloatWidget,
+ )
from .fake_models.fake_partner_form import FakePartnerForm
from .fake_models.fake_partner_form_protected_fields import (
FakePartnerFormProtectedFields,
)
- return (FakeFieldsForm, FakePartnerForm, FakePartnerFormProtectedFields)
+
+ return (
+ FakeFieldsForm,
+ FakeFieldsForm2,
+ FakeFloatWidget,
+ FakePartnerForm,
+ FakePartnerFormProtectedFields,
+ )
def test_form_init(self):
form = self.get_form("cms.form.mixin")
self.assertTrue(isinstance(form.request, Request))
- self.assertTrue(isinstance(form.o_request, http.HttpRequest))
+ self.assertTrue(isinstance(form.o_request, http.Request))
def test_form_init_overrides(self):
overrides = dict(
- model="res.partner",
- mode="foo",
- fields_whitelist=("name",),
- fields_blacklist=("country_id",),
- fields_attributes=("string", "type",),
- wrapper_extra_css_klass="foo",
- extra_css_klass="baz",
+ form_model_name="res.partner",
+ form_mode="foo",
+ form_fields_whitelist=("name",),
+ form_fields_blacklist=("country_id",),
+ form_fields_attributes=(
+ "string",
+ "type",
+ ),
+ form_wrapper_extra_css_klass="foo",
+ form_extra_css_klass="baz",
)
form = self.get_form("cms.form.mixin", **overrides)
for k, v in overrides.items():
- self.assertEqual(getattr(form, "_form_" + k), v)
+ self.assertEqual(form[k], v)
def test_form_mode(self):
form = self.get_form("cms.form.mixin")
self.assertEqual(form.form_mode, "create")
form = self.get_form("cms.form.mixin", main_object=object())
self.assertEqual(form.form_mode, "edit")
- form = self.get_form("cms.form.mixin", mode="custom")
+ form = self.get_form("cms.form.mixin", form_mode="custom")
self.assertEqual(form.form_mode, "custom")
def test_fields_load(self):
form = self.get_form("cms.form.res.partner")
- fields = form.form_fields()
+ fields = form.form_fields_get()
self.assertEqual(len(fields), 3)
self.assertTrue("name" in list(fields.keys()))
self.assertTrue("country_id" in list(fields.keys()))
self.assertTrue("custom" in list(fields.keys()))
# whitelist
- form = self.get_form("cms.form.res.partner", fields_whitelist=("name",))
- fields = form.form_fields()
+ form = self.get_form("cms.form.res.partner", form_fields_whitelist=("name",))
+ fields = form.form_fields_get()
self.assertEqual(len(fields), 1)
self.assertTrue("name" in list(fields.keys()))
self.assertTrue("country_id" not in list(fields.keys()))
self.assertTrue("custom" not in list(fields.keys()))
# blacklist
- form = self.get_form("cms.form.res.partner", fields_blacklist=("country_id",))
- fields = form.form_fields()
+ form = self.get_form(
+ "cms.form.res.partner", form_fields_blacklist=("country_id",)
+ )
+ fields = form.form_fields_get()
self.assertEqual(len(fields), 2)
self.assertTrue("name" in list(fields.keys()))
self.assertTrue("country_id" not in list(fields.keys()))
@@ -73,25 +90,34 @@ def test_fields_load(self):
def test_fields_order(self):
form = self.get_form(
- "cms.form.res.partner", fields_order=["name", "custom", "country_id"],
+ "cms.form.res.partner",
+ form_fields_order=["name", "custom", "country_id"],
)
- fields = form.form_fields()
+ fields = form.form_fields_get()
self.assertEqual(list(fields.keys())[0], "name")
self.assertEqual(list(fields.keys())[1], "custom")
self.assertEqual(list(fields.keys())[2], "country_id")
# change order
form = self.get_form(
- "cms.form.res.partner", fields_order=["country_id", "name", "custom"],
+ "cms.form.res.partner",
+ form_fields_order=["country_id", "name", "custom"],
)
- fields = form.form_fields()
+ fields = form.form_fields_get()
self.assertEqual(list(fields.keys())[0], "country_id")
self.assertEqual(list(fields.keys())[1], "name")
self.assertEqual(list(fields.keys())[2], "custom")
+ def test_form_fields_get(self):
+ form = self.get_form("cms.form.res.partner")
+ fields = form.form_fields_get()
+ # must include ONLY non tech fields and model fields
+ expected = ["name", "country_id", "custom"]
+ self.assertEqual(sorted(fields.keys()), sorted(expected))
+
def test_fields_attributes(self):
form = self.get_form("cms.form.res.partner")
- fields = form.form_fields()
+ fields = form.form_fields_get()
# this one is required in partner model
self.assertTrue(fields["name"]["required"])
# this one is forced to required in our custom form
@@ -100,50 +126,50 @@ def test_fields_attributes(self):
def test_fields_defaults(self):
form = self.get_form("cms.form.res.partner")
self.env["ir.default"].set("res.partner", "name", "DEFAULT NAME")
- fields = form.form_fields()
+ fields = form.form_fields_get()
self.assertEqual(fields["name"]["_default"], "DEFAULT NAME")
self.assertEqual(fields["custom"]["_default"], "I am your default")
def test_fields_hidden(self):
- form = self.get_form("cms.form.res.partner", fields_hidden=("country_id",))
+ form = self.get_form("cms.form.res.partner", form_fields_hidden=("country_id",))
# get all fields (default)
- fields = form.form_fields()
+ fields = form.form_fields_get()
self.assertListEqual(sorted(fields.keys()), ["country_id", "custom", "name"])
# country is flagged as hidden
self.assertTrue(fields["country_id"]["hidden"])
# get only visible
- fields = form.form_fields(hidden=False)
+ fields = form.form_fields_get(hidden=False)
self.assertListEqual(sorted(fields.keys()), ["custom", "name"])
# get only hidden
- fields = form.form_fields(hidden=True)
+ fields = form.form_fields_get(hidden=True)
self.assertListEqual(sorted(fields.keys()), ["country_id"])
self.assertTrue(fields["country_id"]["hidden"])
def test_fields_hidden_keep_order(self):
form = self.get_form(
"cms.form.res.partner",
- fields_hidden=("country_id",),
- fields_order=["country_id", "name", "custom"],
+ form_fields_hidden=("country_id",),
+ form_fields_order=["country_id", "name", "custom"],
)
- fields = form.form_fields(hidden=False)
+ fields = form.form_fields_get(hidden=False)
self.assertListEqual(list(fields.keys()), ["name", "custom"])
form = self.get_form(
"cms.form.res.partner",
- fields_hidden=("country_id",),
- fields_order=["country_id", "custom", "name"],
+ form_fields_hidden=("country_id",),
+ form_fields_order=["country_id", "custom", "name"],
)
- fields = form.form_fields(hidden=False)
+ fields = form.form_fields_get(hidden=False)
self.assertListEqual(list(fields.keys()), ["custom", "name"])
def test_subfields(self):
form = self.get_form(
"cms.form.res.partner",
- sub_fields={
+ form_sub_fields={
"name": {"_all": ("custom",)},
"do_not_exists": {"_all": ("foo",)}, # skipped
},
)
- fields = form.form_fields()
+ fields = form.form_fields_get()
self.assertEqual(
fields["name"]["subfields"], {"_all": {"custom": fields["custom"]}}
)
@@ -151,24 +177,24 @@ def test_subfields(self):
def test_fields_binary(self):
form = self.get_form(
- "cms.form.res.partner", model_fields=["name", "image_1024"]
+ "cms.form.res.partner", form_model_fields=["name", "image_1024"]
)
self.assertEqual(list(form.form_file_fields.keys()), ["image_1024"])
def test_fields_protected(self):
- group = self.env.ref("website.group_website_designer")
+ group = self.env.ref("base.group_system")
user = self.env.ref("base.user_demo")
# user does not have the group
self.assertNotIn(group, user.groups_id)
form = self.get_form("cms.form.protected.fields", sudo_uid=user.id)
- fields = form.form_fields()
+ fields = form.form_fields_get()
# field is skipped
self.assertEqual(list(fields.keys()), ["nogroup"])
# now add the group
user.write({"groups_id": [(4, group.id)]})
- fields = form.form_fields()
+ fields = form.form_fields_get()
# now we get protected field too
- self.assertEqual(list(fields.keys()), ["ihaveagroup", "nogroup"])
+ self.assertEqual(sorted(fields.keys()), sorted(["ihaveagroup", "nogroup"]))
def test_get_loader(self):
form = self.get_form("cms.form.test_fields")
@@ -183,22 +209,32 @@ def test_get_loader(self):
),
None,
)
- fields = form.form_fields()
+ fields = form.form_fields_get()
for fname, loader in expected.items():
self.assertEqual(loader, form.form_get_loader(fname, fields[fname]))
- def custom_loader(*pa, **ka):
+ def custom_loader(self, *pa, **ka):
return pa, ka
- # by type
- form._form_load_char = custom_loader
- form._form_load_integer = custom_loader
- form._form_load_float = custom_loader
- # by name
- form._form_load_a_many2many = custom_loader
-
- for fname in ("a_char", "a_number", "a_float", "a_many2many"):
- self.assertEqual(custom_loader, form.form_get_loader(fname, fields[fname]))
+ with (
+ mock.patch.object(
+ type(form), "_form_load_char", custom_loader, create=True
+ ),
+ mock.patch.object(
+ type(form), "_form_load_integer", custom_loader, create=True
+ ),
+ mock.patch.object(
+ type(form), "_form_load_float", custom_loader, create=True
+ ),
+ mock.patch.object(
+ type(form), "_form_load_a_many2many", custom_loader, create=True
+ ),
+ ):
+ for fname in ("a_char", "a_number", "a_float", "a_many2many"):
+ self.assertEqual(
+ custom_loader.__name__,
+ form.form_get_loader(fname, fields[fname]).__name__,
+ )
def test_get_extractor(self):
form = self.get_form("cms.form.test_fields")
@@ -213,24 +249,35 @@ def test_get_extractor(self):
),
None,
)
- fields = form.form_fields()
+ fields = form.form_fields_get()
for fname, loader in expected.items():
self.assertEqual(loader, form.form_get_extractor(fname, fields[fname]))
- def custom_extractor(*pa, **ka):
+ def custom_extractor(self, *pa, **ka):
return pa, ka
- # by type
- form._form_extract_char = custom_extractor
- form._form_extract_integer = custom_extractor
- form._form_extract_float = custom_extractor
- # by name
- form._form_extract_a_many2many = custom_extractor
-
- for fname in ("a_char", "a_number", "a_float", "a_many2many"):
- self.assertEqual(
- custom_extractor, form.form_get_extractor(fname, fields[fname])
- )
+ with (
+ mock.patch.object(
+ type(form), "_form_extract_char", custom_extractor, create=True
+ ),
+ mock.patch.object(
+ type(form), "_form_extract_integer", custom_extractor, create=True
+ ),
+ mock.patch.object(
+ type(form), "_form_extract_integer", custom_extractor, create=True
+ ),
+ mock.patch.object(
+ type(form), "_form_extract_float", custom_extractor, create=True
+ ),
+ mock.patch.object(
+ type(form), "_form_extract_a_many2many", custom_extractor, create=True
+ ),
+ ):
+ for fname in ("a_char", "a_number", "a_float", "a_many2many"):
+ self.assertEqual(
+ custom_extractor.__name__,
+ form.form_get_extractor(fname, fields[fname]).__name__,
+ )
def test_load_defaults(self):
# create mode, no main_object
@@ -293,7 +340,7 @@ def test_extract_from_request(self):
self.assertEqual(expected[k], v)
# read mode
form = self.get_form(
- "cms.form.test_fields", req=request, extract_value_mode="read"
+ "cms.form.test_fields", req=request, form_extract_value_mode="read"
)
values = form.form_extract_values()
expected.update({"a_many2many": [1, 2, 3], "a_one2many": [4, 5, 6]})
@@ -342,13 +389,19 @@ def test_extract_from_request_custom_extractor(self):
# write mode
form = self.get_form("cms.form.test_fields", req=request)
- def custom_extractor(form, fname, value, **request_values):
+ def custom_extractor(self, form, fname, value, **request_values):
return "custom for: " + fname
# by type
- form._form_extract_a_char = custom_extractor
- form._form_extract_a_number = custom_extractor
- values = form.form_extract_values()
+ with (
+ mock.patch.object(
+ type(form), "_form_extract_a_char", custom_extractor, create=True
+ ),
+ mock.patch.object(
+ type(form), "_form_extract_a_number", custom_extractor, create=True
+ ),
+ ):
+ values = form.form_extract_values()
expected = {
"a_char": "custom for: a_char",
"a_number": "custom for: a_number",
@@ -367,7 +420,6 @@ def test_form_process_GET(self):
{
"main_object": None,
"form": form,
- "form_data": {},
"errors": {},
"errors_messages": {},
},
@@ -389,10 +441,10 @@ def test_form_process_GET(self):
"a_number": None,
"a_one2many": "[]",
}
+ self.assertEqual(form.form_data, default_form_data)
expected = {
"main_object": None,
"form": form,
- "form_data": default_form_data,
"errors": {},
"errors_messages": {},
}
@@ -406,7 +458,6 @@ def test_form_process_GET(self):
"extra_key2": "baz",
"main_object": None,
"form": form,
- "form_data": default_form_data,
"errors": {},
"errors_messages": {},
},
@@ -432,10 +483,10 @@ def test_form_process_POST(self):
"a_number": None,
"a_one2many": "[]",
}
+ self.assertEqual(form.form_data, default_form_data)
expected = {
"main_object": None,
"form": form,
- "form_data": default_form_data,
"errors": {},
"errors_messages": {},
}
@@ -450,14 +501,13 @@ def test_form_process_POST(self):
"extra_key4": "baz",
"main_object": None,
"form": form,
- "form_data": default_form_data,
"errors": {},
"errors_messages": {},
},
)
def test_get_widget(self):
- form = self.get_form("cms.form.test_fields")
+ form = self.get_form("cms.form.test_fields2")
expected = {
"a_char": "cms.form.widget.char",
"a_number": "cms.form.widget.integer",
@@ -465,12 +515,10 @@ def test_get_widget(self):
"a_many2one": "cms.form.widget.many2one",
"a_many2many": "cms.form.widget.many2many",
"a_one2many": "cms.form.widget.one2many",
+ "a_float_with_another_widget": self.FakeFloatWidget._name,
}
- fields = form.form_fields()
+ fields = form.form_fields_get()
for fname, widget_model in expected.items():
- self.assertEqual(
- widget_model, form.form_get_widget_model(fname, fields[fname])
- )
self.assertEqual(
form.form_get_widget(fname, fields[fname]).__class__,
self.env[widget_model].__class__,
@@ -480,7 +528,7 @@ def test_wrapper_css_klass(self):
form = self.get_form("cms.form.res.partner")
expected = "cms_form_wrapper cms_form_res_partner " "res_partner mode_create"
self.assertEqual(form.form_wrapper_css_klass, expected)
- form._form_wrapper_extra_css_klass = "foo"
+ form.form_wrapper_extra_css_klass = "foo"
expected = (
"cms_form_wrapper cms_form_res_partner " "res_partner foo mode_create"
)
@@ -489,37 +537,34 @@ def test_wrapper_css_klass(self):
def test_css_klass(self):
form = self.get_form("cms.form.res.partner")
self.assertEqual(form.form_css_klass, "form-horizontal")
- form._form_extra_css_klass = "cool"
+ form.form_extra_css_klass = "cool"
self.assertEqual(form.form_css_klass, "form-horizontal cool")
form.form_display_mode = "vertical"
self.assertEqual(form.form_css_klass, "form-vertical cool")
def test_field_wrapper_css_klass(self):
form = self.get_form("cms.form.res.partner")
+ field = form.form_fields_get()["custom"]
self.assertEqual(
- form.form_make_field_wrapper_klass(
- "foo_field", {"type": "char", "required": False}
- ),
- "form-group form-field field-char field-foo_field",
+ form.form_make_field_wrapper_klass("custom", field),
+ "form-group form-field field-char field-custom",
)
+ field = form.form_fields_get()["country_id"]
self.assertEqual(
- form.form_make_field_wrapper_klass(
- "foo_field_id", {"type": "many2one", "required": True}
- ),
- (
- "form-group form-field field-many2one "
- "field-foo_field_id field-required"
- ),
+ form.form_make_field_wrapper_klass("country_id", field),
+ ("form-group form-field field-many2one " "field-country_id field-required"),
)
+ field = form.form_fields_get()["custom"]
+ field["required"] = True
self.assertEqual(
form.form_make_field_wrapper_klass(
- "foo_field",
- {"type": "float", "required": True},
- errors={"foo_field": "bad_value"},
+ "custom",
+ field,
+ errors={"custom": "bad_value"},
),
(
- "form-group form-field field-float "
- "field-foo_field field-required has-error"
+ "form-group form-field field-char "
+ "field-custom field-required has-error"
),
)
diff --git a/cms_form/tests/test_form_cms.py b/cms_form/tests/test_form_cms.py
index b9fe23ed..8e09a990 100644
--- a/cms_form/tests/test_form_cms.py
+++ b/cms_form/tests/test_form_cms.py
@@ -1,28 +1,31 @@
# Copyright 2017 Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-import mock
+from unittest import mock
from odoo import exceptions
from odoo.tools import mute_logger
from .common import FormTestCase
-from .utils import fake_request
+from .utils import fake_request, mock_request
class TestCMSForm(FormTestCase):
@staticmethod
def _get_test_models():
from .fake_models.fake_fields_form import FakeFieldsForm
+ from .fake_models.fake_partner_channel_form import (
+ FakePartnerRelatedForm,
+ FakePartnerRelModel,
+ )
from .fake_models.fake_partner_form import FakePartnerForm
- from .fake_models.fake_partner_channel_form import FakePartnerChannelForm
- from .fake_models.fake_pub_model_form import FakePubModel
- from .fake_models.fake_pub_model_form import FakePubModelForm
+ from .fake_models.fake_pub_model_form import FakePubModel, FakePubModelForm
return (
FakePartnerForm,
FakeFieldsForm,
- FakePartnerChannelForm,
+ FakePartnerRelatedForm,
+ FakePartnerRelModel,
FakePubModel,
FakePubModelForm,
)
@@ -43,12 +46,10 @@ def test_form_base_attrs(self):
self.assertEqual(form.form_title, 'Edit "%s"' % partner.name)
# now edit a record that has a m2o as rec name
partner.name = "Johnny"
- partner_channel = self.env["mail.channel.partner"].create(
+ partner_rel = self.env[self.FakePartnerRelModel._name].create(
{"partner_id": partner.id}
)
- form = self.get_form(
- "cms.form.mail.channel.partner", main_object=partner_channel
- )
+ form = self.get_form(self.FakePartnerRelatedForm._name, main_object=partner_rel)
self.assertEqual(form.form_title, 'Edit "Johnny"')
def test_form_special_attrs_getter_setter(self):
@@ -73,11 +74,15 @@ def test_next_url(self):
# edit a record: get to its ws URL
record = self.env["fake.publishable"].create({"name": "Baz"})
form = self.get_form("cms.form.fake.publishable", main_object=record)
- self.assertEqual(form.form_next_url(), "/publishable/%d" % record.id)
+ self.assertEqual(
+ form.form_next_url(), "/cms/view/fake.publishable/%d" % record.id
+ )
# edit a record that has an URL but got redirect in request
request = fake_request(query_string="redirect=/sorry/go/here")
form = self.get_form(
- "cms.form.fake.publishable", req=request, main_object=record,
+ "cms.form.fake.publishable",
+ req=request,
+ main_object=record,
)
self.assertEqual(form.form_next_url(), "/sorry/go/here")
@@ -92,11 +97,15 @@ def test_cancel_url(self):
# edit a record: get to its ws URL
record = self.env["fake.publishable"].create({"name": "Baz"})
form = self.get_form("cms.form.fake.publishable", main_object=record)
- self.assertEqual(form.form_cancel_url(), "/publishable/%d" % record.id)
+ self.assertEqual(
+ form.form_cancel_url(), "/cms/view/fake.publishable/%d" % record.id
+ )
# edit a record that has an URL but got redirect in request
request = fake_request(query_string="redirect=/sorry/go/here")
form = self.get_form(
- "cms.form.fake.publishable", req=request, main_object=record,
+ "cms.form.fake.publishable",
+ req=request,
+ main_object=record,
)
self.assertEqual(form.form_cancel_url(), "/sorry/go/here")
@@ -113,7 +122,7 @@ def test_validate(self):
request = fake_request(form_data=data)
required = ("a_many2one", "a_many2many")
form = self.get_form(
- "cms.form.test_fields", req=request, required_fields=required
+ "cms.form.test_fields", req=request, form_required_fields=required
)
errors, errors_message = form.form_validate()
self.assertEqual(
@@ -140,7 +149,7 @@ def test_create_or_update(self):
}
request = fake_request(form_data=data, method="POST")
form = self.get_form(
- "cms.form.res.partner", req=request, required_fields=("name",)
+ "cms.form.res.partner", req=request, form_required_fields=("name",)
)
form.form_process()
main_object = form.main_object
@@ -157,7 +166,7 @@ def test_create_or_update(self):
"cms.form.res.partner",
req=request,
main_object=main_object,
- required_fields=("name",),
+ form_required_fields=("name",),
)
form.form_process()
self.assertEqual(main_object.name, data["name"])
@@ -166,7 +175,9 @@ def test_create_or_update(self):
def test_create_or_update_with_errors(self):
request = fake_request(form_data={}, method="POST")
form = self.get_form("cms.form.res.partner", req=request)
- with mute_logger("odoo.sql_db"):
+ with mute_logger("odoo.sql_db"), mock_request(
+ self.env, httprequest=request.httprequest
+ ):
values = form.form_process_POST({})
self.assertFalse(form.form_success)
self.assertTrue(
@@ -174,7 +185,9 @@ def test_create_or_update_with_errors(self):
"_integrity" in values["errors"]
or "_validation" in values["errors"]
)
- with mock.patch.object(type(form), "form_create_or_update") as mocked:
+ with mock.patch.object(
+ type(form), "form_create_or_update"
+ ) as mocked, mock_request(self.env, httprequest=request.httprequest):
random_msg = (
"Error while validating constraint\n"
"\nEnd Date cannot be set before Start Date.\nNone"
diff --git a/cms_form/tests/test_form_permission.py b/cms_form/tests/test_form_permission.py
index 6c35bd96..a16457b4 100644
--- a/cms_form/tests/test_form_permission.py
+++ b/cms_form/tests/test_form_permission.py
@@ -1,7 +1,7 @@
# Copyright 2017 Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-import mock
+from unittest import mock
from odoo import exceptions
@@ -11,11 +11,12 @@
class TestFormPermCheck(FormTestCase):
@staticmethod
def _get_test_models():
+ from .fake_models.fake_nonpub_model_form import (
+ FakeNonPubModel,
+ FakeNonPubModelForm,
+ )
from .fake_models.fake_partner_form import FakePartnerForm
- from .fake_models.fake_pub_model_form import FakePubModel
- from .fake_models.fake_pub_model_form import FakePubModelForm
- from .fake_models.fake_nonpub_model_form import FakeNonPubModel
- from .fake_models.fake_nonpub_model_form import FakeNonPubModelForm
+ from .fake_models.fake_pub_model_form import FakePubModel, FakePubModelForm
return (
FakeNonPubModel,
@@ -55,7 +56,7 @@ def test_form_check_permission_cannot_create(self):
msg = (
"You are not allowed to create any record " "for the model `%s`."
) % self.FakePubModel._name
- self.assertEqual(err.name, msg)
+ self.assertEqual(err.args[0], msg)
def test_form_check_permission_can_edit(self):
form = self.get_form(self.FakePubModelForm._name, main_object=self.record)
@@ -76,7 +77,7 @@ def test_form_check_permission_cannot_edit(self):
self.record._name,
self.record.id,
)
- self.assertEqual(err.name, msg)
+ self.assertEqual(err.args[0], msg)
def test_form_check_permission_no_ws_mixin_can_create(self):
form = self.get_form(self.FakeNonPubModelForm._name, main_object=None)
@@ -89,14 +90,20 @@ def test_form_check_permission_no_ws_mixin_can_edit(self):
def test_form_check_permission_no_record_no_model_can_edit_create(self):
form = self.get_form(self.FakePartnerForm._name, main_object=None)
- form._form_model = None
+ form.form_model_name = None
self.assertTrue(form._can_edit())
self.assertTrue(form._can_create())
def test_form_check_permission_form_cannot_edit(self):
- form = self.get_form(self.FakePartnerForm._name, main_object=self.record)
- with mock.patch.object(type(self.record), "check_access_rights") as patched:
+ form = self.get_form(self.FakePartnerForm._name)
+ with mock.patch.object(
+ type(self.record), "check_access_rights", spec=True
+ ) as patched:
patched.side_effect = exceptions.AccessError("boom")
with self.assertRaises(exceptions.AccessError):
+ # FIXME: for some reason entering this ctx manager and exiting
+ # wipes the main_object thus I have to set it twice
+ form.main_object = self.record
form._can_edit()
+ form.main_object = self.record
self.assertFalse(form._can_edit(raise_exception=False))
diff --git a/cms_form/tests/test_form_render.py b/cms_form/tests/test_form_render.py
index 50d64d32..45705b8e 100644
--- a/cms_form/tests/test_form_render.py
+++ b/cms_form/tests/test_form_render.py
@@ -27,7 +27,7 @@ def test_render_form_attrs(self):
}
self.assert_match_attrs(node.attrib, expected_attrs)
- def test_render_form_fields(self):
+ def test_render_form_fields_get(self):
form = self.get_form("cms.form.test_fields")
html = form.form_render()
node = self.to_xml_node(html)[0]
@@ -46,7 +46,7 @@ def test_render_form_fields(self):
def test_field_wrapper_attrs(self):
form = self.get_form("cms.form.test_fields")
- form_fields = form.form_fields()
+ form_fields = form.form_fields_get()
html = form.form_render()
node = self.to_xml_node(html)[0]
expected_fields = (
@@ -66,7 +66,7 @@ def test_field_wrapper_attrs(self):
form.form_make_field_wrapper_klass(fname, form_fields[fname]),
)
- def test_render_form_fieldsets(self):
+ def test_render_form_fieldsets_get(self):
form = self.get_form("cms.form.test_fieldsets")
html = form.form_render()
node = self.to_xml_node(html)[0]
@@ -85,7 +85,7 @@ def test_render_form_fieldsets(self):
self.assertEqual(len(node[0].xpath("//input|//select")), len(expected_fields))
self.assert_match_inputs(node, expected_fields)
# and they are organized by fieldset
- for fset in form.form_fieldsets():
+ for fset in form.form_fieldsets_get():
fset_node = node.xpath('//fieldset[@id="%s"]' % fset["id"])[0]
if fset.get("title"):
legend_node = fset_node.find("legend")
diff --git a/cms_form/tests/test_form_search.py b/cms_form/tests/test_form_search.py
index 13f51951..6a7367bd 100644
--- a/cms_form/tests/test_form_search.py
+++ b/cms_form/tests/test_form_search.py
@@ -1,7 +1,7 @@
# Copyright 2017 Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-import mock
+from unittest import mock
from .common import FormTestCase
from .utils import fake_request
@@ -27,6 +27,10 @@ def setUpClass(cls):
super().setUpClass()
cls._setup_records()
+ def tearDown(self):
+ self.FakeSearchPartnerForm._set_test_record_ids([])
+ super().tearDown()
+
@classmethod
def _setup_records(cls):
cls.partner_model = cls.env["res.partner"].with_context(tracking_disable=True)
@@ -34,11 +38,26 @@ def _setup_records(cls):
cls.expected_partners = []
cls.expected_partners_ids = []
cls._expected_partners = (
- ("Salmo", cls.env.ref("base.it").id,),
- ("Marracash", cls.env.ref("base.it").id,),
- ("Notorious BIG", cls.env.ref("base.us").id,),
- ("Dr. Dre", cls.env.ref("base.us").id,),
- ("NTM", cls.env.ref("base.fr").id,),
+ (
+ "Salmo",
+ cls.env.ref("base.it").id,
+ ),
+ (
+ "Marracash",
+ cls.env.ref("base.it").id,
+ ),
+ (
+ "Notorious BIG",
+ cls.env.ref("base.us").id,
+ ),
+ (
+ "Dr. Dre",
+ cls.env.ref("base.us").id,
+ ),
+ (
+ "NTM",
+ cls.env.ref("base.fr").id,
+ ),
)
for name, country_id in cls._expected_partners:
@@ -62,7 +81,7 @@ def get_search_form(self, data, form_model="cms.form.search.res.partner", **kw):
request = fake_request(form_data=data)
form = self.get_form(form_model, req=request, **kw)
# restrict search results to these ids
- form.test_record_ids = self.expected_partners_ids
+ self.FakeSearchPartnerForm._set_test_record_ids(self.expected_partners_ids)
return form
def test_form_base_attrs(self):
@@ -72,8 +91,8 @@ def test_form_base_attrs(self):
def test_search_domain(self):
form = self.get_search_form({})
- form.test_record_ids = []
- form._form_search_domain_rules = {
+ self.FakeSearchPartnerForm._set_test_record_ids([])
+ form.form_search_domain_rules = {
"float_field": lambda field, value, search_values: (
"float_field",
">",
@@ -95,7 +114,7 @@ def mock_fields(form):
"m2m_field": {"type": "many2many"},
}
- with mock.patch.object(type(form), "form_fields", mock_fields):
+ with mock.patch.object(type(form), "form_fields_get", mock_fields):
search_values = {
"char_field": "foo",
"text_field": "",
@@ -118,7 +137,8 @@ def mock_fields(form):
("o2m_field", "in", [1, 2, 3]),
]
self.assertEqual(
- sorted(form.form_search_domain(search_values)), sorted(expected),
+ sorted(form.form_search_domain(search_values)),
+ sorted(expected),
)
def test_search(self):
@@ -159,7 +179,7 @@ def test_search(self):
self.assert_results(form, 1, self.expected_partners[4:])
def test_search_no_result(self):
- form = self.get_search_form({}, show_results_no_submit=False)
+ form = self.get_search_form({}, form_show_results_no_submit=False)
form.form_process()
self.assertEqual(form.form_search_results, {})
@@ -230,7 +250,8 @@ def test_search_custom_rules(self):
"country_id": "Italy",
}
form = self.get_search_form(
- data, search_domain_rules={"country_id": ("country_id.name", "ilike", "")},
+ data,
+ form_search_domain_rules={"country_id": ("country_id.name", "ilike", "")},
)
form.form_process()
self.assert_results(form, 2, self.expected_partners[:2])
diff --git a/cms_form/tests/test_form_wizard.py b/cms_form/tests/test_form_wizard.py
index c015647e..4873b93f 100644
--- a/cms_form/tests/test_form_wizard.py
+++ b/cms_form/tests/test_form_wizard.py
@@ -4,6 +4,9 @@
from .common import FormSessionTestCase
from .utils import fake_request
+# TODO: add tests w/ real session
+# to make sure there's no regression between versions
+
class TestCMSFormWizard(FormSessionTestCase):
@staticmethod
@@ -55,7 +58,7 @@ def test_wiz_base_attrs(self):
def test_wiz_use_session_by_default(self):
req = fake_request(session=self.session)
form = self.get_form("cms.form.wizard", req=req)
- self.assertEqual(form._wiz_storage.__class__.__name__, "OpenERPSession")
+ self.assertEqual(form._wiz_storage.__class__.__name__, "Session")
def test_wiz_configure_steps(self):
form = self.get_form("cms.form.wizard")
@@ -149,7 +152,10 @@ def test_wiz_stored_fields(self):
"to_be_stored": "Whatever",
}
req = fake_request(form_data=data, method="POST")
- form = self.get_form(self.FakeWizStep2Partner._name, req=req)
+ form = self.get_form(
+ self.FakeWizStep2Partner._name,
+ req=req,
+ )
main_object = form.form_create_or_update()
self.assertEqual(main_object.name, "John Doe")
step_values = form.wiz_load_step()
@@ -161,8 +167,12 @@ def test_wiz_stored_fields_all(self):
"to_be_stored": "Whatever",
}
req = fake_request(form_data=data, method="POST")
- form = self.get_form(self.FakeWizStep2Partner._name, req=req)
- form._wiz_step_stored_fields = "all"
+ form = self.get_form(
+ self.FakeWizStep2Partner._name,
+ req=req,
+ form_step_store_all_fields=True,
+ form_step_stored_fields="[]",
+ )
main_object = form.form_create_or_update()
self.assertEqual(main_object.name, "John Doe")
step_values = form.wiz_load_step()
diff --git a/cms_form/tests/test_marshallers.py b/cms_form/tests/test_marshallers.py
index 8be49005..339bb016 100644
--- a/cms_form/tests/test_marshallers.py
+++ b/cms_form/tests/test_marshallers.py
@@ -3,9 +3,10 @@
import unittest
-from werkzeug.datastructures import MultiDict
+from werkzeug.datastructures import FileMultiDict, Headers, MultiDict
from .. import marshallers
+from .utils import fake_file_from_request, file_as_stream
class TestMarshallers(unittest.TestCase):
@@ -36,7 +37,7 @@ def test_marshal_int(self):
self.assertEqual(marshalled["a"], "1")
self.assertEqual(marshalled["b"], 2)
self.assertEqual(marshalled["c"], 3)
- self.assertEqual(marshalled["d"], "bad")
+ self.assertEqual(marshalled["d"], None)
def test_marshal_float(self):
data = MultiDict(
@@ -53,7 +54,7 @@ def test_marshal_float(self):
self.assertEqual(marshalled["b"], 2.0)
self.assertEqual(marshalled["c"], 3.0)
self.assertEqual(marshalled["d"], 4.0)
- self.assertEqual(marshalled["e"], "bad")
+ self.assertEqual(marshalled["e"], None)
def test_marshal_dict(self):
data = MultiDict(
@@ -80,3 +81,67 @@ def test_marshal_esc(self):
marshalled = marshallers.marshal_request_values(data)
self.assertEqual(marshalled["a"], "<span>I'm bad</span>")
self.assertEqual(marshalled["b"], "
I'm bad but I don't care")
+ self.assertNotIn("a:esc", marshalled)
+
+ def test_marshal_dict_list(self):
+ data = MultiDict(
+ [
+ ("a", "1"),
+ ("b.1.x:dict:list", "b1x"),
+ ("b.1.y:dict:list", "b1y"),
+ ("b.1.z:dict:list", "b1z"),
+ ("b.2.x:dict:list", "b2x"),
+ ("b.2.y:dict:list", "b2y"),
+ ("b.2.z:dict:list", "b2z"),
+ ("b.3.x:dict:list", "b3x"),
+ ("b.3.y:dict:list", "b3y"),
+ ("b.3.z:dict:list", "b3z"),
+ ("c", "3"),
+ ]
+ )
+ marshalled = marshallers.marshal_request_values(data)
+ self.assertEqual(marshalled["a"], "1")
+ self.assertEqual(
+ marshalled["b"],
+ [
+ {"x": "b1x", "y": "b1y", "z": "b1z"},
+ {"x": "b2x", "y": "b2y", "z": "b2z"},
+ {"x": "b3x", "y": "b3y", "z": "b3z"},
+ ],
+ )
+ self.assertEqual(marshalled["c"], "3")
+ self.assertNotIn("b.1.x:dict:list", marshalled)
+ self.assertNotIn("b.2.y:dict:list", marshalled)
+
+ def test_marshal_file(self):
+ data = FileMultiDict()
+ content = b"a,b,c\n1,2,3\n4,5,6"
+ with file_as_stream(content) as stream:
+ data.add_file(
+ "one:file",
+ fake_file_from_request(
+ "one",
+ stream=stream,
+ filename="one.csv",
+ content_type="text/csv",
+ content_length=len(content),
+ ),
+ )
+ marshalled = marshallers.marshal_request_values(data)
+ self.assertEqual(
+ marshalled["one"],
+ {
+ "_from_request": True,
+ "content_length": 17,
+ "content_type": "text/csv",
+ "filename": "one.csv",
+ "headers": Headers(
+ [("Content-Type", "text/csv"), ("Content-Length", "17")]
+ ),
+ "mimetype": "text/csv",
+ "mimetype_params": {},
+ "raw_value": "YSxiLGMKMSwyLDMKNCw1LDY=",
+ "value": "YSxiLGMKMSwyLDMKNCw1LDY=",
+ },
+ )
+ self.assertNotIn("one:file", marshalled)
diff --git a/cms_form/tests/test_utils.py b/cms_form/tests/test_utils.py
index 1e46584f..1eac3b6a 100644
--- a/cms_form/tests/test_utils.py
+++ b/cms_form/tests/test_utils.py
@@ -19,7 +19,7 @@ def test_safe_to_float(self):
self.assertEqual(utils.safe_to_float(""), None)
self.assertEqual(utils.safe_to_float(False), 0.0)
self.assertEqual(utils.safe_to_float("abc"), None)
- self.assertEqual(utils.safe_to_float("10,0"), None)
+ self.assertEqual(utils.safe_to_float("10,0"), 10.0)
self.assertEqual(utils.safe_to_float("10.0"), 10.0)
self.assertEqual(utils.safe_to_float("0"), 0.0)
self.assertEqual(utils.safe_to_float("10"), 10.0)
diff --git a/cms_form/tests/utils.py b/cms_form/tests/utils.py
index b1238188..cccd6479 100644
--- a/cms_form/tests/utils.py
+++ b/cms_form/tests/utils.py
@@ -5,14 +5,17 @@
import io
import urllib.parse
from contextlib import contextmanager
+from unittest import mock
-import mock
-from werkzeug.contrib.sessions import SessionStore
from werkzeug.datastructures import FileStorage
from werkzeug.wrappers import Request
from odoo import api, http
from odoo.tests.common import get_db_name
+from odoo.tools import DotDict
+from odoo.tools._vendor.sessions import SessionStore
+
+from odoo.addons.website.tools import MockRequest
def fake_request(
@@ -24,7 +27,6 @@ def fake_request(
session=None,
):
data = urllib.parse.urlencode(form_data or {})
- query_string = query_string or ""
content_type = content_type or "application/x-www-form-urlencoded"
# werkzeug request
w_req = Request.from_values(
@@ -35,23 +37,58 @@ def fake_request(
content_type=content_type,
method=method,
)
- w_req.session = session if session is not None else mock.MagicMock()
# odoo request
- o_req = http.HttpRequest(w_req)
- o_req.website = mock.MagicMock()
+ o_req = http.Request(w_req)
o_req.csrf_token = mock.MagicMock()
o_req.httprequest = w_req
+ o_req.session = session if session is not None else mock.MagicMock()
o_req.__testing__ = True
return o_req
+@contextmanager
+def mock_request(
+ env,
+ request=None,
+ httprequest=None,
+ extra_headers=None,
+ request_attrs=None,
+ httprequest_attrs=None,
+ **kw
+):
+ # TODO: refactor this ctx mngr from website to:
+ # - make it independent
+ # - use real request and session as per fake_request above
+ with MockRequest(env, **kw) as mocked_request:
+ if httprequest:
+ if isinstance(httprequest, dict):
+ httprequest = DotDict(httprequest)
+ mocked_request.httprequest = httprequest
+ headers = {}
+ headers.update(extra_headers or {})
+ mocked_request.httprequest.headers = headers
+ request_attrs = request_attrs or {}
+ for k, v in request_attrs.items():
+ setattr(mocked_request, k, v)
+ httprequest_attrs = httprequest_attrs or {}
+ for k in ("args", "form", "files", "_cms_form_files_processed"):
+ if k not in httprequest_attrs:
+ httprequest_attrs[k] = {}
+ for k, v in httprequest_attrs.items():
+ setattr(mocked_request.httprequest, k, v)
+ mocked_request.make_response = lambda data, **kw: data
+ mocked_request.registry._init_modules = set()
+ mocked_request.session.touch = lambda: True
+ yield mocked_request
+
+
class FakeSessionStore(SessionStore):
def delete(self, session):
session.clear()
del session
-session_store = FakeSessionStore(session_class=http.OpenERPSession)
+session_store = FakeSessionStore(session_class=http.Session)
def fake_session(env, **kw):
@@ -64,7 +101,6 @@ def fake_session(env, **kw):
session.password = ""
session.context = dict(env.context)
session.context["uid"] = env.uid
- session._fix_lang(session.context)
for k, v in kw.items():
if hasattr(session, k):
setattr(session, k, v)
@@ -73,13 +109,19 @@ def fake_session(env, **kw):
@contextmanager
-def b64_as_stream(b64_content):
+def file_as_stream(content):
stream = io.BytesIO()
- stream.write(base64.b64decode(b64_content))
+ stream.write(content)
stream.seek(0)
yield stream
stream.close()
+@contextmanager
+def b64_as_stream(b64_content):
+ with file_as_stream(base64.b64decode(b64_content)) as stream:
+ yield stream
+
+
def fake_file_from_request(input_name, stream, **kw):
return FileStorage(name=input_name, stream=stream, **kw)
diff --git a/cms_form/tests/widgets/common.py b/cms_form/tests/widgets/common.py
index 803b0e98..4f97b2f9 100644
--- a/cms_form/tests/widgets/common.py
+++ b/cms_form/tests/widgets/common.py
@@ -1,21 +1,20 @@
# Copyright 2018 Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-import mock
-from odoo.tests.common import SavepointCase
+from odoo.tests.common import TransactionCase
from ..common import HTMLRenderMixin
+from ..utils import fake_request
-def fake_form(main_object=None, **data):
+def fake_form(env, main_object=None, **data):
"""Get a mocked fake form.
:param data: kw args for setting form values
"""
- form = mock.MagicMock(name="FakeForm")
- form_values = mock.PropertyMock(return_value={"form_data": data})
- type(form).form_render_values = form_values
- form.main_object = main_object
+ form = env["cms.form"].form_init(
+ fake_request(), main_object=main_object, form_data=data
+ )
return form
@@ -46,13 +45,13 @@ def get_widget(env, fname, field, form=None, widget_model=None, **kw):
"""
assert form or widget_model
if not form:
- form = fake_form()
+ form = fake_form(env)
if not widget_model:
- widget_model = form.form_get_widget_model(fname, field)
+ widget_model = form._form_get_default_widget_model(fname, field)
return env[widget_model].widget_init(form, fname, field, **kw)
-class TestWidgetCase(SavepointCase, HTMLRenderMixin):
+class TestWidgetCase(TransactionCase, HTMLRenderMixin):
at_install = False
post_install = True
diff --git a/cms_form/tests/widgets/test_widget_base.py b/cms_form/tests/widgets/test_widget_base.py
index eabbabb2..8cefeb10 100644
--- a/cms_form/tests/widgets/test_widget_base.py
+++ b/cms_form/tests/widgets/test_widget_base.py
@@ -5,7 +5,6 @@
class TestWidgetBase(TestWidgetCase, FakeModelMixin):
-
@classmethod
def setUpClass(cls):
super().setUpClass()
@@ -24,15 +23,15 @@ def _get_test_models():
def test_widget_init(self):
form = get_form(self.env, "cms.form.res.partner")
- field = form.form_fields()["custom"]
+ field = form.form_fields_get()["custom"]
widget = self.get_widget("custom", field, form=form)
self.assertEqual(widget.w_form, form)
self.assertEqual(widget.w_form_model, form.form_model)
self.assertEqual(widget.w_record, form.main_object)
- self.assertEqual(widget.w_form_values, form.form_render_values)
+ self.assertEqual(widget.w_form_values, form.form_data)
self.assertEqual(widget.w_fname, "custom")
self.assertDictEqual(widget.w_field, field)
- self.assertEqual(widget.w_field_value, None)
+ self.assertEqual(widget.w_field_value, "oh yeah!")
self.assertEqual(widget.w_data, {})
self.assertEqual(widget.w_subfields, {})
@@ -69,15 +68,30 @@ def test_w_ids_from_input(self):
self.assertEqual(widget.w_ids_from_input(""), [])
# not valid values are skipped
self.assertEqual(
- widget.w_ids_from_input("1,2,3,#4, 70, 1XX, 200"), [1, 2, 3, 70, 200],
+ widget.w_ids_from_input("1,2,3,#4, 70, 1XX, 200"),
+ [1, 2, 3, 70, 200],
)
def test_subfields_get(self):
form = get_form(
self.env,
"cms.form.res.partner",
- sub_fields={"name": {"_all": ("custom",)}},
+ form_sub_fields={"name": {"_all": ("custom",)}},
)
- fields = form.form_fields()
+ fields = form.form_fields_get()
widget = self.get_widget("name", fields["name"], form=form)
self.assertEqual(widget.w_subfields_by_value(), {"custom": fields["custom"]})
+
+ def test_html_name(self):
+ form = get_form(self.env, "cms.form.res.partner")
+ for fname, field in form.form_fields_get().items():
+ widget = self.get_widget(fname, field, form=form)
+ self.assertEqual(widget.html_fname, fname)
+ form = get_form(
+ self.env,
+ "cms.form.res.partner",
+ form_fname_pattern="pre_{widget.w_fname}_post",
+ )
+ for fname, field in form.form_fields_get().items():
+ widget = self.get_widget(fname, field, form=form)
+ self.assertEqual(widget.html_fname, f"pre_{fname}_post")
diff --git a/cms_form/tests/widgets/test_widget_binary.py b/cms_form/tests/widgets/test_widget_binary.py
index e759bf43..09be988b 100644
--- a/cms_form/tests/widgets/test_widget_binary.py
+++ b/cms_form/tests/widgets/test_widget_binary.py
@@ -1,6 +1,10 @@
# Copyright 2019 Simone Orsi
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-from odoo.addons.cms_form.tests.utils import b64_as_stream, fake_file_from_request
+from odoo.addons.cms_form.marshallers import Marshaller # pylint: disable=W8150
+from odoo.addons.cms_form.tests.utils import ( # pylint: disable=W8150
+ b64_as_stream,
+ fake_file_from_request,
+)
from .common import TestWidgetCase, fake_field, fake_form
@@ -42,14 +46,23 @@ class TestWidgetBinary(TestWidgetCase):
def setUpClass(cls):
super().setUpClass()
cls.partner = cls.env["res.partner"].search([], limit=1)
- cls.form = fake_form(main_object=cls.partner)
cls.maxDiff = None
+ def setUp(self):
+ super().setUp()
+ self.form = fake_form(self.env, main_object=self.partner)
+
# TODO: we have only an image widget ATM -> add a file widget and test it
def test_widget_binary_base(self):
- w_name, w_field = fake_field("image", type="binary",)
+ w_name, w_field = fake_field(
+ "image",
+ type="binary",
+ )
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.image",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.image",
)
node_items = self.to_xml_node(widget.render())
self.assertEqual(len(node_items), 1)
@@ -58,7 +71,9 @@ def test_widget_binary_base(self):
"class": "image-widget-wrapper",
}
self._test_element_attributes(
- node_wrapper, "div", expected_attrs,
+ node_wrapper,
+ "div",
+ expected_attrs,
)
# no existing value so we get only the input
self.assertEqual(len(node_wrapper.getchildren()), 1)
@@ -72,16 +87,24 @@ def test_widget_binary_base(self):
"accept": "image/*",
}
self._test_element_attributes(
- node_input, "input", expected_attrs,
+ node_input,
+ "input",
+ expected_attrs,
)
def test_widget_binary_load_from_record(self):
- w_name, w_field = fake_field("image_1024", type="binary",)
+ w_name, w_field = fake_field(
+ "image_1024",
+ type="binary",
+ )
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.image",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.image",
)
# test conversion
- self.assertEqual(widget.w_load(image_1024=False), {})
+ self.assertEqual(widget.w_load(image_1024=False), False)
# set value on partner image
self.partner.image_1024 = TEST_IMAGE_GIF
self.assertEqual(
@@ -90,39 +113,51 @@ def test_widget_binary_load_from_record(self):
"value": "data:image/png;base64,{}".format(TEST_IMAGE_GIF),
"raw_value": TEST_IMAGE_GIF,
"mimetype": "image/png",
- "from_request": False,
+ "content_type": "image/png",
+ "content_lenght": len(TEST_IMAGE_GIF),
},
)
def test_widget_binary_load_from_request(self):
- w_name, w_field = fake_field("image_1024", type="binary",)
+ w_name, w_field = fake_field(
+ "image_1024",
+ type="binary",
+ )
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.image",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.image",
)
# test conversion
- self.assertEqual(widget.w_load(image_1024=False), {})
+ self.assertEqual(widget.w_load(image_1024=False), False)
with b64_as_stream(TEST_IMAGE_JPG) as stream:
- req_image = fake_file_from_request(
- "image_1024",
- stream=stream,
- filename="foo.jpg",
- content_type="image/jpeg",
+ req_image = Marshaller._filedata_from_filestorage(
+ fake_file_from_request(
+ "image_1024",
+ stream=stream,
+ filename="foo.jpg",
+ content_type="image/jpeg",
+ )
)
- self.assertEqual(
- widget.w_load(image_1024=req_image),
- {
- "value": "data:image/jpeg;base64,{}".format(TEST_IMAGE_JPG),
- "raw_value": TEST_IMAGE_JPG,
- "mimetype": "image/jpeg",
- "from_request": True,
- },
+ res = widget.w_load(image_1024=req_image)
+ expected = dict(
+ req_image, value="data:image/jpg;base64,{}".format(TEST_IMAGE_JPG)
)
+ for k, v in res.items():
+ self.assertEqual(v, expected[k], f"{k} not matching")
def test_widget_binary_extract_string(self):
- w_name, w_field = fake_field("image", type="binary",)
+ w_name, w_field = fake_field(
+ "image",
+ type="binary",
+ )
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.image",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.image",
)
# no value in request -> None
@@ -130,33 +165,52 @@ def test_widget_binary_extract_string(self):
# req value can come as string
req_val = "data:image/jpeg;base64,{}".format(TEST_IMAGE_JPG)
# value in request but no check flag -> None
- self.assertEqual(widget.w_extract(image=req_val), None)
+ self.assertEqual(widget.w_extract(image=req_val), TEST_IMAGE_JPG)
# value in request but keep flag is ON -> None
self.assertEqual(widget.w_extract(image=req_val, image_keepcheck="yes"), None)
# value in request but keep flag is ON -> None
self.assertEqual(
- widget.w_extract(image=req_val, image_keepcheck="no"), TEST_IMAGE_JPG,
+ widget.w_extract(image=req_val, image_keepcheck="no"),
+ TEST_IMAGE_JPG,
)
def test_widget_binary_extract_filestorage(self):
- w_name, w_field = fake_field("image", type="binary",)
+ w_name, w_field = fake_field(
+ "image",
+ type="binary",
+ )
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.image",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.image",
)
# value in request but no check flag -> None
with b64_as_stream(TEST_IMAGE_JPG) as stream:
- req_val = fake_file_from_request(
- "image", stream=stream, filename="foo.jpg", content_type="image/jpeg",
+ req_image = Marshaller._filedata_from_filestorage(
+ fake_file_from_request(
+ "image",
+ stream=stream,
+ filename="foo.jpg",
+ content_type="image/jpeg",
+ )
)
self.assertEqual(
- widget.w_extract(image=req_val, image_keepcheck="no"), TEST_IMAGE_JPG,
+ widget.w_extract(image=req_image, image_keepcheck="no"),
+ TEST_IMAGE_JPG,
)
def test_widget_binary_check_empty(self):
- w_name, w_field = fake_field("image", type="binary",)
+ w_name, w_field = fake_field(
+ "image",
+ type="binary",
+ )
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.image",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.image",
)
# no value at all -> empty
@@ -171,25 +225,35 @@ def test_widget_binary_check_empty(self):
self.assertIs(widget.w_check_empty_value(req_val), True)
# no filename and keep flag -> empty, since we want to preserve
self.assertIs(
- widget.w_check_empty_value(req_val, image_keepcheck="yes"), False,
+ widget.w_check_empty_value(req_val, image_keepcheck="yes"),
+ False,
)
req_val = fake_file_from_request(
- "image", stream=stream, filename="foo.jpg", content_type="image/jpeg",
+ "image",
+ stream=stream,
+ filename="foo.jpg",
+ content_type="image/jpeg",
)
# got file w/ filename and no keep flag -> not empty
self.assertIs(widget.w_check_empty_value(req_val), False)
req_val = fake_file_from_request(
- "image", stream=stream, filename="foo.jpg", content_type="image/jpeg",
+ "image",
+ stream=stream,
+ filename="foo.jpg",
+ content_type="image/jpeg",
)
# got file w/ filename and yes keep flag -> not empty
self.assertIs(
- widget.w_check_empty_value(req_val, image_keepcheck="yes"), False,
+ widget.w_check_empty_value(req_val, image_keepcheck="yes"),
+ False,
)
# got file w/ filename and no keep flag -> not empty
self.assertIs(
- widget.w_check_empty_value(req_val, image_keepcheck="no"), False,
+ widget.w_check_empty_value(req_val, image_keepcheck="no"),
+ False,
)
# got file w/ filename and yes keep flag -> not empty
self.assertIs(
- widget.w_check_empty_value(req_val, image_keepcheck="yes"), False,
+ widget.w_check_empty_value(req_val, image_keepcheck="yes"),
+ False,
)
diff --git a/cms_form/tests/widgets/test_widget_boolean.py b/cms_form/tests/widgets/test_widget_boolean.py
index 75700764..f5165a3a 100644
--- a/cms_form/tests/widgets/test_widget_boolean.py
+++ b/cms_form/tests/widgets/test_widget_boolean.py
@@ -6,8 +6,11 @@
class TestWidgetBoolean(TestWidgetCase):
def _get_widget(self, field_value=False):
"""Initialize form w/ given value and return the widget."""
- form = fake_form(a_boolean_field=field_value)
- w_name, w_field = fake_field("a_boolean_field", type="boolean",)
+ form = fake_form(self.env, a_boolean_field=field_value)
+ w_name, w_field = fake_field(
+ "a_boolean_field",
+ type="boolean",
+ )
return self.get_widget(
w_name, w_field, form=form, widget_model="cms.form.widget.boolean"
)
@@ -19,7 +22,7 @@ def test_widget_boolean_input(self):
"type": "checkbox",
"id": "a_boolean_field",
"name": "a_boolean_field",
- "class": "form-control ",
+ "class": "form-check-input ",
}
self._test_widget_attributes(widget, "input", expected_attrs)
@@ -31,7 +34,7 @@ def test_widget_boolean_input_required(self):
"type": "checkbox",
"id": "a_boolean_field",
"name": "a_boolean_field",
- "class": "form-control ",
+ "class": "form-check-input ",
"required": "1",
}
self._test_widget_attributes(widget, "input", expected_attrs)
@@ -43,7 +46,7 @@ def test_widget_boolean_input_checked(self):
"type": "checkbox",
"id": "a_boolean_field",
"name": "a_boolean_field",
- "class": "form-control ",
+ "class": "form-check-input ",
"checked": "checked",
}
self._test_widget_attributes(widget, "input", expected_attrs)
diff --git a/cms_form/tests/widgets/test_widget_date.py b/cms_form/tests/widgets/test_widget_date.py
index 69dc2bc1..c2884dc5 100644
--- a/cms_form/tests/widgets/test_widget_date.py
+++ b/cms_form/tests/widgets/test_widget_date.py
@@ -6,13 +6,15 @@
class TestWidgetDate(TestWidgetCase):
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- cls.form = fake_form(a_date_field="2019-01-12", type="date")
- cls.w_name, cls.w_field = fake_field("a_date_field")
- cls.widget = cls.get_widget(
- cls.w_name, cls.w_field, form=cls.form, widget_model="cms.form.widget.date",
+ def setUp(self):
+ super().setUp()
+ self.form = fake_form(self.env, a_date_field="2019-01-12", type="date")
+ self.w_name, self.w_field = fake_field("a_date_field")
+ self.widget = self.get_widget(
+ self.w_name,
+ self.w_field,
+ form=self.form,
+ widget_model="cms.form.widget.date",
)
def test_widget_date_input(self):
@@ -32,7 +34,7 @@ def test_widget_date_input(self):
# TODO: check on json params on other widgets too
self.assertEqual(
json.loads(node_input_disp[0].attrib["data-params"]),
- {"defaultToday": True},
+ {"defaultToday": True, "name": "a_date_field"},
)
# and the real one holding the value which is hidden
node_input = self.find_input_name(node, self.w_name)
@@ -67,13 +69,16 @@ def test_widget_date_input_custom_dp_attrs(self):
)
self.assertEqual(widget.w_placeholder, "Custom")
self.assertEqual(
- widget.w_data_json(), '{"defaultToday": true, "dp": {"format": "%m.%Y"}}',
+ widget.w_data_json(),
+ '{"defaultToday": true, "dp": {"format": "%m.%Y"}, "name": "a_date_field"}',
)
def test_widget_date_input_all_elems(self):
node = self.to_xml_node(self.widget.render())[0]
self._test_element_attributes(
- node, "div", {"class": "input-group"},
+ node,
+ "div",
+ {"class": "input-group"},
)
self.assertEqual(len(node.getchildren()), 3)
self._test_element_attributes(node.getchildren()[0], "input", {})
@@ -86,7 +91,9 @@ def test_widget_date_input_all_elems(self):
{"class": "input-group-addon js_datepicker_trigger"},
)
self._test_element_attributes(
- node.getchildren()[2].getchildren()[0], "span", {"class": "fa fa-calendar"},
+ node.getchildren()[2].getchildren()[0],
+ "span",
+ {"class": "fa fa-calendar"},
)
def test_widget_date_input_extract_default_format(self):
diff --git a/cms_form/tests/widgets/test_widget_float.py b/cms_form/tests/widgets/test_widget_float.py
index 3c7dd334..fc874aa4 100644
--- a/cms_form/tests/widgets/test_widget_float.py
+++ b/cms_form/tests/widgets/test_widget_float.py
@@ -4,13 +4,15 @@
class TestWidgetFloat(TestWidgetCase):
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- form = fake_form(a_float_field=2.0)
- cls.w_name, cls.w_field = fake_field("a_float_field", type="float")
- cls.widget = cls.get_widget(
- cls.w_name, cls.w_field, form=form, widget_model="cms.form.widget.float",
+ def setUp(self):
+ super().setUp()
+ form = fake_form(self.env, a_float_field=2.0)
+ self.w_name, self.w_field = fake_field("a_float_field", type="float")
+ self.widget = self.get_widget(
+ self.w_name,
+ self.w_field,
+ form=form,
+ widget_model="cms.form.widget.float",
)
def test_widget_float_input(self):
@@ -49,5 +51,5 @@ def test_widget_float_input_required(self):
def test_widget_float_input_extract(self):
self.assertEqual(self.widget.w_extract(a_float_field="1"), 1.0)
self.assertEqual(self.widget.w_extract(a_float_field="2.0"), 2.0)
- self.assertEqual(self.widget.w_extract(a_float_field="2,0"), None)
+ self.assertEqual(self.widget.w_extract(a_float_field="2,0"), 2.0)
self.assertEqual(self.widget.w_extract(a_float_field=""), None)
diff --git a/cms_form/tests/widgets/test_widget_hidden.py b/cms_form/tests/widgets/test_widget_hidden.py
index 30af5d94..160af936 100644
--- a/cms_form/tests/widgets/test_widget_hidden.py
+++ b/cms_form/tests/widgets/test_widget_hidden.py
@@ -4,10 +4,10 @@
class TestWidgetHidden(TestWidgetCase):
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- cls.form = fake_form(
+ def setUp(self):
+ super().setUp()
+ self.form = fake_form(
+ self.env,
# fake defaults
char_field="abc",
int_field=5,
@@ -22,7 +22,10 @@ def test_widget_char_input_hidden(self):
"""Test char field hidden."""
w_name, w_field = fake_field("char_field")
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.hidden",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.hidden",
)
expected_attrs = {
"type": "hidden",
@@ -41,7 +44,10 @@ def test_widget_integer_input_hidden(self):
"""Test int field hidden."""
w_name, w_field = fake_field("int_field", type="integer")
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.hidden",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.hidden",
)
expected_attrs = {
"type": "hidden",
@@ -55,7 +61,10 @@ def test_widget_float_input_hidden(self):
"""Test float field hidden."""
w_name, w_field = fake_field("float_field", type="float")
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.hidden",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.hidden",
)
expected_attrs = {
"type": "hidden",
@@ -68,10 +77,15 @@ def test_widget_float_input_hidden(self):
def test_widget_selection_string_input_hidden(self):
"""Test selection field hidden with string values."""
w_name, w_field = fake_field(
- "selection_str_field", type="selection", selection=[("1", "A"), ("2", "B")],
+ "selection_str_field",
+ type="selection",
+ selection=[("1", "A"), ("2", "B")],
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.hidden",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.hidden",
)
expected_attrs = {
"type": "hidden",
@@ -84,10 +98,15 @@ def test_widget_selection_string_input_hidden(self):
def test_widget_selection_integer_input_hidden(self):
"""Test selection field hidden with integer values."""
w_name, w_field = fake_field(
- "selection_integer_field", type="selection", selection=[(1, "A"), (2, "B")],
+ "selection_integer_field",
+ type="selection",
+ selection=[(1, "A"), (2, "B")],
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.hidden",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.hidden",
)
expected_attrs = {
"type": "hidden",
@@ -105,7 +124,10 @@ def test_widget_selection_float_input_hidden(self):
selection=[(4.0, "A"), (8.0, "B")],
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.hidden",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.hidden",
)
expected_attrs = {
"type": "hidden",
@@ -119,7 +141,10 @@ def test_widget_many2one_input_hidden(self):
"""Test many2one field hidden."""
w_name, w_field = fake_field("many2one_field", type="many2one")
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.hidden",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.hidden",
)
expected_attrs = {
"type": "hidden",
diff --git a/cms_form/tests/widgets/test_widget_integer.py b/cms_form/tests/widgets/test_widget_integer.py
index 7e746307..fd3c371d 100644
--- a/cms_form/tests/widgets/test_widget_integer.py
+++ b/cms_form/tests/widgets/test_widget_integer.py
@@ -7,21 +7,23 @@ class TestWidgetInteger(TestWidgetCase):
# TODO: test extraction and conversion to proper field value
# on EVERY widget and not just rely on the marshallers.
- # Of course we have to switch to `w_html_fname` approach as hidden widget.
+ # Of course we have to switch to `html_fname` approach as hidden widget.
# This implies that we test w/ a full request too.
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- form = fake_form(an_int_field=10)
- cls.w_name, cls.w_field = fake_field("an_int_field", type="integer")
- cls.widget = cls.get_widget(
- cls.w_name, cls.w_field, form=form, widget_model="cms.form.widget.integer",
+ def setUp(self):
+ super().setUp()
+ form = fake_form(self.env, an_int_field=10)
+ self.w_name, self.w_field = fake_field("an_int_field", type="integer")
+ self.widget = self.get_widget(
+ self.w_name,
+ self.w_field,
+ form=form,
+ widget_model="cms.form.widget.integer",
)
def test_widget_integer_input(self):
expected_attrs = {
- "type": "text",
+ "type": "number",
"id": "an_int_field",
"name": "an_int_field",
"placeholder": "An int field...",
@@ -33,7 +35,7 @@ def test_widget_integer_input(self):
def test_widget_integer_input_required(self):
self.widget.w_field["required"] = True
expected_attrs = {
- "type": "text",
+ "type": "number",
"id": "an_int_field",
"name": "an_int_field",
"placeholder": "An int field...",
diff --git a/cms_form/tests/widgets/test_widget_many2one.py b/cms_form/tests/widgets/test_widget_many2one.py
index 08602aad..39a4c34e 100644
--- a/cms_form/tests/widgets/test_widget_many2one.py
+++ b/cms_form/tests/widgets/test_widget_many2one.py
@@ -10,9 +10,13 @@ class TestWidgetM2O(TestWidgetCase):
def setUpClass(cls):
super().setUpClass()
cls.partners = cls.env["res.partner"].search([], limit=4)
- cls.form = fake_form(
+
+ def setUp(self):
+ super().setUp()
+ self.form = fake_form(
+ self.env,
# fake defaults
- m2o_field=cls.partners.ids[0],
+ m2o_field=self.partners.ids[0],
)
def test_widget_many2one_base(self):
@@ -23,7 +27,10 @@ def test_widget_many2one_base(self):
domain=[("id", "in", self.partners.ids)],
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.many2one",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.many2one",
)
self.assertEqual(widget.w_comodel, self.env["res.partner"])
self.assertEqual(widget.w_domain, [("id", "in", self.partners.ids)])
@@ -32,10 +39,15 @@ def test_widget_many2one_base(self):
def test_widget_many2one_base_load(self):
# TODO: test load value from form record
w_name, w_field = fake_field(
- "m2o_field", type="many2one", relation="res.partner",
+ "m2o_field",
+ type="many2one",
+ relation="res.partner",
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.many2one",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.many2one",
)
self.assertEqual(widget.w_load(m2o_field="1"), 1)
@@ -49,10 +61,15 @@ def test_widget_many2one_base_load(self):
def test_widget_many2one_base_extract(self):
w_name, w_field = fake_field(
- "m2o_field", type="many2one", relation="res.partner",
+ "m2o_field",
+ type="many2one",
+ relation="res.partner",
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.many2one",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.many2one",
)
self.assertEqual(widget.w_extract(m2o_field="1"), 1)
@@ -70,10 +87,15 @@ def test_widget_many2one_base_extract(self):
def test_widget_many2one_base_render(self):
w_name, w_field = fake_field(
- "m2o_field", type="many2one", relation="res.partner",
+ "m2o_field",
+ type="many2one",
+ relation="res.partner",
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.many2one",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.many2one",
)
expected_attrs = {
"id": "m2o_field",
@@ -86,7 +108,9 @@ def test_widget_many2one_base_render(self):
def test_widget_many2one_multi(self):
w_name, w_field = fake_field(
- "m2o_field", type="many2one", relation="res.partner",
+ "m2o_field",
+ type="many2one",
+ relation="res.partner",
)
widget = self.get_widget(
w_name,
@@ -108,7 +132,9 @@ def test_widget_many2one_multi(self):
def test_widget_many2one_multi_load(self):
w_name, w_field = fake_field(
- "m2o_field", type="many2one", relation="res.partner",
+ "m2o_field",
+ type="many2one",
+ relation="res.partner",
)
widget = self.get_widget(
w_name,
@@ -125,7 +151,9 @@ def test_widget_many2one_multi_load(self):
def test_widget_many2one_multi_extract(self):
w_name, w_field = fake_field(
- "m2o_field", type="many2one", relation="res.partner",
+ "m2o_field",
+ type="many2one",
+ relation="res.partner",
)
widget = self.get_widget(
w_name,
diff --git a/cms_form/tests/widgets/test_widget_radio.py b/cms_form/tests/widgets/test_widget_radio.py
index 3cef7d49..b2fc3b44 100644
--- a/cms_form/tests/widgets/test_widget_radio.py
+++ b/cms_form/tests/widgets/test_widget_radio.py
@@ -8,6 +8,7 @@ class TestWidgetRadio(TestWidgetCase):
def setUpClass(cls):
super().setUpClass()
cls.form = fake_form(
+ cls.env,
# fake defaults
radio_field="opt2",
)
@@ -19,10 +20,15 @@ def test_widget_radio_base(self):
("opt3", "Option 3"),
]
w_name, w_field = fake_field(
- "radio_field", type="selection", selection=select_options,
+ "radio_field",
+ type="selection",
+ selection=select_options,
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.radio",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.radio",
)
node = self.to_xml_node(widget.render())[0]
self.assertIn("radio-select", node.attrib["class"])
@@ -50,9 +56,14 @@ def test_widget_radio_base(self):
if i == 1:
expected_attrs["checked"] = "checked"
self._test_element_attributes(
- node_input, "input", expected_attrs,
+ node_input,
+ "input",
+ expected_attrs,
)
node_span = node_label.getchildren()[1]
self._test_element_attributes(
- node_span, "span", {}, text="Option %s" % (i + 1),
+ node_span,
+ "span",
+ {},
+ text="Option %s" % (i + 1),
)
diff --git a/cms_form/tests/widgets/test_widget_selection.py b/cms_form/tests/widgets/test_widget_selection.py
index d634025e..d427f096 100644
--- a/cms_form/tests/widgets/test_widget_selection.py
+++ b/cms_form/tests/widgets/test_widget_selection.py
@@ -4,10 +4,10 @@
class TestWidgetSelection(TestWidgetCase):
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- cls.form = fake_form(
+ def setUp(self):
+ super().setUp()
+ self.form = fake_form(
+ self.env,
# fake defaults
selection_char_field="opt1",
selection_integer_field=2,
@@ -19,10 +19,15 @@ def test_widget_selection_base(self):
("opt1", "Option 1"),
]
w_name, w_field = fake_field(
- "selection_char_field", type="selection", selection=select_options,
+ "selection_char_field",
+ type="selection",
+ selection=select_options,
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.selection",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.selection",
)
expected_attrs = {
"id": "selection_char_field",
@@ -40,10 +45,15 @@ def test_widget_selection_char(self):
("opt3", "Option 3"),
]
w_name, w_field = fake_field(
- "selection_char_field", type="selection", selection=select_options,
+ "selection_char_field",
+ type="selection",
+ selection=select_options,
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.selection",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.selection",
)
expected_attrs = {
"id": "selection_char_field",
@@ -72,10 +82,15 @@ def test_widget_selection_integer(self):
(3, "Option 3"),
]
w_name, w_field = fake_field(
- "selection_integer_field", type="selection", selection=select_options,
+ "selection_integer_field",
+ type="selection",
+ selection=select_options,
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.selection",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.selection",
)
expected_attrs = {
"id": "selection_integer_field",
@@ -104,10 +119,15 @@ def test_widget_selection_float(self):
(3.0, "Option 3"),
]
w_name, w_field = fake_field(
- "selection_float_field", type="selection", selection=select_options,
+ "selection_float_field",
+ type="selection",
+ selection=select_options,
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.selection",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.selection",
)
expected_attrs = {
"id": "selection_float_field",
@@ -127,7 +147,10 @@ def test_widget_selection_float(self):
if i == 3:
expected_attrs["selected"] = "selected"
self._test_element_attributes(
- node_children[i], "option", expected_attrs, text="Option %s" % i,
+ node_children[i],
+ "option",
+ expected_attrs,
+ text="Option %s" % i,
)
# test conversion
extracted = widget.w_extract(selection_float_field="3.0")
@@ -140,7 +163,10 @@ def test_widget_selection_non_selection_field(self):
# do not pass `selection`
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.selection",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.selection",
)
# no selection found: should not fail and give back an empty list
self.assertEqual(widget.w_option_items, [])
diff --git a/cms_form/tests/widgets/test_widget_text.py b/cms_form/tests/widgets/test_widget_text.py
index 42a6a3d4..15335d67 100644
--- a/cms_form/tests/widgets/test_widget_text.py
+++ b/cms_form/tests/widgets/test_widget_text.py
@@ -9,15 +9,21 @@
class TestWidgetTxt(TestWidgetCase):
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- cls.form = fake_form(a_char_field="Just a string", a_text_field=TXT,)
+ def setUp(self):
+ super().setUp()
+ self.form = fake_form(
+ self.env,
+ a_char_field="Just a string",
+ a_text_field=TXT,
+ )
def test_widget_char_input(self):
w_name, w_field = fake_field("a_char_field")
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.char",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.char",
)
expected_attrs = {
"type": "text",
@@ -35,7 +41,10 @@ def test_widget_char_input(self):
def test_widget_text_input(self):
w_name, w_field = fake_field("a_text_field", type="text")
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.text",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.text",
)
expected_attrs = {
"id": "a_text_field",
@@ -50,7 +59,10 @@ def test_widget_text_input(self):
def test_widget_text_input_maxlength(self):
w_name, w_field = fake_field("a_text_field", type="text", maxlength=100)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.text",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.text",
)
node_items = self.to_xml_node(widget.render())
self.assertEqual(len(node_items), 2)
@@ -62,7 +74,10 @@ def test_widget_text_input_maxlength(self):
"maxlength": "100",
}
self._test_element_attributes(
- node_textarea, "textarea", expected_attrs, text=TXT,
+ node_textarea,
+ "textarea",
+ expected_attrs,
+ text=TXT,
)
node_counter = node_items[1]
expected_attrs = {
@@ -73,5 +88,7 @@ def test_widget_text_input_maxlength(self):
"class": "form-control text-counter",
}
self._test_element_attributes(
- node_counter, "input", expected_attrs,
+ node_counter,
+ "input",
+ expected_attrs,
)
diff --git a/cms_form/tests/widgets/test_widget_x2many.py b/cms_form/tests/widgets/test_widget_x2many.py
index f5bedc6c..3c1ab691 100644
--- a/cms_form/tests/widgets/test_widget_x2many.py
+++ b/cms_form/tests/widgets/test_widget_x2many.py
@@ -10,10 +10,14 @@ class TestWidgetX2M(TestWidgetCase):
def setUpClass(cls):
super().setUpClass()
cls.partners = cls.env["res.partner"].search([], limit=4)
- cls.form = fake_form(
+
+ def setUp(self):
+ super().setUp()
+ self.form = fake_form(
+ self.env,
# fake defaults
# behavior of o2m or m2m ATM is the same
- m2m_field=cls.partners.ids[0:2],
+ m2m_field=self.partners.ids[0:2],
)
def test_widget_x2many_base(self):
@@ -24,17 +28,25 @@ def test_widget_x2many_base(self):
domain=[("id", "in", self.partners.ids)],
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.many2many",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.many2many",
)
self.assertEqual(widget.w_comodel, self.env["res.partner"])
self.assertEqual(widget.w_domain, [("id", "in", self.partners.ids)])
def test_widget_x2many(self):
w_name, w_field = fake_field(
- "m2m_field", type="many2many", relation="res.partner",
+ "m2m_field",
+ type="many2many",
+ relation="res.partner",
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.many2many",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.many2many",
)
expected_attrs = {
"id": "m2m_field",
@@ -50,10 +62,15 @@ def test_widget_x2many(self):
def test_widget_x2many_base_load(self):
w_name, w_field = fake_field(
- "m2m_field", type="many2many", relation="res.partner",
+ "m2m_field",
+ type="many2many",
+ relation="res.partner",
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.many2many",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.many2many",
)
# test conversion
self.assertEqual(widget.w_load(m2m_field=False), "[]")
@@ -66,14 +83,20 @@ def test_widget_x2many_base_load_from_record(self):
categs = self.env["res.partner.category"].search([], limit=3)
partner = self.partners[0]
form = fake_form(
+ self.env,
# category_id=categs,
main_object=partner,
)
w_name, w_field = fake_field(
- "category_id", type="many2many", relation=categs._name,
+ "category_id",
+ type="many2many",
+ relation=categs._name,
)
widget = self.get_widget(
- w_name, w_field, form=form, widget_model="cms.form.widget.many2many",
+ w_name,
+ w_field,
+ form=form,
+ widget_model="cms.form.widget.many2many",
)
# flush categories if any
partner.category_id = False
@@ -94,20 +117,33 @@ def test_widget_x2many_base_load_from_record(self):
def test_widget_x2many_base_extract(self):
w_name, w_field = fake_field(
- "m2m_field", type="many2many", relation="res.partner",
+ "m2m_field",
+ type="many2many",
+ relation="res.partner",
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.many2many",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.many2many",
)
# test conversion
+ self.assertEqual(widget.w_extract(m2m_field="1,2,3"), [(6, False, [1, 2, 3])])
+ self.assertEqual(widget.w_extract(m2m_field=""), [(5,)])
+ self.form.form_extract_value_mode = "read"
self.assertEqual(widget.w_extract(m2m_field="1,2,3"), [1, 2, 3])
def test_widget_x2many_load_no_value(self):
w_name, w_field = fake_field(
- "m2m_field", type="many2many", relation="res.partner",
+ "m2m_field",
+ type="many2many",
+ relation="res.partner",
)
widget = self.get_widget(
- w_name, w_field, form=self.form, widget_model="cms.form.widget.many2many",
+ w_name,
+ w_field,
+ form=self.form,
+ widget_model="cms.form.widget.many2many",
)
self.assertEqual(widget.w_load(m2m_field=""), "[]")
# empty value from default_get
diff --git a/cms_form/utils.py b/cms_form/utils.py
index 11fdad8f..0265de19 100644
--- a/cms_form/utils.py
+++ b/cms_form/utils.py
@@ -6,14 +6,16 @@ def safe_to_integer(value, **kw):
"""Convert to integer safely."""
try:
return int(value)
- except (ValueError, TypeError):
+ except (ValueError, TypeError, AttributeError):
return None
def safe_to_float(value, **kw):
+ if value is False:
+ return 0.0
try:
- return float(value)
- except (ValueError, TypeError):
+ return float(value.replace(",", "."))
+ except (ValueError, TypeError, AttributeError):
return None
@@ -79,5 +81,5 @@ def data_merge(a, b):
except TypeError as e: # pragma: no cover
raise TypeError(
'"{}" in key "{}" when merging "{}" into "{}"'.format(e, key, b, a)
- )
+ ) from e
return a
diff --git a/setup/cms_form/odoo/addons/cms_form b/setup/cms_form/odoo/addons/cms_form
new file mode 120000
index 00000000..7a2c1e03
--- /dev/null
+++ b/setup/cms_form/odoo/addons/cms_form
@@ -0,0 +1 @@
+../../../../cms_form
\ No newline at end of file
diff --git a/setup/cms_form/setup.py b/setup/cms_form/setup.py
new file mode 100644
index 00000000..28c57bb6
--- /dev/null
+++ b/setup/cms_form/setup.py
@@ -0,0 +1,6 @@
+import setuptools
+
+setuptools.setup(
+ setup_requires=['setuptools-odoo'],
+ odoo_addon=True,
+)