// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. // // /// /// /// module WinJSTests { "use strict"; var utilities = WinJS.Utilities; var _element: HTMLDivElement; var oldHasWinRT; var oldRenderSelection; var ItemContainer = WinJS.UI.ItemContainer; function eventOnElement(element) { var rect = element.getBoundingClientRect(); // Simulate clicking the middle of the element return { target: element, clientX: (rect.left + rect.right) / 2, clientY: (rect.top + rect.bottom) / 2, defaultPrevented: false, preventDefault: function () { this.defaultPrevented = true; } }; } function click(control, eventObject) { var target = eventObject.target, elementCoords; if (typeof eventObject.button !== "number") { eventObject.button = WinJS.UI._LEFT_MSPOINTER_BUTTON; } target.scrollIntoView(false); elementCoords = eventOnElement(target); eventObject.clientX = elementCoords.clientX; eventObject.clientY = elementCoords.clientY; eventObject.preventDefault = function () { }; control._onPointerDown(eventObject); control._onPointerUp(eventObject); control._onClick(); } function createEvent(element, key, shiftKey = false, ctrlKey = false) { return { keyCode: key, shiftKey: shiftKey, ctrlKey: ctrlKey, target: element, stopPropagation: function () { }, preventDefault: function () { } }; } function verifySelectionVisual(element, selected) { var parts = element.querySelectorAll(WinJS.Utilities._selectionPartsSelector); if (selected) { LiveUnit.Assert.isTrue(utilities.hasClass(element, WinJS.UI._selectedClass)); LiveUnit.Assert.areEqual(4, parts.length, "The control's container should have 4 selection parts"); } else { LiveUnit.Assert.isFalse(utilities.hasClass(element, WinJS.UI._selectedClass)); LiveUnit.Assert.areEqual(0, parts.length, "The control's container should not have selection parts"); } } export class ItemContainerTests { // This is the setup function that will be called at the beginning of each test function. setUp() { LiveUnit.LoggingCore.logComment("In setup"); var newNode = document.createElement("div"); newNode.id = "host"; document.body.appendChild(newNode); _element = newNode; oldHasWinRT = WinJS.Utilities.hasWinRT; oldRenderSelection = WinJS.UI._ItemEventsHandler.renderSelection; WinJS.Utilities._setHasWinRT(false); } tearDown() { LiveUnit.LoggingCore.logComment("In tearDown"); if (_element) { WinJS.Utilities.disposeSubTree(_element); document.body.removeChild(_element); _element = null; } WinJS.Utilities._setHasWinRT(oldHasWinRT); WinJS.UI._ItemEventsHandler.renderSelection = oldRenderSelection; } testElementProperty(complete) { var element = document.getElementById("host"); var control = new ItemContainer(element); LiveUnit.Assert.areEqual(WinJS.Utilities._uniqueID(element), WinJS.Utilities._uniqueID(control.element), "Invalid element returned by the control's element property"); complete(); } testDraggableProperty(complete) { if (utilities.isPhone) { complete(); return; } var element = document.getElementById("host"); var control = new ItemContainer(element); LiveUnit.Assert.isFalse(control.draggable, "The control should not be draggable by default"); LiveUnit.Assert.isNull(element.getAttribute("draggable")); control.draggable = true; var itemBox = element.querySelector(".win-itembox"); LiveUnit.Assert.areEqual("true", itemBox.getAttribute("draggable")); complete(); } testSelectedProperty(complete) { var element = document.getElementById("host"); var control = new ItemContainer(element); LiveUnit.Assert.isFalse(control.selected, "The control should not be selected by default"); LiveUnit.Assert.isFalse(utilities.hasClass(element, WinJS.UI._selectedClass)); control.selected = true; LiveUnit.Assert.isTrue(control.selected, "The control's selected property setter is not working"); verifySelectionVisual(element, true); complete(); } testSelectionDisabledProperty(complete) { var element = document.getElementById("host"); var control = new ItemContainer(element); LiveUnit.Assert.isFalse(control.selectionDisabled, "The default selectionDisabled should be false"); LiveUnit.Assert.areEqual(control._selectionMode, WinJS.UI.SelectionMode.single); control.selectionDisabled = true; LiveUnit.Assert.isTrue(control.selectionDisabled); LiveUnit.Assert.areEqual(control._selectionMode, WinJS.UI.SelectionMode.none); complete(); } testTapBehaviorProperty(complete) { var element = document.getElementById("host"); var control = new ItemContainer(element); LiveUnit.Assert.areEqual(WinJS.UI.TapBehavior.invokeOnly, control.tapBehavior, "The control's tabBehavior property should default to invokeOnly"); complete(); } testInvokedEvent(complete) { var element = document.getElementById("host"); var control = new ItemContainer(element); var listener1Called = false; var listener2Called = false; control.addEventListener("invoked", function (ev) { listener1Called = true; LiveUnit.Assert.areEqual(WinJS.Utilities._uniqueID(element), WinJS.Utilities._uniqueID(ev.target)); }); control.oninvoked = function (ev) { listener2Called = true; LiveUnit.Assert.areEqual(WinJS.Utilities._uniqueID(element), WinJS.Utilities._uniqueID(ev.target)); }; click(control, { target: element }); LiveUnit.Assert.isTrue(listener1Called); LiveUnit.Assert.isTrue(listener2Called); complete(); } testInvokedEventWithTabBehaviorNone(complete) { var element = document.getElementById("host"); var control = new ItemContainer(element); control.tapBehavior = WinJS.UI.TapBehavior.none; control.addEventListener("invoked", function (ev) { LiveUnit.Assert.fail("invoked event should not be called when tabBehavior is none"); }); click(control, { target: element }); WinJS.Utilities._setImmediate(complete); } testSelectionChangingEventCalled(complete) { var invoked = false; var element = document.getElementById("host"); function selectionChanging(ev) { invoked = true; LiveUnit.Assert.areEqual(WinJS.Utilities._uniqueID(element), WinJS.Utilities._uniqueID(ev.target)); } var control = new ItemContainer(element, { onselectionchanging: selectionChanging }); LiveUnit.Assert.isFalse(invoked); control._onKeyDown(createEvent(element, utilities.Key.space)); LiveUnit.Assert.isTrue(invoked, "Selectionchanging event should have been called"); complete(); } testSelectionChangingEventPrevented(complete) { var element = document.getElementById("host"); function selectionChanging(ev) { ev.preventDefault(); } var control = new ItemContainer(element, { onselectionchanging: selectionChanging }); control.onselectionchanged = function () { LiveUnit.Assert.fail("Selectionchanged should not be called because it was prevented on selectionchanging"); }; control._onKeyDown(createEvent(element, utilities.Key.space)); complete(); } testSelectionChangedEventCalled(complete) { var invoked = false; var element = document.getElementById("host"); function selectionChanged(ev) { invoked = true; LiveUnit.Assert.areEqual(WinJS.Utilities._uniqueID(element), WinJS.Utilities._uniqueID(ev.target)); } var control = new ItemContainer(element, { onselectionchanged: selectionChanged }); LiveUnit.Assert.isFalse(invoked); control._onKeyDown(createEvent(element, utilities.Key.space)); LiveUnit.Assert.isTrue(invoked, "Selectionchanged event should have been called"); complete(); } testKeyboardFocusBlur(complete) { var element = document.getElementById("host"); var control = new ItemContainer(element); WinJS.UI._keyboardSeenLast = false; LiveUnit.Assert.areEqual(0, element.querySelectorAll("." + WinJS.UI._itemFocusOutlineClass).length); control._onFocusIn(); LiveUnit.Assert.areEqual(0, element.querySelectorAll("." + WinJS.UI._itemFocusOutlineClass).length); WinJS.UI._keyboardSeenLast = true; control._onFocusIn(); LiveUnit.Assert.areEqual(1, element.querySelectorAll("." + WinJS.UI._itemFocusOutlineClass).length); WinJS.UI._keyboardSeenLast = null; control._onFocusOut(); LiveUnit.Assert.areEqual(0, element.querySelectorAll("." + WinJS.UI._itemFocusOutlineClass).length); complete(); } testTabIndex(complete) { var element = document.getElementById("host"); var control = new ItemContainer(element); LiveUnit.Assert.areEqual("0", element.getAttribute("tabindex"), "The control should set tabindex to 0 if a tabindex was not provided"); var element2 = document.createElement("div"); document.body.appendChild(element2); element2.setAttribute("tabindex", "3"); var control2 = new ItemContainer(element2); LiveUnit.Assert.areEqual("3", element2.getAttribute("tabindex"), "The control should not set tabindex if one was provided"); element2.parentNode.removeChild(element2); complete(); } testItemEventsHandlerIntegration(complete) { var element = document.getElementById("host"); var control = new ItemContainer(element); var handler = control._itemEventsHandler; var site = handler._site; LiveUnit.Assert.isNotNull(handler); LiveUnit.Assert.areEqual(site.containerFromElement(null), element); LiveUnit.Assert.areEqual(site.indexForItemElement(null), 1); LiveUnit.Assert.areEqual(site.itemBoxAtIndex(0), control._itemBox); LiveUnit.Assert.areEqual(site.itemAtIndex(0), element); LiveUnit.Assert.areEqual(site.containerAtIndex(0), element); LiveUnit.Assert.areEqual(site.selectionMode, WinJS.UI.SelectionMode.single); LiveUnit.Assert.areEqual(site.tapBehavior, WinJS.UI.TapBehavior.invokeOnly); control.tapBehavior = WinJS.UI.TapBehavior.toggleSelect; LiveUnit.Assert.areEqual(site.tapBehavior, WinJS.UI.TapBehavior.toggleSelect); LiveUnit.Assert.isFalse(site.draggable); LiveUnit.Assert.isTrue(site.skipPreventDefaultOnPointerDown); if (!utilities.isPhone) { control.draggable = true; LiveUnit.Assert.isTrue(site.draggable); } LiveUnit.Assert.isFalse(site.selection.selected); control.selected = true; LiveUnit.Assert.isTrue(site.selection.selected); LiveUnit.Assert.isNull(site.customFootprintParent); complete(); } testDispose(complete) { var element = document.getElementById("host"); var child = document.createElement("div"); WinJS.Utilities.addClass(child, "win-disposable"); element.appendChild(child); var childDisposed = false; child.winControl = { dispose: function () { childDisposed = true; } }; var control = new ItemContainer(element); LiveUnit.Assert.isFalse(!!control._disposed); LiveUnit.Assert.isFalse(childDisposed); control.dispose(); LiveUnit.Assert.isTrue(control._disposed); LiveUnit.Assert.isTrue(childDisposed); complete(); } testForceLayout(complete) { var element = document.getElementById("host"); var control = new ItemContainer(element); LiveUnit.Assert.isFalse(utilities.hasClass(element, "win-rtl")); element.style.direction = "rtl"; control.forceLayout(); LiveUnit.Assert.isTrue(utilities.hasClass(element, "win-rtl")); complete(); } testSetupInternalTree(complete) { var element = document.getElementById("host"); var child1 = document.createElement("div"); child1.className = "child1"; var child2 = document.createElement("div2"); child2.className = "child2"; element.appendChild(child1); element.appendChild(child2); var control = new ItemContainer(element); LiveUnit.Assert.isTrue(element, ItemContainer._ClassName.itemContainer); LiveUnit.Assert.isTrue(utilities.hasClass(element, WinJS.UI._containerClass)); // The main element should now have two children (the itembox + the captureProxy) LiveUnit.Assert.areEqual(2, element.children.length); var itemBox = element.children[0]; LiveUnit.Assert.isTrue(utilities.hasClass(itemBox, WinJS.UI._itemBoxClass)); LiveUnit.Assert.areEqual(1, itemBox.children.length); var itemElement = itemBox.children[0]; LiveUnit.Assert.isTrue(itemElement, WinJS.UI._itemClass); LiveUnit.Assert.areEqual(2, itemElement.children.length); LiveUnit.Assert.isTrue(utilities.hasClass(itemElement.children[0], "child1")); LiveUnit.Assert.isTrue(utilities.hasClass(itemElement.children[1], "child2")); complete(); } testSpaceBarSelection(complete) { var element = document.getElementById("host"); element.textContent = "my item"; var control = new ItemContainer(element); LiveUnit.Assert.isFalse(control.selected); control._onKeyDown(createEvent(element, utilities.Key.space)); LiveUnit.Assert.isTrue(control.selected); control._onKeyDown(createEvent(element, utilities.Key.space)); LiveUnit.Assert.isFalse(control.selected) complete(); } testToggleSelect(complete) { var element = document.getElementById("host"); element.textContent = "my item"; var control = new ItemContainer(element); control.tapBehavior = WinJS.UI.TapBehavior.toggleSelect; LiveUnit.Assert.isFalse(control.selected); click(control, { target: element }); verifySelectionVisual(element, true); LiveUnit.Assert.isTrue(control.selected); click(control, { target: element }); verifySelectionVisual(element, false); complete(); } testSetAriaRole(complete) { var control1 = new ItemContainer(document.getElementById("host")); LiveUnit.Assert.areEqual(control1.element.getAttribute("role"), "option", "By default selection/tap is enabled, so the default role should be option"); var element2 = document.createElement("div"); document.body.appendChild(element2); var control2 = new ItemContainer(element2, { tapBehavior: WinJS.UI.TapBehavior.none }); LiveUnit.Assert.areEqual(control2.element.getAttribute("role"), "option", "By default selection is enabled, so the default role should be option"); element2.parentNode.removeChild(element2); var element3 = document.createElement("div"); document.body.appendChild(element3); var control3 = new ItemContainer(element3, { selectionDisabled: true }); LiveUnit.Assert.areEqual(control3.element.getAttribute("role"), "option", "By default tap is enabled, so the default role should be option"); element3.parentNode.removeChild(element3); var element4 = document.createElement("div"); document.body.appendChild(element4); var control4 = new ItemContainer(element4, { selectionDisabled: true, tapBehavior: WinJS.UI.TapBehavior.none }); LiveUnit.Assert.areEqual(control4.element.getAttribute("role"), "listitem", "Selection and Tap are disabled, the default role should be listitem"); element4.parentNode.removeChild(element4); var elementWithRole = document.createElement("div"); elementWithRole.setAttribute("role", "listbox"); document.body.appendChild(elementWithRole); var control5 = new ItemContainer(elementWithRole); LiveUnit.Assert.areEqual(control5.element.getAttribute("role"), "listbox", "A role was already specified. The control should not change it"); elementWithRole.parentNode.removeChild(elementWithRole); var element6 = document.createElement("div"); document.body.appendChild(element6); var control6 = new ItemContainer(element6); LiveUnit.Assert.areEqual(control6.element.getAttribute("role"), "option", "By default selection/tap is enabled, so the default role should be option"); control6.tapBehavior = WinJS.UI.TapBehavior.none; LiveUnit.Assert.areEqual(control6.element.getAttribute("role"), "option", "By default selection is enabled, so the default role should be option"); control6.selectionDisabled = true; LiveUnit.Assert.areEqual(control6.element.getAttribute("role"), "listitem", "Selection is disabled and TapBehavior is none, the default role should be listitem"); element6.parentNode.removeChild(element6); complete(); } testUIASelect(complete) { function blockSelection(eventObject) { eventObject.preventDefault(); } function test() { var prevSelection; function verifySelection(expectedSelection) { prevSelection = expectedSelection; LiveUnit.Assert.areEqual(expectedSelection, itemContainer.selected, "ItemContainer should be selected"); LiveUnit.Assert.areEqual(expectedSelection, WinJS.Utilities.hasClass(itemContainer.element, WinJS.UI._selectedClass), "ItemContainer selected class is in the wrong state"); LiveUnit.Assert.areEqual(expectedSelection, itemContainer.element.getAttribute("aria-selected") === "true", "ItemContainer aria-selected is incorrect"); } var itemContainer = new ItemContainer(document.getElementById("host")); return WinJS.Promise.timeout().then(function () { // Simulate UIA SelectionItem.Select changes verifySelection(false); itemContainer.element.setAttribute("aria-selected", "false"); return WinJS.Promise.timeout(); }).then(function () { verifySelection(false); itemContainer.element.setAttribute("aria-selected", "true"); return WinJS.Promise.timeout(); }).then(function () { verifySelection(true); itemContainer.element.setAttribute("aria-selected", "false"); return WinJS.Promise.timeout(); }).then(function () { verifySelection(false); itemContainer.selected = true; return WinJS.Promise.timeout(); }).then(function () { // Simulate UIA SelectionItem.Select with blocked selection itemContainer.addEventListener("selectionchanging", blockSelection, false); itemContainer.element.setAttribute("aria-selected", "false"); return WinJS.Promise.timeout(); }).then(function () { verifySelection(true); itemContainer.removeEventListener("selectionchanging", blockSelection, false); itemContainer.selected = false; return WinJS.Promise.timeout(); }).then(function () { // Simulate UIA SelectionItem.Select on item with selectionDisabled = true itemContainer.selectionDisabled = true; itemContainer.element.setAttribute("aria-selected", "true"); return WinJS.Promise.timeout(); }).then(function () { verifySelection(false); }); } test().done(complete); } }; } LiveUnit.registerTestClass("WinJSTests.ItemContainerTests");