browser/metro/base/tests/mochitest/browser_tiles.js

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 let doc;
michael@0 2
michael@0 3 function test() {
michael@0 4 waitForExplicitFinish();
michael@0 5 Task.spawn(function(){
michael@0 6 info(chromeRoot + "browser_tilegrid.xul");
michael@0 7 yield addTab(chromeRoot + "browser_tilegrid.xul");
michael@0 8 doc = Browser.selectedTab.browser.contentWindow.document;
michael@0 9 }).then(runTests);
michael@0 10 }
michael@0 11
michael@0 12 function _checkIfBoundByRichGrid_Item(expected, node, idx) {
michael@0 13 let binding = node.ownerDocument.defaultView.getComputedStyle(node).MozBinding;
michael@0 14 let result = ('url("chrome://browser/content/bindings/grid.xml#richgrid-item")' == binding);
michael@0 15 return (result == expected);
michael@0 16 }
michael@0 17 let isBoundByRichGrid_Item = _checkIfBoundByRichGrid_Item.bind(this, true);
michael@0 18 let isNotBoundByRichGrid_Item = _checkIfBoundByRichGrid_Item.bind(this, false);
michael@0 19
michael@0 20 gTests.push({
michael@0 21 desc: "richgrid binding is applied",
michael@0 22 run: function() {
michael@0 23 ok(doc, "doc got defined");
michael@0 24
michael@0 25 let grid = doc.querySelector("#grid1");
michael@0 26 ok(grid, "#grid1 is found");
michael@0 27 is(typeof grid.clearSelection, "function", "#grid1 has the binding applied");
michael@0 28 is(grid.items.length, 2, "#grid1 has a 2 items");
michael@0 29 is(grid.items[0].control, grid, "#grid1 item's control points back at #grid1'");
michael@0 30 ok(Array.every(grid.items, isBoundByRichGrid_Item), "All items are bound by richgrid-item");
michael@0 31 }
michael@0 32 });
michael@0 33
michael@0 34 gTests.push({
michael@0 35 desc: "item clicks are handled",
michael@0 36 run: function() {
michael@0 37 let grid = doc.querySelector("#grid1");
michael@0 38 is(typeof grid.handleItemClick, "function", "grid.handleItemClick is a function");
michael@0 39 let handleStub = stubMethod(grid, 'handleItemClick');
michael@0 40 let itemId = "grid1_item1"; // grid.items[0].getAttribute("id");
michael@0 41
michael@0 42 // send click to item and wait for next tick;
michael@0 43 EventUtils.sendMouseEvent({type: 'click'}, itemId, doc.defaultView);
michael@0 44 yield waitForMs(0);
michael@0 45
michael@0 46 is(handleStub.callCount, 1, "handleItemClick was called when we clicked an item");
michael@0 47 handleStub.restore();
michael@0 48
michael@0 49 // if the grid has a controller, it should be called too
michael@0 50 let gridController = {
michael@0 51 handleItemClick: function() {}
michael@0 52 };
michael@0 53 let controllerHandleStub = stubMethod(gridController, "handleItemClick");
michael@0 54 let origController = grid.controller;
michael@0 55 grid.controller = gridController;
michael@0 56
michael@0 57 // send click to item and wait for next tick;
michael@0 58 EventUtils.sendMouseEvent({type: 'click'}, itemId, doc.defaultView);
michael@0 59 yield waitForMs(0);
michael@0 60
michael@0 61 is(controllerHandleStub.callCount, 1, "controller.handleItemClick was called when we clicked an item");
michael@0 62 is(controllerHandleStub.calledWith[0], doc.getElementById(itemId), "controller.handleItemClick was passed the grid item");
michael@0 63 grid.controller = origController;
michael@0 64 }
michael@0 65 });
michael@0 66
michael@0 67 gTests.push({
michael@0 68 desc: "arrangeItems",
michael@0 69 run: function() {
michael@0 70 // implements an arrangeItems method, with optional cols, rows signature
michael@0 71 let container = doc.getElementById("alayout");
michael@0 72 let grid = doc.querySelector("#grid_layout");
michael@0 73
michael@0 74 is(typeof grid.arrangeItems, "function", "arrangeItems is a function on the grid");
michael@0 75
michael@0 76 ok(grid.tileHeight, "grid has truthy tileHeight value");
michael@0 77 ok(grid.tileWidth, "grid has truthy tileWidth value");
michael@0 78
michael@0 79 // make the container big enough for 3 rows
michael@0 80 container.style.height = 3 * grid.tileHeight + 20 + "px";
michael@0 81
michael@0 82 // add some items
michael@0 83 grid.appendItem("test title", "about:blank", true);
michael@0 84 grid.appendItem("test title", "about:blank", true);
michael@0 85 grid.appendItem("test title", "about:blank", true);
michael@0 86 grid.appendItem("test title", "about:blank", true);
michael@0 87 grid.appendItem("test title", "about:blank", true);
michael@0 88
michael@0 89 grid.arrangeItems();
michael@0 90 // they should all fit nicely in a 3x2 grid
michael@0 91 is(grid.rowCount, 3, "rowCount is calculated correctly for a given container height and tileheight");
michael@0 92 is(grid.columnCount, 2, "columnCount is calculated correctly for a given container maxWidth and tilewidth");
michael@0 93
michael@0 94 // squish the available height
michael@0 95 // should overflow (clip) a 2x2 grid
michael@0 96
michael@0 97 let under3rowsHeight = (3 * grid.tileHeight -20) + "px";
michael@0 98 container.style.height = under3rowsHeight;
michael@0 99
michael@0 100 let arrangedPromise = waitForEvent(grid, "arranged");
michael@0 101 grid.arrangeItems();
michael@0 102 yield arrangedPromise;
michael@0 103
michael@0 104 ok(true, "arranged event is fired when arrangeItems is called");
michael@0 105 is(grid.rowCount, 2, "rowCount is re-calculated correctly for a given container height");
michael@0 106 }
michael@0 107 });
michael@0 108
michael@0 109 gTests.push({
michael@0 110 desc: "clearAll",
michael@0 111 run: function() {
michael@0 112 let grid = doc.getElementById("clearGrid");
michael@0 113 grid.arrangeItems();
michael@0 114
michael@0 115 // grid has rows=2 so we expect at least 2 rows and 2 columns with 3 items
michael@0 116 is(typeof grid.clearAll, "function", "clearAll is a function on the grid");
michael@0 117 is(grid.itemCount, 3, "grid has 3 items initially");
michael@0 118 is(grid.rowCount, 2, "grid has 2 rows initially");
michael@0 119 is(grid.columnCount, 2, "grid has 2 cols initially");
michael@0 120
michael@0 121 let arrangeSpy = spyOnMethod(grid, "arrangeItems");
michael@0 122 grid.clearAll();
michael@0 123
michael@0 124 is(grid.itemCount, 0, "grid has 0 itemCount after clearAll");
michael@0 125 is(grid.items.length, 0, "grid has 0 items after clearAll");
michael@0 126 // now that we use slots, an empty grid may still have non-zero rows & columns
michael@0 127
michael@0 128 is(arrangeSpy.callCount, 1, "arrangeItems is called once when we clearAll");
michael@0 129 arrangeSpy.restore();
michael@0 130 }
michael@0 131 });
michael@0 132
michael@0 133 gTests.push({
michael@0 134 desc: "empty grid",
michael@0 135 run: function() {
michael@0 136 // XXX grids have minSlots and may not be ever truly empty
michael@0 137
michael@0 138 let grid = doc.getElementById("emptyGrid");
michael@0 139 grid.arrangeItems();
michael@0 140 yield waitForCondition(() => !grid.isArranging);
michael@0 141
michael@0 142 // grid has 2 rows, 6 slots, 0 items
michael@0 143 ok(grid.isBound, "binding was applied");
michael@0 144 is(grid.itemCount, 0, "empty grid has 0 items");
michael@0 145 // minSlots attr. creates unpopulated slots
michael@0 146 is(grid.rowCount, grid.getAttribute("rows"), "empty grid with rows-attribute has that number of rows");
michael@0 147 is(grid.columnCount, 3, "empty grid has expected number of columns");
michael@0 148
michael@0 149 // remove rows attribute and allow space for the grid to find its own height
michael@0 150 // for its number of slots
michael@0 151 grid.removeAttribute("rows");
michael@0 152 grid.parentNode.style.height = 20+(grid.tileHeight*grid.minSlots)+"px";
michael@0 153
michael@0 154 grid.arrangeItems();
michael@0 155 yield waitForCondition(() => !grid.isArranging);
michael@0 156 is(grid.rowCount, grid.minSlots, "empty grid has this.minSlots rows");
michael@0 157 is(grid.columnCount, 1, "empty grid has 1 column");
michael@0 158 }
michael@0 159 });
michael@0 160
michael@0 161 gTests.push({
michael@0 162 desc: "appendItem",
michael@0 163 run: function() {
michael@0 164 // implements an appendItem with signature title, uri, returns item element
michael@0 165 // appendItem triggers arrangeItems
michael@0 166 let grid = doc.querySelector("#emptygrid");
michael@0 167
michael@0 168 is(grid.itemCount, 0, "0 itemCount when empty");
michael@0 169 is(grid.items.length, 0, "0 items when empty");
michael@0 170 is(typeof grid.appendItem, "function", "appendItem is a function on the grid");
michael@0 171
michael@0 172 let arrangeStub = stubMethod(grid, "arrangeItems");
michael@0 173 let newItem = grid.appendItem("test title", "about:blank");
michael@0 174
michael@0 175 ok(newItem && grid.items[0]==newItem, "appendItem gives back the item");
michael@0 176 is(grid.itemCount, 1, "itemCount is incremented when we appendItem");
michael@0 177 is(newItem.getAttribute("label"), "test title", "title ends up on label attribute");
michael@0 178 is(newItem.getAttribute("value"), "about:blank", "url ends up on value attribute");
michael@0 179
michael@0 180 is(arrangeStub.callCount, 1, "arrangeItems is called when we appendItem");
michael@0 181 arrangeStub.restore();
michael@0 182 }
michael@0 183 });
michael@0 184
michael@0 185 gTests.push({
michael@0 186 desc: "getItemAtIndex",
michael@0 187 run: function() {
michael@0 188 // implements a getItemAtIndex method
michael@0 189 let grid = doc.querySelector("#grid2");
michael@0 190 is(typeof grid.getItemAtIndex, "function", "getItemAtIndex is a function on the grid");
michael@0 191 is(grid.getItemAtIndex(0).getAttribute("id"), "grid2_item1", "getItemAtIndex retrieves the first item");
michael@0 192 is(grid.getItemAtIndex(1).getAttribute("id"), "grid2_item2", "getItemAtIndex item at index 2");
michael@0 193 ok(!grid.getItemAtIndex(5), "getItemAtIndex out-of-bounds index returns falsy");
michael@0 194 }
michael@0 195 });
michael@0 196
michael@0 197 gTests.push({
michael@0 198 desc: "removeItemAt",
michael@0 199 run: function() {
michael@0 200 // implements a removeItemAt method, with 'index' signature
michael@0 201 // removeItemAt triggers arrangeItems
michael@0 202 let grid = doc.querySelector("#grid2");
michael@0 203
michael@0 204 is(grid.itemCount, 2, "2 items initially");
michael@0 205 is(typeof grid.removeItemAt, "function", "removeItemAt is a function on the grid");
michael@0 206
michael@0 207 let arrangeStub = stubMethod(grid, "arrangeItems");
michael@0 208 let removedItem = grid.removeItemAt(0);
michael@0 209
michael@0 210 ok(removedItem, "removeItemAt gives back an item");
michael@0 211 is(removedItem.getAttribute("id"), "grid2_item1", "removeItemAt gives back the correct item");
michael@0 212 is(grid.items[0].getAttribute("id"), "grid2_item2", "2nd item becomes the first item");
michael@0 213 is(grid.itemCount, 1, "itemCount is decremented when we removeItemAt");
michael@0 214
michael@0 215 is(arrangeStub.callCount, 1, "arrangeItems is called when we removeItemAt");
michael@0 216 arrangeStub.restore();
michael@0 217 }
michael@0 218 });
michael@0 219
michael@0 220 gTests.push({
michael@0 221 desc: "insertItemAt",
michael@0 222 run: function() {
michael@0 223 // implements an insertItemAt method, with index, title, uri.spec signature
michael@0 224 // insertItemAt triggers arrangeItems
michael@0 225 let grid = doc.querySelector("#grid3");
michael@0 226
michael@0 227 is(grid.itemCount, 2, "2 items initially");
michael@0 228 is(typeof grid.insertItemAt, "function", "insertItemAt is a function on the grid");
michael@0 229
michael@0 230 let arrangeStub = stubMethod(grid, "arrangeItems");
michael@0 231 let insertedAt0 = grid.insertItemAt(0, "inserted item 0", "http://example.com/inserted0");
michael@0 232 let insertedAt00 = grid.insertItemAt(0, "inserted item 00", "http://example.com/inserted00");
michael@0 233
michael@0 234 ok(insertedAt0 && insertedAt00, "insertItemAt gives back an item");
michael@0 235
michael@0 236 is(insertedAt0.getAttribute("label"), "inserted item 0", "insertItemAt creates item with the correct label");
michael@0 237 is(insertedAt0.getAttribute("value"), "http://example.com/inserted0", "insertItemAt creates item with the correct url value");
michael@0 238
michael@0 239 is(grid.items[0], insertedAt00, "item is inserted at the correct index");
michael@0 240 is(grid.children[0], insertedAt00, "first item occupies the first slot");
michael@0 241 is(grid.items[1], insertedAt0, "item is inserted at the correct index");
michael@0 242 is(grid.children[1], insertedAt0, "next item occupies the next slot");
michael@0 243
michael@0 244 is(grid.items[2].getAttribute("label"), "First item", "Old first item is now at index 2");
michael@0 245 is(grid.items[3].getAttribute("label"), "2nd item", "Old 2nd item is now at index 3");
michael@0 246
michael@0 247 is(grid.itemCount, 4, "itemCount is incremented when we insertItemAt");
michael@0 248
michael@0 249 is(arrangeStub.callCount, 2, "arrangeItems is called when we insertItemAt");
michael@0 250 arrangeStub.restore();
michael@0 251 }
michael@0 252 });
michael@0 253
michael@0 254 gTests.push({
michael@0 255 desc: "getIndexOfItem",
michael@0 256 run: function() {
michael@0 257 // implements a getIndexOfItem method, with item (element) signature
michael@0 258 // insertItemAt triggers arrangeItems
michael@0 259 let grid = doc.querySelector("#grid4");
michael@0 260
michael@0 261 is(grid.itemCount, 2, "2 items initially");
michael@0 262 is(typeof grid.getIndexOfItem, "function", "getIndexOfItem is a function on the grid");
michael@0 263
michael@0 264 let item = doc.getElementById("grid4_item2");
michael@0 265 let badItem = doc.createElement("richgriditem");
michael@0 266
michael@0 267 is(grid.getIndexOfItem(item), 1, "getIndexOfItem returns the correct value for an item");
michael@0 268 is(grid.getIndexOfItem(badItem), -1, "getIndexOfItem returns -1 for items it doesn't contain");
michael@0 269 }
michael@0 270 });
michael@0 271
michael@0 272 gTests.push({
michael@0 273 desc: "getItemsByUrl",
michael@0 274 run: function() {
michael@0 275 let grid = doc.querySelector("#grid5");
michael@0 276
michael@0 277 is(grid.itemCount, 4, "4 items total");
michael@0 278 is(typeof grid.getItemsByUrl, "function", "getItemsByUrl is a function on the grid");
michael@0 279
michael@0 280 ['about:blank', 'http://bugzilla.mozilla.org/'].forEach(function(testUrl) {
michael@0 281 let items = grid.getItemsByUrl(testUrl);
michael@0 282 is(items.length, 2, "2 matching items in the test grid");
michael@0 283 is(items.item(0).url, testUrl, "Matched item has correct url property");
michael@0 284 is(items.item(1).url, testUrl, "Matched item has correct url property");
michael@0 285 });
michael@0 286
michael@0 287 let badUrl = 'http://gopher.well.com:70/';
michael@0 288 let items = grid.getItemsByUrl(badUrl);
michael@0 289 is(items.length, 0, "0 items matched url: "+badUrl);
michael@0 290
michael@0 291 }
michael@0 292 });
michael@0 293
michael@0 294 gTests.push({
michael@0 295 desc: "removeItem",
michael@0 296 run: function() {
michael@0 297 let grid = doc.querySelector("#grid5");
michael@0 298
michael@0 299 is(grid.itemCount, 4, "4 items total");
michael@0 300 is(typeof grid.removeItem, "function", "removeItem is a function on the grid");
michael@0 301
michael@0 302 let arrangeStub = stubMethod(grid, "arrangeItems");
michael@0 303 let removedFirst = grid.removeItem( grid.items[0] );
michael@0 304
michael@0 305 is(arrangeStub.callCount, 1, "arrangeItems is called when we removeItem");
michael@0 306
michael@0 307 let removed2nd = grid.removeItem( grid.items[0], true);
michael@0 308 is(removed2nd.getAttribute("label"), "2nd item", "the next item was returned");
michael@0 309 is(grid.itemCount, 2, "2 items remain");
michael@0 310
michael@0 311 // callCount should still be at 1
michael@0 312 is(arrangeStub.callCount, 1, "arrangeItems is not called when we pass the truthy skipArrange param");
michael@0 313
michael@0 314 let otherItem = grid.ownerDocument.querySelector("#grid6_item1");
michael@0 315 let removedFail = grid.removeItem(otherItem);
michael@0 316 ok(!removedFail, "Falsy value returned when non-child item passed");
michael@0 317 is(grid.itemCount, 2, "2 items remain");
michael@0 318
michael@0 319 // callCount should still be at 1
michael@0 320 is(arrangeStub.callCount, 1, "arrangeItems is not called when nothing is matched");
michael@0 321
michael@0 322 arrangeStub.restore();
michael@0 323 }
michael@0 324 });
michael@0 325
michael@0 326 gTests.push({
michael@0 327 desc: "selections (single)",
michael@0 328 run: function() {
michael@0 329 // when seltype is single,
michael@0 330 // maintains a selectedItem property
michael@0 331 // maintains a selectedIndex property
michael@0 332 // clearSelection, selectItem, toggleItemSelection methods are implemented
michael@0 333 // 'select' events are implemented
michael@0 334 let grid = doc.querySelector("#grid-select1");
michael@0 335
michael@0 336 is(typeof grid.clearSelection, "function", "clearSelection is a function on the grid");
michael@0 337 is(typeof grid.selectedItems, "object", "selectedItems is a property on the grid");
michael@0 338 is(typeof grid.toggleItemSelection, "function", "toggleItemSelection is function on the grid");
michael@0 339 is(typeof grid.selectItem, "function", "selectItem is a function on the grid");
michael@0 340
michael@0 341 is(grid.itemCount, 2, "2 items initially");
michael@0 342 is(grid.selectedItems.length, 0, "nothing selected initially");
michael@0 343
michael@0 344 grid.toggleItemSelection(grid.items[1]);
michael@0 345 ok(grid.items[1].selected, "toggleItemSelection sets truthy selected prop on previously-unselected item");
michael@0 346 is(grid.selectedIndex, 1, "selectedIndex is correct");
michael@0 347
michael@0 348 grid.toggleItemSelection(grid.items[1]);
michael@0 349 ok(!grid.items[1].selected, "toggleItemSelection sets falsy selected prop on previously-selected item");
michael@0 350 is(grid.selectedIndex, -1, "selectedIndex reports correctly with nothing selected");
michael@0 351
michael@0 352 // item selection
michael@0 353 grid.selectItem(grid.items[1]);
michael@0 354 ok(grid.items[1].selected, "Item selected property is truthy after grid.selectItem");
michael@0 355 ok(grid.items[1].getAttribute("selected"), "Item selected attribute is truthy after grid.selectItem");
michael@0 356 ok(grid.selectedItems.length, "There are selectedItems after grid.selectItem");
michael@0 357
michael@0 358 // select events
michael@0 359 // in seltype=single mode, select is like the default action for the tile
michael@0 360 // (think <a>, not <select multiple>)
michael@0 361 let handler = {
michael@0 362 handleEvent: function(aEvent) {}
michael@0 363 };
michael@0 364 let handlerStub = stubMethod(handler, "handleEvent");
michael@0 365
michael@0 366 grid.items[1].selected = true;
michael@0 367
michael@0 368 doc.defaultView.addEventListener("select", handler, false);
michael@0 369 info("select listener added");
michael@0 370
michael@0 371 // clearSelection
michael@0 372 grid.clearSelection();
michael@0 373 is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
michael@0 374 is(grid.selectedIndex, -1, "selectedIndex resets after clearSelection");
michael@0 375 is(handlerStub.callCount, 0, "clearSelection should not fire a selectionchange event");
michael@0 376
michael@0 377 info("calling selectItem, currently it is:" + grid.items[0].selected);
michael@0 378 // Note: A richgrid in seltype=single mode fires "select" events from selectItem
michael@0 379 grid.selectItem(grid.items[0]);
michael@0 380 info("calling selectItem, now it is:" + grid.items[0].selected);
michael@0 381 yield waitForMs(0);
michael@0 382
michael@0 383 is(handlerStub.callCount, 1, "select event handler was called when we selected an item");
michael@0 384 is(handlerStub.calledWith[0].type, "select", "handler got a select event");
michael@0 385 is(handlerStub.calledWith[0].target, grid, "select event had the originating grid as the target");
michael@0 386 handlerStub.restore();
michael@0 387 doc.defaultView.removeEventListener("select", handler, false);
michael@0 388 }
michael@0 389 });
michael@0 390
michael@0 391 gTests.push({
michael@0 392 desc: "selections (multiple)",
michael@0 393 run: function() {
michael@0 394 // when seltype is multiple,
michael@0 395 // maintains a selectedItems property
michael@0 396 // clearSelection, selectItem, toggleItemSelection methods are implemented
michael@0 397 // 'selectionchange' events are implemented
michael@0 398 let grid = doc.querySelector("#grid-select2");
michael@0 399
michael@0 400 is(typeof grid.clearSelection, "function", "clearSelection is a function on the grid");
michael@0 401 is(typeof grid.selectedItems, "object", "selectedItems is a property on the grid");
michael@0 402 is(typeof grid.toggleItemSelection, "function", "toggleItemSelection is function on the grid");
michael@0 403 is(typeof grid.selectItem, "function", "selectItem is a function on the grid");
michael@0 404
michael@0 405 is(grid.itemCount, 2, "2 items initially");
michael@0 406 is(grid.selectedItems.length, 0, "nothing selected initially");
michael@0 407
michael@0 408 grid.toggleItemSelection(grid.items[1]);
michael@0 409 ok(grid.items[1].selected, "toggleItemSelection sets truthy selected prop on previously-unselected item");
michael@0 410 is(grid.selectedItems.length, 1, "1 item selected when we first toggleItemSelection");
michael@0 411 is(grid.selectedItems[0], grid.items[1], "the right item is selected");
michael@0 412 is(grid.selectedIndex, 1, "selectedIndex is correct");
michael@0 413
michael@0 414 grid.toggleItemSelection(grid.items[1]);
michael@0 415 is(grid.selectedItems.length, 0, "Nothing selected when we toggleItemSelection again");
michael@0 416
michael@0 417 // selectionchange events
michael@0 418 // in seltype=multiple mode, we track selected state on all items
michael@0 419 // (think <select multiple> not <a>)
michael@0 420 let handler = {
michael@0 421 handleEvent: function(aEvent) {}
michael@0 422 };
michael@0 423 let handlerStub = stubMethod(handler, "handleEvent");
michael@0 424 doc.defaultView.addEventListener("selectionchange", handler, false);
michael@0 425 info("selectionchange listener added");
michael@0 426
michael@0 427 // clearSelection
michael@0 428 grid.items[0].selected=true;
michael@0 429 grid.items[1].selected=true;
michael@0 430 is(grid.selectedItems.length, 2, "Both items are selected before calling clearSelection");
michael@0 431 grid.clearSelection();
michael@0 432 is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection");
michael@0 433 ok(!(grid.items[0].selected || grid.items[1].selected), "selected properties all falsy when we clearSelection");
michael@0 434 is(handlerStub.callCount, 0, "clearSelection should not fire a selectionchange event");
michael@0 435
michael@0 436 info("calling toggleItemSelection, currently it is:" + grid.items[0].selected);
michael@0 437 // Note: A richgrid in seltype=single mode fires "select" events from selectItem
michael@0 438 grid.toggleItemSelection(grid.items[0]);
michael@0 439 info("/calling toggleItemSelection, now it is:" + grid.items[0].selected);
michael@0 440 yield waitForMs(0);
michael@0 441
michael@0 442 is(handlerStub.callCount, 1, "selectionchange event handler was called when we selected an item");
michael@0 443 is(handlerStub.calledWith[0].type, "selectionchange", "handler got a selectionchange event");
michael@0 444 is(handlerStub.calledWith[0].target, grid, "select event had the originating grid as the target");
michael@0 445 handlerStub.restore();
michael@0 446 doc.defaultView.removeEventListener("selectionchange", handler, false);
michael@0 447 }
michael@0 448 });
michael@0 449
michael@0 450 gTests.push({
michael@0 451 desc: "selectNone",
michael@0 452 run: function() {
michael@0 453 let grid = doc.querySelector("#grid-select2");
michael@0 454
michael@0 455 is(typeof grid.selectNone, "function", "selectNone is a function on the grid");
michael@0 456
michael@0 457 is(grid.itemCount, 2, "2 items initially");
michael@0 458
michael@0 459 // selectNone should fire a selectionchange event
michael@0 460 let handler = {
michael@0 461 handleEvent: function(aEvent) {}
michael@0 462 };
michael@0 463 let handlerStub = stubMethod(handler, "handleEvent");
michael@0 464 doc.defaultView.addEventListener("selectionchange", handler, false);
michael@0 465 info("selectionchange listener added");
michael@0 466
michael@0 467 grid.items[0].selected=true;
michael@0 468 grid.items[1].selected=true;
michael@0 469 is(grid.selectedItems.length, 2, "Both items are selected before calling selectNone");
michael@0 470 grid.selectNone();
michael@0 471
michael@0 472 is(grid.selectedItems.length, 0, "Nothing selected when we selectNone");
michael@0 473 ok(!(grid.items[0].selected || grid.items[1].selected), "selected properties all falsy when we selectNone");
michael@0 474
michael@0 475 is(handlerStub.callCount, 1, "selectionchange event handler was called when we selectNone");
michael@0 476 is(handlerStub.calledWith[0].type, "selectionchange", "handler got a selectionchange event");
michael@0 477 is(handlerStub.calledWith[0].target, grid, "selectionchange event had the originating grid as the target");
michael@0 478 handlerStub.restore();
michael@0 479 doc.defaultView.removeEventListener("selectionchange", handler, false);
michael@0 480 }
michael@0 481 });
michael@0 482
michael@0 483 function gridSlotsSetup() {
michael@0 484 let grid = this.grid = doc.createElement("richgrid");
michael@0 485 grid.setAttribute("minSlots", 6);
michael@0 486 doc.documentElement.appendChild(grid);
michael@0 487 is(grid.ownerDocument, doc, "created grid in the expected document");
michael@0 488 }
michael@0 489 function gridSlotsTearDown() {
michael@0 490 this.grid && this.grid.parentNode.removeChild(this.grid);
michael@0 491 }
michael@0 492
michael@0 493 gTests.push({
michael@0 494 desc: "richgrid slots init",
michael@0 495 setUp: gridSlotsSetup,
michael@0 496 run: function() {
michael@0 497 let grid = this.grid;
michael@0 498 // grid is initially populated with empty slots matching the minSlots attribute
michael@0 499 is(grid.children.length, 6, "minSlots slots are created");
michael@0 500 is(grid.itemCount, 0, "slots do not count towards itemCount");
michael@0 501 ok(Array.every(grid.children, (node) => node.nodeName == 'richgriditem'), "slots have nodeName richgriditem");
michael@0 502 ok(Array.every(grid.children, isNotBoundByRichGrid_Item), "slots aren't bound by the richgrid-item binding");
michael@0 503
michael@0 504 ok(!grid.isItem(grid.children[0]), "slot fails isItem validation");
michael@0 505 },
michael@0 506 tearDown: gridSlotsTearDown
michael@0 507 });
michael@0 508
michael@0 509 gTests.push({
michael@0 510 desc: "richgrid using slots for items",
michael@0 511 setUp: gridSlotsSetup, // creates grid with minSlots = num. slots = 6
michael@0 512 run: function() {
michael@0 513 let grid = this.grid;
michael@0 514 let numSlots = grid.getAttribute("minSlots");
michael@0 515 is(grid.children.length, numSlots);
michael@0 516 // adding items occupies those slots
michael@0 517 for (let idx of [0,1,2,3,4,5,6]) {
michael@0 518 let slot = grid.children[idx];
michael@0 519 let item = grid.appendItem("item "+idx, "about:mozilla");
michael@0 520 if (idx < numSlots) {
michael@0 521 is(grid.children.length, numSlots);
michael@0 522 is(slot, item, "The same node is reused when an item is assigned to a slot");
michael@0 523 } else {
michael@0 524 is(typeof slot, 'undefined');
michael@0 525 ok(item);
michael@0 526 is(grid.children.length, grid.itemCount);
michael@0 527 }
michael@0 528 }
michael@0 529 },
michael@0 530 tearDown: gridSlotsTearDown
michael@0 531 });
michael@0 532
michael@0 533 gTests.push({
michael@0 534 desc: "richgrid assign and release slots",
michael@0 535 setUp: function(){
michael@0 536 info("assign and release slots setUp");
michael@0 537 this.grid = doc.getElementById("slots_grid");
michael@0 538 this.grid.scrollIntoView();
michael@0 539 let rect = this.grid.getBoundingClientRect();
michael@0 540 info("slots grid at top: " + rect.top + ", window.pageYOffset: " + doc.defaultView.pageYOffset);
michael@0 541 },
michael@0 542 run: function() {
michael@0 543 let grid = this.grid;
michael@0 544 // start with 5 of 6 slots occupied
michael@0 545 for (let idx of [0,1,2,3,4]) {
michael@0 546 let item = grid.appendItem("item "+idx, "about:mozilla");
michael@0 547 item.setAttribute("id", "test_item_"+idx);
michael@0 548 }
michael@0 549 is(grid.itemCount, 5);
michael@0 550 is(grid.children.length, 6); // see setup, where we init with 6 slots
michael@0 551 let firstItem = grid.items[0];
michael@0 552
michael@0 553 ok(firstItem.ownerDocument, "item has ownerDocument");
michael@0 554 is(doc, firstItem.ownerDocument, "item's ownerDocument is the document we expect");
michael@0 555
michael@0 556 is(firstItem, grid.children[0], "Item and assigned slot are one and the same");
michael@0 557 is(firstItem.control, grid, "Item is bound and its .control points back at the grid");
michael@0 558
michael@0 559 // before releasing, the grid should be nofified of clicks on that slot
michael@0 560 let testWindow = grid.ownerDocument.defaultView;
michael@0 561
michael@0 562 let rect = firstItem.getBoundingClientRect();
michael@0 563 {
michael@0 564 let handleStub = stubMethod(grid, 'handleItemClick');
michael@0 565 // send click to item and wait for next tick;
michael@0 566 sendElementTap(testWindow, firstItem);
michael@0 567 yield waitForMs(0);
michael@0 568
michael@0 569 is(handleStub.callCount, 1, "handleItemClick was called when we clicked an item");
michael@0 570 handleStub.restore();
michael@0 571 }
michael@0 572 // _releaseSlot is semi-private, we don't expect consumers of the binding to call it
michael@0 573 // but want to be sure it does what we expect
michael@0 574 grid._releaseSlot(firstItem);
michael@0 575
michael@0 576 is(grid.itemCount, 4, "Releasing a slot gives us one less item");
michael@0 577 is(firstItem, grid.children[0],"Released slot is still the same node we started with");
michael@0 578
michael@0 579 // after releasing, the grid should NOT be nofified of clicks
michael@0 580 {
michael@0 581 let handleStub = stubMethod(grid, 'handleItemClick');
michael@0 582 // send click to item and wait for next tick;
michael@0 583 sendElementTap(testWindow, firstItem);
michael@0 584 yield waitForMs(0);
michael@0 585
michael@0 586 is(handleStub.callCount, 0, "handleItemClick was NOT called when we clicked a released slot");
michael@0 587 handleStub.restore();
michael@0 588 }
michael@0 589
michael@0 590 ok(!firstItem.mozMatchesSelector("richgriditem[value]"), "Released slot doesn't match binding selector");
michael@0 591 ok(isNotBoundByRichGrid_Item(firstItem), "Released slot is no longer bound");
michael@0 592
michael@0 593 waitForCondition(() => isNotBoundByRichGrid_Item(firstItem));
michael@0 594 ok(true, "Slot eventually gets unbound");
michael@0 595 is(firstItem, grid.children[0], "Released slot is still at expected index in children collection");
michael@0 596
michael@0 597 let firstSlot = grid.children[0];
michael@0 598 firstItem = grid.insertItemAt(0, "New item 0", "about:blank");
michael@0 599 ok(firstItem == grid.items[0], "insertItemAt 0 creates item at expected index");
michael@0 600 ok(firstItem == firstSlot, "insertItemAt occupies the released slot with the new item");
michael@0 601 is(grid.itemCount, 5);
michael@0 602 is(grid.children.length, 6);
michael@0 603 is(firstItem.control, grid,"Item is bound and its .control points back at the grid");
michael@0 604
michael@0 605 let nextSlotIndex = grid.itemCount;
michael@0 606 let lastItem = grid.insertItemAt(9, "New item 9", "about:blank");
michael@0 607 // Check we don't create sparse collection of items
michael@0 608 is(lastItem, grid.children[nextSlotIndex], "Item is appended at the next index when an out of bounds index is provided");
michael@0 609 is(grid.children.length, 6);
michael@0 610 is(grid.itemCount, 6);
michael@0 611
michael@0 612 grid.appendItem("one more", "about:blank");
michael@0 613 is(grid.children.length, 7);
michael@0 614 is(grid.itemCount, 7);
michael@0 615
michael@0 616 // clearAll results in slots being emptied
michael@0 617 grid.clearAll();
michael@0 618 is(grid.children.length, 6, "Extra slots are trimmed when we clearAll");
michael@0 619 ok(!Array.some(grid.children, (node) => node.hasAttribute("value")), "All slots have no value attribute after clearAll")
michael@0 620 },
michael@0 621 tearDown: gridSlotsTearDown
michael@0 622 });
michael@0 623
michael@0 624 gTests.push({
michael@0 625 desc: "richgrid slot management",
michael@0 626 setUp: gridSlotsSetup,
michael@0 627 run: function() {
michael@0 628 let grid = this.grid;
michael@0 629 // populate grid with some items
michael@0 630 let numSlots = grid.getAttribute("minSlots");
michael@0 631 for (let idx of [0,1,2,3,4,5]) {
michael@0 632 let item = grid.appendItem("item "+idx, "about:mozilla");
michael@0 633 }
michael@0 634
michael@0 635 is(grid.itemCount, 6, "Grid setup with 6 items");
michael@0 636 is(grid.children.length, 6, "Full grid has the expected number of slots");
michael@0 637
michael@0 638 // removing an item creates a replacement slot *on the end of the stack*
michael@0 639 let item = grid.removeItemAt(0);
michael@0 640 is(item.getAttribute("label"), "item 0", "removeItemAt gives back the populated node");
michael@0 641 is(grid.children.length, 6);
michael@0 642 is(grid.itemCount, 5);
michael@0 643 is(grid.items[0].getAttribute("label"), "item 1", "removeItemAt removes the node so the nextSibling takes its place");
michael@0 644 ok(grid.children[5] && !grid.children[5].hasAttribute("value"), "empty slot is added at the end of the existing children");
michael@0 645
michael@0 646 let item1 = grid.removeItem(grid.items[0]);
michael@0 647 is(grid.children.length, 6);
michael@0 648 is(grid.itemCount, 4);
michael@0 649 is(grid.items[0].getAttribute("label"), "item 2", "removeItem removes the node so the nextSibling takes its place");
michael@0 650 },
michael@0 651 tearDown: gridSlotsTearDown
michael@0 652 });
michael@0 653
michael@0 654 gTests.push({
michael@0 655 desc: "richgrid empty slot selection",
michael@0 656 setUp: gridSlotsSetup,
michael@0 657 run: function() {
michael@0 658 let grid = this.grid;
michael@0 659 // leave grid empty, it has 6 slots
michael@0 660
michael@0 661 is(grid.itemCount, 0, "Grid setup with 0 items");
michael@0 662 is(grid.children.length, 6, "Empty grid has the expected number of slots");
michael@0 663
michael@0 664 info("slot is initially selected: " + grid.children[0].selected);
michael@0 665 grid.selectItem(grid.children[0]);
michael@0 666 info("after selectItem, slot is selected: " + grid.children[0].selected);
michael@0 667
michael@0 668 ok(!grid.children[0].selected, "Attempting to select an empty slot has no effect");
michael@0 669
michael@0 670 grid.toggleItemSelection(grid.children[0]);
michael@0 671 ok(!grid.children[0].selected, "Attempting to toggle selection on an empty slot has no effect");
michael@0 672
michael@0 673 },
michael@0 674 tearDown: gridSlotsTearDown
michael@0 675 });

mercurial