Skip to content

Add blur simulation and implicit focus tracking tests - #164

Open
mstahv wants to merge 7 commits into
mainfrom
bugfix/blur-issues
Open

Add blur simulation and implicit focus tracking tests#164
mstahv wants to merge 7 commits into
mainfrom
bugfix/blur-issues

Conversation

@mstahv

@mstahv mstahv commented Aug 27, 2026

Copy link
Copy Markdown
Member

Fixes #163 (missing blur simulation, reported in this forum thread (https://vaadin.com/forum/t/missing-blur-event-simulation-api-in-browserless-test/179736)): blur listeners often carry business logic, but there was no tester API to trigger them, and Focusable.focus()/blur() are no-ops without a browser.

The new FocusTracker keeps per-UI bookkeeping of the focused component and makes focus behave like with a real user — no manual blur calls needed:

  • Setting a value through a tester first focuses the field; interacting with anything else (another setValue, a click, ...) blurs the previously focused component before the new interaction is handled, in browser order.
  • Events fire as DOM events through ElementListenerMap (fromClient=true), the same route as a real round-trip.
  • Server-side Focusable.focus()/blur() calls (e.g. a click listener opening a dialog and focusing a field in it) are picked up from the pending JavaScript queue and applied at the end of each simulated interaction and on round-trips (review feedback from @knoobie).
  • ComponentTester gains explicit focus(), blur() and isFocused().

Known coupling: server-side call detection matches the JS generated by Focusable. If that changes in Flow, the serverSideFocus* tests in BlurSimulationTest fail immediately and a comment at the match site points to both ends.

BlurSimulationTest covers the scenarios above; full suite passes (1241 tests).

mstahv and others added 3 commits August 27, 2026 12:10
Reproduces https://vaadin.com/forum/t/missing-blur-event-simulation-api-in-browserless-test/179736

Business logic is often attached to blur listeners, but there is no
public tester API to trigger blur, and Focusable.blur() is a no-op
without a browser. Beyond an explicit API, focus and blur should
happen implicitly like with a real user: interacting with a component
through a tester should focus it and blur the previously focused one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FocusTracker keeps per-UI bookkeeping of the focused component and
fires focus/blur DOM events through ElementListenerMap, so listeners
see events as if they came from the client.

Focus moves implicitly like with a real user: setting a value through
a tester focuses the field, and interacting with any other component
(another setValue, a click, ...) blurs the previously focused one
before the new interaction is handled. Interacting again with the
already focused component is a no-op. ComponentTester additionally
exposes explicit focus() and blur() methods.

Fixes the case from
https://vaadin.com/forum/t/missing-blur-event-simulation-api-in-browserless-test/179736

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread junit6/src/test/java/com/vaadin/browserless/BlurSimulationTest.java
mstahv and others added 3 commits August 27, 2026 18:10
Focusable.focus()/blur() only schedule a client-side JavaScript call,
so they used to be no-ops in browserless tests. FocusTracker now
consumes the pending JavaScript queue like a browser would: it
materializes beforeClientResponse executions, picks up this.focus()/
this.blur() invocations and fires the corresponding focus/blur DOM
events back, updating the focus bookkeeping.

The queue is processed at the end of each simulated user interaction
(click, setValue, explicit focus/blur) and on server round-trips, so
the typical case of a click listener opening a dialog and focusing a
field in it works implicitly. ComponentTester also gets isFocused()
for asserting the currently focused component.

Addresses review feedback in
#164

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Calling focus() directly in test code was contrived; focusing the next
field from a value change listener is the realistic application logic
this simulates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread junit6/src/test/java/com/vaadin/browserless/BlurSimulationTest.java
Flow's Focusable.focus()/blur() set _nextFocusIsFromClient/
_nextBlurIsFromClient markers in the generated JavaScript, and the
FocusEvent/BlurEvent @eventdata constructors use them to override
fromClient. Mirror that: server-initiated focus/blur simulation now
includes the marker in the DOM event data, so isFromClient() returns
false, matching real browser behavior. The blur fired on the
previously focused component stays fromClient=true, as it is a plain
browser reaction.

Also adds a test proving that focus(FocusOption...), which generates
this.focus($0) instead of this.focus(), is detected as well.

Addresses review feedback from knoobie in #164.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🔎Iteration reviews

Development

Successfully merging this pull request may close these issues.

Blur and focus events should work

4 participants