diff --git a/playwright/fixtures/ui5-fixtures.ts b/playwright/fixtures/ui5-fixtures.ts index 8b41f11700e..e987ccf05fc 100644 --- a/playwright/fixtures/ui5-fixtures.ts +++ b/playwright/fixtures/ui5-fixtures.ts @@ -19,6 +19,19 @@ function escapeAttributeValue(value: string): string { export class UI5WCHelpers { constructor(protected page: Page) {} + /** + * Clicks a UI5 custom element via its shadow-inner root node + * (`getDomRef()`), with a fallback to the host element. + * + * @param locator Locator of the UI5 custom element to click. + */ + private async clickViaDomRef(locator: Locator): Promise { + await locator.evaluate((element: HTMLElement) => { + const host = element as HTMLElement & { getDomRef?: () => HTMLElement | null }; + (host.getDomRef?.() ?? host).click(); + }); + } + /** * Types a value into a UI5 input component. * Works with Input, ComboBox, MultiComboBox, MultiInput, DatePicker, etc. @@ -146,6 +159,37 @@ export class UI5WCHelpers { findTabPopoverButtonByText(tabContainer: Locator, text: string): Locator { return tabContainer.locator('[role="tab"]').filter({ hasText: text }).locator('[ui5-button]'); } + + /** + * Selects a menu item inside an open Menu by its rendered text. + * Must be called after the menu is open. + * + * @example + * const menu = page.getByTestId('my-menu'); + * await ui5wc.selectMenuItemByText(menu, 'Save'); + */ + async selectMenuItemByText(menu: Locator, text: string): Promise { + await expect(menu).toHaveAttribute('open', ''); + const menuItem = menu.locator(`[ui5-menu-item][text="${escapeAttributeValue(text)}"]`).first(); + await this.clickViaDomRef(menuItem); + } + + /** + * Expands a UI5 TreeItem by clicking its toggle icon. + * + * @example + * await ui5wc.expandTreeItem(page.getByTestId('my-tree-item')); + */ + async expandTreeItem(treeItem: Locator): Promise { + await expect(treeItem).toBeVisible(); + await treeItem.evaluate((element: HTMLElement) => { + const icon = element.shadowRoot?.querySelector('[part="toggle-icon"]') as HTMLElement | null; + if (!icon) { + throw new Error('Toggle icon not found in shadow root of tree item'); + } + icon.click(); + }); + } } export const test = base.extend({ diff --git a/playwright/test/UI5Fixtures.gallery.tsx b/playwright/test/UI5Fixtures.gallery.tsx index aaee8aba120..77baa684c1e 100644 --- a/playwright/test/UI5Fixtures.gallery.tsx +++ b/playwright/test/UI5Fixtures.gallery.tsx @@ -3,6 +3,8 @@ import { ComboBox } from '@ui5/webcomponents-react/ComboBox'; import { ComboBoxItem } from '@ui5/webcomponents-react/ComboBoxItem'; import { Dialog } from '@ui5/webcomponents-react/Dialog'; import { Input } from '@ui5/webcomponents-react/Input'; +import { Menu } from '@ui5/webcomponents-react/Menu'; +import { MenuItem } from '@ui5/webcomponents-react/MenuItem'; import { MultiComboBox } from '@ui5/webcomponents-react/MultiComboBox'; import { MultiComboBoxItem } from '@ui5/webcomponents-react/MultiComboBoxItem'; import { MultiInput } from '@ui5/webcomponents-react/MultiInput'; @@ -12,6 +14,8 @@ import { SuggestionItem } from '@ui5/webcomponents-react/SuggestionItem'; import { Tab } from '@ui5/webcomponents-react/Tab'; import { TabContainer } from '@ui5/webcomponents-react/TabContainer'; import { TextArea } from '@ui5/webcomponents-react/TextArea'; +import { Tree } from '@ui5/webcomponents-react/Tree'; +import { TreeItem } from '@ui5/webcomponents-react/TreeItem'; import { useState } from 'react'; export const InputTestComp = () => { @@ -160,3 +164,37 @@ export const MultiInputWithSuggestionsTestComp = () => { ); }; + +export const MenuTestComp = () => { + const [open, setOpen] = useState(false); + const [clickedItem, setClickedItem] = useState(''); + return ( + <> + + setOpen(false)} + onItemClick={(e) => setClickedItem(e.detail.item.getAttribute('text') || '')} + > + + + + + {clickedItem} + + ); +}; + +export const TreeTestComp = () => { + return ( + + + + + + ); +}; diff --git a/playwright/test/ui5-fixtures.spec.tsx b/playwright/test/ui5-fixtures.spec.tsx index 9ef54debed7..1d0315dff7a 100644 --- a/playwright/test/ui5-fixtures.spec.tsx +++ b/playwright/test/ui5-fixtures.spec.tsx @@ -101,6 +101,15 @@ test.describe('UI5 Web Components Fixtures', () => { await expect(page.getByTestId('multicombobox-values')).toHaveText('Green'); }); + test('selectMenuItemByText - selects Menu item', async ({ mount, page, ui5wc }) => { + await mount('test/UI5Fixtures/MenuTestComp'); + + await page.getByTestId('menu-opener').click(); + await ui5wc.selectMenuItemByText(page.getByTestId('test-menu'), 'Delete'); + + await expect(page.getByTestId('menu-clicked-item')).toHaveText('Delete'); + }); + test('findTabByText - finds and clicks tab', async ({ mount, page, ui5wc }) => { await mount('test/UI5Fixtures/TabContainerTestComp'); @@ -161,4 +170,16 @@ test.describe('UI5 Web Components Fixtures', () => { await expect(multiInput.locator('[text="Suggestion X"]')).toBeVisible(); }); + + test('expandTreeItem - expands a tree item, revealing its child', async ({ mount, page, ui5wc }) => { + await mount('test/UI5Fixtures/TreeTestComp'); + + const parent = page.getByTestId('test-tree-parent'); + const child = page.getByTestId('test-tree-child'); + await expect(child).toBeHidden(); + + await ui5wc.expandTreeItem(parent); + + await expect(child).toBeVisible(); + }); });