"use strict"; import { FaviconFeed, fetchIconFromRedirects } from "lib/FaviconFeed.jsm"; import { actionTypes as at } from "common/Actions.sys.mjs"; import { GlobalOverrider } from "test/unit/utils"; const FAKE_ENDPOINT = "https://foo.com/"; describe("FaviconFeed", () => { let feed; let globals; let sandbox; let clock; let siteIconsPref; beforeEach(() => { clock = sinon.useFakeTimers(); globals = new GlobalOverrider(); sandbox = globals.sandbox; globals.set("PlacesUtils", { favicons: { setAndFetchFaviconForPage: sandbox.spy(), getFaviconDataForPage: () => Promise.resolve(null), FAVICON_LOAD_NON_PRIVATE: 1, }, history: { TRANSITIONS: { REDIRECT_TEMPORARY: 1, REDIRECT_PERMANENT: 2, }, }, }); globals.set("NewTabUtils", { activityStreamProvider: { executePlacesQuery: () => Promise.resolve([]) }, }); siteIconsPref = true; sandbox .stub(global.Services.prefs, "getBoolPref") .withArgs("browser.chrome.site_icons") .callsFake(() => siteIconsPref); feed = new FaviconFeed(); feed.store = { dispatch: sinon.spy(), getState() { return this.state; }, state: { Prefs: { values: { "tippyTop.service.endpoint": FAKE_ENDPOINT } }, }, }; }); afterEach(() => { clock.restore(); globals.restore(); }); it("should create a FaviconFeed", () => { assert.instanceOf(feed, FaviconFeed); }); describe("#fetchIcon", () => { let domain; let url; beforeEach(() => { domain = "mozilla.org"; url = `https://${domain}/`; feed.getSite = sandbox .stub() .returns(Promise.resolve({ domain, image_url: `${url}/icon.png` })); feed._queryForRedirects.clear(); }); it("should setAndFetchFaviconForPage if the url is in the TippyTop data", async () => { await feed.fetchIcon(url); assert.calledOnce(global.PlacesUtils.favicons.setAndFetchFaviconForPage); assert.calledWith( global.PlacesUtils.favicons.setAndFetchFaviconForPage, sinon.match({ spec: url }), { ref: "tippytop", spec: `${url}/icon.png` }, false, global.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null, undefined ); }); it("should NOT setAndFetchFaviconForPage if site_icons pref is false", async () => { siteIconsPref = false; await feed.fetchIcon(url); assert.notCalled(global.PlacesUtils.favicons.setAndFetchFaviconForPage); }); it("should NOT setAndFetchFaviconForPage if the url is NOT in the TippyTop data", async () => { feed.getSite = sandbox.stub().returns(Promise.resolve(null)); await feed.fetchIcon("https://example.com"); assert.notCalled(global.PlacesUtils.favicons.setAndFetchFaviconForPage); }); it("should issue a fetchIconFromRedirects if the url is NOT in the TippyTop data", async () => { feed.getSite = sandbox.stub().returns(Promise.resolve(null)); sandbox.spy(global.Services.tm, "idleDispatchToMainThread"); await feed.fetchIcon("https://example.com"); assert.calledOnce(global.Services.tm.idleDispatchToMainThread); }); it("should only issue fetchIconFromRedirects once on the same url", async () => { feed.getSite = sandbox.stub().returns(Promise.resolve(null)); sandbox.spy(global.Services.tm, "idleDispatchToMainThread"); await feed.fetchIcon("https://example.com"); await feed.fetchIcon("https://example.com"); assert.calledOnce(global.Services.tm.idleDispatchToMainThread); }); it("should issue fetchIconFromRedirects twice on two different urls", async () => { feed.getSite = sandbox.stub().returns(Promise.resolve(null)); sandbox.spy(global.Services.tm, "idleDispatchToMainThread"); await feed.fetchIcon("https://example.com"); await feed.fetchIcon("https://another.example.com"); assert.calledTwice(global.Services.tm.idleDispatchToMainThread); }); }); describe("#getSite", () => { it("should return site data if RemoteSettings has an entry for the domain", async () => { const get = () => Promise.resolve([{ domain: "example.com", image_url: "foo.img" }]); feed._tippyTop = { get }; const site = await feed.getSite("example.com"); assert.equal(site.domain, "example.com"); }); it("should return null if RemoteSettings doesn't have an entry for the domain", async () => { const get = () => Promise.resolve([]); feed._tippyTop = { get }; const site = await feed.getSite("example.com"); assert.isNull(site); }); it("should lazy init _tippyTop", async () => { assert.isUndefined(feed._tippyTop); await feed.getSite("example.com"); assert.ok(feed._tippyTop); }); }); describe("#onAction", () => { it("should fetchIcon on RICH_ICON_MISSING", async () => { feed.fetchIcon = sinon.spy(); const url = "https://mozilla.org"; feed.onAction({ type: at.RICH_ICON_MISSING, data: { url } }); assert.calledOnce(feed.fetchIcon); assert.calledWith(feed.fetchIcon, url); }); }); describe("#fetchIconFromRedirects", () => { let domain; let url; let iconUrl; beforeEach(() => { domain = "mozilla.org"; url = `https://${domain}/`; iconUrl = `${url}/icon.png`; }); it("should setAndFetchFaviconForPage if the url was redirected with a icon", async () => { sandbox .stub(global.NewTabUtils.activityStreamProvider, "executePlacesQuery") .resolves([ { visit_id: 1, url: domain }, { visit_id: 2, url }, ]); sandbox .stub(global.PlacesUtils.favicons, "getFaviconDataForPage") .callsArgWith(1, { spec: iconUrl }, 0, null, null, 96); await fetchIconFromRedirects(domain); assert.calledOnce(global.PlacesUtils.favicons.setAndFetchFaviconForPage); assert.calledWith( global.PlacesUtils.favicons.setAndFetchFaviconForPage, sinon.match({ spec: domain }), { spec: iconUrl }, false, global.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null, undefined ); }); it("should NOT setAndFetchFaviconForPage if the url doesn't have any redirect", async () => { sandbox .stub(global.NewTabUtils.activityStreamProvider, "executePlacesQuery") .resolves([]); await fetchIconFromRedirects(domain); assert.notCalled(global.PlacesUtils.favicons.setAndFetchFaviconForPage); }); it("should NOT setAndFetchFaviconForPage if the original url doesn't have a icon", async () => { sandbox .stub(global.NewTabUtils.activityStreamProvider, "executePlacesQuery") .resolves([ { visit_id: 1, url: domain }, { visit_id: 2, url }, ]); sandbox .stub(global.PlacesUtils.favicons, "getFaviconDataForPage") .callsArgWith(1, null, null, null, null, null); await fetchIconFromRedirects(domain); assert.notCalled(global.PlacesUtils.favicons.setAndFetchFaviconForPage); }); it("should NOT setAndFetchFaviconForPage if the original url doesn't have a rich icon", async () => { sandbox .stub(global.NewTabUtils.activityStreamProvider, "executePlacesQuery") .resolves([ { visit_id: 1, url: domain }, { visit_id: 2, url }, ]); sandbox .stub(global.PlacesUtils.favicons, "getFaviconDataForPage") .callsArgWith(1, { spec: iconUrl }, 0, null, null, 16); await fetchIconFromRedirects(domain); assert.notCalled(global.PlacesUtils.favicons.setAndFetchFaviconForPage); }); }); });