browser/metro/base/tests/mochitest/browser_topsites.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 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* Any copyright is dedicated to the Public Domain.
     4    http://creativecommons.org/publicdomain/zero/1.0/ */
     6 "use strict";
     8 //////////////////////////////////////////////////////////////////////////
     9 // Test helpers
    11 let TopSitesTestHelper = {
    12   get grid() {
    13     return Browser.selectedBrowser.contentDocument.getElementById("start-topsites-grid");
    14   },
    15   get document() {
    16     return Browser.selectedBrowser.contentDocument;
    17   },
    18   setup: function() {
    19     return Task.spawn(function(){
    20       if (BrowserUI.isStartTabVisible)
    21         return;
    23       yield addTab("about:start");
    25       yield waitForCondition(() => BrowserUI.isStartTabVisible);
    26     });
    27   },
    28   mockLinks: function th_mockLinks(aLinks) {
    29     // create link objects where index. corresponds to grid position
    30     // falsy values are set to null
    31     let links = (typeof aLinks == "string") ?
    32                 aLinks.split(/\s*,\s*/) : aLinks;
    34     links = links.map(function (id) {
    35       return (id) ? {url: "http://example.com/#" + id, title: id} : null;
    36     });
    37     return links;
    38   },
    39   siteFromNode: function th_siteFromNode(aNode) {
    40     return {
    41       url: aNode.getAttribute("value"),
    42       title: aNode.getAttribute("label")
    43     }
    44   },
    45   clearHistory: function th_clearHistory() {
    46     PlacesUtils.history.removeAllPages();
    47   },
    48   fillHistory: function th_fillHistory(aLinks) {
    49     return Task.spawn(function(){
    50       let numLinks = aLinks.length;
    51       let transitionLink = Ci.nsINavHistoryService.TRANSITION_LINK;
    53       let updateDeferred = Promise.defer();
    55       for (let link of aLinks.reverse()) {
    56         let place = {
    57           uri: Util.makeURI(link.url),
    58           title: link.title,
    59           visits: [{visitDate: Date.now() * 1000, transitionType: transitionLink}]
    60         };
    61         try {
    62           PlacesUtils.asyncHistory.updatePlaces(place, {
    63             handleError: function (aError) {
    64               ok(false, "couldn't add visit to history");
    65               throw new Task.Result(aError);
    66             },
    67             handleResult: function () {},
    68             handleCompletion: function () {
    69               if(--numLinks <= 0) {
    70                 updateDeferred.resolve(true);
    71               }
    72             }
    73           });
    74         } catch(e) {
    75           ok(false, "because: " + e);
    76         }
    77       }
    78       return updateDeferred.promise;
    79     });
    80   },
    82   /**
    83    * Allows to specify the list of pinned links (that have a fixed position in
    84    * the grid.
    85    * @param aLinksPattern the pattern (see below)
    86    *
    87    * Example: setPinnedLinks("foo,,bar")
    88    * Result: 'http://example.com/#foo' is pinned in the first cell. 'http://example.com/#bar' is
    89    *         pinned in the third cell.
    90    */
    91   setPinnedLinks: function th_setPinnedLinks(aLinks) {
    92     let links = TopSitesTestHelper.mockLinks(aLinks);
    94     // (we trust that NewTabUtils works, and test our consumption of it)
    95     // clear all existing pins
    96     Array.forEach(NewTabUtils.pinnedLinks.links, function(aLink){
    97       if(aLink)
    98         NewTabUtils.pinnedLinks.unpin(aLink);
    99     });
   101     links.forEach(function(aLink, aIndex){
   102       if(aLink) {
   103         NewTabUtils.pinnedLinks.pin(aLink, aIndex);
   104       }
   105     });
   106     NewTabUtils.pinnedLinks.save();
   107   },
   109   /**
   110    * Allows to provide a list of links that is used to construct the grid.
   111    * @param aLinksPattern the pattern (see below)
   112    * @param aPinnedLinksPattern the pattern (see below)
   113    *
   114    * Example: setLinks("dougal,florence,zebedee")
   115    * Result: [{url: "http://example.com/#dougal", title: "dougal"},
   116    *          {url: "http://example.com/#florence", title: "florence"}
   117    *          {url: "http://example.com/#zebedee", title: "zebedee"}]
   118    * Example: setLinks("dougal,florence,zebedee","dougal,,zebedee")
   119    * Result: http://example.com/#dougal is pinned at index 0, http://example.com/#florence at index 2
   120    */
   122   setLinks: function th_setLinks(aLinks, aPinnedLinks) {
   123     let links = TopSitesTestHelper.mockLinks(aLinks);
   124     if (links.filter(function(aLink){
   125       return !aLink;
   126     }).length) {
   127       throw new Error("null link objects in setLinks");
   128     }
   130     return Task.spawn(function() {
   131       TopSitesTestHelper.clearHistory();
   133       yield Task.spawn(TopSitesTestHelper.fillHistory(links));
   135       if(aPinnedLinks) {
   136         TopSitesTestHelper.setPinnedLinks(aPinnedLinks);
   137       }
   139       // reset the TopSites state, have it update its cache with the new data fillHistory put there
   140       yield TopSites.prepareCache(true);
   141     });
   142   },
   144   updatePagesAndWait: function th_updatePagesAndWait() {
   145     let deferredUpdate = Promise.defer();
   146     let updater = {
   147       update: function() {
   148         NewTabUtils.allPages.unregister(updater);
   149         deferredUpdate.resolve(true);
   150       }
   151     };
   152     NewTabUtils.allPages.register(updater);
   153     setTimeout(function() {
   154       NewTabUtils.allPages.update();
   155     }, 0);
   156     return deferredUpdate.promise;
   157   },
   159   tearDown: function th_tearDown() {
   160     TopSitesTestHelper.clearHistory();
   161   }
   162 };
   165 //////////////////////////////////////////////////////////////////////////
   168 function test() {
   169   registerCleanupFunction(TopSitesTestHelper.tearDown);
   170   runTests();
   171 }
   173 gTests.push({
   174   desc: "TopSites dependencies",
   175   run: function() {
   176     ok(NewTabUtils, "NewTabUtils is truthy");
   177     ok(TopSites, "TopSites is truthy");
   178   }
   179 });
   181 gTests.push({
   182   desc: "load and display top sites",
   183   setUp: function() {
   184     yield TopSitesTestHelper.setup();
   185     let grid = TopSitesTestHelper.grid;
   187     // setup - set history to known state
   188     yield TopSitesTestHelper.setLinks("brian,dougal,dylan,ermintrude,florence,moose,sgtsam,train,zebedee,zeebad");
   190     let arrangedPromise = waitForEvent(grid, "arranged");
   191     yield TopSitesTestHelper.updatePagesAndWait();
   192     // pause until the update has fired and the view is finishd updating
   193     yield arrangedPromise;
   194   },
   195   run: function() {
   196     let grid = TopSitesTestHelper.grid;
   197     let items = grid.items;
   198     is(items.length, 8, "should be 8 topsites"); // i.e. not 10
   199     if(items.length) {
   200       let firstitem = items[0];
   201       is(
   202         firstitem.getAttribute("label"),
   203         "brian",
   204         "first item label should be 'brian': " + firstitem.getAttribute("label")
   205       );
   206       is(
   207         firstitem.getAttribute("value"),
   208         "http://example.com/#brian",
   209         "first item url should be 'http://example.com/#brian': " + firstitem.getAttribute("url")
   210       );
   211     }
   212   }
   213 });
   215 gTests.push({
   216   desc: "pinned sites",
   217   pins: "dangermouse,zebedee,,,dougal",
   218   setUp: function() {
   219     yield TopSitesTestHelper.setup();
   220     // setup - set history to known state
   221     yield TopSitesTestHelper.setLinks(
   222       "brian,dougal,dylan,ermintrude,florence,moose,sgtsam,train,zebedee,zeebad",
   223       this.pins
   224     );
   225     // pause until the update has fired and the view is finishd updating
   226     let arrangedPromise = waitForEvent(TopSitesTestHelper.grid, "arranged");
   227     yield TopSitesTestHelper.updatePagesAndWait();
   228     yield arrangedPromise;
   229   },
   230   run: function() {
   231     // test that pinned state of each site as rendered matches our expectations
   232     let pins = this.pins.split(",");
   233     let items = TopSitesTestHelper.grid.items;
   234     is(items.length, 8, "should be 8 topsites in the grid");
   236     is(TopSitesTestHelper.document.querySelectorAll("#start-topsites-grid > [pinned]").length, 3, "should be 3 children with 'pinned' attribute");
   237     try {
   238       Array.forEach(items, function(aItem, aIndex){
   239         // pinned state should agree with the pins array
   240         is(
   241             aItem.hasAttribute("pinned"), !!pins[aIndex],
   242             "site at index " + aIndex + " was " +aItem.hasAttribute("pinned")
   243             +", should agree with " + !!pins[aIndex]
   244         );
   245         if (pins[aIndex]) {
   246           is(
   247             aItem.getAttribute("label"),
   248             pins[aIndex],
   249             "pinned site has correct label: " + pins[aIndex] +"=="+ aItem.getAttribute("label")
   250           );
   251         }
   252       }, this);
   253     } catch(e) {
   254       ok(false, this.desc + ": Test of pinned state on items failed");
   255       info("because: " + e.message + "\n" + e.stack);
   256     }
   257   }
   258 });
   260 gTests.push({
   261   desc: "pin site",
   262   setUp: function() {
   263     yield TopSitesTestHelper.setup();
   264     // setup - set history to known state
   265     yield TopSitesTestHelper.setLinks("sgtsam,train,zebedee,zeebad", []); // nothing initially pinned
   266     // pause until the update has fired and the view is finishd updating
   267     let arrangedPromise = waitForEvent(TopSitesTestHelper.grid, "arranged");
   268     yield TopSitesTestHelper.updatePagesAndWait();
   269     yield arrangedPromise;
   270   },
   271   run: function() {
   272     // pin a site
   273     // test that site is pinned as expected
   274     // and that sites fill positions around it
   275     let grid = TopSitesTestHelper.grid,
   276         items = grid.items;
   277     is(items.length, 4, this.desc + ": should be 4 topsites");
   279     let tile = grid.items[2],
   280         url = tile.getAttribute("value"),
   281         title = tile.getAttribute("label");
   283     info(this.desc + ": pinning site at index 2");
   284     TopSites.pinSites([{
   285           url: url,
   286           title: title
   287         }], [2]);
   289     // pinning shouldn't require re-arranging - just wait for isUpdating flag to flip
   290     yield waitForCondition(function(){
   291       return !grid.controller.isUpdating;
   292     });
   294     let thirdTile = grid.items[2];
   295     ok( thirdTile.hasAttribute("pinned"), thirdTile.getAttribute("value")+ " should look pinned" );
   297     // visit some more sites
   298     yield TopSitesTestHelper.fillHistory( TopSitesTestHelper.mockLinks("brian,dougal,dylan,ermintrude,florence,moose") );
   300     // force flush and repopulation of links cache
   301     yield TopSites.prepareCache(true);
   302     // pause until the update has fired and the view is finishd updating
   303     let arrangedPromise = waitForEvent(grid, "arranged");
   304     yield TopSitesTestHelper.updatePagesAndWait();
   305     yield arrangedPromise;
   307     // check zebedee is still pinned at index 2
   308     is( items[2].getAttribute("label"), "zebedee", "Pinned site remained at its index" );
   309     ok( items[2].hasAttribute("pinned"), "3rd site should still look pinned" );
   310   }
   311 });
   313 gTests.push({
   314   desc: "unpin site",
   315   pins: ",zebedee",
   316   setUp: function() {
   317     yield TopSitesTestHelper.setup();
   318     // setup - set history to known state
   319     yield TopSitesTestHelper.setLinks(
   320       "brian,dougal,dylan,ermintrude,florence,moose,sgtsam,train,zebedee,zeebad",
   321       this.pins
   322     );
   323     // pause until the update has fired and the view is finishd updating
   324     let arrangedPromise = waitForEvent(TopSitesTestHelper.grid, "arranged");
   325     yield TopSitesTestHelper.updatePagesAndWait();
   326     yield arrangedPromise;
   327   },
   328   run: function() {
   329     // unpin a pinned site
   330     // test that sites are unpinned as expected
   331     let grid = TopSitesTestHelper.grid,
   332         items = grid.items;
   333     is(items.length, 8, this.desc + ": should be 8 topsites");
   334     let site = {
   335       url: items[1].getAttribute("value"),
   336       title: items[1].getAttribute("label")
   337     };
   338     // verify assumptions before unpinning this site
   339     ok( NewTabUtils.pinnedLinks.isPinned(site), "2nd item is pinned" );
   340     ok( items[1].hasAttribute("pinned"), "2nd item has pinned attribute" );
   342     // unpinning shouldn't require re-arranging - just wait for isUpdating flag to flip
   343     TopSites.unpinSites([site]);
   345     yield waitForCondition(function(){
   346       return !grid.controller.isUpdating;
   347     });
   349     let secondTile = grid.items[1];
   350     ok( !secondTile.hasAttribute("pinned"), "2nd item should no longer be marked as pinned" );
   351     ok( !NewTabUtils.pinnedLinks.isPinned(site), "2nd item should no longer be pinned" );
   352   }
   353 });
   355 gTests.push({
   356   desc: "block/unblock sites",
   357   setUp: function() {
   358     yield TopSitesTestHelper.setup();
   359     // setup - set topsites to known state
   360     yield TopSitesTestHelper.setLinks(
   361       "brian,dougal,dylan,ermintrude,florence,moose,sgtsam,train,zebedee,zeebad,basic,coral",
   362       ",dougal"
   363     );
   364     // pause until the update has fired and the view is finishd updating
   365     let arrangedPromise = waitForEvent(TopSitesTestHelper.grid, "arranged");
   366     yield TopSitesTestHelper.updatePagesAndWait();
   367     yield arrangedPromise;
   368   },
   369   run: function() {
   370     try {
   371       // block a site
   372       // test that sites are removed from the grid as expected
   373       let grid = TopSitesTestHelper.grid,
   374           items = grid.items;
   375       is(items.length, 8, this.desc + ": should be 8 topsites");
   377       let brianSite = TopSitesTestHelper.siteFromNode(items[0]);
   378       let dougalSite = TopSitesTestHelper.siteFromNode(items[1]);
   379       let dylanSite = TopSitesTestHelper.siteFromNode(items[2]);
   381       let arrangedPromise = waitForEvent(grid, "arranged");
   382       // we'll block brian (he's not pinned)
   383       TopSites.hideSites([brianSite]);
   384       // pause until the update has fired and the view is finished updating
   385       yield arrangedPromise;
   387       // verify brian is blocked and removed from the grid
   388       ok( (new Site(brianSite)).blocked, "Site has truthy blocked property" );
   389       ok( NewTabUtils.blockedLinks.isBlocked(brianSite), "Site was blocked" );
   390       is( grid.querySelectorAll("[value='"+brianSite.url+"']").length, 0, "Blocked site was removed from grid");
   392       // make sure the empty slot was backfilled
   393       is(items.length, 8, this.desc + ": should be 8 topsites");
   395       arrangedPromise = waitForEvent(grid, "arranged");
   396       // block dougal,dylan. dougal is currently pinned at index 1
   397       TopSites.hideSites([dougalSite, dylanSite]);
   398       // pause until the update has fired and the view is finished updating
   399       yield arrangedPromise;
   401       // verify dougal is blocked and removed from the grid
   402       ok( (new Site(dougalSite)).blocked, "Site has truthy blocked property" );
   403       ok( NewTabUtils.blockedLinks.isBlocked(dougalSite), "Site was blocked" );
   404       ok( !NewTabUtils.pinnedLinks.isPinned(dougalSite), "Blocked Site is no longer pinned" );
   405       is( grid.querySelectorAll("[value='"+dougalSite.url+"']").length, 0, "Blocked site was removed from grid");
   407       // verify dylan is blocked and removed from the grid
   408       ok( (new Site(dylanSite)).blocked, "Site has truthy blocked property" );
   409       ok( NewTabUtils.blockedLinks.isBlocked(dylanSite), "Site was blocked" );
   410       ok( !NewTabUtils.pinnedLinks.isPinned(dylanSite), "Blocked Site is no longer pinned" );
   411       is( grid.querySelectorAll("[value='"+dylanSite.url+"']").length, 0, "Blocked site was removed from grid");
   413       // make sure the empty slots were backfilled
   414       is(items.length, 8, this.desc + ": should be 8 topsites");
   416       arrangedPromise = waitForEvent(grid, "arranged");
   417       TopSites.restoreSites([brianSite, dougalSite, dylanSite]);
   418       // pause until the update has fired and the view is finished updating
   419       yield arrangedPromise;
   421       // verify brian, dougal and dyland are unblocked and back in the grid
   422       ok( !NewTabUtils.blockedLinks.isBlocked(brianSite), "site was unblocked" );
   423       is( grid.querySelectorAll("[value='"+brianSite.url+"']").length, 1, "Unblocked site is back in the grid");
   425       ok( !NewTabUtils.blockedLinks.isBlocked(dougalSite), "site was unblocked" );
   426       is( grid.querySelectorAll("[value='"+dougalSite.url+"']").length, 1, "Unblocked site is back in the grid");
   427       // ..and that a previously pinned site is re-pinned after being blocked, then restored
   428       ok( NewTabUtils.pinnedLinks.isPinned(dougalSite), "Restoring previously pinned site makes it pinned again" );
   429       is( grid.items[1].getAttribute("value"), dougalSite.url, "Blocked Site restored to pinned index" );
   431       ok( !NewTabUtils.blockedLinks.isBlocked(dylanSite), "site was unblocked" );
   432       is( grid.querySelectorAll("[value='"+dylanSite.url+"']").length, 1, "Unblocked site is back in the grid");
   434     } catch(ex) {
   436       ok(false, this.desc+": Caught exception in test: " + ex);
   437       info("trace: " + ex.stack);
   438     }
   439   }
   440 });
   442 gTests.push({
   443   desc: "delete and restore site tiles",
   444   pins: "brian",
   445   setUp: function() {
   446     yield TopSitesTestHelper.setup();
   447     // setup - set history to known state
   448     yield TopSitesTestHelper.setLinks(
   449       "brian,dougal,dylan,ermintrude",
   450       this.pins
   451     );
   452     // pause until the update has fired and the view is finishd updating
   453     let arrangedPromise = waitForEvent(TopSitesTestHelper.grid, "arranged");
   454     yield TopSitesTestHelper.updatePagesAndWait();
   455     yield arrangedPromise;
   456   },
   457   run: function() {
   458     // delete a both pinned and unpinned sites
   459     // test that sites are removed from the grid
   460     let grid = TopSitesTestHelper.grid,
   461         items = grid.items;
   462     is(items.length, 4, this.desc + ": should be 4 topsites");
   464     let brianTile = grid.querySelector('richgriditem[value$="brian"]');
   465     let dougalTile = grid.querySelector('richgriditem[value$="dougal"]')
   467     // verify assumptions before deleting sites
   468     ok( brianTile, "Tile for Brian was created");
   469     ok( dougalTile, "Tile for Dougal was created");
   471     let brianSite = TopSitesTestHelper.siteFromNode(brianTile);
   472     let dougalSite = TopSitesTestHelper.siteFromNode(dougalTile);
   473     ok( NewTabUtils.pinnedLinks.isPinned( brianSite ), "Brian tile is pinned" );
   475     // select the 2 tiles
   476     grid.toggleItemSelection(brianTile);
   477     grid.toggleItemSelection(dougalTile);
   478     is(grid.selectedItems.length, 2, "2 tiles were selected");
   480     // pause until the update has fired and the view is finishd updating
   481     let arrangedPromise = waitForEvent(grid, "arranged");
   483     // raise a mock context-action event to trigger deletion of the selection
   484     let event = document.createEvent("Events");
   485     event.action = "delete";
   486     event.initEvent("context-action", true, true); // is cancelable
   487     grid.dispatchEvent(event);
   489     yield arrangedPromise;
   491     // those sites are blocked and their tiles have been removed from the grid?
   492     ok( !grid.querySelector('richgriditem[value="'+brianSite.value+'"]'));
   493     ok( !grid.querySelector('richgriditem[value="'+dougalSite.value+'"]'));
   494     ok( NewTabUtils.blockedLinks.isBlocked(brianSite), "Brian site was blocked" );
   495     ok( NewTabUtils.blockedLinks.isBlocked(dougalSite), "Dougal site was blocked" );
   496     // with the tiles deleted, selection should be empty
   497     is( grid.selectedItems.length, 0, "Gris selection is empty after deletion" );
   499     // raise a mock context-action event to trigger restore
   500     arrangedPromise = waitForEvent(grid, "arranged");
   501     event = document.createEvent("Events");
   502     event.action = "restore";
   503     event.initEvent("context-action", true, true); // is cancelable
   504     grid.dispatchEvent(event);
   506     yield arrangedPromise;
   507     brianTile = grid.querySelector('richgriditem[value$="brian"]');
   508     dougalTile = grid.querySelector('richgriditem[value$="dougal"]');
   510     // those tiles have been restored to the grid?
   511     ok( brianTile, "First tile was restored to the grid" );
   512     ok( dougalTile, "2nd tile was restored to the grid" );
   514     is(grid.selectedItems.length, 2, "2 tiles are still selected");
   515     is( grid.selectedItems[0], brianTile, "Brian is still selected" );
   516     is( grid.selectedItems[1], dougalTile, "Dougal is still selected" );
   517     ok( NewTabUtils.pinnedLinks.isPinned( brianSite ), "Brian tile is still pinned" );
   518     ok( !NewTabUtils.blockedLinks.isBlocked(brianSite), "Brian site was unblocked" );
   519     ok( !NewTabUtils.blockedLinks.isBlocked(dougalSite), "Dougal site was unblocked" );
   521   }
   522 });

mercurial