// 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 ListView = WinJS.UI.ListView; var Key = WinJS.Utilities.Key; var testRootEl; var ITEMS_COUNT = 10; var ddEventsList = ["itemdragstart", "itemdragenter", "itemdragend", "itemdragbetween", "itemdragleave", "itemdragchanged", "itemdragdrop"]; function getRawData(count?) { var rawData = []; for (var i = 0; i < (count ? count : ITEMS_COUNT); i++) { rawData.push({ itemInfo: "Tile" + i }); } return rawData; } function getDataSource(count?) { var rawData = getRawData(count); return new WinJS.Binding.List(rawData).dataSource; } function getAdjustedKeys(rtl) { return { left: rtl ? WinJS.Utilities.Key.rightArrow : WinJS.Utilities.Key.leftArrow, up: WinJS.Utilities.Key.upArrow, right: rtl ? WinJS.Utilities.Key.leftArrow : WinJS.Utilities.Key.rightArrow, down: WinJS.Utilities.Key.downArrow }; } function basicRenderer(itemPromise) { var element = document.createElement("div"); element.style.width = "50px"; element.style.height = "50px"; return { element: element, renderComplete: itemPromise.then(function (item) { element.textContent = item.data.itemInfo; if (item.data.markUndraggable) { element.className += " win-nondraggable"; } }) }; } function generateEventInElement(listView, name, element, rtl, offset?) { var elementRect = element.getBoundingClientRect(); if (!offset) { offset = { x: element.offsetWidth / 2, y: element.offsetHeight / 2, }; } var fakeEventObject = { clientX: elementRect[rtl ? "right" : "left"] + (rtl ? -offset.x : offset.x), clientY: elementRect.top + offset.y, preventDefault: function () { }, dataTransfer: { setData: function () { } }, target: element }; listView._currentMode()[name](fakeEventObject); } function generateKeyEventInListView(listView, keyPressed, ctrlDown, altDown, shiftDown) { var fakeEventObject = { ctrlKey: !!ctrlDown, altKey: !!altDown, shiftKey: !!shiftDown, keyCode: keyPressed, preventDefault: function () { }, stopPropagation: function () { }, target: listView.element }; listView._currentMode()["onKeyDown"](fakeEventObject); } function changeListViewFocus(listView, focusedIndex) { listView._selection._setFocused({ type: "item", index: focusedIndex }, false); } // Creates a VDS out of the provided array (data) or // creates a new data array of specified size function createDragDropVDSDataSource(size?, data?, isSynchronous?) { // Populate a data array if (!data) { data = getRawData(size); } // isSynchronous defaults to true if (isSynchronous === undefined) { isSynchronous = true; } // Create the datasource var controller = { directivesForMethod: function (method) { return { callMethodSynchronously: isSynchronous, delay: isSynchronous ? undefined : 0, sendChangeNotifications: true, countBeforeDelta: 0, countAfterDelta: 0, countBeforeOverride: -1, countAfterOverride: -1 }; } }; // Data adapter abilities - requirements for ListView Reordering. var abilities = { itemsFromIndex: true, // If these change, update the public docs!! itemsFromKey: true, remove: true, getCount: true, setNotificationHandler: true, moveBefore: true, moveAfter: true, moveToStart: true, }; return Helper.ItemsManager.createTestDataSource(data, controller, abilities); }; function nabItem(listView, index) { if (listView.itemDataSource._list) { return listView.itemDataSource._list.getItem(index); } else { return listView.itemDataSource.testDataAdapter.getItems()[index]; } } function getEventHandlerCallbacks(listview) { var eventHandler = {}; for (var i = 0; i < ddEventsList.length; i++) { (function () { var eventName = ddEventsList[i]; listview.addEventListener(eventName, function (e) { if (eventHandler[eventName]) { eventHandler[eventName](e, eventName); } }); })(); } return eventHandler; } function failTestIfCalled(e, eventName) { LiveUnit.Assert.fail(eventName + " was called unexpectedly"); } function collectItemData(listView) { var data = [], list = listView.itemDataSource._list || listView.itemDataSource.testDataAdapter.getItems(); for (var i = 0; i < list.length; i++) { data.push(nabItem(listView, i).data.itemInfo); } return data; } function compareData(originalData, actual, expectedSwaps) { LiveUnit.Assert.areEqual(originalData.length, actual.length); for (var i = 0; i < originalData.length; i++) { var swapAtIndex = null; for (var j = 0; j < expectedSwaps.length; j++) { if (expectedSwaps[j].start === i) { swapAtIndex = expectedSwaps[j]; break; } } if (swapAtIndex) { LiveUnit.Assert.areEqual(originalData[i], actual[swapAtIndex.end]); } else { LiveUnit.Assert.areEqual(originalData[i], actual[i]); } } } function compareSelection(listView, expected) { var indicesSelected = listView.selection.getIndices(); LiveUnit.Assert.areEqual(expected.length, indicesSelected.length, "Wrong number of items selected"); for (var i = 0; i < expected.length; i++) { var foundExpected = false; for (var j = 0; j < indicesSelected.length; j++) { if (indicesSelected[j] === expected[i]) { ; foundExpected = true; break; } } LiveUnit.Assert.isTrue(foundExpected, "An item wasn't selected when we expected it to be"); } } function ensureSelectionContiguous(listView) { var indicesSelected = listView.selection.getIndices(); for (var i = 0; i < indicesSelected.length - 1; i++) { LiveUnit.Assert.isTrue((indicesSelected[i] === (indicesSelected[i + 1] - 1))); } } export class ListViewDragDropTest { setUp() { LiveUnit.LoggingCore.logComment("In setup"); testRootEl = document.createElement("div"); testRootEl.className = "file-listview-css"; var newNode = document.createElement("div"); newNode.id = "DragDropTest"; newNode.style.width = "200px"; newNode.style.height = "300px"; testRootEl.appendChild(newNode); document.body.appendChild(testRootEl); } tearDown() { LiveUnit.LoggingCore.logComment("In tearDown"); WinJS.Utilities.disposeSubTree(testRootEl); document.body.removeChild(testRootEl); } testGridLayoutHitTest(complete) { var largeListView = document.createElement("div"); largeListView.id = "GridLayoutHitTest"; testRootEl.appendChild(largeListView); var listView = new WinJS.UI.ListView(largeListView, { itemDataSource: getDataSource(10), itemTemplate: basicRenderer, layout: new WinJS.UI.GridLayout() }); listView.addEventListener("loadingstatechanged", function onloadingstatechanged(e) { // This grid uses 50px X 50px items, with large (400px) margins to the left, top, and bottom of the surface. // The grid should be laid out as such: // Column 0: Contains items 0 - 3, starting at 400, 400 and ending at 400, 600 // Column 1: Contains items 4 - 7, starting at 450, 400 and ending at 450, 600 // Column 2: Contains items 8 and 9, starting at 500, 400 and ending at 500, 500 if (listView.loadingState === "complete") { var layout = listView.layout; // Test everything to the left of column 0 LiveUnit.Assert.areEqual(-1, layout.hitTest(0, 0).insertAfterIndex); LiveUnit.Assert.areEqual(-1, layout.hitTest(100, 100).insertAfterIndex); LiveUnit.Assert.areEqual(-1, layout.hitTest(200, 200).insertAfterIndex); LiveUnit.Assert.areEqual(-1, layout.hitTest(300, 300).insertAfterIndex); LiveUnit.Assert.areEqual(-1, layout.hitTest(300, 800).insertAfterIndex); // Test above column 0 LiveUnit.Assert.areEqual(-1, layout.hitTest(425, 0).insertAfterIndex); LiveUnit.Assert.areEqual(-1, layout.hitTest(425, 100).insertAfterIndex); LiveUnit.Assert.areEqual(-1, layout.hitTest(425, 200).insertAfterIndex); LiveUnit.Assert.areEqual(-1, layout.hitTest(425, 300).insertAfterIndex); LiveUnit.Assert.areEqual(-1, layout.hitTest(425, 400).insertAfterIndex); // Test below column 0 LiveUnit.Assert.areEqual(3, layout.hitTest(425, 600).insertAfterIndex); LiveUnit.Assert.areEqual(3, layout.hitTest(425, 700).insertAfterIndex); LiveUnit.Assert.areEqual(3, layout.hitTest(425, 800).insertAfterIndex); LiveUnit.Assert.areEqual(3, layout.hitTest(425, 900).insertAfterIndex); LiveUnit.Assert.areEqual(3, layout.hitTest(425, 1000).insertAfterIndex); // Test above column 1 LiveUnit.Assert.areEqual(3, layout.hitTest(475, 0).insertAfterIndex); LiveUnit.Assert.areEqual(3, layout.hitTest(475, 100).insertAfterIndex); LiveUnit.Assert.areEqual(3, layout.hitTest(475, 200).insertAfterIndex); LiveUnit.Assert.areEqual(3, layout.hitTest(475, 300).insertAfterIndex); LiveUnit.Assert.areEqual(3, layout.hitTest(475, 400).insertAfterIndex); // Test below column 1 LiveUnit.Assert.areEqual(7, layout.hitTest(475, 600).insertAfterIndex); LiveUnit.Assert.areEqual(7, layout.hitTest(475, 700).insertAfterIndex); LiveUnit.Assert.areEqual(7, layout.hitTest(475, 800).insertAfterIndex); LiveUnit.Assert.areEqual(7, layout.hitTest(475, 900).insertAfterIndex); LiveUnit.Assert.areEqual(7, layout.hitTest(475, 1000).insertAfterIndex); // Test above column 2 LiveUnit.Assert.areEqual(7, layout.hitTest(525, 0).insertAfterIndex); LiveUnit.Assert.areEqual(7, layout.hitTest(525, 100).insertAfterIndex); LiveUnit.Assert.areEqual(7, layout.hitTest(525, 200).insertAfterIndex); LiveUnit.Assert.areEqual(7, layout.hitTest(525, 300).insertAfterIndex); LiveUnit.Assert.areEqual(7, layout.hitTest(525, 400).insertAfterIndex); // Test below column 2 LiveUnit.Assert.isTrue(layout.hitTest(525, 500).insertAfterIndex >= 9); LiveUnit.Assert.isTrue(layout.hitTest(525, 700).insertAfterIndex >= 9); LiveUnit.Assert.isTrue(layout.hitTest(525, 800).insertAfterIndex >= 9); LiveUnit.Assert.isTrue(layout.hitTest(525, 900).insertAfterIndex >= 9); LiveUnit.Assert.isTrue(layout.hitTest(525, 1000).insertAfterIndex >= 9); // Test right of column 2 LiveUnit.Assert.isTrue(layout.hitTest(575, 500).insertAfterIndex >= 9); LiveUnit.Assert.isTrue(layout.hitTest(675, 700).insertAfterIndex >= 9); LiveUnit.Assert.isTrue(layout.hitTest(775, 800).insertAfterIndex >= 9); LiveUnit.Assert.isTrue(layout.hitTest(550, 900).insertAfterIndex >= 9); listView.removeEventListener("loadingstatechanged", onloadingstatechanged); WinJS.Utilities.disposeSubTree(largeListView); testRootEl.removeChild(largeListView); complete(); } }); } } function generate(name, testFunction, itemsCount?, layoutIndependent?) { function generateTest(layout, dataSource, rtl?) { var fullName = name + layout + dataSource + (rtl ? "_rtl" : "_ltr"); ListViewDragDropTest.prototype[fullName] = function (complete) { LiveUnit.LoggingCore.logComment("in " + fullName); if (dataSource === "VDS") { dataSource = createDragDropVDSDataSource(itemsCount, getRawData(itemsCount), true/*sync*/) } else if (dataSource === "BindingList") { dataSource = getDataSource(itemsCount) } var element = document.getElementById("DragDropTest"); element.style.direction = rtl ? "rtl" : "ltr"; var listView = new ListView(element, { itemDataSource: dataSource, itemTemplate: basicRenderer, layout: new WinJS.UI[layout]() }); var ready = false; listView._animationsDisabled = function () { return true; }; // Autoscroll stops dragBetween events from triggering. None of these tests need autoscroll, so we'll disable it here so it doesn't start during a test listView._currentMode()._checkAutoScroll = function () { return; }; listView.addEventListener("loadingstatechanged", function (e) { if (listView.loadingState === "complete" && !ready) { ready = true; testFunction(listView, rtl, complete); } }); }; } generateTest("ListLayout", "BindingList"); if (!layoutIndependent) { generateTest("GridLayout", "BindingList"); generateTest("ListLayout", "VDS", true); generateTest("GridLayout", "VDS", true); } } generate("testDraggableAttribute", function (listView, rtl, complete) { listView.itemsDraggable = false; listView.itemsReorderable = false; LiveUnit.Assert.areEqual(0, listView.element.querySelectorAll("[draggable=true]").length); listView.itemsDraggable = true; LiveUnit.Assert.areEqual(ITEMS_COUNT, listView.element.querySelectorAll("[draggable=true]").length); listView.itemsDraggable = false; LiveUnit.Assert.areEqual(0, listView.element.querySelectorAll("[draggable=true]").length); listView.itemsReorderable = true; LiveUnit.Assert.areEqual(ITEMS_COUNT, listView.element.querySelectorAll("[draggable=true]").length); listView.itemsReorderable = false; LiveUnit.Assert.areEqual(0, listView.element.querySelectorAll("[draggable=true]").length); listView.itemsDraggable = true; listView.itemsReorderable = true; LiveUnit.Assert.areEqual(ITEMS_COUNT, listView.element.querySelectorAll("[draggable=true]").length); listView.itemsReorderable = false; LiveUnit.Assert.areEqual(ITEMS_COUNT, listView.element.querySelectorAll("[draggable=true]").length); listView.itemsDraggable = false; listView.itemsReorderable = true; LiveUnit.Assert.areEqual(ITEMS_COUNT, listView.element.querySelectorAll("[draggable=true]").length); listView.itemsDraggable = true; var tests = [ function () { LiveUnit.Assert.areEqual(ITEMS_COUNT, listView.element.querySelectorAll("[draggable=true]").length); listView.itemDataSource._list.push({ itemInfo: "UndraggableItem", markUndraggable: true }); return true; }, function () { LiveUnit.Assert.areEqual(ITEMS_COUNT, listView.element.querySelectorAll("[draggable=true]").length); listView.itemDataSource._list.push({ itemInfo: "DraggableItem", markUndraggable: false }); return true; }, function () { LiveUnit.Assert.areEqual(ITEMS_COUNT + 1, listView.element.querySelectorAll("[draggable=true]").length); listView.itemsDraggable = false; listView.itemsReorderable = false; LiveUnit.Assert.areEqual(0, listView.element.querySelectorAll("[draggable=true]").length); listView.itemsDraggable = true; LiveUnit.Assert.areEqual(ITEMS_COUNT + 1, listView.element.querySelectorAll("[draggable=true]").length); complete(); } ]; listView.itemDataSource = getDataSource(ITEMS_COUNT); Helper.ListView.runTests(listView, tests); }, null, true); generate("testDragEnterUnpreventedNotReorderable", function (listView, rtl, complete) { // When a ListView's items are reorderable and draggable, dragEnter/leave should be fired, but with dragEnter not handled, // nothing else should be fired. var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = true; listView.itemsReorderable = false; handlers["itemdragend"] = failTestIfCalled; handlers["itemdragbetween"] = failTestIfCalled; handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragdrop"] = failTestIfCalled; var startEventsReceived = 0, enterEventsReceived = 0; handlers["itemdragstart"] = function (e) { startEventsReceived++; LiveUnit.Assert.areEqual(1, startEventsReceived); }; handlers["itemdragenter"] = function (e) { enterEventsReceived++; LiveUnit.Assert.areEqual(1, enterEventsReceived); }; generateEventInElement(listView, "onDragStart", listView.elementFromIndex(0), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(1), rtl); generateEventInElement(listView, "onDragOver", listView.elementFromIndex(1), rtl); generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(1), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(2), rtl); generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(2), rtl); complete(); }, null, true); generate("testDragEnterInReorderableSource", function (listView, rtl, complete) { // When a ListView's items are reorderable and draggable, dragEnter/leave/between should be fired even if we don't handle itemDragEnter var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = true; listView.itemsReorderable = true; handlers["itemdragend"] = failTestIfCalled; handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragdrop"] = failTestIfCalled; var startEventsReceived = 0; handlers["itemdragstart"] = function (e) { startEventsReceived++; LiveUnit.Assert.areEqual(1, startEventsReceived); LiveUnit.Assert.areEqual(1, e.detail.dragInfo.getIndices().length); }; generateEventInElement(listView, "onDragStart", listView.elementFromIndex(0), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(1), rtl); generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(1), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(2), rtl); generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(2), rtl); handlers["itemdragleave"] = function (e) { handlers["itemdragenter"] = function (e) { handlers["itemdragbetween"] = function (e) { handlers["itemdragleave"] = function (e) { complete(); }; WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(1), rtl); }); }; WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragOver", listView.elementFromIndex(1), rtl); }); }; WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(1), rtl); }); }; }, null, true); generate("testDragEnterFromExternalSource", function (listView, rtl, complete) { // Drag from an external source that isn't handled shouldn't raise anything other than enter/leave events var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = true; listView.itemsReorderable = true; handlers["itemdragend"] = failTestIfCalled; handlers["itemdragbetween"] = failTestIfCalled; handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragdrop"] = failTestIfCalled; handlers["itemdragstart"] = failTestIfCalled; generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(1), rtl); generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(1), rtl); generateEventInElement(listView, "onDragOver", listView.elementFromIndex(1), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(2), rtl); generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(2), rtl); handlers["itemdragleave"] = function (e) { handlers["itemdragenter"] = function (e) { complete(); }; WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(1), rtl); }); }; }, null, true); generate("testDragLeave", function (listView, rtl, complete) { // Drag from an external source that isn't handled shouldn't raise anything other than enter/leave events var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = true; listView.itemsReorderable = true; handlers["itemdragend"] = failTestIfCalled; handlers["itemdragbetween"] = failTestIfCalled; handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragdrop"] = failTestIfCalled; handlers["itemdragleave"] = failTestIfCalled; // HTML5's DnD events are noisy, and kind of weird. When the user drags over a listview item, the listview's going to be spammed with // dragEnter/Leave events for the cursor moving inside of each of the item's child regions. The ListView should only fire one itemdragenter // event per unique drag-into-listview-region operation instead of spamming itemdragenter with HTML5's enter events. // Enter/Leave are weird because they fire in an unexpected order: Enter is fired before leave, so in theory a control // could make the mistake of thinking two items are being dragged over at once, or make the mistake of assuming events go in a // leave-then-enter fashion, and clear out a global drag over flag when leave is received. We'll test to make sure the ListView // doesn't fire multiple dragEnter events, and handles dragEnter/leave in any order. generateEventInElement(listView, "onDragStart", listView.elementFromIndex(2), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(2), rtl); handlers["itemdragenter"] = failTestIfCalled; generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(2), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(3), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(4), rtl); generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(3), rtl); handlers["itemdragleave"] = function (e) { handlers["itemdragenter"] = function (e) { handlers["itemdragenter"] = failTestIfCalled; handlers["itemdragleave"] = function (e) { complete(); }; WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(4), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(5), rtl); generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(5), rtl); }); }; WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(4), rtl); }); }; generateEventInElement(listView, "onDragLeave", listView.elementFromIndex(4), rtl); }, null, true); generate("testDragUnselected", function (listView, rtl, complete) { var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = true; listView.itemsReorderable = true; handlers["itemdragbetween"] = failTestIfCalled; handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragdrop"] = failTestIfCalled; handlers["itemdragleave"] = failTestIfCalled; var dragStartCalled = false; handlers["itemdragstart"] = function (e) { var indicesSelected = e.detail.dragInfo.getIndices(); dragStartCalled = true; LiveUnit.Assert.areEqual(1, indicesSelected.length); LiveUnit.Assert.areEqual(2, indicesSelected[0]); }; generateEventInElement(listView, "onDragStart", listView.elementFromIndex(2), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(2), rtl); generateEventInElement(listView, "onDragEnd", listView.elementFromIndex(2), rtl); LiveUnit.Assert.isTrue(dragStartCalled); listView.selection.set([0, 1, 2]); dragStartCalled = false; handlers["itemdragstart"] = function (e) { var indicesSelected = e.detail.dragInfo.getIndices(); dragStartCalled = true; LiveUnit.Assert.areEqual(1, indicesSelected.length); LiveUnit.Assert.areEqual(3, indicesSelected[0]); complete(); }; generateEventInElement(listView, "onDragStart", listView.elementFromIndex(3), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(3), rtl); generateEventInElement(listView, "onDragEnd", listView.elementFromIndex(3), rtl); }, null, true); generate("testDragSelected", function (listView, rtl, complete) { var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = true; listView.itemsReorderable = true; handlers["itemdragbetween"] = failTestIfCalled; handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragdrop"] = failTestIfCalled; handlers["itemdragleave"] = failTestIfCalled; var dragStartCalled = false; handlers["itemdragstart"] = function (e) { var indicesSelected = e.detail.dragInfo.getIndices(); dragStartCalled = true; LiveUnit.Assert.areEqual(1, indicesSelected.length); LiveUnit.Assert.areEqual(2, indicesSelected[0]); }; generateEventInElement(listView, "onDragStart", listView.elementFromIndex(2), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(2), rtl); generateEventInElement(listView, "onDragEnd", listView.elementFromIndex(2), rtl); LiveUnit.Assert.isTrue(dragStartCalled); listView.selection.set([0, 1, 2]); dragStartCalled = false; handlers["itemdragstart"] = function (e) { var indicesSelected = e.detail.dragInfo.getIndices(); dragStartCalled = true; LiveUnit.Assert.areEqual(1, indicesSelected.length); LiveUnit.Assert.areEqual(3, indicesSelected[0]); complete(); }; generateEventInElement(listView, "onDragStart", listView.elementFromIndex(3), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(3), rtl); generateEventInElement(listView, "onDragEnd", listView.elementFromIndex(3), rtl); }, null, true); generate("testDragEnd", function (listView, rtl, complete) { var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = true; listView.itemsReorderable = true; handlers["itemdragbetween"] = failTestIfCalled; handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragdrop"] = failTestIfCalled; handlers["itemdragleave"] = failTestIfCalled; var dragStartCalled = false; handlers["itemdragstart"] = function (e) { var indicesSelected = e.detail.dragInfo.getIndices(); dragStartCalled = true; LiveUnit.Assert.areEqual(1, indicesSelected.length); LiveUnit.Assert.areEqual(2, indicesSelected[0]); }; handlers["itemdragend"] = function (e) { complete(); }; generateEventInElement(listView, "onDragStart", listView.elementFromIndex(2), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(2), rtl); WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragEnd", listView.elementFromIndex(2), rtl); }); }, null, true); generate("testDragBetween", function (listView, rtl, complete) { var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = true; listView.itemsReorderable = true; handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragdrop"] = failTestIfCalled; handlers["itemdragleave"] = failTestIfCalled; var dragStartCalled = false; handlers["itemdragstart"] = function (e) { var indicesSelected = e.detail.dragInfo.getIndices(); dragStartCalled = true; LiveUnit.Assert.areEqual(3, indicesSelected.length); LiveUnit.Assert.areEqual(2, indicesSelected[0]); LiveUnit.Assert.areEqual(3, indicesSelected[1]); LiveUnit.Assert.areEqual(4, indicesSelected[2]); }; listView.selection.set([2, 3, 4]); generateEventInElement(listView, "onDragStart", listView.elementFromIndex(2), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(2), rtl); var indicesToTest = [ { index: 0, topHalf: { expectedInsertAfter: -1, expectedShiftUp: null, expectedShiftDown: listView.elementFromIndex(0) }, bottomHalf: { expectedInsertAfter: 0, expectedShiftUp: listView.elementFromIndex(0), expectedShiftDown: listView.elementFromIndex(1) } }, { index: 1, topHalf: { expectedInsertAfter: 0, expectedShiftUp: listView.elementFromIndex(0), expectedShiftDown: listView.elementFromIndex(1) }, bottomHalf: { expectedInsertAfter: 1, expectedShiftUp: listView.elementFromIndex(1), expectedShiftDown: listView.elementFromIndex(5) } }, { index: 2, topHalf: { expectedInsertAfter: 1, expectedShiftUp: listView.elementFromIndex(1), expectedShiftDown: listView.elementFromIndex(5) }, bottomHalf: { expectedInsertAfter: 2, expectedShiftUp: listView.elementFromIndex(1), expectedShiftDown: listView.elementFromIndex(5) } }, { index: 3, topHalf: { expectedInsertAfter: 2, expectedShiftUp: listView.elementFromIndex(1), expectedShiftDown: listView.elementFromIndex(5) }, bottomHalf: { expectedInsertAfter: 3, expectedShiftUp: listView.elementFromIndex(1), expectedShiftDown: listView.elementFromIndex(5) } }, { index: 4, topHalf: { expectedInsertAfter: 3, expectedShiftUp: listView.elementFromIndex(1), expectedShiftDown: listView.elementFromIndex(5) }, bottomHalf: { expectedInsertAfter: 4, expectedShiftUp: listView.elementFromIndex(1), expectedShiftDown: listView.elementFromIndex(5) } }, { index: 5, topHalf: { expectedInsertAfter: 4, expectedShiftUp: listView.elementFromIndex(1), expectedShiftDown: listView.elementFromIndex(5) }, bottomHalf: { expectedInsertAfter: 5, expectedShiftUp: listView.elementFromIndex(5), expectedShiftDown: listView.elementFromIndex(6) } }, { index: ITEMS_COUNT - 1, topHalf: { expectedInsertAfter: ITEMS_COUNT - 2, expectedShiftUp: listView.elementFromIndex(ITEMS_COUNT - 2), expectedShiftDown: listView.elementFromIndex(ITEMS_COUNT - 1) }, bottomHalf: { expectedInsertAfter: ITEMS_COUNT - 1, expectedShiftUp: listView.elementFromIndex(ITEMS_COUNT - 1), expectedShiftDown: null } }, ]; function getCursorOffsetInTopHalfOfItem(element) { return { x: element.offsetWidth / 2, y: element.offsetHeight / 4 }; } function getCursorOffsetInBottomHalfOfItem(element) { return { x: element.offsetWidth / 2, y: element.offsetHeight / 4 * 3 }; } function unsetTransforms(testSet) { if (testSet.topHalf.expectedShiftUp) { testSet.topHalf.expectedShiftUp.parentNode.parentNode.style[WinJS.Utilities._browserStyleEquivalents["transform"].scriptName] = ""; } if (testSet.topHalf.expectedShiftDown) { testSet.topHalf.expectedShiftDown.parentNode.parentNode.style[WinJS.Utilities._browserStyleEquivalents["transform"].scriptName] = ""; } if (testSet.bottomHalf.expectedShiftUp) { testSet.bottomHalf.expectedShiftUp.parentNode.parentNode.style[WinJS.Utilities._browserStyleEquivalents["transform"].scriptName] = ""; } if (testSet.bottomHalf.expectedShiftDown) { testSet.bottomHalf.expectedShiftDown.parentNode.parentNode.style[WinJS.Utilities._browserStyleEquivalents["transform"].scriptName] = ""; } } function getTransformInfo(element) { var badTransformPoint = { x: 0, y: 0 }; var transform = element.style[WinJS.Utilities._browserStyleEquivalents["transform"].scriptName]; if (!transform || transform === "" || transform.indexOf("translate") === -1) { return badTransformPoint; } var searchString = "translate("; var si = transform.indexOf(searchString) + searchString.length; var ei = transform.indexOf(")", si); var translations = transform.substring(si, ei).split(","); if (translations.length !== 2) { return badTransformPoint; } return { x: parseInt(translations[0], 10), y: parseInt(translations[1], 10) }; } function verifyShiftsAreCorrect(testSetHalf) { if (testSetHalf.expectedShiftUp) { var translation = getTransformInfo(testSetHalf.expectedShiftUp.parentNode.parentNode); LiveUnit.Assert.isTrue(translation.y < 0) } if (testSetHalf.expectedShiftDown) { var translation = getTransformInfo(testSetHalf.expectedShiftDown.parentNode.parentNode); LiveUnit.Assert.isTrue(translation.y > 0) } } var testedBefore = false, testedAfter = false, lastOverElement = listView.elementFromIndex(2), currentTest = 0; function testNextSet() { if (currentTest === indicesToTest.length) { complete(); } else { WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragLeave", lastOverElement, rtl); (function () { var currentSet = indicesToTest[currentTest++]; lastOverElement = listView.elementFromIndex(currentSet.index); unsetTransforms(currentSet); generateEventInElement(listView, "onDragEnter", lastOverElement, rtl); handlers["itemdragbetween"] = function (e) { LiveUnit.Assert.areEqual(currentSet.topHalf.expectedInsertAfter, e.detail.insertAfterIndex); WinJS.Utilities._setImmediate(function () { verifyShiftsAreCorrect(currentSet.topHalf); unsetTransforms(currentSet); handlers["itemdragbetween"] = function (e) { LiveUnit.Assert.areEqual(currentSet.bottomHalf.expectedInsertAfter, e.detail.insertAfterIndex); WinJS.Utilities._setImmediate(function () { verifyShiftsAreCorrect(currentSet.bottomHalf); unsetTransforms(currentSet); // We need to reset after each test by moving to 0, 0, since items that share drop targets (eg, between the bottom half of 0 // and the top half of 1) won't fire duplicate dragBetween events. Calling this top half 0 at the end of each set makes sure // the next set can run properly. handlers["itemdragbetween"] = function (e) { WinJS.Utilities._setImmediate(function () { testNextSet(); }); }; generateEventInElement(listView, "onDragLeave", lastOverElement, rtl); lastOverElement = listView.elementFromIndex(0); generateEventInElement(listView, "onDragEnter", lastOverElement, rtl, getCursorOffsetInTopHalfOfItem(lastOverElement)); WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragOver", lastOverElement, rtl, getCursorOffsetInTopHalfOfItem(lastOverElement)); }); }); }; generateEventInElement(listView, "onDragOver", lastOverElement, rtl, getCursorOffsetInBottomHalfOfItem(lastOverElement)); }); }; WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragOver", lastOverElement, rtl, getCursorOffsetInTopHalfOfItem(lastOverElement)); }); })(); }); } } testNextSet(); }); generate("testDisabledDragBetween", function (listView, rtl, complete) { var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = true; listView.itemsReorderable = true; handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragdrop"] = failTestIfCalled; handlers["itemdragleave"] = failTestIfCalled; var dragStartCalled = false; handlers["itemdragstart"] = function (e) { var indicesSelected = e.detail.dragInfo.getIndices(); dragStartCalled = true; LiveUnit.Assert.areEqual(1, indicesSelected.length); LiveUnit.Assert.areEqual(0, indicesSelected[0]); }; generateEventInElement(listView, "onDragStart", listView.elementFromIndex(0), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(0), rtl); var element2 = listView.elementFromIndex(2); var cursorOffset = { x: element2.offsetWidth / 2, y: element2.offsetHeight / 4 }; handlers["itemdragbetween"] = function (e) { LiveUnit.Assert.areEqual(1, e.detail.insertAfterIndex); listView._layout.dragOver = function () { LiveUnit.Assert.fail("Drag over shouldn't have been called"); }; var dragLeaveCalled = false; listView._layout.dragLeave = function () { dragLeaveCalled = true; }; e.preventDefault(); handlers["itemdragend"] = function (e) { complete(); }; WinJS.Utilities._setImmediate(function () { LiveUnit.Assert.isTrue(dragLeaveCalled); generateEventInElement(listView, "onDrop", element2, rtl); WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragEnd", element2, rtl); }); }); }; generateEventInElement(listView, "onDragOver", element2, rtl, cursorOffset); }, null, true); generate("testReorderOutOfBounds", function (listView, rtl, complete) { var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = true; listView.itemsReorderable = true; // TODO: Uncomment this line when WinBlue 99641 is fixed //handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragleave"] = failTestIfCalled; var dragStartCallCount = 0, dragDropCallCount = 0; handlers["itemdragstart"] = function (e) { var indicesSelected = e.detail.dragInfo.getIndices(); dragStartCallCount++; LiveUnit.Assert.areEqual(1, indicesSelected.length); LiveUnit.Assert.areEqual(0, indicesSelected[0]); }; handlers["itemdragdrop"] = function (e) { dragDropCallCount++; }; var item0 = listView.elementFromIndex(0); var dataAt0 = nabItem(listView, 0); var dataAt1 = nabItem(listView, 1); var dataAt2 = nabItem(listView, 2); generateEventInElement(listView, "onDragStart", listView.elementFromIndex(0), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(0), rtl); var cursorOffset = { x: item0.offsetWidth / 2, y: listView._viewport.offsetHeight - 1 }; handlers["itemdragbetween"] = function (e) { LiveUnit.Assert.areEqual(2, e.detail.insertAfterIndex); WinJS.Utilities._setImmediate(function () { function onLoadingStateChanged() { if (listView.loadingState === "complete") { LiveUnit.Assert.areEqual(dragDropCallCount, 1); LiveUnit.Assert.areEqual(dragStartCallCount, 1); listView.removeEventListener("loadingstatechanged", onLoadingStateChanged); LiveUnit.Assert.areEqual(dataAt1, nabItem(listView, 0)); LiveUnit.Assert.areEqual(dataAt2, nabItem(listView, 1)); LiveUnit.Assert.areEqual(dataAt0, nabItem(listView, 2)); complete(); } } listView.addEventListener("loadingstatechanged", onLoadingStateChanged); generateEventInElement(listView, "onDrop", listView._viewport, rtl, cursorOffset); WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragEnd", listView._viewport, rtl, cursorOffset); }); }); }; generateEventInElement(listView, "onDragOver", listView._viewport, rtl, cursorOffset); }, 3); generate("testSelectionAfterReorder", function (listView, rtl, complete) { // This test verifies that selection via a range isn't lost after a reorder. var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = true; listView.itemsReorderable = true; // Set selection listView.selection.set({ firstIndex: 3, lastIndex: 5 }); var dragStartCalled = false; handlers["itemdragstart"] = function (e) { var indicesSelected = e.detail.dragInfo.getIndices(); dragStartCalled = true; LiveUnit.Assert.areEqual(3, indicesSelected.length); LiveUnit.Assert.areEqual(3, indicesSelected[0]); LiveUnit.Assert.areEqual(4, indicesSelected[1]); LiveUnit.Assert.areEqual(5, indicesSelected[2]); }; var dataAt3 = nabItem(listView, 3); var dataAt4 = nabItem(listView, 4); var dataAt5 = nabItem(listView, 5); generateEventInElement(listView, "onDragStart", listView.elementFromIndex(3), rtl); generateEventInElement(listView, "onDragEnter", listView.elementFromIndex(3), rtl); var targetElement = listView.elementFromIndex(6); // Drop the items below target element. var cursorOffset = { x: targetElement.offsetWidth / 2, y: 3 * targetElement.offsetHeight / 4 }; handlers["itemdragbetween"] = function (e) { LiveUnit.Assert.areEqual(6, e.detail.insertAfterIndex); WinJS.Utilities._setImmediate(function () { function onLoadingStateChanged() { if (listView.loadingState === "complete") { listView.removeEventListener("loadingstatechanged", onLoadingStateChanged); LiveUnit.Assert.areEqual(dataAt3, nabItem(listView, 4)); LiveUnit.Assert.areEqual(dataAt4, nabItem(listView, 5)); LiveUnit.Assert.areEqual(dataAt5, nabItem(listView, 6)); // Test selection after reorder var selection = listView.selection; LiveUnit.Assert.isTrue(selection.getIndices().length === 3, "Selection should contain these 3 items"); LiveUnit.Assert.isTrue(selection._isIncluded(4)); LiveUnit.Assert.isTrue(selection._isIncluded(5)); LiveUnit.Assert.isTrue(selection._isIncluded(6)); complete(); } } listView.addEventListener("loadingstatechanged", onLoadingStateChanged); generateEventInElement(listView, "onDrop", targetElement, rtl, cursorOffset); WinJS.Utilities._setImmediate(function () { generateEventInElement(listView, "onDragEnd", targetElement, rtl, cursorOffset); }); }); }; generateEventInElement(listView, "onDragOver", targetElement, rtl, cursorOffset); }); generate("testUnselectedKeyboardReorder", function (listView, rtl, complete) { var adjustedKeys = getAdjustedKeys(rtl); listView.itemsReorderable = true; var originalListData = collectItemData(listView); listView.selection.set([0, 2]); changeListViewFocus(listView, 1); var endIndex = originalListData.length - 1; var tests = [ function () { compareSelection(listView, [0, 2]); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, [{ start: 0, end: 1 }, { start: 1, end: 0 }]); compareSelection(listView, [1, 2]); LiveUnit.Assert.areEqual(0, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); generateKeyEventInListView(listView, adjustedKeys.left, false, true, true); }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, [{ start: 0, end: 1 }, { start: 1, end: 0 }]); compareSelection(listView, [1, 2]); LiveUnit.Assert.areEqual(0, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.down, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, []); compareSelection(listView, [0, 2]); LiveUnit.Assert.areEqual(1, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.right, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, [{ start: 2, end: 1 }, { start: 1, end: 2 }]); compareSelection(listView, [0, 1]); LiveUnit.Assert.areEqual(2, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.left, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, []); compareSelection(listView, [0, 2]); LiveUnit.Assert.areEqual(1, listView._selection._getFocused().index); changeListViewFocus(listView, endIndex); generateKeyEventInListView(listView, adjustedKeys.down, false, true, true); generateKeyEventInListView(listView, adjustedKeys.right, false, true, true); }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, []); compareSelection(listView, [0, 2]); LiveUnit.Assert.areEqual(endIndex, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.left, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, [{ start: endIndex, end: endIndex - 1 }, { start: endIndex - 1, end: endIndex }]); compareSelection(listView, [0, 2]); LiveUnit.Assert.areEqual(endIndex - 1, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, [{ start: endIndex, end: endIndex - 2 }, { start: endIndex - 1, end: endIndex }, { start: endIndex - 2, end: endIndex - 1 }]); compareSelection(listView, [0, 2]); LiveUnit.Assert.areEqual(endIndex - 2, listView._selection._getFocused().index); complete(); } ]; Helper.ListView.runTests(listView, tests); }); generate("testUnselectedKeyboardReorderNonDraggable", function (listView, rtl, complete) { var adjustedKeys = getAdjustedKeys(rtl); listView.itemsReorderable = true; var originalListData = collectItemData(listView); changeListViewFocus(listView, 1); var tests = [ function () { WinJS.Utilities.addClass(listView.elementFromIndex(1), WinJS.UI._nonDraggableClass); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); }, function () { setTimeout(function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, []); complete(); }, 50) } ]; Helper.ListView.runTests(listView, tests); }); generate("testSingleSelectionKeyboardReorder", function (listView, rtl, complete) { var adjustedKeys = getAdjustedKeys(rtl); listView.itemsReorderable = true; var originalListData = collectItemData(listView); listView.selection.set([1]); changeListViewFocus(listView, 1); var endIndex = originalListData.length - 1; var tests = [ function () { compareSelection(listView, [1]); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, [{ start: 0, end: 1 }, { start: 1, end: 0 }]); compareSelection(listView, [0]); LiveUnit.Assert.areEqual(0, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); generateKeyEventInListView(listView, adjustedKeys.left, false, true, true); }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, [{ start: 0, end: 1 }, { start: 1, end: 0 }]); compareSelection(listView, [0]); LiveUnit.Assert.areEqual(0, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.down, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, []); compareSelection(listView, [1]); LiveUnit.Assert.areEqual(1, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.right, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, [{ start: 2, end: 1 }, { start: 1, end: 2 }]); compareSelection(listView, [2]); LiveUnit.Assert.areEqual(2, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.left, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, []); compareSelection(listView, [1]); LiveUnit.Assert.areEqual(1, listView._selection._getFocused().index); changeListViewFocus(listView, endIndex); listView.selection.set([endIndex]); generateKeyEventInListView(listView, adjustedKeys.down, false, true, true); generateKeyEventInListView(listView, adjustedKeys.right, false, true, true); }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, []); compareSelection(listView, [endIndex]); LiveUnit.Assert.areEqual(endIndex, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.left, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, [{ start: endIndex, end: endIndex - 1 }, { start: endIndex - 1, end: endIndex }]); compareSelection(listView, [endIndex - 1]); LiveUnit.Assert.areEqual(endIndex - 1, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); return true; }, function () { var currentListData = collectItemData(listView); compareData(originalListData, currentListData, [{ start: endIndex, end: endIndex - 2 }, { start: endIndex - 1, end: endIndex }, { start: endIndex - 2, end: endIndex - 1 }]); compareSelection(listView, [endIndex - 2]); LiveUnit.Assert.areEqual(endIndex - 2, listView._selection._getFocused().index); complete(); } ]; Helper.ListView.runTests(listView, tests); }); generate("testContiguousSelectionReorder", function (listView, rtl, complete) { var adjustedKeys = getAdjustedKeys(rtl); listView.itemsReorderable = true; var originalListData = collectItemData(listView); var endIndex = originalListData.length - 1; var firstSelectionBlock = [1, 2, 3]; var lastSelectionBlock = [endIndex - 1, endIndex - 2, endIndex - 3]; function offsetSelectionBlock(originalBlock, offset) { var offsetBlock = []; for (var i = 0; i < originalBlock.length; i++) { offsetBlock.push(originalBlock[i] + offset); } return offsetBlock; } function offsetDataBlock(originalDataLocations, offset, extraData) { var offsetBlock = []; for (var i = 0; i < originalDataLocations.length; i++) { offsetBlock.push({ start: originalDataLocations[i], end: originalDataLocations[i] + offset }); } for (var i = 0; i < extraData.length; i++) { offsetBlock.push({ start: extraData[i].start, end: extraData[i].end }); } return offsetBlock; } var tests = []; for (var i = 0; i < firstSelectionBlock.length; i++) { (function () { var focusedIndex = firstSelectionBlock[i]; tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, firstSelectionBlock); changeListViewFocus(listView, focusedIndex); compareData(originalListData, currentListData, []); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); return true; }); tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, offsetSelectionBlock(firstSelectionBlock, -1)); compareData(originalListData, currentListData, offsetDataBlock(firstSelectionBlock, -1, [{ start: 0, end: 3 }])); LiveUnit.Assert.areEqual(focusedIndex - 1, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); generateKeyEventInListView(listView, adjustedKeys.left, false, true, true); }); tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, offsetSelectionBlock(firstSelectionBlock, -1)); compareData(originalListData, currentListData, offsetDataBlock(firstSelectionBlock, -1, [{ start: 0, end: 3 }])); LiveUnit.Assert.areEqual(focusedIndex - 1, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.down, false, true, true); return true; }); tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, firstSelectionBlock); compareData(originalListData, currentListData, []); LiveUnit.Assert.areEqual(focusedIndex, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.right, false, true, true); return true; }); tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, offsetSelectionBlock(firstSelectionBlock, 1)); compareData(originalListData, currentListData, offsetDataBlock(firstSelectionBlock, 1, [{ start: 4, end: 1 }])); LiveUnit.Assert.areEqual(focusedIndex + 1, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.left, false, true, true); return true; }); tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, firstSelectionBlock); compareData(originalListData, currentListData, []); LiveUnit.Assert.areEqual(focusedIndex, listView._selection._getFocused().index); }); })(); } tests.push(function () { listView.selection.set(lastSelectionBlock); }); for (var i = 0; i < lastSelectionBlock.length; i++) { (function () { var focusedIndex = lastSelectionBlock[i]; tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, lastSelectionBlock); changeListViewFocus(listView, focusedIndex); compareData(originalListData, currentListData, []); generateKeyEventInListView(listView, adjustedKeys.down, false, true, true); return true; }); tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, offsetSelectionBlock(lastSelectionBlock, 1)); compareData(originalListData, currentListData, offsetDataBlock(lastSelectionBlock, 1, [{ start: endIndex, end: endIndex - 3 }])); LiveUnit.Assert.areEqual(focusedIndex + 1, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.down, false, true, true); generateKeyEventInListView(listView, adjustedKeys.right, false, true, true); }); tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, offsetSelectionBlock(lastSelectionBlock, 1)); compareData(originalListData, currentListData, offsetDataBlock(lastSelectionBlock, 1, [{ start: endIndex, end: endIndex - 3 }])); LiveUnit.Assert.areEqual(focusedIndex + 1, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); return true; }); tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, lastSelectionBlock); compareData(originalListData, currentListData, []); LiveUnit.Assert.areEqual(focusedIndex, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.left, false, true, true); return true; }); tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, offsetSelectionBlock(lastSelectionBlock, -1)); compareData(originalListData, currentListData, offsetDataBlock(lastSelectionBlock, -1, [{ start: endIndex - 4, end: endIndex - 1 }])); LiveUnit.Assert.areEqual(focusedIndex - 1, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.right, false, true, true); return true; }); tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, lastSelectionBlock); compareData(originalListData, currentListData, []); LiveUnit.Assert.areEqual(focusedIndex, listView._selection._getFocused().index); }); })(); } listView.selection.set(firstSelectionBlock); tests.push(function () { complete(); }); Helper.ListView.runTests(listView, tests); }); generate("testUncontiguousSelectionReorder", function (listView, rtl, complete) { listView.itemsReorderable = true; var adjustedKeys = getAdjustedKeys(rtl); var originalListData; var listSize = ITEMS_COUNT; var selectionOffsetsToTest = [ [0, 2, 3, 5], [0, 1, 3, 5, 6] ]; var tests = []; function resetListView(selection) { listView.itemDataSource = getDataSource(listSize); listView.selection.set(selection); originalListData = collectItemData(listView); } function inSelection(selection, index) { for (var l = 0; l < selection.length; l++) { if (selection[l] === index) { return true; } } return false; } function calcExpectedListState(key, originalSelection, focusedIndex, unselectedItemData, contiguousSelectedData) { var insertMethod = "atStart", insertIndex = focusedIndex; if (key === adjustedKeys.up || key === adjustedKeys.left) { do { insertIndex--; } while (insertIndex >= 0 && inSelection(originalSelection, insertIndex)); if (insertIndex >= 0) { insertMethod = "beforeIndex"; } } else { insertMethod = "afterIndex"; do { insertIndex++; } while (insertIndex < originalListData.length && inSelection(originalSelection, insertIndex)); if (insertIndex >= originalListData.length) { insertMethod = "atEnd"; } } var insertIndexInUnselectedArray = -1; if (insertIndex > -1 && insertIndex < originalListData.length) { for (var k = 0; k < unselectedItemData.length; k++) { if (unselectedItemData[k] === originalListData[insertIndex]) { insertIndexInUnselectedArray = k; break; } } LiveUnit.Assert.isTrue(insertIndexInUnselectedArray !== -1); } var expectedFinalList = []; var expectedFinalSelection = []; function addSelectedData() { for (var k = 0; k < contiguousSelectedData.length; k++) { expectedFinalSelection.push(expectedFinalList.length); expectedFinalList.push(contiguousSelectedData[k]); } } function addUnselectedData(startIndex, count) { for (var k = 0; k < count; k++) { expectedFinalList.push(unselectedItemData[startIndex + k]); } } if (insertMethod === "atStart") { addSelectedData(); addUnselectedData(0, unselectedItemData.length); } else if (insertMethod === "atEnd") { addUnselectedData(0, unselectedItemData.length); addSelectedData(); } else if (insertMethod === "beforeIndex") { addUnselectedData(0, insertIndexInUnselectedArray); addSelectedData(); addUnselectedData(insertIndexInUnselectedArray, unselectedItemData.length - insertIndexInUnselectedArray); } else { addUnselectedData(0, insertIndexInUnselectedArray + 1); addSelectedData(); addUnselectedData(insertIndexInUnselectedArray + 1, unselectedItemData.length - insertIndexInUnselectedArray - 1); } LiveUnit.Assert.areEqual(originalListData.length, expectedFinalList.length); var mappedMoves = [], newFocusedIndex = -1; for (var k = 0; k < originalListData.length; k++) { for (var l = 0; l < expectedFinalList.length; l++) { if (originalListData[k] === expectedFinalList[l]) { if (k !== l) { mappedMoves.push({ start: k, end: l }); } if (focusedIndex === k) { newFocusedIndex = l; } break; } } } return { selection: expectedFinalSelection, moves: mappedMoves, focus: newFocusedIndex }; } function createSingleKeyTest(key, originalSelection, focusedIndex) { tests.push(function () { resetListView(originalSelection); return true; }); tests.push(function () { var currentListData = collectItemData(listView); compareSelection(listView, originalSelection); compareData(originalListData, currentListData, []); changeListViewFocus(listView, focusedIndex); generateKeyEventInListView(listView, key, false, true, true); return true; }); tests.push(function () { var currentListData = collectItemData(listView); ensureSelectionContiguous(listView); var contiguousSelectedData = [], unselectedItemData = []; for (var k = 0; k < originalListData.length; k++) { if (inSelection(originalSelection, k)) { contiguousSelectedData.push(originalListData[k]); } else { unselectedItemData.push(originalListData[k]); } } var expectedState = calcExpectedListState(key, originalSelection, focusedIndex, unselectedItemData, contiguousSelectedData); compareSelection(listView, expectedState.selection); compareData(originalListData, currentListData, expectedState.moves); LiveUnit.Assert.areEqual(expectedState.focus, listView._selection._getFocused().index); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); }); } function createTestsForOffsetsFromIndex(index, offsets) { var originalSelection = []; for (var j = 0; j < offsets.length; j++) { originalSelection.push(index + offsets[j]); } for (var j = 0; j < offsets.length; j++) { createSingleKeyTest(adjustedKeys.up, originalSelection, originalSelection[j]); createSingleKeyTest(adjustedKeys.left, originalSelection, originalSelection[j]); createSingleKeyTest(adjustedKeys.down, originalSelection, originalSelection[j]); createSingleKeyTest(adjustedKeys.right, originalSelection, originalSelection[j]); } } function createTestsForOffsets(offsets) { // Test that everything works as expected when one item in the selection is at the beginning, // when the selection has neither an item at the beginning nor end, // and when the selection has an item at the end. Every index will have every arrow key tested against it. createTestsForOffsetsFromIndex(0, offsets); createTestsForOffsetsFromIndex(1, offsets); var offsetsRange = offsets[offsets.length - 1] - offsets[0]; createTestsForOffsetsFromIndex(listSize - offsetsRange - 1, offsets); } for (var i = 0; i < selectionOffsetsToTest.length; i++) { createTestsForOffsets(selectionOffsetsToTest[i]); } tests.push(function () { complete(); }); Helper.ListView.runTests(listView, tests); }); generate("testKeyboardWhenNotReorderable", function (listView, rtl, complete) { var adjustedKeys = getAdjustedKeys(rtl); var handlers = getEventHandlerCallbacks(listView); listView.itemsDraggable = false; listView.itemsReorderable = false; handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragdrop"] = failTestIfCalled; handlers["itemdragleave"] = failTestIfCalled; handlers["itemdragstart"] = failTestIfCalled; handlers["itemdragbetween"] = failTestIfCalled; listView.selection.set([1]); changeListViewFocus(listView, 1); var tests = [ function () { compareSelection(listView, [1]); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); }, function () { compareSelection(listView, [1]); generateKeyEventInListView(listView, adjustedKeys.down, false, true, true); }, function () { compareSelection(listView, [1]); generateKeyEventInListView(listView, adjustedKeys.left, false, true, true); }, function () { compareSelection(listView, [1]); generateKeyEventInListView(listView, adjustedKeys.right, false, true, true); }, function () { complete(); } ]; Helper.ListView.runTests(listView, tests); }, null, true); generate("testCanceledKeyboardReorder", function (listView, rtl, complete) { var adjustedKeys = getAdjustedKeys(rtl); var handlers = getEventHandlerCallbacks(listView); listView.itemsReorderable = true; handlers["itemdragchanged"] = failTestIfCalled; handlers["itemdragdrop"] = failTestIfCalled; handlers["itemdragleave"] = failTestIfCalled; handlers["itemdragstart"] = failTestIfCalled; var dragBetweensFired = 0; handlers["itemdragbetween"] = function (eventObject) { dragBetweensFired++; eventObject.preventDefault(); }; listView._currentMode()._reorderItems = function (dropIndex, reorderedItems, reorderingUnselectedItem, useMoveBefore) { LiveUnit.Assert.fail("ListView attempted to reorder items when the reorder was canceled"); } listView.selection.set([1]); changeListViewFocus(listView, 1); var tests = [ function () { compareSelection(listView, [1]); generateKeyEventInListView(listView, adjustedKeys.up, false, true, true); }, function () { LiveUnit.Assert.areEqual(dragBetweensFired, 1); compareSelection(listView, [1]); generateKeyEventInListView(listView, adjustedKeys.down, false, true, true); }, function () { LiveUnit.Assert.areEqual(dragBetweensFired, 2); compareSelection(listView, [1]); generateKeyEventInListView(listView, adjustedKeys.left, false, true, true); }, function () { LiveUnit.Assert.areEqual(dragBetweensFired, 3); compareSelection(listView, [1]); generateKeyEventInListView(listView, adjustedKeys.right, false, true, true); }, function () { LiveUnit.Assert.areEqual(dragBetweensFired, 4); complete(); } ]; Helper.ListView.runTests(listView, tests); }, null, true); } if (!WinJS.Utilities.isPhone) { LiveUnit.registerTestClass("WinJSTests.ListViewDragDropTest"); }