// 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 defaultDisableCustomPagesPrefetch;
var ListView = WinJS.UI.ListView;
function parent(element) {
document.body.appendChild(element);
return function () {
WinJS.Utilities.disposeSubTree(element);
document.body.removeChild(element);
};
}
function errorHandler(msg) {
try {
LiveUnit.Assert.fail('There was an unhandled error in your test: ' + msg);
} catch (ex) { }
}
function getRandomColor() {
return "rgb(" + Math.floor(Math.random() * 256) + "," + Math.floor(Math.random() * 256) + "," + Math.floor(Math.random() * 256) + ")";
}
// A renderer which checks that, upon receiving focus, the item's ARIA attributes
// are up-to-date for a Narrator announcement. The only attributes that are needed
// for a Narrator announcement are:
// - role
// - aria-posinset
// - aria-setsize
function createFocusCheckingRenderer(listViewElement) {
function onFocusForItem(eventObject) {
var item = eventObject.target,
lv = listViewElement.winControl,
index = lv._view.items.index(item);
LiveUnit.Assert.areEqual(lv._itemRole, item.getAttribute("role"), "Item should have an up-to-date role before receiving focus");
LiveUnit.Assert.areEqual((index + 1).toString(), item.getAttribute("aria-posinset"), "Item should have an up-to-date aria-posinset before receiving focus");
LiveUnit.Assert.areEqual("100", item.getAttribute("aria-setsize"), "Item should have an up-to-date aria-setsize before receiving focus");
}
function focusCheckingRenderer(itemPromise) {
return itemPromise.then(function (item) {
var element = document.createElement("div");
element.id = item.data.title;
element.style.width = item.data.itemWidth;
element.style.height = item.data.itemHeight;
element.innerHTML = item.data.title;
element.addEventListener("focus", onFocusForItem, false);
return element;
});
}
return focusCheckingRenderer;
}
function testFailingDataAdptor(validator, layoutName, failWhen, errorName, failingItem, async?, groups?, suffix?) {
function createDataSource(failWhen, error, failingItem, async) {
function asyncPromise() {
return async ? WinJS.Promise.timeout() : WinJS.Promise.wrap();
}
var itemsCount = 100,
previousError,
listDataAdaptor = {
itemsFromIndex: function (index, countBefore, countAfter) {
var startIndex = Math.max(0, index - countBefore),
size = Math.max(countBefore + 1, 3);
var items = [];
for (var i = startIndex; i < startIndex + size; i++) {
items.push({
key: i.toString(),
data: {
title: "Tile" + i,
group: Math.floor(i / 7)
}
});
}
return asyncPromise().then(function () {
if (previousError === "noResponse" || (failWhen === "itemsFromIndex" && startIndex <= failingItem && failingItem <= (startIndex + size - 1))) {
previousError = error;
return WinJS.Promise.wrapError(new WinJS.ErrorFromName(error));
} else {
return WinJS.Promise.wrap({
items: items,
offset: index - startIndex,
totalCount: previousError ? failingItem : itemsCount,
absoluteIndex: index
});
}
});
},
getCount: function () {
return asyncPromise().then(function () {
if (failWhen === "getCount" || previousError === "noResponse") {
return WinJS.Promise.wrapError(new WinJS.ErrorFromName(error));
} else {
return WinJS.Promise.wrap(previousError ? failingItem : itemsCount);
}
});
}
};
return new WinJS.UI.ListDataSource(listDataAdaptor);
}
ListViewTests.prototype["test" + upFirstChar(failWhen) + failingItem.toString() + "FailsWith" + upFirstChar(errorName) + "In" + suffix + (groups ? "WithGroups" : "") + (async ? "Async" : "")] = function (complete) {
Helper.initUnhandledErrors();
var element = document.createElement("div");
element.style.width = "300px";
element.style.height = "300px";
document.body.appendChild(element);
var dataSource = createDataSource(failWhen, errorName, failingItem, async);
function renderer(itemPromise) {
return itemPromise.then(function (item) {
var element = document.createElement("div");
element.textContent = item.data.title;
element.style.width = "100px";
element.style.height = "100px";
return element;
});
}
var listView;
if (groups) {
var groupedDataSource = createGroupedVDS(dataSource);
listView = new WinJS.UI.ListView(element, {
layout: new WinJS.UI[layoutName](),
itemDataSource: groupedDataSource,
groupDataSource: groupedDataSource.groups,
groupHeaderTemplate: renderer,
itemTemplate: renderer
});
} else {
listView = new WinJS.UI.ListView(element, {
layout: new WinJS.UI[layoutName](),
itemDataSource: dataSource,
itemTemplate: renderer
});
}
validator(listView).
then(Helper.validateUnhandledErrorsOnIdle).
done(function () {
element.winControl.dispose();
document.body.removeChild(element);
complete();
});
};
}
function upFirstChar(str) {
return str.charAt(0).toUpperCase() + str.substr(1);
}
function createGroupedVDS(vds) {
return WinJS.UI.computeDataSourceGroups(vds,
function (item) {
return item.data.group.toString();
}, function (item) {
return {
title: item.data.group.toString()
};
}
);
}
function defaultGetGroupInfo() {
return {
enableCellSpanning: true,
cellWidth: 100,
cellHeight: 100
};
}
function completeValidator(listView) {
return Helper.ListView.waitForReady(listView)().then(function () {
LiveUnit.Assert.areEqual(0, document.querySelectorAll(".win-progress").length, "Progress indicator visible");
});
}
function scrollAndCompleteValidator(scrollTo) {
return function (listView) {
return Helper.ListView.waitForReady(listView)().then(function () {
return listView.ensureVisible(scrollTo);
}).then(function () {
LiveUnit.Assert.areEqual(0, document.querySelectorAll(".win-progress").length, "Progress indicator visible");
});
}
}
export class ListViewTests {
setUp() {
WinJS.UI._VirtualizeContentsView._maxTimePerCreateContainers = 5;
defaultDisableCustomPagesPrefetch = WinJS.UI._VirtualizeContentsView._disableCustomPagesPrefetch;
}
tearDown() {
LiveUnit.LoggingCore.logComment("In tearDown");
WinJS.UI._VirtualizeContentsView._disableCustomPagesPrefetch = defaultDisableCustomPagesPrefetch;
}
testForceLayoutSavesContainers = function (complete) {
var listViewEl = document.createElement("DIV");
document.body.appendChild(listViewEl);
listViewEl.style.cssText = "height: 100px; width: 100px;";
var data = [];
for (var i = 0; i < 100; i++) {
data.push({
id: i,
title: "Item" + i
});
}
var bindingList = new WinJS.Binding.List(data);
var groupedList = bindingList.createGrouped(function (item) {
return "" + Math.floor(item.id / 10);
}, function (item) {
var groupId = Math.floor(item.id / 10);
return {
id: groupId,
title: "Group" + groupId
};
})
var listView = new WinJS.UI.ListView(listViewEl, {
itemDataSource: groupedList.dataSource,
groupDataSource: groupedList.groups.dataSource,
itemTemplate: function (itemPromise) {
return itemPromise.then(function (item) {
var element = document.createElement("DIV");
element.style.cssText = "width: 75px; height: 75px";
element.textContent = item.data.title;
element.style.backgroundColor = getRandomColor();
return element;
});
},
groupHeaderTemplate: function (itemPromise) {
return itemPromise.then(function (item) {
var element = document.createElement("DIV");
element.style.cssText = "width: 75px; height: 75px";
element.textContent = item.data.title;
element.style.backgroundColor = getRandomColor();
return element;
});
}
});
var tests = [
function () {
listViewEl.querySelector('.win-container')['expando'] = "woot1";
listViewEl.querySelector('.win-groupheadercontainer')['expando'] = "woot2";
listView.forceLayout();
return true;
},
function () {
LiveUnit.Assert.areEqual("woot1", listViewEl.querySelector('.win-container')['expando'], "Same item container");
LiveUnit.Assert.areEqual("woot2", listViewEl.querySelector('.win-groupheadercontainer')['expando'], "Same group header container");
complete();
}
];
Helper.ListView.runTests(listView, tests);
};
testCurrentItemChangesPivot = function (complete) {
var listViewEl = document.createElement("DIV");
document.body.appendChild(listViewEl);
listViewEl.style.cssText = "height: 100px; width: 100px;";
var listView = new ListView(listViewEl, {
itemDataSource: new WinJS.Binding.List([
{ title: 'a' },
{ title: 'b' },
{ title: 'c' },
{ title: 'd' },
{ title: 'e' },
{ title: 'f' },
{ title: 'g' },
{ title: 'h' }
]).dataSource,
itemTemplate: function (itemPromise) {
return itemPromise.then(function (item) {
var element = document.createElement("DIV");
element.style.cssText = "width: 75px; height: 75px";
element.textContent = item.data.title;
return element;
});
}
});
var tests = [
function () {
LiveUnit.Assert.areEqual(-1, listView.selection._pivot, "Pivot started off at -1");
listView.currentItem = { index: 1 };
LiveUnit.Assert.areEqual(1, listView.selection._pivot, "Pivot moved to 1");
listView.itemDataSource._list.unshift({ title: "i" });
LiveUnit.Assert.areEqual(1, listView.selection._pivot, "Pivot still stayed");
return true;
},
function () {
LiveUnit.Assert.areEqual(2, listView.selection._pivot, "Pivot moved after unshift");
listView.itemDataSource._list.unshift({ title: "j" });
LiveUnit.Assert.areEqual(2, listView.selection._pivot, "Pivot still stayed again");
listView.currentItem = { index: 1 };
LiveUnit.Assert.areEqual(1, listView.selection._pivot, "Pivot moved to 1 again");
return true;
},
function () {
LiveUnit.Assert.areEqual(1, listView.selection._pivot, "Pivot didn't move after unshift :-)");
complete();
}
];
Helper.ListView.runTests(listView, tests);
};
testListViewDispose = function (complete) {
var dispose = function () {
if (this.disposed) {
LiveUnit.Assert.fail("Disposed was called again.");
}
this.disposed = true;
itemsAlive--;
};
var data = [1, 2, 3, 4, 5, 6, 7];
var list = new WinJS.Binding.List(data);
var itemsAlive = 0;
var lv = new WinJS.UI.ListView();
lv.element.id = "lv";
document.body.appendChild(lv.element);
lv.itemTemplate = function (itemPromise) {
return itemPromise.then(function (item) {
var div = document.createElement("div");
div.textContent = item.data;
WinJS.Utilities.addClass(div, "win-disposable");
div.dispose = dispose.bind(div);
itemsAlive++;
return div;
});
};
lv.addEventListener("loadingstatechanged", function () {
LiveUnit.Assert.isTrue(itemsAlive > 1);
var element = lv.element;
lv.dispose();
LiveUnit.Assert.areEqual(itemsAlive, 0, "At least one element wasn't cleaned up.");
document.body.removeChild(element);
complete();
});
lv.itemDataSource = list.dataSource;
};
testEnsureVisibleWithVeryLargeItems = function (complete) {
var data = [];
for (var i = 0; i < 20; i++) {
data.push({ data: i });
}
var list = new WinJS.Binding.List(data);
var lv = new ListView();
lv.itemDataSource = list.dataSource;
lv.itemTemplate = function (itemPromise) {
return itemPromise.then(function (item) {
var div = document.createElement("div");
div.textContent = item.data.data;
div.style.height = "50px";
div.style.width = "5000px";
return div;
});
};
document.body.appendChild(lv.element);
lv.ensureVisible({ type: WinJS.UI.ObjectType.item, index: 0 });
var scrollPosition;
Helper.ListView.waitForReady(lv)().then(function () {
scrollPosition = lv.scrollPosition;
lv.ensureVisible({ type: WinJS.UI.ObjectType.item, index: 0 });
return Helper.ListView.waitForReady(lv, -1)();
}).done(function () {
LiveUnit.Assert.areEqual(scrollPosition, lv.scrollPosition);
complete();
});
};
testDisposeDuringReloadRaceCondition = function (complete) {
var data = [];
for (var i = 0; i < 20; i++) {
data.push({ data: i });
}
var list = new WinJS.Binding.List(data);
var filtered = list.createFiltered(function (item) {
return true;
});
var lv = new WinJS.UI.ListView();
lv.itemDataSource = filtered.dataSource;
document.body.appendChild(lv.element);
Helper.ListView.waitForReady(lv)().done(function () {
filtered.dispose();
document.body.removeChild(lv.element);
lv.dispose();
WinJS.Utilities._setImmediate(complete);
});
};
testFocusDuringGroupDataSourceChanging = function (complete) {
Helper.initUnhandledErrors();
var data = [];
for (var i = 0; i < 20; i++) {
data.push({ data: i });
}
var list = new WinJS.Binding.List(data);
var glist = list.createGrouped(function (item) {
return Math.floor(item.data / 10) + "";
}, function (item) {
return { data: Math.floor(item.data / 10) + "" };
});
var lv = new WinJS.UI.ListView();
lv.itemDataSource = glist.dataSource;
lv.groupDataSource = glist.groups.dataSource;
document.body.appendChild(lv.element);
Helper.ListView.waitForReady(lv)().
then(function () {
lv.groupDataSource = glist.groups.dataSource;
lv.element.focus();
return Helper.ListView.waitForReady(lv, -1)();
}).
then(function () {
document.body.removeChild(lv.element);
lv.dispose();
return Helper.validateUnhandledErrorsOnIdle();
}).
done(complete);
};
testCorrectRangeInFirstColumnForHeaders = function (complete) {
var data = [];
for (var i = 0; i < 200; i++) {
data.push({ data: i });
}
var list = new WinJS.Binding.List(data);
var glist = list.createGrouped(function (item) {
return Math.floor(item.data / 10) + "";
}, function (item) {
return { data: Math.floor(item.data / 10) + "" };
});
var lv = new ListView();
lv.itemDataSource = glist.dataSource;
lv.groupDataSource = glist.groups.dataSource;
lv.itemTemplate = function (itemPromise) {
return itemPromise.then(function (item) {
var div = document.createElement("div");
div.innerHTML = item.data.data;
div.style.width = "200px";
div.style.height = "200px";
return div;
});
};
document.body.appendChild(lv.element);
Helper.ListView.waitForReady(lv, -1)().then(function () {
(lv.element.querySelector(".win-surface")).style.margin = "20px";
lv.scrollPosition = 400;
return Helper.ListView.waitForReady(lv, -1)();
}).then(function () {
lv.ensureVisible({ type: WinJS.UI.ObjectType.groupHeader, index: 0 });
return Helper.ListView.waitForReady(lv, -1)();
}).done(function () {
LiveUnit.Assert.areEqual(0, lv.scrollPosition);
complete();
});
};
testEnsureVisibleHeaderPartiallyOffscreen = function (complete) {
var data = [];
for (var i = 0; i < 20; i++) {
data.push({ data: i });
}
var list = new WinJS.Binding.List(data);
var glist = list.createGrouped(function (item) {
return item.data + "";
}, function (item) {
return { data: item.data + "" };
});
var lv = new ListView();
lv.element.style.width = "250px";
lv.itemDataSource = glist.dataSource;
lv.groupDataSource = glist.groups.dataSource;
document.body.appendChild(lv.element);
var groupWidth;
var scrollPosition;
Helper.ListView.waitForReady(lv, -1)().then(function () {
// Add 70px because groupleaders have 70px margins
groupWidth = (document.body.querySelector(".win-groupheadercontainer")).offsetWidth + 70;
lv.ensureVisible({ type: WinJS.UI.ObjectType.groupHeader, index: 10 });
return Helper.ListView.waitForReady(lv, -1)();
}).then(function () {
lv.ensureVisible({ type: WinJS.UI.ObjectType.groupHeader, index: 2 });
return Helper.ListView.waitForReady(lv, -1)();
}).then(function () {
// The 3rd header should be left aligned and the 4th header should be partially visible
scrollPosition = lv.scrollPosition;
lv.ensureVisible({ type: WinJS.UI.ObjectType.groupHeader, index: 3 });
return Helper.ListView.waitForReady(lv, -1)();
}).then(function () {
LiveUnit.Assert.isTrue(groupWidth >= Math.abs(lv.scrollPosition - scrollPosition), "View should not have scrolled more than the width of an entire group");
scrollPosition = lv.scrollPosition;
lv.ensureVisible({ type: WinJS.UI.ObjectType.groupHeader, index: 4 });
return Helper.ListView.waitForReady(lv, -1)();
}).done(function () {
LiveUnit.Assert.isTrue(groupWidth >= Math.abs(lv.scrollPosition - scrollPosition), "View should not have scrolled more than the width of an entire group");
complete();
});
};
testEnsureVisibleHeaderFullyVisibleGroupPartiallyOffscreen = function (complete) {
var data = [];
for (var i = 0; i < 30; i++) {
data.push({ data: i });
}
var list = new WinJS.Binding.List(data);
var glist = list.createGrouped(function (item) {
return Math.floor(item.data / 3) + "";
}, function (item) {
return { data: Math.floor(item.data / 3) + "" };
});
var lv = new ListView();
lv.element.id = "EnsureVisibleHeaderTest";
lv.itemDataSource = glist.dataSource;
lv.groupDataSource = glist.groups.dataSource;
document.body.appendChild(lv.element);
Helper.ListView.waitForReady(lv, -1)().then(function () {
lv.ensureVisible({ type: WinJS.UI.ObjectType.groupHeader, index: 1 });
return Helper.ListView.waitForReady(lv, -1)();
}).then(function () {
LiveUnit.Assert.areEqual(0, lv.scrollPosition, "GroupHeader 1 is fully visible and no scrolling should have occured.");
complete();
});
};
// Verifies that when the data source is synchronous, the item promises that are passed to the
// item renderer are complete at the time that the renderer is called.
// Regression test for WinBlue#225665.
testCollapsingOfStages0And1 = function (complete) {
var rendererRan = false;
function basicRenderer(itemPromise) {
return itemPromise.then(function (item) {
var element = document.createElement("div");
element.style.width = item.data.itemWidth;
element.style.height = item.data.itemHeight;
element.innerHTML = item.data.title;
return element;
});
}
function verifyingRenderer(itemPromise) {
var sync = false;
itemPromise.then(function () {
sync = true;
});
LiveUnit.Assert.isTrue(sync, "itemPromise should have completed synchronously");
rendererRan = true;
return basicRenderer(itemPromise);
}
var div = document.createElement("DIV"),
cleanup = parent(div),
myData = [],
lv;
div.style.width = "500px";
div.style.height = "100px";
for (var i = 0; i < 100; ++i) {
myData.push({
title: "Tile" + i,
itemWidth: "100px",
itemHeight: "75px"
});
}
lv = new WinJS.UI.ListView(div, {
itemDataSource: new WinJS.Binding.List(myData).dataSource,
itemTemplate: verifyingRenderer
});
Helper.ListView.waitForReady(lv)().then(function () {
LiveUnit.Assert.isTrue(rendererRan, "Item renderer should have ran");
cleanup();
complete();
});
};
// Verifies that the app will not crash when the image loader is used, the image
// load fails, and the app handles the error thru the loadImage promise.
// Regression test for WinBlue#138768.
testLoadImageError = function (complete) {
Helper.initUnhandledErrors();
function imageLoadingRenderer(itemPromise) {
var el = document.createElement("div");
var imgEl = document.createElement("img");
el.appendChild(imgEl);
el.style.width = "100px";
el.style.height = "75px";
el.style.backgroundColor = 'steelblue';
return {
element: el,
renderComplete: itemPromise.then(function (item) {
return item.loadImage("foo", imgEl).then(function () { // use image loader with a bad uri
LiveUnit.Assert.fail("Image should not have loaded successfully");
}, function () {
pendingImageCount--;
});
})
};
}
var div = document.createElement("DIV"),
cleanup = parent(div),
pendingImageCount = 3,
lv;
div.style.width = "500px";
div.style.height = "100px";
lv = new WinJS.UI.ListView(div, {
itemDataSource: new WinJS.Binding.List(['', '', '']).dataSource,
itemTemplate: imageLoadingRenderer
});
Helper.ListView.waitForReady(lv, -1)().
then(function () {
LiveUnit.Assert.areEqual(0, pendingImageCount, "All of the images should have errored");
lv.itemDataSource.change("0", '');
return Helper.ListView.waitForReady(lv, -1)();
}).
then(Helper.validateUnhandledErrorsOnIdle).
done(function () {
cleanup();
complete();
});
};
// Verifies that LayoutCommon's measuring code properly handles a synchronous resize. In other
// words, when reading from the DOM in the middle of the measuring function, user code may run
// (resize handlers) which invalidates the state of ListView's layout. The measuring code should
// handle this.
// Regression test for WinBlue#390892
testResizeDuringMeasuring = function (complete) {
Helper.initUnhandledErrors();
function onResize() {
lv.layout = makeLayout();
}
function makeLayout() {
return new WinJS.UI.GridLayout();
}
function cleanUp() {
lv._elementResizeInstrument.removeEventListener("resize", onResize);
unparent();
}
var div = document.createElement("DIV"),
unparent = parent(div),
myData = [],
lv: WinJS.UI.PrivateListView;
div.style.width = "500px";
div.style.height = "100px";
for (var i = 0; i < 100; ++i) {
myData.push({
title: "Tile" + i,
itemWidth: "100px",
itemHeight: "75px"
});
}
lv = > new WinJS.UI.ListView(div, {
itemDataSource: new WinJS.Binding.List(myData).dataSource,
itemTemplate: Helper.ListView.templates.syncJSTemplate,
layout: makeLayout()
});
Helper.ListView.waitForReady(lv)().then(function () {
var layout = lv.layout,
origMeasureItem = layout._measureItem;
lv._elementResizeInstrument.addEventListener("resize", onResize);
// Hook the measuring function so that we can cause resize handlers to run in the middle of it.
layout._measureItem = function () {
layout._measureItem = origMeasureItem;
lv.element.style.width = "510px";
return layout._measureItem.apply(this, arguments);
}
// Trigger a layout & measuring pass. During measuring, we will trigger resize handlers to run.
lv.itemDataSource = lv.itemDataSource;
lv.recalculateItemPosition();
return Helper.ListView.waitForReady(lv, -1)();
}).then(function () {
return Helper.validateUnhandledErrorsOnIdle();
}).then(null, function () {
}).then(function () {
cleanUp();
complete();
});
};
// Verifies that the cell spanning measuring code in GridLayout properly handles a
// synchronous resize. In other words, when reading from the DOM in the middle of the
// measuring function, user code may run (resize handlers) which invalidates the state
// of ListView's layout. The measuring code should handle this. This affects WinJS.UI.GridLayouts
// with cell spanning groups which use the default itemInfo function.
// Regression test for WinBlue#384025.
testResizeDuringCellSpanningMeasuring = function (complete) {
if (!Helper.Browser.supportsCSSGrid) {
LiveUnit.LoggingCore.logComment("Cellspanning layout not supported on this platform.");
complete();
return;
}
Helper.initUnhandledErrors();
function onResize() {
lv.layout = makeLayout();
}
function groupInfo() {
return {
enableCellSpanning: true,
cellWidth: 100,
cellHeight: 75
};
}
function makeLayout() {
return new WinJS.UI.GridLayout({
groupInfo: groupInfo
});
}
function verifyContainer(data) {
var container = lv._view.containers[data.index];
LiveUnit.Assert.areEqual(data.textContent, container.textContent.trim(),
"win-container " + data.index + " has incorrect textContent");
LiveUnit.Assert.areEqual(data.offsetLeft, container.offsetLeft,
"win-container " + data.index + " has incorrect offsetLeft");
LiveUnit.Assert.areEqual(data.offsetTop, container.offsetTop,
"win-container " + data.index + " has incorrect offsetTop");
}
function cleanUp() {
lv._elementResizeInstrument.removeEventListener("resize", onResize);
unparent();
}
var div = document.createElement("DIV"),
unparent = parent(div),
myData = [],
lv: WinJS.UI.PrivateListView;
div.style.width = "500px";
div.style.height = "100px";
for (var i = 0; i < 100; ++i) {
myData.push({
title: "Tile" + i,
itemWidth: "100px",
itemHeight: "75px"
});
}
lv = > new WinJS.UI.ListView(div, {
itemDataSource: new WinJS.Binding.List(myData).dataSource,
itemTemplate: Helper.ListView.templates.syncJSTemplate,
layout: makeLayout()
});
Helper.ListView.waitForReady(lv)().then(function () {
var layout = lv.layout,
origMeasureElements = layout._measureElements;
lv._elementResizeInstrument.addEventListener("resize", onResize);
// Hook the measuring function so that we can cause resize handlers to run in the middle of it.
layout._measureElements = function () {
layout._measureElements = origMeasureElements;
// Schedule a job to do a resize immediately before the measuring job runs
// (_measureElements schedules its measuring in a job at high).
WinJS.Utilities.Scheduler.schedulePromiseHigh().then(function () {
lv.element.style.width = "510px";
});
return layout._measureElements.apply(this, arguments);
}
// Trigger a layout & measuring pass. During measuring, we will trigger resize handlers to run.
lv.itemDataSource.list.unshift({
title: "NewTile0",
itemWidth: "100px",
itemHeight: "75px"
});
return Helper.ListView.waitForReady(lv, -1)();
}).then(function () {
return Helper.validateUnhandledErrorsOnIdle();
}).then(function () {
verifyContainer({
index: 0,
textContent: "NewTile0",
offsetLeft: 5,
offsetTop: 5
});
verifyContainer({
index: 1,
textContent: "Tile0",
offsetLeft: 115,
offsetTop: 5
});
}).done(function () {
cleanUp();
complete();
});
};
testDeleteBeforeListViewLoadingStateComplete = function (complete) {
Helper.initUnhandledErrors();
function render(item) {
var div = document.createElement("div");
div.textContent = item.data.title;
div.style.width = div.style.height = "300px";
return div;
};
var data = [1, 2, 3];
var list = new WinJS.Binding.List(data);
var lv = new ListView();
lv.itemDataSource = list.dataSource;
lv.itemTemplate = function (itemPromise) {
return itemPromise.then(function (item): any {
if (item.index <= 1) {
return render(item);
} else {
return new WinJS.Promise(function (c) {
setTimeout(function () {
c(render(item));
}, 1500);
});
}
});
};
document.body.appendChild(lv.element);
setTimeout(function () {
lv.currentItem = { type: WinJS.UI.ObjectType.item, index: 0, hasFocus: true, showFocus: true };
lv.itemDataSource.list.shift();
Helper.ListView.waitForReady(lv, -1)().
then(Helper.validateUnhandledErrorsOnIdle).
done(complete);
}, 1000);
};
//WinBlue: 297330
xtestMirageHandling = function (complete) {
Helper.initUnhandledErrors();
var dataSourceImpl = {
itemsFromIndex: function (index, countBefore, countAfter) {
var startIndex = Math.max(0, index - countBefore),
endIndex = Math.min(index + countAfter, 999);
var items = [];
for (var i = startIndex; i < 10 && i <= endIndex; i++) {
items.push({
key: i.toString(),
data: {
title: "Tile" + i
}
});
}
return WinJS.Promise.timeout().then(function () {
return {
items: items,
offset: index - startIndex,
totalCount: endIndex >= 10 ? 10 : 1000,
absoluteIndex: index
};
});
},
getCount: function () {
return WinJS.Promise.wrap(1000);
}
};
var element = document.createElement("DIV");
element.style.width = element.style.height = "500px";
document.body.appendChild(element);
var listView = new WinJS.UI.ListView(element, {
itemDataSource: new WinJS.UI.ListDataSource(dataSourceImpl),
itemTemplate: function (itemPromise) {
var e = document.createElement("div");
itemPromise.then(function (item) {
e.textContent = item.data.title;
});
return e;
}
});
Helper.ListView.waitForReady(listView)().
then(Helper.validateUnhandledErrorsOnIdle).
done(function () {
element.winControl.dispose();
document.body.removeChild(element);
complete();
});
};
testFocusHeaderBeforeAnyAreRendered = function () {
var data = [];
for (var i = 0; i < 30; i++) {
data.push({ data: i });
}
var list = new WinJS.Binding.List(data);
var glist = list.createGrouped(function (item) {
return Math.floor(item.data / 3) + "";
}, function (item) {
return { data: Math.floor(item.data / 3) + "" };
});
var lv = new ListView();
lv.itemDataSource = glist.dataSource;
lv.groupDataSource = glist.groups.dataSource;
document.body.appendChild(lv.element);
// To make listView delegate focus to a header, we want to shift-tab
// into the listView, the detail field in the eventArgs on tabEntered
// specifies the tab direction, 0 is shift-tab, 1 is tab.
lv._mode.onTabEntered({ detail: 0, srcElement: lv._canvas, preventDefault: function () { } });
document.body.removeChild(lv.element);
lv.dispose();
};
testUpdaterDoesNotIncorrectlyChangesFocusIndex = function (complete) {
var data = [];
for (var i = 0; i < 10; i++) {
data.push({ data: i });
}
var list = new WinJS.Binding.List(data);
var glist = list.createGrouped(function (item) {
return "1";
}, function (item) {
return { data: "1" };
});
var lv = new ListView();
lv.itemDataSource = glist.dataSource;
lv.groupDataSource = glist.groups.dataSource;
document.body.appendChild(lv.element);
Helper.ListView.waitForReady(lv, -1)().then(function () {
lv._mode.onTabEntered({ detail: 0, preventDefault: function () { } });
return Helper.ListView.waitForReady(lv, -1)();
}).then(function () {
glist.dataSource.insertAtStart(null, { data: "new" });
return Helper.ListView.waitForReady(lv, -1)();
}).done(function () {
lv._mode.onTabEntered({ detail: 1, srcElement: lv._canvas, preventDefault: function () { } });
document.body.removeChild(lv.element);
lv.dispose();
complete();
});
};
testTabbingAfterDeletingAllGroupsDoesNotCrash = function (complete) {
var data = [];
for (var i = 0; i < 1; i++) {
data.push({ data: i });
}
var list = new WinJS.Binding.List(data);
var glist = list.createGrouped(function (item) {
return "0";
}, function (item) {
return { data: "0" };
});
var lv = new ListView();
lv.itemDataSource = glist.dataSource;
lv.groupDataSource = glist.groups.dataSource;
document.body.appendChild(lv.element);
Helper.ListView.waitForReady(lv, -1)().then(function () {
lv._mode.onTabEntered({ detail: 0, preventDefault: function () { } });
return Helper.ListView.waitForReady(lv, -1)();
}).then(function () {
glist.dataSource.remove("0");
return Helper.ListView.waitForReady(lv, -1)();
}).done(function () {
lv._mode.onTabEntered({ detail: 1, srcElement: lv._canvas, preventDefault: function () { } });
document.body.removeChild(lv.element);
lv.dispose();
complete();
});
};
testSetScrollPositionToHighValidValueBeforeAllContainersAreCreated = function (complete) {
var data = [];
for (var i = 0; i < 1000; i++) {
data.push({
title: i
});
}
var element = document.createElement("div");
element.style.width = "600px";
element.style.height = "600px";
document.body.appendChild(element);
var listView = new ListView(element, {
itemDataSource: new WinJS.Binding.List(data).dataSource,
itemTemplate: function (itemPromise) {
var div = document.createElement("div");
div.style.width = "100px";
div.style.height = "100px";
return {
element: div,
renderComplete: itemPromise.then(function (item) {
div.textContent = item.data.title;
})
};
}
});
// In this test even on a fast machine we want to ensure that we create containers in multiple stages
WinJS.UI._VirtualizeContentsView._maxTimePerCreateContainers = -1;
var jobNode;
listView._view._scheduleLazyTreeCreation = function () {
jobNode = Object.getPrototypeOf(listView._view)._scheduleLazyTreeCreation.call(listView._view);
// pause the tree creation
jobNode.pause();
return jobNode;
};
Helper.ListView.waitForReady(listView, -1)().done(function () {
// calculate a scroll position beyond the current valid scroll position
var scrollValue = listView._viewport.scrollWidth + 1000;
// resumes tree creation
jobNode.resume();
listView.scrollPosition = scrollValue;
Helper.ListView.waitForReady(listView, -1)().done(function () {
LiveUnit.Assert.areEqual(scrollValue, listView.scrollPosition, "The scroll position is invalid");
complete();
});
});
};
testDataSourceCancelGetCountPromises = function (complete) {
Helper.initUnhandledErrors();
var placeholder = document.createElement("div");
placeholder.style.width = placeholder.style.height = "300px";
document.body.appendChild(placeholder);
var signal = new WinJS._Signal();
function createDataSource() {
var COUNT = 4;
var dataSource = {
itemsFromIndex: function (index, countBefore, countAfter) {
return new WinJS.Promise(function (complete) {
if (index >= 0 && index < COUNT) {
var startIndex = Math.max(0, index - countBefore),
endIndex = Math.min(index + Math.max(20, countAfter), COUNT - 1),
size = endIndex - startIndex + 1;
var items = [];
for (var i = startIndex; i < startIndex + size; i++) {
items.push({
key: i.toString(),
data: {
title: i
}
});
}
complete({
items: items,
offset: index - startIndex,
totalCount: COUNT,
absoluteIndex: index
});
} else {
complete({});
}
});
},
getCount: function () {
return signal.promise.then(function () {
return COUNT;
});
}
};
return new WinJS.UI.ListDataSource(dataSource);
}
var list,
listView = new WinJS.UI.ListView(placeholder, {
itemDataSource: createDataSource(),
itemTemplate: function render(itemPromise) {
var elem = document.createElement("div");
elem.style.backgroundColor = "blue";
elem.style.width = elem.style.height = "90px";
itemPromise.then(function (item) {
elem.textContent = item.data.title;
});
return elem;
}
});
WinJS.Promise.timeout(100).then(function () {
var data = [];
for (var i = 0; i < 15; i++) {
data.push({
title: i
});
}
list = new WinJS.Binding.List(data);
listView.itemDataSource = list.dataSource;
return Helper.ListView.waitForReady(listView, -1)();
}).then(function () {
LiveUnit.Assert.areEqual(15, placeholder.querySelectorAll('.win-container').length);
signal.complete();
list.shift();
list.push({
title: "NewItem"
});
return Helper.ListView.waitForReady(listView, -1)();
}).then(Helper.validateUnhandledErrorsOnIdle).done(function () {
document.body.removeChild(placeholder);
complete();
});
};
}
function generateBug17087(layoutName) {
ListViewTests.prototype["testBug17087" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) {
var listViewEl = document.createElement("DIV");
document.body.appendChild(listViewEl);
listViewEl.style.cssText = "height: 100px; width: 100px;";
var listView = new WinJS.UI.ListView(listViewEl, {
layout: new WinJS.UI[layoutName](),
itemDataSource: new WinJS.Binding.List([
{ title: 'a' },
{ title: 'b' },
{ title: 'c' },
{ title: 'd' },
{ title: 'e' },
{ title: 'f' },
{ title: 'g' },
{ title: 'h' }
]).dataSource,
itemTemplate: function (itemPromise) {
return itemPromise.then(function (item) {
var element = document.createElement("DIV");
element.style.cssText = "width: 75px; height: 75px";
element.textContent = item.data.title;
return element;
});
}
});
// This causes us to get an error in the scrollToPromise.
// We should probably retry the scrollToPromise to get to 3 once
// we are able to get a height/width. However currently we just
// claim this is not supported and the app should recall indexOfFirstVisible
// themselves.
listViewEl.style.display = "none";
listView.indexOfFirstVisible = 3;
listViewEl.addEventListener('loadingstatechanged', function (ev) {
if (listView.loadingState === "complete") {
if (listView.indexOfFirstVisible === -1) {
listViewEl.style.display = "block";
// With bug 17087 we would not actually do another realize pass
// because we had a bad scrollToPromise.
listView.forceLayout();
} else {
WinJS.Utilities.disposeSubTree(listViewEl);
document.body.removeChild(listViewEl);
complete();
}
}
});
};
};
generateBug17087("GridLayout");
function generateListViewInstantiation(layoutName) {
ListViewTests.prototype["testListViewInstantiation" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) {
var div = document.createElement("DIV");
var cleanup = parent(div);
WinJS.Promise.wrap().then(function () {
var lv = new WinJS.UI.ListView(div, {
layout: new WinJS.UI[layoutName]()
});
}).
then(null, errorHandler).
then(cleanup).
then(complete);
};
};
generateListViewInstantiation("GridLayout");
// When the viewport has focus and the focused item becomes unrealized, ensure
// that the viewport maintains focus (rather than moving focus to the
// keyboardEventsHelper).
// This is important when scrolling with Narrator Touch. During scrolling,
// Narrator Touch moves focus to the viewport and we want to leave focus
// on the viewport for a seamless scrolling experience.
// Also ensure that when the focused item is re-realized, its ARIA attributes are
// updated and then it receives focus.
function generateViewportFocus(layoutName) {
ListViewTests.prototype["testViewportFocus" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) {
var div = document.createElement("DIV"),
cleanup = parent(div),
myData = [],
lv;
div.style.width = "500px";
div.style.height = "100px";
for (var i = 0; i < 100; ++i) {
myData.push({
title: "Tile" + i,
itemWidth: "100px",
itemHeight: "75px"
});
}
lv = new WinJS.UI.ListView(div, {
layout: new WinJS.UI[layoutName](),
itemDataSource: new WinJS.Binding.List(myData).dataSource,
itemTemplate: createFocusCheckingRenderer(div)
});
// Give focus to ListView so that item 0 will receive focus
div.focus();
Helper.ListView.waitForDeferredAction(lv)().then(function () {
LiveUnit.Assert.isTrue(!!lv.elementFromIndex(0), "Item 0 is not in the DOM");
LiveUnit.Assert.areEqual(lv.elementFromIndex(0), document.activeElement, "Item 0 doesn't have focus");
// Give the viewport focus to simulate the scrolling scenario in Narrator Touch
lv._viewport.focus();
// Scroll 10 pages so that the focused item becomes unrealized and ListView must
// decide where to put the focus.
lv.scrollPosition = 5000;
return Helper.ListView.waitForDeferredAction(lv)();
}).then(function () {
LiveUnit.Assert.isFalse(!!lv.elementFromIndex(0), "Item 0 is in the DOM");
LiveUnit.Assert.areEqual(lv._viewport, document.activeElement, "Viewport doesn't have focus");
// Scroll back to the beginning so that the focused item will become realized and receive focus
lv.scrollPosition = 0;
return Helper.ListView.waitForDeferredAction(lv)();
}).then(function () {
LiveUnit.Assert.areEqual(lv.elementFromIndex(0), document.activeElement, "Item 0 doesn't have focus after scrolling to the beginning");
cleanup();
complete();
});
};
};
generateViewportFocus("GridLayout");
function generateInsertAtEndOnHiddenListView(layoutName) {
ListViewTests.prototype["testInsertAtEndOnHiddenListView" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) {
Helper.initUnhandledErrors();
function generateRenderer(size) {
return function (itemPromise) {
return itemPromise.then(function (item) {
var element = document.createElement("div");
element.textContent = item.data.title;
element.style.width = element.style.height = size;
return element;
});
}
}
function groupKey(data) {
return data.group.toString();
}
function groupData(data) {
return {
title: data.group.toString()
};
}
var list = new WinJS.Binding.List([{ title: "Tile0", group: "0" }]);
var myGroupedList = list.createGrouped(groupKey, groupData);
var placeholder = document.createElement("DIV");
document.body.appendChild(placeholder);
var listView = new WinJS.UI.ListView(placeholder, {
itemDataSource: myGroupedList.dataSource,
groupDataSource: myGroupedList.groups.dataSource,
itemTemplate: generateRenderer("100px"),
groupHeaderTemplate: generateRenderer("50px"),
layout: new WinJS.UI[layoutName](),
});
var completed = 0;
listView.addEventListener('loadingstatechanged', function (ev) {
if (listView.loadingState === 'complete') {
completed++;
listView.element.style.display = "none";
if (completed === 1) {
listView.itemDataSource.insertAtEnd(null, { group: "0", title: "Tile 1" });
WinJS.Utilities._setImmediate(function () {
Helper.validateUnhandledErrorsOnIdle().
done(function () {
WinJS.Utilities.disposeSubTree(placeholder);
document.body.removeChild(placeholder);
complete();
});
});
}
}
});
};
};
generateInsertAtEndOnHiddenListView("GridLayout");
// Ensure that if the focused item is changed one or more times in a
// notification cycle, then it retains focus.
function generateChangeFocusedItem(layoutName) {
ListViewTests.prototype["testChangeFocusedItem" + (layoutName == "GridLayout" ? "" : layoutName)] = function (complete) {
var newTileCounter = 0;
function currentTitle() {
return "NewTile" + newTileCounter;
}
function newItem() {
newTileCounter++;
return {
title: currentTitle(),
itemWidth: "100px",
itemHeight: "75px"
};
}
var div = document.createElement("DIV"),
cleanup = parent(div),
myData = [],
lv;
div.style.width = "500px";
div.style.height = "100px";
for (var i = 0; i < 100; ++i) {
myData.push({
title: "Tile" + i,
itemWidth: "100px",
itemHeight: "75px"
});
}
lv = new WinJS.UI.ListView(div, {
layout: new WinJS.UI[layoutName](),
itemDataSource: new WinJS.Binding.List(myData).dataSource,
itemTemplate: createFocusCheckingRenderer(div)
});
var tests = [
function () {
// Give focus to item 1
lv.currentItem = { index: 1, hasFocus: true };
LiveUnit.Assert.areEqual(lv.elementFromIndex(1), document.activeElement, "Item 1 doesn't have focus");
// Ensure focused item retains focus after being changed
lv.itemDataSource.change("1", newItem());
return true;
},
function () {
LiveUnit.Assert.areEqual(currentTitle(), lv.elementFromIndex(1).innerHTML, "Item 1 doesn't have correct title after change");
LiveUnit.Assert.areEqual(lv.elementFromIndex(1), document.activeElement, "Item 1 doesn't have focus after change");
// Ensure focused item retains focus after being changed multiple items in a notification cycle
lv.itemDataSource.beginEdits();
lv.itemDataSource.change("1", newItem());
lv.itemDataSource.change("1", newItem());
lv.itemDataSource.endEdits();
return true;
},
function () {
LiveUnit.Assert.areEqual(currentTitle(), lv.elementFromIndex(1).innerHTML, "Item 1 doesn't have correct title after 2 changes");
LiveUnit.Assert.areEqual(lv.elementFromIndex(1), document.activeElement, "Item 1 doesn't have focus after 2 changes");
cleanup();
complete();
}
];
Helper.ListView.runTests(lv, tests);
};
};
generateChangeFocusedItem("GridLayout");
function generateListViewDisposeTest(layoutName) {
ListViewTests.prototype["testListViewDispose" + layoutName] = function (complete) {
var dispose = function () {
if (this.disposed) {
LiveUnit.Assert.fail("Disposed was called again.");
}
this.disposed = true;
itemsAlive--;
};
var data = [1, 2, 3, 4, 5, 6, 7];
var list = new WinJS.Binding.List(data);
var itemsAlive = 0;
var lv = new WinJS.UI.ListView();
WinJS.Application.start();
lv.element.id = "lv";
document.body.appendChild(lv.element);
lv.layout = new WinJS.UI[layoutName]();
lv.itemTemplate = function (itemPromise) {
return itemPromise.then(function (item) {
var div = document.createElement("div");
div.textContent = item.data;
WinJS.Utilities.addClass(div, "win-disposable");
div.dispose = dispose.bind(div);
itemsAlive++;
return div;
});
};
lv.addEventListener("loadingstatechanged", function () {
LiveUnit.Assert.isTrue(itemsAlive > 1);
var element = lv.element;
lv.dispose();
LiveUnit.Assert.areEqual(itemsAlive, 0, "At least one element wasn't cleaned up.");
document.body.removeChild(element);
WinJS.Utilities.Scheduler.requestDrain(WinJS.Utilities.Scheduler.Priority.idle).done(function () {
WinJS.Application.stop();
complete();
});
});
lv.itemDataSource = list.dataSource;
}
};
generateListViewDisposeTest("GridLayout");
function generateListViewDisposeDuringVirtualizationTest(layoutName) {
WinJS.UI._VirtualizeContentsView._disableCustomPagesPrefetch = true;
ListViewTests.prototype["testListViewDisposeDuringVirtualization" + layoutName] = function (complete) {
var disposing = false;
var dispose = function () {
if (this.disposed && !disposing) {
LiveUnit.Assert.fail("Disposed was called again.");
}
this.disposed = true;
itemsAlive--;
var data = parseInt(this.textContent);
if (!disposing && data > 4) {
LiveUnit.Assert.fail("Only items 0-4 should get disposed as we are scrolling to item 7, meaning items 5-9 should be alive");
}
};
var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
var list = new WinJS.Binding.List(data);
var itemsAlive = 0;
var lv = new WinJS.UI.ListView();
lv.element.id = "lv";
document.body.appendChild(lv.element);
var cs = window.getComputedStyle(lv.element);
var height = cs.height;
var width = cs.width;
lv.layout = new WinJS.UI[layoutName]();
lv.itemTemplate = function (itemPromise) {
return itemPromise.then(function (item) {
var div = document.createElement("div");
div.textContent = item.data;
div.style.height = height;
div.style.width = width;
WinJS.Utilities.addClass(div, "win-disposable");
div.dispose = dispose.bind(div);
itemsAlive++;
return div;
});
};
lv.itemDataSource = list.dataSource;
Helper.ListView.waitForReady(lv)().then(function () {
// Initialized, since each item should take up roughly the entire viewport, we should have 1 + 2xPagesToLoad pages alive.
LiveUnit.Assert.areEqual(1 + 1 * WinJS.UI._VirtualizeContentsView._defaultPagesToPrefetch, itemsAlive);
lv.ensureVisible(7);
return Helper.ListView.waitForDeferredAction(lv)();
}).then(function () {
// This is called after scrolling is done and items have been disposed, the current number of live items should be 1 + 2xPagesToLoad again.
LiveUnit.Assert.areEqual(1 + 2 * WinJS.UI._VirtualizeContentsView._defaultPagesToPrefetch, itemsAlive);
var element = lv.element;
disposing = true;
lv.dispose();
document.body.removeChild(element);
complete();
});
};
};
generateListViewDisposeDuringVirtualizationTest("GridLayout");
function testFailingEdits(name, layoutName, error, action, async, groups, suffix) {
function createDataSource() {
function asyncPromise() {
return async ? WinJS.Promise.timeout(10) : WinJS.Promise.wrap();
}
var itemsCount = 100,
listDataAdaptor = {
itemsFromIndex: function (index, countBefore, countAfter) {
var startIndex = Math.max(0, index - countBefore),
size = Math.max(countBefore + 1, 3);
var items = [];
for (var i = startIndex; i < startIndex + size; i++) {
items.push({
key: i.toString(),
data: {
title: "Tile" + i,
group: Math.floor(i / 7)
}
});
}
return asyncPromise().then(function () {
return {
items: items,
offset: index - startIndex,
totalCount: itemsCount,
absoluteIndex: index
};
});
},
getCount: function () {
return asyncPromise().then(function () {
return itemsCount;
});
},
insertBefore: function (key, data, nextKey) {
return asyncPromise().then(function () {
return WinJS.Promise.wrapError(new WinJS.ErrorFromName(error));
});
},
remove: function (key) {
return asyncPromise().then(function () {
return WinJS.Promise.wrapError(new WinJS.ErrorFromName(error));
});
},
change: function (key, newData) {
return asyncPromise().then(function () {
return WinJS.Promise.wrapError(new WinJS.ErrorFromName(error));
});
},
moveBefore: function (key, nextKey) {
return asyncPromise().then(function () {
return WinJS.Promise.wrapError(new WinJS.ErrorFromName(error));
});
},
};
return new WinJS.UI.ListDataSource(listDataAdaptor);
}
function checkTile(listview, index, text) {
var tile = listview.elementFromIndex(index);
LiveUnit.Assert.areEqual(text, tile.textContent);
}
ListViewTests.prototype["testFailDuring" + upFirstChar(name) + "With" + upFirstChar(error) + "In" + suffix + (groups ? "WithGroups" : "") + (async ? "Async" : "")] = function (complete) {
Helper.initUnhandledErrors();
var element = document.createElement("div");
element.style.width = "300px";
element.style.height = "300px";
document.body.appendChild(element);
var dataSource = createDataSource();
function renderer(itemPromise) {
return itemPromise.then(function (item) {
var element = document.createElement("div");
element.textContent = item.data.title;
element.style.width = "100px";
element.style.height = "100px";
return element;
});
}
var listView;
if (groups) {
var groupedDataSource = createGroupedVDS(dataSource);
listView = new WinJS.UI.ListView(element, {
layout: new WinJS.UI[layoutName](),
itemDataSource: groupedDataSource,
groupDataSource: groupedDataSource.groups,
groupHeaderTemplate: renderer,
itemTemplate: renderer
});
} else {
listView = new WinJS.UI.ListView(element, {
layout: new WinJS.UI[layoutName](),
itemDataSource: dataSource,
itemTemplate: renderer
});
}
Helper.ListView.waitForReady(listView)().
then(function () {
checkTile(listView, 0, "Tile0");
checkTile(listView, 1, "Tile1");
checkTile(listView, 2, "Tile2");
return action(listView.itemDataSource);
}).
then(function () {
return WinJS.Promise.timeout();
}).
then(function () {
return listView.itemDataSource.getCount();
}).
then(function (count) {
if (count) {
checkTile(listView, 0, "Tile0");
checkTile(listView, 1, "Tile1");
checkTile(listView, 2, "Tile2");
}
return Helper.validateUnhandledErrorsOnIdle();
}).
done(function () {
element.winControl.dispose();
document.body.removeChild(element);
complete();
});
};
}
function generateTest(validator, failWhen, error, failingItem) {
testFailingDataAdptor(validator, "GridLayout", failWhen, error, failingItem, false, false, "UniformGrid");
testFailingDataAdptor(validator, "GridLayout", failWhen, error, failingItem, true, false, "UniformGrid");
if (Helper.Browser.supportsCSSGrid) {
testFailingDataAdptor(validator, "GridLayout", failWhen, error, failingItem, false, false, "Multisize");
}
testFailingDataAdptor(validator, "GridLayout", failWhen, error, failingItem, false, true, "UniformGrid");
testFailingDataAdptor(validator, "GridLayout", failWhen, error, failingItem, false, false, "UniformGrid");
testFailingDataAdptor(validator, "GridLayout", failWhen, error, failingItem, true, false, "UniformGrid");
}
generateTest(completeValidator, "getCount", "noResponse", 0);
["noResponse", "doesNotExist"].forEach(function (error) {
generateTest(completeValidator, "itemsFromIndex", error, 0);
generateTest(completeValidator, "itemsFromIndex", error, 5);
generateTest(scrollAndCompleteValidator(50), "itemsFromIndex", error, 50);
});
function generateEditTest(name, action) {
["noResponse", "notPermitted", "noLongerMeaningful"].forEach(function (error) {
testFailingEdits(name, "GridLayout", error, action, false, false, "UniformGrid");
testFailingEdits(name, "GridLayout", error, action, false, true, "UniformGrid");
if (Helper.Browser.supportsCSSGrid) {
testFailingEdits(name, "GridLayout", error, action, false, false, "Multisize");
}
})
}
generateEditTest("insert", function (dataSource) {
return Helper.ListView.getDataObjects(dataSource, [1]).then(function (dataObjects) {
return dataSource.insertBefore(null, { title: "NewTile" }, dataObjects[0].key);
}).then(null, function () {
// handle error
});
});
generateEditTest("remove", function (dataSource) {
return Helper.ListView.getDataObjects(dataSource, [1]).then(function (dataObjects) {
return dataSource.remove(dataObjects[0].key);
}).then(null, function () {
// handle error
});
});
generateEditTest("change", function (dataSource) {
return Helper.ListView.getDataObjects(dataSource, [1]).then(function (dataObjects) {
return dataSource.change(dataObjects[0].key, { title: "UpdatedTile" });
}).then(null, function () {
// handle error
});
});
generateEditTest("move", function (dataSource) {
return Helper.ListView.getDataObjects(dataSource, [0, 2]).then(function (dataObjects) {
return dataSource.moveBefore(dataObjects[0].key, dataObjects[1].key);
}).then(null, function () {
// handle error
});
});
function generateSetCurrentItemBeforeReady(type, index) {
ListViewTests.prototype["testSetCurrentItemBeforeReady" + type + index] = function () {
var data = [];
for (var i = 0; i < 30; i++) {
data.push({ data: i });
}
var list = new WinJS.Binding.List(data);
var glist = list.createGrouped(function (item) {
return Math.floor(item.data / 3) + "";
}, function (item) {
return { data: Math.floor(item.data / 3) + "" };
});
var lv = new WinJS.UI.ListView();
lv.itemDataSource = glist.dataSource;
lv.groupDataSource = glist.groups.dataSource;
document.body.appendChild(lv.element);
lv.currentItem = { type: type, index: index };
document.body.removeChild(lv.element);
lv.dispose();
};
};
generateSetCurrentItemBeforeReady(WinJS.UI.ObjectType.item, 0);
generateSetCurrentItemBeforeReady(WinJS.UI.ObjectType.item, 2);
generateSetCurrentItemBeforeReady(WinJS.UI.ObjectType.groupHeader, 0);
generateSetCurrentItemBeforeReady(WinJS.UI.ObjectType.groupHeader, 2);
(function cssTransformTests() {
function push(obj, key, value) {
if (obj[key] === undefined) {
obj[key] = value;
} else if (!(obj[key] instanceof Array)) {
obj[key] = [obj[key], value];
} else {
obj[key].push(value);
}
}
function parseCssTransform(transform) {
var element = document.createElement("div");
element.style[WinJS.Utilities._browserStyleEquivalents["transform"].scriptName] = transform;
transform = element.style[WinJS.Utilities._browserStyleEquivalents["transform"].scriptName];
var i = 0;
function skipWhiteSpace() {
while (i < transform.length && transform[i] === " ") {
i++;
}
}
// In "scale(1.5) matrix3d(...)" returns "scale" and updates *i* to point to the
// opening paren.
function nextTransformName() {
var start = i;
if (i < transform.length) {
var end = transform.indexOf("(", i);
if (end >= 0 && end <= transform.length) {
i = end;
return transform.substring(start, end);
}
}
return null;
}
// In "(1.5) matrix3d(...)" returns "1.5" and updates *i* to point to
// the characetr following the closing paren.
function nextTransformValue() {
var start = i + 1;
if (transform[i] === "(" && start < transform.length) {
var end = transform.indexOf(")", start);
if (end >= 0 && end <= transform.length) {
i = end + 1;
return transform.substring(start, end);
}
}
return null;
}
var result: any = {};
while (i < transform.length) {
skipWhiteSpace();
var name = nextTransformName();
var value = nextTransformValue();
if (typeof name === "string" && typeof value === "string") {
push(result, name, value);
} else {
return null;
}
}
return result;
}
})();
}
LiveUnit.registerTestClass("WinJSTests.ListViewTests");