Skip to content

Commit a3c91e0

Browse files
author
User
committed
Guard remaining empty-form submits against the same native-reload race
The reviewer flagged that `test_form_basic`, `test_form_bootstrap`, and `test_form_orm_model` use the identical bare `submit -> wait_for(error)` pattern as `test_form_sync_events`, so they share the same latent exposure to a native form submission (which reloads the page and resets state) when the `DjangoForm` `componentDidMount` submit listener has not yet attached under load. Extend the shared `_retry_submit_until` guard to those three sites as well. Added a `submit_selector` parameter because the bootstrap form uses `button[type=submit]` instead of `input[type=submit]`. Test-only; no product code touched. Verified: `hatch fmt` clean, full `test_components.py` green under CPU-contention load (74 passed), and the 5-test form family green across repeated load runs.
1 parent f41bda5 commit a3c91e0

1 file changed

Lines changed: 10 additions & 9 deletions

File tree

‎tests/test_app/tests/test_components.py‎

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -771,8 +771,7 @@ def test_form_basic(self):
771771
self.page.wait_for_selector("#id_password_field")
772772
self.page.wait_for_selector("#id_model_choice_field")
773773
self.page.wait_for_selector("#id_model_multiple_choice_field")
774-
self.page.wait_for_selector("input[type=submit]").click(delay=DELAY)
775-
self.page.wait_for_selector(".errorlist")
774+
self._retry_submit_until(".errorlist", fill=None)
776775

777776
# Submitting an empty form should result in 22 error elements.
778777
# The number of errors may change if/when new test form elements are created.
@@ -859,8 +858,7 @@ def test_form_bootstrap(self):
859858
self.page.wait_for_selector("#id_boolean_field")
860859
self.page.wait_for_selector("#id_char_field")
861860
self.page.wait_for_selector("#id_choice_field")
862-
self.page.wait_for_selector("button[type=submit]").click(delay=DELAY)
863-
self.page.wait_for_selector(".invalid-feedback")
861+
self._retry_submit_until(".invalid-feedback", fill=None, submit_selector="button[type=submit]")
864862

865863
# Submitting an empty form should result in 2 error elements.
866864
# The number of errors may change if/when new test form elements are created.
@@ -883,8 +881,7 @@ def test_form_bootstrap(self):
883881
def test_form_orm_model(self):
884882
uuid = uuid4().hex
885883
self.page.wait_for_selector("form")
886-
self.page.wait_for_selector("input[type=submit]").click(delay=DELAY)
887-
self.page.wait_for_selector(".errorlist")
884+
self._retry_submit_until(".errorlist", fill=None)
888885

889886
# Submitting an empty form should result in 1 error element.
890887
error_list = self.page.locator(".errorlist").all()
@@ -912,7 +909,9 @@ def test_form_orm_model(self):
912909
finally:
913910
os.environ.pop("DJANGO_ALLOW_ASYNC_UNSAFE")
914911

915-
def _retry_submit_until(self, selector: str, *, fill: str | None) -> None:
912+
def _retry_submit_until(
913+
self, selector: str, *, fill: str | None, submit_selector: str = "input[type=submit]"
914+
) -> None:
916915
"""Re-submit the form until an awaited state ``selector`` becomes visible.
917916
918917
The ``DjangoForm`` client component registers a native ``submit`` listener that
@@ -923,12 +922,14 @@ def _retry_submit_until(self, selector: str, *, fill: str | None) -> None:
923922
performs a *native* form submission: the browser reloads the page, which resets
924923
all ReactPy state, so the awaited callback never appears. Retry the submit
925924
(re-typing ``char_field`` when ``fill`` is set) until ``selector`` is satisfied
926-
instead of racing the mount/reconcile. Pass ``fill=None`` to submit unchanged.
925+
instead of racing the mount/reconcile. Pass ``fill=None`` to submit unchanged,
926+
and ``submit_selector`` when the submit control is not ``input[type=submit]``
927+
(e.g. the bootstrap form uses ``button[type=submit]``).
927928
"""
928929
for _ in range(3):
929930
if fill is not None:
930931
self.page.wait_for_selector("#id_char_field").type(fill, delay=DELAY)
931-
self.page.wait_for_selector("input[type=submit]").click(delay=DELAY)
932+
self.page.wait_for_selector(submit_selector).click(delay=DELAY)
932933
try:
933934
self.page.wait_for_selector(selector, timeout=5000)
934935
return

0 commit comments

Comments
 (0)