AboutPreferences.test.js (12693B)
1 /* global Services */ 2 import { 3 AboutPreferences, 4 PREFERENCES_LOADED_EVENT, 5 } from "lib/AboutPreferences.sys.mjs"; 6 import { actionTypes as at, actionCreators as ac } from "common/Actions.mjs"; 7 import { GlobalOverrider } from "test/unit/utils"; 8 9 describe("AboutPreferences Feed", () => { 10 let globals; 11 let sandbox; 12 let Sections; 13 let DiscoveryStream; 14 let instance; 15 16 beforeEach(() => { 17 globals = new GlobalOverrider(); 18 sandbox = globals.sandbox; 19 Sections = []; 20 DiscoveryStream = { config: { enabled: false } }; 21 instance = new AboutPreferences(); 22 instance.store = { 23 dispatch: sandbox.stub(), 24 getState: () => ({ Sections, DiscoveryStream }), 25 }; 26 globals.set("NimbusFeatures", { 27 newtab: { getAllVariables: sandbox.stub() }, 28 }); 29 }); 30 afterEach(() => { 31 globals.restore(); 32 }); 33 34 describe("#onAction", () => { 35 it("should call .init() on an INIT action", () => { 36 const stub = sandbox.stub(instance, "init"); 37 38 instance.onAction({ type: at.INIT }); 39 40 assert.calledOnce(stub); 41 }); 42 it("should call .uninit() on an UNINIT action", () => { 43 const stub = sandbox.stub(instance, "uninit"); 44 45 instance.onAction({ type: at.UNINIT }); 46 47 assert.calledOnce(stub); 48 }); 49 it("should call .openPreferences on SETTINGS_OPEN", () => { 50 const action = { 51 type: at.SETTINGS_OPEN, 52 _target: { browser: { ownerGlobal: { openPreferences: sinon.spy() } } }, 53 }; 54 instance.onAction(action); 55 assert.calledOnce(action._target.browser.ownerGlobal.openPreferences); 56 }); 57 it("should call .BrowserAddonUI.openAddonsMgr with the extension id on OPEN_WEBEXT_SETTINGS", () => { 58 const action = { 59 type: at.OPEN_WEBEXT_SETTINGS, 60 data: "foo", 61 _target: { 62 browser: { 63 ownerGlobal: { 64 BrowserAddonUI: { openAddonsMgr: sinon.spy() }, 65 }, 66 }, 67 }, 68 }; 69 instance.onAction(action); 70 assert.calledWith( 71 action._target.browser.ownerGlobal.BrowserAddonUI.openAddonsMgr, 72 "addons://detail/foo" 73 ); 74 }); 75 }); 76 77 describe("#observe", () => { 78 let renderPreferenceSection; 79 let toggleRestoreDefaults; 80 81 beforeEach(() => { 82 // Stub out All The Things 83 renderPreferenceSection = sandbox.stub( 84 instance, 85 "renderPreferenceSection" 86 ); 87 toggleRestoreDefaults = sandbox.stub(instance, "toggleRestoreDefaults"); 88 }); 89 90 it("should watch for about:preferences loading", () => { 91 sandbox.stub(Services.obs, "addObserver"); 92 93 instance.init(); 94 95 assert.calledOnce(Services.obs.addObserver); 96 assert.calledWith( 97 Services.obs.addObserver, 98 instance, 99 PREFERENCES_LOADED_EVENT 100 ); 101 }); 102 it("should stop watching on uninit", () => { 103 sandbox.stub(Services.obs, "removeObserver"); 104 105 instance.uninit(); 106 107 assert.calledOnce(Services.obs.removeObserver); 108 assert.calledWith( 109 Services.obs.removeObserver, 110 instance, 111 PREFERENCES_LOADED_EVENT 112 ); 113 }); 114 it("should try to render on event", async () => { 115 Sections.push({ 116 rowsPref: "row_pref", 117 maxRows: 3, 118 pref: { descString: "foo" }, 119 learnMore: { link: "https://foo.com" }, 120 id: "topstories", 121 }); 122 123 Sections.push({ 124 rowsPref: "row_pref", 125 maxRows: 3, 126 pref: { descString: "foo" }, 127 learnMore: { link: "https://foo.com" }, 128 id: "highlights", 129 }); 130 131 await instance.observe(window, PREFERENCES_LOADED_EVENT); 132 133 // Render all the prefs 134 assert.callCount(renderPreferenceSection, 6); 135 136 // Show or hide the "Restore defaults" button depending on prefs 137 assert.calledOnce(toggleRestoreDefaults); 138 }); 139 }); 140 141 describe("#renderPreferenceSection", () => { 142 let node; 143 let Preferences; 144 let document; 145 146 beforeEach(() => { 147 node = { 148 appendChild: sandbox.stub().returnsArg(0), 149 addEventListener: sandbox.stub(), 150 classList: { add: sandbox.stub(), remove: sandbox.stub() }, 151 cloneNode: sandbox.stub().returnsThis(), 152 insertAdjacentElement: sandbox.stub().returnsArg(1), 153 setAttribute: sandbox.stub(), 154 remove: sandbox.stub(), 155 style: {}, 156 }; 157 document = { 158 createXULElement: sandbox.stub().returns(node), 159 l10n: { 160 setAttributes(el, id, args) { 161 el.setAttribute("data-l10n-id", id); 162 el.setAttribute("data-l10n-args", JSON.stringify(args)); 163 }, 164 }, 165 createProcessingInstruction: sandbox.stub(), 166 createElementNS: sandbox.stub().callsFake(() => node), 167 getElementById: sandbox.stub().returns(node), 168 insertBefore: sandbox.stub().returnsArg(0), 169 querySelector: sandbox.stub().returns({ appendChild: sandbox.stub() }), 170 }; 171 Preferences = { 172 add: sandbox.stub(), 173 get: sandbox.stub().returns({ 174 on: sandbox.stub(), 175 }), 176 }; 177 }); 178 179 describe("#linkPref", () => { 180 it("should add a pref to the global", () => { 181 const sectionData = { pref: { feed: "feed" } }; 182 instance.renderPreferenceSection(sectionData, document, Preferences); 183 184 assert.calledOnce(Preferences.add); 185 }); 186 187 it("should skip adding if not shown", () => { 188 const sectionData = { shouldHidePref: true }; 189 instance.renderPreferenceSection(sectionData, document, Preferences); 190 191 assert.notCalled(Preferences.add); 192 }); 193 }); 194 195 describe("title line", () => { 196 it("should render a title", () => { 197 const titleString = "the_title"; 198 const sectionData = { pref: { titleString } }; 199 instance.renderPreferenceSection(sectionData, document, Preferences); 200 201 assert.calledWith(node.setAttribute, "data-l10n-id", titleString); 202 }); 203 }); 204 205 describe("top stories", () => { 206 const href = "https://disclaimer/"; 207 const eventSource = "https://disclaimer/"; 208 let sectionData; 209 210 beforeEach(() => { 211 sectionData = { 212 id: "topstories", 213 pref: { feed: "feed", learnMore: { link: { href } } }, 214 eventSource, 215 }; 216 }); 217 218 it("should setup a user event for top stories eventSource", () => { 219 sinon.spy(instance, "setupUserEvent"); 220 instance.renderPreferenceSection(sectionData, document, Preferences); 221 222 assert.calledWith(node.addEventListener, "command"); 223 assert.calledWith(instance.setupUserEvent, node, eventSource); 224 }); 225 226 it("should setup a user event for top stories nested pref eventSource", () => { 227 sinon.spy(instance, "setupUserEvent"); 228 const section = { 229 id: "topstories", 230 pref: { 231 feed: "feed", 232 learnMore: { link: { href } }, 233 nestedPrefs: [ 234 { 235 name: "showSponsored", 236 titleString: 237 "home-prefs-recommended-by-option-sponsored-stories", 238 icon: "icon-info", 239 eventSource: "POCKET_SPOCS", 240 }, 241 ], 242 }, 243 }; 244 instance.renderPreferenceSection(section, document, Preferences); 245 246 assert.calledWith(node.addEventListener, "command"); 247 assert.calledWith(instance.setupUserEvent, node, "POCKET_SPOCS"); 248 }); 249 250 it("should fire store dispatch with onCommand", () => { 251 const element = { 252 addEventListener: (command, action) => { 253 // Trigger the action right away because we only care about testing the action here. 254 action({ target: { checked: true } }); 255 }, 256 }; 257 instance.setupUserEvent(element, eventSource); 258 assert.calledWith( 259 instance.store.dispatch, 260 ac.UserEvent({ 261 event: "PREF_CHANGED", 262 source: eventSource, 263 value: { menu_source: "ABOUT_PREFERENCES", status: true }, 264 }) 265 ); 266 }); 267 268 // The Weather pref now has a link to learn more, other prefs such as Top Stories don't any more 269 it("should add a link for weather", () => { 270 const section = { 271 id: "weather", 272 pref: { feed: "feed", learnMore: { link: { href } } }, 273 eventSource, 274 }; 275 276 instance.renderPreferenceSection(section, document, Preferences); 277 278 assert.calledWith(node.setAttribute, "href", href); 279 }); 280 }); 281 282 describe("description line", () => { 283 it("should render a description", () => { 284 const descString = "the_desc"; 285 const sectionData = { pref: { descString } }; 286 287 instance.renderPreferenceSection(sectionData, document, Preferences); 288 289 assert.calledWith(node.setAttribute, "data-l10n-id", descString); 290 }); 291 292 it("should render rows dropdown with appropriate number", () => { 293 const sectionData = { 294 rowsPref: "row_pref", 295 maxRows: 3, 296 pref: { descString: "foo" }, 297 }; 298 299 instance.renderPreferenceSection(sectionData, document, Preferences); 300 301 assert.calledWith(node.setAttribute, "value", 1); 302 assert.calledWith(node.setAttribute, "value", 2); 303 assert.calledWith(node.setAttribute, "value", 3); 304 }); 305 }); 306 describe("nested prefs", () => { 307 const titleString = "im_nested"; 308 let sectionData; 309 310 beforeEach(() => { 311 sectionData = { pref: { nestedPrefs: [{ titleString }] } }; 312 }); 313 314 it("should render a nested pref", () => { 315 instance.renderPreferenceSection(sectionData, document, Preferences); 316 317 assert.calledWith(node.setAttribute, "data-l10n-id", titleString); 318 }); 319 320 it("should set node hidden to true", () => { 321 sectionData.pref.nestedPrefs[0].hidden = true; 322 323 instance.renderPreferenceSection(sectionData, document, Preferences); 324 325 assert.isTrue(node.hidden); 326 }); 327 it("should add a change event", () => { 328 instance.renderPreferenceSection(sectionData, document, Preferences); 329 330 assert.calledOnce(Preferences.get().on); 331 assert.calledWith(Preferences.get().on, "change"); 332 }); 333 334 it("should default node disabled to false", async () => { 335 Preferences.get = sandbox.stub().returns({ 336 on: sandbox.stub(), 337 _value: true, 338 }); 339 340 instance.renderPreferenceSection(sectionData, document, Preferences); 341 342 assert.isFalse(node.disabled); 343 }); 344 it("should default node disabled to true", async () => { 345 instance.renderPreferenceSection(sectionData, document, Preferences); 346 347 assert.isTrue(node.disabled); 348 }); 349 it("should set node disabled to true", async () => { 350 const pref = { 351 on: sandbox.stub(), 352 _value: true, 353 }; 354 Preferences.get = sandbox.stub().returns(pref); 355 356 instance.renderPreferenceSection(sectionData, document, Preferences); 357 pref._value = !pref._value; 358 await Preferences.get().on.firstCall.args[1](); 359 360 assert.isTrue(node.disabled); 361 }); 362 it("should set node disabled to false", async () => { 363 const pref = { 364 on: sandbox.stub(), 365 _value: false, 366 }; 367 Preferences.get = sandbox.stub().returns(pref); 368 369 instance.renderPreferenceSection(sectionData, document, Preferences); 370 pref._value = !pref._value; 371 await Preferences.get().on.firstCall.args[1](); 372 373 assert.isFalse(node.disabled); 374 }); 375 }); 376 }); 377 378 describe("#toggleRestoreDefaults", () => { 379 it("should call toggleRestoreDefaultsBtn", async () => { 380 let gHomePane; 381 gHomePane = { toggleRestoreDefaultsBtn: sandbox.stub() }; 382 383 await instance.toggleRestoreDefaults(gHomePane); 384 385 assert.calledOnce(gHomePane.toggleRestoreDefaultsBtn); 386 }); 387 }); 388 389 describe("#getString", () => { 390 it("should not fail if titleString is not provided", () => { 391 const emptyPref = {}; 392 393 const returnString = instance.getString(emptyPref); 394 assert.equal(returnString, undefined); 395 }); 396 397 it("should return the string id if titleString is just a string", () => { 398 const titleString = "foo"; 399 400 const returnString = instance.getString(titleString); 401 assert.equal(returnString, titleString); 402 }); 403 404 it("should set id and args if titleString is an object with id and values", () => { 405 const titleString = { id: "foo", values: { provider: "bar" } }; 406 407 const returnString = instance.getString(titleString); 408 assert.equal(returnString, titleString.id); 409 }); 410 }); 411 });