Skip to content

fix: make ContextMenuTester open and close the menu like a real user - #249

Merged
mcollovati merged 7 commits into
mainfrom
fix/context-menu-tester-open-as-user
Sep 18, 2026
Merged

mcollovati merged 7 commits into
mainfrom
fix/context-menu-tester-open-as-user

Conversation

@totally-not-ai

@totally-not-ai totally-not-ai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

ContextMenuTester.open() and close() changed the menu's opened property from the server, so the resulting OpenedChangeEvent said it did not come from the client. The tester now pushes that property through the client path, and a refused open no longer leaves the menu half-opened.

Fixes #233

What changed

Behavior change (affects tests that open or close a ContextMenu or GridContextMenu through the testers):

  • OpenedChangeEvent from ContextMenuTester.open() and close() now reports isFromClient() == true. Application code that branches on isFromClient() now takes the "user action" path, as it does in a real browser. Tests asserting isFromClient() == false for tester-driven opens will need updating.
  • An open() that is refused because the menu is not usable (for example hidden) now leaves the menu closed and detached from the UI. Before, the menu was left with its content attached, so a top level find(...) could still reach items of a closed menu.
  • GridContextMenuTester.open(int, String) now checks usability before it marks the menu as opened. A refused open no longer fires a GridContextMenuOpenedEvent, and it fails with the tester's own "is not usable" message instead of the generic "unable to simulate a client side update".

Internal cleanup: the shared "undo the attach when the usability check refuses the open" logic now lives once in ComponentTester#ensureComponentIsUsableOrDetach(), used by both menu testers. This is additive for subclasses.

Also fixes a stale test expectation: getItemTexts() now expects the visible "Duplicated Hidden" item that was appended to the test view, so the list is 10 items instead of 9.

API Changes

com.vaadin.browserless.ComponentTester

// Added
protected void ensureComponentIsUsableOrDetach() // like ensureComponentIsUsable(), but detaches the component from the UI before rethrowing

Test summary

  • Opening and closing a context menu through the tester reports both opened-change events as user actions.
  • Opening a context menu that is not usable fails, and leaves the menu closed, detached, and its content unreachable through a top level find(...).
  • The failure message for a refused open is the tester's "is not usable" message, not a generic client-update error.
  • Opening a grid context menu on a row when the menu is not usable fails the same way and leaves no open menu behind.
  • The list of visible item texts includes every item the user can see, in browser order.

totally-not-ai Bot and others added 3 commits September 18, 2026 14:48
ContextMenuTester.open() and close() wrote the menu's opened element
property with a plain server-side setProperty, so the resulting
OpenedChangeEvent reported isFromClient() as false. An application that
branches on isFromClient() saw the tester's open as a programmatic one.

The opened property is @Synchronize-d on ContextMenuBase#isOpened(), so
the update now goes through setPropertyAsUser, the same helper Details
and Accordion use for that property.

The usability check in open() moves ahead of the property update: a
client originated update is refused on a component that is not usable,
so running the check first keeps the tester's own error message and
leaves the menu closed when it throws.
The usability check in ContextMenuTester.open() now runs before the
property update, so a menu that is not usable stays closed when open()
throws instead of being left half-opened.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
open() fires the before-open DOM event, which attaches the menu content
to the UI, before it can check usability: the menu is not attached until
that event, so ensureComponentIsUsable() cannot run any earlier. A menu
that turned out not to be usable was therefore left attached with
opened=false, so a closed menu stayed reachable through a top level
find(). The attach is now undone when the check refuses the open, making
open() leave no trace when it throws.

The test also pins the message of the refusal, so that moving the
usability check back after the property update is caught: that would
throw the generic "unable to simulate a client side update" instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

1 536 tests   1 536 ✅  44s ⏱️
  152 suites      0 💤
  152 files        0 ❌

Results for commit af86286.

♻️ This comment has been updated with latest results.

