diff --git a/packages/main/cypress/specs/Tree.cy.tsx b/packages/main/cypress/specs/Tree.cy.tsx index 6e62fdba897b..0b0d08adb239 100644 --- a/packages/main/cypress/specs/Tree.cy.tsx +++ b/packages/main/cypress/specs/Tree.cy.tsx @@ -3,6 +3,12 @@ import "../../src/TreeItem.js"; import TreeItem from "../../src/TreeItem.js"; import Icon from "../../src/Icon.js"; import bell from "@ui5/webcomponents-icons/dist/bell.js"; +import { setAnimationMode } from "@ui5/webcomponents-base/dist/config/AnimationMode.js"; +import TreeItemCustom from "../../src/TreeItemCustom.js"; +import Button from "../../src/Button.js"; +import Option from "../../src/Option.js"; +import Select from "../../src/Select.js"; + describe("Tree Tests", () => { it("tests accessibility properties forwarded to the list", () => { @@ -70,3 +76,707 @@ describe("Tree Props", () => { .should("exist") }); }); + +before(() => { + cy.wrap({ setAnimationMode }) + .then(api => { + return api.setAnimationMode("none"); + }); +}) + +const getVisibleTreeItems = (treeSelector = "[ui5-tree]") => { + return cy.get(`${treeSelector} [ui5-tree-item]`).filter(":visible"); +}; + +const getVisibleTreeItemsCount = (treeSelector = "[ui5-tree]") => { + return getVisibleTreeItems(treeSelector).then($items => $items.length); +}; + +describe("Tree general interaction", () => { + it("Tree is rendered", () => { + cy.mount( + + + + + + + + + + + + + + + + + + + + + ); + + cy.get("[ui5-tree]") + .shadow() + .find("[ui5-tree-list]") + .should("exist"); + }); + + it("Tree items can be collapsed", () => { + cy.mount( + + + + + + + + + + ); + + getVisibleTreeItemsCount().then(initialCount => { + cy.get("[ui5-tree-item][expanded]") + .shadow() + .find(".ui5-li-tree-toggle-icon") + .click(); + + getVisibleTreeItemsCount().should("be.lessThan", initialCount); + }); + }); + + it("Tree items can be expanded", () => { + cy.mount( + + + + + + + + + + ); + + getVisibleTreeItemsCount().then(initialCount => { + cy.get("[ui5-tree-item]:first") + .shadow() + .find(".ui5-li-tree-toggle-icon") + .click(); + + getVisibleTreeItemsCount().should("be.greaterThan", initialCount); + }); + }); + + it("keyboard handling on F2", () => { + cy.mount( + + + + + + + + + + + + + + ); + + cy.get("[ui5-tree-item-custom].item").should("exist"); + cy.get(".itemBtn").should("exist"); + + cy.get("[ui5-tree-item-custom].item").click(); + + cy.get("[ui5-tree-item-custom].item").should("be.focused"); + + cy.get("[ui5-tree-item-custom].item").realPress("F2"); + + cy.get(".itemBtn").should("be.focused"); + + cy.get(".itemBtn").realPress("F2"); + + cy.get("[ui5-tree-item-custom].item").should("be.focused"); + }); +}); + +describe("Tree proxies properties to list", () => { + it("Mouseover/mouseout events", () => { + cy.mount( + + + + + + + + + + ); + + cy.get("[ui5-tree]").then($tree => { + $tree[0].addEventListener("ui5-item-mouseover", cy.stub().as("mouseoverStub")); + $tree[0].addEventListener("ui5-item-mouseout", cy.stub().as("mouseoutStub")); + }); + + cy.get("[ui5-tree-item]:first") + .shadow() + .find(".ui5-li-root-tree") + .realHover(); + + cy.get("@mouseoverStub") + .should("have.been.calledOnce"); + + cy.get("[ui5-tree-item]:eq(1)") + .shadow() + .find(".ui5-li-root-tree") + .realHover(); + + cy.get("@mouseoverStub") + .should("have.been.calledTwice"); + + cy.get("@mouseoutStub") + .should("have.been.calledOnce"); + }); + + it("SelectionMode works", () => { + cy.mount( + + + + + + + + + + + + + ); + + cy.get("[ui5-tree]") + .shadow() + .find("[ui5-tree-list]") + .should("have.attr", "selection-mode", "Multiple"); + + cy.get("[ui5-tree-item]:first") + .should("have.attr", "_selection-mode", "Multiple"); + + const modes = ["None", "Single", "SingleStart", "SingleEnd", "Multiple", "Delete"]; + modes.forEach(selectionMode => { + cy.get("[ui5-tree]") + .invoke("attr", "selection-mode", selectionMode); + + cy.get("[ui5-tree]") + .shadow() + .find("[ui5-tree-list]") + .should("have.attr", "selection-mode", selectionMode); + }); + }); + + it("SelectionMode works recursively", () => { + cy.mount( + + + + + + + + + + ); + + cy.get(".lastItem") + .should("have.attr", "_selection-mode", "Multiple"); + }); + + it("headerText, footerText, noDataText work", () => { + cy.mount( + + + + ); + + cy.get("[ui5-tree]") + .invoke("attr", "header-text", "header text"); + + cy.get("[ui5-tree]") + .shadow() + .find("[ui5-tree-list]") + .should("have.attr", "header-text", "header text"); + + cy.get("[ui5-tree]") + .invoke("attr", "footer-text", "footer text"); + + cy.get("[ui5-tree]") + .shadow() + .find("[ui5-tree-list]") + .should("have.attr", "footer-text", "footer text"); + + cy.get("[ui5-tree]") + .invoke("attr", "no-data-text", "no data text"); + + cy.get("[ui5-tree]") + .shadow() + .find("[ui5-tree-list]") + .should("have.attr", "no-data-text", "no data text"); + }); + + it("Tests the prevention of the ui5-itemClick event", () => { + cy.mount( + + + + + + ); + + cy.get("[ui5-tree]").then($tree => { + $tree[0].addEventListener("ui5-item-click", cy.stub().as("itemClickStub").callsFake((event) => { + event.preventDefault(); + })); + }); + + cy.get("[ui5-tree-item]:first") + .click() + .should("not.have.attr", "selected"); + + cy.get("@itemClickStub") + .should("have.been.calledOnce"); + }); + + it("selectionChange event provides targetItem parameter", () => { + cy.mount( + + + + + + + ); + + let selectionChangeStub; + + cy.get("[ui5-tree]").then($tree => { + selectionChangeStub = cy.stub().as("selectionChangeStub"); + $tree[0].addEventListener("ui5-selection-change", selectionChangeStub); + }); + + cy.get("[ui5-tree-item]:first") + .invoke("attr", "id", "item1") + .click() + .then(() => { + expect(selectionChangeStub).to.have.been.calledOnce; + expect(selectionChangeStub.getCall(0).args[0].detail.targetItem.id).to.equal("item1"); + }); + }); +}); + +describe("Tree has screen reader support", () => { + it("List role is correct", () => { + cy.mount( + + + + + + + + + + ); + + cy.get("[ui5-tree]") + .shadow() + .find("[ui5-tree-list]") + .shadow() + .find("ul") + .should("have.attr", "role", "tree"); + }); + + it("List item acc attributes correct", () => { + cy.mount( + + + + + + + + + + ); + + cy.get("[ui5-tree] [ui5-tree-item]").each(($item) => { + cy.wrap($item) + .shadow() + .find("li") + .should("have.attr", "role", "treeitem"); + + cy.wrap($item) + .invoke("attr", "level") + .then((level) => { + cy.wrap($item) + .shadow() + .find("li") + .should("have.attr", "aria-level", level); + }); + + cy.wrap($item) + .invoke("prop", "showToggleButton") + .then((showToggleButton) => { + cy.wrap($item) + .invoke("prop", "expanded") + .then((expanded) => { + const ariaExpandedValues = { + "true": { + "true": "true", + "false": "false", + }, + "false": { + "true": null, + "false": null, + } + }; + + const expectedValue = ariaExpandedValues[showToggleButton.toString()][expanded.toString()]; + + if (expectedValue === null) { + cy.wrap($item) + .shadow() + .find("li") + .should("not.have.attr", "aria-expanded"); + } else { + cy.wrap($item) + .shadow() + .find("li") + .should("have.attr", "aria-expanded", expectedValue); + } + }); + }); + }); + }); +}); + +describe("Tree slots", () => { + it("items slot", () => { + cy.mount( + <> + + + + + + + + + + + + ); + + cy.get("[ui5-tree-item]:first") + .find("[ui5-tree-item]:first") + .as("treeItem"); + + cy.get("@treeItem") + .invoke("prop", "items") + .should("have.length", 1); + + cy.get("#btn").then(($btn) => { + $btn[0].addEventListener("click", () => { + cy.get("@treeItem").then(($treeItem) => { + const newTreeItem = document.createElement("ui5-tree-item") as any; + const currentCount = $treeItem[0].querySelectorAll("[ui5-tree-item]").length; + newTreeItem.text = `1-1-${currentCount + 1}`; + $treeItem[0].appendChild(newTreeItem); + }); + }); + }); + + cy.get("#btn").click(); + + cy.get("@treeItem") + .invoke("prop", "items") + .should("have.length", 2); + + cy.get("@treeItem") + .find("[ui5-tree-item]:last-child") + .should("have.prop", "text", "1-1-2") + .and("have.prop", "level", 3); + }); +}); + +describe("Tree drag and drop tests", () => { + const setupDragAndDrop = (tree: HTMLElement, options: { + allowBefore?: boolean; + allowAfter?: boolean; + allowOn?: boolean; + preventParentChildMove?: boolean; + } = {}) => { + const { allowBefore = true, allowAfter = true, allowOn = true, preventParentChildMove = false } = options; + + tree.addEventListener("ui5-move-over", (e: CustomEvent) => { + const { destination, source } = e.detail; + + if (!tree.contains(source.element)) { + return; + } + + // Prevent parent-child moves if specified + if (preventParentChildMove && source.element.contains(destination.element)) { + return; + } + + // Check allowed placements + if (!allowBefore && destination.placement === "Before") { + return; + } + if (!allowAfter && destination.placement === "After") { + return; + } + if (!allowOn && destination.placement === "On") { + return; + } + + // Special nesting rules + if (destination.placement === "On" && !("allowsNesting" in destination.element.dataset)) { + return; + } + + e.preventDefault(); + }); + + tree.addEventListener("ui5-move", (e: CustomEvent) => { + const { destination, source } = e.detail; + + // Prevent self-moves + if (source.element === destination.element) { + return; + } + + // Prevent parent-child moves if specified + if (preventParentChildMove && source.element.contains(destination.element)) { + return; + } + + switch (destination.placement) { + case "Before": + destination.element.before(source.element); + break; + case "After": + destination.element.after(source.element); + break; + case "On": + destination.element.prepend(source.element); + break; + } + }); + }; + + const dispatchMoveEvent = (sourceAlias: string, targetAlias: string, placement: string) => { + cy.get(sourceAlias).then($source => { + cy.get(targetAlias).then($target => { + const moveEvent = new CustomEvent("ui5-move", { + detail: { + source: { element: $source[0] }, + destination: { element: $target[0], placement } + } + }); + cy.get("[ui5-tree]").then($tree => { + $tree[0].dispatchEvent(moveEvent); + }); + }); + }); + }; + + beforeEach(() => { + cy.mount( + + + + + + + + + + + + + + + + + + + + + + + ); + }); + + it("Moving item After another", () => { + cy.get("[ui5-tree]").then($tree => setupDragAndDrop($tree[0], { allowBefore: false })); + + cy.get("[ui5-tree] > [ui5-tree-item]").eq(0).as("firstItem"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(1).as("secondItem"); + + dispatchMoveEvent("@firstItem", "@secondItem", "After"); + + cy.get("[ui5-tree] > [ui5-tree-item]").eq(0).should("have.attr", "text", "Tree 2 ALLOWS NESTING"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(1).should("have.attr", "text", "Tree 1"); + + cy.get("[ui5-tree] > [ui5-tree-item]").eq(1).as("movedFirst"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(2).as("third"); + + dispatchMoveEvent("@movedFirst", "@third", "After"); + + cy.get("[ui5-tree] > [ui5-tree-item]").eq(0).should("have.attr", "text", "Tree 2 ALLOWS NESTING"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(1).should("have.attr", "text", "Tree 3 (no icon)"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(2).should("have.attr", "text", "Tree 1"); + }); + + it("Moving item Before another", () => { + cy.get("[ui5-tree]").then($tree => setupDragAndDrop($tree[0], { allowAfter: false })); + + cy.get("[ui5-tree] > [ui5-tree-item]").eq(0).as("firstItem"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(2).as("thirdItem"); + + dispatchMoveEvent("@firstItem", "@thirdItem", "Before"); + + cy.get("[ui5-tree] > [ui5-tree-item]").eq(0).should("have.attr", "text", "Tree 2 ALLOWS NESTING"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(1).should("have.attr", "text", "Tree 1"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(2).should("have.attr", "text", "Tree 3 (no icon)"); + + cy.get("[ui5-tree] > [ui5-tree-item]").eq(1).as("movedFirst"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(0).as("second"); + + dispatchMoveEvent("@movedFirst", "@second", "Before"); + + cy.get("[ui5-tree] > [ui5-tree-item]").eq(0).should("have.attr", "text", "Tree 1"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(1).should("have.attr", "text", "Tree 2 ALLOWS NESTING"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(2).should("have.attr", "text", "Tree 3 (no icon)"); + }); + + it("Moving item ON another", () => { + cy.get("[ui5-tree]").then($tree => setupDragAndDrop($tree[0])); + + cy.get("[ui5-tree] > [ui5-tree-item]").eq(0).as("firstItem"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(1).as("secondItem"); + + dispatchMoveEvent("@firstItem", "@firstItem", "On"); + + cy.get("[ui5-tree] > [ui5-tree-item]").should("have.length", 3); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(0).should("have.attr", "text", "Tree 1"); + + dispatchMoveEvent("@firstItem", "@secondItem", "On"); + + cy.get("[ui5-tree] > [ui5-tree-item]").should("have.length", 2); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(0).should("have.attr", "text", "Tree 2 ALLOWS NESTING"); + cy.get("[ui5-tree] > [ui5-tree-item]").eq(0).find("[ui5-tree-item]").first().should("have.attr", "text", "Tree 1"); + }); + + it("Rearranging leafs", () => { + cy.get("[ui5-tree]").then($tree => setupDragAndDrop($tree[0])); + + cy.get("[ui5-tree-item]").shadow().find(".ui5-li-tree-toggle-icon").first().click(); + + cy.get("[ui5-tree] [ui5-tree-item]").then($allItems => { + const items = Array.from($allItems); + + const moveEvent1 = new CustomEvent("ui5-move", { + detail: { + source: { element: items[12] }, + destination: { element: items[13], placement: "After" } + } + }); + + cy.get("[ui5-tree]").then($tree => { + $tree[0].dispatchEvent(moveEvent1); + }); + + cy.get("[ui5-tree] [ui5-tree-item]").then($newItems => { + const newItems = Array.from($newItems); + expect(newItems[12]).to.equal(items[13]); + expect(newItems[13]).to.equal(items[12]); + }); + + cy.get("[ui5-tree] [ui5-tree-item]").then($currentItems => { + const currentItems = Array.from($currentItems); + const moveEvent2 = new CustomEvent("ui5-move", { + detail: { + source: { element: currentItems[13] }, + destination: { element: currentItems[12], placement: "Before" } + } + }); + + cy.get("[ui5-tree]").then($tree => { + $tree[0].dispatchEvent(moveEvent2); + }); + + cy.get("[ui5-tree] [ui5-tree-item]").then($finalItems => { + const finalItems = Array.from($finalItems); + expect(finalItems[12]).to.equal(items[12]); + expect(finalItems[13]).to.equal(items[13]); + }); + }); + }); + }); + + it("Nesting parent among its children should be impossible", () => { + cy.get("[ui5-tree]").then($tree => setupDragAndDrop($tree[0], { preventParentChildMove: true })); + + cy.get("[ui5-tree] [ui5-tree-item]").then($allItems => { + const items = Array.from($allItems); + const originalOrder = items.map(item => item.getAttribute('text')); + + const moveEvent = new CustomEvent("ui5-move", { + detail: { + source: { element: items[0] }, + destination: { element: items[1], placement: "After" } + } + }); + + cy.get("[ui5-tree]").then($tree => { + $tree[0].dispatchEvent(moveEvent); + }); + + // Verify no change + cy.get("[ui5-tree] [ui5-tree-item]").then($newItems => { + const newItems = Array.from($newItems); + const newOrder = newItems.map(item => item.getAttribute('text')); + expect(newOrder).to.deep.equal(originalOrder); + }); + }); + }); +}); \ No newline at end of file diff --git a/packages/main/test/specs/Tree.spec.js b/packages/main/test/specs/Tree.spec.js deleted file mode 100644 index afa6e6ed32ad..000000000000 --- a/packages/main/test/specs/Tree.spec.js +++ /dev/null @@ -1,342 +0,0 @@ -import { assert } from "chai"; - -async function getItemsCount(selector) { - const items = await getItems(selector); - return items.length; -} - -async function getItems(selector) { - const listItems = await browser.$$(`${selector} [ui5-tree-item]`); - - const promises = listItems.map(async (item) => { - const isDisplayed = await item.isDisplayedInViewport(); - return isDisplayed ? item : null; - },); - - const items = await Promise.all(promises); - - return items.filter((item) => item); -} - -describe("Tree general interaction", () => { - before(async () => { - await browser.url(`test/pages/Tree.html`); - }); - - it("Tree is rendered", async () => { - const treeRoot = await browser.$("#tree").shadow$("ui5-tree-list"); - assert.ok(await treeRoot.isExisting(), "Tree is rendered."); - }); - - it("Tree items can be collapsed", async () => { - const listItemsBefore = await getItemsCount("#tree"); - const toggleButton = await browser.$(">>>#tree ui5-tree-item[expanded] ui5-icon.ui5-li-tree-toggle-icon"); - - await toggleButton.click(); - const listItemsAfter = await getItemsCount("#tree"); - assert.isBelow(listItemsAfter, listItemsBefore, "After collapsing a node, there are less items in the list"); - }); - - it("Tree items can be expanded", async () => { - const listItemsBefore = await getItemsCount("#tree"); - const toggleButton = await browser.$(">>>#tree ui5-tree-item ui5-icon.ui5-li-tree-toggle-icon"); - - await toggleButton.click(); - const listItemsAfter = await getItemsCount("#tree"); - assert.isAbove(listItemsAfter, listItemsBefore, "After expanding a node, there are more items in the list"); - }); - - it("keyboard handling on F2", async () => { - const item = await browser.$("ui5-tree-item-custom.item"); - const itemBtn = await browser.$("ui5-button.itemBtn"); - - await item.click(); - assert.ok(await item.isFocused(), "item is focused"); - - // act: F2 from item -> the focus should go to "Click me" button - await item.keys("F2"); - assert.ok(await itemBtn.isFocused(), "the 1st tabbable element (button) is focused"); - - // act: f2 from the "Click me" button - the focus should go back to the parent item - await itemBtn.keys("F2"); - assert.ok(await item.isFocused(), "the parent item is focused"); - }); - -}); - -describe("Tree proxies properties to list", () => { - before(async () => { - await browser.url(`test/pages/Tree.html`); - }); - - it("Mouseover/mouseout events", async () => { - const treeItems = await browser.$$(">>>#tree ui5-tree-item .ui5-li-root-tree"); - const inputMouseover = await browser.$("#mouseover-counter"); - const inputMouseout = await browser.$("#mouseout-counter"); - - await treeItems[0].moveTo(); - - assert.strictEqual(await inputMouseover.getAttribute("value"), "1", "Mouseover event is fired when item is accessed"); - - await treeItems[1].moveTo(); - assert.strictEqual(await inputMouseover.getAttribute("value"), "2", "Mouseover event is fired when other item is accessed result"); - assert.strictEqual(await inputMouseout.getAttribute("value"), "1", "Mouseout event is fired when the first item is not hovered"); - }) - - it("SelectionMode works", async () => { - const tree = await browser.$("#tree"); - const list = await tree.shadow$("ui5-tree-list"); - - const treeItem = await browser.$("#firstCollapsedItem"); - assert.strictEqual(await treeItem.getAttribute("_selection-mode"), "Multiple", "SelectionMode applied to the tree item"); - - const modes = ["None", "Single", "SingleStart", "SingleEnd", "Multiple", "Delete"]; - modes.forEach(async selectionMode => { - await tree.setAttribute("selection-mode", selectionMode); - assert.strictEqual(await list.getAttribute("selection-mode"), selectionMode, "SelectionMode applied"); - }); - }); - - it("SelectionMode works recursively", async () => { - const lastItem = await browser.$(">>>#allItemsMultiple .lastItem"); - assert.strictEqual(await lastItem.getAttribute("_selection-mode"), "Multiple", "SelectionMode applied to the last tree item"); - }); - - it("headerText, footerText, noDataText work", async () => { - const tree = await browser.$("#tree"); - const list = await tree.shadow$("ui5-tree-list"); - - await tree.setAttribute("header-text", "header text"); - await tree.setAttribute("footer-text", "footer text"); - await tree.setAttribute("no-data-text", "no data text"); - - assert.strictEqual(await list.getAttribute("header-text"), "header text", "header text applied"); - assert.strictEqual(await list.getAttribute("footer-text"), "footer text", "footer text applied"); - assert.strictEqual(await list.getAttribute("no-data-text"), "no data text", "no data text applied"); - }) - - it("Tests the prevention of the ui5-itemClick event", async () => { - const treeItems = await browser.$$("#preventable-click-event ui5-tree-item"); - const firstItem = treeItems[0]; - - await firstItem.click(); - - assert.notOk(await firstItem.getAttribute("selected"), "The first item is not selected when we prevent the click event."); - }); - - it("selectionChange event provides targetItem parameter", async () => { - const selectionChangeTargetItemResult = await browser.$("#selectionChangeTargetItemResult"); - const listItems = await browser.$$("#treeIndeterminate ui5-tree-item"); - const firstTreeItem = await browser.$("#treeIndeterminate #item1"); - let firstTreeItemId, targetItemId; - - await listItems[0].click(); - - firstTreeItemId = await firstTreeItem.getProperty("id"); - targetItemId = await selectionChangeTargetItemResult.getProperty("value"); - - assert.strictEqual(targetItemId, firstTreeItemId, "targetItem parameter holds correct tree item"); - }); -}); - -describe("Tree has screen reader support", () => { - before(async () => { - await browser.url(`test/pages/Tree.html`); - }); - - it("List role is correct", async () => { - const tree = await browser.$("#tree"); - const list = await tree.shadow$("ui5-tree-list"); - assert.strictEqual(await list.shadow$("ul").getAttribute("role"), "tree", "List role is tree"); - }); - - it("List item acc attributes correct", async () => { - const listItems = await browser.$$("#tree ui5-tree-item"); - - const promises = listItems.map(async (item, idx) => { - const li = await item.shadow$("li"); - const itemExpandable = await item.getProperty("showToggleButton"); - const itemExpanded = await item.getProperty("expanded"); - const liAriaExpanded = await li.getAttribute("aria-expanded"); - - const ariaExpandedValues = { - // (1) expandable: aria-expanded can be 'true' or 'false' - "true": { - "true" : "true", - "false": "false", - }, - // (2) not expandable: aria-expanded is null - not present - "false": { - "true" : null, - "false": null, - } - }; - - assert.strictEqual(await li.getAttribute("role"), "treeitem", "List item role is correct"); - assert.strictEqual(await li.getAttribute("aria-level"), await item.getAttribute("level"), "aria level is correct"); - assert.equal(liAriaExpanded, ariaExpandedValues[itemExpandable][itemExpanded], - "aria-expanded is correct."); - }); - - await Promise.all(promises); - }); - - it ("Tree's internal List receives aria-label from the accessibleName property", async () => { - const tree = await browser.$("#tree"); - const list = await tree.shadow$("ui5-tree-list"); - assert.strictEqual(await list.shadow$("ul").getAttribute("aria-label"), "Tree with accessibleName", "list aria label is correct"); - }); - - it ("Tree's internal List receives aria-label from the accessibleNameRef property", async () => { - const tree = await browser.$("#preventable-click-event"); - const list = await tree.shadow$("ui5-tree-list"); - const treeLabel = await browser.$("#tree-label"); - assert.strictEqual(await list.shadow$("ul").getAttribute("aria-label"), await treeLabel.getHTML(false), "list aria label is correct"); - }); - - it ("Tree list item receives aria-labelledby from the accessibleName property", async () => { - const listTreeItem = await browser.$("#tree ui5-tree-item"); - const listItem = await listTreeItem.shadow$("li"); - const liAriaLabelledBy = await listItem.getAttribute("aria-labelledby"); - const ariaLabelText = await listItem.$(`#${liAriaLabelledBy}`).getText(); - - assert.ok(ariaLabelText.includes("Tree item with accessibleName"), "aria label text is correct"); - }); - -}); - - -describe("Tree slots", () => { - before(async () => { - await browser.url(`test/pages/Tree.html`); - }); - - it("items slot", async () => { - const treeItem = await browser.$("#treeItem"); - const btn = await browser.$("#btn"); - - let items = await treeItem.getProperty("items"); - assert.strictEqual(items.length, 1, "Correct items count"); - - await btn.click(); - - items = await treeItem.getProperty("items"); - const newlyAddedItem = await treeItem.$('#treeItem [ui5-tree-item]:last-child'); - - assert.strictEqual(items.length, 2, "Dynamic item is added correctly"); - assert.strictEqual(await newlyAddedItem.getProperty("text"), "1-1-2", "Dynamic item is added correctly"); - assert.strictEqual(await newlyAddedItem.getProperty("level"), 3, "Dynamic item is displayed correctly"); - }); -}); - -describe("Tree drag and drop tests", () => { - const getDragOffset = async (draggedElement, dropTargetElement, targetPosition) => { - const draggedRectangle = { - ...await draggedElement.getLocation(), - ...await draggedElement.getSize() - }; - - const dropTargetElementRectangle = { - ...await dropTargetElement.getLocation(), - ...await dropTargetElement.getSize() - } - const EXTRA_OFFSET = Math.floor(dropTargetElementRectangle.height / 3); - - const draggedElementCenter = (draggedRectangle.y + draggedRectangle.height / 2); - const droppedElementCenter = (dropTargetElementRectangle.y + dropTargetElementRectangle.height / 2); - - let offsetToCenter = Math.round(droppedElementCenter - draggedElementCenter); - - if (targetPosition === "Before") { - offsetToCenter -= EXTRA_OFFSET - } else if (targetPosition === "After") { - offsetToCenter += EXTRA_OFFSET; - } - - return offsetToCenter; - }; - - const compareItemsOrder = async (treeId, expectedItems, nestedTag) => { - let treeItems; - if (nestedTag) { - treeItems = await browser.$$(`#${treeId} [${nestedTag}]`); - } else { - treeItems = await browser.$$(`#${treeId} > *`); // direct children - } - const results = await Promise.all(expectedItems.map((item, i) => item.isEqual(treeItems[i]))); - - return results.every(value => value); - } - - before(async () => { - await browser.url(`test/pages/TreeDragAndDrop.html`); - }); - - it("Moving item After another", async () => { - const [firstItem, secondItem, thirdItem] = await browser.$$("#tree > [ui5-tree-item]"); - - let dragOffset = await getDragOffset(firstItem, secondItem, "After"); - - await firstItem.dragAndDrop({ x: 0, y: dragOffset}); - assert.ok(await compareItemsOrder("tree", [secondItem, firstItem, thirdItem]), "Items order has changed"); - - dragOffset = await getDragOffset(firstItem, thirdItem, "After"); - await firstItem.dragAndDrop({ x: 0, y: dragOffset}); - assert.ok(await compareItemsOrder("tree", [secondItem, thirdItem, firstItem]), "Items order has changed"); - }); - - it("Moving item Before another", async () => { - const [secondItem, thirdItem, firstItem] = await browser.$$("#tree > [ui5-tree-item]"); - - let dragOffset = await getDragOffset(firstItem, thirdItem, "Before"); - await firstItem.dragAndDrop({ x: 0, y: dragOffset}); - assert.ok(await compareItemsOrder("tree", [secondItem, firstItem, thirdItem]), "Items order has changed"); - - dragOffset = await getDragOffset(firstItem, secondItem, "Before") - await firstItem.dragAndDrop({ x: 0, y: dragOffset}); - assert.ok(await compareItemsOrder("tree", [firstItem, secondItem, thirdItem]), "Items order has changed"); - }); - - it("Moving item ON another", async () => { - const [firstItem, secondItem, thirdItem] = await browser.$$("#tree > [ui5-tree-item]"); - - await firstItem.dragAndDrop({ x: 0, y: 0 }); - assert.ok(await compareItemsOrder("tree", [firstItem, secondItem, thirdItem]), "Items order has NOT changed"); - - const dragOffset = await getDragOffset(firstItem, secondItem); - await firstItem.dragAndDrop({ x: 0, y: dragOffset}); - assert.ok(await compareItemsOrder("tree", [secondItem, thirdItem]), "First item nested in second"); - }); - - it("Rearranging leafs", async () => { - const toggleButton = await browser.$(">>>#tree ui5-tree-item ui5-icon.ui5-li-tree-toggle-icon"); - await toggleButton.click(); - - const allItems = await browser.$$("#tree [ui5-tree-item]"); - let secondToLastLeaf = allItems[12]; - let lastLeaf = allItems[13]; - - let dragOffset = await getDragOffset(secondToLastLeaf, lastLeaf, "After"); - await secondToLastLeaf.dragAndDrop({ x: 0, y: dragOffset}); - [allItems[12], allItems[13]] = [allItems[13], allItems[12]]; - assert.ok(await compareItemsOrder("tree", allItems, 'ui5-tree-item'), "Second-to-last leaf moved after last"); - - secondToLastLeaf = allItems[12]; - lastLeaf = allItems[13]; - - dragOffset = await getDragOffset(lastLeaf, secondToLastLeaf, "Before"); - await lastLeaf.dragAndDrop({ x: 0, y: dragOffset}); - [allItems[13], allItems[12]] = [allItems[12], allItems[13]]; - assert.ok(await compareItemsOrder("tree", allItems, 'ui5-tree-item'), "Last leaf moved before second-to-last"); - }); - - it("Nesting parent among its children should be impossible", async () => { - const allItems = await browser.$$("#tree [ui5-tree-item]"); - const parent = allItems[0]; - const child = allItems[1]; - - const dragOffset = await getDragOffset(parent, child, "After"); - await parent.dragAndDrop({ x: 0, y: dragOffset}); - assert.ok(await compareItemsOrder("tree", allItems, 'ui5-tree-item'), "Order stays the same. Parent not nested among its children."); - }); -}); \ No newline at end of file