Add blur simulation and implicit focus tracking tests - #164
Open
mstahv wants to merge 7 commits into
Open
Conversation
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>
knoobie
reviewed
Aug 27, 2026
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>
knoobie
reviewed
Aug 27, 2026
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
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).