Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -470,4 +470,67 @@ void openAndFind_ContextMenuItemsCanBeAccessed() {
Assertions.assertFalse(div.isAttached());
}

@Test
void getItemTexts_hiddenItemIgnored_componentItemHasNoText() {
ContextMenuTester<ContextMenu> menu_ = test(view.menu);
menu_.open();

Assertions.assertIterableEquals(
List.of("Foo", "Bar", "Text", "Duplicated", "Duplicated", "",
"Checkable", "Disabled", "Hierarchical",
"Duplicated Hidden"),
menu_.getItemTexts(),
"texts should be the visible items, in the order the browser shows them");
}

@Test
void getItemTexts_positionsMatchClickItemPositions() {
ContextMenuTester<ContextMenu> menu_ = test(view.menu);
menu_.open();

// Hidden is filtered out of both, so Hierarchical is at position 8
// although it is the tenth item that was added
menu_.clickItem(menu_.getItemTexts().indexOf("Hierarchical"), 0);

Assertions.assertIterableEquals(List.of("Hierarchical / Level2"),
view.clickedItems);
}

@Test
void getItemTexts_subMenuByPath_hiddenItemIgnored() {
ContextMenuTester<ContextMenu> menu_ = test(view.menu);
menu_.open();

Assertions.assertIterableEquals(
List.of("Level2", "NestedSubMenu", "Nested Checkable",
"NestedDisabled"),
menu_.getItemTexts("Hierarchical"),
"NestedInvisible should not be reported");
Assertions.assertIterableEquals(List.of("Level3"),
menu_.getItemTexts("Hierarchical", "NestedSubMenu"));
}

@Test
void getItemTexts_itemWithoutSubMenu_throws() {
ContextMenuTester<ContextMenu> menu_ = test(view.menu);
menu_.open();

IllegalArgumentException exception = Assertions.assertThrows(
IllegalArgumentException.class,
() -> menu_.getItemTexts("Foo"));
Assertions.assertTrue(
exception.getMessage().contains("has no children"),
"expected the missing sub menu to be reported, but got: "
+ exception.getMessage());
}

