FaviconFeed.test.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. "use strict";
  2. import {FaviconFeed, fetchIconFromRedirects} from "lib/FaviconFeed.jsm";
  3. import {actionTypes as at} from "common/Actions.jsm";
  4. import {GlobalOverrider} from "test/unit/utils";
  5. const FAKE_ENDPOINT = "https://foo.com/";
  6. describe("FaviconFeed", () => {
  7. let feed;
  8. let globals;
  9. let sandbox;
  10. let clock;
  11. let siteIconsPref;
  12. beforeEach(() => {
  13. clock = sinon.useFakeTimers();
  14. globals = new GlobalOverrider();
  15. sandbox = globals.sandbox;
  16. globals.set("PlacesUtils", {
  17. favicons: {
  18. setAndFetchFaviconForPage: sandbox.spy(),
  19. getFaviconDataForPage: () => Promise.resolve(null),
  20. FAVICON_LOAD_NON_PRIVATE: 1,
  21. },
  22. history: {
  23. TRANSITIONS: {
  24. REDIRECT_TEMPORARY: 1,
  25. REDIRECT_PERMANENT: 2,
  26. },
  27. },
  28. });
  29. globals.set("NewTabUtils", {activityStreamProvider: {executePlacesQuery: () => Promise.resolve([])}});
  30. siteIconsPref = true;
  31. sandbox.stub(global.Services.prefs, "getBoolPref")
  32. .withArgs("browser.chrome.site_icons").callsFake(() => siteIconsPref);
  33. feed = new FaviconFeed();
  34. feed.store = {
  35. dispatch: sinon.spy(),
  36. getState() { return this.state; },
  37. state: {Prefs: {values: {"tippyTop.service.endpoint": FAKE_ENDPOINT}}},
  38. };
  39. });
  40. afterEach(() => {
  41. clock.restore();
  42. globals.restore();
  43. });
  44. it("should create a FaviconFeed", () => {
  45. assert.instanceOf(feed, FaviconFeed);
  46. });
  47. describe("#fetchIcon", () => {
  48. let domain;
  49. let url;
  50. beforeEach(() => {
  51. domain = "mozilla.org";
  52. url = `https://${domain}/`;
  53. feed.getSite = sandbox.stub().returns(Promise.resolve({domain, image_url: `${url}/icon.png`}));
  54. feed._queryForRedirects.clear();
  55. });
  56. it("should setAndFetchFaviconForPage if the url is in the TippyTop data", async () => {
  57. await feed.fetchIcon(url);
  58. assert.calledOnce(global.PlacesUtils.favicons.setAndFetchFaviconForPage);
  59. assert.calledWith(global.PlacesUtils.favicons.setAndFetchFaviconForPage,
  60. sinon.match({spec: url}),
  61. {ref: "tippytop", spec: `${url}/icon.png`},
  62. false,
  63. global.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
  64. null,
  65. undefined);
  66. });
  67. it("should NOT setAndFetchFaviconForPage if site_icons pref is false", async () => {
  68. siteIconsPref = false;
  69. await feed.fetchIcon(url);
  70. assert.notCalled(global.PlacesUtils.favicons.setAndFetchFaviconForPage);
  71. });
  72. it("should NOT setAndFetchFaviconForPage if the url is NOT in the TippyTop data", async () => {
  73. feed.getSite = sandbox.stub().returns(Promise.resolve(null));
  74. await feed.fetchIcon("https://example.com");
  75. assert.notCalled(global.PlacesUtils.favicons.setAndFetchFaviconForPage);
  76. });
  77. it("should issue a fetchIconFromRedirects if the url is NOT in the TippyTop data", async () => {
  78. feed.getSite = sandbox.stub().returns(Promise.resolve(null));
  79. sandbox.spy(global.Services.tm, "idleDispatchToMainThread");
  80. await feed.fetchIcon("https://example.com");
  81. assert.calledOnce(global.Services.tm.idleDispatchToMainThread);
  82. });
  83. it("should only issue fetchIconFromRedirects once on the same url", async () => {
  84. feed.getSite = sandbox.stub().returns(Promise.resolve(null));
  85. sandbox.spy(global.Services.tm, "idleDispatchToMainThread");
  86. await feed.fetchIcon("https://example.com");
  87. await feed.fetchIcon("https://example.com");
  88. assert.calledOnce(global.Services.tm.idleDispatchToMainThread);
  89. });
  90. it("should issue fetchIconFromRedirects twice on two different urls", async () => {
  91. feed.getSite = sandbox.stub().returns(Promise.resolve(null));
  92. sandbox.spy(global.Services.tm, "idleDispatchToMainThread");
  93. await feed.fetchIcon("https://example.com");
  94. await feed.fetchIcon("https://another.example.com");
  95. assert.calledTwice(global.Services.tm.idleDispatchToMainThread);
  96. });
  97. });
  98. describe("#getSite", () => {
  99. it("should return site data if RemoteSettings has an entry for the domain", async () => {
  100. const get = () => Promise.resolve([{domain: "example.com", image_url: "foo.img"}]);
  101. feed._tippyTop = {get};
  102. const site = await feed.getSite("example.com");
  103. assert.equal(site.domain, "example.com");
  104. });
  105. it("should return null if RemoteSettings doesn't have an entry for the domain", async () => {
  106. const get = () => Promise.resolve([]);
  107. feed._tippyTop = {get};
  108. const site = await feed.getSite("example.com");
  109. assert.isNull(site);
  110. });
  111. it("should lazy init _tippyTop", async () => {
  112. assert.isUndefined(feed._tippyTop);
  113. await feed.getSite("example.com");
  114. assert.ok(feed._tippyTop);
  115. });
  116. });
  117. describe("#onAction", () => {
  118. it("should fetchIcon on RICH_ICON_MISSING", async () => {
  119. feed.fetchIcon = sinon.spy();
  120. const url = "https://mozilla.org";
  121. feed.onAction({type: at.RICH_ICON_MISSING, data: {url}});
  122. assert.calledOnce(feed.fetchIcon);
  123. assert.calledWith(feed.fetchIcon, url);
  124. });
  125. });
  126. describe("#fetchIconFromRedirects", () => {
  127. let domain;
  128. let url;
  129. let iconUrl;
  130. beforeEach(() => {
  131. domain = "mozilla.org";
  132. url = `https://${domain}/`;
  133. iconUrl = `${url}/icon.png`;
  134. });
  135. it("should setAndFetchFaviconForPage if the url was redirected with a icon", async () => {
  136. sandbox.stub(global.NewTabUtils.activityStreamProvider, "executePlacesQuery")
  137. .resolves([{visit_id: 1, url: domain}, {visit_id: 2, url}]);
  138. sandbox.stub(global.PlacesUtils.favicons, "getFaviconDataForPage")
  139. .callsArgWith(1, {spec: iconUrl}, 0, null, null, 96);
  140. await fetchIconFromRedirects(domain);
  141. assert.calledOnce(global.PlacesUtils.favicons.setAndFetchFaviconForPage);
  142. assert.calledWith(global.PlacesUtils.favicons.setAndFetchFaviconForPage,
  143. sinon.match({spec: domain}),
  144. {spec: iconUrl},
  145. false,
  146. global.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
  147. null,
  148. undefined);
  149. });
  150. it("should NOT setAndFetchFaviconForPage if the url doesn't have any redirect", async () => {
  151. sandbox.stub(global.NewTabUtils.activityStreamProvider, "executePlacesQuery")
  152. .resolves([]);
  153. await fetchIconFromRedirects(domain);
  154. assert.notCalled(global.PlacesUtils.favicons.setAndFetchFaviconForPage);
  155. });
  156. it("should NOT setAndFetchFaviconForPage if the original url doesn't have a icon", async () => {
  157. sandbox.stub(global.NewTabUtils.activityStreamProvider, "executePlacesQuery")
  158. .resolves([{visit_id: 1, url: domain}, {visit_id: 2, url}]);
  159. sandbox.stub(global.PlacesUtils.favicons, "getFaviconDataForPage")
  160. .callsArgWith(1, null, null, null, null, null);
  161. await fetchIconFromRedirects(domain);
  162. assert.notCalled(global.PlacesUtils.favicons.setAndFetchFaviconForPage);
  163. });
  164. it("should NOT setAndFetchFaviconForPage if the original url doesn't have a rich icon", async () => {
  165. sandbox.stub(global.NewTabUtils.activityStreamProvider, "executePlacesQuery")
  166. .resolves([{visit_id: 1, url: domain}, {visit_id: 2, url}]);
  167. sandbox.stub(global.PlacesUtils.favicons, "getFaviconDataForPage")
  168. .callsArgWith(1, {spec: iconUrl}, 0, null, null, 16);
  169. await fetchIconFromRedirects(domain);
  170. assert.notCalled(global.PlacesUtils.favicons.setAndFetchFaviconForPage);
  171. });
  172. });
  173. });