From 59e6db3d0f5a47691dc93b9a34a91239e8e9d322 Mon Sep 17 00:00:00 2001 From: "totally-not-ai[bot]" <290682512+totally-not-ai[bot]@users.noreply.github.com> Date: Sat, 19 Sep 2026 08:28:02 +0200 Subject: [PATCH] feat!: refuse text values the browser physically prevents in TextFieldTester and TextAreaTester (#252) `TextFieldTester.setValue` and `TextAreaTester.setValue` used to accept values a real user could never type, such as text longer than `maxLength` or characters that `allowedCharPattern` blocks. They now refuse those values with an `IllegalArgumentException`, so a test cannot reach a state the browser would never produce. Fixes #248 --- CONVENTIONS.md | 8 +- guidelines/testers.md | 13 +- .../textfield/TextAreaTesterTest.java | 39 ++++- .../textfield/TextFieldTesterTest.java | 41 ++++- .../component/textfield/TextAreaTester.java | 27 +++- .../component/textfield/TextFieldTester.java | 29 +++- .../textfield/TextInputConstraints.java | 143 ++++++++++++++++++ 7 files changed, 271 insertions(+), 29 deletions(-) create mode 100644 shared/src/main/java/com/vaadin/flow/component/textfield/TextInputConstraints.java diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 2eadf3ff..58769be8 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -52,8 +52,12 @@ with `isValid()` rather than refusing the value at set time. Refuse a value only when the real control physically cannot produce it: a slider clamps to its range and snaps to its step, so `RangeInputTester` and -`NumberSliderTester` do reject out-of-range and off-step values. Structural -refusals stay too, such as `null` on a field whose empty value is not `null`. +`NumberSliderTester` do reject out-of-range and off-step values, and a text +input truncates at `maxLength` and filters the keystrokes `allowedCharPattern` +does not match, so `TextFieldTester` and `TextAreaTester` reject a value that +breaks either one. `minLength`, `pattern` and required are validation-only and +keep committing an invalid value. Structural refusals stay too, such as `null` +on a field whose empty value is not `null`. Read-only state counts towards usability. `isUsable()` is enabled + attached + effectively visible + not inert + not read-only, and effective visibility walks diff --git a/guidelines/testers.md b/guidelines/testers.md index f5865e1c..7e0faf9d 100644 --- a/guidelines/testers.md +++ b/guidelines/testers.md @@ -81,9 +81,20 @@ for it — not enforced at set time. The exception is a control that physically cannot produce the value: a slider clamps to its range and snaps to its step, so `RangeInputTester` and -`NumberSliderTester` do refuse out-of-range and off-step values. Structural +`NumberSliderTester` do refuse out-of-range and off-step values. A text input +truncates what is over `maxLength` and filters out the keystrokes +`allowedCharPattern` does not match, so `TextFieldTester` and `TextAreaTester` +refuse a value that breaks either one — while `minLength`, `pattern` and +required stay validation-only and keep committing an invalid value. Structural refusals stay too, such as `null` on a field whose empty value is not `null`. +Where the line falls is a question about the control, not about the constraint: +ask whether a user sitting in front of the component could hand the field that +value at all. When they could not, refuse it with an `IllegalArgumentException` +whose message says what the browser does instead, rather than silently +correcting the value — a test that asks for the impossible has a bug in it, and +truncating or clamping behind its back would hide it. + `isValid()` means "not marked invalid, and the current value passes the component's own default validator" — it delegates to `getDefaultValidator()` rather than re-checking required / `min` / `max` / `step` by hand. Re-checking diff --git a/junit6/src/test/java/com/vaadin/flow/component/textfield/TextAreaTesterTest.java b/junit6/src/test/java/com/vaadin/flow/component/textfield/TextAreaTesterTest.java index 43f007b2..3960ecf3 100644 --- a/junit6/src/test/java/com/vaadin/flow/component/textfield/TextAreaTesterTest.java +++ b/junit6/src/test/java/com/vaadin/flow/component/textfield/TextAreaTesterTest.java @@ -93,26 +93,33 @@ public void nonInteractableField_throwsOnSetValue() { @Test void textAreaWithValidation_doNotPreventInvalid_doNotThrow() { // Only accept numbers - view.textArea.setAllowedCharPattern("\\d*"); + view.textArea.setPattern("\\d*"); final TextAreaTester