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 --- .../browser_tab_pickup_device_added_telemetry.js | 278 +++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 browser/components/firefoxview/tests/browser/browser_tab_pickup_device_added_telemetry.js (limited to 'browser/components/firefoxview/tests/browser/browser_tab_pickup_device_added_telemetry.js') diff --git a/browser/components/firefoxview/tests/browser/browser_tab_pickup_device_added_telemetry.js b/browser/components/firefoxview/tests/browser/browser_tab_pickup_device_added_telemetry.js new file mode 100644 index 0000000000..ce77090077 --- /dev/null +++ b/browser/components/firefoxview/tests/browser/browser_tab_pickup_device_added_telemetry.js @@ -0,0 +1,278 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +registerCleanupFunction(async function () { + await clearAllParentTelemetryEvents(); + cleanup_tab_pickup(); +}); + +function setupWithFxaDevices() { + const sandbox = (gSandbox = setupSyncFxAMocks({ + state: UIState.STATUS_SIGNED_IN, + fxaDevices: [ + { + id: 1, + name: "My desktop", + isCurrentDevice: true, + type: "desktop", + }, + { + id: 2, + name: "Other device", + isCurrentDevice: false, + type: "mobile", + }, + ], + })); + return sandbox; +} + +const mockDesktopTab1 = { + client: "6c12bonqXZh8", + device: "My desktop", + deviceType: "desktop", + 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 mockDesktopTab2 = { + client: "6c12bonqXZh8", + device: "My desktop", + deviceType: "desktop", + 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 mockMobileTab1 = { + client: "9d0y686hBXel", + device: "My phone", + deviceType: "mobile", + type: "tab", + title: "Element", + url: "https://chat.mozilla.org/#room:mozilla.org", + icon: "https://chat.mozilla.org/vector-icons/favicon.ico", + lastUsed: 1664571288, +}; + +const NO_TABS_EVENTS = [ + ["firefoxview", "entered", "firefoxview", undefined], + ["firefoxview", "synced_tabs", "tabs", undefined, { count: "0" }], +]; +const SINGLE_TAB_EVENTS = [ + ["firefoxview", "entered", "firefoxview", undefined], + ["firefoxview", "synced_tabs", "tabs", undefined, { count: "1" }], +]; +const DEVICE_ADDED_NO_TABS_EVENTS = [ + ["firefoxview", "synced_tabs", "tabs", undefined, undefined], + ["firefoxview", "synced_tabs_empty", "since_device_added", undefined], +]; +const DEVICE_ADDED_TABS_EVENTS = [ + ["firefoxview", "synced_tabs", "tabs", undefined, undefined], +]; + +async function whenResolved(functionSpy, functionLabel) { + info(`Waiting for ${functionLabel} to be called`); + await TestUtils.waitForCondition( + () => functionSpy.called, + `Waiting for ${functionLabel} to be called` + ); + is( + functionSpy.getCall(0).returnValue.constructor.name, + "Promise", + `${functionLabel} returned a promise` + ); + info(`Waiting for the promise returned by ${functionLabel} to be resolved`); + await functionSpy.getCall(0).returnValue; + info(`${functionLabel} promise resolved`); +} + +async function test_device_added({ + initialRecentTabsResult, + expectedInitialTelementryEvents, + expectedDeviceAddedTelementryEvents, +}) { + const recentTabsResult = initialRecentTabsResult; + await clearAllParentTelemetryEvents(); + const sandbox = setupWithFxaDevices(); + const syncedTabsMock = sandbox.stub(SyncedTabs, "getRecentTabs"); + + syncedTabsMock.callsFake(() => { + info( + `Stubbed SyncedTabs.getRecentTabs returning a promise that resolves to ${recentTabsResult.length} tabs\n` + ); + return Promise.resolve(recentTabsResult); + }); + + ok( + !isFirefoxViewTabSelected(), + "Before we call withFirefoxView, about:firefoxview tab is not selected" + ); + ok( + !TabsSetupFlowManager.hasVisibleViews, + "Initially hasVisibleViews is false" + ); + + await withFirefoxView({}, async browser => { + info("inside withFirefoxView taskFn, waiting for setupListState"); + const { document } = browser.contentWindow; + const stopWaitingSpy = sandbox.spy( + TabsSetupFlowManager, + "stopWaitingForTabs" + ); + const signedInChangeSpy = sandbox.spy( + TabsSetupFlowManager, + "onSignedInChange" + ); + + await setupListState(browser); + info("setupListState finished"); + + // ensure any tab syncs triggered by Fxa sign-in are complete before proceeding + await whenResolved(signedInChangeSpy, "onSignedInChange"); + if (!recentTabsResult.length) { + info("No synced tabs so we wait for the result of the sync we trigger"); + await whenResolved(stopWaitingSpy, "stopWaitingForTabs"); + info("stopWaitingForTabs finished"); + } + + const isTablistVisible = !!initialRecentTabsResult.length; + testVisibility(browser, { + expectedVisible: { + "ol.synced-tabs-list": isTablistVisible, + "#synced-tabs-placeholder": !isTablistVisible, + }, + }); + const syncedTabsItems = document.querySelectorAll( + "ol.synced-tabs-list > li:not(.synced-tab-li-placeholder)" + ); + info( + "list items: " + + Array.from(syncedTabsItems) + .map(li => `li.${li.className}`) + .join(", ") + ); + is( + syncedTabsItems.length, + initialRecentTabsResult.length, + `synced-tabs-list should have initial count of ${initialRecentTabsResult.length} non-placeholder list items` + ); + + // confirm telemetry is in expected state? + info( + "Checking telemetry against expectedInitialTelementryEvents: " + + JSON.stringify(expectedInitialTelementryEvents, null, 2) + ); + TelemetryTestUtils.assertEvents( + expectedInitialTelementryEvents, + { category: "firefoxview" }, + { clear: true, process: "parent" } + ); + + // add a new mock device + info("Adding a new mock fxa dedvice"); + gMockFxaDevices.push({ + id: 1, + name: "My primary phone", + isCurrentDevice: false, + type: "mobile", + }); + + const startWaitingSpy = sandbox.spy( + TabsSetupFlowManager, + "startWaitingForNewDeviceTabs" + ); + // Notify of the newly added device + info("Notifying devicelist_updated with the new mobile device"); + Services.obs.notifyObservers(null, "fxaccounts:devicelist_updated"); + + // Some time passes here waiting for sync to get data from that device + // we expect new-device handling to kick in. If there are 0 tabs we'll signal we're waiting, + // create a timestamp and only clear it when there are > 0 tabs. + // If there are already > 0 tabs, we'll basically do nothing, showing any new tabs when they arrive + await whenResolved(startWaitingSpy, "startWaitingForNewDeviceTabs"); + + info( + "Initial tabs count: " + + recentTabsResult.length + + ", assert on _noTabsVisibleFromAddedDeviceTimestamp: " + + TabsSetupFlowManager._noTabsVisibleFromAddedDeviceTimestamp + ); + if (recentTabsResult.length) { + ok( + !TabsSetupFlowManager._noTabsVisibleFromAddedDeviceTimestamp, + "Should not be waiting if there were > 0 tabs initially" + ); + } else { + ok( + TabsSetupFlowManager._noTabsVisibleFromAddedDeviceTimestamp, + "Should be waiting if there were 0 tabs initially" + ); + } + + // Add tab data from this new device and notify of the changed data + recentTabsResult.push(mockMobileTab1); + stopWaitingSpy.resetHistory(); + + info("Notifying tabs.changed with the new mobile device's tabs"); + Services.obs.notifyObservers(null, "services.sync.tabs.changed"); + + // handling the tab.change and clearing the timestamp is necessarily async + // as counting synced tabs via getRecentTabs() is async. + // There may not be any outcome depending on the tab state, so we just wait + // for stopWaitingForTabs to get called and its promise to resolve + info("Waiting for the stopWaitingSpy to be called"); + await whenResolved(stopWaitingSpy, "stopWaitingForTabs"); + await TestUtils.waitForTick(); // allow time for the telemetry event to get recorded + + info( + "We've added a synced tab and updated the tab list, got snapshotEvents:" + + JSON.stringify( + Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + false + ), + null, + 2 + ) + ); + // confirm no telemetry was recorded for tabs from the newly-added device + // as the tab list was never empty + info( + "Checking telemetry against expectedDeviceAddedTelementryEvents: " + + JSON.stringify(expectedDeviceAddedTelementryEvents, null, 2) + ); + TelemetryTestUtils.assertEvents( + expectedDeviceAddedTelementryEvents, + { category: "firefoxview" }, + { clear: true, process: "parent" } + ); + }); + sandbox.restore(); + cleanup_tab_pickup(); +} + +add_task(async function test_device_added_with_existing_tabs() { + /* Confirm that no telemetry is recorded when a new device is added while the synced tabs list has tabs */ + await test_device_added({ + initialRecentTabsResult: [mockDesktopTab1], + expectedInitialTelementryEvents: SINGLE_TAB_EVENTS, + expectedDeviceAddedTelementryEvents: DEVICE_ADDED_TABS_EVENTS, + }); +}); + +add_task(async function test_device_added_with_empty_list() { + /* Confirm that telemetry is recorded when a device is added and the synced tabs list + is empty until its tabs get synced + */ + await test_device_added({ + initialRecentTabsResult: [], + expectedInitialTelementryEvents: NO_TABS_EVENTS, + expectedDeviceAddedTelementryEvents: DEVICE_ADDED_NO_TABS_EVENTS, + }); +}); -- cgit v1.2.3