// 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 testRootEl; var ListView = WinJS.UI.ListView; function groupKey(item) { var groupIndex = Math.floor(item.data ? (item.data.index / 10) : (item.index / 10)); return groupIndex.toString(); } function groupData(item) { var groupIndex = Math.floor(item.data ? (item.data.index / 10) : (item.index / 10)); var groupData = { title: "group" + groupIndex, index: groupIndex, itemWidth: "150px", itemHeight: "150px" }; return groupData; } function createData(size) { var data = []; for (var i = 0; i < size; i++) { data.push({ title: "title" + i, index: i, itemWidth: "80px", itemHeight: "80px" }); } return data; } function createBindingList(size, data?) { return (data ? new WinJS.Binding.List(data) : new WinJS.Binding.List(createData(size))); } function createTestDataSource(size, data?) { // Populate a data array if (!data) { data = createData(size); } // Create the datasource var controller = { directivesForMethod: function (method, args) { var implementedNotifications = ["insertAtIndex", "changeAtIndex", "removeAtIndex"]; if (-1 !== implementedNotifications.indexOf(method)) { return { callMethodSynchronously: true, sendChangeNotifications: true, countBeforeDelta: 0, countAfterDelta: 0, countBeforeOverride: -1, countAfterOverride: -1 }; } else { return { callMethodSynchronously: true, sendChangeNotifications: false, countBeforeDelta: 0, countAfterDelta: 0, countBeforeOverride: -1, countAfterOverride: -1 }; } } }; // Data adapter abilities var abilities = { itemsFromIndex: true, itemsFromKey: true, getCount: true, setNotificationHandler: true }; return Helper.ItemsManager.createTestDataSource(data, controller, abilities); } function testLayout(layoutName) { var placeholder1 = document.getElementById("test1"), placeholder2 = document.getElementById("test2"), expectedLayout = WinJS.UI[layoutName], listView1 = new ListView(placeholder1, { layout: { type: expectedLayout } }), listView2 = new ListView(placeholder2, { layout: new expectedLayout() }); Helper.ListView.validateListView(listView1); Helper.ListView.validateListView(listView2); LiveUnit.Assert.isTrue(listView1.layout instanceof expectedLayout); LiveUnit.Assert.isTrue(listView2.layout instanceof expectedLayout); LiveUnit.Assert.isTrue(listView1._horizontal() === (layoutName.indexOf("GridLayout") == 0)); LiveUnit.Assert.isTrue(listView2._horizontal() === (layoutName.indexOf("GridLayout") == 0)); } export class InitializationTests { // This setup function will be called at the beginning of each test function. setUp() { LiveUnit.LoggingCore.logComment("In setup"); testRootEl = document.createElement("div"); testRootEl.className = "file-listview-css"; var newNode = document.createElement("div"); newNode.id = "InitializationTests"; newNode.innerHTML = "
" + "
"; testRootEl.appendChild(newNode); document.body.appendChild(testRootEl); } tearDown() { LiveUnit.LoggingCore.logComment("In tearDown"); WinJS.Utilities.disposeSubTree(testRootEl); document.body.removeChild(testRootEl); } // Test listView initialization with an invalid element like H1 // Tests listView initialization with grid layout testGridLayout = function (complete) { LiveUnit.LoggingCore.logComment("In testGridLayout"); testLayout("GridLayout"); complete(); } // Tests listView initialization with list layout testListLayout = function (complete) { LiveUnit.LoggingCore.logComment("In testListLayout"); testLayout("ListLayout"); complete(); }; // Tests default values of listView properties after initialization testInitializationDefaults = function (complete) { LiveUnit.LoggingCore.logComment("In testInitializationDefaults"); var testElement = document.getElementById("test1"), listView = new WinJS.UI.ListView(testElement), defaults = Helper.ListView.defaultOptions(); LiveUnit.Assert.isTrue(listView.layout instanceof WinJS.UI.GridLayout); for (var param in defaults) { LiveUnit.Assert.isTrue(listView[param] === defaults[param]); } complete(); }; // Regression test for WinBlue: 104695 // testPassLayoutInConstructorOptionsDifferentFromDefault = function (complete) { var bl = createBindingList(400), groupBl = bl.createGrouped(groupKey, groupData), updateLayoutCalledCount = 0, oldUpdateLayout = ListView.prototype._updateLayout; ListView.prototype._updateLayout = function () { updateLayoutCalledCount++; oldUpdateLayout.call(this); }; var element = document.getElementById("test1"), listView = new WinJS.UI.ListView(element, { itemDataSource: groupBl.dataSource, itemTemplate: Helper.ListView.templates.syncJSTemplate, groupDataSource: groupBl.groups.dataSource, groupHeaderTemplate: Helper.ListView.templates.syncJSTemplate, layout: new WinJS.UI.ListLayout() }); Helper.ListView.waitForReady(listView)(). then(function () { LiveUnit.Assert.areEqual(1, updateLayoutCalledCount, "ListView_updateLayout should be called only once when a layout is passed it the constructor options"); ListView.prototype._updateLayout = oldUpdateLayout; }). done(complete, function (er) { throw er; }); }; } (function () { function generateTest(gids, gds, layout, testPrefix) { function generateTest1(action) { return function (complete) { var element = document.getElementById("test1"), listView = new WinJS.UI.ListView(element, { itemDataSource: gids, itemTemplate: Helper.ListView.templates.syncJSTemplate, groupDataSource: gds, groupHeaderTemplate: Helper.ListView.templates.syncJSTemplate, layout: layout }); Helper.ListView.waitForReady(listView)(). then(function () { action(); }). then(Helper.ListView.waitForReady(listView)). done(complete, function (er) { throw er; }); }; } InitializationTests.prototype["test" + testPrefix + "SetItemDataSource"] = generateTest1(function () { var elem = document.getElementById("test1"), lv = elem.winControl; if (lv.itemDataSource) { lv.itemDataSource = lv.itemDataSource; } else { LiveUnit.Assert.fail("ItemDataSource is not set"); } }); InitializationTests.prototype["test" + testPrefix + "SetItemTemplate"] = generateTest1(function () { var elem = document.getElementById("test1"), lv = elem.winControl; if (lv.itemTemplate) { lv.itemTemplate = lv.itemTemplate; } else { LiveUnit.Assert.fail("ItemTemplate is not set"); } }); InitializationTests.prototype["test" + testPrefix + "SetGroupHeaderTemplate"] = generateTest1(function () { var elem = document.getElementById("test1"), lv = elem.winControl; if (lv.groupHeaderTemplate) { lv.groupHeaderTemplate = lv.groupHeaderTemplate; } }); InitializationTests.prototype["test" + testPrefix + "SetGroupDataSource"] = generateTest1(function () { var elem = document.getElementById("test1"), lv = elem.winControl; if (lv.groupDataSource) { lv.groupDataSource = lv.groupDataSource; } }); InitializationTests.prototype["test" + testPrefix + "SetLayout"] = generateTest1(function () { var elem = document.getElementById("test1"), lv = elem.winControl; if (lv.layout) { lv.layout = lv.layout; } else { LiveUnit.Assert.fail("Layout is not set"); } }); } var bl = createBindingList(400), groupBl = bl.createGrouped(groupKey, groupData), vds = createTestDataSource(400), groupVds = WinJS.UI.computeDataSourceGroups(vds, groupKey, groupData); // Since these test reassign the control properties to the same value, there // are no changes to underlying. Hence we should be able to reuse the datasources // across tests. // TODO: Add tests for Multisize grid and multisize grouped grid layout generateTest(groupBl.dataSource, groupBl.groups.dataSource, { type: WinJS.UI.GridLayout }, "GroupedGridLayoutBindingList"); generateTest(groupBl.dataSource, groupBl.groups.dataSource, { type: WinJS.UI.ListLayout }, "GroupedListLayoutBindingList"); generateTest(groupBl.dataSource, null, { type: WinJS.UI.GridLayout }, "GridLayoutBindingList"); generateTest(groupBl.dataSource, null, { type: WinJS.UI.ListLayout }, "ListLayoutBindingList"); generateTest(groupVds, groupVds.groups, { type: WinJS.UI.GridLayout }, "GroupedGridLayoutVDS"); generateTest(groupVds, groupVds.groups, { type: WinJS.UI.ListLayout }, "GroupedListLayoutVDS"); generateTest(groupVds, null, { type: WinJS.UI.GridLayout }, "GridLayoutVDS"); generateTest(groupVds, null, { type: WinJS.UI.ListLayout }, "ListLayoutVDS"); })(); function generateWithoutElement(layoutName) { InitializationTests.prototype["testWithoutElement" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) { function checkTile(listview, index, left, top) { var tile = listview.elementFromIndex(index), container = Helper.ListView.containerFrom(tile); LiveUnit.Assert.areEqual("Tile" + index, tile.textContent); LiveUnit.Assert.areEqual(left, Helper.ListView.offsetLeftFromSurface(listview, container)); LiveUnit.Assert.areEqual(top, Helper.ListView.offsetTopFromSurface(listview, container)); } var data = []; for (var i = 0; i < 100; i++) { data.push({ label: "Tile" + i }); } var list = new WinJS.Binding.List(data); var listView = new WinJS.UI.ListView(null, { 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.label; element.style.width = element.style.height = "100px"; return element; }); } }); listView.element.style.width = "300px"; listView.element.style.height = "300px"; testRootEl.appendChild(listView.element); Helper.ListView.runTests(listView, [ function () { checkTile(listView, 0, 0, 0); checkTile(listView, 1, 0, 100); checkTile(listView, 2, 0, 200); checkTile(listView, 3, 100, 0); testRootEl.removeChild(listView.element); complete(); } ]); }; }; generateWithoutElement("GridLayout"); // Tests listView initialization with empty datasource function generateEmptyDataSource(layoutName) { InitializationTests.prototype["testEmptyDataSource" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) { LiveUnit.LoggingCore.logComment("In testEmptyDataSource"); var testElement = document.getElementById("test1"), listView = new WinJS.UI.ListView(testElement, { layout: new WinJS.UI[layoutName](), itemDataSource: new WinJS.Binding.List().dataSource }); Helper.ListView.validateListView(listView); complete(); }; }; generateEmptyDataSource("GridLayout"); function generateItemsAccess(layoutName) { InitializationTests.prototype["testItemsAccess" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) { LiveUnit.LoggingCore.logComment("In testItemsAccess"); var element = document.getElementById("test1"), size = 13, myData = [], listView, counter; for (var i = 0; i < size; ++i) { myData.push("
index: " + i + "
"); } var list = new WinJS.Binding.List(myData); listView = new WinJS.UI.ListView(element, { layout: new WinJS.UI[layoutName](), itemDataSource: list.dataSource }); function bind(index) { return LiveUnit.GetWrappedCallback(function dataAvailable(item) { LiveUnit.Assert.areEqual("
index: " + index + "
", item.data); if (--counter === 0) { complete(); } }); } function countAvailable(count) { LiveUnit.Assert.areEqual(size, count); counter = count; for (var i = 0; i < count; ++i) { listView.itemDataSource.itemFromIndex(i).then(bind(i)); } } listView.itemDataSource.getCount().then(LiveUnit.GetWrappedCallback(countAvailable)); }; }; generateItemsAccess("GridLayout"); function generateEventHandlers(layoutName) { InitializationTests.prototype["testEventHandlers" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) { var myData = []; for (var i = 0; i < 10; ++i) { myData.push({ title: "Tile" + i }); } var newNode = document.createElement("div"); newNode.style.width = "1000px"; newNode.style.height = "600px"; testRootEl.appendChild(newNode); var listView = new WinJS.UI.ListView(newNode, { layout: new WinJS.UI[layoutName](), itemDataSource: (new WinJS.Binding.List(myData)).dataSource }); var handlerCount = 0; listView.addEventListener("selectionchanged", function (eventObject) { handlerCount++; }); listView.onselectionchanged = function (eventObject) { handlerCount++; }; Helper.ListView.runTests(listView, [ function () { listView.selection.set([0]); LiveUnit.Assert.areEqual(2, handlerCount); testRootEl.removeChild(newNode); complete(); } ]); }; }; generateEventHandlers("GridLayout"); function generateRendererValidations(layoutName) { InitializationTests.prototype["testRendererValidation" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) { var element = document.createElement("div"); testRootEl.appendChild(element); // The order of controls is invalid. Template is instantiated after ListView. element.innerHTML = '
' + '
'; var originalValidation = WinJS.validation; WinJS.validation = true; function cleanup() { element.parentNode.removeChild(element); WinJS.validation = originalValidation; } WinJS.UI.processAll(element).done( function () { LiveUnit.Assert.fail("ProcessAll shouldn't succeed"); }, function (error) { LiveUnit.Assert.areEqual("WinJS.UI.ListView.invalidTemplate", error.name); cleanup(); complete(); } ); }; }; generateRendererValidations("GridLayout"); function generateKeysValidation(layoutName) { InitializationTests.prototype["testKeysValidation" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) { var element = document.createElement("div"); element.style.width = "300px"; element.style.height = "300px"; testRootEl.appendChild(element); function createDataSource() { var count = 1000; var dataSource = { 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: i + 1, // this is wrong. Key needs to be a string data: { label: "Tile" + i } }); } complete({ items: items, offset: index - startIndex, totalCount: count, absoluteIndex: index }); } else { complete({}); } }); }, getCount: function () { return WinJS.Promise.wrap(count); } }; return new WinJS.UI.ListDataSource(dataSource); } var originalValidation = WinJS.validation; WinJS.validation = true; function cleanup() { element.parentNode.removeChild(element); WinJS.validation = originalValidation; } function errorHandler(error) { LiveUnit.Assert.areEqual("WinJS.UI.ListDataSource.KeyIsInvalid", error.detail.exception.name); WinJS.Promise.removeEventListener("error", errorHandler); cleanup(); complete(); }; WinJS.Promise.addEventListener("error", errorHandler); var listView = new WinJS.UI.ListView(element, { layout: new WinJS.UI[layoutName](), itemDataSource: createDataSource() }); }; }; generateKeysValidation("GridLayout"); function generateSwitchingDataSourceDuringGetCount(layoutName) { InitializationTests.prototype["testSwitchingDataSourceDuringGetCount" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) { function createDataSource() { var count = 1000; var dataSource = { 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: (i + 1).toString(), data: { label: "Tile" + i } }); } complete({ items: items, offset: index - startIndex, totalCount: count, absoluteIndex: index }); } else { complete({}); } }); }, getCount: function () { return WinJS.Promise.timeout(100).then(function () { return count; }); } }; return new WinJS.UI.ListDataSource(dataSource); } Helper.initUnhandledErrors(); var element = document.createElement("div"); element.style.width = "300px"; element.style.height = "300px"; testRootEl.appendChild(element); var listView = new WinJS.UI.ListView(element, { layout: new WinJS.UI[layoutName](), itemDataSource: createDataSource() }); WinJS.Promise.timeout(50). then(function () { var myData = []; for (var i = 0; i < 100; ++i) { myData.push({ title: "Tile" + i }); } listView.itemDataSource = (new WinJS.Binding.List(myData)).dataSource; }). then(Helper.ListView.waitForReady(listView, 100)). then(Helper.validateUnhandledErrorsOnIdle). done(function () { element.parentNode.removeChild(element); complete(); }); }; }; generateSwitchingDataSourceDuringGetCount("GridLayout"); } // register the object as a test class by passing in the name LiveUnit.registerTestClass("WinJSTests.InitializationTests");