totally-not-ai Bot and others added 4 commits September 18, 2026 15:05
GridContextMenuTester.open(int, String) pushed opened=true through the
client path before checking that the menu is usable. A menu that is not
usable was therefore left opened and attached, and a
GridContextMenuOpenedEvent was fired for an open that then threw.

The usability check now runs before the property update, and the attach
done by the before-open event is undone when the check refuses the open,
so a refused open leaves no trace. This mirrors ContextMenuTester.open().

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ters

ContextMenuTester.open() and GridContextMenuTester.open(int, String) had
the same try/catch undoing the attach done by the before-open event when
the usability check refuses the open. The invariant now lives once, as
ComponentTester#ensureComponentIsUsableOrDetach(), next to the check it
wraps, so the two menu testers cannot drift apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The getItemTexts expectation was written against the item list as it was
before a visible "Duplicated Hidden" item was appended to
ContextMenuView, so it asserted 9 items where the menu now shows 10.
Both changes pass on their own, which is why neither pull request caught
it; together the expectation is stale.

The tester is right: the appended item is visible, so it belongs in the
texts a user sees.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mcollovati
mcollovati merged commit 0f69e12 into main Sep 18, 2026
6 checks passed
@mcollovati
mcollovati deleted the fix/context-menu-tester-open-as-user branch September 18, 2026 15:34
mcollovati pushed a commit that referenced this pull request Sep 18, 2026
…249) (CP: 25.3) (#254)

This PR cherry-picks changes from the original PR #249 to branch 25.3.
---
#### Original PR description
> ## Summary
> `ContextMenuTester.open()` and `close()` changed the menu's `opened`
property from the server, so the resulting `OpenedChangeEvent` said it
did not come from the client. The tester now pushes that property
through the client path, and a refused open no longer leaves the menu
half-opened.
> 
> Fixes #233 
> 
> ## What changed
> 
> **Behavior change (affects tests that open or close a `ContextMenu` or
`GridContextMenu` through the testers):**
> 
> - `OpenedChangeEvent` from `ContextMenuTester.open()` and `close()`
now reports `isFromClient() == true`. Application code that branches on
`isFromClient()` now takes the "user action" path, as it does in a real
browser. Tests asserting `isFromClient() == false` for tester-driven
opens will need updating.
> - An `open()` that is refused because the menu is not usable (for
example hidden) now leaves the menu closed and detached from the UI.
Before, the menu was left with its content attached, so a top level
`find(...)` could still reach items of a closed menu.
> - `GridContextMenuTester.open(int, String)` now checks usability
*before* it marks the menu as opened. A refused open no longer fires a
`GridContextMenuOpenedEvent`, and it fails with the tester's own "is not
usable" message instead of the generic "unable to simulate a client side
update".
> 
> Internal cleanup: the shared "undo the attach when the usability check
refuses the open" logic now lives once in
`ComponentTester#ensureComponentIsUsableOrDetach()`, used by both menu
testers. This is additive for subclasses.
> 
> Also fixes a stale test expectation: `getItemTexts()` now expects the
visible "Duplicated Hidden" item that was appended to the test view, so
the list is 10 items instead of 9.
> 
> ## API Changes
> 
> ### com.vaadin.browserless.ComponentTester
> 
> ```java
> // Added
> protected void ensureComponentIsUsableOrDetach() // like
ensureComponentIsUsable(), but detaches the component from the UI before
rethrowing
> ```
> 
> ## Test summary
> - Opening and closing a context menu through the tester reports both
opened-change events as user actions.
> - Opening a context menu that is not usable fails, and leaves the menu
closed, detached, and its content unreachable through a top level
`find(...)`.
> - The failure message for a refused open is the tester's "is not
usable" message, not a generic client-update error.
> - Opening a grid context menu on a row when the menu is not usable
fails the same way and leaves no open menu behind.
> - The list of visible item texts includes every item the user can see,
in browser order.

Co-authored-by: totally-not-ai[bot] <290682512+totally-not-ai[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ContextMenuTester opens and closes the menu server-side, so OpenedChangeEvent reports isFromClient() as false

2 participants