summaryrefslogtreecommitdiffstats
path: root/browser/components/firefoxview/tests/browser/browser_tab_pickup_device_added_telemetry.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/firefoxview/tests/browser/browser_tab_pickup_device_added_telemetry.js')
-rw-r--r--browser/components/firefoxview/tests/browser/browser_tab_pickup_device_added_telemetry.js278
1 files changed, 278 insertions, 0 deletions
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,
+ });
+});