From e564dd922a666ea90f1d8390cceca1b617bb9194 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 21 Sep 2026 08:47:46 -0600 Subject: [PATCH] Add waits for form validation to settle and to report a message A validation response clears the records' server errors before checking whether it is stale, so a message a newer response raised can show and then vanish. Tests can now wait out in-flight validation before editing and wait for a message with the same settle-and-recheck the clearing wait uses. --- .../test/tests/ehr/AbstractEHRTest.java | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/ehr/test/src/org/labkey/test/tests/ehr/AbstractEHRTest.java b/ehr/test/src/org/labkey/test/tests/ehr/AbstractEHRTest.java index 72413eaae..e81411a9a 100644 --- a/ehr/test/src/org/labkey/test/tests/ehr/AbstractEHRTest.java +++ b/ehr/test/src/org/labkey/test/tests/ehr/AbstractEHRTest.java @@ -1081,6 +1081,31 @@ protected void waitForFormValidationToClear() waitForValidationToClear(FORM_ERROR_SUMMARY, () -> summary.existsIn(getDriver()), FORM_ERROR_SUMMARY_TIMEOUT); } + /** Waits out any server validation already in flight, so a later edit's result cannot be undone by an earlier response landing after it. */ + protected void waitForFormValidationToSettle() + { + waitFor(this::isFormValidationQuiet, "Form validation did not settle", WAIT_FOR_JAVASCRIPT); + } + + /** + * Waits for the form to report a validation message, re-running server-side validation once if it does not. A + * validation response clears the records' server errors before checking whether it is stale, so a message a newer + * response raised can show and then vanish when an older one lands after it. + */ + protected void waitForValidationToReport(String message) + { + BooleanSupplier reported = () -> isTextPresent(message); + if (waitForValidationToSettleWith(reported, WAIT_FOR_JAVASCRIPT)) + return; + + log("Form did not settle reporting '" + message + "', re-validating"); + if (!revalidateForm()) + Assert.fail("Form did not report, and offers no Re-Validate to raise it: " + message); + + if (!waitForValidationToSettleWith(reported, WAIT_FOR_JAVASCRIPT)) + Assert.fail("Form did not report after re-validating: " + message); + } + /** * Waits for a validation message to clear, re-running server-side validation once if it does not. A value can be * accepted at the field while the form's error summary still lists it, which the form itself handles by pointing @@ -1111,21 +1136,26 @@ private void waitForValidationToClear(String message, BooleanSupplier reported, Assert.fail("Form kept reporting after re-validating: " + message); } + private boolean waitForValidationToSettleWithout(BooleanSupplier reported, int timeout) + { + return waitForValidationToSettleWith(() -> !reported.getAsBoolean(), timeout); + } + /** - * Waits for the form to go quiet without reporting the given message. DataEntryErrorPanel repaints on a buffered + * Waits for the form to go quiet with the summary in the given state. DataEntryErrorPanel repaints on a buffered * event rather than when the validation response lands, so the summary trails the form's actual state by up to a * second: a message can read as absent before validation has reported it, and read as present after the value - * that raised it was accepted. Neither is worth acting on, so require the form quiet and the message absent, then - * re-check after the repaint window to confirm the absence survives it. + * that raised it was accepted. Neither is worth acting on, so require the form quiet and the state reached, then + * re-check after the repaint window to confirm it survives. */ - private boolean waitForValidationToSettleWithout(BooleanSupplier reported, int timeout) + private boolean waitForValidationToSettleWith(BooleanSupplier settled, int timeout) { return waitFor(() -> { - if (!isFormValidationQuiet() || reported.getAsBoolean()) + if (!isFormValidationQuiet() || !settled.getAsBoolean()) return false; sleep(ERROR_PANEL_REPAINT_BUFFER); - return isFormValidationQuiet() && !reported.getAsBoolean(); + return isFormValidationQuiet() && settled.getAsBoolean(); }, timeout); }