@Test
void getItemTexts_menuNotOpened_throws() {
ContextMenuTester<ContextMenu> menu_ = test(view.menu);

Assertions.assertThrows(IllegalStateException.class,
menu_::getItemTexts);
Assertions.assertThrows(IllegalStateException.class,
() -> menu_.getItemTexts("Hierarchical"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,50 @@ void contextMenuFromGridTester_gridWithSeveralContextMenus_throws() {
"expected the ambiguity to be reported, but got: "
+ exception.getMessage());
}

@Test
void getItemTexts_openMenu_hiddenItemIgnored_subMenuByPath() {
GridContextMenuTester<GridContextMenu<String>, String> menu_ = test(
view.menu);
menu_.open(0);

Assertions.assertIterableEquals(
List.of("Edit", "", "Checkable", "Disabled", "Share"),
menu_.getItemTexts(),
"texts should be the visible items, with no text for the checkbox item");

Assertions.assertIterableEquals(List.of("Copy link", "Email"),
menu_.getItemTexts("Share"));
}

@Test
void getItemTexts_menuNotOpened_throws() {
GridContextMenuTester<GridContextMenu<String>, String> menu_ = test(
view.menu);

Assertions.assertThrows(IllegalStateException.class,
menu_::getItemTexts);
Assertions.assertThrows(IllegalStateException.class,
() -> menu_.getItemTexts("Share"));
}

@Test
void getItemTexts_dynamicContentHandler_reportsItemsForTheOpenedRow() {
GridMenuItem<String> edit = view.menu.getItems().get(0);
view.menu.setDynamicContentHandler(item -> {
edit.setVisible(GridContextMenuView.BOB.equals(item));
return true;
});
GridContextMenuTester<GridContextMenu<String>, String> menu_ = test(
view.menu);

menu_.open(1);
Assertions.assertTrue(menu_.getItemTexts().contains("Edit"),
"Edit should be reported for the row the handler shows it for");

menu_.close();
menu_.open(0);
Assertions.assertFalse(menu_.getItemTexts().contains("Edit"),
"Edit should not be reported for a row the handler hides it for");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -306,4 +306,29 @@ void getItemTooltipText_notExisting_throws() {
() -> menu_.getItemTooltipText(22));
}

@Test
void getItemTexts_hiddenItemIgnored_subMenuByPath() {
Assertions.assertIterableEquals(
List.of("Foo", "Bar", "Text", "Duplicated", "Duplicated",
"Checkables", "Disabled", "Hierarchical"),
menu_.getItemTexts(),
"texts should be the visible items, in the order the browser shows them");

Assertions.assertIterableEquals(
List.of("Level2", "NestedSubMenu", "Nested Checkable",
"NestedDisabled"),
menu_.getItemTexts("Hierarchical"),
"NestedInvisible should not be reported");
}

@Test
void getItemTexts_disabledMenuBar_itemsAreStillReported() {
view.menu.setEnabled(false);

Assertions.assertIterableEquals(
List.of("Foo", "Bar", "Text", "Duplicated", "Duplicated",
"Checkables", "Disabled", "Hierarchical"),
menu_.getItemTexts(),
"a user can read the items of a disabled menu bar");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,55 @@ public static <C extends ContextMenuBase<C, I, S>, I extends MenuItemBase<C, I,
return menuItem;
}

/**
* Gets the texts of the given menu items as the browser shows them, i.e.
* only the visible ones, in their order.
* <p>
* An item created from a component has no text of its own, so it is
* reported as an empty string.
*
* @param items
* the items of a menu level
* @return the texts of the visible items
*/
public static List<String> visibleTexts(
List<? extends MenuItemBase<?, ?, ?>> items) {
return items.stream().filter(Component::isVisible)
.map(MenuItemBase::getText).collect(Collectors.toList());
}

/**
* Gets the texts of the visible items of the sub menu of the item addressed
* by the given text path.
*
* @param rootItems
* the items of the top level menu
* @param topLevelText
* the text content of the top level menu item, not
* {@literal null}
* @param nestedItemsText
* text content of the nested menu items
* @param <C>
* menu type
* @param <I>
* menu item type
* @param <S>
* sub menu type
* @return the texts of the visible items of the sub menu
* @throws IllegalArgumentException
* if the provided text does not identify a menu item, or if the
* item at the given path has no sub menu
* @throws IllegalStateException
* if there are multiple matching items at any level, or if the
* item at the given path is disabled or not visible
*/
public static <C extends ContextMenuBase<C, I, S>, I extends MenuItemBase<C, I, S>, S extends SubMenuBase<C, I, S>> List<String> visibleSubMenuTexts(
List<I> rootItems, String topLevelText, String... nestedItemsText) {
I menuItem = findByPath(rootItems, topLevelText, nestedItemsText);
ensureParentItem(menuItem, pathToString(topLevelText, nestedItemsText));
return visibleTexts(menuItem.getSubMenu().getItems());
}

/**
* Ensures that the given menu item is checkable, as only a checkable item
* has a checked state to read.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package com.vaadin.flow.component.contextmenu;

import java.util.List;

import com.vaadin.browserless.ComponentQuery;
import com.vaadin.browserless.ComponentTester;
import com.vaadin.browserless.Tests;
Expand Down Expand Up @@ -383,6 +385,94 @@ public String getItemTooltipText(int topLevelPosition,
return menuItem.getElement().getProperty("tooltip");
}

/**
* Gets the texts of the menu items, as the browser shows them.
* <p>
* Hidden items are ignored, so the returned texts are aligned with the
* positions used by {@link #clickItem(int, int...)}. A text can also be
* given to {@link #clickItem(String, String...)}, as long as it identifies
* a single enabled item: a text that several visible items share is
* ambiguous, and a disabled item cannot be clicked.
* <p>
* An item created from a component has no text of its own, and is reported
* as an empty string. Use {@link #find(Class)} to reach such an item.
* <p>
* The menu has to be open, since its items are not part of the UI before
* that.
*
* <pre>
* {@code
*
* menu.addItem("Preview", event -> {
* });
* menu.addItem("Hidden", event -> {
* }).setVisible(false);
* menu.addItem("Share");
*
* // ["Preview", "Share"]
* tester.getItemTexts();
* }
* </pre>
*
* @return the texts of the visible top level menu items, in the order they
* are shown in
* @throws IllegalStateException
* if the menu is not open, or is not visible
*/
public List<String> getItemTexts() {
ensureVisible();
return MenuItemNavigation.visibleTexts(getComponent().getItems());
}

/**
* Gets the texts of the items of the sub menu of the item matching the
* given text, as the browser shows them.
* <p>
* For a nested sub menu, provide the text of each menu item in the
* hierarchy, the same way as in {@link #clickItem(String, String...)}.
* <p>
* Hidden items are ignored at every level, both when following the path and
* in the returned texts.
* <p>
* The menu has to be open, since its items are not part of the UI before
* that.
*
* <pre>
* {@code
*
* var subMenu = menu.addItem("Share").getSubMenu();
* subMenu.addItem("Copy link", event -> {
* });
* subMenu.addItem("Email", event -> {
* });
*
* // ["Copy link", "Email"]
* tester.getItemTexts("Share");
* }
* </pre>
*
* @param topLevelText
* the text content of the top level menu item, not
* {@literal null}.
* @param nestedItemsText
* text content of the nested menu items
* @return the texts of the visible items of the sub menu, in the order they
* are shown in
* @throws IllegalArgumentException
* if the provided text does not identify a menu item, or if the
* item at the given path has no sub menu.
* @throws IllegalStateException
* if the menu is not open or not visible, if there are multiple
* matching items at any level, or if the item at the given path
* is disabled or not visible.
*/
public List<String> getItemTexts(String topLevelText,
String... nestedItemsText) {
ensureVisible();
return MenuItemNavigation.visibleSubMenuTexts(getComponent().getItems(),
topLevelText, nestedItemsText);
}

/**
* {@inheritDoc}
* <p>
Expand Down
Loading
Loading