AboutPreferences.test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /* global Services */
  2. import {AboutPreferences, PREFERENCES_LOADED_EVENT} from "lib/AboutPreferences.jsm";
  3. import {actionTypes as at} from "common/Actions.jsm";
  4. import {GlobalOverrider} from "test/unit/utils";
  5. describe("AboutPreferences Feed", () => {
  6. let globals;
  7. let sandbox;
  8. let Sections;
  9. let DiscoveryStream;
  10. let instance;
  11. beforeEach(() => {
  12. globals = new GlobalOverrider();
  13. sandbox = globals.sandbox;
  14. Sections = [];
  15. DiscoveryStream = {config: {enabled: false}};
  16. instance = new AboutPreferences();
  17. instance.store = {
  18. dispatch: sandbox.stub(),
  19. getState: () => ({Sections, DiscoveryStream}),
  20. };
  21. });
  22. afterEach(() => {
  23. globals.restore();
  24. });
  25. describe("#onAction", () => {
  26. it("should call .init() on an INIT action", () => {
  27. const stub = sandbox.stub(instance, "init");
  28. instance.onAction({type: at.INIT});
  29. assert.calledOnce(stub);
  30. });
  31. it("should call .uninit() on an UNINIT action", () => {
  32. const stub = sandbox.stub(instance, "uninit");
  33. instance.onAction({type: at.UNINIT});
  34. assert.calledOnce(stub);
  35. });
  36. it("should call .openPreferences on SETTINGS_OPEN", () => {
  37. const action = {type: at.SETTINGS_OPEN, _target: {browser: {ownerGlobal: {openPreferences: sinon.spy()}}}};
  38. instance.onAction(action);
  39. assert.calledOnce(action._target.browser.ownerGlobal.openPreferences);
  40. });
  41. it("should call .BrowserOpenAddonsMgr with the extension id on OPEN_WEBEXT_SETTINGS", () => {
  42. const action = {type: at.OPEN_WEBEXT_SETTINGS, data: "foo", _target: {browser: {ownerGlobal: {BrowserOpenAddonsMgr: sinon.spy()}}}};
  43. instance.onAction(action);
  44. assert.calledWith(
  45. action._target.browser.ownerGlobal.BrowserOpenAddonsMgr,
  46. "addons://detail/foo"
  47. );
  48. });
  49. });
  50. describe("#observe", () => {
  51. it("should watch for about:preferences loading", () => {
  52. sandbox.stub(Services.obs, "addObserver");
  53. instance.init();
  54. assert.calledOnce(Services.obs.addObserver);
  55. assert.calledWith(Services.obs.addObserver, instance, PREFERENCES_LOADED_EVENT);
  56. });
  57. it("should stop watching on uninit", () => {
  58. sandbox.stub(Services.obs, "removeObserver");
  59. instance.uninit();
  60. assert.calledOnce(Services.obs.removeObserver);
  61. assert.calledWith(Services.obs.removeObserver, instance, PREFERENCES_LOADED_EVENT);
  62. });
  63. it("should try to render on event", async () => {
  64. const stub = sandbox.stub(instance, "renderPreferences");
  65. instance._strings = {};
  66. Sections.push({});
  67. await instance.observe(window, PREFERENCES_LOADED_EVENT);
  68. assert.calledOnce(stub);
  69. assert.equal(stub.firstCall.args[0], window);
  70. assert.deepEqual(stub.firstCall.args[1], instance._strings);
  71. assert.include(stub.firstCall.args[2], Sections[0]);
  72. });
  73. it("Hide highlights in sections if discovery stream is enabled", async () => {
  74. const stub = sandbox.stub(instance, "renderPreferences");
  75. instance._strings = {};
  76. const titleString = "title";
  77. Sections.push({pref: {titleString}, id: "highlights"});
  78. DiscoveryStream = {config: {enabled: true}};
  79. await instance.observe(window, PREFERENCES_LOADED_EVENT);
  80. assert.calledOnce(stub);
  81. assert.equal(stub.firstCall.args[2][0].id, "search");
  82. assert.equal(stub.firstCall.args[2][1].id, "topsites");
  83. assert.equal(stub.firstCall.args[2][2].id, "highlights");
  84. assert.isTrue(stub.firstCall.args[2][2].shouldHidePref);
  85. });
  86. it("Hide topstories rows select in sections if discovery stream is enabled", async () => {
  87. const stub = sandbox.stub(instance, "renderPreferences");
  88. instance._strings = {};
  89. Sections.push({rowsPref: "row_pref", maxRows: 3, pref: {descString: "foo"}, learnMore: {link: "https://foo.com"}, id: "topstories"});
  90. DiscoveryStream = {config: {enabled: true}};
  91. await instance.observe(window, PREFERENCES_LOADED_EVENT);
  92. assert.calledOnce(stub);
  93. assert.equal(stub.firstCall.args[2][0].id, "search");
  94. assert.equal(stub.firstCall.args[2][1].id, "topsites");
  95. assert.equal(stub.firstCall.args[2][2].id, "topstories");
  96. assert.isEmpty(stub.firstCall.args[2][2].rowsPref);
  97. });
  98. });
  99. describe("#strings", () => {
  100. let activityStreamLocale;
  101. let fetchStub;
  102. let fetchText;
  103. beforeEach(() => {
  104. global.Cc["@mozilla.org/browser/aboutnewtab-service;1"] = {
  105. getService() {
  106. return {activityStreamLocale};
  107. },
  108. };
  109. fetchStub = sandbox.stub().resolves({text: () => Promise.resolve(fetchText)});
  110. globals.set("fetch", fetchStub);
  111. });
  112. it("should use existing strings if they exist", async () => {
  113. instance._strings = {};
  114. const strings = await instance.strings;
  115. assert.equal(strings, instance._strings);
  116. });
  117. it("should report failure if missing", async () => {
  118. sandbox.stub(Cu, "reportError");
  119. const strings = await instance.strings;
  120. assert.calledOnce(Cu.reportError);
  121. assert.deepEqual(strings, {});
  122. });
  123. it("should fetch with the appropriate locale", async () => {
  124. activityStreamLocale = "en-TEST";
  125. await instance.strings;
  126. assert.calledOnce(fetchStub);
  127. assert.include(fetchStub.firstCall.args[0], activityStreamLocale);
  128. });
  129. it("should extract strings from js text", async () => {
  130. const testStrings = {hello: "world"};
  131. fetchText = `var strings = ${JSON.stringify(testStrings)};`;
  132. const strings = await instance.strings;
  133. assert.deepEqual(strings, testStrings);
  134. });
  135. });
  136. describe("#renderPreferences", () => {
  137. let node;
  138. let strings;
  139. let prefStructure;
  140. let Preferences;
  141. let gHomePane;
  142. const testRender = () => instance.renderPreferences({
  143. document: {
  144. createXULElement: sandbox.stub().returns(node),
  145. createProcessingInstruction: sandbox.stub(),
  146. createElementNS: sandbox.stub().callsFake((NS, el) => node),
  147. getElementById: sandbox.stub().returns(node),
  148. insertBefore: sandbox.stub().returnsArg(0),
  149. querySelector: sandbox.stub().returns({appendChild: sandbox.stub()}),
  150. },
  151. Preferences,
  152. gHomePane,
  153. }, strings, prefStructure, DiscoveryStream.config);
  154. beforeEach(() => {
  155. node = {
  156. appendChild: sandbox.stub().returnsArg(0),
  157. addEventListener: sandbox.stub(),
  158. classList: {add: sandbox.stub(), remove: sandbox.stub()},
  159. cloneNode: sandbox.stub().returnsThis(),
  160. insertAdjacentElement: sandbox.stub().returnsArg(1),
  161. setAttribute: sandbox.stub(),
  162. remove: sandbox.stub(),
  163. style: {},
  164. };
  165. strings = {};
  166. prefStructure = [];
  167. Preferences = {
  168. add: sandbox.stub(),
  169. get: sandbox.stub().returns({}),
  170. };
  171. gHomePane = {toggleRestoreDefaultsBtn: sandbox.stub()};
  172. });
  173. describe("#formatString", () => {
  174. it("should fall back to string id if missing", () => {
  175. testRender();
  176. assert.equal(node.textContent, "prefs_home_description");
  177. });
  178. it("should use provided plain string", () => {
  179. strings = {prefs_home_description: "hello"};
  180. testRender();
  181. assert.equal(node.textContent, "hello");
  182. });
  183. it("should fall back to string object if missing", () => {
  184. const titleString = {id: "foo"};
  185. prefStructure = [{pref: {titleString}}];
  186. testRender();
  187. assert.calledWith(node.setAttribute, "label", JSON.stringify(titleString));
  188. });
  189. it("should use provided string object id", () => {
  190. strings = {foo: "bar"};
  191. prefStructure = [{pref: {titleString: {id: "foo"}}}];
  192. testRender();
  193. assert.calledWith(node.setAttribute, "label", "bar");
  194. });
  195. it("should use values in string object", () => {
  196. strings = {foo: "l{n}{n}t"};
  197. prefStructure = [{pref: {titleString: {id: "foo", values: {n: 3}}}}];
  198. testRender();
  199. assert.calledWith(node.setAttribute, "label", "l33t");
  200. });
  201. });
  202. describe("#linkPref", () => {
  203. it("should add a pref to the global", () => {
  204. prefStructure = [{}];
  205. testRender();
  206. assert.calledOnce(Preferences.add);
  207. });
  208. it("should skip adding if not shown", () => {
  209. prefStructure = [{shouldHidePref: true}];
  210. testRender();
  211. assert.notCalled(Preferences.add);
  212. });
  213. });
  214. describe("pref icon", () => {
  215. it("should default to webextension icon", () => {
  216. prefStructure = [{}];
  217. testRender();
  218. assert.calledWith(node.setAttribute, "src", "resource://activity-stream/data/content/assets/glyph-webextension-16.svg");
  219. });
  220. it("should use desired glyph icon", () => {
  221. prefStructure = [{icon: "highlights"}];
  222. testRender();
  223. assert.calledWith(node.setAttribute, "src", "resource://activity-stream/data/content/assets/glyph-highlights-16.svg");
  224. });
  225. it("should use specified chrome icon", () => {
  226. const icon = "chrome://the/icon.svg";
  227. prefStructure = [{icon}];
  228. testRender();
  229. assert.calledWith(node.setAttribute, "src", icon);
  230. });
  231. });
  232. describe("title line", () => {
  233. it("should render a title", () => {
  234. const titleString = "the_title";
  235. prefStructure = [{pref: {titleString}}];
  236. testRender();
  237. assert.calledWith(node.setAttribute, "label", titleString);
  238. });
  239. it("should add a link for top stories", () => {
  240. const href = "https://disclaimer/";
  241. prefStructure = [{learnMore: {link: {href}}, id: "topstories"}];
  242. testRender();
  243. assert.calledWith(node.setAttribute, "href", href);
  244. });
  245. });
  246. describe("description line", () => {
  247. it("should render a description", () => {
  248. const descString = "the_desc";
  249. prefStructure = [{pref: {descString}}];
  250. testRender();
  251. assert.equal(node.textContent, descString);
  252. });
  253. it("should render rows dropdown with appropriate number", () => {
  254. prefStructure = [{rowsPref: "row_pref", maxRows: 3, pref: {descString: "foo"}}];
  255. testRender();
  256. assert.calledWith(node.setAttribute, "value", 1);
  257. assert.calledWith(node.setAttribute, "value", 2);
  258. assert.calledWith(node.setAttribute, "value", 3);
  259. });
  260. });
  261. describe("nested prefs", () => {
  262. it("should render a nested pref", () => {
  263. const titleString = "im_nested";
  264. prefStructure = [{pref: {nestedPrefs: [{titleString}]}}];
  265. testRender();
  266. assert.calledWith(node.setAttribute, "label", titleString);
  267. });
  268. });
  269. describe("restore defaults btn", () => {
  270. it("should call toggleRestoreDefaultsBtn", () => {
  271. testRender();
  272. assert.calledOnce(gHomePane.toggleRestoreDefaultsBtn);
  273. });
  274. });
  275. });
  276. });