tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_ext_topSites.js (9220B)


      1 "use strict";
      2 
      3 const { PlacesUtils } = ChromeUtils.importESModule(
      4  "resource://gre/modules/PlacesUtils.sys.mjs"
      5 );
      6 const { NewTabUtils } = ChromeUtils.importESModule(
      7  "resource://gre/modules/NewTabUtils.sys.mjs"
      8 );
      9 const { PlacesTestUtils } = ChromeUtils.importESModule(
     10  "resource://testing-common/PlacesTestUtils.sys.mjs"
     11 );
     12 
     13 const SEARCH_SHORTCUTS_EXPERIMENT_PREF =
     14  "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts";
     15 
     16 // A small 1x1 test png
     17 const IMAGE_1x1 =
     18  "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
     19 
     20 add_task(async function test_topSites() {
     21  Services.prefs.setBoolPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF, false);
     22  let visits = [];
     23  // `getTopFrecentSites` uses a threshold to filter low frecency results.
     24  // The minimum visit threshold is meant to mimic meaningful user engagement.
     25  // The date cutoff is to ensure visits in the recent past are still visible.
     26  // Both values are meant to accomodate variations in the frecency scoring
     27  // algorithm.
     28  const numVisits = 5;
     29  let visitDate = new Date();
     30  visitDate.setMonth(visitDate.getMonth() - 1);
     31  visitDate = visitDate.getTime();
     32 
     33  // Add a visit that is outside the default threshold. It'll be visible
     34  // when the threshold is the bare minimum but not when using the default
     35  // higher threshold used by the extension is applied.
     36  const OLD_URL = "https://www.oldexample.com";
     37  let olderVisitDate = new Date();
     38  olderVisitDate.setMonth(olderVisitDate.getMonth() - 6);
     39  olderVisitDate = olderVisitDate.getTime();
     40  // We do two older visits because a likely default threshold could filter out
     41  // one visit.
     42  PlacesTestUtils.addVisits([
     43    {
     44      url: OLD_URL,
     45      visitDate: olderVisitDate * 1000,
     46    },
     47    {
     48      url: OLD_URL,
     49      visitDate: olderVisitDate * 1000,
     50    },
     51  ]);
     52 
     53  async function setVisit(visit) {
     54    for (let j = 0; j < numVisits; ++j) {
     55      visitDate -= 1000;
     56      visit.visits.push({ date: new Date(visitDate) });
     57    }
     58    visits.push(visit);
     59    await PlacesUtils.history.insert(visit);
     60  }
     61  // Stick a couple sites into history.
     62  for (let i = 0; i < 2; ++i) {
     63    await setVisit({
     64      url: `http://example${i}.com/`,
     65      title: `visit${i}`,
     66      visits: [],
     67    });
     68    await setVisit({
     69      url: `http://www.example${i}.com/foobar`,
     70      title: `visit${i}-www`,
     71      visits: [],
     72    });
     73  }
     74  NewTabUtils.init();
     75 
     76  // Insert a favicon to show that favicons are not returned by default.
     77  let faviconData = new Map();
     78  faviconData.set("http://example0.com", IMAGE_1x1);
     79  await PlacesTestUtils.addFavicons(faviconData);
     80 
     81  // Ensure our links show up in activityStream.
     82  let links = await NewTabUtils.activityStreamLinks.getTopSites({
     83    onePerDomain: false,
     84    topsiteFrecency: 1,
     85  });
     86 
     87  // Sanity checks.
     88  Assert.ok(
     89    !visits.some(visit => visit.url && visit.url.includes(OLD_URL)),
     90    "Recent visits don't include the oldest visit."
     91  );
     92  Assert.ok(
     93    links.some(link => link.url && link.url.includes(OLD_URL)),
     94    "The returned links do include the oldest visit."
     95  );
     96 
     97  equal(
     98    links.length,
     99    // The recent visits plus the one older visit.
    100    visits.length + 1,
    101    "Top sites has been successfully initialized"
    102  );
    103 
    104  // Drop the visits.visits for later testing.
    105  visits = visits.map(v => {
    106    return { url: v.url, title: v.title, favicon: undefined, type: "url" };
    107  });
    108 
    109  // Test that results from all providers are returned by default.
    110  let extension = ExtensionTestUtils.loadExtension({
    111    manifest: {
    112      permissions: ["topSites"],
    113    },
    114    background() {
    115      browser.test.onMessage.addListener(async options => {
    116        let sites = await browser.topSites.get(options);
    117        browser.test.sendMessage("sites", sites);
    118      });
    119    },
    120  });
    121 
    122  await extension.startup();
    123 
    124  function getSites(options) {
    125    extension.sendMessage(options);
    126    return extension.awaitMessage("sites");
    127  }
    128 
    129  Assert.deepEqual(
    130    [visits[0], visits[2]],
    131    await getSites(),
    132    "got topSites default"
    133  );
    134  Assert.deepEqual(
    135    visits,
    136    await getSites({ onePerDomain: false }),
    137    "got topSites all links"
    138  );
    139 
    140  NewTabUtils.activityStreamLinks.blockURL(visits[0]);
    141  ok(
    142    NewTabUtils.blockedLinks.isBlocked(visits[0]),
    143    `link ${visits[0].url} is blocked`
    144  );
    145 
    146  Assert.deepEqual(
    147    [visits[2], visits[1]],
    148    await getSites(),
    149    "got topSites with blocked links filtered out"
    150  );
    151  Assert.deepEqual(
    152    [visits[0], visits[2]],
    153    await getSites({ includeBlocked: true }),
    154    "got topSites with blocked links included"
    155  );
    156 
    157  // Test favicon result
    158  let topSites = await getSites({ includeBlocked: true, includeFavicon: true });
    159  equal(topSites[0].favicon, IMAGE_1x1, "received favicon");
    160 
    161  equal(
    162    1,
    163    (await getSites({ limit: 1, includeBlocked: true })).length,
    164    "limit 1 topSite"
    165  );
    166 
    167  NewTabUtils.uninit();
    168  await extension.unload();
    169  await PlacesUtils.history.clear();
    170  Services.prefs.clearUserPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF);
    171 });
    172 
    173 // Test pinned likns and search shortcuts.
    174 add_task(async function test_topSites_complete() {
    175  Services.prefs.setBoolPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF, true);
    176  NewTabUtils.init();
    177  let time = new Date();
    178  let pinnedIndex = 0;
    179  let entries = [
    180    {
    181      url: `http://pinned1.com/`,
    182      title: "pinned1",
    183      type: "url",
    184      pinned: pinnedIndex++,
    185      visitDate: time,
    186    },
    187    {
    188      url: `http://search1.com/`,
    189      title: "@search1",
    190      type: "search",
    191      pinned: pinnedIndex++,
    192      visitDate: new Date(--time),
    193    },
    194    {
    195      url: `https://amazon.com`,
    196      title: "@amazon",
    197      type: "search",
    198      visitDate: new Date(--time),
    199    },
    200    {
    201      url: `http://history1.com/`,
    202      title: "history1",
    203      type: "url",
    204      visitDate: new Date(--time),
    205    },
    206    {
    207      url: `http://history2.com/`,
    208      title: "history2",
    209      type: "url",
    210      visitDate: new Date(--time),
    211    },
    212    {
    213      url: `https://blocked1.com/`,
    214      title: "blocked1",
    215      type: "blocked",
    216      visitDate: new Date(--time),
    217    },
    218  ];
    219 
    220  for (let entry of entries) {
    221    // Build up frecency.
    222    await PlacesUtils.history.insert({
    223      url: entry.url,
    224      title: entry.title,
    225      visits: new Array(15).fill({
    226        date: entry.visitDate,
    227        transition: PlacesUtils.history.TRANSITIONS.LINK,
    228      }),
    229    });
    230    // Insert a favicon to show that favicons are not returned by default.
    231    await PlacesTestUtils.addFavicons(new Map([[entry.url, IMAGE_1x1]]));
    232    if (entry.pinned !== undefined) {
    233      let info =
    234        entry.type == "search"
    235          ? { url: entry.url, label: entry.title, searchTopSite: true }
    236          : { url: entry.url, title: entry.title };
    237      NewTabUtils.pinnedLinks.pin(info, entry.pinned);
    238    }
    239    if (entry.type == "blocked") {
    240      NewTabUtils.activityStreamLinks.blockURL({ url: entry.url });
    241    }
    242  }
    243 
    244  // Some transformation is necessary to match output data.
    245  let expectedResults = entries
    246    .filter(e => e.type != "blocked")
    247    .map(e => {
    248      e.favicon = undefined;
    249      delete e.visitDate;
    250      delete e.pinned;
    251      return e;
    252    });
    253 
    254  let extension = ExtensionTestUtils.loadExtension({
    255    manifest: {
    256      permissions: ["topSites"],
    257    },
    258    background() {
    259      browser.test.onMessage.addListener(async options => {
    260        let sites = await browser.topSites.get(options);
    261        browser.test.sendMessage("sites", sites);
    262      });
    263    },
    264  });
    265 
    266  await extension.startup();
    267 
    268  // Test that results are returned by the API.
    269  function getSites(options) {
    270    extension.sendMessage(options);
    271    return extension.awaitMessage("sites");
    272  }
    273 
    274  Assert.deepEqual(
    275    expectedResults,
    276    await getSites({ includePinned: true, includeSearchShortcuts: true }),
    277    "got topSites all links"
    278  );
    279 
    280  // Test no shortcuts.
    281  dump(JSON.stringify(await getSites({ includePinned: true })) + "\n");
    282  Assert.ok(
    283    !(await getSites({ includePinned: true })).some(
    284      link => link.type == "search"
    285    ),
    286    "should get no shortcuts"
    287  );
    288 
    289  // Test favicons.
    290  let topSites = await getSites({
    291    includePinned: true,
    292    includeSearchShortcuts: true,
    293    includeFavicon: true,
    294  });
    295  Assert.ok(
    296    topSites.every(f => f.favicon == IMAGE_1x1),
    297    "favicon is correct"
    298  );
    299 
    300  // Test options.limit.
    301  Assert.equal(
    302    1,
    303    (
    304      await getSites({
    305        includePinned: true,
    306        includeSearchShortcuts: true,
    307        limit: 1,
    308      })
    309    ).length,
    310    "limit to 1 topSite"
    311  );
    312 
    313  // Clear history for a pinned entry, then check results.
    314  await PlacesUtils.history.remove("http://pinned1.com/");
    315  let links = await getSites({ includePinned: true });
    316  Assert.ok(
    317    links.find(link => link.url == "http://pinned1.com/"),
    318    "Check unvisited pinned links are returned."
    319  );
    320  links = await getSites();
    321  Assert.ok(
    322    !links.find(link => link.url == "http://pinned1.com/"),
    323    "Check unvisited pinned links are not returned."
    324  );
    325 
    326  await extension.unload();
    327  NewTabUtils.uninit();
    328  await PlacesUtils.history.clear();
    329  await PlacesUtils.bookmarks.eraseEverything();
    330  Services.prefs.clearUserPref(SEARCH_SHORTCUTS_EXPERIMENT_PREF);
    331 });