/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";

const { PlacesTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/PlacesTestUtils.sys.mjs"
);

const {
  ExtensionUtils: { makeDataURI },
} = ChromeUtils.importESModule("resource://gre/modules/ExtensionUtils.sys.mjs");

// A small 1x1 test png
const IMAGE_1x1 =
  "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";

async function updateTopSites(condition) {
  // Toggle the pref to clear the feed cache and force an update.
  await SpecialPowers.pushPrefEnv({
    set: [
      ["browser.newtabpage.activity-stream.feeds.system.topsites", false],
      ["browser.newtabpage.activity-stream.feeds.system.topsites", true],
    ],
  });

  // Wait for the feed to be updated.
  await TestUtils.waitForCondition(() => {
    let sites = AboutNewTab.getTopSites();
    return condition(sites);
  }, "Waiting for top sites to be updated");
}

async function loadExtension() {
  let extension = ExtensionTestUtils.loadExtension({
    manifest: {
      permissions: ["topSites"],
    },
    background() {
      browser.test.onMessage.addListener(async options => {
        let sites = await browser.topSites.get(options);
        browser.test.sendMessage("sites", sites);
      });
    },
  });
  await extension.startup();
  return extension;
}

async function getSites(extension, options) {
  extension.sendMessage(options);
  return extension.awaitMessage("sites");
}

add_setup(async function () {
  await PlacesUtils.history.clear();
  await PlacesUtils.bookmarks.eraseEverything();

  await SpecialPowers.pushPrefEnv({
    set: [
      // The pref for TopSites is empty by default.
      [
        "browser.newtabpage.activity-stream.default.sites",
        "https://www.youtube.com/,https://www.facebook.com/,https://www.amazon.com/,https://www.reddit.com/,https://www.wikipedia.org/,https://twitter.com/",
      ],
      // Toggle the feed off and on as a workaround to read the new prefs.
      ["browser.newtabpage.activity-stream.feeds.system.topsites", false],
      ["browser.newtabpage.activity-stream.feeds.system.topsites", true],
      [
        "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts",
        true,
      ],
    ],
  });

  let tab = await BrowserTestUtils.openNewForegroundTab(
    gBrowser,
    "about:newtab",
    false
  );
  registerCleanupFunction(() => {
    BrowserTestUtils.removeTab(tab);
  });
});

// Tests newtab links with an empty history.
add_task(async function test_topSites_newtab_emptyHistory() {
  let extension = await loadExtension();

  let expectedResults = [
    {
      type: "search",
      url: "https://amazon.com",
      title: "@amazon",
      favicon: null,
    },
    {
      type: "url",
      url: "https://www.youtube.com/",
      title: "youtube",
      favicon: null,
    },
    {
      type: "url",
      url: "https://www.facebook.com/",
      title: "facebook",
      favicon: null,
    },
    {
      type: "url",
      url: "https://www.reddit.com/",
      title: "reddit",
      favicon: null,
    },
    {
      type: "url",
      url: "https://www.wikipedia.org/",
      title: "wikipedia",
      favicon: null,
    },
    {
      type: "url",
      url: "https://twitter.com/",
      title: "twitter",
      favicon: null,
    },
  ];

  Assert.deepEqual(
    expectedResults,
    await getSites(extension, { newtab: true }),
    "got topSites newtab links"
  );

  await extension.unload();
});

// Tests newtab links with some visits.
add_task(async function test_topSites_newtab_visits() {
  let extension = await loadExtension();

  // Add some visits to a couple of URLs.  We need to add at least two visits
  // per URL for it to show up.  Add some extra to be safe, and add one more to
  // the first so that its frecency is larger.
  for (let i = 0; i < 5; i++) {
    await PlacesTestUtils.addVisits([
      "http://example-1.com/",
      "http://example-2.com/",
    ]);
  }
  await PlacesTestUtils.addVisits("http://example-1.com/");

  // Wait for example-1.com to be listed second, after the Amazon search link.
  await updateTopSites(sites => {
    return sites && sites[1] && sites[1].url == "http://example-1.com/";
  });

  let expectedResults = [
    {
      type: "search",
      url: "https://amazon.com",
      title: "@amazon",
      favicon: null,
    },
    {
      type: "url",
      url: "http://example-1.com/",
      title: "test visit for http://example-1.com/",
      favicon: null,
    },
    {
      type: "url",
      url: "http://example-2.com/",
      title: "test visit for http://example-2.com/",
      favicon: null,
    },
    {
      type: "url",
      url: "https://www.youtube.com/",
      title: "youtube",
      favicon: null,
    },
    {
      type: "url",
      url: "https://www.facebook.com/",
      title: "facebook",
      favicon: null,
    },
    {
      type: "url",
      url: "https://www.reddit.com/",
      title: "reddit",
      favicon: null,
    },
    {
      type: "url",
      url: "https://www.wikipedia.org/",
      title: "wikipedia",
      favicon: null,
    },
    {
      type: "url",
      url: "https://twitter.com/",
      title: "twitter",
      favicon: null,
    },
  ];

  Assert.deepEqual(
    expectedResults,
    await getSites(extension, { newtab: true }),
    "got topSites newtab links"
  );

  await extension.unload();
  await PlacesUtils.history.clear();
});

