diff --git a/app/forms/field_handlers/address_handler.py b/app/forms/field_handlers/address_handler.py index 5c3baa3ec3..2ccd5c3985 100644 --- a/app/forms/field_handlers/address_handler.py +++ b/app/forms/field_handlers/address_handler.py @@ -1,6 +1,7 @@ from functools import cached_property from wtforms import FormField +from wtforms.fields.core import UnboundField from wtforms.validators import InputRequired from app.forms.address_form import AddressValidatorTypes, get_address_form @@ -28,7 +29,7 @@ def validators(self) -> AddressValidatorTypes: return validate_with - def get_field(self) -> FormField: + def get_field(self) -> UnboundField | FormField: return FormField( get_address_form(self.validators), label=self.label, diff --git a/app/forms/field_handlers/date_handlers.py b/app/forms/field_handlers/date_handlers.py index 5ea2d6bc1d..6330329aa4 100644 --- a/app/forms/field_handlers/date_handlers.py +++ b/app/forms/field_handlers/date_handlers.py @@ -1,8 +1,9 @@ from datetime import datetime, timezone from functools import cached_property -from typing import Any, Optional, Union +from typing import Any, Optional from dateutil.relativedelta import relativedelta +from wtforms.fields.core import UnboundField from app.forms.field_handlers.field_handler import FieldHandler from app.forms.fields import DateField, MonthYearDateField, YearDateField @@ -14,10 +15,7 @@ format_message_with_title, ) from app.questionnaire.rules.utils import parse_datetime - -DateValidatorTypes = list[ - Union[OptionalForm, DateRequired, DateCheck, SingleDatePeriodCheck] -] +from app.utilities.types import DateValidatorType class DateHandler(FieldHandler): @@ -26,8 +24,8 @@ class DateHandler(FieldHandler): DISPLAY_FORMAT = "d MMMM yyyy" @cached_property - def validators(self) -> DateValidatorTypes: - validate_with: DateValidatorTypes = [OptionalForm()] + def validators(self) -> list[DateValidatorType]: + validate_with: list[DateValidatorType] = [OptionalForm()] if self.answer_schema["mandatory"] is True: validate_with = [ @@ -52,8 +50,10 @@ def validators(self) -> DateValidatorTypes: validate_with.append(min_max_validator) return validate_with - def get_field(self) -> DateField: - return DateField(self.validators, label=self.label, description=self.guidance) + def get_field(self) -> UnboundField | DateField: + return DateField( + validators=self.validators, label=self.label, description=self.guidance + ) def get_min_max_validator( self, minimum_date: Optional[datetime], maximum_date: Optional[datetime] @@ -123,9 +123,9 @@ class MonthYearDateHandler(DateHandler): DATE_FORMAT = "yyyy-mm" DISPLAY_FORMAT = "MMMM yyyy" - def get_field(self) -> MonthYearDateField: + def get_field(self) -> UnboundField | MonthYearDateField: return MonthYearDateField( - self.validators, label=self.label, description=self.guidance + validators=self.validators, label=self.label, description=self.guidance ) def get_min_max_validator( @@ -152,9 +152,9 @@ class YearDateHandler(DateHandler): DATE_FORMAT = "yyyy" DISPLAY_FORMAT = "yyyy" - def get_field(self) -> YearDateField: + def get_field(self) -> UnboundField | YearDateField: return YearDateField( - self.validators, label=self.label, description=self.guidance + validators=self.validators, label=self.label, description=self.guidance ) def get_min_max_validator( diff --git a/app/forms/field_handlers/dropdown_handler.py b/app/forms/field_handlers/dropdown_handler.py index ec969293dc..6feec04a9f 100644 --- a/app/forms/field_handlers/dropdown_handler.py +++ b/app/forms/field_handlers/dropdown_handler.py @@ -2,13 +2,11 @@ from flask_babel import lazy_gettext from wtforms import SelectField +from wtforms.fields.core import UnboundField -from app.forms.field_handlers.select_handlers import ( - Choice, - ChoiceWithDetailAnswer, - SelectHandlerBase, -) +from app.forms.field_handlers.select_handlers import SelectHandlerBase from app.questionnaire.questionnaire_schema import InvalidSchemaConfigurationException +from app.utilities.types import Choice, ChoiceWithDetailAnswer class DropdownHandler(SelectHandlerBase): @@ -31,7 +29,7 @@ def choices(self) -> Sequence[Choice]: def _get_placeholder_text(self) -> str: return self.answer_schema.get("placeholder", self.DEFAULT_PLACEHOLDER) - def get_field(self) -> SelectField: + def get_field(self) -> UnboundField | SelectField: return SelectField( label=self.label, description=self.guidance, diff --git a/app/forms/field_handlers/duration_handler.py b/app/forms/field_handlers/duration_handler.py index e8024900a9..8c691d1d41 100644 --- a/app/forms/field_handlers/duration_handler.py +++ b/app/forms/field_handlers/duration_handler.py @@ -1,4 +1,5 @@ from wtforms import FormField +from wtforms.fields.core import UnboundField from app.forms.duration_form import get_duration_form from app.forms.field_handlers.field_handler import FieldHandler @@ -7,7 +8,7 @@ class DurationHandler(FieldHandler): MANDATORY_MESSAGE_KEY = "MANDATORY_DURATION" - def get_field(self) -> FormField: + def get_field(self) -> UnboundField | FormField: return FormField( get_duration_form(self.answer_schema, dict(self.error_messages)), label=self.label, diff --git a/app/forms/field_handlers/mobile_number_handler.py b/app/forms/field_handlers/mobile_number_handler.py index ecf95ddfdf..2344ad5c69 100644 --- a/app/forms/field_handlers/mobile_number_handler.py +++ b/app/forms/field_handlers/mobile_number_handler.py @@ -2,6 +2,7 @@ from typing import Union from wtforms import StringField +from wtforms.fields.core import UnboundField from app.forms.field_handlers.field_handler import FieldHandler from app.forms.validators import MobileNumberCheck, ResponseRequired @@ -21,7 +22,7 @@ def validators(self) -> MobileNumberValidatorTypes: return validate_with - def get_field(self) -> StringField: + def get_field(self) -> UnboundField | StringField: return StringField( label=self.label, description=self.guidance, validators=self.validators ) diff --git a/app/forms/field_handlers/number_handler.py b/app/forms/field_handlers/number_handler.py index 70c70f57a6..4d71ccb7af 100644 --- a/app/forms/field_handlers/number_handler.py +++ b/app/forms/field_handlers/number_handler.py @@ -2,6 +2,7 @@ from typing import Any, Type, Union from wtforms import DecimalField, IntegerField +from wtforms.fields.core import UnboundField from app.forms.field_handlers.field_handler import FieldHandler from app.forms.fields import DecimalFieldWithSeparator, IntegerFieldWithSeparator @@ -64,7 +65,7 @@ def _field_type( else IntegerFieldWithSeparator ) - def get_field(self) -> Union[DecimalField, IntegerField]: + def get_field(self) -> UnboundField | DecimalField | IntegerField: additional_args = ( {"places": self.max_decimals} if self._field_type == DecimalFieldWithSeparator diff --git a/app/forms/field_handlers/select_handlers.py b/app/forms/field_handlers/select_handlers.py index 647110505c..e76e7d65cf 100644 --- a/app/forms/field_handlers/select_handlers.py +++ b/app/forms/field_handlers/select_handlers.py @@ -1,5 +1,6 @@ -from collections import namedtuple -from typing import Any, Optional, Sequence, Union +from typing import Any, Optional, Sequence + +from wtforms.fields.core import UnboundField from app.forms.field_handlers.field_handler import FieldHandler from app.forms.fields import ( @@ -8,13 +9,7 @@ ) from app.questionnaire.dynamic_answer_options import DynamicAnswerOptions from app.questionnaire.questionnaire_schema import InvalidSchemaConfigurationException - -Choice = namedtuple("Choice", "value label") -ChoiceWithDetailAnswer = namedtuple( - "ChoiceWithDetailAnswer", "value label detail_answer_id" -) - -ChoiceType = Union[Choice, ChoiceWithDetailAnswer] +from app.utilities.types import ChoiceType, ChoiceWithDetailAnswer class SelectHandlerBase(FieldHandler): @@ -75,7 +70,7 @@ def coerce_str_unless_none(value: Optional[str]) -> Optional[str]: # not providing an answer and them selecting the 'None' option otherwise. # https://github.com/ONSdigital/eq-survey-runner/issues/1013 # See related WTForms PR: https://github.com/wtforms/wtforms/pull/288 - def get_field(self) -> SelectFieldWithDetailAnswer: + def get_field(self) -> UnboundField | SelectFieldWithDetailAnswer: return SelectFieldWithDetailAnswer( label=self.label, description=self.guidance, @@ -88,7 +83,7 @@ def get_field(self) -> SelectFieldWithDetailAnswer: class SelectMultipleHandler(SelectHandler): MANDATORY_MESSAGE_KEY = "MANDATORY_CHECKBOX" - def get_field(self) -> MultipleSelectFieldWithDetailAnswer: + def get_field(self) -> UnboundField | MultipleSelectFieldWithDetailAnswer: return MultipleSelectFieldWithDetailAnswer( label=self.label, description=self.guidance, diff --git a/app/forms/field_handlers/string_handler.py b/app/forms/field_handlers/string_handler.py index 661a645c41..161cb9e1ff 100644 --- a/app/forms/field_handlers/string_handler.py +++ b/app/forms/field_handlers/string_handler.py @@ -2,6 +2,7 @@ from typing import Union from wtforms import StringField, validators +from wtforms.fields.core import UnboundField from wtforms.validators import Length from app.forms.field_handlers.field_handler import FieldHandler @@ -33,7 +34,7 @@ def max_length(self) -> int: max_length: int = self.answer_schema.get("max_length", self.MAX_LENGTH) return max_length - def get_field(self) -> StringField: + def get_field(self) -> UnboundField | StringField: return StringField( label=self.label, description=self.guidance, validators=self.validators ) diff --git a/app/forms/field_handlers/text_area_handler.py b/app/forms/field_handlers/text_area_handler.py index 1d9eedfd82..5bc484f10a 100644 --- a/app/forms/field_handlers/text_area_handler.py +++ b/app/forms/field_handlers/text_area_handler.py @@ -2,6 +2,7 @@ from typing import Union from wtforms import validators +from wtforms.fields.core import UnboundField from wtforms.validators import Length from app.forms.field_handlers.field_handler import FieldHandler @@ -32,7 +33,7 @@ def get_length_validator(self) -> Length: return validators.length(-1, self.max_length, message=length_message) - def get_field(self) -> MaxTextAreaField: + def get_field(self) -> UnboundField | MaxTextAreaField: return MaxTextAreaField( label=self.label, description=self.guidance, diff --git a/app/forms/fields/date_field.py b/app/forms/fields/date_field.py index afe1f0f481..cf26d9344a 100644 --- a/app/forms/fields/date_field.py +++ b/app/forms/fields/date_field.py @@ -1,13 +1,17 @@ import logging from functools import cached_property +from typing import Any, Callable, Sequence, Type +from werkzeug.datastructures import MultiDict from wtforms import Form, FormField, StringField -from wtforms.utils import unset_value +from wtforms.utils import UnsetValue, unset_value + +from app.utilities.types import DateValidatorType logger = logging.getLogger(__name__) -def get_form_class(validators): +def get_form_class(validators: Sequence[DateValidatorType]) -> Type[Form]: class DateForm(Form): # Validation is only ever added to the 1 field that shows in all 3 variants # This is to prevent an error message for each input box @@ -16,7 +20,7 @@ class DateForm(Form): day = StringField() @cached_property - def data(self): + def data(self) -> str | None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation data = super().data @@ -30,11 +34,24 @@ def data(self): class DateField(FormField): - def __init__(self, validators, **kwargs): + def __init__( + self, + *, + validators: Sequence[DateValidatorType], + **kwargs: Any, + ) -> None: form_class = get_form_class(validators) - super().__init__(form_class, **kwargs) - - def process(self, formdata, data=unset_value, extra_filters=None): + super().__init__( + form_class, + **kwargs, + ) + + def process( + self, + formdata: MultiDict | None = None, + data: str | UnsetValue = unset_value, + extra_filters: Sequence[Callable] | None = None, + ) -> None: if data is not unset_value: substrings = data.split("-") data = {"year": substrings[0], "month": substrings[1], "day": substrings[2]} diff --git a/app/forms/fields/decimal_field_with_separator.py b/app/forms/fields/decimal_field_with_separator.py index ea202f0b50..24e574653a 100644 --- a/app/forms/fields/decimal_field_with_separator.py +++ b/app/forms/fields/decimal_field_with_separator.py @@ -1,4 +1,5 @@ from decimal import Decimal, InvalidOperation +from typing import Any, Sequence from wtforms import DecimalField @@ -15,11 +16,11 @@ class DecimalFieldWithSeparator(DecimalField): DecimalPlace validators """ - def __init__(self, **kwargs): + def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.data = None + self.data: Decimal | None = None - def process_formdata(self, valuelist): + def process_formdata(self, valuelist: Sequence[str] | None = None) -> None: if valuelist: try: self.data = Decimal(sanitise_number(valuelist[0])) diff --git a/app/forms/fields/integer_field_with_separator.py b/app/forms/fields/integer_field_with_separator.py index d218ab29b3..e4af166f22 100644 --- a/app/forms/fields/integer_field_with_separator.py +++ b/app/forms/fields/integer_field_with_separator.py @@ -1,3 +1,5 @@ +from typing import Any, Sequence + from wtforms import IntegerField from app.helpers.form_helpers import sanitise_number @@ -13,11 +15,11 @@ class IntegerFieldWithSeparator(IntegerField): DecimalPlace validators """ - def __init__(self, **kwargs): + def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.data = None + self.data: int | None = None - def process_formdata(self, valuelist): + def process_formdata(self, valuelist: Sequence[str] | None = None) -> None: if valuelist: try: self.data = int(sanitise_number(valuelist[0])) diff --git a/app/forms/fields/max_text_area_field.py b/app/forms/fields/max_text_area_field.py index 563351c897..a32b8f318d 100644 --- a/app/forms/fields/max_text_area_field.py +++ b/app/forms/fields/max_text_area_field.py @@ -1,8 +1,16 @@ +from typing import Any + from wtforms import TextAreaField class MaxTextAreaField(TextAreaField): - def __init__(self, label="", validators=None, rows=None, maxlength=None, **kwargs): - super().__init__(label, validators, **kwargs) + def __init__( + self, + *, + rows: int, + maxlength: int, + **kwargs: Any, + ) -> None: + super().__init__(**kwargs) self.rows = rows self.maxlength = maxlength diff --git a/app/forms/fields/month_year_date_field.py b/app/forms/fields/month_year_date_field.py index 4bee2feb8e..99373b9810 100644 --- a/app/forms/fields/month_year_date_field.py +++ b/app/forms/fields/month_year_date_field.py @@ -1,19 +1,23 @@ import logging from functools import cached_property +from typing import Any, Callable, Sequence, Type +from werkzeug.datastructures import MultiDict from wtforms import Form, FormField, StringField -from wtforms.utils import unset_value +from wtforms.utils import UnsetValue, unset_value + +from app.utilities.types import DateValidatorType logger = logging.getLogger(__name__) -def get_form_class(validators): +def get_form_class(validators: Sequence[DateValidatorType]) -> Type[Form]: class YearMonthDateForm(Form): year = StringField(validators=validators) month = StringField() @cached_property - def data(self): + def data(self) -> str | None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation data = super().data @@ -27,11 +31,24 @@ def data(self): class MonthYearDateField(FormField): - def __init__(self, validators, **kwargs): + def __init__( + self, + *, + validators: Sequence[DateValidatorType], + **kwargs: Any, + ) -> None: form_class = get_form_class(validators) - super().__init__(form_class, **kwargs) - - def process(self, formdata, data=unset_value, extra_filters=None): + super().__init__( + form_class, + **kwargs, + ) + + def process( + self, + formdata: MultiDict | None = None, + data: str | UnsetValue = unset_value, + extra_filters: Sequence[Callable] | None = None, + ) -> None: if data is not unset_value: substrings = data.split("-") data = {"year": substrings[0], "month": substrings[1]} diff --git a/app/forms/fields/multiple_select_field_with_detail_answer.py b/app/forms/fields/multiple_select_field_with_detail_answer.py index 619ec0b646..4276285041 100644 --- a/app/forms/fields/multiple_select_field_with_detail_answer.py +++ b/app/forms/fields/multiple_select_field_with_detail_answer.py @@ -1,4 +1,8 @@ -from wtforms import SelectMultipleField +from typing import Any, Generator, Sequence + +from wtforms import SelectFieldBase, SelectMultipleField + +from app.utilities.types import ChoiceType, ChoiceWidgetRenderType class MultipleSelectFieldWithDetailAnswer(SelectMultipleField): @@ -7,10 +11,18 @@ class MultipleSelectFieldWithDetailAnswer(SelectMultipleField): This saves us having to later map options with their detail_answer. """ - def __init__(self, **kwargs): - super().__init__(**kwargs) + def __init__( + self, + *, + choices: Sequence[ChoiceType], + **kwargs: Any, + ) -> None: + super().__init__( + choices=choices, + **kwargs, + ) - def __iter__(self): + def __iter__(self) -> Generator[SelectFieldBase._Option, None, None]: opts = { "widget": self.option_widget, "name": self.name, @@ -26,7 +38,7 @@ def __iter__(self): opt.checked = checked yield opt - def iter_choices(self): + def iter_choices(self) -> Generator[ChoiceWidgetRenderType, None, None]: for value, label, detail_answer_id in self.choices: selected = self.data is not None and self.coerce(value) in self.data yield value, label, selected, detail_answer_id diff --git a/app/forms/fields/select_field_with_detail_answer.py b/app/forms/fields/select_field_with_detail_answer.py index 5a5299939a..a52384d7d1 100644 --- a/app/forms/fields/select_field_with_detail_answer.py +++ b/app/forms/fields/select_field_with_detail_answer.py @@ -1,6 +1,10 @@ -from wtforms import SelectField +from typing import Any, Generator, Sequence + +from wtforms import SelectField, SelectFieldBase from wtforms.validators import ValidationError +from app.utilities.types import ChoiceType, ChoiceWidgetRenderType + class SelectFieldWithDetailAnswer(SelectField): """ @@ -8,10 +12,18 @@ class SelectFieldWithDetailAnswer(SelectField): This saves us having to later map options with their detail_answer. """ - def __init__(self, **kwargs): - super().__init__(**kwargs) + def __init__( + self, + *, + choices: Sequence[ChoiceType], + **kwargs: Any, + ) -> None: + super().__init__( + choices=choices, + **kwargs, + ) - def __iter__(self): + def __iter__(self) -> Generator[SelectFieldBase._Option, None, None]: opts = { "widget": self.option_widget, "name": self.name, @@ -27,11 +39,11 @@ def __iter__(self): opt.checked = checked yield opt - def iter_choices(self): + def iter_choices(self) -> Generator[ChoiceWidgetRenderType, None, None]: for value, label, detail_answer_id in self.choices: yield value, label, self.coerce(value) == self.data, detail_answer_id - def pre_validate(self, _): + def pre_validate(self, _: Any) -> None: for _, _, match, _ in self.iter_choices(): if match: break diff --git a/app/forms/fields/year_date_field.py b/app/forms/fields/year_date_field.py index f78915ef8c..b229bef315 100644 --- a/app/forms/fields/year_date_field.py +++ b/app/forms/fields/year_date_field.py @@ -1,18 +1,22 @@ import logging from functools import cached_property +from typing import Any, Callable, Sequence, Type +from werkzeug.datastructures import MultiDict from wtforms import Form, FormField, StringField -from wtforms.utils import unset_value +from wtforms.utils import UnsetValue, unset_value + +from app.utilities.types import DateValidatorType logger = logging.getLogger(__name__) -def get_form_class(validators): +def get_form_class(validators: Sequence[DateValidatorType]) -> Type[Form]: class YearDateForm(Form): year = StringField(validators=validators) @cached_property - def data(self): + def data(self) -> str | None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation data = super().data @@ -26,11 +30,24 @@ def data(self): class YearDateField(FormField): - def __init__(self, validators, **kwargs): + def __init__( + self, + *, + validators: Sequence[DateValidatorType], + **kwargs: Any, + ): form_class = get_form_class(validators) - super().__init__(form_class, **kwargs) - - def process(self, formdata, data=None, extra_filters=None): + super().__init__( + form_class, + **kwargs, + ) + + def process( + self, + formdata: MultiDict | None = None, + data: str | UnsetValue = unset_value, + extra_filters: Sequence[Callable] | None = None, + ) -> None: if data is not unset_value: substrings = data.split("-") data = {"year": substrings[0]} diff --git a/app/forms/validators.py b/app/forms/validators.py index 9d125f6f6c..4a0b479d95 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -127,7 +127,7 @@ def __call__( form: "QuestionnaireForm", field: Union[DecimalFieldWithSeparator, IntegerFieldWithSeparator], ) -> None: - value: Union[int, Decimal] = field.data + value: int | Decimal | None = field.data if value is not None: decimal_limit = ( diff --git a/app/utilities/types.py b/app/utilities/types.py index d0691c00b5..9a0c7d8d2c 100644 --- a/app/utilities/types.py +++ b/app/utilities/types.py @@ -1,6 +1,12 @@ from typing import TYPE_CHECKING, NamedTuple, TypeAlias, TypedDict, Union if TYPE_CHECKING: + from app.forms.validators import ( # pragma: no cover + DateCheck, + DateRequired, + OptionalForm, + SingleDatePeriodCheck, + ) from app.questionnaire.location import Location # pragma: no cover from app.questionnaire.relationship_location import ( RelationshipLocation, # pragma: no cover @@ -10,6 +16,13 @@ SupplementaryDataKeyType: TypeAlias = tuple[str, str | None] SupplementaryDataValueType: TypeAlias = dict | str | list | None +DateValidatorType: TypeAlias = Union[ + "OptionalForm", "DateRequired", "DateCheck", "SingleDatePeriodCheck" +] + +ChoiceType: TypeAlias = Union["Choice", "ChoiceWithDetailAnswer"] +ChoiceWidgetRenderType: TypeAlias = tuple[str, str, bool, str | None] + class SectionKeyDict(TypedDict): section_id: str @@ -44,3 +57,14 @@ def section_key(self) -> SectionKey: class SupplementaryDataListMapping(TypedDict): identifier: str | int list_item_id: str + + +class Choice(NamedTuple): + value: str + label: str + + +class ChoiceWithDetailAnswer(NamedTuple): + value: str + label: str + detail_answer_id: str | None diff --git a/mypy.ini b/mypy.ini index b318531b70..b86fec5d3b 100644 --- a/mypy.ini +++ b/mypy.ini @@ -46,7 +46,7 @@ disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.forms.field_handlers.*] +[mypy-app.forms.*] disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True diff --git a/tests/app/forms/field_handlers/conftest.py b/tests/app/forms/field_handlers/conftest.py index 45900567b5..7a29043aaf 100644 --- a/tests/app/forms/field_handlers/conftest.py +++ b/tests/app/forms/field_handlers/conftest.py @@ -3,10 +3,10 @@ from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore -from app.forms.field_handlers.select_handlers import Choice, ChoiceWithDetailAnswer from app.questionnaire import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver +from app.utilities.types import Choice, ChoiceWithDetailAnswer def get_mock_schema(): diff --git a/tests/app/forms/fields/test_date_field.py b/tests/app/forms/fields/test_date_field.py index f1a65d9fe2..7581fb1a23 100644 --- a/tests/app/forms/fields/test_date_field.py +++ b/tests/app/forms/fields/test_date_field.py @@ -1,10 +1,11 @@ -from wtforms import Form, validators +from wtforms import Form from app.forms.fields import date_field +from app.forms.validators import OptionalForm def test_generate_date_form_creates_empty_form(): - form_class = date_field.get_form_class([validators.Optional()]) + form_class = date_field.get_form_class([OptionalForm()]) assert hasattr(form_class, "day") assert hasattr(form_class, "month") @@ -12,7 +13,7 @@ def test_generate_date_form_creates_empty_form(): def test_date_form_empty_data(): - form = date_field.get_form_class([validators.Optional()]) + form = date_field.get_form_class([OptionalForm()]) assert form().data is None @@ -21,7 +22,7 @@ def test_date_form_format_data(): data = {"field": "2000-01-01"} class TestForm(Form): - field = date_field.DateField([validators.Optional()]) + field = date_field.DateField(validators=[OptionalForm()]) test_form = TestForm(data=data) diff --git a/tests/app/forms/fields/test_month_year_date_field.py b/tests/app/forms/fields/test_month_year_date_field.py index b618428b00..1cf89f1951 100644 --- a/tests/app/forms/fields/test_month_year_date_field.py +++ b/tests/app/forms/fields/test_month_year_date_field.py @@ -1,10 +1,11 @@ -from wtforms import Form, validators +from wtforms import Form from app.forms.fields import month_year_date_field +from app.forms.validators import OptionalForm def test_generate_month_year_date_form_creates_empty_form(): - form_class = month_year_date_field.get_form_class([validators.Optional()]) + form_class = month_year_date_field.get_form_class([OptionalForm()]) assert not hasattr(form_class, "day") assert hasattr(form_class, "month") @@ -12,7 +13,7 @@ def test_generate_month_year_date_form_creates_empty_form(): def test_month_year_date_form_empty_data(): - form = month_year_date_field.get_form_class([validators.Optional()]) + form = month_year_date_field.get_form_class([OptionalForm()]) assert form().data is None @@ -21,7 +22,7 @@ def test_month_year_date_form_format_data(): data = {"field": "2000-01"} class TestForm(Form): - field = month_year_date_field.MonthYearDateField([validators.Optional()]) + field = month_year_date_field.MonthYearDateField(validators=[OptionalForm()]) test_form = TestForm(data=data) diff --git a/tests/app/forms/fields/test_year_date_field.py b/tests/app/forms/fields/test_year_date_field.py index d44207b912..813e1127e3 100644 --- a/tests/app/forms/fields/test_year_date_field.py +++ b/tests/app/forms/fields/test_year_date_field.py @@ -1,10 +1,11 @@ -from wtforms import Form, validators +from wtforms import Form from app.forms.fields import year_date_field +from app.forms.validators import OptionalForm def test_generate_year_date_form_creates_empty_form(): - form_class = year_date_field.get_form_class([validators.Optional()]) + form_class = year_date_field.get_form_class([OptionalForm()]) assert not hasattr(form_class, "day") assert not hasattr(form_class, "month") @@ -12,7 +13,7 @@ def test_generate_year_date_form_creates_empty_form(): def test_year_date_form_empty_data(): - form = year_date_field.get_form_class([validators.Optional()]) + form = year_date_field.get_form_class([OptionalForm()]) assert form().data is None @@ -21,7 +22,7 @@ def test_year_date_form_format_data(): data = {"field": "2000"} class TestForm(Form): - field = year_date_field.YearDateField([validators.Optional()]) + field = year_date_field.YearDateField(validators=[OptionalForm()]) test_form = TestForm(data=data) diff --git a/tests/app/forms/test_custom_fields.py b/tests/app/forms/test_custom_fields.py index ef79081356..ad6f48542c 100644 --- a/tests/app/forms/test_custom_fields.py +++ b/tests/app/forms/test_custom_fields.py @@ -11,13 +11,23 @@ def test_text_area_a_wtforms_field(mock_form): - text_area = MaxTextAreaField("LabelText", _form=mock_form, name="aName") + text_area = MaxTextAreaField( + label="LabelText", + _form=mock_form, + name="aName", + rows=0, + maxlength=0, + ) assert isinstance(text_area, Field) def test_text_area_supports_maxlength_property(mock_form): text_area = MaxTextAreaField( - "TestLabel", maxlength=20, _form=mock_form, name="aName" + label="TestLabel", + maxlength=20, + _form=mock_form, + name="aName", + rows=0, ) assert isinstance(text_area, Field) assert text_area.maxlength == 20