|
3 | 3 | from wagtail.test.utils import WagtailPageTestCase |
4 | 4 | from wagtail_localize.models import TranslationSource |
5 | 5 |
|
6 | | -from forms.models import FormPage |
| 6 | +from forms.models import FormField, FormPage |
7 | 7 |
|
8 | 8 |
|
9 | 9 | class FormsTestCase(WagtailPageTestCase): |
@@ -56,6 +56,57 @@ def test_incorrect_form_is_rejected(self): |
56 | 56 | ) |
57 | 57 | # Updates sometimes mess with the order of the translations and so the displayed translation. Both are fine. |
58 | 58 |
|
| 59 | + def test_form_field_clean_name_set_on_save(self): |
| 60 | + """ |
| 61 | + Regression test: FormField.clean_name must never be empty after save. |
| 62 | +
|
| 63 | + Certain code paths (e.g. reconstruction from a page revision via |
| 64 | + from_serializable_data) can produce FormField instances whose pk is |
| 65 | + already set before save() is called, causingAbstractFormField.save() |
| 66 | + to skip the clean_name computation (it only runs when pk is None). |
| 67 | + This leaves clean_name as "" in the database, making all BO responses |
| 68 | + appear as None. |
| 69 | + """ |
| 70 | + form_page = FormPage.objects.first() |
| 71 | + |
| 72 | + # Simulate the problematic path: a FormField whose pk is pre-assigned |
| 73 | + # (non-None) but whose clean_name is still empty, as happens when |
| 74 | + # from_serializable_data reconstructs a child object from a revision. |
| 75 | + field = FormField.objects.filter(page=form_page).first() |
| 76 | + original_clean_name = field.clean_name |
| 77 | + |
| 78 | + field.clean_name = "" |
| 79 | + field.save() |
| 80 | + |
| 81 | + field.refresh_from_db() |
| 82 | + self.assertNotEqual(field.clean_name, "", "clean_name should not be empty after save") |
| 83 | + self.assertEqual(field.clean_name, original_clean_name) |
| 84 | + |
| 85 | + def test_get_data_fields_has_no_empty_clean_names(self): |
| 86 | + """ |
| 87 | + Regression test: every FormField returned by get_data_fields() must |
| 88 | + have a non-empty clean_name. |
| 89 | +
|
| 90 | + If clean_name is "" in the DB, get_data_fields() returns ("", label) |
| 91 | + tuples. The BO then calls form_data.get("") which is always None, and |
| 92 | + the xlsx export collapses all "" keys so every column header becomes |
| 93 | + the last field's label. |
| 94 | + """ |
| 95 | + form_page = FormPage.objects.first() |
| 96 | + |
| 97 | + # Force empty clean_names on all fields to simulate the buggy state. |
| 98 | + FormField.objects.filter(page=form_page).update(clean_name="") |
| 99 | + |
| 100 | + # Re-save each field; the overridden save() must restore clean_name. |
| 101 | + for field in FormField.objects.filter(page=form_page): |
| 102 | + field.save() |
| 103 | + |
| 104 | + data_fields = form_page.get_data_fields() |
| 105 | + for name, label in data_fields: |
| 106 | + if name == "submit_time": |
| 107 | + continue |
| 108 | + self.assertNotEqual(name, "", f"Field '{label}' has an empty clean_name after save()") |
| 109 | + |
59 | 110 | def test_form_field_labels_are_translatable(self): |
60 | 111 | form_page = FormPage.objects.first() |
61 | 112 | source, _ = TranslationSource.update_or_create_from_instance(form_page) |
|
0 commit comments