From 2ec3e41afb402970c81208d22cb1fc62208deb24 Mon Sep 17 00:00:00 2001 From: Nayden Naydenov Date: Fri, 12 Sep 2025 14:22:59 +0300 Subject: [PATCH 1/5] chore(ui5-multi-combobox): migrate tests to cypress --- .../main/cypress/specs/MultiComboBox.cy.tsx | 238 ++++++++++++++++-- packages/main/src/MultiComboBox.ts | 27 +- packages/main/src/Tokenizer.ts | 4 +- 3 files changed, 247 insertions(+), 22 deletions(-) diff --git a/packages/main/cypress/specs/MultiComboBox.cy.tsx b/packages/main/cypress/specs/MultiComboBox.cy.tsx index 5c1b87e8ebba..596a0beb80bd 100644 --- a/packages/main/cypress/specs/MultiComboBox.cy.tsx +++ b/packages/main/cypress/specs/MultiComboBox.cy.tsx @@ -1387,7 +1387,7 @@ describe("Selection and filtering", () => { }); describe("Validation & Value State", () => { - const handleInput = (e:Event) => { + const handleInput = (e: Event) => { (e.target as MultiComboBox).valueState = (e.target as MultiComboBox).value.length ? "Negative" : "Information"; } @@ -1537,7 +1537,7 @@ describe("Validation & Value State", () => { }); it("Should remove value state header when value state is reset", () => { - const onSelectionChange = (e:Event) => { + const onSelectionChange = (e: Event) => { (e.target as MultiComboBox).valueState = "None"; } @@ -1750,7 +1750,7 @@ describe("Keyboard interaction when pressing Ctrl + Alt + F8 for navigation", () describe("Event firing", () => { it("tests if open and close events are fired correctly", () => { - const onFocusIn = (e:Event) => { + const onFocusIn = (e: Event) => { (e.target as MultiComboBox).setAttribute("open", "true"); } @@ -1804,7 +1804,7 @@ describe("Event firing", () => { }); it("Should prevent selection-change when clicking an item", () => { - const onSelectionChange = (e:Event) => { + const onSelectionChange = (e: Event) => { e.preventDefault(); } @@ -1853,7 +1853,7 @@ describe("Event firing", () => { }); it("Should prevent selection-change when deleting a token", () => { - const onSelectionChange = (e:Event) => { + const onSelectionChange = (e: Event) => { e.preventDefault(); } @@ -1896,7 +1896,7 @@ describe("Event firing", () => { }); it("Should prevent selection-change on CTRL+A", () => { - const onSelectionChange = (e:Event) => { + const onSelectionChange = (e: Event) => { e.preventDefault(); } @@ -1990,7 +1990,7 @@ describe("Event firing", () => { }); it("Should not fire submit, when an item is tokenized", () => { - const onSubmit = cy.spy((e:Event) => e.preventDefault()).as("submitEvent"); + const onSubmit = cy.spy((e: Event) => e.preventDefault()).as("submitEvent"); cy.mount( <> @@ -2228,13 +2228,13 @@ describe("MultiComboBox RTL/LTR Arrow Navigation", () => { .realClick() .should("have.focus") - cy.get("@input") + cy.get("@input") .should("have.value", "") .should(($input) => { expect(($input[0] as HTMLInputElement).selectionStart).to.equal(0); }); - cy.get("@mcb").realPress("ArrowRight"); ; + cy.get("@mcb").realPress("ArrowRight");; cy.focused().should("have.class", "ui5-token--wrapper"); }); }); @@ -2307,9 +2307,9 @@ describe("Accessibility", () => { const resourceBundle = (el.constructor as any).i18nBundle; cy.get("[ui5-multi-combobox]") - .shadow() - .find(".ui5-hidden-text") - .should("have.text", resourceBundle.getText(TOKENIZER_ARIA_CONTAIN_SEVERAL_TOKENS.defaultText, 2)); + .shadow() + .find(".ui5-hidden-text") + .should("have.text", resourceBundle.getText(TOKENIZER_ARIA_CONTAIN_SEVERAL_TOKENS.defaultText, 2)); }) cy.get("[ui5-multi-combobox]") @@ -2493,7 +2493,7 @@ describe("Accessibility", () => { .shadow() .find(".ui5-checkbox-root") .should("not.have.attr", "tabindex"); -}); + }); }); describe("Grouping", () => { @@ -2537,7 +2537,7 @@ describe("Grouping", () => { const itemArray = Array.from(items); expect(itemArray.filter(item => (item as MultiComboBoxItem)._isVisible).length).to.equal(2); } - ); + ); cy.get("@mcb") .get("[ui5-mcb-item-group]") @@ -2545,7 +2545,7 @@ describe("Grouping", () => { const itemArray = Array.from(items); expect(itemArray.filter(item => (item as MultiComboBoxItem).assignedSlot).length).to.equal(2); } - ); + ); }); it("Tests group item focusability", () => { @@ -3905,4 +3905,212 @@ describe("Keyboard Handling", () => { .find("ui5-responsive-popover") .should("not.have.attr", "open") }); + + describe.only("Copy/Cut/Paste keyboard shortcuts", () => { + const dispatchCopyEvent = () => { + cy.get("@input").then($input => { + const input = $input.get(0) as HTMLInputElement; + + const pasteEvent = new ClipboardEvent("paste", { + bubbles: true, + cancelable: true, + }); + + input.dispatchEvent(pasteEvent); + }); + }; + + it("copy a token with CTRL+C", () => { + cy.mount(<> + + + + + + + ) + + cy.window().then(win => { + cy.spy(win.navigator.clipboard, 'writeText').as('writeTextSpy'); + }); + + cy.get("[ui5-multi-combobox]").as("mcb"); + cy.get("@mcb").shadow().find(".ui5-multi-combobox-token").as("tokens"); + + cy.get("@tokens").eq(0).realClick(); + cy.get("@tokens").eq(0).should("be.focused"); + + cy.realPress(["Control", "c"]); + + cy.get("@writeTextSpy").should("have.been.calledOnceWith", "11111"); + }); + + it("paste a token with CTRL+V", () => { + cy.mount(<> + + + + + + + + + ) + + cy.window().then(win => { + cy.stub(win.navigator.clipboard, "readText") + .as("clipboardRead") + .returns("22222"); + }); + + cy.get("[ui5-multi-combobox]").as("mcb2"); + cy.get("@mcb2").shadow().find("input").as("input"); + + cy.get("@input").realClick(); + cy.get("@input").should("be.focused"); + + dispatchCopyEvent(); + + cy.get("@clipboardRead").should("have.been.calledOnce"); + + cy.get("@input").should("have.value", "22222"); + cy.get("@mcb2").should("have.prop", "open", true); + + dispatchCopyEvent(); + + cy.get("@clipboardRead").should("have.been.calledTwice"); + + cy.get("@input").should("have.value", "2222222222"); + }); + + it("not be able to paste token with CTRL+V in read only", async () => { + cy.mount(<> + + ) + + cy.get("[ui5-multi-combobox]").as("mcb2"); + cy.get("@mcb2").shadow().find("input").as("input"); + cy.get("@mcb2").shadow().find(".ui5-multi-combobox-token").as("tokens"); + + cy.get("@input").realClick(); + cy.get("@input").should("be.focused"); + + dispatchCopyEvent(); + + cy.get("@clipboardRead").should("have.been.calledOnce"); + + cy.get("@input").should("have.value", ""); + cy.get("@tokenes").should("not.exist"); + }); + + it("should cut a token with CTRL+X", () => { + cy.mount(<> + + + + + + + ) + + cy.window().then(win => { + cy.spy(win.navigator.clipboard, 'writeText').as('writeTextSpy'); + }); + + cy.get("[ui5-multi-combobox]").as("mcb"); + cy.get("@mcb").shadow().find(".ui5-multi-combobox-token").as("tokens"); + + cy.get("@tokens").should("have.length", 3); + + cy.get("@tokens").eq(0).realClick(); + cy.get("@tokens").eq(0).should("be.focused"); + + cy.realPress(["Control", "x"]); + + cy.get("@writeTextSpy").should("have.been.calledOnceWith", "33333"); + cy.get("@tokens").should("have.length", 2); + }); + + it("cut a token with SHIFT+DELETE", () => { + cy.mount(<> + + + + + + + ) + + cy.window().then(win => { + cy.spy(win.navigator.clipboard, 'writeText').as('writeTextSpy'); + }); + + cy.get("[ui5-multi-combobox]").as("mcb"); + cy.get("@mcb").shadow().find(".ui5-multi-combobox-token").as("tokens"); + + cy.get("@tokens").eq(0).realClick(); + cy.get("@tokens").eq(0).should("be.focused"); + + cy.realPress(["Shift", "Delete"]); + + cy.get("@writeTextSpy").should("have.been.calledOnceWith", "44444"); + }); + + it("copy a token with CTRL+INSERT ", () => { + cy.mount(<> + + + + + + + ) + + cy.window().then(win => { + cy.spy(win.navigator.clipboard, 'writeText').as('writeTextSpy'); + }); + + cy.get("[ui5-multi-combobox]").as("mcb"); + cy.get("@mcb").shadow().find(".ui5-multi-combobox-token").as("tokens"); + + cy.get("@tokens").eq(0).realClick(); + cy.get("@tokens").eq(0).should("be.focused"); + + cy.realPress(["Control", "Insert"]); + + cy.get("@writeTextSpy").should("have.been.calledOnceWith", "55555"); + }); + + it("paste a token with SHIFT+INSERT", () => { + cy.mount(<> + + + + + + + + + ) + + cy.window().then(win => { + cy.stub(win.navigator.clipboard, "readText") + .as("clipboardRead") + .returns("66666"); + }); + + cy.get("[ui5-multi-combobox]").as("mcb2"); + cy.get("@mcb2").shadow().find("input").as("input"); + + cy.get("@input").realClick(); + cy.get("@input").should("be.focused"); + + cy.realPress(["Shift", "Insert"]); + + cy.get("@clipboardRead").should("have.been.called"); + + cy.get("@input").should("have.value", "66666"); + cy.get("@mcb2").should("have.prop", "open", true); + }); + }); }); \ No newline at end of file diff --git a/packages/main/src/MultiComboBox.ts b/packages/main/src/MultiComboBox.ts index cf45801924c9..9f7b52d7159a 100644 --- a/packages/main/src/MultiComboBox.ts +++ b/packages/main/src/MultiComboBox.ts @@ -908,12 +908,22 @@ class MultiComboBox extends UI5Element implements IFormInputElement { }); } - _handlePaste(e: ClipboardEvent) { - if (this.readonly || !e.clipboardData) { + async _handlePaste(e: ClipboardEvent) { + if (this.readonly) { return; } - const pastedText = (e.clipboardData).getData("text/plain"); + e.preventDefault(); + + const pastedText = await navigator.clipboard.readText(); + document.execCommand("insertText", true, pastedText ?? ""); + const inputEvent = new Event("input", { + bubbles: true, + cancelable: true, + }); + + // Dispatch it + this._innerInput.dispatchEvent(inputEvent); if (!pastedText) { return; @@ -936,8 +946,17 @@ class MultiComboBox extends UI5Element implements IFormInputElement { if (this.readonly || isFirefox()) { return; } + e.preventDefault(); const pastedText = await navigator.clipboard.readText(); + document.execCommand("insertText", true, pastedText ?? ""); + const inputEvent = new Event("input", { + bubbles: true, + cancelable: true, + }); + + // Dispatch it + this._innerInput.dispatchEvent(inputEvent); if (!pastedText) { return; @@ -2000,7 +2019,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { const links: Array = []; if (this.valueStateMessage) { this.valueStateMessage.forEach(element => { - if (element.children.length) { + if (element.children.length) { element.querySelectorAll("ui5-link").forEach(link => { links.push(link as HTMLElement); }); diff --git a/packages/main/src/Tokenizer.ts b/packages/main/src/Tokenizer.ts index 3158e75e463b..1ebe091632f8 100644 --- a/packages/main/src/Tokenizer.ts +++ b/packages/main/src/Tokenizer.ts @@ -967,9 +967,7 @@ class Tokenizer extends UI5Element { const tokensTexts = tokens.filter(token => token.selected).map(token => token.text).join("\r\n"); const cutToClipboard = (e: ClipboardEvent) => { - if (e.clipboardData) { - e.clipboardData.setData("text/plain", tokensTexts); - } + navigator.clipboard.writeText(tokensTexts); e.preventDefault(); }; From eda77bdd433b207c978f6985081879628c2c583c Mon Sep 17 00:00:00 2001 From: Nayden Naydenov Date: Fri, 12 Sep 2025 14:24:18 +0300 Subject: [PATCH 2/5] chore: delete spec file --- .../main/test/specs/MultiComboBox.spec.js | 114 ------------------ 1 file changed, 114 deletions(-) delete mode 100644 packages/main/test/specs/MultiComboBox.spec.js diff --git a/packages/main/test/specs/MultiComboBox.spec.js b/packages/main/test/specs/MultiComboBox.spec.js deleted file mode 100644 index ae5af98aa9bd..000000000000 --- a/packages/main/test/specs/MultiComboBox.spec.js +++ /dev/null @@ -1,114 +0,0 @@ -import { assert } from "chai"; - -const getVisibleItems = async (combo) => { - const items = await combo.$$("ui5-mcb-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-mcb-item-group"); - const assignedItems = await Promise.all(items.map(async item => { - return (await item.getProperty("assignedSlot")) ? item : null; - })); - - return assignedItems.filter(item => item !== null); -}; - -describe("MultiComboBox general interaction", () => { - beforeEach(async () => { - await browser.url(`test/pages/MultiComboBox.html`); - await browser.setWindowSize(1920, 1080); - }); - - describe("keyboard handling", () => { - it("should copy a token with CTRL+C and paste it with CTRL+V", async () => { - const mcb = await browser.$("#multi1"); - const mcb2 = await browser.$("#mcb"); - const input = await mcb2.shadow$("input"); - const tokens = await mcb.shadow$$(".ui5-multi-combobox-token"); - - await tokens[1].click(); - await tokens[1].keys(["Control", "c"]); - await input.click(); - await input.keys(["Control", "v"]); - - assert.strictEqual(await mcb2.getProperty("value"), "Condensed", "Token is pasted into the second control"); - assert.ok(await mcb2.getProperty("open"), "Popover should be open"); - - await input.keys(["Control", "v"]); - - assert.equal(await mcb2.getProperty("value"), "CondensedCondensed", "Pasting second time should append the as text"); - }); - - it("should not be able to paste token with CTRL+V in read only multi combo box", async () => { - const mcb = await browser.$("#multi1"); - const mcb2 = await browser.$("#readonly-value-state-mcb"); - const input = await mcb2.shadow$("input"); - const tokens = await mcb.shadow$$(".ui5-multi-combobox-token"); - - await tokens[1].click(); - await tokens[1].keys(["Control", "c"]); - await input.click(); - await input.keys(["Control", "v"]); - - const mcb2Tokens = await mcb2.shadow$$(".ui5-multi-combobox-token"); - assert.equal(await mcb2.getProperty("value"), "", "Token is not pasted into the second control"); - assert.equal(mcb2Tokens.length, 0, "No token was created."); - }); - - it("should cut a token with CTRL+X and paste it with CTRL+V", async () => { - const mcb = await browser.$("#multi1"); - const mcb2 = await browser.$("#mcb"); - const input = await mcb2.shadow$("input"); - let tokens = await mcb.shadow$$(".ui5-multi-combobox-token"); - - await tokens[1].click(); - await tokens[1].keys(["Control", "x"]); - - tokens = await mcb.shadow$$(".ui5-multi-combobox-token"); - assert.equal(await tokens.length, 2, "One of the tokens is cut from the control"); - - await input.click(); - await input.keys(["Control", "v"]); - - assert.equal(await mcb2.getProperty("value"), "Condensed", "Token is pasted into the second control"); - }); - - it("should cut a token with SHIFT+DELETE and paste it with SHIFT+INSERT", async () => { - const mcb = await browser.$("#multi1"); - const mcb2 = await browser.$("#mcb"); - const input = await mcb2.shadow$("input"); - let tokens = await mcb.shadow$$(".ui5-multi-combobox-token"); - - await tokens[1].click(); - await tokens[1].keys(["Shift", "Delete"]); - - tokens = await mcb.shadow$$(".ui5-multi-combobox-token"); - assert.equal(await tokens.length, 2, "One of the tokens is cut from the control"); - - await input.click(); - await input.keys(["Shift", "Insert"]); - - assert.equal(await mcb2.getProperty("value"), "Condensed", "Token is pasted into the second control"); - }); - - it("should copy a token with CTRL+INSERT and paste it with SHIFT+INSERT", async () => { - const mcb = await browser.$("#multi1"); - const mcb2 = await browser.$("#mcb"); - const input = await mcb2.shadow$("input"); - const tokens = await mcb.shadow$$(".ui5-multi-combobox-token"); - - await tokens[1].click(); - await tokens[1].keys(["Control", "Insert"]); - await input.click(); - await input.keys(["Shift", "Insert"]); - - assert.equal(await mcb2.getProperty("value"), "Condensed", "Token is pasted into the second control"); - }); - }); -}); From 653fc012bfc6f3177a82e3514cc92f17ee22f2cb Mon Sep 17 00:00:00 2001 From: Nayden Naydenov Date: Fri, 12 Sep 2025 15:14:07 +0300 Subject: [PATCH 3/5] chore: remove only --- packages/main/cypress/specs/MultiComboBox.cy.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/cypress/specs/MultiComboBox.cy.tsx b/packages/main/cypress/specs/MultiComboBox.cy.tsx index 596a0beb80bd..b83b3bb28684 100644 --- a/packages/main/cypress/specs/MultiComboBox.cy.tsx +++ b/packages/main/cypress/specs/MultiComboBox.cy.tsx @@ -3906,7 +3906,7 @@ describe("Keyboard Handling", () => { .should("not.have.attr", "open") }); - describe.only("Copy/Cut/Paste keyboard shortcuts", () => { + describe("Copy/Cut/Paste keyboard shortcuts", () => { const dispatchCopyEvent = () => { cy.get("@input").then($input => { const input = $input.get(0) as HTMLInputElement; From a03ddcf44759349b326276c9fc4671dcb5291c2e Mon Sep 17 00:00:00 2001 From: Nayden Naydenov Date: Tue, 16 Sep 2025 17:12:29 +0300 Subject: [PATCH 4/5] chore: rename function --- packages/main/cypress/specs/MultiComboBox.cy.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/main/cypress/specs/MultiComboBox.cy.tsx b/packages/main/cypress/specs/MultiComboBox.cy.tsx index b83b3bb28684..5cd9f7406829 100644 --- a/packages/main/cypress/specs/MultiComboBox.cy.tsx +++ b/packages/main/cypress/specs/MultiComboBox.cy.tsx @@ -3907,7 +3907,7 @@ describe("Keyboard Handling", () => { }); describe("Copy/Cut/Paste keyboard shortcuts", () => { - const dispatchCopyEvent = () => { + const dispatchPasteEvent = () => { cy.get("@input").then($input => { const input = $input.get(0) as HTMLInputElement; @@ -3969,14 +3969,14 @@ describe("Keyboard Handling", () => { cy.get("@input").realClick(); cy.get("@input").should("be.focused"); - dispatchCopyEvent(); + dispatchPasteEvent(); cy.get("@clipboardRead").should("have.been.calledOnce"); cy.get("@input").should("have.value", "22222"); cy.get("@mcb2").should("have.prop", "open", true); - dispatchCopyEvent(); + dispatchPasteEvent(); cy.get("@clipboardRead").should("have.been.calledTwice"); @@ -3995,7 +3995,7 @@ describe("Keyboard Handling", () => { cy.get("@input").realClick(); cy.get("@input").should("be.focused"); - dispatchCopyEvent(); + dispatchPasteEvent(); cy.get("@clipboardRead").should("have.been.calledOnce"); From 51eadef497bd5a10c519e9d76287c0f7cbcb86c3 Mon Sep 17 00:00:00 2001 From: Nayden Naydenov Date: Tue, 16 Sep 2025 17:37:58 +0300 Subject: [PATCH 5/5] chore: remove wdio tests from main --- packages/main/config/wdio.conf.cjs | 10 - .../main/test/specs/ComboBox.mobile.spec.js | 329 ------------------ packages/tools/components-package/nps.js | 4 +- 3 files changed, 2 insertions(+), 341 deletions(-) delete mode 100644 packages/main/test/specs/ComboBox.mobile.spec.js diff --git a/packages/main/config/wdio.conf.cjs b/packages/main/config/wdio.conf.cjs index e8f8893bbf07..09fc7419bbbd 100644 --- a/packages/main/config/wdio.conf.cjs +++ b/packages/main/config/wdio.conf.cjs @@ -1,15 +1,5 @@ const wdio = require("@ui5/webcomponents-tools/components-package/wdio.js"); -wdio.config.suites = { - "suite1": [ - './test/specs/base/*.spec.js', - './test/specs/[A-I]*.spec.js', - ], - "suite2": [ - './test/specs/[^A-I]*.spec.js', - ], -}; - wdio.config.services.push("devtools"); module.exports = wdio; 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"); - }); -}); diff --git a/packages/tools/components-package/nps.js b/packages/tools/components-package/nps.js index 8af1d1331314..46063cd104b0 100644 --- a/packages/tools/components-package/nps.js +++ b/packages/tools/components-package/nps.js @@ -145,8 +145,8 @@ const getScripts = (options) => { "test-cy-ci-suite-1": `${cypressEnvVariables(options, ["TEST_SUITE=SUITE1"])} yarn cypress run --component --browser chrome`, "test-cy-ci-suite-2": `${cypressEnvVariables(options, ["TEST_SUITE=SUITE2"])} yarn cypress run --component --browser chrome`, "test-cy-open": `${cypressEnvVariables(options)} yarn cypress open --component --browser chrome`, - "test-suite-1": `node "${LIB}/test-runner/test-runner.js" --suite suite1`, - "test-suite-2": `node "${LIB}/test-runner/test-runner.js" --suite suite2`, + "test-suite-1": ``, + "test-suite-2": ``, startWithScope: "nps scope.prepare scope.watchWithBundle", scope: { prepare: "nps scope.lint scope.testPages",