Skip to content

Commit

Permalink
🦺 [#72] Fine tune validations of selects fields
Browse files Browse the repository at this point in the history
* Ensure multiple false|true is handled appropriately
* Model current behaviour of frontend data in tests
* Ensure that empty options are treated correctly
  depending on required/optional config
  • Loading branch information
sergei-maertens committed Apr 2, 2024
1 parent bc7fcfe commit 2db8fd0
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 7 deletions.
21 changes: 18 additions & 3 deletions src/openforms/formio/components/vanilla.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import logging
from datetime import time
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any

from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator
from django.utils.translation import gettext_lazy as _
Expand Down Expand Up @@ -440,16 +440,31 @@ def localize(self, component: SelectComponent, language_code: str, enabled: bool

def build_serializer_field(
self, component: SelectComponent
) -> serializers.MultipleChoiceField:
) -> serializers.ChoiceField:
validate = component.get("validate", {})
required = validate.get("required", False)
assert "values" in component["data"]
choices = [
(value["value"], value["label"]) for value in component["data"]["values"]
]
return serializers.MultipleChoiceField(

# map multiple false/true to the respective serializer field configuration
field_kwargs: dict[str, Any]
match component:
case {"multiple": True}:
field_cls = serializers.MultipleChoiceField
field_kwargs = {"allow_empty": not required}
case _:
field_cls = serializers.ChoiceField
field_kwargs = {}

return field_cls(
choices=choices,
required=required,
# See #4084 - form builder bug causes empty option to be added. allow_blank
# is therefore required for select with `multiple: true` too.
allow_blank=not required,
**field_kwargs,
)


Expand Down
34 changes: 34 additions & 0 deletions src/openforms/formio/tests/validation/test_currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,37 @@ def test_currency_with_plugin_validator(self):
is_valid, _ = validate_formio_data(component, {"foo": 1.5})

self.assertFalse(is_valid)

def test_currency_required_validation(self):
component: Component = {
"type": "currency",
"key": "foo",
"label": "Test",
"validate": {"required": True},
}

invalid_values = [
({}, "required"),
({"foo": None}, "null"),
]

for data, error_code in invalid_values:
with self.subTest(data=data):
is_valid, errors = validate_formio_data(component, data)

self.assertFalse(is_valid)
self.assertIn(component["key"], errors)
error = extract_error(errors, component["key"])
self.assertEqual(error.code, error_code)

def test_currency_optional_allows_empty(self):
component: Component = {
"type": "currency",
"key": "foo",
"label": "Test",
"validate": {"required": False},
}

is_valid, _ = validate_formio_data(component, {"foo": None})

self.assertTrue(is_valid)
67 changes: 63 additions & 4 deletions src/openforms/formio/tests/validation/test_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ def test_select_field_required_validation(self):
"key": "foo",
"label": "Test",
"data": {
"url": "",
"json": "",
"custom": "",
"values": [
{
"label": "first",
Expand All @@ -27,14 +24,76 @@ def test_select_field_required_validation(self):
"openForms": {"translations": {}},
},
],
"resource": "",
},
"validate": {"required": True},
}

invalid_values = [
({}, "required"),
({"foo": None}, "null"),
({"foo": ["first"]}, "invalid_choice"),
]

for data, error_code in invalid_values:
with self.subTest(data=data):
is_valid, errors = validate_formio_data(component, data)

self.assertFalse(is_valid)
self.assertIn(component["key"], errors)
error = extract_error(errors, component["key"])
self.assertEqual(error.code, error_code)

def test_optional_single_select(self):
component: SelectComponent = {
"type": "select",
"key": "foo",
"label": "Test",
"data": {
"values": [
{
"label": "A",
"value": "a",
},
{
"label": "B",
"value": "b",
},
],
},
"validate": {"required": False},
}

is_valid, _ = validate_formio_data(component, {"foo": ""})

self.assertTrue(is_valid)

def test_required_multiple_select(self):
component: SelectComponent = {
"type": "select",
"key": "foo",
"label": "Test",
"multiple": True,
"data": {
"values": [
{
"label": "A",
"value": "a",
},
{
"label": "B",
"value": "b",
},
],
},
"validate": {"required": True},
}

invalid_values = [
({}, "required"),
({"foo": None}, "null"),
({"foo": []}, "empty"),
({"foo": [""]}, "invalid_choice"),
({"foo": "b"}, "not_a_list"),
]

for data, error_code in invalid_values:
Expand Down

0 comments on commit 2db8fd0

Please sign in to comment.