// Tests that the newtab parameter is ignored if newtab Top Sites are disabled.
add_task(async function test_topSites_newtab_ignored() {
  let extension = await loadExtension();
  // Add some visits to a couple of URLs.  We need to add at least two visits
  // per URL for it to show up.  Add some extra to be safe, and add one more to
  // the first so that its frecency is larger.
  for (let i = 0; i < 5; i++) {
    await PlacesTestUtils.addVisits([
      "http://example-1.com/",
      "http://example-2.com/",
    ]);
  }
  await PlacesTestUtils.addVisits("http://example-1.com/");

  // Wait for example-1.com to be listed second, after the Amazon search link.
  await updateTopSites(sites => {
    return sites && sites[1] && sites[1].url == "http://example-1.com/";
  });

  await SpecialPowers.pushPrefEnv({
    set: [["browser.newtabpage.activity-stream.feeds.system.topsites", false]],
  });

  let expectedResults = [
    {
      type: "url",
      url: "http://example-1.com/",
      title: "test visit for http://example-1.com/",
      favicon: null,
    },
    {
      type: "url",
      url: "http://example-2.com/",
      title: "test visit for http://example-2.com/",
      favicon: null,
    },
  ];

  Assert.deepEqual(
    expectedResults,
    await getSites(extension, { newtab: true }),
    "Got top-frecency links from Places"
  );

  await SpecialPowers.popPrefEnv();
  await extension.unload();
  await PlacesUtils.history.clear();
});

// Tests newtab links with some visits and favicons.
add_task(async function test_topSites_newtab_visits_favicons() {
  let extension = await loadExtension();

  // Add some visits to a couple of URLs.  We need to add at least two visits
  // per URL for it to show up.  Add some extra to be safe, and add one more to
  // the first so that its frecency is larger.
  for (let i = 0; i < 5; i++) {
    await PlacesTestUtils.addVisits([
      "http://example-1.com/",
      "http://example-2.com/",
    ]);
  }
  await PlacesTestUtils.addVisits("http://example-1.com/");

  // Give the first URL a favicon but not the second so that we can test links
  // both with and without favicons.
  let faviconData = new Map();
  faviconData.set("http://example-1.com", IMAGE_1x1);
  await PlacesTestUtils.addFavicons(faviconData);

  // Wait for example-1.com to be listed second, after the Amazon search link.
  await updateTopSites(sites => {
    return sites && sites[1] && sites[1].url == "http://example-1.com/";
  });

  let base = "chrome://activity-stream/content/data/content/tippytop/images/";

  let expectedResults = [
    {
      type: "search",
      url: "https://amazon.com",
      title: "@amazon",
      favicon: await makeDataURI(`${base}amazon@2x.png`),
    },
    {
      type: "url",
      url: "http://example-1.com/",
      title: "test visit for http://example-1.com/",
      favicon: IMAGE_1x1,
    },
    {
      type: "url",
      url: "http://example-2.com/",
      title: "test visit for http://example-2.com/",
      favicon: null,
    },
    {
      type: "url",
      url: "https://www.youtube.com/",
      title: "youtube",
      favicon: await makeDataURI(`${base}youtube-com@2x.png`),
    },
    {
      type: "url",
      url: "https://www.facebook.com/",
      title: "facebook",
      favicon: await makeDataURI(`${base}facebook-com@2x.png`),
    },
    {
      type: "url",
      url: "https://www.reddit.com/",
      title: "reddit",
      favicon: await makeDataURI(`${base}reddit-com@2x.png`),
    },
    {
      type: "url",
      url: "https://www.wikipedia.org/",
      title: "wikipedia",
      favicon: await makeDataURI(`${base}wikipedia-org@2x.png`),
    },
    {
      type: "url",
      url: "https://twitter.com/",
      title: "twitter",
      favicon: await makeDataURI(`${base}twitter-com@2x.png`),
    },
  ];

  Assert.deepEqual(
    expectedResults,
    await getSites(extension, { newtab: true, includeFavicon: true }),
    "got topSites newtab links"
  );

  await extension.unload();
  await PlacesUtils.history.clear();
});

// Tests newtab links with some visits, favicons, and the `limit` option.
add_task(async function test_topSites_newtab_visits_favicons_limit() {
  let extension = await loadExtension();

  // Add some visits to a couple of URLs.  We need to add at least two visits
  // per URL for it to show up.  Add some extra to be safe, and add one more to
  // the first so that its frecency is larger.
  for (let i = 0; i < 5; i++) {
    await PlacesTestUtils.addVisits([
      "http://example-1.com/",
      "http://example-2.com/",
    ]);
  }
  await PlacesTestUtils.addVisits("http://example-1.com/");

  // Give the first URL a favicon but not the second so that we can test links
  // both with and without favicons.
  let faviconData = new Map();
  faviconData.set("http://example-1.com", IMAGE_1x1);
  await PlacesTestUtils.addFavicons(faviconData);

  // Wait for example-1.com to be listed second, after the Amazon search link.
  await updateTopSites(sites => {
    return sites && sites[1] && sites[1].url == "http://example-1.com/";
  });

  let expectedResults = [
    {
      type: "search",
      url: "https://amazon.com",
      title: "@amazon",
      favicon: await makeDataURI(
        "chrome://activity-stream/content/data/content/tippytop/images/amazon@2x.png"
      ),
    },
    {
      type: "url",
      url: "http://example-1.com/",
      title: "test visit for http://example-1.com/",
      favicon: IMAGE_1x1,
    },
    {
      type: "url",
      url: "http://example-2.com/",
      title: "test visit for http://example-2.com/",
      favicon: null,
    },
  ];

  Assert.deepEqual(
    expectedResults,
    await getSites(extension, { newtab: true, includeFavicon: true, limit: 3 }),
    "got topSites newtab links"
  );

  await extension.unload();
  await PlacesUtils.history.clear();
});
