From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../tests/browser/browser_tab_pickup_list.js | 794 +++++++++++++++++++++ 1 file changed, 794 insertions(+) create mode 100644 browser/components/firefoxview/tests/browser/browser_tab_pickup_list.js (limited to 'browser/components/firefoxview/tests/browser/browser_tab_pickup_list.js') diff --git a/browser/components/firefoxview/tests/browser/browser_tab_pickup_list.js b/browser/components/firefoxview/tests/browser/browser_tab_pickup_list.js new file mode 100644 index 0000000000..875b8a5a10 --- /dev/null +++ b/browser/components/firefoxview/tests/browser/browser_tab_pickup_list.js @@ -0,0 +1,794 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +ChromeUtils.defineESModuleGetters(globalThis, { + SyncedTabs: "resource://services-sync/SyncedTabs.sys.mjs", +}); + +const twoTabs = [ + { + type: "tab", + title: "Phabricator Home", + url: "https://phabricator.services.mozilla.com/", + icon: "https://phabricator.services.mozilla.com/favicon.d25d81d39065.ico", + lastUsed: 1655745700, // Mon, 20 Jun 2022 17:21:40 GMT + }, + { + type: "tab", + title: "Firefox Privacy Notice", + url: "https://www.mozilla.org/en-US/privacy/firefox/", + icon: "https://www.mozilla.org/media/img/favicons/mozilla/favicon.d25d81d39065.ico", + lastUsed: 1655745700, // Mon, 20 Jun 2022 17:21:40 GMT + }, +]; +const syncedTabsData2 = structuredClone(syncedTabsData1); +syncedTabsData2[1].tabs = [...syncedTabsData2[1].tabs, ...twoTabs]; + +const syncedTabsData3 = [ + { + id: 1, + type: "client", + name: "My desktop", + clientType: "desktop", + lastModified: 1655730486760, + tabs: [ + { + type: "tab", + title: "Sandboxes - Sinon.JS", + url: "https://sinonjs.org/releases/latest/sandbox/", + icon: "https://sinonjs.org/assets/images/favicon.png", + lastUsed: 1655391592, // Thu Jun 16 2022 14:59:52 GMT+0000 + }, + ], + }, +]; + +const syncedTabsData4 = structuredClone(syncedTabsData3); +syncedTabsData4[0].tabs = [...syncedTabsData4[0].tabs, ...twoTabs]; + +const syncedTabsData5 = [ + { + id: 1, + type: "client", + name: "My desktop", + clientType: "desktop", + lastModified: Date.now(), + tabs: [ + { + type: "tab", + title: "Example2", + url: "https://example.com", + icon: "https://example/favicon.png", + lastUsed: Math.floor((Date.now() - 1000 * 60) / 1000), // This is one minute from now, which is below the threshold for 'Just now' + }, + ], + }, +]; + +const desktopTabs = [ + { + type: "tab", + title: "Internet for people, not profits - Mozilla", + url: "https://www.mozilla.org/", + icon: "https://www.mozilla.org/media/img/favicons/mozilla/favicon.d25d81d39065.ico", + lastUsed: 1655730486, // Mon Jan 19 1970 22:55:30 GMT+0000 + }, + { + type: "tab", + title: "Firefox Privacy Notice", + url: "https://www.mozilla.org/en-US/privacy/firefox/", + icon: "https://www.mozilla.org/media/img/favicons/mozilla/favicon.d25d81d39065.ico", + lastUsed: 1673991540155, // Tue, 17 Jan 2023 16:39:00 GMT + }, + { + type: "tab", + title: "Bugzilla Main Page", + url: "https://bugzilla.mozilla.org/", + icon: "https://bugzilla.mozilla.org/extensions/BMO/web/images/favicon.ico", + lastUsed: 1673513538000, // Thu, 12 Jan 2023 03:52:18 GMT + }, +]; + +const mobileTabs = [ + { + type: "tab", + title: "Internet for people, not profits - Mozilla", + url: "https://www.mozilla.org/", + icon: "https://www.mozilla.org/media/img/favicons/mozilla/favicon.d25d81d39065.ico", + lastUsed: 1606510800000, // Fri Nov 27 2020 16:00:00 GMT+0000 + }, + { + type: "tab", + title: "Firefox Privacy Notice", + url: "https://www.mozilla.org/en-US/privacy/firefox/", + icon: "https://www.mozilla.org/media/img/favicons/mozilla/favicon.d25d81d39065.ico", + lastUsed: 1606510800000, // Fri Nov 27 2020 16:00:00 GMT+0000 + }, +]; + +const syncedTabsData6 = structuredClone(syncedTabsData1); +syncedTabsData6[0].tabs = desktopTabs; +syncedTabsData6[1].tabs = mobileTabs; + +const NO_TABS_EVENTS = [ + ["firefoxview", "entered", "firefoxview", undefined], + ["firefoxview", "synced_tabs", "tabs", undefined, { count: "0" }], +]; + +const TAB_PICKUP_EVENT = [ + ["firefoxview", "entered", "firefoxview", undefined], + ["firefoxview", "synced_tabs", "tabs", undefined, { count: "1" }], + [ + "firefoxview", + "tab_pickup", + "tabs", + undefined, + { position: "1", deviceType: "desktop" }, + ], +]; + +const TAB_PICKUP_OPEN_EVENT = [ + ["firefoxview", "tab_pickup_open", "tabs", "false"], +]; + +registerCleanupFunction(async function () { + cleanup_tab_pickup(); +}); + +add_setup(async function setup() { + // set updateTimeMs to 0 to prevent unexpected/unrelated DOM mutations during testing + await SpecialPowers.pushPrefEnv({ + set: [["browser.tabs.firefox-view.updateTimeMs", 0]], + }); +}); + +add_task(async function test_tab_list_ordering() { + const sandbox = setupRecentDeviceListMocks(); + const syncedTabsMock = sandbox.stub(SyncedTabs, "getRecentTabs"); + let mockTabs1 = getMockTabData(syncedTabsData1); + let mockTabs2 = getMockTabData(syncedTabsData2); + let getRecentTabsResult = mockTabs1; + syncedTabsMock.callsFake(() => { + info( + `Stubbed SyncedTabs.getRecentTabs returning a promise that resolves to ${getRecentTabsResult.length} tabs\n` + ); + return Promise.resolve(getRecentTabsResult); + }); + + await withFirefoxView({}, async browser => { + const { document } = browser.contentWindow; + + await setupListState(browser); + + testVisibility(browser, { + expectedVisible: { + "ol.synced-tabs-list": true, + }, + }); + + ok( + document.querySelector("ol.synced-tabs-list").children.length === 3, + "synced-tabs-list should have three list items" + ); + + ok( + document + .querySelector("ol.synced-tabs-list") + .firstChild.textContent.includes("Internet for people, not profits"), + "First list item in synced-tabs-list is in the correct order" + ); + + ok( + document + .querySelector("ol.synced-tabs-list") + .children[2].textContent.includes("Sandboxes - Sinon.JS"), + "Last list item in synced-tabs-list is in the correct order" + ); + + getRecentTabsResult = mockTabs2; + // Initiate a synced tabs update + Services.obs.notifyObservers(null, "services.sync.tabs.changed"); + + const syncedTabsList = document.querySelector("ol.synced-tabs-list"); + // first list item has been updated + await BrowserTestUtils.waitForMutationCondition( + syncedTabsList, + { childList: true, subtree: true }, + () => syncedTabsList.firstChild.textContent.includes("Firefox") + ); + + ok( + document.querySelector("ol.synced-tabs-list").children.length === 3, + "Synced-tabs-list should still have three list items" + ); + + ok( + document + .querySelector("ol.synced-tabs-list") + .children[1].textContent.includes("Phabricator"), + "Second list item in synced-tabs-list has been updated" + ); + + ok( + document + .querySelector("ol.synced-tabs-list") + .children[2].textContent.includes("Internet for people, not profits"), + "Last list item in synced-tabs-list has been updated" + ); + + sandbox.restore(); + cleanup_tab_pickup(); + }); +}); + +add_task(async function test_empty_list_items() { + const sandbox = setupRecentDeviceListMocks(); + const syncedTabsMock = sandbox.stub(SyncedTabs, "getRecentTabs"); + let mockTabs1 = getMockTabData(syncedTabsData3); + let mockTabs2 = getMockTabData(syncedTabsData4); + let getRecentTabsResult = mockTabs1; + syncedTabsMock.callsFake(() => { + info( + `Stubbed SyncedTabs.getRecentTabs returning a promise that resolves to ${getRecentTabsResult.length} tabs\n` + ); + return Promise.resolve(getRecentTabsResult); + }); + + await withFirefoxView({}, async browser => { + const { document } = browser.contentWindow; + + await setupListState(browser); + + testVisibility(browser, { + expectedVisible: { + "ol.synced-tabs-list": true, + }, + }); + + ok( + document.querySelector("ol.synced-tabs-list").children.length === 3, + "synced-tabs-list should have three list items" + ); + + ok( + document + .querySelector("ol.synced-tabs-list") + .firstChild.textContent.includes("Sandboxes - Sinon.JS"), + "First list item in synced-tabs-list is in the correct order" + ); + + ok( + document + .querySelector("ol.synced-tabs-list") + .children[1].classList.contains("synced-tab-li-placeholder"), + "Second list item in synced-tabs-list should be a placeholder" + ); + + ok( + document + .querySelector("ol.synced-tabs-list") + .lastChild.classList.contains("synced-tab-li-placeholder"), + "Last list item in synced-tabs-list should be a placeholder" + ); + + getRecentTabsResult = mockTabs2; + // Initiate a synced tabs update + Services.obs.notifyObservers(null, "services.sync.tabs.changed"); + + const syncedTabsList = document.querySelector("ol.synced-tabs-list"); + // first list item has been updated + await BrowserTestUtils.waitForMutationCondition( + syncedTabsList, + { childList: true, subtree: true }, + () => + syncedTabsList.firstChild.textContent.includes("Firefox Privacy Notice") + ); + + ok( + document + .querySelector("ol.synced-tabs-list") + .children[1].textContent.includes("Phabricator"), + "Second list item in synced-tabs-list has been updated" + ); + + ok( + document + .querySelector("ol.synced-tabs-list") + .lastChild.textContent.includes("Sandboxes - Sinon.JS"), + "Last list item in synced-tabs-list has been updated" + ); + + sandbox.restore(); + cleanup_tab_pickup(); + }); +}); + +add_task(async function test_empty_list() { + await clearAllParentTelemetryEvents(); + const sandbox = setupRecentDeviceListMocks(); + const syncedTabsMock = sandbox.stub(SyncedTabs, "getRecentTabs"); + let mockTabs1 = getMockTabData([]); + let mockTabs2 = getMockTabData(syncedTabsData4); + let getRecentTabsResult = mockTabs1; + syncedTabsMock.callsFake(() => { + info( + `Stubbed SyncedTabs.getRecentTabs returning a promise that resolves to ${getRecentTabsResult.length} tabs\n` + ); + return Promise.resolve(getRecentTabsResult); + }); + + await withFirefoxView({}, async browser => { + const { document } = browser.contentWindow; + + await setupListState(browser); + info("setupListState complete, checking placeholder and list visibility"); + testVisibility(browser, { + expectedVisible: { + "#synced-tabs-placeholder": true, + "ol.synced-tabs-list": false, + }, + }); + + ok( + document + .querySelector("#synced-tabs-placeholder") + .classList.contains("empty-container"), + "collapsible container should have correct styling when the list is empty" + ); + + await TestUtils.waitForCondition( + () => { + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + false + ).parent; + return events && events.length >= 2; + }, + "Waiting for entered and synced_tabs firefoxview telemetry events.", + 200, + 100 + ); + + TelemetryTestUtils.assertEvents( + NO_TABS_EVENTS, + { category: "firefoxview" }, + { clear: true, process: "parent" } + ); + + getRecentTabsResult = mockTabs2; + // Initiate a synced tabs update + Services.obs.notifyObservers(null, "services.sync.tabs.changed"); + + const syncedTabsList = document.querySelector("ol.synced-tabs-list"); + await BrowserTestUtils.waitForMutationCondition( + syncedTabsList, + { childList: true, subtree: true }, + () => syncedTabsList.children.length + ); + + testVisibility(browser, { + expectedVisible: { + "#synced-tabs-placeholder": false, + "ol.synced-tabs-list": true, + }, + }); + + sandbox.restore(); + cleanup_tab_pickup(); + }); +}); + +add_task(async function test_time_updates_correctly() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.tabs.firefox-view.updateTimeMs", 100]], + }); + await clearAllParentTelemetryEvents(); + + const sandbox = setupRecentDeviceListMocks(); + const syncedTabsMock = sandbox.stub(SyncedTabs, "getRecentTabs"); + let mockTabs1 = getMockTabData(syncedTabsData5); + let getRecentTabsResult = mockTabs1; + syncedTabsMock.callsFake(() => { + info( + `Stubbed SyncedTabs.getRecentTabs returning a promise that resolves to ${getRecentTabsResult.length} tabs\n` + ); + return Promise.resolve(getRecentTabsResult); + }); + + await withFirefoxView({}, async browser => { + const { document } = browser.contentWindow; + + await setupListState(browser); + + let initialTimeText = document.querySelector( + "span.synced-tab-li-time" + ).textContent; + Assert.stringContains( + initialTimeText, + "Just now", + "synced-tab-li-time text is 'Just now'" + ); + + await SpecialPowers.pushPrefEnv({ + set: [["browser.tabs.firefox-view.updateTimeMs", 100]], + }); + + const timeLabel = document.querySelector("span.synced-tab-li-time"); + await BrowserTestUtils.waitForMutationCondition( + timeLabel, + { childList: true, subtree: true }, + () => !timeLabel.textContent.includes("now") + ); + + isnot( + timeLabel.textContent, + initialTimeText, + "synced-tab-li-time text has updated" + ); + + document.querySelector(".synced-tab-a").click(); + + await TestUtils.waitForCondition( + () => { + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + false + ).parent; + return events && events.length >= 3; + }, + "Waiting for entered, synced_tabs, and tab_pickup firefoxview telemetry events.", + 200, + 100 + ); + + TelemetryTestUtils.assertEvents( + TAB_PICKUP_EVENT, + { category: "firefoxview" }, + { clear: true, process: "parent" } + ); + + let gBrowser = browser.getTabBrowser(); + is( + gBrowser.visibleTabs.indexOf(gBrowser.selectedTab), + 0, + "Tab opened at the beginning of the tab strip" + ); + gBrowser.removeTab(gBrowser.selectedTab); + // make sure we're back on fx-view + browser.ownerGlobal.FirefoxViewHandler.openTab(); + + info("Waiting for the tab pickup summary to be visible"); + await waitForElementVisible(browser, "#tab-pickup-container > summary"); + // click on the details summary and verify telemetry gets logged for this event + await clearAllParentTelemetryEvents(); + info("clicking the summary to collapse it"); + document.querySelector("#tab-pickup-container > summary").click(); + + await TestUtils.waitForCondition( + () => { + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + false + ).parent; + return events && events.length >= 1; + }, + "Waiting for tab_pickup_open firefoxview telemetry event.", + 200, + 100 + ); + TelemetryTestUtils.assertEvents( + TAB_PICKUP_OPEN_EVENT, + { category: "firefoxview" }, + { clear: true, process: "parent" } + ); + + sandbox.restore(); + cleanup_tab_pickup(); + await SpecialPowers.popPrefEnv(); + }); +}); + +/** + * Ensure that tabs sync when a user reloads Firefox View. + * This is accomplished by asserting that a new set of tabs are loaded + * on page reload. + */ +add_task(async function test_tabs_sync_on_user_page_reload() { + const sandbox = setupRecentDeviceListMocks(); + sandbox.stub(SyncedTabs._internal, "syncTabs").resolves(true); + const syncedTabsMock = sandbox.stub(SyncedTabs, "getRecentTabs"); + let mockTabs1 = getMockTabData(syncedTabsData1); + let expectedTabsAfterReload = getMockTabData(syncedTabsData3); + let getRecentTabsResult = mockTabs1; + syncedTabsMock.callsFake(() => { + info( + `Stubbed SyncedTabs.getRecentTabs returning a promise that resolves to ${getRecentTabsResult.length} tabs\n` + ); + return Promise.resolve(getRecentTabsResult); + }); + + await withFirefoxView({}, async browser => { + let reloadButton = browser.ownerDocument.getElementById("reload-button"); + + await setupListState(browser); + + let tabLoaded = BrowserTestUtils.browserLoaded(browser); + EventUtils.synthesizeMouseAtCenter(reloadButton, {}, browser.ownerGlobal); + await tabLoaded; + // Wait until the window is reloaded, then get the current instance + // of the contentWindow + const { document } = browser.contentWindow; + ok(true, "Firefox View has been reloaded"); + ok(TabsSetupFlowManager.waitingForTabs, "waitingForTabs is true"); + + let waitedForTabs = TestUtils.waitForCondition(() => { + return !TabsSetupFlowManager.waitingForTabs; + }); + + getRecentTabsResult = expectedTabsAfterReload; + Services.obs.notifyObservers(null, "services.sync.tabs.changed"); + + const syncedTabsList = document.querySelector("ol.synced-tabs-list"); + // The tab pickup list has been updated + await BrowserTestUtils.waitForMutationCondition( + syncedTabsList, + { childList: true, subtree: true }, + () => + syncedTabsList.firstChild.textContent.includes("Sandboxes - Sinon.JS") + ); + await waitedForTabs; + + sandbox.restore(); + cleanup_tab_pickup(); + }); +}); + +add_task(async function test_keyboard_navigation() { + TabsSetupFlowManager.resetInternalState(); + + const sandbox = setupRecentDeviceListMocks(); + const syncedTabsMock = sandbox.stub(SyncedTabs, "getRecentTabs"); + let mockTabs1 = getMockTabData(syncedTabsData1); + let getRecentTabsResult = mockTabs1; + syncedTabsMock.callsFake(() => { + info( + `Stubbed SyncedTabs.getRecentTabs returning a promise that resolves to ${getRecentTabsResult.length} tabs\n` + ); + return Promise.resolve(getRecentTabsResult); + }); + + await withFirefoxView({}, async browser => { + const { document } = browser.contentWindow; + let win = browser.ownerGlobal; + + await setupListState(browser); + const tab = (shiftKey = false) => { + info(`${shiftKey ? "Shift + Tab" : "Tab"}`); + EventUtils.synthesizeKey("KEY_Tab", { shiftKey }, win); + }; + const arrowDown = () => { + info("Arrow Down"); + EventUtils.synthesizeKey("KEY_ArrowDown", {}, win); + }; + const arrowUp = () => { + info("Arrow Up"); + EventUtils.synthesizeKey("KEY_ArrowUp", {}, win); + }; + const arrowLeft = () => { + info("Arrow Left"); + EventUtils.synthesizeKey("KEY_ArrowLeft", {}, win); + }; + const arrowRight = () => { + info("Arrow Right"); + EventUtils.synthesizeKey("KEY_ArrowRight", {}, win); + }; + + let syncedTabsLinks = document + .querySelector("ol.synced-tabs-list") + .querySelectorAll("a"); + let summary = document + .getElementById("tab-pickup-container") + .querySelector("summary"); + summary.focus(); + tab(); + is( + syncedTabsLinks[0], + document.activeElement, + "First synced tab should be focused" + ); + arrowDown(); + is( + syncedTabsLinks[1], + document.activeElement, + "Second synced tab should be focused" + ); + arrowDown(); + is( + syncedTabsLinks[2], + document.activeElement, + "Third synced tab should be focused" + ); + arrowDown(); + is( + syncedTabsLinks[2], + document.activeElement, + "Third synced tab should still be focused" + ); + arrowUp(); + is( + syncedTabsLinks[1], + document.activeElement, + "Second synced tab should be focused" + ); + arrowLeft(); + is( + syncedTabsLinks[0], + document.activeElement, + "First synced tab should be focused" + ); + arrowRight(); + is( + syncedTabsLinks[1], + document.activeElement, + "Second synced tab should be focused" + ); + arrowDown(); + is( + syncedTabsLinks[2], + document.activeElement, + "Third synced tab should be focused" + ); + arrowLeft(); + is( + syncedTabsLinks[0], + document.activeElement, + "First synced tab should be focused" + ); + + tab(true); + is( + summary, + document.activeElement, + "Summary element should be focused when shift tabbing away from list" + ); + + sandbox.restore(); + cleanup_tab_pickup(); + }); +}); + +add_task(async function test_duplicate_tab_filter() { + const sandbox = setupRecentDeviceListMocks(); + const syncedTabsMock = sandbox.stub(SyncedTabs, "getRecentTabs"); + let mockTabs6 = getMockTabData(syncedTabsData6); + let getRecentTabsResult = mockTabs6; + syncedTabsMock.callsFake(() => { + info( + `Stubbed SyncedTabs.getRecentTabs returning a promise that resolves to ${getRecentTabsResult.length} tabs\n` + ); + return Promise.resolve(getRecentTabsResult); + }); + + await withFirefoxView({}, async browser => { + await setupListState(browser); + + Assert.equal( + mockTabs6[0].title, + "Firefox Privacy Notice", + `First tab should be ${mockTabs6[0].title}` + ); + + Assert.equal( + mockTabs6[0].lastUsed, + 1673991540155, + `First tab lastUsed value should be ${mockTabs6[0].lastUsed}` + ); + + Assert.equal( + mockTabs6[1].title, + "Bugzilla Main Page", + `Second tab should be ${mockTabs6[1].title}` + ); + + Assert.equal( + mockTabs6[1].lastUsed, + 1673513538000, + `Second tab lastUsed value should be ${mockTabs6[1].lastUsed}` + ); + + Assert.equal( + mockTabs6[2].title, + "Internet for people, not profits - Mozilla", + `Third tab should be ${mockTabs6[2].title}` + ); + + Assert.equal( + mockTabs6[2].lastUsed, + 1606510800000, + `Third tab lastUsed value should be ${mockTabs6[2].lastUsed}` + ); + + sandbox.restore(); + cleanup_tab_pickup(); + }); +}); + +add_task(async function test_tabs_dont_update_unnecessarily() { + const sandbox = setupRecentDeviceListMocks(); + const syncedTabsMock = sandbox.stub(SyncedTabs, "getRecentTabs"); + let mockTabs1 = getMockTabData(syncedTabsData1); + let getRecentTabsResult = mockTabs1; + syncedTabsMock.callsFake(() => { + info( + `Stubbed SyncedTabs.getRecentTabs returning a promise that resolves to ${getRecentTabsResult.length} tabs\n` + ); + return Promise.resolve(getRecentTabsResult); + }); + + await withFirefoxView({}, async browser => { + await setupListState(browser); + + const { document } = browser.contentWindow; + const syncedTabsList = document.querySelector("ol.synced-tabs-list"); + + Assert.ok( + syncedTabsList.children.length === 3, + "Tab Pickup list should have three list items" + ); + + Assert.ok( + syncedTabsList.firstChild.textContent.includes( + "Internet for people, not profits - Mozilla" + ), + `First item in the Tab Pickup list is ${mockTabs1[0].title}` + ); + + Assert.ok( + syncedTabsList.children[1].textContent.includes("The Times"), + `Second item in Tab Pickup list is ${mockTabs1[1].title}` + ); + + Assert.ok( + syncedTabsList.children[2].textContent.includes("Sandboxes - Sinon.JS"), + `Third item in Tab Pickup list is ${mockTabs1[2].title}` + ); + + let wasMutated = false; + + const callback = mutationList => { + // some logging so if this starts to fail we have some clues as to why + for (const mutation of mutationList) { + if (mutation.type === "childList") { + info( + "A child node has been added or removed:" + mutation.target.nodeName + ); + } else if (mutation.type === "attributes") { + info(`The ${mutation.attributeName} attribute was modified.`); + } else if (mutation.type === "characterData") { + info(`The characterData was modified.`); + } + } + wasMutated = true; + }; + + const observer = new MutationObserver(callback); + + observer.observe(syncedTabsList, { childList: true, subtree: true }); + + getRecentTabsResult = mockTabs1; + const tabPickupList = document.querySelector("tab-pickup-list"); + const updateTabsListSpy = sandbox.spy(tabPickupList, "updateTabsList"); + + // Initiate a synced tabs update + Services.obs.notifyObservers(null, "services.sync.tabs.changed"); + await TestUtils.waitForCondition(() => { + return !TabsSetupFlowManager.waitingForTabs; + }); + await TestUtils.waitForCondition(() => updateTabsListSpy.called); + Assert.ok(!wasMutated, "The synced tabs list was not mutated"); + + observer.disconnect(); + sandbox.restore(); + cleanup_tab_pickup(); + }); +}); -- cgit v1.2.3