/* 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", }); async function tearDown(sandbox) { sandbox?.restore(); Services.prefs.clearUserPref("services.sync.lastTabFetch"); } function checkLoadingState(browser, isLoading = false) { const { document } = browser.contentWindow; const tabsContainer = document.querySelector("#tabpickup-tabs-container"); const tabsList = document.querySelector( "#tabpickup-tabs-container tab-pickup-list" ); const loadingElem = document.querySelector( "#tabpickup-tabs-container .loading-content" ); const setupElem = document.querySelector("#tabpickup-steps"); if (isLoading) { ok( tabsContainer.classList.contains("loading"), "Tabs container has loading class" ); BrowserTestUtils.is_visible( loadingElem, "Loading content is visible when loading" ); !tabsList || BrowserTestUtils.is_hidden( tabsList, "Synced tabs list is not visible when loading" ); !setupElem || BrowserTestUtils.is_hidden( setupElem, "Setup content is not visible when loading" ); } else { ok( !tabsContainer.classList.contains("loading"), "Tabs container has no loading class" ); !loadingElem || BrowserTestUtils.is_hidden( loadingElem, "Loading content is not visible when tabs are loaded" ); BrowserTestUtils.is_visible( tabsList, "Synced tabs list is visible when loaded" ); !setupElem || BrowserTestUtils.is_hidden( setupElem, "Setup content is not visible when tabs are loaded" ); } } function setupMocks(recentTabs, syncEnabled = true) { const sandbox = (gSandbox = setupRecentDeviceListMocks()); sandbox.stub(SyncedTabs, "getRecentTabs").callsFake(() => { info( `SyncedTabs.getRecentTabs will return a promise resolving to ${recentTabs.length} tabs` ); return Promise.resolve(recentTabs); }); return sandbox; } add_setup(async function () { registerCleanupFunction(() => { // reset internal state so it doesn't affect the next tests TabsSetupFlowManager.resetInternalState(); }); // gSync.init() is called in a requestIdleCallback. Force its initialization. gSync.init(); registerCleanupFunction(async function () { Services.prefs.clearUserPref("services.sync.engine.tabs"); await tearDown(gSandbox); }); }); add_task(async function test_tab_sync_loading() { // empty synced tabs, so we're relying on tabs.changed or sync:finish notifications to clear the waiting state const recentTabsData = []; const sandbox = setupMocks(recentTabsData); // stub syncTabs so it resolves to true - meaning yes it will trigger a sync, which is the case // we want to cover in this test. sandbox.stub(SyncedTabs._internal, "syncTabs").resolves(true); await withFirefoxView({}, async browser => { Services.obs.notifyObservers(null, UIState.ON_UPDATE); const { document } = browser.contentWindow; const tabsContainer = document.querySelector("#tabpickup-tabs-container"); await waitForElementVisible(browser, "#tabpickup-steps", false); await waitForElementVisible(browser, "#tabpickup-tabs-container", true); checkMobilePromo(browser, { mobilePromo: false, mobileConfirmation: false, }); ok(TabsSetupFlowManager.waitingForTabs, "waitingForTabs is true"); checkLoadingState(browser, true); Services.obs.notifyObservers(null, "services.sync.tabs.changed"); await BrowserTestUtils.waitForMutationCondition( tabsContainer, { attributeFilter: ["class"], attributes: true }, () => { return !tabsContainer.classList.contains("loading"); } ); checkLoadingState(browser, false); checkMobilePromo(browser, { mobilePromo: false, mobileConfirmation: false, }); }); await tearDown(sandbox); }); add_task(async function test_tab_no_sync() { // Ensure we take down the waiting message if SyncedTabs determines it doesnt need to sync const recentTabsData = structuredClone(syncedTabsData1[0].tabs); const sandbox = setupMocks(recentTabsData); // stub syncTabs so it resolves to false - meaning it will not trigger a sync, which is the case // we want to cover in this test. sandbox.stub(SyncedTabs._internal, "syncTabs").resolves(false); await withFirefoxView({}, async browser => { Services.obs.notifyObservers(null, UIState.ON_UPDATE); await waitForElementVisible(browser, "#tabpickup-steps", false); await waitForElementVisible(browser, "#tabpickup-tabs-container", true); ok(!TabsSetupFlowManager.waitingForTabs, "waitingForTabs is false"); checkLoadingState(browser, false); }); await tearDown(sandbox); }); add_task(async function test_recent_tabs_loading() { // Simulate stale data by setting lastTabFetch to 10mins ago const TEN_MINUTES_MS = 1000 * 60 * 10; const staleFetchSeconds = Math.floor((Date.now() - TEN_MINUTES_MS) / 1000); info("updating lastFetch:" + staleFetchSeconds); Services.prefs.setIntPref("services.sync.lastTabFetch", staleFetchSeconds); // cached tabs data is available, so we shouldn't wait on lastTabFetch pref value const recentTabsData = structuredClone(syncedTabsData1[0].tabs); const sandbox = setupMocks(recentTabsData); await withFirefoxView({}, async browser => { Services.obs.notifyObservers(null, UIState.ON_UPDATE); await waitForElementVisible(browser, "#tabpickup-steps", false); await waitForElementVisible(browser, "#tabpickup-tabs-container", true); checkMobilePromo(browser, { mobilePromo: false, mobileConfirmation: false, }); checkLoadingState(browser, false); }); await tearDown(sandbox); });