ASRouterPreferences.test.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import {_ASRouterPreferences, ASRouterPreferences as ASRouterPreferencesSingleton, TEST_PROVIDERS} from "lib/ASRouterPreferences.jsm";
  2. const FAKE_PROVIDERS = [{id: "foo"}, {id: "bar"}];
  3. const PROVIDER_PREF_BRANCH = "browser.newtabpage.activity-stream.asrouter.providers.";
  4. const DEVTOOLS_PREF = "browser.newtabpage.activity-stream.asrouter.devtoolsEnabled";
  5. const SNIPPETS_USER_PREF = "browser.newtabpage.activity-stream.feeds.snippets";
  6. const CFR_USER_PREF_ADDONS = "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons";
  7. const CFR_USER_PREF_FEATURES = "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features";
  8. /** NUMBER_OF_PREFS_TO_OBSERVE includes:
  9. * 1. asrouter.providers. pref branch
  10. * 2. asrouter.devtoolsEnabled
  11. * 3. browser.newtabpage.activity-stream.feeds.snippets (user preference - snippets)
  12. * 4. browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons (user preference - cfr)
  13. * 4. browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features (user preference - cfr)
  14. * 5. services.sync.username
  15. */
  16. const NUMBER_OF_PREFS_TO_OBSERVE = 6;
  17. describe("ASRouterPreferences", () => {
  18. let ASRouterPreferences;
  19. let sandbox;
  20. let addObserverStub;
  21. let stringPrefStub;
  22. let boolPrefStub;
  23. beforeEach(() => {
  24. ASRouterPreferences = new _ASRouterPreferences();
  25. sandbox = sinon.createSandbox();
  26. addObserverStub = sandbox.stub(global.Services.prefs, "addObserver");
  27. stringPrefStub = sandbox.stub(global.Services.prefs, "getStringPref");
  28. FAKE_PROVIDERS.forEach(provider => {
  29. stringPrefStub.withArgs(`${PROVIDER_PREF_BRANCH}${provider.id}`).returns(JSON.stringify(provider));
  30. });
  31. sandbox.stub(global.Services.prefs, "getChildList")
  32. .withArgs(PROVIDER_PREF_BRANCH).returns(FAKE_PROVIDERS.map(provider => `${PROVIDER_PREF_BRANCH}${provider.id}`));
  33. boolPrefStub = sandbox.stub(global.Services.prefs, "getBoolPref").returns(false);
  34. });
  35. afterEach(() => {
  36. sandbox.restore();
  37. });
  38. function getPrefNameForProvider(providerId) {
  39. return `${PROVIDER_PREF_BRANCH}${providerId}`;
  40. }
  41. function setPrefForProvider(providerId, value) {
  42. stringPrefStub.withArgs(getPrefNameForProvider(providerId)).returns(JSON.stringify(value));
  43. }
  44. it("ASRouterPreferences should be an instance of _ASRouterPreferences", () => {
  45. assert.instanceOf(ASRouterPreferencesSingleton, _ASRouterPreferences);
  46. });
  47. describe("#init", () => {
  48. it("should set ._initialized to true", () => {
  49. ASRouterPreferences.init();
  50. assert.isTrue(ASRouterPreferences._initialized);
  51. });
  52. it(`should set ${NUMBER_OF_PREFS_TO_OBSERVE} observers and not re-initialize if already initialized`, () => {
  53. ASRouterPreferences.init();
  54. assert.callCount(addObserverStub, NUMBER_OF_PREFS_TO_OBSERVE);
  55. ASRouterPreferences.init();
  56. ASRouterPreferences.init();
  57. assert.callCount(addObserverStub, NUMBER_OF_PREFS_TO_OBSERVE);
  58. });
  59. });
  60. describe("#uninit", () => {
  61. it("should set ._initialized to false", () => {
  62. ASRouterPreferences.init();
  63. ASRouterPreferences.uninit();
  64. assert.isFalse(ASRouterPreferences._initialized);
  65. });
  66. it("should clear cached values for ._initialized, .devtoolsEnabled", () => {
  67. ASRouterPreferences.init();
  68. // trigger caching
  69. const result = [ASRouterPreferences.providers, ASRouterPreferences.devtoolsEnabled]; // eslint-disable-line no-unused-vars
  70. assert.isNotNull(ASRouterPreferences._providers, "providers should not be null");
  71. assert.isNotNull(ASRouterPreferences._devtoolsEnabled, "devtolosEnabled should not be null");
  72. ASRouterPreferences.uninit();
  73. assert.isNull(ASRouterPreferences._providers);
  74. assert.isNull(ASRouterPreferences._devtoolsEnabled);
  75. });
  76. it("should clear all listeners and remove observers (only once)", () => {
  77. const removeStub = sandbox.stub(global.Services.prefs, "removeObserver");
  78. ASRouterPreferences.init();
  79. ASRouterPreferences.addListener(() => {});
  80. ASRouterPreferences.addListener(() => {});
  81. assert.equal(ASRouterPreferences._callbacks.size, 2);
  82. ASRouterPreferences.uninit();
  83. // Tests to make sure we don't remove observers that weren't set
  84. ASRouterPreferences.uninit();
  85. assert.callCount(removeStub, NUMBER_OF_PREFS_TO_OBSERVE);
  86. assert.calledWith(removeStub, PROVIDER_PREF_BRANCH);
  87. assert.calledWith(removeStub, DEVTOOLS_PREF);
  88. assert.isEmpty(ASRouterPreferences._callbacks);
  89. });
  90. });
  91. describe(".providers", () => {
  92. it("should return the value the first time .providers is accessed", () => {
  93. ASRouterPreferences.init();
  94. const result = ASRouterPreferences.providers;
  95. assert.deepEqual(result, FAKE_PROVIDERS);
  96. // once per pref
  97. assert.calledTwice(stringPrefStub);
  98. });
  99. it("should return the cached value the second time .providers is accessed", () => {
  100. ASRouterPreferences.init();
  101. const [, secondCall] = [ASRouterPreferences.providers, ASRouterPreferences.providers];
  102. assert.deepEqual(secondCall, FAKE_PROVIDERS);
  103. // once per pref
  104. assert.calledTwice(stringPrefStub);
  105. });
  106. it("should just parse the pref each time if ASRouterPreferences hasn't been initialized yet", () => {
  107. // Intentionally not initialized
  108. const [firstCall, secondCall] = [ASRouterPreferences.providers, ASRouterPreferences.providers];
  109. assert.deepEqual(firstCall, FAKE_PROVIDERS);
  110. assert.deepEqual(secondCall, FAKE_PROVIDERS);
  111. assert.callCount(stringPrefStub, 4);
  112. });
  113. it("should skip the pref without throwing if a pref is not parsable", () => {
  114. stringPrefStub.withArgs(`${PROVIDER_PREF_BRANCH}foo`).returns("not json");
  115. ASRouterPreferences.init();
  116. assert.deepEqual(ASRouterPreferences.providers, [{id: "bar"}]);
  117. });
  118. it("should include TEST_PROVIDERS if devtools is turned on", () => {
  119. boolPrefStub.withArgs(DEVTOOLS_PREF).returns(true);
  120. ASRouterPreferences.init();
  121. assert.deepEqual(ASRouterPreferences.providers, [...TEST_PROVIDERS, ...FAKE_PROVIDERS]);
  122. });
  123. });
  124. describe(".devtoolsEnabled", () => {
  125. it("should read the pref the first time .devtoolsEnabled is accessed", () => {
  126. ASRouterPreferences.init();
  127. const result = ASRouterPreferences.devtoolsEnabled;
  128. assert.deepEqual(result, false);
  129. assert.calledOnce(boolPrefStub);
  130. });
  131. it("should return the cached value the second time .devtoolsEnabled is accessed", () => {
  132. ASRouterPreferences.init();
  133. const [, secondCall] = [ASRouterPreferences.devtoolsEnabled, ASRouterPreferences.devtoolsEnabled];
  134. assert.deepEqual(secondCall, false);
  135. assert.calledOnce(boolPrefStub);
  136. });
  137. it("should just parse the pref each time if ASRouterPreferences hasn't been initialized yet", () => {
  138. // Intentionally not initialized
  139. const [firstCall, secondCall] = [ASRouterPreferences.devtoolsEnabled, ASRouterPreferences.devtoolsEnabled];
  140. assert.deepEqual(firstCall, false);
  141. assert.deepEqual(secondCall, false);
  142. assert.calledTwice(boolPrefStub);
  143. });
  144. });
  145. describe("#getUserPreference(providerId)", () => {
  146. it("should return the user preference for snippets", () => {
  147. boolPrefStub.withArgs(SNIPPETS_USER_PREF).returns(true);
  148. assert.isTrue(ASRouterPreferences.getUserPreference("snippets"));
  149. });
  150. });
  151. describe("#getAllUserPreferences", () => {
  152. it("should return all user preferences", () => {
  153. boolPrefStub.withArgs(SNIPPETS_USER_PREF).returns(true);
  154. boolPrefStub.withArgs(CFR_USER_PREF_ADDONS).returns(false);
  155. boolPrefStub.withArgs(CFR_USER_PREF_FEATURES).returns(true);
  156. const result = ASRouterPreferences.getAllUserPreferences();
  157. assert.deepEqual(result, {snippets: true, cfrAddons: false, cfrFeatures: true});
  158. });
  159. });
  160. describe("#enableOrDisableProvider", () => {
  161. it("should enable an existing provider if second param is true", () => {
  162. const setStub = sandbox.stub(global.Services.prefs, "setStringPref");
  163. setPrefForProvider("foo", {id: "foo", enabled: false});
  164. assert.isFalse(ASRouterPreferences.providers[0].enabled);
  165. ASRouterPreferences.enableOrDisableProvider("foo", true);
  166. assert.calledWith(setStub, getPrefNameForProvider("foo"), JSON.stringify({id: "foo", enabled: true}));
  167. });
  168. it("should disable an existing provider if second param is false", () => {
  169. const setStub = sandbox.stub(global.Services.prefs, "setStringPref");
  170. setPrefForProvider("foo", {id: "foo", enabled: true});
  171. assert.isTrue(ASRouterPreferences.providers[0].enabled);
  172. ASRouterPreferences.enableOrDisableProvider("foo", false);
  173. assert.calledWith(setStub, getPrefNameForProvider("foo"), JSON.stringify({id: "foo", enabled: false}));
  174. });
  175. it("should not throw if the id does not exist", () => {
  176. assert.doesNotThrow(() => {
  177. ASRouterPreferences.enableOrDisableProvider("does_not_exist", true);
  178. });
  179. });
  180. it("should not throw if pref is not parseable", () => {
  181. stringPrefStub.withArgs(getPrefNameForProvider("foo")).returns("not valid");
  182. assert.doesNotThrow(() => {
  183. ASRouterPreferences.enableOrDisableProvider("foo", true);
  184. });
  185. });
  186. });
  187. describe("#setUserPreference", () => {
  188. it("should do nothing if the pref doesn't exist", () => {
  189. ASRouterPreferences.setUserPreference("foo", true);
  190. assert.notCalled(boolPrefStub);
  191. });
  192. it("should set the given pref", () => {
  193. const setStub = sandbox.stub(global.Services.prefs, "setBoolPref");
  194. ASRouterPreferences.setUserPreference("snippets", true);
  195. assert.calledWith(setStub, SNIPPETS_USER_PREF, true);
  196. });
  197. });
  198. describe("#resetProviderPref", () => {
  199. it("should reset the pref and user prefs", () => {
  200. const resetStub = sandbox.stub(global.Services.prefs, "clearUserPref");
  201. ASRouterPreferences.resetProviderPref();
  202. FAKE_PROVIDERS.forEach(provider => {
  203. assert.calledWith(resetStub, getPrefNameForProvider(provider.id));
  204. });
  205. assert.calledWith(resetStub, SNIPPETS_USER_PREF);
  206. assert.calledWith(resetStub, CFR_USER_PREF_ADDONS);
  207. assert.calledWith(resetStub, CFR_USER_PREF_FEATURES);
  208. });
  209. });
  210. describe("observer, listeners", () => {
  211. it("should invalidate .providers when the pref is changed", () => {
  212. const testProvider = {id: "newstuff"};
  213. const newProviders = [...FAKE_PROVIDERS, testProvider];
  214. ASRouterPreferences.init();
  215. assert.deepEqual(ASRouterPreferences.providers, FAKE_PROVIDERS);
  216. stringPrefStub.withArgs(getPrefNameForProvider(testProvider.id)).returns(JSON.stringify(testProvider));
  217. global.Services.prefs.getChildList
  218. .withArgs(PROVIDER_PREF_BRANCH).returns(newProviders.map(provider => getPrefNameForProvider(provider.id)));
  219. ASRouterPreferences.observe(null, null, getPrefNameForProvider(testProvider.id));
  220. // Cache should be invalidated so we access the new value of the pref now
  221. assert.deepEqual(ASRouterPreferences.providers, newProviders);
  222. });
  223. it("should invalidate .devtoolsEnabled and .providers when the pref is changed", () => {
  224. ASRouterPreferences.init();
  225. assert.isFalse(ASRouterPreferences.devtoolsEnabled);
  226. boolPrefStub.withArgs(DEVTOOLS_PREF).returns(true);
  227. global.Services.prefs.getChildList
  228. .withArgs(PROVIDER_PREF_BRANCH).returns([]);
  229. ASRouterPreferences.observe(null, null, DEVTOOLS_PREF);
  230. // Cache should be invalidated so we access the new value of the pref now
  231. // Note that providers needs to be invalidated because devtools adds test content to it.
  232. assert.isTrue(ASRouterPreferences.devtoolsEnabled);
  233. assert.deepEqual(ASRouterPreferences.providers, TEST_PROVIDERS);
  234. });
  235. it("should call listeners added with .addListener", () => {
  236. const callback1 = sinon.stub();
  237. const callback2 = sinon.stub();
  238. ASRouterPreferences.init();
  239. ASRouterPreferences.addListener(callback1);
  240. ASRouterPreferences.addListener(callback2);
  241. ASRouterPreferences.observe(null, null, getPrefNameForProvider("foo"));
  242. assert.calledWith(callback1, getPrefNameForProvider("foo"));
  243. ASRouterPreferences.observe(null, null, DEVTOOLS_PREF);
  244. assert.calledWith(callback2, DEVTOOLS_PREF);
  245. });
  246. it("should not call listeners after they are removed with .removeListeners", () => {
  247. const callback = sinon.stub();
  248. ASRouterPreferences.init();
  249. ASRouterPreferences.addListener(callback);
  250. ASRouterPreferences.observe(null, null, getPrefNameForProvider("foo"));
  251. assert.calledWith(callback, getPrefNameForProvider("foo"));
  252. callback.reset();
  253. ASRouterPreferences.removeListener(callback);
  254. ASRouterPreferences.observe(null, null, DEVTOOLS_PREF);
  255. assert.notCalled(callback);
  256. });
  257. });
  258. });