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

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

mercurial