Skip to content

feat(cypress-commands): add commands for dropdown menus #5756

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 27, 2024
Merged
71 changes: 70 additions & 1 deletion packages/cypress-commands/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,11 @@ declare global {
* __Note:__ The select popover must be visible, otherwise it can lead to unwanted side effects.
*
* @param text text of the ui5-option that should be clicked
* @example cy.get('[ui5-select]').clickUi5SelectOptionByText('Option2');
* @param options ClickOptions
*
* @deprecated: This command is deprecated. Please use `clickDropdownMenuItemByText` instead.
*
* @example cy.get('[ui5-select]').clickUi5SelectOptionByText('Option2');*
*/
clickUi5SelectOptionByText(text: string, options?: Partial<ClickOptions>): Chainable<Element>;

Expand All @@ -82,9 +85,39 @@ declare global {
*
* __Note:__ The select popover must be visible, otherwise it can lead to unwanted side effects.
*
* @deprecated: This command is deprecated. Please use `clickDropdownMenuItem` instead.
*
* @example cy.get('[ui5-option]').clickUi5SelectOption();
*/
clickUi5SelectOption(options?: Partial<ClickOptions>): Chainable<Element>;

/**
* Click on an option of "select-like" components by text. Currently supported components are `ui5-select`, `ui5-combobox` and `ui5-multi-combobox`.
*
* __Note:__ The popover must be visible, otherwise it can lead to unwanted side effects.
*
* @param text text of the item inside the popover that should be clicked
* @param options Cypress.ClickOptions
* @example cy.get('[ui5-select]').clickDropdownMenuItemByText('Option2');
*
*/
clickDropdownMenuItemByText(text: string, options?: Partial<ClickOptions>): Chainable<Element>;

/**
* Click on a chained option of "select-like" components. Currently supported components are `ui5-option`, `ui5-mcb-item` and `ui5-cb-item` (since v1.24.3 of `@ui5/webcomponents`).
*
* __Note:__ The popover must be visible, otherwise it can lead to unwanted side effects.
*
* @example cy.get('[ui5-option]').clickDropdownMenuItem();
*/
clickDropdownMenuItem(options?: Partial<ClickOptions>): Chainable<Element>;

/**
* Click on the open button in "select-like" components to open the popover. Currently supported components are `ui5-select`, `ui5-combobox` and `ui5-multi-combobox`.
*
* @example cy.get('[ui5-select]').openDropDownByClick();
*/
openDropDownByClick(options?: Partial<ClickOptions>): Chainable<Element>;
}
}
}
Expand Down Expand Up @@ -155,4 +188,40 @@ Cypress.Commands.add('clickUi5SelectOption', { prevSubject: 'element' }, (subjec
});
});

Cypress.Commands.add('clickDropdownMenuItemByText', { prevSubject: 'element' }, (subject, text, options = {}) => {
cy.wrap(subject).then(async ($dropdown) => {
// @ts-expect-error: ui5-webcomponent types are not bundled in
const staticArea = await $dropdown.get(0).getStaticAreaItemDomRef();
cy.wrap(staticArea).find('[ui5-responsive-popover][open]').should('be.visible');
// necessary as otherwise focusing the ui5-li is flaky
cy.wait(300);
cy.wrap(staticArea)
.contains(text)
.then(async ($li) => {
await $li.get(0).focus();
cy.wrap($li)
.find('li')
.click({ force: true, ...options });
});
});
});

Cypress.Commands.add('clickDropdownMenuItem', { prevSubject: 'element' }, (subject, options = {}) => {
cy.wrap(subject).then(($option) => {
// @ts-expect-error: ui5-webcomponent types are not bundled in
const domRef = $option.get(0).getDomRef();
cy.wrap(domRef)
.find('li')
.click({ force: true, ...options });
});
});

