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