// 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; function createDataSource(async?) { return createDataSourceImpl(async, 26); } function createDataSourceImpl(async, count) { var dataSource = { itemsFromKey: function (key, countBefore, countAfter) { var index = key.charCodeAt(0) - "a".charCodeAt(0); return this.itemsFromIndex(index, countBefore, countAfter); }, itemsFromIndex: function (index, countBefore, countAfter) { return new WinJS.Promise(function (complete, error) { if (index >= 0 && index < count) { var startIndex = Math.max(0, index - countBefore), endIndex = Math.min(index + countAfter, count - 1), size = endIndex - startIndex + 1; var items = []; for (var i = startIndex; i < startIndex + size; i++) { items.push({ key: String.fromCharCode("a".charCodeAt(0) + i), data: { letter: String.fromCharCode("A".charCodeAt(0) + i) } }); } var retVal = { items: items, offset: index - startIndex, totalCount: count, absoluteIndex: index }; if (async) { WinJS.Promise.timeout(50).then(function () { complete(retVal); }); } else { complete(retVal); } } else { complete({}); } }); }, getCount: function () { return WinJS.Promise.wrap(count); } }; return new WinJS.UI.ListDataSource(dataSource); } function compareArrays(expected, actual) { LiveUnit.Assert.areEqual(expected.length, actual.length); for (var i = 0; i < expected.length; i++) { LiveUnit.Assert.areEqual(JSON.stringify(expected[i]), JSON.stringify(actual[i])); } } function compareRanges(expected, actual) { compareArrays(expected.map(function (value) { return { firstIndex: value.firstIndex, lastIndex: value.lastIndex, firstKey: String.fromCharCode("a".charCodeAt(0) + value.firstIndex), lastKey: String.fromCharCode("a".charCodeAt(0) + value.lastIndex) }; }), actual); } function compareIndices(expected, actual) { compareArrays(expected, actual.map(function (value) { return { firstIndex: value.firstIndex, lastIndex: value.lastIndex }; })); } function checkTileSelection(listview, index, selected) { var tile = listview.elementFromIndex(index).parentNode; LiveUnit.Assert.areEqual(selected, WinJS.Utilities.hasClass(tile, WinJS.UI._selectedClass)); } function realizedCount(listView) { return listView._element.querySelectorAll(".win-item").length; } function createListBinding(retainedItems, dataSource) { if (dataSource) { var listBinding = dataSource.createListBinding({ beginNotifications: function () { }, endNotifications: function () { }, indexChanged: function (item, newIndex, oldIndex) { }, itemAvailable: function (item, placeholder) { }, countChanged: function (newCount, oldCount) { }, changed: function (newItem, oldItem) { }, removed: function (itemHandle, mirage) { }, inserted: function (itemPromise, previousHandle, nextHandle) { }, moved: function (itemPromise, previousHandle, nextHandle) { }, reload: function () { } }); traceRefCounts(retainedItems, listBinding); return listBinding; } else { return null; } } function traceRefCounts(retainedItems, listBinding) { function incrementRefCount(handle) { var record = retainedItems[handle]; if (!record) { retainedItems[handle] = { count: 0 }; record = retainedItems[handle]; } record.count++; } function decrementRefCount(handle) { var record = retainedItems[handle]; if (record && --record.count === 0) { delete retainedItems[handle]; } } var retainItem = listBinding._retainItem; if (retainItem) { listBinding._retainItem = function (slot, listenerID) { retainItem.apply(this, arguments); incrementRefCount(slot.handle); }; } var releaseItem = listBinding._releaseItem; if (releaseItem) { listBinding._releaseItem = function (handle) { releaseItem.apply(this, arguments); decrementRefCount(handle); }; } var addRef = listBinding._addRef; if (addRef) { listBinding._addRef = function (item, index) { addRef.apply(this, arguments); if (item.index === index) { incrementRefCount(item.handle); } }; } var release = listBinding._release; if (release) { listBinding._release = function (item, index) { release.apply(this, arguments); decrementRefCount(item.handle); }; } } function validate(selection, retainedItems, realizedItems?) { var realizedHandles = {} if (realizedItems === +realizedItems) { var listBinding = selection._getListBinding(); for (var i = 0; i < realizedItems; i++) { var handle = listBinding.fromIndex(i).handle; realizedHandles[handle] = true; } } var selectionHandles = []; for (var i = 0; i < selection._ranges.length; i++) { var range = selection._ranges[i]; LiveUnit.Assert.isTrue(range.firstIndex <= range.lastIndex); LiveUnit.Assert.isTrue(!i || selection._ranges[i - 1].lastIndex < range.firstIndex); LiveUnit.Assert.isTrue(range.firstPromise.index === range.firstIndex); LiveUnit.Assert.isTrue(range.lastPromise.index === range.lastIndex); selectionHandles.push(range.firstPromise.handle); selectionHandles.push(range.lastPromise.handle); } if (retainedItems) { var retainedHandles = []; Object.keys(retainedItems).forEach(function (handle) { var itemRecord = retainedItems[handle], refCount = itemRecord.count; if (realizedHandles[handle]) { refCount--; } LiveUnit.Assert.isTrue(refCount >= 0 && refCount <= 2); for (var n = 0; n < refCount; n++) { retainedHandles.push(handle); } }) selectionHandles.sort(); retainedHandles.sort(); compareArrays(selectionHandles, retainedHandles); } } function createSite(dataSource, retainedItems) { return { _itemsManager: { dataSource: dataSource, _listBinding: createListBinding(retainedItems, dataSource) }, _itemsCount: function () { return dataSource.getCount(); } }; } var selectionUpdated = 0, selectionChanging = 0, selectionChanged = 0; function setupSelectionManager(dataSource, retainedItems) { var selectionManager = new WinJS.UI._SelectionManager({ _versionManager: new WinJS.UI._VersionManager(), _selectionAllowed: function () { return true; }, _multiSelection: function () { return this._selectionMode === "multi"; }, _selectionMode: "multi", _updateSelection: function () { selectionUpdated++; }, _itemsManager: { dataSource: dataSource, _listBinding: createListBinding(retainedItems, dataSource) }, _itemsCount: function () { return dataSource.getCount(); } }); selectionManager._fireSelectionChanging = function () { selectionChanging++; return WinJS.Promise.wrap(true); }; selectionManager._fireSelectionChanged = function () { selectionChanged++; }; return selectionManager; } function editsOutsideOfRealizedRange(layoutName, dataSource, splice, move) { return new WinJS.Promise(function (complete) { var element = document.createElement("div"); element.style.width = "300px"; element.style.height = "300px"; document.body.appendChild(element); var listView = new ListView(element, { layout: new WinJS.UI[layoutName](), itemDataSource: dataSource, itemTemplate: function (itemPromise) { return itemPromise.then(function (item) { var element = document.createElement("div"); element.textContent = item.data.label; element.style.width = element.style.height = "100px"; return element; }); } }); var retainedItems = {}; traceRefCounts(retainedItems, listView._itemsManager._listBinding); listView.selection.set([{ firstIndex: 0, lastIndex: 2 }, 10, { firstIndex: 97, lastIndex: 99 }]); function checkSelection(expected) { listView.selection.getItems().then(function (items) { var labels = []; for (var i = 0; i < items.length; ++i) { labels.push(items[i].data['label']); } Helper.ListView.elementsEqual(expected, labels); }); } Helper.ListView.waitForReady(listView)().then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); checkSelection(["T0", "T1", "T2", "T10", "T97", "T98", "T99"]); compareArrays([0, 1, 2, 10, 97, 98, 99], listView.selection.getIndices()); return splice(7, 1); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); checkSelection(["T0", "T1", "T2", "T10", "T97", "T98", "T99"]); compareArrays([0, 1, 2, 9, 96, 97, 98], listView.selection.getIndices()); return splice(7, 0, { label: "New" }); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); checkSelection(["T0", "T1", "T2", "T10", "T97", "T98", "T99"]); compareArrays([0, 1, 2, 10, 97, 98, 99], listView.selection.getIndices()); return splice(98, 0, { label: "New" }); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); checkSelection(["T0", "T1", "T2", "T10", "T97", "New", "T98", "T99"]); compareArrays([0, 1, 2, 10, 97, 98, 99, 100], listView.selection.getIndices()); return splice(80, 0, { label: "New" }); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); checkSelection(["T0", "T1", "T2", "T10", "T97", "New", "T98", "T99"]); compareArrays([0, 1, 2, 10, 98, 99, 100, 101], listView.selection.getIndices()); return splice(100, 1); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); checkSelection(["T0", "T1", "T2", "T10", "T97", "New", "T99"]); compareArrays([0, 1, 2, 10, 98, 99, 100], listView.selection.getIndices()); return splice(98, 1); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); checkSelection(["T0", "T1", "T2", "T10"]); compareArrays([0, 1, 2, 10], listView.selection.getIndices()); return listView.selection.add({ firstIndex: 90, lastIndex: 92 }); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { compareArrays([0, 1, 2, 10, 90, 91, 92], listView.selection.getIndices()); validate(listView.selection._selected, retainedItems, realizedCount(listView)); return move(92, 50); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareArrays([0, 1, 2, 10], listView.selection.getIndices()); document.body.removeChild(element); complete(); }); }); } function VDSWrapper(dataSource) { var uniqueId = 1000; this.splice = function splice(index, howMany, item) { var promise: any = WinJS.Promise.wrap(); dataSource.beginEdits(); if (howMany) { var indices = []; for (var i = 0; i < howMany; i++) { indices.push(index + i); } promise = Helper.ListView.getDataObjects(dataSource, indices).then(function (dataObjects) { for (var i = 0; i < dataObjects.length; i++) { dataSource.remove(dataObjects[i].key); } }); } if (item) { promise = promise.then(function () { return Helper.ListView.getDataObjects(dataSource, [index]); }).then(function (dataObjects) { dataSource.insertBefore(uniqueId.toString(), item, dataObjects[0].key); uniqueId++; }); } return promise.then(function () { dataSource.endEdits(); }); }; this.move = function (oldIndex, newIndex) { var that = this; return Helper.ListView.getDataObjects(dataSource, [oldIndex, newIndex]).then(function (dataObjects) { return dataSource.moveBefore(dataObjects[0].key, dataObjects[1].key); }); }; this.push = function (item) { return dataSource.insertAtEnd(uniqueId.toString(), item); }; } function editsWithSelectAll(layoutName, dataSource, splice, push) { return new WinJS.Promise(function (complete) { var element = document.createElement("div"); element.style.width = "300px"; element.style.height = "300px"; document.body.appendChild(element); var listView = new ListView(element, { layout: new WinJS.UI[layoutName](), itemDataSource: dataSource, itemTemplate: function (itemPromise) { return itemPromise.then(function (item) { var element = document.createElement("div"); element.textContent = item.data.label; element.style.width = element.style.height = "100px"; return element; }); } }); var retainedItems = {}; traceRefCounts(retainedItems, listView._itemsManager._listBinding); listView.selection.selectAll(); function checkTile(listview, index, text, selected) { var tile = listview.elementFromIndex(index), wrapper = tile.parentNode; LiveUnit.Assert.areEqual(text, tile.textContent); LiveUnit.Assert.areEqual(selected, WinJS.Utilities.hasClass(wrapper, WinJS.UI._selectedClass)); } Helper.ListView.waitForReady(listView)().then(function () { for (var i = 0; i < 12; i++) { checkTile(listView, i, "Tile" + i, true); } LiveUnit.Assert.isTrue(listView.selection.isEverything()); return splice(0, 1); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices([], listView.selection.getRanges()); listView.selection.selectAll(); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices([{ firstIndex: 0, lastIndex: 98 }], listView.selection.getRanges()); return splice(0, 0, { label: "NewTile0" }); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices([{ firstIndex: 1, lastIndex: 99 }], listView.selection.getRanges()); return splice(3, 0, { label: "NewTile1" }); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices([{ firstIndex: 1, lastIndex: 100 }], listView.selection.getRanges()); return splice(10, 1); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices([{ firstIndex: 1, lastIndex: 99 }], listView.selection.getRanges()); return listView.selection.selectAll(); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices([{ firstIndex: 0, lastIndex: 99 }], listView.selection.getRanges()); LiveUnit.Assert.isTrue(listView.selection.isEverything()); return push({ label: "NewTile" }); }).then(Helper.ListView.waitForReady(listView, -1)). then(function () { compareIndices([{ firstIndex: 0, lastIndex: 99 }], listView.selection.getRanges()); LiveUnit.Assert.isFalse(listView.selection.isEverything()); document.body.removeChild(element); complete(); }); }); } function getSlowDS() { var data = []; for (var i = 0; i < 100; i++) { data.push({ title: 'item' + i }); } var controller = { directivesForMethod: function (method, args) { return { callMethodSynchronously: false, sendChangeNotifications: false, countBeforeDelta: 0, countAfterDelta: 0, countBeforeOverride: -1, countAfterOverride: -1, delay: WinJS.UI._animationTimeAdjustment(1000), }; } } var abilities = null; return Helper.ItemsManager.createTestDataSource(data, controller, abilities); } export class SelectionManagerTest { setUp() { LiveUnit.LoggingCore.logComment("In setup"); var newNode = document.createElement("div"); newNode.id = "SelectionManagerTest"; newNode.innerHTML = "
" + ""; document.body.appendChild(newNode); } tearDown() { LiveUnit.LoggingCore.logComment("In tearDown"); var element = document.getElementById("SelectionManagerTest"); document.body.removeChild(element); } testItemSet = function (complete) { var listView = { _itemsManager: { dataSource: createDataSource() } }; var set = new WinJS.UI._ItemSet(listView, [{ firstIndex: 0, lastIndex: 2 }, { firstIndex: 4, lastIndex: 4 }]); LiveUnit.Assert.isFalse(set.isEverything()); compareIndices([{ firstIndex: 0, lastIndex: 2 }, { firstIndex: 4, lastIndex: 4 }], set.getRanges()); compareArrays([0, 1, 2, 4], set.getIndices()); LiveUnit.Assert.areEqual(4, set.count()); var emptySet = new WinJS.UI._ItemSet(listView, []); LiveUnit.Assert.isFalse(emptySet.isEverything()); compareIndices([], emptySet.getRanges()); compareArrays([], emptySet.getIndices()); LiveUnit.Assert.areEqual(0, emptySet.count()); var all = new WinJS.UI._ItemSet(listView, [{ firstIndex: 0, lastIndex: 5 }], 6); LiveUnit.Assert.isTrue(all.isEverything()); compareIndices([{ firstIndex: 0, lastIndex: 5 }], all.getRanges()); compareArrays([0, 1, 2, 3, 4, 5], all.getIndices()); LiveUnit.Assert.areEqual(6, all.count()); set.getItems().then(function (items) { compareArrays(["A", "B", "C", "E"], items.map(function (item) { return item.data.letter; })); complete(); }); }; testSelectionSetIndex = function (complete) { var dataSource = createDataSource(), retainedItems = {}, site = createSite(dataSource, retainedItems), selection = new WinJS.UI._Selection(site); selection.set(10).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 10, lastIndex: 10 }], selection.getRanges()); compareArrays([10], selection.getIndices()); return selection.set("foo"); }).then(function () { validate(selection, retainedItems); compareRanges([], selection.getRanges()); selection.clear(); compareRanges([], selection.getRanges()); LiveUnit.Assert.areEqual(0, selection.count()); return selection.set([{ firstIndex: 0, lastIndex: 2 }, 4]); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 2 }, { firstIndex: 4, lastIndex: 4 }], selection.getRanges()); compareArrays([0, 1, 2, 4], selection.getIndices()); return selection.set({ firstIndex: 6 }); }).then(function () { validate(selection, retainedItems); compareRanges([], selection.getRanges()); return selection.set({ firstIndex: 6, lastIndex: "foo" }); }).then(function () { validate(selection, retainedItems); compareRanges([], selection.getRanges()); return selection.set([0, 2, 4]); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }, { firstIndex: 2, lastIndex: 2 }, { firstIndex: 4, lastIndex: 4 }], selection.getRanges()); selection.set([]); compareRanges([], selection.getRanges()); LiveUnit.Assert.areEqual(0, selection.count()); return selection.set([0, 1, 2, 4]); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }, { firstIndex: 1, lastIndex: 1 }, { firstIndex: 2, lastIndex: 2 }, { firstIndex: 4, lastIndex: 4 }], selection.getRanges()); return selection.set([4, 2, 1, 0]); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }, { firstIndex: 1, lastIndex: 1 }, { firstIndex: 2, lastIndex: 2 }, { firstIndex: 4, lastIndex: 4 }], selection.getRanges()); return selection.set(null); }).then(function () { validate(selection, retainedItems); compareRanges([], selection.getRanges()); complete(); }); }; testSelectionAddIndex = function (complete) { var dataSource = createDataSource(), retainedItems = {}, site = createSite(dataSource, retainedItems), selection = new WinJS.UI._Selection(site); selection.add(0).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }], selection.getRanges()); return selection.add(0); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }], selection.getRanges()); return selection.add(6); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }, { firstIndex: 6, lastIndex: 6 }], selection.getRanges()); return selection.add(1); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }, { firstIndex: 1, lastIndex: 1 }, { firstIndex: 6, lastIndex: 6 }], selection.getRanges()); return selection.add(2); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }, { firstIndex: 1, lastIndex: 1 }, { firstIndex: 2, lastIndex: 2 }, { firstIndex: 6, lastIndex: 6 }], selection.getRanges()); return selection.add([11, null]); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }, { firstIndex: 1, lastIndex: 1 }, { firstIndex: 2, lastIndex: 2 }, { firstIndex: 6, lastIndex: 6 }, { firstIndex: 11, lastIndex: 11 }], selection.getRanges()); return selection.add(10000000); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }, { firstIndex: 1, lastIndex: 1 }, { firstIndex: 2, lastIndex: 2 }, { firstIndex: 6, lastIndex: 6 }, { firstIndex: 11, lastIndex: 11 }], selection.getRanges()); complete(); }); }; testSelectionAddIndexRanges = function (complete) { function test(dataSource) { var retainedItems = {}, site = createSite(dataSource, retainedItems), selection = new WinJS.UI._Selection(site); return selection.add({ firstIndex: 2, lastIndex: 3 }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 3 }], selection.getRanges()); return selection.add({ firstIndex: 8, lastIndex: 9 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 3 }, { firstIndex: 8, lastIndex: 9 }], selection.getRanges()); return selection.add({ firstIndex: 5, lastIndex: 6 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 3 }, { firstIndex: 5, lastIndex: 6 }, { firstIndex: 8, lastIndex: 9 }], selection.getRanges()); return selection.add({ firstIndex: 0, lastIndex: 0 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }, { firstIndex: 2, lastIndex: 3 }, { firstIndex: 5, lastIndex: 6 }, { firstIndex: 8, lastIndex: 9 }], selection.getRanges()); // merging first selection.set([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 7, lastIndex: 9 }]); return selection.add({ firstIndex: 1, lastIndex: 5 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 1, lastIndex: 5 }, { firstIndex: 7, lastIndex: 9 }], selection.getRanges()); selection.set([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 7, lastIndex: 9 }]); return selection.add({ firstIndex: 2, lastIndex: 5 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 5 }, { firstIndex: 7, lastIndex: 9 }], selection.getRanges()); selection.set([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 7, lastIndex: 9 }]); return selection.add({ firstIndex: 3, lastIndex: 5 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 5 }, { firstIndex: 7, lastIndex: 9 }], selection.getRanges()); selection.set([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 7, lastIndex: 9 }]); return selection.add({ firstIndex: 4, lastIndex: 5 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 5 }, { firstIndex: 7, lastIndex: 9 }], selection.getRanges()); selection.set([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 7, lastIndex: 9 }]); return selection.add({ firstIndex: 5, lastIndex: 5 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 5, lastIndex: 5 }, { firstIndex: 7, lastIndex: 9 }], selection.getRanges()); // merging last selection.set([{ firstIndex: 0, lastIndex: 3 }, { firstIndex: 8, lastIndex: 10 }]); return selection.add({ firstIndex: 5, lastIndex: 7 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 3 }, { firstIndex: 5, lastIndex: 7 }, { firstIndex: 8, lastIndex: 10 }], selection.getRanges()); selection.set([{ firstIndex: 0, lastIndex: 3 }, { firstIndex: 8, lastIndex: 10 }]); return selection.add({ firstIndex: 5, lastIndex: 8 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 3 }, { firstIndex: 5, lastIndex: 10 }], selection.getRanges()); selection.set([{ firstIndex: 0, lastIndex: 3 }, { firstIndex: 8, lastIndex: 10 }]); return selection.add({ firstIndex: 5, lastIndex: 9 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 3 }, { firstIndex: 5, lastIndex: 10 }], selection.getRanges()); selection.set([{ firstIndex: 0, lastIndex: 3 }, { firstIndex: 8, lastIndex: 10 }]); return selection.add({ firstIndex: 5, lastIndex: 10 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 3 }, { firstIndex: 5, lastIndex: 10 }], selection.getRanges()); selection.set([{ firstIndex: 0, lastIndex: 3 }, { firstIndex: 8, lastIndex: 10 }]); return selection.add({ firstIndex: 5, lastIndex: 11 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 3 }, { firstIndex: 5, lastIndex: 11 }], selection.getRanges()); // both selection.set([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 7, lastIndex: 9 }]); return selection.add({ firstIndex: 0, lastIndex: 11 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 11 }], selection.getRanges()); selection.set([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 7, lastIndex: 9 }]); return selection.add({ firstIndex: 2, lastIndex: 9 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 9 }], selection.getRanges()); selection.set([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 7, lastIndex: 9 }]); return selection.add({ firstIndex: 3, lastIndex: 8 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 9 }], selection.getRanges()); selection.set([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 7, lastIndex: 9 }]); return selection.add({ firstIndex: 4, lastIndex: 7 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 9 }], selection.getRanges()); selection.set([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 7, lastIndex: 9 }]); return selection.add({ firstIndex: 5, lastIndex: 6 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 4 }, { firstIndex: 5, lastIndex: 6 }, { firstIndex: 7, lastIndex: 9 }], selection.getRanges()); selection.set([{ firstIndex: 2, lastIndex: 9 }]); return selection.add([20, 10, 13]); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 9 }, { firstIndex: 10, lastIndex: 10 }, { firstIndex: 13, lastIndex: 13 }, { firstIndex: 20, lastIndex: 20 }], selection.getRanges()); }); } test(createDataSource()).then(function () { return test(createDataSource(true)); }).then(complete); }; testSelectionRemoveIndex = function (complete) { function test(dataSource) { var retainedItems = {}, site = createSite(dataSource, retainedItems), selection = new WinJS.UI._Selection(site); return selection.set([{ firstIndex: 0, lastIndex: 7 }, { firstIndex: 9, lastIndex: 10 }]).then(function () { validate(selection, retainedItems); return selection.remove([3, 7, 9, 10]); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 2 }, { firstIndex: 4, lastIndex: 6 }], selection.getRanges()); return selection.remove(0); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 1, lastIndex: 2 }, { firstIndex: 4, lastIndex: 6 }], selection.getRanges()); return selection.remove(2); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 1, lastIndex: 1 }, { firstIndex: 4, lastIndex: 6 }], selection.getRanges()); return selection.remove(1); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 4, lastIndex: 6 }], selection.getRanges()); return selection.remove(5); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 4, lastIndex: 4 }, { firstIndex: 6, lastIndex: 6 }], selection.getRanges()); return selection.remove(100); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 4, lastIndex: 4 }, { firstIndex: 6, lastIndex: 6 }], selection.getRanges()); selection.set([{ firstIndex: 4, lastIndex: 7 }]); return selection.remove({ firstIndex: 0, lastIndex: 1 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 4, lastIndex: 7 }], selection.getRanges()); return selection.remove({ firstIndex: 8, lastIndex: 9 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 4, lastIndex: 7 }], selection.getRanges()); return selection.remove({ firstIndex: 1, lastIndex: 4 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 5, lastIndex: 7 }], selection.getRanges()); selection.set([{ firstIndex: 4, lastIndex: 7 }]); return selection.remove({ firstIndex: 1, lastIndex: 5 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 6, lastIndex: 7 }], selection.getRanges()); selection.set([{ firstIndex: 4, lastIndex: 7 }]); return selection.remove({ firstIndex: 1, lastIndex: 7 }); }).then(function () { validate(selection, retainedItems); compareRanges([], selection.getRanges()); selection.set([{ firstIndex: 4, lastIndex: 7 }]); return selection.remove({ firstIndex: 4, lastIndex: 7 }); }).then(function () { validate(selection, retainedItems); compareRanges([], selection.getRanges()); selection.set([{ firstIndex: 4, lastIndex: 7 }]); return selection.remove({ firstIndex: 0, lastIndex: 10 }); }).then(function () { validate(selection, retainedItems); compareRanges([], selection.getRanges()); selection.set([{ firstIndex: 4, lastIndex: 7 }]); return selection.remove({ firstIndex: 5, lastIndex: 6 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 4, lastIndex: 4 }, { firstIndex: 7, lastIndex: 7 }], selection.getRanges()); selection.set([{ firstIndex: 4, lastIndex: 7 }]); return selection.remove({ firstIndex: 4, lastIndex: 8 }); }).then(function () { validate(selection, retainedItems); compareRanges([], selection.getRanges()); selection.set([{ firstIndex: 4, lastIndex: 7 }]); return selection.remove({ firstIndex: 5, lastIndex: 8 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 4, lastIndex: 4 }], selection.getRanges()); selection.set([{ firstIndex: 4, lastIndex: 7 }]); return selection.remove({ firstIndex: 7, lastIndex: 8 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 4, lastIndex: 6 }], selection.getRanges()); }); } test(createDataSource()).then(function () { return test(createDataSource(true)); }).then(complete); }; testSelectionWithItems = function (complete) { var dataSource = createDataSource(), retainedItems = {}, site = createSite(dataSource, retainedItems), selection = new WinJS.UI._Selection(site); site._itemsManager._listBinding.fromIndex(1).then(function (item) { return selection.set(item); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 1, lastIndex: 1 }], selection.getRanges()); complete(); }); }; testSelectionAll = function (complete) { var dataSource = createDataSource(), retainedItems = {}, site = createSite(dataSource, retainedItems), selection = new WinJS.UI._Selection(site); selection.selectAll().then(function () { validate(selection, retainedItems); LiveUnit.Assert.isTrue(selection.isEverything()); compareIndices([{ firstIndex: 0, lastIndex: 25 }], selection.getRanges()); var array = []; for (var i = 0; i < 26; i++) { array.push(i); } compareArrays(array, selection.getIndices()); return dataSource.getCount(); }).then(function (count) { validate(selection, retainedItems); LiveUnit.Assert.areEqual(count, selection.count()); return selection.set(0); }).then(function () { validate(selection, retainedItems); LiveUnit.Assert.isFalse(selection.isEverything()); compareIndices([{ firstIndex: 0, lastIndex: 0 }], selection.getRanges()); LiveUnit.Assert.areEqual(1, selection.count()); return selection.selectAll(); }).then(function () { validate(selection, retainedItems); return selection.add(5); }).then(function () { validate(selection, retainedItems); LiveUnit.Assert.isTrue(selection.isEverything()); compareIndices([{ firstIndex: 0, lastIndex: 25 }], selection.getRanges()); return selection.remove(5); }).then(function () { validate(selection, retainedItems); LiveUnit.Assert.isFalse(selection.isEverything()); compareIndices([{ firstIndex: 0, lastIndex: 4 }, { firstIndex: 6, lastIndex: 25 }], selection.getRanges()); return selection.set({ firstIndex: 0, lastIndex: Number.MAX_VALUE }); }).then(function () { validate(selection, retainedItems); LiveUnit.Assert.isTrue(selection.isEverything()); complete(); }); }; testSelectAllOnEmptyDataSource = function (complete) { var dataSource = createDataSourceImpl(false, 0), retainedItems = {}, site = createSite(dataSource, retainedItems), selection = new WinJS.UI._Selection(site); selection.selectAll().then(function () { complete(); }); }; testSelectionWithKeys = function (complete) { var dataSource = createDataSource(), retainedItems = {}, site = createSite(dataSource, retainedItems), selection = new WinJS.UI._Selection(site); LiveUnit.Assert.areEqual(0, selection.count()); selection.set({ key: "b" }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 1, lastIndex: 1 }], selection.getRanges()); return selection.set({ firstKey: "c", lastKey: "d" }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 3 }], selection.getRanges()); return selection.remove({ key: "c" }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 3, lastIndex: 3 }], selection.getRanges()); return selection.remove({ firstKey: "c", lastKey: "e" }); }).then(function () { validate(selection, retainedItems); compareRanges([], selection.getRanges()); return selection.add({ key: "c" }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 2 }], selection.getRanges()); return selection.add({ key: "d" }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 2 }, { firstIndex: 3, lastIndex: 3 }], selection.getRanges()); return selection.add({ firstKey: "a" }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 2 }, { firstIndex: 3, lastIndex: 3 }], selection.getRanges()); return selection.add({ firstKey: "f", lastKey: "g" }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 2 }, { firstIndex: 3, lastIndex: 3 }, { firstIndex: 5, lastIndex: 6 }], selection.getRanges()); return selection.set({ firstKey: "a", firstIndex: 10, lastKey: "b", lastIndex: 11 }); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 1 }], selection.getRanges()); retainedItems = {}; dataSource = createDataSource(true); site = createSite(dataSource, retainedItems); selection = new WinJS.UI._Selection(site); return selection.set([{ firstKey: "a", lastKey: "a" }, { firstKey: "b", lastKey: "b" }, { firstKey: "d", lastKey: "e" }]); }).then(function () { validate(selection, retainedItems); compareRanges([{ firstIndex: 0, lastIndex: 0 }, { firstIndex: 1, lastIndex: 1 }, { firstIndex: 3, lastIndex: 4 }], selection.getRanges()); return selection.set({ firstKey: "$", lastKey: "%" }); }).then(function () { validate(selection, retainedItems); compareRanges([], selection.getRanges()); complete(); }); }; testSelectionManager = function (complete) { function test(dataSource) { var retainedItems = {}; selectionUpdated = 0; selectionChanging = 0; selectionChanged = 0; var selection = setupSelectionManager(dataSource, retainedItems); LiveUnit.Assert.areEqual(0, selection.count()); LiveUnit.Assert.isFalse(selection.isEverything()); LiveUnit.Assert.isFalse(selection._isIncluded(0)); LiveUnit.Assert.isFalse(selection._isIncluded(1)); compareRanges([], selection.getRanges()); return selection.set([0, 1, 2]).then(function () { validate(selection._selected, retainedItems); LiveUnit.Assert.isTrue(selection._isIncluded(0)); LiveUnit.Assert.isTrue(selection._isIncluded(1)); LiveUnit.Assert.isTrue(selection._isIncluded(2)); LiveUnit.Assert.isFalse(selection._isIncluded(3)); LiveUnit.Assert.isFalse(selection.isEverything()); compareRanges([{ firstIndex: 0, lastIndex: 0 }, { firstIndex: 1, lastIndex: 1 }, { firstIndex: 2, lastIndex: 2 }], selection.getRanges()); LiveUnit.Assert.areEqual(1, selectionChanging); LiveUnit.Assert.areEqual(1, selectionChanged); LiveUnit.Assert.areEqual(1, selectionUpdated); return selection.getItems(); }).then(function (items) { compareArrays(["A", "B", "C"], items.map(function (item) { return item.data.letter; })); return selection.clear(); }).then(function () { validate(selection._selected, retainedItems); compareRanges([], selection.getRanges()); LiveUnit.Assert.areEqual(2, selectionChanging); LiveUnit.Assert.areEqual(2, selectionChanged); LiveUnit.Assert.areEqual(2, selectionUpdated); return selection.set({ firstIndex: 1, lastIndex: 3 }); }).then(function () { validate(selection._selected, retainedItems); compareRanges([{ firstIndex: 1, lastIndex: 3 }], selection.getRanges()); return selection.add(5); }).then(function () { validate(selection._selected, retainedItems); compareRanges([{ firstIndex: 1, lastIndex: 3 }, { firstIndex: 5, lastIndex: 5 }], selection.getRanges()); LiveUnit.Assert.areEqual(4, selectionChanging); LiveUnit.Assert.areEqual(4, selectionChanged); LiveUnit.Assert.areEqual(4, selectionUpdated); return selection.remove(6); }).then(function () { validate(selection._selected, retainedItems); compareRanges([{ firstIndex: 1, lastIndex: 3 }, { firstIndex: 5, lastIndex: 5 }], selection.getRanges()); return selection.remove(1); }).then(function () { validate(selection._selected, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 3 }, { firstIndex: 5, lastIndex: 5 }], selection.getRanges()); LiveUnit.Assert.areEqual(6, selectionChanging); LiveUnit.Assert.areEqual(6, selectionChanged); LiveUnit.Assert.areEqual(6, selectionUpdated); selection._fireSelectionChanging = function () { selectionChanging++; return WinJS.Promise.wrap(false); }; return selection.set([0, 1]); }).then(function () { validate(selection._selected, retainedItems); compareRanges([{ firstIndex: 2, lastIndex: 3 }, { firstIndex: 5, lastIndex: 5 }], selection.getRanges()); LiveUnit.Assert.areEqual(7, selectionChanging); LiveUnit.Assert.areEqual(6, selectionChanged); LiveUnit.Assert.areEqual(6, selectionUpdated); selection._fireSelectionChanging = function (newSelection) { selectionChanging++; newSelection.set([9, 10]); return WinJS.Promise.wrap(true); }; return selection.set([0, 1]); }).then(function () { compareIndices([{ firstIndex: 9, lastIndex: 9 }, { firstIndex: 10, lastIndex: 10 }], selection.getRanges()); LiveUnit.Assert.areEqual(8, selectionChanging); LiveUnit.Assert.areEqual(7, selectionChanged); LiveUnit.Assert.areEqual(7, selectionUpdated); LiveUnit.Assert.areEqual(2, selection.count()); compareArrays([9, 10], selection.getIndices()); }); } LiveUnit.LoggingCore.logComment("synchronous data source"); test(createDataSource()).then(function () { LiveUnit.LoggingCore.logComment("asynchronous data source"); return test(createDataSource(true)); }).then(complete); }; testFocus = function () { var selection = setupSelectionManager(null, null); var focused = selection._getFocused(); LiveUnit.Assert.areEqual("item", focused.type); LiveUnit.Assert.areEqual(0, focused.index); selection._setFocused({ type: "item", index: 1 }); focused = selection._getFocused(); LiveUnit.Assert.areEqual("item", focused.type); LiveUnit.Assert.areEqual(1, focused.index); }; testSelectionWithSlowDataSourceAfterScrolling = function (complete) { Helper.initUnhandledErrors(); var testDS = getSlowDS(); var testDiv = document.querySelector("#SelectionManagerTest"); var lvElement = document.createElement("div"); lvElement.style.width = "200px"; lvElement.style.height = "1000px" testDiv.appendChild(lvElement); var listView = new ListView(lvElement, { itemDataSource: testDS, itemTemplate: function (itemPromise) { return itemPromise.then(function (item) { var element = document.createElement("div"); element.textContent = item.data.title; element.style.width = element.style.height = "100px"; return element; }); } }); Helper.ListView.waitForReady(listView)().then(function () { listView.addEventListener("loadingstatechanged", function loadingStateHandler() { if (listView.loadingState === "viewPortLoaded") { listView.removeEventListener("loadingstatechanged", loadingStateHandler); WinJS.Promise.timeout(20).then(function () { listView.selection.add(98); }); } }); listView.ensureVisible(98); }); Helper.ListView.waitForReady(listView)(). then(Helper.validateUnhandledErrorsOnIdle). done(complete); } } var generateSelectionChanging = function (layoutName) { SelectionManagerTest.prototype["testSelectionChanging" + layoutName] = function (complete) { var element = document.createElement("div"); document.body.appendChild(element); var data = []; for (var i = 0; i < 100; i++) { data.push({ label: "Item" + i }); } var listView = new ListView(element, { layout: new WinJS.UI[layoutName](), itemDataSource: (new WinJS.Binding.List(data)).dataSource, selectionMode: "multi" }); Helper.ListView.whenLoadingComplete(listView, function () { listView.selection.set(0).then(function () { checkTileSelection(listView, 0, true); checkTileSelection(listView, 1, false); var promise; listView.addEventListener("selectionchanging", function (eventObject) { promise = WinJS.Promise.timeout(100); promise.then(function () { compareIndices([{ firstIndex: 1, lastIndex: 1 }, { firstIndex: 2, lastIndex: 2 }], eventObject.detail.newSelection.getRanges()); eventObject.detail.newSelection.remove(1); }); eventObject.detail.setPromise(promise) }, false); listView.selection.set([1, 2]); checkTileSelection(listView, 1, false); checkTileSelection(listView, 2, false); return promise.then(function () { return WinJS.Promise.timeout(); }); }).then(function () { checkTileSelection(listView, 1, false); checkTileSelection(listView, 2, true); document.body.removeChild(element); complete(); }); }); }; }; generateSelectionChanging("GridLayout"); var generateChainingAsyncChanges = function (layoutName) { SelectionManagerTest.prototype["testChainingAsyncChanges" + layoutName] = function (complete) { function createAsyncDataSource() { var count = 1000; var dataSource = { itemsFromIndex: function (index, countBefore, countAfter) { return new WinJS.Promise(function (complete, error) { if (index < count) { var startIndex = Math.max(0, index - countBefore), endIndex = Math.min(index + countAfter, count - 1), size = endIndex - startIndex + 1; var items = []; for (var i = startIndex; i < startIndex + size; i++) { items.push({ key: i.toString(), data: { label: "Tile" + i } }); } var retVal = { items: items, offset: index - startIndex, totalCount: count, absoluteIndex: index }; WinJS.Promise.timeout(WinJS.UI._animationTimeAdjustment(50)).then(function () { complete(retVal); }); } else { complete({}); } }); }, getCount: function () { return WinJS.Promise.wrap(count); } }; return new WinJS.UI.ListDataSource(dataSource); } var element = document.createElement("div"); element.style.width = "300px"; element.style.height = "300px"; document.body.appendChild(element); var listView = new ListView(element, { layout: new WinJS.UI[layoutName](), itemDataSource: createAsyncDataSource(), itemTemplate: function (itemPromise) { return itemPromise.then(function (item) { var element = document.createElement("div"); element.textContent = item.data.label; element.style.width = "100px"; element.style.height = "100px"; return element; }); } }); Helper.ListView.whenLoadingComplete(listView, function () { var promises = []; promises.push(listView.selection.add(0)); promises.push(listView.selection.add(100)); promises.push(listView.selection.add(200)); promises.push(listView.selection.add(300)); promises.push(listView.selection.add(400)); promises.push(listView.selection.remove(400)); WinJS.Promise.join(promises).then(function () { Helper.ListView.elementsEqual([0, 100, 200, 300], listView.selection.getIndices()); checkTileSelection(listView, 0, true); checkTileSelection(listView, 1, false); document.body.removeChild(element); complete(); }); }); }; }; generateChainingAsyncChanges("GridLayout"); var generateEditsOutsideOfRealizedRange_BindingList = function (layoutName) { SelectionManagerTest.prototype["testEditsOutsideOfRealizedRange_BindingList" + layoutName] = function (complete) { var data = []; for (var i = 0; i < 100; i++) { data.push({ label: "T" + i }); } var list = new WinJS.Binding.List(data); editsOutsideOfRealizedRange(layoutName, list.dataSource, list.splice.bind(list), list.move.bind(list)).then(complete); }; }; generateEditsOutsideOfRealizedRange_BindingList("GridLayout"); var generateEditsOutsideOfRealizedRange_VDS = function (layoutName) { SelectionManagerTest.prototype["testEditsOutsideOfRealizedRange_VDS" + layoutName] = function (complete) { var data = []; for (var i = 0; i < 100; i++) { data.push({ label: "T" + i }); } var dataSource = Helper.ItemsManager.simpleSynchronousArrayDataSource(data); var wrapper = new VDSWrapper(dataSource); editsOutsideOfRealizedRange(layoutName, dataSource, wrapper.splice.bind(wrapper), wrapper.move.bind(wrapper)).then(complete); }; }; generateEditsOutsideOfRealizedRange_VDS("GridLayout"); var generateEditsWithSelectAll_BindingList = function (layoutName) { SelectionManagerTest.prototype["testEditsWithSelectAll_BindingList" + layoutName] = function (complete) { var data = []; for (var i = 0; i < 100; i++) { data.push({ label: "Tile" + i }); } var list = new WinJS.Binding.List(data); editsWithSelectAll(layoutName, list.dataSource, list.splice.bind(list), list.push.bind(list)).then(complete); }; }; generateEditsWithSelectAll_BindingList("GridLayout"); var generateEditsWithSelectAll_VDS = function (layoutName) { SelectionManagerTest.prototype["testEditsWithSelectAll_VDS" + layoutName] = function (complete) { var data = []; for (var i = 0; i < 100; i++) { data.push({ label: "Tile" + i }); } var dataSource = Helper.ItemsManager.simpleSynchronousArrayDataSource(data); var wrapper = new VDSWrapper(dataSource); editsWithSelectAll(layoutName, dataSource, wrapper.splice.bind(wrapper), wrapper.push.bind(wrapper)).then(complete); }; }; generateEditsWithSelectAll_VDS("GridLayout"); var generateSelectionDispose = function (layoutName) { SelectionManagerTest.prototype["testSelectionDispose" + layoutName] = function (complete) { var element = document.createElement("div"); element.style.width = "300px"; element.style.height = "300px"; document.body.appendChild(element); var items = []; for (var i = 0; i < 100; ++i) { items[i] = { title: "Tile" + i }; } var list = (new WinJS.Binding.List(items)); var listView = new ListView(element, { layout: new WinJS.UI[layoutName](), itemDataSource: list.dataSource, itemTemplate: function (itemPromise) { return itemPromise.then(function (item) { var element = document.createElement("div"); element.textContent = item.data.title; element.style.width = element.style.height = "100px"; return element; }); } }); var retainedItems = {}; traceRefCounts(retainedItems, listView._itemsManager._listBinding); listView.selection.set([{ firstIndex: 0, lastIndex: 2 }, 10, { firstIndex: 97, lastIndex: 99 }]); var tests = [ function () { var selection = listView.selection._selected; validate(selection, retainedItems, realizedCount(listView)); document.body.removeChild(element); setTimeout(function () { validate(selection, retainedItems); complete(); }, 1000); } ]; Helper.ListView.runTests(listView, tests); }; }; generateSelectionDispose("GridLayout"); var generateSelectionEventsAfterEdits = function (layoutName) { SelectionManagerTest.prototype["testSelectionEventsAfterEdits" + layoutName] = function (complete) { var element = document.createElement("div"); element.style.width = "300px"; element.style.height = "300px"; document.body.appendChild(element); var items = []; for (var i = 0; i < 100; ++i) { items[i] = { title: "Tile" + i }; } var list = (new WinJS.Binding.List(items)); var listView = new ListView(element, { layout: new WinJS.UI[layoutName](), itemDataSource: list.dataSource, itemTemplate: function (itemPromise) { return itemPromise.then(function (item) { var element = document.createElement("div"); element.textContent = item.data.title; element.style.width = element.style.height = "100px"; return element; }); } }); var retainedItems = {}; traceRefCounts(retainedItems, listView._itemsManager._listBinding); listView.selection.set([{ firstIndex: 5, lastIndex: 10 }, { firstIndex: 20, lastIndex: 25 }]); var expectedRanges, changingCount = 0, changedCount = 0; listView.addEventListener("selectionchanging", function (eventObject) { compareIndices(expectedRanges, eventObject.detail.newSelection.getRanges()); changingCount++; }, false); listView.addEventListener("selectionchanged", function (eventObject) { changedCount++; }, false); var tests = [ function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); expectedRanges = [{ firstIndex: 4, lastIndex: 9 }, { firstIndex: 19, lastIndex: 24 }]; list.splice(0, 1); return true; }, function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices(expectedRanges, listView.selection.getRanges()); // Even though the data change didn't affect any of the items that were selected, we should still get selectionchanged events // since the selection indices changed from [5-10, 20-25] to [4-9, 19-24]. LiveUnit.Assert.areEqual(1, changingCount); LiveUnit.Assert.areEqual(1, changedCount); expectedRanges = [{ firstIndex: 18, lastIndex: 23 }]; list.splice(4, 1); return true; }, function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices(expectedRanges, listView.selection.getRanges()); LiveUnit.Assert.areEqual(2, changingCount); LiveUnit.Assert.areEqual(2, changedCount); expectedRanges = [{ firstIndex: 18, lastIndex: 22 }]; list.splice(19, 1); return true; }, function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices(expectedRanges, listView.selection.getRanges()); LiveUnit.Assert.areEqual(3, changingCount); LiveUnit.Assert.areEqual(3, changedCount); expectedRanges = [{ firstIndex: 18, lastIndex: 23 }]; list.splice(19, 0, { title: "NewTile1" }); return true; }, function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices(expectedRanges, listView.selection.getRanges()); LiveUnit.Assert.areEqual(4, changingCount); LiveUnit.Assert.areEqual(4, changedCount); expectedRanges = [{ firstIndex: 19, lastIndex: 24 }]; list.splice(1, 0, { title: "NewTile2" }); return true; }, function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices(expectedRanges, listView.selection.getRanges()); LiveUnit.Assert.areEqual(5, changingCount); LiveUnit.Assert.areEqual(5, changedCount); expectedRanges = [{ firstIndex: 18, lastIndex: 24 }]; list.move(0, 21); return true; }, function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices(expectedRanges, listView.selection.getRanges()); LiveUnit.Assert.areEqual(6, changingCount); LiveUnit.Assert.areEqual(6, changedCount); expectedRanges = [{ firstIndex: 18, lastIndex: 23 }]; list.move(20, 80); return true; }, function () { validate(listView.selection._selected, retainedItems, realizedCount(listView)); compareIndices(expectedRanges, listView.selection.getRanges()); LiveUnit.Assert.areEqual(7, changingCount); LiveUnit.Assert.areEqual(7, changedCount); document.body.removeChild(element); complete(); } ]; Helper.ListView.runTests(listView, tests); }; }; generateSelectionEventsAfterEdits("GridLayout"); var generateResetSelectionOnSlowDS = function (layoutName) { SelectionManagerTest.prototype["testResetSelectionOnSlowDS" + layoutName] = function (complete) { var testDS = getSlowDS(); var testDiv = document.querySelector("#SelectionManagerTest"); var lvElement = document.createElement("div"); testDiv.appendChild(lvElement); var listView = new ListView(lvElement, { layout: new WinJS.UI[layoutName]() }); Helper.ListView.waitForReady(listView)(). then(function () { listView.itemDataSource = testDS; listView.selection.set([{ key: '10' }]); listView.itemDataSource = new WinJS.Binding.List(listView.itemDataSource['testDataAdapter'].getItems()).dataSource; return Helper.ListView.waitForReady(listView)(); }). then(function () { return listView.selection.set([{ key: '0' }]); }). then(function () { LiveUnit.Assert.areEqual([0].toString(), listView.selection.getIndices().toString()); var selectedInDom = listView.element.querySelectorAll(".win-selectioncheckmark"); LiveUnit.Assert.areEqual(1, selectedInDom.length); }). then(function () { return listView.selection.set([]); }). then(function () { LiveUnit.Assert.areEqual([].toString(), listView.selection.getIndices().toString()); var selectedInDom = listView.element.querySelectorAll(".win-selectioncheckmark"); LiveUnit.Assert.areEqual(0, selectedInDom.length); }). done(complete); }; }; generateResetSelectionOnSlowDS("GridLayout"); var generateInvalidSelection = function (layoutName) { SelectionManagerTest.prototype["testInvalidSelection" + layoutName] = function (complete) { var testDS = getSlowDS(); var testDiv = document.querySelector("#SelectionManagerTest"); var lvElement = document.createElement("div"); testDiv.appendChild(lvElement); var listView = new ListView(lvElement, { layout: new WinJS.UI[layoutName](), itemDataSource: testDS }); listView.selection.set([{ key: 'foo' }]); Helper.ListView.waitForReady(listView)(). then(function () { LiveUnit.Assert.areEqual([].toString(), listView.selection.getIndices().toString()); var selectedInDom = listView.element.querySelectorAll(".win-selectioncheckmark"); LiveUnit.Assert.areEqual(0, selectedInDom.length); }). then(function () { return listView.selection.set([0]); }). then(function () { LiveUnit.Assert.areEqual([0].toString(), listView.selection.getIndices().toString()); var selectedInDom = listView.element.querySelectorAll(".win-selectioncheckmark"); LiveUnit.Assert.areEqual(1, selectedInDom.length); }). done(complete); }; }; generateInvalidSelection("GridLayout"); } // register the object as a test class by passing in the name LiveUnit.registerTestClass("WinJSTests.SelectionManagerTest");