Cypress.Commands.add('openDropDownByClick', { prevSubject: 'element' }, (subject, options = {}) => {
if (subject.get(0).hasAttribute('ui5-multi-combobox')) {
// mcb needs a lot of calculation time to make the popover available
cy.wait(500);
}
cy.wrap(subject).shadow().find('[input-icon]').click(options);
});

export {};
86 changes: 86 additions & 0 deletions packages/cypress-commands/test/UI5WebComponentsChild.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,90 @@ describe('UI5 Web Components - Child Commands', () => {
// the web component doesn't fire the event if the popover is not opened
cy.get('@select').should('have.been.calledTwice');
});

it('clickDropdownMenuItemByText', () => {
const selectItemText =
'This very long item should be selected by first focusing it and then pressing it. A focus is applied first, because otherwise it wouldnt be visible. Strangely, longer items tend to result in occasional test failures compared to smaller ones, which is why this item has this text.';
const changeSpy = cy.spy().as('change');
let callCounter = 1;
const components = [
<ComboBox key="ui5-combobox" onSelectionChange={changeSpy}>
{...new Array(30).fill(<ComboBoxItem text="Item" />)}
<ComboBoxItem text={selectItemText} />
</ComboBox>,
<MultiComboBox key="ui5-multi-combobox" onSelectionChange={changeSpy}>
{...new Array(30).fill(<MultiComboBoxItem text="Item" />)}
<MultiComboBoxItem text={selectItemText} />
</MultiComboBox>,
<Select key="ui5-select" onChange={changeSpy}>
{...new Array(30).fill(<Option>Item</Option>)}
<Option>{selectItemText}</Option>
</Select>
];

components.forEach((component) => {
cy.mount(component);
cy.get(`[${component.key}]`).openDropDownByClick();
cy.get(`[${component.key}]`).clickDropdownMenuItemByText(selectItemText);

switch (component.key) {
case 'ui5-combobox':
cy.get(`[${component.key}]`).should('have.value', selectItemText);
break;
case 'ui5-multi-combobox':
cy.get(`[${component.key}]`).find('[ui5-token]').contains(selectItemText);
break;
case 'ui5-select':
cy.get(`[${component.key}]`).should('have.value', selectItemText);
break;
}

cy.get('@change').should('have.callCount', callCounter);
callCounter++;
cy.wait(200);
});
});

it('clickDropDownMenuItem', () => {
const selectItemText = 'Select me';
const changeSpy = cy.spy().as('change');
let callCounter = 1;
const components = [
<ComboBox key="ui5-combobox" onSelectionChange={changeSpy}>
{...new Array(5).fill(<ComboBoxItem text="Item" />)}
<ComboBoxItem text={selectItemText} data-testid="selectItem" />
</ComboBox>,
<MultiComboBox key="ui5-multi-combobox" onSelectionChange={changeSpy}>
{...new Array(5).fill(<MultiComboBoxItem text="Item" />)}
<MultiComboBoxItem text={selectItemText} data-testid="selectItem" />
</MultiComboBox>,
<Select key="ui5-select" onChange={changeSpy}>
{...new Array(5).fill(<Option>Item</Option>)}
<Option data-testid="selectItem">{selectItemText}</Option>
</Select>
];

components.forEach((component) => {
cy.mount(component);
cy.get(`[${component.key}]`).openDropDownByClick();
cy.get('[ui5-responsive-popover][open]').should('be.visible');
cy.get(`[data-testid="selectItem"]`).clickDropdownMenuItem();

switch (component.key) {
case 'ui5-combobox':
cy.get(`[${component.key}]`).should('have.value', selectItemText);
break;
case 'ui5-multi-combobox':
cy.get(`[${component.key}]`).find('[ui5-token]').contains(selectItemText);
break;
case 'ui5-select':
cy.get(`[${component.key}]`).should('have.value', selectItemText);
break;
}

cy.get('@change').should('have.callCount', callCounter);
callCounter++;
cy.wait(200);
});
});
});
Loading