test_PlacesFeed.js (41164B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 ChromeUtils.defineESModuleGetters(this, { 7 actionCreators: "resource://newtab/common/Actions.mjs", 8 actionTypes: "resource://newtab/common/Actions.mjs", 9 AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs", 10 NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs", 11 PartnerLinkAttribution: "resource:///modules/PartnerLinkAttribution.sys.mjs", 12 PlacesFeed: "resource://newtab/lib/PlacesFeed.sys.mjs", 13 PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", 14 PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", 15 SearchService: "resource://gre/modules/SearchService.sys.mjs", 16 sinon: "resource://testing-common/Sinon.sys.mjs", 17 TestUtils: "resource://testing-common/TestUtils.sys.mjs", 18 }); 19 20 const FAKE_BOOKMARK = { 21 bookmarkGuid: "D3r1sKRobtbW", 22 bookmarkTitle: "Foo", 23 dateAdded: 123214232, 24 url: "foo.com", 25 }; 26 const TYPE_BOOKMARK = 1; // This is fake, for testing 27 const SOURCES = { 28 DEFAULT: 0, 29 SYNC: 1, 30 IMPORT: 2, 31 RESTORE: 5, 32 RESTORE_ON_STARTUP: 6, 33 }; 34 35 // The event dispatched in NewTabUtils when a link is blocked; 36 const BLOCKED_EVENT = "newtab-linkBlocked"; 37 38 const TOP_SITES_BLOCKED_SPONSORS_PREF = "browser.topsites.blockedSponsors"; 39 40 function getPlacesFeedForTest(sandbox) { 41 let feed = new PlacesFeed(); 42 feed.store = { 43 dispatch: sandbox.spy(), 44 feeds: { 45 get: sandbox.stub(), 46 }, 47 }; 48 49 sandbox.stub(AboutNewTab, "activityStream").value({ 50 store: feed.store, 51 }); 52 53 return feed; 54 } 55 56 add_task(async function test_construction() { 57 info("PlacesFeed construction should work"); 58 let feed = new PlacesFeed(); 59 Assert.ok(feed, "PlacesFeed could be constructed."); 60 }); 61 62 add_task(async function test_PlacesObserver() { 63 info("PlacesFeed should have a PlacesObserver that dispatches to the store"); 64 let sandbox = sinon.createSandbox(); 65 let feed = getPlacesFeedForTest(sandbox); 66 67 let action = { type: "FOO" }; 68 feed.placesObserver.dispatch(action); 69 70 await TestUtils.waitForTick(); 71 Assert.ok(feed.store.dispatch.calledOnce, "PlacesFeed.store dispatch called"); 72 Assert.equal(feed.store.dispatch.firstCall.args[0].type, action.type); 73 74 sandbox.restore(); 75 }); 76 77 add_task(async function test_addToBlockedTopSitesSponsors_add_to_blocklist() { 78 info( 79 "PlacesFeed.addToBlockedTopSitesSponsors should add the blocked sponsors " + 80 "to the blocklist" 81 ); 82 let sandbox = sinon.createSandbox(); 83 let feed = getPlacesFeedForTest(sandbox); 84 Services.prefs.setStringPref( 85 TOP_SITES_BLOCKED_SPONSORS_PREF, 86 `["foo","bar"]` 87 ); 88 89 feed.addToBlockedTopSitesSponsors([ 90 { url: "test.com" }, 91 { url: "test1.com" }, 92 ]); 93 94 let blockedSponsors = JSON.parse( 95 Services.prefs.getStringPref(TOP_SITES_BLOCKED_SPONSORS_PREF) 96 ); 97 Assert.deepEqual( 98 new Set(["foo", "bar", "test", "test1"]), 99 new Set(blockedSponsors) 100 ); 101 102 Services.prefs.clearUserPref(TOP_SITES_BLOCKED_SPONSORS_PREF); 103 sandbox.restore(); 104 }); 105 106 add_task(async function test_addToBlockedTopSitesSponsors_no_dupes() { 107 info( 108 "PlacesFeed.addToBlockedTopSitesSponsors should not add duplicate " + 109 "sponsors to the blocklist" 110 ); 111 let sandbox = sinon.createSandbox(); 112 let feed = getPlacesFeedForTest(sandbox); 113 Services.prefs.setStringPref( 114 TOP_SITES_BLOCKED_SPONSORS_PREF, 115 `["foo","bar"]` 116 ); 117 118 feed.addToBlockedTopSitesSponsors([ 119 { url: "foo.com" }, 120 { url: "bar.com" }, 121 { url: "test.com" }, 122 ]); 123 124 let blockedSponsors = JSON.parse( 125 Services.prefs.getStringPref(TOP_SITES_BLOCKED_SPONSORS_PREF) 126 ); 127 Assert.deepEqual(new Set(["foo", "bar", "test"]), new Set(blockedSponsors)); 128 129 Services.prefs.clearUserPref(TOP_SITES_BLOCKED_SPONSORS_PREF); 130 sandbox.restore(); 131 }); 132 133 add_task(async function test_onAction_PlacesEvents() { 134 info( 135 "PlacesFeed.onAction should add bookmark, history, places, blocked " + 136 "observers on INIT" 137 ); 138 let sandbox = sinon.createSandbox(); 139 let feed = getPlacesFeedForTest(sandbox); 140 sandbox.stub(feed.placesObserver, "handlePlacesEvent"); 141 142 feed.onAction({ type: actionTypes.INIT }); 143 // The PlacesObserver registration happens at the next tick of the 144 // event loop. 145 await TestUtils.waitForTick(); 146 147 // These are some dummy PlacesEvents that we'll pass through the 148 // PlacesObserver service, checking that the handlePlacesEvent receives them 149 // properly. 150 let notifications = [ 151 new PlacesBookmarkAddition({ 152 dateAdded: 0, 153 guid: "dQFSYrbM5SJN", 154 id: -1, 155 index: 0, 156 isTagging: false, 157 itemType: 1, 158 parentGuid: "n_HOEFys1qsL", 159 parentId: -2, 160 source: 0, 161 title: "test-123", 162 tags: "tags", 163 url: "http://example.com/test-123", 164 frecency: 0, 165 hidden: false, 166 visitCount: 0, 167 lastVisitDate: 0, 168 targetFolderGuid: null, 169 targetFolderItemId: -1, 170 targetFolderTitle: null, 171 }), 172 new PlacesBookmarkRemoved({ 173 id: -1, 174 url: "http://example.com/test-123", 175 title: "test-123", 176 itemType: 1, 177 parentId: -2, 178 index: 0, 179 guid: "M3WYgJlm2Jlx", 180 parentGuid: "DO1f97R4KC3Y", 181 source: 0, 182 isTagging: false, 183 isDescendantRemoval: false, 184 }), 185 new PlacesHistoryCleared(), 186 new PlacesVisitRemoved({ 187 url: "http://example.com/test-123", 188 pageGuid: "sPVcW2V4H7Rg", 189 reason: PlacesVisitRemoved.REASON_DELETED, 190 transitionType: 0, 191 isRemovedFromStore: true, 192 isPartialVisistsRemoval: false, 193 }), 194 ]; 195 196 for (let notification of notifications) { 197 PlacesUtils.observers.notifyListeners([notification]); 198 Assert.ok( 199 feed.placesObserver.handlePlacesEvent.calledOnce, 200 "PlacesFeed.handlePlacesEvent called" 201 ); 202 Assert.ok(feed.placesObserver.handlePlacesEvent.calledWith([notification])); 203 feed.placesObserver.handlePlacesEvent.resetHistory(); 204 } 205 206 info( 207 "PlacesFeed.onAction remove bookmark, history, places, blocked " + 208 "observers, and timers on UNINIT" 209 ); 210 211 let placesChangedTimerCancel = sandbox.spy(); 212 feed.placesChangedTimer = { 213 cancel: placesChangedTimerCancel, 214 }; 215 216 // Unlike INIT, UNINIT removes the observers synchronously, so no need to 217 // wait for the event loop to tick around again. 218 feed.onAction({ type: actionTypes.UNINIT }); 219 220 for (let notification of notifications) { 221 PlacesUtils.observers.notifyListeners([notification]); 222 Assert.ok( 223 feed.placesObserver.handlePlacesEvent.notCalled, 224 "PlacesFeed.handlePlacesEvent not called" 225 ); 226 feed.placesObserver.handlePlacesEvent.resetHistory(); 227 } 228 229 Assert.equal(feed.placesChangedTimer, null); 230 Assert.ok(placesChangedTimerCancel.calledOnce); 231 232 sandbox.restore(); 233 }); 234 235 add_task(async function test_onAction_BLOCK_URL() { 236 info("PlacesFeed.onAction should block a url on BLOCK_URL"); 237 let sandbox = sinon.createSandbox(); 238 let feed = getPlacesFeedForTest(sandbox); 239 sandbox.stub(NewTabUtils.activityStreamLinks, "blockURL"); 240 241 feed.onAction({ 242 type: actionTypes.BLOCK_URL, 243 data: [{ url: "apple.com", pocket_id: 1234 }], 244 }); 245 Assert.ok( 246 NewTabUtils.activityStreamLinks.blockURL.calledWith({ 247 url: "apple.com", 248 pocket_id: 1234, 249 }) 250 ); 251 252 sandbox.restore(); 253 }); 254 255 add_task(async function test_onAction_BLOCK_URL_topsites_sponsors() { 256 info( 257 "PlacesFeed.onAction BLOCK_URL should update the blocked top " + 258 "sites sponsors" 259 ); 260 let sandbox = sinon.createSandbox(); 261 let feed = getPlacesFeedForTest(sandbox); 262 sandbox.stub(feed, "addToBlockedTopSitesSponsors"); 263 264 feed.onAction({ 265 type: actionTypes.BLOCK_URL, 266 data: [{ url: "foo.com", pocket_id: 1234, isSponsoredTopSite: 1 }], 267 }); 268 Assert.ok(feed.addToBlockedTopSitesSponsors.calledWith([{ url: "foo.com" }])); 269 270 sandbox.restore(); 271 }); 272 273 add_task(async function test_onAction_BOOKMARK_URL() { 274 info("PlacesFeed.onAction should bookmark a url on BOOKMARK_URL"); 275 let sandbox = sinon.createSandbox(); 276 let feed = getPlacesFeedForTest(sandbox); 277 sandbox.stub(NewTabUtils.activityStreamLinks, "addBookmark"); 278 279 let data = { url: "pear.com", title: "A pear" }; 280 let _target = { browser: { ownerGlobal() {} } }; 281 feed.onAction({ type: actionTypes.BOOKMARK_URL, data, _target }); 282 Assert.ok( 283 NewTabUtils.activityStreamLinks.addBookmark.calledWith( 284 data, 285 _target.browser.ownerGlobal 286 ) 287 ); 288 289 sandbox.restore(); 290 }); 291 292 add_task(async function test_onAction_DELETE_BOOKMARK_BY_ID() { 293 info("PlacesFeed.onAction should delete a bookmark on DELETE_BOOKMARK_BY_ID"); 294 let sandbox = sinon.createSandbox(); 295 let feed = getPlacesFeedForTest(sandbox); 296 sandbox.stub(NewTabUtils.activityStreamLinks, "deleteBookmark"); 297 298 feed.onAction({ type: actionTypes.DELETE_BOOKMARK_BY_ID, data: "g123kd" }); 299 Assert.ok( 300 NewTabUtils.activityStreamLinks.deleteBookmark.calledWith("g123kd") 301 ); 302 303 sandbox.restore(); 304 }); 305 306 add_task(async function test_onAction_DELETE_HISTORY_URL() { 307 info( 308 "PlacesFeed.onAction should delete a history entry on DELETE_HISTORY_URL" 309 ); 310 let sandbox = sinon.createSandbox(); 311 let feed = getPlacesFeedForTest(sandbox); 312 sandbox.stub(NewTabUtils.activityStreamLinks, "deleteHistoryEntry"); 313 sandbox.stub(NewTabUtils.activityStreamLinks, "blockURL"); 314 315 feed.onAction({ 316 type: actionTypes.DELETE_HISTORY_URL, 317 data: { url: "guava.com", forceBlock: null }, 318 }); 319 Assert.ok( 320 NewTabUtils.activityStreamLinks.deleteHistoryEntry.calledWith("guava.com") 321 ); 322 Assert.ok(NewTabUtils.activityStreamLinks.blockURL.notCalled); 323 324 sandbox.restore(); 325 }); 326 327 add_task(async function test_onAction_DELETE_HISTORY_URL_and_block() { 328 info( 329 "PlacesFeed.onAction should delete a history entry on " + 330 "DELETE_HISTORY_URL and force a site to be blocked if specified" 331 ); 332 let sandbox = sinon.createSandbox(); 333 let feed = getPlacesFeedForTest(sandbox); 334 sandbox.stub(NewTabUtils.activityStreamLinks, "deleteHistoryEntry"); 335 sandbox.stub(NewTabUtils.activityStreamLinks, "blockURL"); 336 337 feed.onAction({ 338 type: actionTypes.DELETE_HISTORY_URL, 339 data: { url: "guava.com", forceBlock: "g123kd" }, 340 }); 341 Assert.ok( 342 NewTabUtils.activityStreamLinks.deleteHistoryEntry.calledWith("guava.com") 343 ); 344 Assert.ok( 345 NewTabUtils.activityStreamLinks.blockURL.calledWith({ 346 url: "guava.com", 347 pocket_id: undefined, 348 }) 349 ); 350 351 sandbox.restore(); 352 }); 353 354 add_task(async function test_onAction_OPEN_NEW_WINDOW() { 355 info( 356 "PlacesFeed.onAction should call openTrustedLinkIn with the " + 357 "correct url, where and params on OPEN_NEW_WINDOW" 358 ); 359 let sandbox = sinon.createSandbox(); 360 let feed = getPlacesFeedForTest(sandbox); 361 let openTrustedLinkIn = sandbox.stub(); 362 let openWindowAction = { 363 type: actionTypes.OPEN_NEW_WINDOW, 364 data: { url: "https://foo.com" }, 365 _target: { browser: { ownerGlobal: { openTrustedLinkIn } } }, 366 }; 367 368 feed.onAction(openWindowAction); 369 370 Assert.ok(openTrustedLinkIn.calledOnce, "openTrustedLinkIn called"); 371 let [url, where, params] = openTrustedLinkIn.firstCall.args; 372 Assert.equal(url, "https://foo.com"); 373 Assert.equal(where, "window"); 374 Assert.ok(!params.private); 375 Assert.ok(!params.forceForeground); 376 377 sandbox.restore(); 378 }); 379 380 add_task(async function test_onAction_OPEN_PRIVATE_WINDOW() { 381 info( 382 "PlacesFeed.onAction should call openTrustedLinkIn with the " + 383 "correct url, where, params and privacy args on OPEN_PRIVATE_WINDOW" 384 ); 385 let sandbox = sinon.createSandbox(); 386 let feed = getPlacesFeedForTest(sandbox); 387 let openTrustedLinkIn = sandbox.stub(); 388 let openWindowAction = { 389 type: actionTypes.OPEN_PRIVATE_WINDOW, 390 data: { url: "https://foo.com" }, 391 _target: { browser: { ownerGlobal: { openTrustedLinkIn } } }, 392 }; 393 394 feed.onAction(openWindowAction); 395 396 Assert.ok(openTrustedLinkIn.calledOnce, "openTrustedLinkIn called"); 397 let [url, where, params] = openTrustedLinkIn.firstCall.args; 398 Assert.equal(url, "https://foo.com"); 399 Assert.equal(where, "window"); 400 Assert.ok(params.private); 401 Assert.ok(!params.forceForeground); 402 403 sandbox.restore(); 404 }); 405 406 add_task(async function test_onAction_OPEN_LINK() { 407 info( 408 "PlacesFeed.onAction should call openTrustedLinkIn with the " + 409 "correct url, where and params on OPEN_LINK" 410 ); 411 let sandbox = sinon.createSandbox(); 412 let feed = getPlacesFeedForTest(sandbox); 413 let openTrustedLinkIn = sandbox.stub(); 414 let openLinkAction = { 415 type: actionTypes.OPEN_LINK, 416 data: { url: "https://foo.com" }, 417 _target: { 418 browser: { 419 ownerGlobal: { openTrustedLinkIn }, 420 }, 421 }, 422 }; 423 feed.onAction(openLinkAction); 424 425 Assert.ok(openTrustedLinkIn.calledOnce, "openTrustedLinkIn called"); 426 let [url, where, params] = openTrustedLinkIn.firstCall.args; 427 Assert.equal(url, "https://foo.com"); 428 Assert.equal(where, "current"); 429 Assert.ok(!params.private); 430 Assert.ok(!params.forceForeground); 431 432 sandbox.restore(); 433 }); 434 435 add_task(async function test_onAction_OPEN_LINK_referrer() { 436 info("PlacesFeed.onAction should open link with referrer on OPEN_LINK"); 437 let sandbox = sinon.createSandbox(); 438 let feed = getPlacesFeedForTest(sandbox); 439 let openTrustedLinkIn = sandbox.stub(); 440 let openLinkAction = { 441 type: actionTypes.OPEN_LINK, 442 data: { url: "https://foo.com", referrer: "https://foo.com/ref" }, 443 _target: { 444 browser: { 445 ownerGlobal: { openTrustedLinkIn, whereToOpenLink: () => "tab" }, 446 }, 447 }, 448 }; 449 feed.onAction(openLinkAction); 450 451 Assert.ok(openTrustedLinkIn.calledOnce, "openTrustedLinkIn called"); 452 let [, , params] = openTrustedLinkIn.firstCall.args; 453 Assert.equal(params.referrerInfo.referrerPolicy, 5); 454 Assert.equal( 455 params.referrerInfo.originalReferrer.spec, 456 "https://foo.com/ref" 457 ); 458 459 sandbox.restore(); 460 }); 461 462 add_task(async function test_onAction_OPEN_LINK_typed_bonus() { 463 info( 464 "PlacesFeed.onAction should mark link with typed bonus as " + 465 "typed before opening OPEN_LINK" 466 ); 467 let sandbox = sinon.createSandbox(); 468 let callOrder = []; 469 // We can't stub out PlacesUtils.history.markPageAsTyped, since that's an 470 // XPCOM component. We'll stub out history instead. 471 sandbox.stub(PlacesUtils, "history").get(() => { 472 return { 473 markPageAsTyped: sandbox.stub().callsFake(() => { 474 callOrder.push("markPageAsTyped"); 475 }), 476 }; 477 }); 478 479 let feed = getPlacesFeedForTest(sandbox); 480 let openTrustedLinkIn = sandbox.stub().callsFake(() => { 481 callOrder.push("openTrustedLinkIn"); 482 }); 483 let openLinkAction = { 484 type: actionTypes.OPEN_LINK, 485 data: { 486 typedBonus: true, 487 url: "https://foo.com", 488 }, 489 _target: { 490 browser: { 491 ownerGlobal: { openTrustedLinkIn, whereToOpenLink: () => "tab" }, 492 }, 493 }, 494 }; 495 feed.onAction(openLinkAction); 496 497 Assert.deepEqual(callOrder, ["markPageAsTyped", "openTrustedLinkIn"]); 498 499 sandbox.restore(); 500 }); 501 502 add_task(async function test_onAction_OPEN_LINK_pocket() { 503 info( 504 "PlacesFeed.onAction should open the pocket link if it's a " + 505 "pocket story on OPEN_LINK" 506 ); 507 let sandbox = sinon.createSandbox(); 508 let feed = getPlacesFeedForTest(sandbox); 509 let openTrustedLinkIn = sandbox.stub(); 510 let openLinkAction = { 511 type: actionTypes.OPEN_LINK, 512 data: { 513 url: "https://foo.com", 514 open_url: "https://getpocket.com/foo", 515 type: "pocket", 516 }, 517 _target: { 518 browser: { 519 ownerGlobal: { openTrustedLinkIn }, 520 }, 521 }, 522 }; 523 524 feed.onAction(openLinkAction); 525 526 Assert.ok(openTrustedLinkIn.calledOnce, "openTrustedLinkIn called"); 527 let [url, where, params] = openTrustedLinkIn.firstCall.args; 528 Assert.equal(url, "https://getpocket.com/foo"); 529 Assert.equal(where, "current"); 530 Assert.ok(!params.private); 531 Assert.ok(!params.forceForeground); 532 533 sandbox.restore(); 534 }); 535 536 add_task(async function test_onAction_OPEN_LINK_not_http() { 537 info("PlacesFeed.onAction should not open link if not http"); 538 let sandbox = sinon.createSandbox(); 539 let feed = getPlacesFeedForTest(sandbox); 540 let openTrustedLinkIn = sandbox.stub(); 541 let openLinkAction = { 542 type: actionTypes.OPEN_LINK, 543 data: { url: "file:///foo.com" }, 544 _target: { 545 browser: { 546 ownerGlobal: { openTrustedLinkIn }, 547 }, 548 }, 549 }; 550 551 feed.onAction(openLinkAction); 552 553 Assert.ok(openTrustedLinkIn.notCalled, "openTrustedLinkIn not called"); 554 555 sandbox.restore(); 556 }); 557 558 add_task(async function test_onAction_FILL_SEARCH_TERM() { 559 info( 560 "PlacesFeed.onAction should call fillSearchTopSiteTerm " + 561 "on FILL_SEARCH_TERM" 562 ); 563 let sandbox = sinon.createSandbox(); 564 let feed = getPlacesFeedForTest(sandbox); 565 sandbox.stub(feed, "fillSearchTopSiteTerm"); 566 567 feed.onAction({ type: actionTypes.FILL_SEARCH_TERM }); 568 569 Assert.ok( 570 feed.fillSearchTopSiteTerm.calledOnce, 571 "PlacesFeed.fillSearchTopSiteTerm called" 572 ); 573 sandbox.restore(); 574 }); 575 576 add_task(async function test_onAction_ABOUT_SPONSORED_TOP_SITES() { 577 info( 578 "PlacesFeed.onAction should call openTrustedLinkIn with the " + 579 "correct SUMO url on ABOUT_SPONSORED_TOP_SITES" 580 ); 581 let sandbox = sinon.createSandbox(); 582 let feed = getPlacesFeedForTest(sandbox); 583 let openTrustedLinkIn = sandbox.stub(); 584 let openLinkAction = { 585 type: actionTypes.ABOUT_SPONSORED_TOP_SITES, 586 _target: { 587 browser: { 588 ownerGlobal: { openTrustedLinkIn }, 589 }, 590 }, 591 }; 592 593 feed.onAction(openLinkAction); 594 595 Assert.ok(openTrustedLinkIn.calledOnce, "openTrustedLinkIn called"); 596 let [url, where] = openTrustedLinkIn.firstCall.args; 597 Assert.ok(url.endsWith("sponsor-privacy")); 598 Assert.equal(where, "tab"); 599 600 sandbox.restore(); 601 }); 602 603 add_task(async function test_onAction_FILL_SEARCH_TERM() { 604 info( 605 "PlacesFeed.onAction should set the URL bar value to the label value " + 606 "on FILL_SEARCH_TERM" 607 ); 608 let sandbox = sinon.createSandbox(); 609 sandbox.stub(SearchService.prototype, "getEngineByAlias").resolves(null); 610 611 let feed = getPlacesFeedForTest(sandbox); 612 let locationBar = { search: sandbox.stub() }; 613 let action = { 614 type: actionTypes.FILL_SEARCH_TERM, 615 data: { label: "@Foo" }, 616 _target: { browser: { ownerGlobal: { gURLBar: locationBar } } }, 617 }; 618 619 await feed.onAction(action); 620 621 Assert.ok(locationBar.search.calledOnce, "gURLBar.search called"); 622 Assert.ok( 623 locationBar.search.calledWithExactly("@Foo", { 624 searchEngine: null, 625 searchModeEntry: "topsites_newtab", 626 }) 627 ); 628 629 sandbox.restore(); 630 }); 631 632 add_task(async function test_onAction_HANDOFF_SEARCH_TO_AWESOMEBAR() { 633 info( 634 "PlacesFeed.onAction should call handoffSearchToAwesomebar " + 635 "on HANDOFF_SEARCH_TO_AWESOMEBAR" 636 ); 637 let sandbox = sinon.createSandbox(); 638 639 let feed = getPlacesFeedForTest(sandbox); 640 sandbox.stub(feed, "handoffSearchToAwesomebar"); 641 642 let action = { 643 type: actionTypes.HANDOFF_SEARCH_TO_AWESOMEBAR, 644 data: { text: "f" }, 645 meta: { fromTarget: {} }, 646 _target: { browser: { ownerGlobal: { gURLBar: { focus: () => {} } } } }, 647 }; 648 649 await feed.onAction(action); 650 651 Assert.ok( 652 feed.handoffSearchToAwesomebar.calledOnce, 653 "PlacesFeed.handoffSearchToAwesomebar called" 654 ); 655 Assert.ok(feed.handoffSearchToAwesomebar.calledWithExactly(action)); 656 657 sandbox.restore(); 658 }); 659 660 add_task(async function test_onAction_PARTNER_LINK_ATTRIBUTION() { 661 info( 662 "PlacesFeed.onAction should call makeAttributionRequest on " + 663 "PARTNER_LINK_ATTRIBUTION" 664 ); 665 let sandbox = sinon.createSandbox(); 666 667 let feed = getPlacesFeedForTest(sandbox); 668 sandbox.stub(feed, "makeAttributionRequest"); 669 670 let data = { targetURL: "https://partnersite.com", source: "topsites" }; 671 feed.onAction({ 672 type: actionTypes.PARTNER_LINK_ATTRIBUTION, 673 data, 674 }); 675 676 Assert.ok( 677 feed.makeAttributionRequest.calledOnce, 678 "PlacesFeed.makeAttributionRequest called" 679 ); 680 Assert.ok(feed.makeAttributionRequest.calledWithExactly(data)); 681 682 sandbox.restore(); 683 }); 684 685 add_task( 686 async function test_makeAttributionRequest_PartnerLinkAttribution_makeReq() { 687 info( 688 "PlacesFeed.makeAttributionRequest should call " + 689 "PartnerLinkAttribution.makeRequest" 690 ); 691 let sandbox = sinon.createSandbox(); 692 693 let feed = getPlacesFeedForTest(sandbox); 694 sandbox.stub(PartnerLinkAttribution, "makeRequest"); 695 696 let data = { targetURL: "https://partnersite.com", source: "topsites" }; 697 feed.makeAttributionRequest(data); 698 699 Assert.ok( 700 PartnerLinkAttribution.makeRequest.calledOnce, 701 "PartnerLinkAttribution.makeRequest called" 702 ); 703 704 sandbox.restore(); 705 } 706 ); 707 708 function createFakeURLBar(sandbox) { 709 let fakeURLBar = { 710 focus: sandbox.spy(), 711 handoff: sandbox.spy(), 712 setHiddenFocus: sandbox.spy(), 713 removeHiddenFocus: sandbox.spy(), 714 addEventListener: (ev, cb) => { 715 fakeURLBar.listeners[ev] = cb; 716 }, 717 removeEventListener: sandbox.spy(), 718 719 listeners: [], 720 }; 721 722 return fakeURLBar; 723 } 724 725 add_task(async function test_handoffSearchToAwesomebar_no_text() { 726 info( 727 "PlacesFeed.handoffSearchToAwesomebar should properly handle handoff " + 728 "with no text passed in" 729 ); 730 731 let sandbox = sinon.createSandbox(); 732 733 let feed = getPlacesFeedForTest(sandbox); 734 let fakeURLBar = createFakeURLBar(sandbox); 735 736 sandbox.stub(PrivateBrowsingUtils, "isBrowserPrivate").returns(false); 737 sandbox.stub(feed, "_getDefaultSearchEngine").returns(null); 738 739 feed.handoffSearchToAwesomebar({ 740 _target: { browser: { ownerGlobal: { gURLBar: fakeURLBar } } }, 741 data: {}, 742 meta: { fromTarget: {} }, 743 }); 744 745 Assert.ok( 746 fakeURLBar.setHiddenFocus.calledOnce, 747 "gURLBar.setHiddenFocus called" 748 ); 749 Assert.ok(fakeURLBar.handoff.notCalled, "gURLBar.handoff not called"); 750 Assert.ok( 751 feed.store.dispatch.notCalled, 752 "PlacesFeed.store.dispatch not called" 753 ); 754 755 // Now type a character. 756 fakeURLBar.listeners.keydown({ key: "f" }); 757 Assert.ok(fakeURLBar.handoff.calledOnce, "gURLBar.handoff called"); 758 Assert.ok( 759 fakeURLBar.removeHiddenFocus.calledOnce, 760 "gURLBar.removeHiddenFocus called" 761 ); 762 Assert.ok(feed.store.dispatch.calledOnce, "PlacesFeed.store.dispatch called"); 763 Assert.ok( 764 feed.store.dispatch.calledWith({ 765 meta: { 766 from: "ActivityStream:Main", 767 skipMain: true, 768 to: "ActivityStream:Content", 769 toTarget: {}, 770 }, 771 type: "DISABLE_SEARCH", 772 }), 773 "PlacesFeed.store.dispatch called" 774 ); 775 776 sandbox.restore(); 777 }); 778 779 add_task(async function test_handoffSearchToAwesomebar_with_text() { 780 info( 781 "PlacesFeed.handoffSearchToAwesomebar should properly handle handoff " + 782 "with text data passed in" 783 ); 784 785 let sandbox = sinon.createSandbox(); 786 787 let feed = getPlacesFeedForTest(sandbox); 788 let fakeURLBar = createFakeURLBar(sandbox); 789 790 sandbox.stub(PrivateBrowsingUtils, "isBrowserPrivate").returns(false); 791 let engine = {}; 792 sandbox.stub(feed, "_getDefaultSearchEngine").returns(engine); 793 794 const SESSION_ID = "decafc0ffee"; 795 AboutNewTab.activityStream.store.feeds.get.returns({ 796 sessions: { 797 get: () => { 798 return { session_id: SESSION_ID }; 799 }, 800 }, 801 }); 802 803 feed.handoffSearchToAwesomebar({ 804 _target: { browser: { ownerGlobal: { gURLBar: fakeURLBar } } }, 805 data: { text: "foo" }, 806 meta: { fromTarget: {} }, 807 }); 808 809 Assert.ok(fakeURLBar.handoff.calledOnce, "gURLBar.handoff was called"); 810 Assert.ok(fakeURLBar.handoff.calledWithExactly("foo", engine, SESSION_ID)); 811 Assert.ok(fakeURLBar.focus.notCalled, "gURLBar.focus not called"); 812 Assert.ok( 813 fakeURLBar.setHiddenFocus.notCalled, 814 "gURLBar.setHiddenFocus not called" 815 ); 816 817 // Now call blur listener. 818 fakeURLBar.listeners.blur(); 819 Assert.ok(feed.store.dispatch.calledOnce, "PlacesFeed.store.dispatch called"); 820 Assert.ok( 821 feed.store.dispatch.calledWith({ 822 meta: { 823 from: "ActivityStream:Main", 824 skipMain: true, 825 to: "ActivityStream:Content", 826 toTarget: {}, 827 }, 828 type: "SHOW_SEARCH", 829 }) 830 ); 831 832 sandbox.restore(); 833 }); 834 835 add_task(async function test_handoffSearchToAwesomebar_with_text_pb_mode() { 836 info( 837 "PlacesFeed.handoffSearchToAwesomebar should properly handle handoff " + 838 "with text data passed in, in private browsing mode" 839 ); 840 841 let sandbox = sinon.createSandbox(); 842 843 let feed = getPlacesFeedForTest(sandbox); 844 let fakeURLBar = createFakeURLBar(sandbox); 845 846 sandbox.stub(PrivateBrowsingUtils, "isBrowserPrivate").returns(true); 847 let engine = {}; 848 sandbox.stub(feed, "_getDefaultSearchEngine").returns(engine); 849 850 feed.handoffSearchToAwesomebar({ 851 _target: { browser: { ownerGlobal: { gURLBar: fakeURLBar } } }, 852 data: { text: "foo" }, 853 meta: { fromTarget: {} }, 854 }); 855 Assert.ok(fakeURLBar.handoff.calledOnce, "gURLBar.handoff was called"); 856 Assert.ok(fakeURLBar.handoff.calledWithExactly("foo", engine, undefined)); 857 Assert.ok(fakeURLBar.focus.notCalled, "gURLBar.focus not called"); 858 Assert.ok( 859 fakeURLBar.setHiddenFocus.notCalled, 860 "gURLBar.setHiddenFocus not called" 861 ); 862 863 // Now call blur listener. 864 fakeURLBar.listeners.blur(); 865 Assert.ok(feed.store.dispatch.calledOnce, "PlacesFeed.store.dispatch called"); 866 Assert.ok( 867 feed.store.dispatch.calledWith({ 868 meta: { 869 from: "ActivityStream:Main", 870 skipMain: true, 871 to: "ActivityStream:Content", 872 toTarget: {}, 873 }, 874 type: "SHOW_SEARCH", 875 }) 876 ); 877 878 sandbox.restore(); 879 }); 880 881 add_task(async function test_handoffSearchToAwesomebar_SHOW_SEARCH_on_esc() { 882 info( 883 "PlacesFeed.handoffSearchToAwesomebar should SHOW_SEARCH on ESC keydown" 884 ); 885 886 let sandbox = sinon.createSandbox(); 887 888 let feed = getPlacesFeedForTest(sandbox); 889 let fakeURLBar = createFakeURLBar(sandbox); 890 891 sandbox.stub(PrivateBrowsingUtils, "isBrowserPrivate").returns(false); 892 let engine = {}; 893 sandbox.stub(feed, "_getDefaultSearchEngine").returns(engine); 894 895 feed.handoffSearchToAwesomebar({ 896 _target: { browser: { ownerGlobal: { gURLBar: fakeURLBar } } }, 897 data: { text: "foo" }, 898 meta: { fromTarget: {} }, 899 }); 900 Assert.ok(fakeURLBar.handoff.calledOnce, "gURLBar.handoff was called"); 901 Assert.ok(fakeURLBar.handoff.calledWithExactly("foo", engine, undefined)); 902 Assert.ok(fakeURLBar.focus.notCalled, "gURLBar.focus not called"); 903 904 // Now call ESC keydown. 905 fakeURLBar.listeners.keydown({ key: "Escape" }); 906 Assert.ok(feed.store.dispatch.calledOnce, "PlacesFeed.store.dispatch called"); 907 Assert.ok( 908 feed.store.dispatch.calledWith({ 909 meta: { 910 from: "ActivityStream:Main", 911 skipMain: true, 912 to: "ActivityStream:Content", 913 toTarget: {}, 914 }, 915 type: "SHOW_SEARCH", 916 }) 917 ); 918 919 sandbox.restore(); 920 }); 921 922 add_task( 923 async function test_handoffSearchToAwesomebar_with_session_id_no_text() { 924 info( 925 "PlacesFeed.handoffSearchToAwesomebar should properly handoff a " + 926 "newtab session id with no text passed in" 927 ); 928 929 let sandbox = sinon.createSandbox(); 930 931 let feed = getPlacesFeedForTest(sandbox); 932 let fakeURLBar = createFakeURLBar(sandbox); 933 934 sandbox.stub(PrivateBrowsingUtils, "isBrowserPrivate").returns(false); 935 let engine = {}; 936 sandbox.stub(feed, "_getDefaultSearchEngine").returns(engine); 937 938 const SESSION_ID = "decafc0ffee"; 939 AboutNewTab.activityStream.store.feeds.get.returns({ 940 sessions: { 941 get: () => { 942 return { session_id: SESSION_ID }; 943 }, 944 }, 945 }); 946 947 feed.handoffSearchToAwesomebar({ 948 _target: { browser: { ownerGlobal: { gURLBar: fakeURLBar } } }, 949 data: {}, 950 meta: { fromTarget: {} }, 951 }); 952 953 Assert.ok( 954 fakeURLBar.setHiddenFocus.calledOnce, 955 "gURLBar.setHiddenFocus was called" 956 ); 957 Assert.ok(fakeURLBar.handoff.notCalled, "gURLBar.handoff not called"); 958 Assert.ok(fakeURLBar.focus.notCalled, "gURLBar.focus not called"); 959 Assert.ok( 960 feed.store.dispatch.notCalled, 961 "PlacesFeed.store.dispatch not called" 962 ); 963 964 // Now type a character. 965 fakeURLBar.listeners.keydown({ key: "f" }); 966 Assert.ok(fakeURLBar.handoff.calledOnce, "gURLBar.handoff was called"); 967 Assert.ok(fakeURLBar.handoff.calledWithExactly("", engine, SESSION_ID)); 968 969 Assert.ok( 970 fakeURLBar.removeHiddenFocus.calledOnce, 971 "gURLBar.removeHiddenFocus was called" 972 ); 973 Assert.ok( 974 feed.store.dispatch.calledOnce, 975 "PlacesFeed.store.dispatch called" 976 ); 977 Assert.ok( 978 feed.store.dispatch.calledWith({ 979 meta: { 980 from: "ActivityStream:Main", 981 skipMain: true, 982 to: "ActivityStream:Content", 983 toTarget: {}, 984 }, 985 type: "DISABLE_SEARCH", 986 }) 987 ); 988 989 sandbox.restore(); 990 } 991 ); 992 993 add_task(async function test_observe_dispatch_PLACES_LINK_BLOCKED() { 994 info( 995 "PlacesFeed.observe should dispatch a PLACES_LINK_BLOCKED action " + 996 "with the url of the blocked link" 997 ); 998 999 let sandbox = sinon.createSandbox(); 1000 1001 let feed = getPlacesFeedForTest(sandbox); 1002 feed.observe(null, BLOCKED_EVENT, "foo123.com"); 1003 Assert.equal( 1004 feed.store.dispatch.firstCall.args[0].type, 1005 actionTypes.PLACES_LINK_BLOCKED 1006 ); 1007 Assert.deepEqual(feed.store.dispatch.firstCall.args[0].data, { 1008 url: "foo123.com", 1009 }); 1010 1011 sandbox.restore(); 1012 }); 1013 1014 add_task(async function test_observe_no_dispatch() { 1015 info( 1016 "PlacesFeed.observe should not call dispatch if the topic is something " + 1017 "other than BLOCKED_EVENT" 1018 ); 1019 1020 let sandbox = sinon.createSandbox(); 1021 1022 let feed = getPlacesFeedForTest(sandbox); 1023 feed.observe(null, "someotherevent"); 1024 Assert.ok( 1025 feed.store.dispatch.notCalled, 1026 "PlacesFeed.store.dispatch not called" 1027 ); 1028 1029 sandbox.restore(); 1030 }); 1031 1032 add_task( 1033 async function test_handlePlacesEvent_dispatch_one_PLACES_LINKS_CHANGED() { 1034 let events = [ 1035 { 1036 message: 1037 "PlacesFeed.handlePlacesEvent should only dispatch 1 PLACES_LINKS_CHANGED action " + 1038 "if many bookmark-added notifications happened at once", 1039 dispatchCallCount: 5, 1040 event: { 1041 itemType: TYPE_BOOKMARK, 1042 source: SOURCES.DEFAULT, 1043 dateAdded: FAKE_BOOKMARK.dateAdded, 1044 guid: FAKE_BOOKMARK.bookmarkGuid, 1045 title: FAKE_BOOKMARK.bookmarkTitle, 1046 url: "https://www.foo.com", 1047 isTagging: false, 1048 type: "bookmark-added", 1049 }, 1050 }, 1051 { 1052 message: 1053 "PlacesFeed.handlePlacesEvent should only dispatch 1 " + 1054 "PLACES_LINKS_CHANGED action if many onItemRemoved notifications " + 1055 "happened at once", 1056 dispatchCallCount: 5, 1057 event: { 1058 id: null, 1059 parentId: null, 1060 index: null, 1061 itemType: TYPE_BOOKMARK, 1062 url: "foo.com", 1063 guid: "rTU_oiklsU7D", 1064 parentGuid: "2BzBQXOPFmuU", 1065 source: SOURCES.DEFAULT, 1066 type: "bookmark-removed", 1067 }, 1068 }, 1069 { 1070 message: 1071 "PlacesFeed.handlePlacesEvent should only dispatch 1 " + 1072 "PLACES_LINKS_CHANGED action if any page-removed notifications " + 1073 "happened at once", 1074 dispatchCallCount: 5, 1075 event: { 1076 type: "page-removed", 1077 url: "foo.com", 1078 isRemovedFromStore: true, 1079 }, 1080 }, 1081 ]; 1082 1083 for (let { message, dispatchCallCount, event } of events) { 1084 info(message); 1085 1086 let sandbox = sinon.createSandbox(); 1087 let feed = getPlacesFeedForTest(sandbox); 1088 1089 await feed.placesObserver.handlePlacesEvent([event]); 1090 await feed.placesObserver.handlePlacesEvent([event]); 1091 await feed.placesObserver.handlePlacesEvent([event]); 1092 await feed.placesObserver.handlePlacesEvent([event]); 1093 1094 Assert.ok(feed.placesChangedTimer, "PlacesFeed dispatch timer created"); 1095 1096 // Let's speed things up a bit. 1097 feed.placesChangedTimer.delay = 0; 1098 1099 // Wait for the timer to go off and get cleared 1100 await TestUtils.waitForCondition( 1101 () => !feed.placesChangedTimer, 1102 "PlacesFeed dispatch timer cleared" 1103 ); 1104 1105 Assert.equal( 1106 feed.store.dispatch.callCount, 1107 dispatchCallCount, 1108 `PlacesFeed.store.dispatch was called ${dispatchCallCount} times` 1109 ); 1110 1111 Assert.ok( 1112 feed.store.dispatch.withArgs( 1113 actionCreators.OnlyToMain({ type: actionTypes.PLACES_LINKS_CHANGED }) 1114 ).calledOnce, 1115 "PlacesFeed.store.dispatch called with PLACES_LINKS_CHANGED once" 1116 ); 1117 1118 sandbox.restore(); 1119 } 1120 } 1121 ); 1122 1123 add_task(async function test_PlacesObserver_dispatches() { 1124 let events = [ 1125 { 1126 message: 1127 "PlacesObserver should dispatch a PLACES_HISTORY_CLEARED action " + 1128 "on history-cleared", 1129 args: { type: "history-cleared" }, 1130 expectedAction: { type: actionTypes.PLACES_HISTORY_CLEARED }, 1131 }, 1132 { 1133 message: 1134 "PlacesObserver should dispatch a PLACES_LINKS_DELETED action " + 1135 "with the right url", 1136 args: { 1137 type: "page-removed", 1138 url: "foo.com", 1139 isRemovedFromStore: true, 1140 }, 1141 expectedAction: { 1142 type: actionTypes.PLACES_LINKS_DELETED, 1143 data: { urls: ["foo.com"] }, 1144 }, 1145 }, 1146 { 1147 message: 1148 "PlacesObserver should dispatch a PLACES_BOOKMARK_ADDED action with " + 1149 "the bookmark data - http", 1150 args: { 1151 itemType: TYPE_BOOKMARK, 1152 source: SOURCES.DEFAULT, 1153 dateAdded: FAKE_BOOKMARK.dateAdded, 1154 guid: FAKE_BOOKMARK.bookmarkGuid, 1155 title: FAKE_BOOKMARK.bookmarkTitle, 1156 url: "http://www.foo.com", 1157 isTagging: false, 1158 type: "bookmark-added", 1159 }, 1160 expectedAction: { 1161 type: actionTypes.PLACES_BOOKMARK_ADDED, 1162 data: { 1163 bookmarkGuid: FAKE_BOOKMARK.bookmarkGuid, 1164 bookmarkTitle: FAKE_BOOKMARK.bookmarkTitle, 1165 dateAdded: FAKE_BOOKMARK.dateAdded * 1000, 1166 url: "http://www.foo.com", 1167 }, 1168 }, 1169 }, 1170 { 1171 message: 1172 "PlacesObserver should dispatch a PLACES_BOOKMARK_ADDED action with " + 1173 "the bookmark data - https", 1174 args: { 1175 itemType: TYPE_BOOKMARK, 1176 source: SOURCES.DEFAULT, 1177 dateAdded: FAKE_BOOKMARK.dateAdded, 1178 guid: FAKE_BOOKMARK.bookmarkGuid, 1179 title: FAKE_BOOKMARK.bookmarkTitle, 1180 url: "https://www.foo.com", 1181 isTagging: false, 1182 type: "bookmark-added", 1183 }, 1184 expectedAction: { 1185 type: actionTypes.PLACES_BOOKMARK_ADDED, 1186 data: { 1187 bookmarkGuid: FAKE_BOOKMARK.bookmarkGuid, 1188 bookmarkTitle: FAKE_BOOKMARK.bookmarkTitle, 1189 dateAdded: FAKE_BOOKMARK.dateAdded * 1000, 1190 url: "https://www.foo.com", 1191 }, 1192 }, 1193 }, 1194 ]; 1195 1196 for (let { message, args, expectedAction } of events) { 1197 info(message); 1198 let sandbox = sinon.createSandbox(); 1199 let dispatch = sandbox.spy(); 1200 let observer = new PlacesFeed.PlacesObserver(dispatch); 1201 await observer.handlePlacesEvent([args]); 1202 Assert.ok(dispatch.calledWith(expectedAction)); 1203 sandbox.restore(); 1204 } 1205 }); 1206 1207 add_task(async function test_PlacesObserver_ignores() { 1208 let events = [ 1209 { 1210 message: 1211 "PlacesObserver should not dispatch a PLACES_BOOKMARK_ADDED action - " + 1212 "not http/https for bookmark-added", 1213 event: { 1214 itemType: TYPE_BOOKMARK, 1215 source: SOURCES.DEFAULT, 1216 dateAdded: FAKE_BOOKMARK.dateAdded, 1217 guid: FAKE_BOOKMARK.bookmarkGuid, 1218 title: FAKE_BOOKMARK.bookmarkTitle, 1219 url: "foo.com", 1220 isTagging: false, 1221 type: "bookmark-added", 1222 }, 1223 }, 1224 { 1225 message: 1226 "PlacesObserver should not dispatch a PLACES_BOOKMARK_ADDED action - " + 1227 "has IMPORT source for bookmark-added", 1228 event: { 1229 itemType: TYPE_BOOKMARK, 1230 source: SOURCES.IMPORT, 1231 dateAdded: FAKE_BOOKMARK.dateAdded, 1232 guid: FAKE_BOOKMARK.bookmarkGuid, 1233 title: FAKE_BOOKMARK.bookmarkTitle, 1234 url: "foo.com", 1235 isTagging: false, 1236 type: "bookmark-added", 1237 }, 1238 }, 1239 { 1240 message: 1241 "PlacesObserver should not dispatch a PLACES_BOOKMARK_ADDED " + 1242 "action - has RESTORE source for bookmark-added", 1243 event: { 1244 itemType: TYPE_BOOKMARK, 1245 source: SOURCES.RESTORE, 1246 dateAdded: FAKE_BOOKMARK.dateAdded, 1247 guid: FAKE_BOOKMARK.bookmarkGuid, 1248 title: FAKE_BOOKMARK.bookmarkTitle, 1249 url: "foo.com", 1250 isTagging: false, 1251 type: "bookmark-added", 1252 }, 1253 }, 1254 { 1255 message: 1256 "PlacesObserver should not dispatch a PLACES_BOOKMARK_ADDED " + 1257 "action - has RESTORE_ON_STARTUP source for bookmark-added", 1258 event: { 1259 itemType: TYPE_BOOKMARK, 1260 source: SOURCES.RESTORE_ON_STARTUP, 1261 dateAdded: FAKE_BOOKMARK.dateAdded, 1262 guid: FAKE_BOOKMARK.bookmarkGuid, 1263 title: FAKE_BOOKMARK.bookmarkTitle, 1264 url: "foo.com", 1265 isTagging: false, 1266 type: "bookmark-added", 1267 }, 1268 }, 1269 { 1270 message: 1271 "PlacesObserver should not dispatch a PLACES_BOOKMARK_ADDED " + 1272 "action - has SYNC source for bookmark-added", 1273 event: { 1274 itemType: TYPE_BOOKMARK, 1275 source: SOURCES.SYNC, 1276 dateAdded: FAKE_BOOKMARK.dateAdded, 1277 guid: FAKE_BOOKMARK.bookmarkGuid, 1278 title: FAKE_BOOKMARK.bookmarkTitle, 1279 url: "foo.com", 1280 isTagging: false, 1281 type: "bookmark-added", 1282 }, 1283 }, 1284 { 1285 message: 1286 "PlacesObserver should ignore events that are not of " + 1287 "TYPE_BOOKMARK for bookmark-added", 1288 event: { 1289 itemType: "nottypebookmark", 1290 source: SOURCES.DEFAULT, 1291 dateAdded: FAKE_BOOKMARK.dateAdded, 1292 guid: FAKE_BOOKMARK.bookmarkGuid, 1293 title: FAKE_BOOKMARK.bookmarkTitle, 1294 url: "https://www.foo.com", 1295 isTagging: false, 1296 type: "bookmark-added", 1297 }, 1298 }, 1299 { 1300 message: 1301 "PlacesObserver should ignore events that are not of " + 1302 "TYPE_BOOKMARK for bookmark-removed", 1303 event: { 1304 id: null, 1305 parentId: null, 1306 index: null, 1307 itemType: "nottypebookmark", 1308 url: null, 1309 guid: "461Z_7daEqIh", 1310 parentGuid: "hkHScG3aI3hh", 1311 source: SOURCES.DEFAULT, 1312 type: "bookmark-removed", 1313 }, 1314 }, 1315 { 1316 message: 1317 "PlacesObserver should not dispatch a PLACES_BOOKMARKS_REMOVED " + 1318 "action - has SYNC source for bookmark-removed", 1319 event: { 1320 id: null, 1321 parentId: null, 1322 index: null, 1323 itemType: TYPE_BOOKMARK, 1324 url: "foo.com", 1325 guid: "uvRE3stjoZOI", 1326 parentGuid: "BnsXZl8VMJjB", 1327 source: SOURCES.SYNC, 1328 type: "bookmark-removed", 1329 }, 1330 }, 1331 { 1332 message: 1333 "PlacesObserver should not dispatch a PLACES_BOOKMARKS_REMOVED " + 1334 "action - has IMPORT source for bookmark-removed", 1335 event: { 1336 id: null, 1337 parentId: null, 1338 index: null, 1339 itemType: TYPE_BOOKMARK, 1340 url: "foo.com", 1341 guid: "VF6YwhGpHrOW", 1342 parentGuid: "7Vz8v9nKcSoq", 1343 source: SOURCES.IMPORT, 1344 type: "bookmark-removed", 1345 }, 1346 }, 1347 { 1348 message: 1349 "PlacesObserver should not dispatch a PLACES_BOOKMARKS_REMOVED " + 1350 "action - has RESTORE source for bookmark-removed", 1351 event: { 1352 id: null, 1353 parentId: null, 1354 index: null, 1355 itemType: TYPE_BOOKMARK, 1356 url: "foo.com", 1357 guid: "eKozFyXJP97R", 1358 parentGuid: "ya8Z2FbjKnD0", 1359 source: SOURCES.RESTORE, 1360 type: "bookmark-removed", 1361 }, 1362 }, 1363 { 1364 message: 1365 "PlacesObserver should not dispatch a PLACES_BOOKMARKS_REMOVED " + 1366 "action - has RESTORE_ON_STARTUP source for bookmark-removed", 1367 event: { 1368 id: null, 1369 parentId: null, 1370 index: null, 1371 itemType: TYPE_BOOKMARK, 1372 url: "foo.com", 1373 guid: "StSGMhrYYfyD", 1374 parentGuid: "vL8wsCe2j_eT", 1375 source: SOURCES.RESTORE_ON_STARTUP, 1376 type: "bookmark-removed", 1377 }, 1378 }, 1379 ]; 1380 1381 for (let { message, event } of events) { 1382 info(message); 1383 let sandbox = sinon.createSandbox(); 1384 let dispatch = sandbox.spy(); 1385 let observer = new PlacesFeed.PlacesObserver(dispatch); 1386 1387 await observer.handlePlacesEvent([event]); 1388 Assert.ok(dispatch.notCalled, "PlacesObserver.dispatch not called"); 1389 sandbox.restore(); 1390 } 1391 }); 1392 1393 add_task(async function test_PlacesObserver_bookmark_removed() { 1394 info( 1395 "PlacesObserver should dispatch a PLACES_BOOKMARKS_REMOVED " + 1396 "action with the right URL and bookmarkGuid for bookmark-removed" 1397 ); 1398 let sandbox = sinon.createSandbox(); 1399 let dispatch = sandbox.spy(); 1400 let observer = new PlacesFeed.PlacesObserver(dispatch); 1401 1402 await observer.handlePlacesEvent([ 1403 { 1404 id: null, 1405 parentId: null, 1406 index: null, 1407 itemType: TYPE_BOOKMARK, 1408 url: "foo.com", 1409 guid: "Xgnxs27I9JnX", 1410 parentGuid: "a4k739PL55sP", 1411 source: SOURCES.DEFAULT, 1412 type: "bookmark-removed", 1413 }, 1414 ]); 1415 1416 Assert.ok( 1417 dispatch.calledWith({ 1418 type: actionTypes.PLACES_BOOKMARKS_REMOVED, 1419 data: { urls: ["foo.com"] }, 1420 }) 1421 ); 1422 sandbox.restore(); 1423 });