feat: add stepUp/stepDown to NumberFieldTester - #191
Conversation
NumberField and IntegerField render +/- step buttons when setStepButtonsVisible(true) is enabled, and there was no tester method for clicking them. stepUp()/stepDown() (and their stepUp(times)/stepDown(times) variants) simulate those clicks: they require the step buttons to be visible, apply the step aligned with the step scale measured from min, and refuse to move outside the min-max range, the same way the web component disables the button. The value is set as coming from the client. Fixes #179
Pin the step alignment for values below the step basis, where both step directions move towards the basis, the same as the web component's remainder-based _getIncrement. Also cover stepping an empty field down and clicking a step button zero times. Share the alignment margin between stepAligned and the empty-field boundary case, and document why the element property is used to detect an unset min.
| for (int i = 1; i < times; i++) { | ||
| value = stepOnce(value, up); | ||
| } | ||
| setValueAsUser(toValue(value)); |
There was a problem hiding this comment.
setting the value only for the last value seems incorrect when times > 1. A real application would fire on a value change event for every step click
There was a problem hiding this comment.
You are right, changed it. step now sets the value once per click instead of computing the end value first, so stepUp(3) fires three value change events, and if a click cannot be performed the value keeps the steps applied before it — same as in the browser. integerField_stepMultipleTimes_oneEventPerClick asserts the full sequence of values seen by the listener.
There was a problem hiding this comment.
There should be a test that verifies that stepUp(N) throws an exception if the number of steps makes the value exceed min or max
There was a problem hiding this comment.
Covered now by stepMultipleTimes_aClickWouldExceedBoundaries_throws_earlierClicksAreKept, in both directions: with min=0, max=10, step=3, stepUp(3) from 4 throws on the third click, and stepDown(3) from 5 throws on the third click.
It also pins what the value is left at, which is worth stating explicitly since the implementation changed after your earlier comment: each click now sets the value on its own, so a failing click leaves the earlier ones applied — 4 → 6 → 9 → throw, and 5 → 3 → 0 → throw — and only the performed clicks fire a value change event.
Clicking a step button commits the value in the browser, so stepUp and stepDown now set the value once per click instead of once per call, and clicking zero times is rejected like a negative count. isValid() exposes the constraints the field applies to its current value, including the step scale, which setValue deliberately does not check: a value off the step scale can be committed from the browser too.
…d-step-buttons # Conflicts: # junit6/src/test/java/com/vaadin/flow/component/textfield/NumberFieldTesterTest.java # shared/src/main/java/com/vaadin/flow/component/textfield/NumberFieldTester.java
A stepUp/stepDown call that hits the range limit part way through keeps the clicks it already performed, which was only promised in the Javadoc. isValid() now also reports a field that is marked invalid from the outside, as a Binder does, and its test covers a value set on the server outside the min-max range.
|
@mcollovati all five points are addressed in the latest commits. The branch also carries a merge of API added on top of |
The multi-click boundary test only exercised the max side; it now steps down past min as well, asserting that the failing click throws and the clicks before it are kept.
Summary
NumberFieldandIntegerFieldcan show +/- step buttons, but the tester had no way to click them. This addsstepUp()/stepDown()(with optional click counts) that simulate those clicks, plus anisValid()helper to check the field's current value against its constraints.What changed
This change is purely additive: no existing method changes behaviour, so current tests keep working.
stepUp()/stepDown()andstepUp(int times)/stepDown(int times)onNumberFieldTester:setStepButtonsVisible(true)) and the field to be usable, otherwise they throwIllegalStateException.stepscale, measured fromminwhenminis set and from zero otherwise. This mirrors the web component: stepping from a value that is off the scale moves to the next aligned value instead of adding a full step.min–maxrange throwsIllegalStateException, because the browser disables the button in that case. In a multi-click call, the clicks performed before the failing one are kept.timesmust be positive;0or a negative number throwsIllegalArgumentException.isValid()onNumberFieldTesterreports whether the field is currently valid: required,min,maxand — whenstepis explicitly set — the step scale, and it also returnsfalsewhen the field was marked invalid from the outside (for example by aBinder).setValuestill does not check the step scale, since a value off the scale can also be committed from the browser; useisValid()to assert on that.Fixes #179
Use case
A developer builds an order form with a quantity
IntegerFieldthat uses step buttons, with a minimum order of 2 and a step of 2. They want a unit test that the user can raise the quantity with the + button and that the order total follows.API Changes
com.vaadin.flow.component.textfield.NumberFieldTester
Test summary
IllegalStateExceptionIllegalStateExceptionminwhen set and from zero otherwise, also for values below the basismin–maxrange throws and leaves the value unchangedtimesof0or a negative number throwsIllegalArgumentExceptiondoublearithmetic would accumulate rounding errorsisValid()is false for a value off the step scale, a value abovemaxset on the server, an empty required field, and a field marked invalid from the outsidesetValuedoes not check the step scalestepfalls back to a step of 1, as the browser doesNumberFieldTesterTest.stepButtonsNotVisible_step_throws→ 1NumberFieldTesterTest.nonUsableField_step_throws→ 2NumberFieldTesterTest.unalignedValue_step_movesToTheClosestValueAlignedWithStep→ 3NumberFieldTesterTest.negativeValue_step_movesTowardsTheStepBasis→ 3NumberFieldTesterTest.stepWouldExceedBoundaries_throws_valueIsNotChanged→ 4NumberFieldTesterTest.stepMultipleTimes_aClickWouldExceedBoundaries_throws_earlierClicksAreKept→ 4, 5, 6NumberFieldTesterTest.integerField_stepMultipleTimes_oneEventPerClick→ 6NumberFieldTesterTest.emptyNumberField_step_oneStepIsSet_clientSideEventIsFired→ 6, 7NumberFieldTesterTest.emptyField_step_startsFromZeroOrTheClosestBoundary→ 7NumberFieldTesterTest.nonPositiveTimes_step_throws→ 8NumberFieldTesterTest.decimalStep_step_doesNotLosePrecision→ 9NumberFieldTesterTest.isValid_reportsConstraintViolationsAndExternalInvalidState→ 10Deliberately untested: the conversion of a stepped value back to
IntegerforIntegerField, which is covered indirectly by the integer stepping test, and the exact wording of the exception messages.