gecko/browser/metro/base/tests/mochitest/browser_tiles.js
2013-06-26 21:54:06 -07:00

422 lines
17 KiB
JavaScript

let doc;
function test() {
waitForExplicitFinish();
Task.spawn(function(){
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
info(chromeRoot + "browser_tilegrid.xul");
yield addTab(chromeRoot + "browser_tilegrid.xul");
doc = Browser.selectedTab.browser.contentWindow.document;
}).then(runTests);
}
gTests.push({
desc: "richgrid binding is applied",
run: function() {
ok(doc, "doc got defined");
let grid = doc.querySelector("#grid1");
ok(grid, "#grid1 is found");
is(typeof grid.clearSelection, "function", "#grid1 has the binding applied");
is(grid.children.length, 2, "#grid1 has a 2 items");
is(grid.children[0].control, grid, "#grid1 item's control points back at #grid1'");
}
});
gTests.push({
desc: "item clicks are handled",
run: function() {
let grid = doc.querySelector("#grid1");
is(typeof grid.handleItemClick, "function", "grid.handleItemClick is a function");
let handleStub = stubMethod(grid, 'handleItemClick');
let itemId = "grid1_item1"; // grid.children[0].getAttribute("id");
// send click to item and wait for next tick;
EventUtils.sendMouseEvent({type: 'click'}, itemId, doc.defaultView);
yield waitForMs(0);
is(handleStub.callCount, 1, "handleItemClick was called when we clicked an item");
handleStub.restore();
// if the grid has a controller, it should be called too
let gridController = {
handleItemClick: function() {}
};
let controllerHandleStub = stubMethod(gridController, "handleItemClick");
let origController = grid.controller;
grid.controller = gridController;
// send click to item and wait for next tick;
EventUtils.sendMouseEvent({type: 'click'}, itemId, doc.defaultView);
yield waitForMs(0);
is(controllerHandleStub.callCount, 1, "controller.handleItemClick was called when we clicked an item");
is(controllerHandleStub.calledWith[0], doc.getElementById(itemId), "controller.handleItemClick was passed the grid item");
grid.controller = origController;
}
});
gTests.push({
desc: "arrangeItems",
run: function() {
// implements an arrangeItems method, with optional cols, rows signature
let container = doc.getElementById("alayout");
let grid = doc.querySelector("#grid_layout");
is(typeof grid.arrangeItems, "function", "arrangeItems is a function on the grid");
ok(grid.tileHeight, "grid has truthy tileHeight value");
ok(grid.tileWidth, "grid has truthy tileWidth value");
// make the container big enough for 3 rows
container.style.height = 3 * grid.tileHeight + 20 + "px";
// add some items
grid.appendItem("test title", "about:blank", true);
grid.appendItem("test title", "about:blank", true);
grid.appendItem("test title", "about:blank", true);
grid.appendItem("test title", "about:blank", true);
grid.appendItem("test title", "about:blank", true);
grid.arrangeItems();
// they should all fit nicely in a 3x2 grid
is(grid.rowCount, 3, "rowCount is calculated correctly for a given container height and tileheight");
is(grid.columnCount, 2, "columnCount is calculated correctly for a given container maxWidth and tilewidth");
// squish the available height
// should overflow (clip) a 2x2 grid
let under3rowsHeight = (3 * grid.tileHeight -20) + "px";
container.style.height = under3rowsHeight;
grid.arrangeItems();
is(grid.rowCount, 2, "rowCount is re-calculated correctly for a given container height");
}
});
gTests.push({
desc: "clearAll",
run: function() {
let grid = doc.getElementById("clearGrid");
grid.arrangeItems();
// grid has rows=2 so we expect at least 2 rows and 2 columns with 3 items
is(typeof grid.clearAll, "function", "clearAll is a function on the grid");
is(grid.itemCount, 3, "grid has 3 items initially");
is(grid.rowCount, 2, "grid has 2 rows initially");
is(grid.columnCount, 2, "grid has 2 cols initially");
let arrangeSpy = spyOnMethod(grid, "arrangeItems");
grid.clearAll();
is(grid.itemCount, 0, "grid has 0 itemCount after clearAll");
is(grid.children.length, 0, "grid has 0 children after clearAll");
is(grid.rowCount, 0, "grid has 0 rows when empty");
is(grid.columnCount, 0, "grid has 0 cols when empty");
is(arrangeSpy.callCount, 1, "arrangeItems is called once when we clearAll");
arrangeSpy.restore();
}
});
gTests.push({
desc: "empty grid",
run: function() {
let grid = doc.getElementById("emptyGrid");
grid.arrangeItems();
yield waitForCondition(() => !grid.isArranging);
// grid has rows=2 but 0 items
ok(grid.isBound, "binding was applied");
is(grid.itemCount, 0, "empty grid has 0 items");
is(grid.rowCount, 0, "empty grid has 0 rows");
is(grid.columnCount, 0, "empty grid has 0 cols");
let columnsNode = grid._grid;
let cStyle = doc.defaultView.getComputedStyle(columnsNode);
is(cStyle.getPropertyValue("-moz-column-count"), "auto", "empty grid has -moz-column-count: auto");
}
});
gTests.push({
desc: "appendItem",
run: function() {
// implements an appendItem with signature title, uri, returns item element
// appendItem triggers arrangeItems
let grid = doc.querySelector("#emptygrid");
is(grid.itemCount, 0, "0 itemCount when empty");
is(grid.children.length, 0, "0 children when empty");
is(typeof grid.appendItem, "function", "appendItem is a function on the grid");
let arrangeStub = stubMethod(grid, "arrangeItems");
let newItem = grid.appendItem("test title", "about:blank");
ok(newItem && grid.children[0]==newItem, "appendItem gives back the item");
is(grid.itemCount, 1, "itemCount is incremented when we appendItem");
is(newItem.getAttribute("label"), "test title", "title ends up on label attribute");
is(newItem.getAttribute("value"), "about:blank", "url ends up on value attribute");
is(arrangeStub.callCount, 1, "arrangeItems is called when we appendItem");
arrangeStub.restore();
}
});
gTests.push({
desc: "getItemAtIndex",
run: function() {
// implements a getItemAtIndex method
let grid = doc.querySelector("#grid2");
is(typeof grid.getItemAtIndex, "function", "getItemAtIndex is a function on the grid");
is(grid.getItemAtIndex(0).getAttribute("id"), "grid2_item1", "getItemAtIndex retrieves the first item");
is(grid.getItemAtIndex(1).getAttribute("id"), "grid2_item2", "getItemAtIndex item at index 2");
ok(!grid.getItemAtIndex(5), "getItemAtIndex out-of-bounds index returns falsy");
}
});
gTests.push({
desc: "removeItemAt",
run: function() {
// implements a removeItemAt method, with 'index' signature
// removeItemAt triggers arrangeItems
let grid = doc.querySelector("#grid2");
is(grid.itemCount, 2, "2 items initially");
is(typeof grid.removeItemAt, "function", "removeItemAt is a function on the grid");
let arrangeStub = stubMethod(grid, "arrangeItems");
let removedItem = grid.removeItemAt(0);
ok(removedItem, "removeItemAt gives back an item");
is(removedItem.getAttribute("id"), "grid2_item1", "removeItemAt gives back the correct item");
is(grid.children[0].getAttribute("id"), "grid2_item2", "2nd item becomes the first item");
is(grid.itemCount, 1, "itemCount is decremented when we removeItemAt");
is(arrangeStub.callCount, 1, "arrangeItems is called when we removeItemAt");
arrangeStub.restore();
}
});
gTests.push({
desc: "insertItemAt",
run: function() {
// implements an insertItemAt method, with index, title, uri.spec signature
// insertItemAt triggers arrangeItems
let grid = doc.querySelector("#grid3");
is(grid.itemCount, 2, "2 items initially");
is(typeof grid.insertItemAt, "function", "insertItemAt is a function on the grid");
let arrangeStub = stubMethod(grid, "arrangeItems");
let insertedItem = grid.insertItemAt(1, "inserted item", "http://example.com/inserted");
ok(insertedItem, "insertItemAt gives back an item");
is(grid.children[1], insertedItem, "item is inserted at the correct index");
is(insertedItem.getAttribute("label"), "inserted item", "insertItemAt creates item with the correct label");
is(insertedItem.getAttribute("value"), "http://example.com/inserted", "insertItemAt creates item with the correct url value");
is(grid.children[2].getAttribute("id"), "grid3_item2", "following item ends up at the correct index");
is(grid.itemCount, 3, "itemCount is incremented when we insertItemAt");
is(arrangeStub.callCount, 1, "arrangeItems is called when we insertItemAt");
arrangeStub.restore();
}
});
gTests.push({
desc: "getIndexOfItem",
run: function() {
// implements a getIndexOfItem method, with item (element) signature
// insertItemAt triggers arrangeItems
let grid = doc.querySelector("#grid4");
is(grid.itemCount, 2, "2 items initially");
is(typeof grid.getIndexOfItem, "function", "getIndexOfItem is a function on the grid");
let item = doc.getElementById("grid4_item2");
let badItem = doc.createElement("richgriditem");
is(grid.getIndexOfItem(item), 1, "getIndexOfItem returns the correct value for an item");
is(grid.getIndexOfItem(badItem), -1, "getIndexOfItem returns -1 for items it doesn't contain");
}
});
gTests.push({
desc: "getItemsByUrl",
run: function() {
let grid = doc.querySelector("#grid5");
is(grid.itemCount, 4, "4 items total");
is(typeof grid.getItemsByUrl, "function", "getItemsByUrl is a function on the grid");
['about:blank', 'http://bugzilla.mozilla.org/'].forEach(function(testUrl) {
let items = grid.getItemsByUrl(testUrl);
is(items.length, 2, "2 matching items in the test grid");
is(items.item(0).url, testUrl, "Matched item has correct url property");
is(items.item(1).url, testUrl, "Matched item has correct url property");
});
let badUrl = 'http://gopher.well.com:70/';
let items = grid.getItemsByUrl(badUrl);
is(items.length, 0, "0 items matched url: "+badUrl);
}
});
gTests.push({
desc: "removeItem",
run: function() {
let grid = doc.querySelector("#grid5");
is(grid.itemCount, 4, "4 items total");
is(typeof grid.removeItem, "function", "removeItem is a function on the grid");
let arrangeStub = stubMethod(grid, "arrangeItems");
let removedFirst = grid.removeItem( grid.children[0] );
is(arrangeStub.callCount, 1, "arrangeItems is called when we removeItem");
let removed2nd = grid.removeItem( grid.children[0], true);
is(removed2nd.getAttribute("label"), "2nd item", "the next item was returned");
is(grid.itemCount, 2, "2 items remain");
// callCount should still be at 1
is(arrangeStub.callCount, 1, "arrangeItems is not called when we pass the truthy skipArrange param");
let otherItem = grid.ownerDocument.querySelector("#grid6_item1");
let removedFail = grid.removeItem(otherItem);
ok(!removedFail, "Falsy value returned when non-child item passed");
is(grid.itemCount, 2, "2 items remain");
// callCount should still be at 1
is(arrangeStub.callCount, 1, "arrangeItems is not called when nothing is matched");
arrangeStub.restore();
}
});
gTests.push({
desc: "selections (single)",
run: function() {
// when seltype is single,
// maintains a selectedItem property
// maintains a selectedIndex property
// clearSelection, selectItem, toggleItemSelection methods are implemented
// 'select' events are implemented
let grid = doc.querySelector("#grid-select1");
is(typeof grid.clearSelection, "function", "clearSelection is a function on the grid");
is(typeof grid.selectedItems, "object", "selectedItems is a property on the grid");
is(typeof grid.toggleItemSelection, "function", "toggleItemSelection is function on the grid");
is(typeof grid.selectItem, "function", "selectItem is a function on the grid");
is(grid.itemCount, 2, "2 items initially");
is(grid.selectedItems.length, 0, "nothing selected initially");
grid.toggleItemSelection(grid.children[1]);
ok(grid.children[1].selected, "toggleItemSelection sets truthy selected prop on previously-unselected item");
is(grid.selectedIndex, 1, "selectedIndex is correct");
grid.toggleItemSelection(grid.children[1]);
ok(!grid.children[1].selected, "toggleItemSelection sets falsy selected prop on previously-selected item");
is(grid.selectedIndex, -1, "selectedIndex reports correctly with nothing selected");
// item selection
grid.selectItem(grid.children[1]);
ok(grid.children[1].selected, "Item selected property is truthy after grid.selectItem");
ok(grid.children[1].getAttribute("selected"), "Item selected attribute is truthy after grid.selectItem");
ok(grid.selectedItems.length, "There are selectedItems after grid.selectItem");
// clearSelection
grid.selectItem(grid.children[0]);
grid.selectItem(grid.children[1]);
grid.clearSelection();
is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
is(grid.selectedIndex, -1, "selectedIndex resets after clearSelection");
// select events
// in seltype=single mode, select is like the default action for the tile
// (think <a>, not <select multiple>)
let handler = {
handleEvent: function(aEvent) {}
};
let handlerStub = stubMethod(handler, "handleEvent");
doc.defaultView.addEventListener("select", handler, false);
info("select listener added");
info("calling selectItem, currently it is:" + grid.children[0].selected);
// Note: A richgrid in seltype=single mode fires "select" events from selectItem
grid.selectItem(grid.children[0]);
info("calling selectItem, now it is:" + grid.children[0].selected);
yield waitForMs(0);
is(handlerStub.callCount, 1, "select event handler was called when we selected an item");
is(handlerStub.calledWith[0].type, "select", "handler got a select event");
is(handlerStub.calledWith[0].target, grid, "select event had the originating grid as the target");
handlerStub.restore();
doc.defaultView.removeEventListener("select", handler, false);
}
});
gTests.push({
desc: "selections (multiple)",
run: function() {
// when seltype is multiple,
// maintains a selectedItems property
// clearSelection, selectItem, toggleItemSelection methods are implemented
// 'selectionchange' events are implemented
let grid = doc.querySelector("#grid-select2");
is(typeof grid.clearSelection, "function", "clearSelection is a function on the grid");
is(typeof grid.selectedItems, "object", "selectedItems is a property on the grid");
is(typeof grid.toggleItemSelection, "function", "toggleItemSelection is function on the grid");
is(typeof grid.selectItem, "function", "selectItem is a function on the grid");
is(grid.itemCount, 2, "2 items initially");
is(grid.selectedItems.length, 0, "nothing selected initially");
grid.toggleItemSelection(grid.children[1]);
ok(grid.children[1].selected, "toggleItemSelection sets truthy selected prop on previously-unselected item");
is(grid.selectedItems.length, 1, "1 item selected when we first toggleItemSelection");
is(grid.selectedItems[0], grid.children[1], "the right item is selected");
is(grid.selectedIndex, 1, "selectedIndex is correct");
grid.toggleItemSelection(grid.children[1]);
is(grid.selectedItems.length, 0, "Nothing selected when we toggleItemSelection again");
// clearSelection
grid.children[0].selected=true;
grid.children[1].selected=true;
is(grid.selectedItems.length, 2, "Both items are selected before calling clearSelection");
grid.clearSelection();
is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
ok(!(grid.children[0].selected || grid.children[1].selected), "selected properties all falsy when we clearSelection");
// selectionchange events
// in seltype=multiple mode, we track selected state on all items
// (think <select multiple> not <a>)
let handler = {
handleEvent: function(aEvent) {}
};
let handlerStub = stubMethod(handler, "handleEvent");
doc.defaultView.addEventListener("selectionchange", handler, false);
info("selectionchange listener added");
info("calling toggleItemSelection, currently it is:" + grid.children[0].selected);
// Note: A richgrid in seltype=single mode fires "select" events from selectItem
grid.toggleItemSelection(grid.children[0]);
info("/calling toggleItemSelection, now it is:" + grid.children[0].selected);
yield waitForMs(0);
is(handlerStub.callCount, 1, "selectionchange event handler was called when we selected an item");
is(handlerStub.calledWith[0].type, "selectionchange", "handler got a selectionchange event");
is(handlerStub.calledWith[0].target, grid, "select event had the originating grid as the target");
handlerStub.restore();
doc.defaultView.removeEventListener("selectionchange", handler, false);
}
});
// implements a getItemAtIndex method (or grid.children[idx] ?)