From 6c88db46da8caa98e4bae7f7105fc56ccd9c63de Mon Sep 17 00:00:00 2001 From: Nikolay Deshev Date: Wed, 10 Sep 2025 15:57:10 +0300 Subject: [PATCH 1/3] test(ui5-combobox): migrate mobile tests to cypress migrate the mobile WebDriver.IO tests to cypress --- .../main/cypress/specs/ComboBox.mobile.cy.tsx | 530 +++++++++++++++++- 1 file changed, 513 insertions(+), 17 deletions(-) diff --git a/packages/main/cypress/specs/ComboBox.mobile.cy.tsx b/packages/main/cypress/specs/ComboBox.mobile.cy.tsx index 3d4a2d7645b1..b6416aacbe79 100644 --- a/packages/main/cypress/specs/ComboBox.mobile.cy.tsx +++ b/packages/main/cypress/specs/ComboBox.mobile.cy.tsx @@ -1,38 +1,534 @@ import ComboBox from "../../src/ComboBox.js"; import ComboBoxItem from "../../src/ComboBoxItem.js"; +import ComboBoxItemGroup from "../../src/ComboBoxItemGroup.js"; +import type ResponsivePopover from "../../src/ResponsivePopover.js"; import { COMBOBOX_DIALOG_OK_BUTTON } from "../../src/generated/i18n/i18n-defaults.js"; -describe("ComboBox on mobile device", () => { - beforeEach(() => { +describe("Basic mobile picker rendering and interaction", () => { + beforeEach(() => { cy.ui5SimulateDevice("phone"); }); - it("checks OK button text in dialog on mobile device", () => { + it("Should render properly the mobile picker", () => { + cy.mount( + + + + + + ); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .as("popover") + .ui5ResponsivePopoverOpened(); + + cy.get("@popover").find("[ui5-input]").should("be.visible"); + cy.get("@popover").find(".ui5-responsive-popover-close-btn").should("be.visible"); + cy.get("@popover").find(".ui5-responsive-popover-footer [ui5-button]").should("be.visible"); + }); + + it("Should close the mobile picker dialog when pressing the close button", () => { + cy.mount( + + + + + ); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .as("popover") + .ui5ResponsivePopoverOpened(); + + cy.get("@popover").find(".ui5-responsive-popover-close-btn").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverClosed(); + }); + + it("Should close the mobile picker dialog when pressing the OK button", () => { cy.mount( - - - + + ); + cy.get("[ui5-combobox]").realClick(); + cy.get("[ui5-combobox]") - .as("combo"); + .shadow() + .find("[ui5-responsive-popover]") + .as("popover") + .ui5ResponsivePopoverOpened(); - cy.get("@combo") - .click(); + cy.get("@popover").find(".ui5-responsive-popover-footer [ui5-button]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverClosed(); + }); - cy.get("@combo") + it("Should propagate the placeholder to the internal input", () => { + cy.mount( + + + + + ); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] [ui5-input]") + .should("have.attr", "placeholder", "Test placeholder"); + }); + + it("Should open and close the mobile picker with value state", () => { + cy.mount( + + + + + ); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .as("popover") + .ui5ResponsivePopoverOpened(); + + cy.get("@popover").find("[ui5-input]").shadow().find("input").realType("A"); + + cy.get("@popover").should("have.attr", "open"); + + cy.get("@popover").find("[ui5-input]").shadow().find("input").realPress("Escape"); + cy.realPress("Escape"); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverClosed(); + }); + + it("checks OK button text in dialog on mobile device", () => { + cy.mount( + + + + + + ); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") .shadow() .find("[ui5-responsive-popover]") - .as("popover"); + .find(".ui5-responsive-popover-footer [ui5-button]") + .should("have.text", COMBOBOX_DIALOG_OK_BUTTON.defaultText); + }); +}); + +describe("Eventing", () => { + beforeEach(() => { + cy.ui5SimulateDevice("phone"); + }); + + it("Should fire change event with correct parameters on item press", () => { + cy.mount( + + + + + + ); + + cy.get("[ui5-combobox]") + .invoke('on', 'ui5-change', cy.spy().as('changeSpy')); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("[ui5-combobox]").find("[ui5-cb-item]").eq(1).realClick(); + + cy.get("@changeSpy").should('have.been.calledOnce'); + cy.get("@changeSpy").should('have.been.calledWithMatch', Cypress.sinon.match(event => { + return event.target.value === "Bulgaria"; + })); + + cy.get("[ui5-combobox]").should("have.prop", "value", "Bulgaria"); + }); + + it("Should fire input event with correct parameters when typing in internal input", () => { + cy.mount( + + + + + + ); + + cy.get("[ui5-combobox]") + .invoke('on', 'input', cy.spy().as('inputSpy')); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] [ui5-input]") + .shadow() + .find("input") + .realType("ABC"); + + cy.get("@inputSpy").should('have.been.called'); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] .ui5-responsive-popover-close-btn") + .realClick(); + }); + + it("Should not fire change event when pressing the picker's Close button", () => { + cy.mount( + + + + + ); + + cy.get("[ui5-combobox]") + .invoke('on', 'ui5-change', cy.spy().as('changeSpy')); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] [ui5-input]") + .as("dialogInput") + .invoke('prop', 'value', ''); + + cy.get("@dialogInput").shadow().find("input").type("A"); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] .ui5-responsive-popover-close-btn") + .realClick(); + + cy.get("@changeSpy").should('not.have.been.called'); + cy.get("[ui5-combobox]").should("have.prop", "value", ""); + }); + + it("Should fire change event when pressing the picker's OK button", () => { + cy.mount( + + + + + ); + + cy.get("[ui5-combobox]") + .invoke('on', 'ui5-change', cy.spy().as('changeSpy')); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] [ui5-input]") + .as("dialogInput") + .invoke('prop', 'value', ''); + + cy.get("@dialogInput").shadow().find("input").type("A"); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] .ui5-responsive-popover-footer [ui5-button]") + .realClick(); + + cy.get("@changeSpy").should('have.been.calledOnce'); + }); + + it("When select an item, then open the dialog again and delete the text, then press OK button, the value should be deleted", () => { + cy.mount( + + + + + ); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .as("popover") + .ui5ResponsivePopoverOpened(); cy.get("@popover") - .find(".ui5-responsive-popover-footer") - .find("[ui5-button]") - .as("footerButton"); + .find("[ui5-input]") + .as("dialogInput") + .shadow() + .find("input") + .realClick(); - cy.get("@footerButton") - .should("have.text", COMBOBOX_DIALOG_OK_BUTTON.defaultText); + cy.get("@dialogInput").invoke('prop', 'value', ''); + cy.get("@dialogInput").shadow().find("input").realType("A"); + + cy.get("@popover") + .find(".ui5-responsive-popover-footer [ui5-button]") + .as("okBtn") + .realClick(); + + cy.get("[ui5-combobox]").should("have.prop", "value", "Algeria"); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("@dialogInput").shadow().find("input").clear(); + + cy.get("@okBtn").realClick(); + + cy.get("[ui5-combobox]").should("have.prop", "value", ""); + }); + + it("Should set clear icon to dialog's input", () => { + cy.mount( + + + + + ); + + cy.get("[ui5-combobox]").shadow().find("input").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] [ui5-input]") + .should("have.prop", "showClearIcon", true); + }); +}); + +describe("Typeahead", () => { + beforeEach(() => { + cy.ui5SimulateDevice("phone"); + }); + + it("Should autocomplete the first matched suggestion item", () => { + cy.mount( + + + + + + ); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] [ui5-input]") + .shadow() + .find("input") + .realClick(); + + cy.realType("Bu"); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] [ui5-input]") + .should("have.prop", "value", "Bulgaria"); + }); + + it("Should not perform typeahead when it is disabled", () => { + cy.mount( + + + + + ); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] [ui5-input]") + .as("dialogInput") + .shadow() + .find("input") + .type("b"); + + cy.get("@dialogInput").should("have.prop", "value", "b"); + }); +}); + +describe("Picker filtering", () => { + beforeEach(() => { + cy.ui5SimulateDevice("phone"); + }); + + it("Should filter items", () => { + cy.mount( + + + + + + + + + + + + + + + + ); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("[ui5-combobox]") + .find("[ui5-cb-item]") + .filter((_, el: Element & { _isVisible?: boolean }) => !!el._isVisible) + .should("have.length", 9); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] [ui5-input]") + .realType("B"); + + cy.get("[ui5-combobox]") + .find("[ui5-cb-item]") + .filter((_, el: Element & { _isVisible?: boolean }) => !!el._isVisible) + .should("have.length", 5); + }); + + it("Should filter group header list items", () => { + cy.mount( + + + + + + + + + + + + + + + ); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("[ui5-combobox]") + .find("[ui5-cb-item-group]") + .filter((_, el: Element & { _isVisible?: boolean }) => !!el._isVisible) + .should("have.length", 3); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] [ui5-input]") + .realType("B"); + + cy.get("[ui5-combobox]") + .find("[ui5-cb-item-group]") + .filter((_, el: Element & { _isVisible?: boolean }) => !!el._isVisible) + .should("have.length", 1); + }); +}); + +describe("Value state header", () => { + beforeEach(() => { + cy.ui5SimulateDevice("phone"); + }); + + it("Should show value state header inside mobile dialog", () => { + cy.mount( + + + + + ); + + cy.get("[ui5-combobox]").realClick(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverOpened(); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-responsive-popover] .ui5-valuestatemessage-header") + .should("be.visible"); }); -}); \ No newline at end of file +}); From f544aff82a1fddb68777ae552db773bb6235b2eb Mon Sep 17 00:00:00 2001 From: Nikolay Deshev Date: Wed, 10 Sep 2025 16:00:49 +0300 Subject: [PATCH 2/3] test(ui5-combobox): migrate mobile tests to cypress delete the old file --- .../main/test/specs/ComboBox.mobile.spec.js | 329 ------------------ 1 file changed, 329 deletions(-) delete mode 100644 packages/main/test/specs/ComboBox.mobile.spec.js diff --git a/packages/main/test/specs/ComboBox.mobile.spec.js b/packages/main/test/specs/ComboBox.mobile.spec.js deleted file mode 100644 index 544f3bb87a36..000000000000 --- a/packages/main/test/specs/ComboBox.mobile.spec.js +++ /dev/null @@ -1,329 +0,0 @@ -import { assert } from "chai"; - -const getVisibleItems = async (combo) => { - const items = await combo.$$("ui5-cb-item"); - const filteredItems = await Promise.all(items.map(async item => { - return (await item.getProperty("_isVisible")) ? item : null; - })); - - // filter out null values - return filteredItems.filter(item => item !== null); -}; - -const getVisibleGroupItems = async (combo) => { - const items = await combo.$$("ui5-cb-item-group"); - const filteredItems = await Promise.all(items.map(async item => { - return (await item.getProperty("_isVisible")) ? item : null; - })); - - // filter out null values - return filteredItems.filter(item => item !== null); -}; - -describe("Basic mobile picker rendering and interaction", () => { - beforeEach(async () => { - await browser.url("test/pages/ComboBox.html"); - await browser.emulateDevice('iPhone X'); - }); - - it("Should render properly the mobile picker", async () => { - const combo = await browser.$("#combo2"); - - await combo.scrollIntoView(); - await combo.click(); - - const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); - assert.ok(await dialogInput.isDisplayed(), "Input is displayed"); - - const dialogCloseButton = await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-close-btn"); - assert.ok(await dialogCloseButton.isDisplayed(), "Close icon is displayed"); - - const dialogOkButton = await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-footer").$("ui5-button"); - assert.ok(await dialogOkButton.isDisplayed(), "Ok button is displayed"); - }); - - it("Should close the mobile picker dialog when pressing the close button", async () => { - const combo = await $("#combo2"); - - await combo.click(); - - const picker = await combo.shadow$("ui5-responsive-popover"); - const dialogCloseButton = await picker.$(".ui5-responsive-popover-close-btn"); - - assert.ok(await picker.isDisplayed(), "Picker is still opened"); - - await dialogCloseButton.click(); - - assert.notOk(await picker.isDisplayedInViewport(), "Picker is closed now"); - }); - - it("Should close the mobile picker dialog when pressing the OK button", async () => { - const combo = await browser.$("#combo2"); - const picker = await combo.shadow$("ui5-responsive-popover"); - const dialogOkButton = await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-footer").$("ui5-button"); - - await combo.scrollIntoView(); - await combo.click(); - - assert.ok(await picker.isDisplayed(), "Picker is opened"); - - await dialogOkButton.click(); - - assert.notOk(await picker.isDisplayedInViewport(), "Picker is closed now"); - }); - - it("Should propagate the placeholder to the internal input", async () => { - const combo = await browser.$("#placeholder_test"); - - await combo.scrollIntoView(); - await combo.click(); - - const dialogInput = await browser.$(`#placeholder_test`).shadow$("ui5-responsive-popover").$("[ui5-input]"); - assert.strictEqual(await dialogInput.getAttribute("placeholder"), await combo.getAttribute("placeholder"), "Correct placeholder shown"); - - // close the suggestions - await browser.keys("Escape"); - }); - - it("Should open and close the mobile picker with value state", async () => { - const comboBoxError = await browser.$("#value-state-error"); - - await comboBoxError.scrollIntoView(); - await comboBoxError.click(); - - const dialogInput = await comboBoxError.shadow$("ui5-responsive-popover").$("[ui5-input]").shadow$("input"); - await dialogInput.click(); - await dialogInput.keys("A"); - - const popover = await comboBoxError.shadow$("ui5-responsive-popover"); - assert.ok(await popover.hasAttribute("open"), "Suggestions are open"); - - // clear the input - await dialogInput.keys("Escape"); - // close the suggestions - await browser.keys("Escape"); - assert.notOk(await popover.hasAttribute("open"), "Suggestions are closed"); - }); -}); - -describe("Eventing", () => { - before(async () => { - await browser.url("test/pages/ComboBox.html"); - await browser.emulateDevice('iPhone X'); - }); - - it("Should fire change event with correct parameters on item press", async () => { - const combo = await browser.$("#change-cb"); - - await combo.scrollIntoView(); - await combo.click(); - - const suggestionItem = (await getVisibleItems(combo))[1]; - await suggestionItem.click(); - - assert.strictEqual(await combo.getAttribute("value"), "Bulgaria", "The combo box's value is updated properly"); - - const changeText = await browser.$("#change-placeholder").getText(); - assert.strictEqual(changeText, "Bulgaria", "Change event value property was correct"); - - const changeCountText = await browser.$("#change-count").getText(); - assert.strictEqual(changeCountText, "1", "Change was fired once"); - }); - - it("Should fire input event with correct parameters when typing in internal input", async () => { - const combo = await browser.$("#input-cb"); - - await combo.scrollIntoView(); - await combo.click(); - - const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]").shadow$("input"); - - await dialogInput.keys("A"); - await dialogInput.keys("B"); - await dialogInput.keys("C"); - - const inputText = await browser.$("#input-placeholder").getText(); - assert.strictEqual(inputText, "ABC", "Input event value property was correct"); - - const inputCountText = await browser.$("#input-count").getText(); - assert.strictEqual(inputCountText, "3", "Change was fired once"); - - await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-close-btn").click(); - }); - - it("Should not fire change event when pressing the picker's Close button", async () => { - await browser.url("test/pages/ComboBox.html"); - const combo = await browser.$("#change-cb"); - - await combo.scrollIntoView(); - await combo.click(); - - const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); - const closeButton = await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-close-btn"); - - await dialogInput.shadow$("input").keys("A"); - await closeButton.click(); - - assert.notOk(await combo.getAttribute("value"), "The combo box does not have value attribute"); - - const changeText = await browser.$("#change-placeholder").getText(); - assert.notOk(changeText, "No change in this field as no change event was fired"); - - const changeCountText = await browser.$("#change-count").getText(); - assert.strictEqual(changeCountText, "0", "Change was fired once"); - }); - - it("Should fire change event when pressing the picker's OK button", async () => { - const combo = await browser.$("#change-cb"); - await combo.scrollIntoView(); - await combo.click(); - - const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); - const dialogOkButton = await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-footer").$("ui5-button"); - - await dialogInput.setProperty("value", ""); - await dialogInput.shadow$("input").keys("A"); - await dialogOkButton.click(); - - assert.strictEqual(await combo.getAttribute("value"), "Argentina", "The combo box have correct value attribute"); - const changeText = await browser.$("#change-placeholder").getText(); - - assert.strictEqual(changeText, "Argentina", "The field was changed as change event was fired"); - const changeCountText = await browser.$("#change-count").getText(); - - assert.strictEqual(changeCountText, "1", "Change was fired once"); - assert.strictEqual(await combo.getValue(), "Argentina", "The original value was changed"); - - }); - - it ("When select an item, then open the dialog again and delete the text, then press OK button, the value should be deleted.", async ()=> { - const cb = await browser.$("#combo2"); - - await cb.click(); - - const resPopover = await cb.shadow$("ui5-responsive-popover"); - const dialogInput = await resPopover.$("[ui5-input]"); - const okBtn = await resPopover.$(".ui5-responsive-popover-footer").$("ui5-button"); - - await dialogInput.shadow$("input").click(); - await dialogInput.setProperty("value", ''); - await dialogInput.shadow$("input").keys('A'); - await okBtn.click(); - - assert.strictEqual(await cb.getProperty("value"), "Algeria", "Value should be Algeria."); - - await cb.click(); - await dialogInput.shadow$("input").keys('Backspace'); - await dialogInput.shadow$("input").keys('Backspace'); - await dialogInput.shadow$("input").keys('Backspace'); - await dialogInput.shadow$("input").keys('Backspace'); - await dialogInput.shadow$("input").keys('Backspace'); - await dialogInput.shadow$("input").keys('Backspace'); - await dialogInput.shadow$("input").keys('Backspace'); - await okBtn.click(); - - assert.strictEqual(await cb.getProperty("value"), "", "Value should be empty."); - }); - - it ("Should set clear icon to dialog's input", async () => { - const cb = await $("#clear-icon-cb"); - - await cb.shadow$("input").click(); - - const resPopover = await cb.shadow$("ui5-responsive-popover"); - const dialogInput = await resPopover.$("[ui5-input]"); - - assert.ok(await dialogInput.getProperty("showClearIcon"), "Clear icon should be propagated to internal ui5-input") - }); -}); - -describe("Typeahead", () => { - before(async () => { - await browser.url("test/pages/ComboBox.html"); - await browser.emulateDevice('iPhone X'); - }); - - it("Should autocomplete the first matched suggestion item", async () => { - const combo = await browser.$("#combo2"); - const sExpected = "Bulgaria"; - - await combo.scrollIntoView(); - await combo.click(); - - const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); - - await dialogInput.shadow$("input").click(); - - await browser.keys("B"); - await browser.keys("u"); - - assert.strictEqual(await dialogInput.getProperty("value"), sExpected, "Value is autocompleted"); - }); - - it("Should not perform typeahead when it is disabled", async () => { - await browser.url("test/pages/ComboBox.html"); - - const combo = await browser.$("#combo-without-type-ahead"); - - await combo.scrollIntoView(); - await combo.click(); - - const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]").shadow$("input"); - await dialogInput.keys("b"); - assert.strictEqual(await dialogInput.getProperty("value"), "b", "Value is not autocompleted"); - }); -}); - -describe("Picker filtering", () => { - before(async () => { - await browser.url("test/pages/ComboBox.html"); - await browser.emulateDevice('iPhone X'); - }); - - it("Should filter items", async () => { - const combo = await browser.$("#value-state-grouping"); - - await combo.scrollIntoView(); - await combo.click(); - - const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); - - assert.strictEqual((await getVisibleItems(combo)).length, 9, "All of the items are shown (8)"); - await dialogInput.keys("B"); - assert.strictEqual((await getVisibleItems(combo)).length, 4, "There are 4 filtered items"); - }); - - it("Should filter group header list items", async () => { - await browser.url("test/pages/ComboBox.html"); - - const combo = await browser.$("#value-state-grouping"); - - await combo.scrollIntoView(); - await combo.click(); - - const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); - - assert.strictEqual((await getVisibleGroupItems(combo)).length, 3, "All of the group header list items are shown (3)"); - await dialogInput.keys("B"); - assert.strictEqual((await getVisibleGroupItems(combo)).length, 2, "There is only 1 visible group"); - }); -}); - - -describe("Value state header", () => { - before(async () => { - await browser.url("test/pages/ComboBox.html"); - await browser.emulateDevice('iPhone X'); - }); - - it("Should show value state header inside mobile dialog", async () => { - const combo = await browser.$("#value-state-grouping"); - - await combo.scrollIntoView(); - await combo.click(); - - const dialogStateHeader = await combo.shadow$("ui5-responsive-popover").$(".ui5-valuestatemessage-header"); - - assert.strictEqual(await dialogStateHeader.isDisplayed(), true, "The value state header is shown"); - }); -}); From 97e240b83956de887851402034c72db3d63e962c Mon Sep 17 00:00:00 2001 From: Nikolay Deshev Date: Tue, 16 Sep 2025 11:21:36 +0300 Subject: [PATCH 3/3] chore(ui5-combobox): migrate mobile tests to cypress restore wdio file --- .../main/test/specs/ComboBox.mobile.spec.js | 329 ++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 packages/main/test/specs/ComboBox.mobile.spec.js diff --git a/packages/main/test/specs/ComboBox.mobile.spec.js b/packages/main/test/specs/ComboBox.mobile.spec.js new file mode 100644 index 000000000000..544f3bb87a36 --- /dev/null +++ b/packages/main/test/specs/ComboBox.mobile.spec.js @@ -0,0 +1,329 @@ +import { assert } from "chai"; + +const getVisibleItems = async (combo) => { + const items = await combo.$$("ui5-cb-item"); + const filteredItems = await Promise.all(items.map(async item => { + return (await item.getProperty("_isVisible")) ? item : null; + })); + + // filter out null values + return filteredItems.filter(item => item !== null); +}; + +const getVisibleGroupItems = async (combo) => { + const items = await combo.$$("ui5-cb-item-group"); + const filteredItems = await Promise.all(items.map(async item => { + return (await item.getProperty("_isVisible")) ? item : null; + })); + + // filter out null values + return filteredItems.filter(item => item !== null); +}; + +describe("Basic mobile picker rendering and interaction", () => { + beforeEach(async () => { + await browser.url("test/pages/ComboBox.html"); + await browser.emulateDevice('iPhone X'); + }); + + it("Should render properly the mobile picker", async () => { + const combo = await browser.$("#combo2"); + + await combo.scrollIntoView(); + await combo.click(); + + const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); + assert.ok(await dialogInput.isDisplayed(), "Input is displayed"); + + const dialogCloseButton = await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-close-btn"); + assert.ok(await dialogCloseButton.isDisplayed(), "Close icon is displayed"); + + const dialogOkButton = await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-footer").$("ui5-button"); + assert.ok(await dialogOkButton.isDisplayed(), "Ok button is displayed"); + }); + + it("Should close the mobile picker dialog when pressing the close button", async () => { + const combo = await $("#combo2"); + + await combo.click(); + + const picker = await combo.shadow$("ui5-responsive-popover"); + const dialogCloseButton = await picker.$(".ui5-responsive-popover-close-btn"); + + assert.ok(await picker.isDisplayed(), "Picker is still opened"); + + await dialogCloseButton.click(); + + assert.notOk(await picker.isDisplayedInViewport(), "Picker is closed now"); + }); + + it("Should close the mobile picker dialog when pressing the OK button", async () => { + const combo = await browser.$("#combo2"); + const picker = await combo.shadow$("ui5-responsive-popover"); + const dialogOkButton = await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-footer").$("ui5-button"); + + await combo.scrollIntoView(); + await combo.click(); + + assert.ok(await picker.isDisplayed(), "Picker is opened"); + + await dialogOkButton.click(); + + assert.notOk(await picker.isDisplayedInViewport(), "Picker is closed now"); + }); + + it("Should propagate the placeholder to the internal input", async () => { + const combo = await browser.$("#placeholder_test"); + + await combo.scrollIntoView(); + await combo.click(); + + const dialogInput = await browser.$(`#placeholder_test`).shadow$("ui5-responsive-popover").$("[ui5-input]"); + assert.strictEqual(await dialogInput.getAttribute("placeholder"), await combo.getAttribute("placeholder"), "Correct placeholder shown"); + + // close the suggestions + await browser.keys("Escape"); + }); + + it("Should open and close the mobile picker with value state", async () => { + const comboBoxError = await browser.$("#value-state-error"); + + await comboBoxError.scrollIntoView(); + await comboBoxError.click(); + + const dialogInput = await comboBoxError.shadow$("ui5-responsive-popover").$("[ui5-input]").shadow$("input"); + await dialogInput.click(); + await dialogInput.keys("A"); + + const popover = await comboBoxError.shadow$("ui5-responsive-popover"); + assert.ok(await popover.hasAttribute("open"), "Suggestions are open"); + + // clear the input + await dialogInput.keys("Escape"); + // close the suggestions + await browser.keys("Escape"); + assert.notOk(await popover.hasAttribute("open"), "Suggestions are closed"); + }); +}); + +describe("Eventing", () => { + before(async () => { + await browser.url("test/pages/ComboBox.html"); + await browser.emulateDevice('iPhone X'); + }); + + it("Should fire change event with correct parameters on item press", async () => { + const combo = await browser.$("#change-cb"); + + await combo.scrollIntoView(); + await combo.click(); + + const suggestionItem = (await getVisibleItems(combo))[1]; + await suggestionItem.click(); + + assert.strictEqual(await combo.getAttribute("value"), "Bulgaria", "The combo box's value is updated properly"); + + const changeText = await browser.$("#change-placeholder").getText(); + assert.strictEqual(changeText, "Bulgaria", "Change event value property was correct"); + + const changeCountText = await browser.$("#change-count").getText(); + assert.strictEqual(changeCountText, "1", "Change was fired once"); + }); + + it("Should fire input event with correct parameters when typing in internal input", async () => { + const combo = await browser.$("#input-cb"); + + await combo.scrollIntoView(); + await combo.click(); + + const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]").shadow$("input"); + + await dialogInput.keys("A"); + await dialogInput.keys("B"); + await dialogInput.keys("C"); + + const inputText = await browser.$("#input-placeholder").getText(); + assert.strictEqual(inputText, "ABC", "Input event value property was correct"); + + const inputCountText = await browser.$("#input-count").getText(); + assert.strictEqual(inputCountText, "3", "Change was fired once"); + + await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-close-btn").click(); + }); + + it("Should not fire change event when pressing the picker's Close button", async () => { + await browser.url("test/pages/ComboBox.html"); + const combo = await browser.$("#change-cb"); + + await combo.scrollIntoView(); + await combo.click(); + + const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); + const closeButton = await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-close-btn"); + + await dialogInput.shadow$("input").keys("A"); + await closeButton.click(); + + assert.notOk(await combo.getAttribute("value"), "The combo box does not have value attribute"); + + const changeText = await browser.$("#change-placeholder").getText(); + assert.notOk(changeText, "No change in this field as no change event was fired"); + + const changeCountText = await browser.$("#change-count").getText(); + assert.strictEqual(changeCountText, "0", "Change was fired once"); + }); + + it("Should fire change event when pressing the picker's OK button", async () => { + const combo = await browser.$("#change-cb"); + await combo.scrollIntoView(); + await combo.click(); + + const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); + const dialogOkButton = await combo.shadow$("ui5-responsive-popover").$(".ui5-responsive-popover-footer").$("ui5-button"); + + await dialogInput.setProperty("value", ""); + await dialogInput.shadow$("input").keys("A"); + await dialogOkButton.click(); + + assert.strictEqual(await combo.getAttribute("value"), "Argentina", "The combo box have correct value attribute"); + const changeText = await browser.$("#change-placeholder").getText(); + + assert.strictEqual(changeText, "Argentina", "The field was changed as change event was fired"); + const changeCountText = await browser.$("#change-count").getText(); + + assert.strictEqual(changeCountText, "1", "Change was fired once"); + assert.strictEqual(await combo.getValue(), "Argentina", "The original value was changed"); + + }); + + it ("When select an item, then open the dialog again and delete the text, then press OK button, the value should be deleted.", async ()=> { + const cb = await browser.$("#combo2"); + + await cb.click(); + + const resPopover = await cb.shadow$("ui5-responsive-popover"); + const dialogInput = await resPopover.$("[ui5-input]"); + const okBtn = await resPopover.$(".ui5-responsive-popover-footer").$("ui5-button"); + + await dialogInput.shadow$("input").click(); + await dialogInput.setProperty("value", ''); + await dialogInput.shadow$("input").keys('A'); + await okBtn.click(); + + assert.strictEqual(await cb.getProperty("value"), "Algeria", "Value should be Algeria."); + + await cb.click(); + await dialogInput.shadow$("input").keys('Backspace'); + await dialogInput.shadow$("input").keys('Backspace'); + await dialogInput.shadow$("input").keys('Backspace'); + await dialogInput.shadow$("input").keys('Backspace'); + await dialogInput.shadow$("input").keys('Backspace'); + await dialogInput.shadow$("input").keys('Backspace'); + await dialogInput.shadow$("input").keys('Backspace'); + await okBtn.click(); + + assert.strictEqual(await cb.getProperty("value"), "", "Value should be empty."); + }); + + it ("Should set clear icon to dialog's input", async () => { + const cb = await $("#clear-icon-cb"); + + await cb.shadow$("input").click(); + + const resPopover = await cb.shadow$("ui5-responsive-popover"); + const dialogInput = await resPopover.$("[ui5-input]"); + + assert.ok(await dialogInput.getProperty("showClearIcon"), "Clear icon should be propagated to internal ui5-input") + }); +}); + +describe("Typeahead", () => { + before(async () => { + await browser.url("test/pages/ComboBox.html"); + await browser.emulateDevice('iPhone X'); + }); + + it("Should autocomplete the first matched suggestion item", async () => { + const combo = await browser.$("#combo2"); + const sExpected = "Bulgaria"; + + await combo.scrollIntoView(); + await combo.click(); + + const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); + + await dialogInput.shadow$("input").click(); + + await browser.keys("B"); + await browser.keys("u"); + + assert.strictEqual(await dialogInput.getProperty("value"), sExpected, "Value is autocompleted"); + }); + + it("Should not perform typeahead when it is disabled", async () => { + await browser.url("test/pages/ComboBox.html"); + + const combo = await browser.$("#combo-without-type-ahead"); + + await combo.scrollIntoView(); + await combo.click(); + + const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]").shadow$("input"); + await dialogInput.keys("b"); + assert.strictEqual(await dialogInput.getProperty("value"), "b", "Value is not autocompleted"); + }); +}); + +describe("Picker filtering", () => { + before(async () => { + await browser.url("test/pages/ComboBox.html"); + await browser.emulateDevice('iPhone X'); + }); + + it("Should filter items", async () => { + const combo = await browser.$("#value-state-grouping"); + + await combo.scrollIntoView(); + await combo.click(); + + const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); + + assert.strictEqual((await getVisibleItems(combo)).length, 9, "All of the items are shown (8)"); + await dialogInput.keys("B"); + assert.strictEqual((await getVisibleItems(combo)).length, 4, "There are 4 filtered items"); + }); + + it("Should filter group header list items", async () => { + await browser.url("test/pages/ComboBox.html"); + + const combo = await browser.$("#value-state-grouping"); + + await combo.scrollIntoView(); + await combo.click(); + + const dialogInput = await combo.shadow$("ui5-responsive-popover").$("[ui5-input]"); + + assert.strictEqual((await getVisibleGroupItems(combo)).length, 3, "All of the group header list items are shown (3)"); + await dialogInput.keys("B"); + assert.strictEqual((await getVisibleGroupItems(combo)).length, 2, "There is only 1 visible group"); + }); +}); + + +describe("Value state header", () => { + before(async () => { + await browser.url("test/pages/ComboBox.html"); + await browser.emulateDevice('iPhone X'); + }); + + it("Should show value state header inside mobile dialog", async () => { + const combo = await browser.$("#value-state-grouping"); + + await combo.scrollIntoView(); + await combo.click(); + + const dialogStateHeader = await combo.shadow$("ui5-responsive-popover").$(".ui5-valuestatemessage-header"); + + assert.strictEqual(await dialogStateHeader.isDisplayed(), true, "The value state header is shown"); + }); +});