diff --git a/docs/developers/backend/core/testing-tools.rst b/docs/developers/backend/core/testing-tools.rst index 58e785f1ca..335187c8d6 100644 --- a/docs/developers/backend/core/testing-tools.rst +++ b/docs/developers/backend/core/testing-tools.rst @@ -4,9 +4,32 @@ Test helpers ============ -HTML assertions +Open Forms tests are built with the core Django testing helpers defined in +:mod:`django.test`, extended with some third party libraries and project-specific +helpers. + +.. contents:: Helpers + :depth: 3 + :local: + :backlinks: entry + +Third party packages +==================== + +* `django-webtest `_: acts more like a browser + without being a full-blown browser. Very useful for admin tests, especially when + combined with `pyquery `_. +* `hypothesis `_: property based testing, very + good for generating fuzzy data to catch edge cases you never would think of. See + :ref:`developers_backend_core_testing_tools_hypothesis_strategies`. + + +Project helpers =============== +HTML assertions +--------------- + .. automodule:: openforms.utils.tests.html_assert :members: @@ -14,26 +37,45 @@ HTML assertions :members: Frontend redirects -================== +------------------ .. automodule:: openforms.frontend.tests :members: Migrations -========== +---------- .. automodule:: openforms.utils.tests.test_migrations :members: Formio assertions -================= +----------------- .. automodule:: openforms.formio.tests.assertions :members: :undoc-members: Recording HTTP traffic -====================== +---------------------- .. automodule:: openforms.utils.tests.vcr :members: + +.. _developers_backend_core_testing_tools_hypothesis_strategies: + +Custom Hypothesis strategies +---------------------------- + +General purpose strategies +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: openforms.tests.search_strategies + :members: + :undoc-members: + +Formio component strategies +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: openforms.formio.tests.search_strategies + :members: + :undoc-members: diff --git a/docs/developers/backend/tests.rst b/docs/developers/backend/tests.rst index 1d4f341056..5d3696fe92 100644 --- a/docs/developers/backend/tests.rst +++ b/docs/developers/backend/tests.rst @@ -7,6 +7,9 @@ Running tests The Open Forms backend comes with an extensive test suite to prevent regressions and also describe intended behaviour. +.. tip:: See the :ref:`developers_backend_core_testing_tools` for a collection of + utilities that you can use when writing tests. + Running the backend tests ========================= diff --git a/docs/developers/index.rst b/docs/developers/index.rst index be52c367e8..c46a93cfd8 100644 --- a/docs/developers/index.rst +++ b/docs/developers/index.rst @@ -31,7 +31,6 @@ familiarize yourself with the design principles. extending extensions checklists - manual_testing i18n csp npm diff --git a/docs/developers/manual_testing.rst b/docs/developers/manual_testing.rst deleted file mode 100644 index 12a1651ce7..0000000000 --- a/docs/developers/manual_testing.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. _developers_manual_testing: - -============== -Manual testing -============== - -Certain (UI) functionalities are not easily tested automatically. To prevent regressions, -the following test scenario's should be checked for a new build. - -Form builder tests -================== - -The form builder is the core tool used by content editors to design the forms to be -rendered by the SDK. It uses formio.js under the hood, with custom field types added. -These custom field types should be verified to be functional. - -1. Navigate to the admin. Then go to **Formulieren > Form definitions**. Next, click - **form definition toevoegen**. - -2. Verify that the following components are present under the label **Configuration**, - and can be added to the form builder by dragging it to the right: - - * Section *Formuliervelden*: - - * Text Field - * IBAN Field - * Text Area - * Number - * Checkbox - * Select boxes - * Select - * Currency - * Radio - - * Section *Opmaak* - - * Content - * Field Set - - * Section *Basisregistratie Personen* - - * Gezinsleden diff --git a/docs/developers/npm.rst b/docs/developers/npm.rst index 27619374cc..f7c9cfc4e8 100644 --- a/docs/developers/npm.rst +++ b/docs/developers/npm.rst @@ -14,7 +14,8 @@ Note that we have "two frontends": Common tooling ============== -The common tooling applies to both Open Forms and the SDK. +The common tooling applies to Open Forms, the SDK and the supporting libraries like +the formio-builder and types repository. **NVM** @@ -38,6 +39,23 @@ formatting when a ``.prettierrc.json`` config file is available. Currently, Prettier is configured to format ``.js`` and ``.scss`` files. +**Storybook** + +All projects/libraries have a Storybook for the UI components. When you're working on +components that aren't in Storybook yet, please add them. + +The `SDK Storybook`_ contains more technical documentation for the SDK. You can usually +run storybook with: + +.. code-block:: bash + + npm run storybook [-- --no-open] + +Writing `interaction tests `_ is +recommended, but please limit those to actual interactions. For more low-level tests, +stick to writing unit tests in Jest, using +`Testing Library `_. + **Managing translations** Translations are extracted from the code with helper scripts, and after the @@ -48,9 +66,9 @@ as we're currently re-organizing the tooling here. Cheat sheet: -* SDK: ``yarn makemessages`` and ``yarn compilemessages`` +* SDK: ``npm run makemessages`` and ``npm run compilemessages`` * Backend: ``./bin/makemessages_js.sh`` and ``./bin/compilemessages_js.sh`` -* Libraries: ``./bin/makemessages.sh`` and ``(yarn|npm run) compilemessages`` +* Libraries: ``./bin/makemessages.sh`` and ``npm run compilemessages`` There is also a ``bin/find_untranslated_js.py`` script to point out potentially missed translations, as the JSON file format is not too friendly to keep track of everything. @@ -61,24 +79,6 @@ Indent sizes and other code formatting rules are specified in the ``.editorconfi which should be supported by most editors. Possibly you need to install a plugin for it to be activated though. -SDK toolchain -============= - -See the `SDK Storybook`_ for more technical documentation. - -**Yarn** - -In the SDK we use yarn_ rather than NPM. There is no particular version pinned at the -moment (since the lockfile format did not recently change). The node version is pinned -in ``.nvmrc`` though. - -The Yarn CLI is almost the same as NPM. - -**Storybook** - -We are shifting isolated/component development and interaction testing towards -storybook. Make sure to check it out and fill the gaps in documentation! - .. _nvm: https://github.com/nvm-sh/nvm .. _yarn: https://yarnpkg.com/ .. _Prettier: https://prettier.io/ diff --git a/src/openforms/formio/tests/search_strategies.py b/src/openforms/formio/tests/search_strategies.py index c9f7ea57c0..04f93c91e6 100644 --- a/src/openforms/formio/tests/search_strategies.py +++ b/src/openforms/formio/tests/search_strategies.py @@ -1,6 +1,9 @@ """ Expose hypothesis derived strategies to work with Formio.js data structures. +.. tip:: Use hypothesis strategies to generate random Formio form configurations to + test implementation robustness. + All the formio component definitions must be JSON-serializable. JSON in itself can handle NULL bytes inside strings (they turn into \u0000), but JSONB as used by postgresql and Django's model.JSONField - where we persist these component definitions - @@ -373,8 +376,8 @@ def map_component(): return st.fixed_dictionaries(_minimal_component_mapping("map"), optional=optional) -def nested_components(max_size=10): - return st.lists(any_component(stop_nesting=True), max_size=max_size) +def nested_components(): + return st.lists(any_component, min_size=1, max_size=5) def edit_grid_component(): @@ -457,8 +460,9 @@ def cosign_v1_component(): ) -def any_component(stop_nesting=False): - inputs = [ +any_component = st.deferred( + lambda: st.one_of( + # INPUTS textfield_component(), email_component(), date_component(), @@ -473,8 +477,7 @@ def any_component(stop_nesting=False): selectboxes_component(), currency_component(), radio_component(), - ] - special = [ + # SPECIAL iban_component(), license_plate_component(), bsn_component(), @@ -483,22 +486,17 @@ def any_component(stop_nesting=False): signature_component(), cosign_v2_component(), map_component(), - ] - # if we keep generating nested structures, we run into Python Recursion limits - if not stop_nesting: - special.append(edit_grid_component()) - layout = [ + edit_grid_component(), + # LAYOUT content_component(), - ] - if not stop_nesting: - layout += [ - columns_component(), - fieldset_component(), - ] - deprecated = [ + columns_component(), + fieldset_component(), + # DEPRECATED postcode_component(), password_component(), cosign_v1_component(), - ] - all_types = inputs + special + layout + deprecated - return st.one_of(all_types) + ) +) +""" +A search strategy returning any possible/supported Formio component dictionary. +""" diff --git a/src/openforms/formio/tests/test_search_strategies.py b/src/openforms/formio/tests/test_search_strategies.py index 7435ab68c3..82ea0138a3 100644 --- a/src/openforms/formio/tests/test_search_strategies.py +++ b/src/openforms/formio/tests/test_search_strategies.py @@ -14,6 +14,7 @@ from ..validators import variable_key_validator from .search_strategies import ( address_nl_component, + any_component, bsn_component, checkbox_component, columns_component, @@ -55,72 +56,67 @@ def test_formio_key_validates(self, key: str): except ValidationError: self.fail("Generated formio key did not pass validation") + @given(any_component) + def test_any_component(self, component: Component): + self.assertIn("type", component) + self.assertIn("key", component) + @given(textfield_component()) def test_textfield_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "textfield") self.assertIn("key", component) self.assertIn("label", component) @given(email_component()) def test_email_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "email") self.assertIn("key", component) self.assertIn("label", component) @given(date_component()) def test_date_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "date") self.assertIn("key", component) self.assertIn("label", component) @given(datetime_component()) def test_datetime_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "datetime") self.assertIn("key", component) self.assertIn("label", component) @given(time_component()) def test_time_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "time") self.assertIn("key", component) self.assertIn("label", component) @given(phone_number_component()) def test_phone_number_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "phoneNumber") self.assertIn("key", component) self.assertIn("label", component) @given(file_component()) def test_file_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "file") self.assertIn("key", component) self.assertIn("label", component) @given(textarea_component()) def test_textarea_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "textarea") self.assertIn("key", component) self.assertIn("label", component) @given(number_component()) def test_number_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "number") self.assertIn("key", component) self.assertIn("label", component) @given(select_component()) def test_select_component(self, component: SelectComponent): - assert "type" in component self.assertEqual(component["type"], "select") self.assertIn("key", component) self.assertIn("label", component) @@ -133,14 +129,12 @@ def test_select_component(self, component: SelectComponent): @given(checkbox_component()) def test_checkbox_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "checkbox") self.assertIn("key", component) self.assertIn("label", component) @given(selectboxes_component()) def test_selectboxes_component(self, component: SelectBoxesComponent): - assert "type" in component self.assertEqual(component["type"], "selectboxes") self.assertIn("key", component) self.assertIn("label", component) @@ -152,14 +146,12 @@ def test_selectboxes_component(self, component: SelectBoxesComponent): @given(currency_component()) def test_currency_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "currency") self.assertIn("key", component) self.assertIn("label", component) @given(radio_component()) def test_radio_component(self, component: RadioComponent): - assert "type" in component self.assertEqual(component["type"], "radio") self.assertIn("key", component) self.assertIn("label", component) @@ -171,84 +163,72 @@ def test_radio_component(self, component: RadioComponent): @given(iban_component()) def test_iban_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "iban") self.assertIn("key", component) self.assertIn("label", component) @given(license_plate_component()) def test_license_plate_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "licenseplate") self.assertIn("key", component) self.assertIn("label", component) @given(bsn_component()) def test_bsn_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "bsn") self.assertIn("key", component) self.assertIn("label", component) @given(address_nl_component()) def test_address_nl_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "addressNL") self.assertIn("key", component) self.assertIn("label", component) @given(np_family_members_component()) def test_np_family_members_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "npFamilyMembers") self.assertIn("key", component) self.assertIn("label", component) @given(signature_component()) def test_signature_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "signature") self.assertIn("key", component) self.assertIn("label", component) @given(cosign_v2_component()) def test_cosign_v2_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "cosign") self.assertIn("key", component) self.assertIn("label", component) @given(map_component()) def test_map_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "map") self.assertIn("key", component) self.assertIn("label", component) @given(postcode_component()) def test_postcode_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "postcode") self.assertIn("key", component) self.assertIn("label", component) @given(password_component()) def test_password_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "password") self.assertIn("key", component) self.assertIn("label", component) @given(cosign_v1_component()) def test_cosign_v1_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "coSign") self.assertIn("key", component) self.assertIn("label", component) @given(content_component()) def test_content_component(self, component: ContentComponent): - assert "type" in component self.assertEqual(component["type"], "content") self.assertIn("key", component) self.assertIn("label", component) @@ -256,9 +236,8 @@ def test_content_component(self, component: ContentComponent): @given(edit_grid_component()) # recursive structure, more expensive to draw examples - @settings(max_examples=10, deadline=500) + @settings(max_examples=10) def test_edit_grid_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "editgrid") self.assertIn("key", component) self.assertIn("label", component) @@ -269,9 +248,8 @@ def test_edit_grid_component(self, component: Component): @given(fieldset_component()) # recursive structure, more expensive to draw examples - @settings(max_examples=10, deadline=500) + @settings(max_examples=10) def test_fieldset_component(self, component: Component): - assert "type" in component self.assertEqual(component["type"], "fieldset") self.assertIn("key", component) self.assertIn("label", component) @@ -283,9 +261,8 @@ def test_fieldset_component(self, component: Component): @given(columns_component()) # recursive structure, more expensive to draw examples - @settings(max_examples=10, deadline=500) + @settings(max_examples=10) def test_columns_component(self, component: ColumnsComponent): - assert "type" in component self.assertEqual(component["type"], "columns") self.assertIn("key", component) self.assertNotIn("label", component) diff --git a/src/openforms/forms/api/serializers/form_definition.py b/src/openforms/forms/api/serializers/form_definition.py index 967c154483..b05fd896c4 100644 --- a/src/openforms/forms/api/serializers/form_definition.py +++ b/src/openforms/forms/api/serializers/form_definition.py @@ -1,5 +1,3 @@ -import warnings - from django.urls import reverse from django.utils.translation import gettext_lazy as _ @@ -14,7 +12,6 @@ ModelTranslationsSerializer, ) -from ...fd_translations_converter import process_component_tree from ...models import Form, FormDefinition from ...validators import ( validate_form_definition_is_reusable, @@ -145,24 +142,10 @@ def to_representation(self, instance): def validate(self, attrs): attrs = super().validate(attrs) if self.instance: + assert isinstance(self.instance, FormDefinition) validate_form_definition_is_reusable( self.instance, new_value=attrs.get("is_reusable") ) - - # during import, process legacy format component translations - if self.context.get("is_import", False) and ( - translations_store := attrs.get("component_translations") - ): - warnings.warn( - "Form-definition component translations are deprecated, the " - "compatibility layer wil be removed in Open Forms 3.0.", - DeprecationWarning, - ) - process_component_tree( - components=attrs["configuration"]["components"], - translations_store=translations_store, - ) - return attrs diff --git a/src/openforms/forms/fd_translations_converter.py b/src/openforms/forms/fd_translations_converter.py deleted file mode 100644 index 1f4415430d..0000000000 --- a/src/openforms/forms/fd_translations_converter.py +++ /dev/null @@ -1,177 +0,0 @@ -""" -Implements the translation converter for form definitions. - -Github issue: https://github.com/open-formulieren/open-forms/issues/2958 - -Before, translations for formio components were saved in the JSONField -``FormDefinition.component_translations``, which is a key-value mapping of language -code to translations mapping, which itself is a key-value mapping of source string to -translation string. This had the drawback that if different components happen to use the -same literal, they also shared the same translation. - -The utilities here help in converting these translations to component-local -translations, where the translations are stored on each component inside the formio -schema. The exact set of supported fields for translation varies per component, the -reference for this is the typescript type definitions: -https://open-formulieren.github.io/types/modules/index.html -""" - -import logging -from typing import TypedDict - -from glom import assign - -from openforms.formio.service import iter_components -from openforms.formio.typing import Component - -logger = logging.getLogger(__name__) - - -class Option(TypedDict): - value: str - label: str - - -def _set_translations( - obj: Component | Option, - translations_from_store: dict[str, str], - locale: str, - fields: list[str], -) -> None: - translations = { - key: translation - for key in fields - if (literal := obj.get(key)) - and (translation := translations_from_store.get(literal)) - } - if not translations: - return - assign(obj, f"openForms.translations.{locale}", translations, missing=dict) - - -def _move_translations(component: Component, locale: str, translations: dict[str, str]): - """ - Given a component and translation store, mutate the component to bake in translations. - - This mutates the ``component`` parameter! - """ - assert "type" in component - - match component: - case {"type": "textfield" | "textarea"}: - _set_translations( - component, - translations, - locale, - fields=[ - "label", - "description", - "tooltip", - "defaultValue", - "placeholder", - ], - ) - case { - "type": "email" - | "date" - | "datetime" - | "time" - | "phoneNumber" - | "file" - | "checkbox" - | "currency" - | "iban" - | "licenseplate" - | "bsn" - | "addressNL" - | "npFamilyMembers" - | "cosign" - | "map" - | "postcode" - | "password" - }: - _set_translations( - component, - translations, - locale, - fields=["label", "description", "tooltip"], - ) - - case {"type": "number"}: - _set_translations( - component, - translations, - locale, - fields=["label", "description", "tooltip", "suffix"], - ) - - case {"type": "select" | "selectboxes" | "radio", **rest}: - _set_translations( - component, - translations, - locale, - fields=["label", "description", "tooltip"], - ) - - # process options, which have their translations inside of each option - match rest: - case {"data": {"values": values}} | {"values": values}: - values: list[Option] - for value in values: - _set_translations(value, translations, locale, fields=["label"]) - - case {"type": "signature"}: - _set_translations( - component, - translations, - locale, - fields=["label", "description", "tooltip", "footer"], - ) - - case {"type": "coSign"}: - _set_translations( - component, translations, locale, fields=["label", "description"] - ) - - case {"type": "editgrid"}: - _set_translations( - component, - translations, - locale, - fields=[ - "label", - "description", - "tooltip", - "groupLabel", - "addAnother", - "saveRow", - "removeRow", - ], - ) - - case {"type": "content"}: - # label is legacy and no longer exposed in the new form builder, but pre-existing - # form definitions may have it set. - _set_translations(component, translations, locale, fields=["label", "html"]) - - case {"type": "columns"}: - pass - - case {"type": "fieldset"}: - _set_translations(component, translations, locale, fields=["label"]) - - case _: # pragma: no cover - # should not happen - logger.warning( - "Could not move translations for unknown component type %s", - component["type"], - ) - - -def process_component_tree( - components: list[Component], translations_store: dict[str, dict[str, str]] -): - tree = {"components": components} - for component in iter_components(tree, recursive=True): # type: ignore - for lang_code, translations in translations_store.items(): - _move_translations(component, lang_code, translations) diff --git a/src/openforms/forms/tests/test_import_export.py b/src/openforms/forms/tests/test_import_export.py index 0722b2578e..7ab4cd882d 100644 --- a/src/openforms/forms/tests/test_import_export.py +++ b/src/openforms/forms/tests/test_import_export.py @@ -1067,76 +1067,6 @@ def test_rountrip_form_with_theme_override(self): imported_form = Form.objects.exclude(pk=form.pk).get() self.assertIsNone(imported_form.theme) - def test_import_form_with_legacy_formio_component_translations_format(self): - """ - Legacy translations need to be converted to the new format. - """ - resources = { - "forms": [ - { - "active": True, - "authentication_backends": [], - "is_deleted": False, - "login_required": False, - "maintenance_mode": False, - "name": "Test Form 1", - "internal_name": "Test Form Internal 1", - "product": None, - "show_progress_indicator": True, - "slug": "translations", - "url": "http://testserver/api/v2/forms/324cadce-a627-4e3f-b117-37ca232f16b2", - "uuid": "324cadce-a627-4e3f-b117-37ca232f16b2", - } - ], - "formSteps": [ - { - "form": "http://testserver/api/v2/forms/324cadce-a627-4e3f-b117-37ca232f16b2", - "form_definition": "http://testserver/api/v2/form-definitions/f0dad93b-333b-49af-868b-a6bcb94fa1b8", - "index": 0, - "slug": "test-step-1", - "uuid": "3ca01601-cd20-4746-bce5-baab47636823", - }, - ], - "formDefinitions": [ - { - "configuration": { - "components": [ - { - "key": "textfield", - "type": "textfield", - "label": "TEXTFIELD_LABEL", - }, - ] - }, - "name": "Def 1 - With condition", - "slug": "test-definition-1", - "url": "http://testserver/api/v2/form-definitions/f0dad93b-333b-49af-868b-a6bcb94fa1b8", - "uuid": "f0dad93b-333b-49af-868b-a6bcb94fa1b8", - "component_translations": { - "nl": { - "TEXTFIELD_LABEL": "Tekstveld", - } - }, - }, - ], - } - - with zipfile.ZipFile(self.filepath, "w") as zip_file: - for name, data in resources.items(): - zip_file.writestr(f"{name}.json", json.dumps(data)) - - call_command("import", import_file=self.filepath) - - fd = FormDefinition.objects.get() - textfield = fd.configuration["components"][0] - - self.assertIn("openForms", textfield) - self.assertIn("translations", textfield["openForms"]) - self.assertIn("nl", textfield["openForms"]["translations"]) - nl_translations = textfield["openForms"]["translations"]["nl"] - self.assertIn("label", nl_translations) - self.assertEqual(nl_translations["label"], "Tekstveld") - @tag("gh-3975") def test_import_form_with_old_service_fetch_config(self): resources = { diff --git a/src/openforms/forms/tests/test_translations_converter.py b/src/openforms/forms/tests/test_translations_converter.py deleted file mode 100644 index f83af22ba0..0000000000 --- a/src/openforms/forms/tests/test_translations_converter.py +++ /dev/null @@ -1,263 +0,0 @@ -from django.test import SimpleTestCase - -from glom import glom -from hypothesis import given, settings - -from openforms.formio.service import iter_components -from openforms.formio.tests.search_strategies import ( - address_nl_component, - any_component, - bsn_component, - checkbox_component, - columns_component, - content_component, - cosign_v1_component, - cosign_v2_component, - currency_component, - date_component, - datetime_component, - edit_grid_component, - email_component, - fieldset_component, - file_component, - iban_component, - license_plate_component, - map_component, - np_family_members_component, - number_component, - password_component, - phone_number_component, - postcode_component, - radio_component, - select_component, - selectboxes_component, - signature_component, - textarea_component, - textfield_component, - time_component, -) -from openforms.formio.typing import ( - ColumnsComponent, - Component, - ContentComponent, - RadioComponent, - SelectBoxesComponent, - SelectComponent, -) - -from ..fd_translations_converter import process_component_tree - - -def _get_dummy_translations_store(component): - translations_store = {"nl": {}} - for comp in iter_components({"components": [component]}, recursive=True): - if literal := comp.get("label"): - translations_store["nl"][literal] = "Vertaald label" - return translations_store - - -def do_processing(component): - translations_store = _get_dummy_translations_store(component) - process_component_tree([component], translations_store) - - -class ConverterRobustnessTests(SimpleTestCase): - """ - Generate fixtures with hypothesis to check that our converter doesn't unexpectedly - crashes on possibly weird but valid data. - """ - - def assertLabelsTranslated(self, component): - for comp in iter_components({"components": [component]}, recursive=True): - if not comp.get("label"): - continue - translation = glom(comp, "openForms.translations.nl.label", default="") - self.assertEqual(translation, "Vertaald label") - - @given(any_component()) - # recursive structure, more expensive to draw examples - @settings(max_examples=25, deadline=500) - def test_any_component_is_properly_processed(self, component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(textfield_component()) - def test_textfield_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(email_component()) - def test_email_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(date_component()) - def test_date_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(datetime_component()) - def test_datetime_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(time_component()) - def test_time_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(phone_number_component()) - def test_phone_number_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(file_component()) - def test_file_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(textarea_component()) - def test_textarea_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(number_component()) - def test_number_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(select_component()) - def test_select_component(self, component: SelectComponent): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(checkbox_component()) - def test_checkbox_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(selectboxes_component()) - def test_selectboxes_component(self, component: SelectBoxesComponent): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(currency_component()) - def test_currency_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(radio_component()) - def test_radio_component(self, component: RadioComponent): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(iban_component()) - def test_iban_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(license_plate_component()) - def test_license_plate_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(bsn_component()) - def test_bsn_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(address_nl_component()) - def test_address_nl_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(np_family_members_component()) - def test_np_family_members_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(signature_component()) - def test_signature_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(cosign_v2_component()) - def test_cosign_v2_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(map_component()) - def test_map_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(postcode_component()) - def test_postcode_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(password_component()) - def test_password_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(cosign_v1_component()) - def test_cosign_v1_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(content_component()) - def test_content_component(self, component: ContentComponent): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(edit_grid_component()) - # recursive structure, more expensive to draw examples - @settings(max_examples=10, deadline=500) - def test_edit_grid_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(fieldset_component()) - # recursive structure, more expensive to draw examples - @settings(max_examples=10, deadline=500) - def test_fieldset_component(self, component: Component): - do_processing(component) - - self.assertLabelsTranslated(component) - - @given(columns_component()) - # recursive structure, more expensive to draw examples - @settings(max_examples=10, deadline=500) - def test_columns_component(self, component: ColumnsComponent): - do_processing(component) - - self.assertLabelsTranslated(component)