Hi,
browserless-test provides a Clickable mixin (click(), middleClick(), rightClick(), …) that lets testers simulate a DOM click by firing the corresponding Vaadin ComponentEvent server-side. There doesn’t seem to be an equivalent for the blur DOM event. Any component whose production logic is wired to BlurNotifier/BlurEvent - a common pattern for “commit value only when the field loses focus” - can’t be exercised through the public tester API.
Affected version: com.vaadin:browserless-test-shared - checked 1.0.0 through 1.2.0-alpha2 (latest released), and the main branch on GitHub as of 2026-08. None expose blur simulation.
Environment: Vaadin 25.2.2 (Flow), Spring Boot 4.1.0, JUnit 5, browserless-test-shared / browserless-test-junit6 / browserless-test-spring.
Problem description
Several Vaadin field components (TextField, NumberField, custom subclasses) implement Focusable, which extends BlurNotifier. Production code frequently attaches business logic - validation, persistence (“save on blur”), or dialog triggers - to addBlurListener(…), deliberately keeping it separate from addValueChangeListener(…) (which may fire on every keystroke or on ON_CHANGE). This mirrors real browser behavior, where an fires change and then blur together when focus leaves the field after an edit.
In our application, a field’s save() method - the sole path to persisting its value and, in some cases, opening a follow-up confirmation dialog - is invoked exclusively from a BlurListener, not from the ValueChangeListener.
When testing this with browserless-test:
- ComponentTester/TextFieldTester/NumberFieldTester provide setValue(…), which fires a ComponentValueChangeEvent - but nothing fires a BlurEvent.
- Focusable.blur() (Vaadin Flow core, not part of the test library) only calls element.executeJs(“this.blur()”). Since there’s no real client attached in a browserless/server-side unit test, this JS call is a no-op and never produces a server-side BlurEvent. Verified by decompiling Focusable.blur() in flow-server - it unconditionally schedules a JS snippet and returns; there’s no synchronous or fallback server-side event dispatch.
- ComponentTester internally already has everything needed to fix this: a protected fireDomEvent(String eventType) method that fires an arbitrary DOM event server-side - exactly the mechanism Clickable.click() builds on for clicks (via ComponentUtil.fireEvent(component, new ClickEvent<>(…))). However, fireDomEvent is protected, so it isn’t usable from test code, and there’s no public wrapper for blur.
As a result, the only way to simulate a blur from test code today is ComponentUtil.fireEvent(field, new BlurNotifier.BlurEvent<>(field, true)) directly, bypassing the tester API entirely - undiscoverable, easy to get wrong (e.g. forgetting fromClient=true), and duplicated at every call site since no shared helper exists.
Expected behavior
A Blurable-style mixin (parallel to Clickable), implemented by ComponentTester (or at least by testers for Focusable components), exposing something like:
public interface Blurable<T extends Component & Focusable> {
T getComponent();
void ensureComponentIsUsable();
default void blur() {
ensureComponentIsUsable();
T component = getComponent();
ComponentUtil.fireEvent(component, new BlurNotifier.BlurEvent<>(component, true));
}
}
Actual behavior
No public API exists to simulate a blur event. Test authors must manually call ComponentUtil.fireEvent(…), working around the tester abstraction.
Reproduction
- Create a TextField with a BlurListener that performs an observable action (sets a flag, saves a value, etc.).
- In a browserless-test/SpringBrowserlessTest-based test, call test(textField).setValue(“x”).
- Assert the blur-triggered action happened - it doesn’t, because only a ComponentValueChangeEvent was fired, never a BlurEvent.
- Calling textField.blur() (Flow’s own Focusable.blur()) doesn’t help either - it emits a JS call with no client to execute it.
Current workaround
test(textField).setValue(“100”);
ComponentUtil.fireEvent(textField, new BlurNotifier.BlurEvent<>(textField, true));
Works, but undocumented, duplicated at every call site, and easy to get wrong.
Suggested fix
Add a Blurable mixin analogous to Clickable, and have ComponentTester (or component-specific testers for Focusable types) implement it - giving test code a discoverable test(field).blur() call that fires a proper server-side BlurEvent, matching how click() already works.