Bug 1938405: Tab interaction metrics for tab groups r=dao,sthompson,tabbrowser-reviewers

This patch adds the most basic tab interaction metrics for tab groups.
As discussed in standup, we agreed to move the `remove_` class of
metrics into its own bug due to extra complexity involved in correctly
capturing these events.

One other thing we discussed was what the scope of events should be for
the `close_` class of events, i.e. should we *only* capture an event
when someone clicks the "X" button or closes from the TOM menu, or
should we also account for things like context menus, keyboard
shortcuts, etc.? Originally we agreed to capture _all_ tab close events
and mark any source that was not one of the above mentioned two as
unknown. However, after thinking about this more, I don't believe this
is the right approach. There are many places in the codebase where a tab
is closed but not because a user deliberately did it (e.g. when moving a
tab to a new window — this is actually done by creating a new tab in a
new window and closing the old). We currently don't distinguish between
user-initiated close actions, so it would take some time to find all
these places and exclude them.

I favour an explicit inclusion approach (which is what we are doing
elsewhere). In this patch I added events for:
  - closing a tab from the "X" close button (`close_tabstrip`)
  - closing a tab from the tab context menu (`close_tabstrip`)
  - closing a tab from the TOM (`close_tabmenu`)

There are other places that could potentially be
addressed, so I suggest moving these to a follow-up bug and addressing
them for 139.

Differential Revision: https://phabricator.services.mozilla.com/D244915
This commit is contained in:
Jeremy Swinarton
2025-04-16 15:01:28 +00:00
parent 89d429fee4
commit 4a0e2c38cc
8 changed files with 225 additions and 11 deletions

View File

@@ -699,3 +699,114 @@ add_task(async function test_tabContextMenu_addTabsToGroup() {
await resetTelemetry();
});
add_task(async function test_tabInteractions() {
await resetTelemetry();
let group = await makeTabGroup();
info(
"Test that selecting a tab in a group records tab_interactions.activate"
);
const tabSelectEvent = BrowserTestUtils.waitForEvent(win, "TabSelect");
win.gBrowser.selectTabAtIndex(1);
await tabSelectEvent;
await BrowserTestUtils.waitForCondition(() => {
return Glean.tabgroup.tabInteractions.activate.testGetValue() !== null;
}, "Wait for tab_interactions.activate to be recorded");
Assert.equal(
Glean.tabgroup.tabInteractions.activate.testGetValue(),
1,
"tab_interactions.activate was recorded"
);
info(
"Test that moving an existing tab into a tab group records tab_interactions.add"
);
let tab1 = BrowserTestUtils.addTab(win.gBrowser, "https://example.com");
win.gBrowser.moveTabToGroup(tab1, group, { isUserTriggered: true });
await BrowserTestUtils.waitForCondition(() => {
return Glean.tabgroup.tabInteractions.add.testGetValue() !== null;
}, "Wait for tab_interactions.add to be recorded");
Assert.equal(
Glean.tabgroup.tabInteractions.add.testGetValue(),
1,
"tab_interactions.add was recorded"
);
info(
"Test that adding a new tab to a tab group records tab_interactions.new"
);
BrowserTestUtils.addTab(win.gBrowser, "https://example.com", {
tabGroup: group,
});
await BrowserTestUtils.waitForCondition(() => {
return Glean.tabgroup.tabInteractions.new.testGetValue() !== null;
}, "Wait for tab_interactions.new to be recorded");
Assert.equal(
Glean.tabgroup.tabInteractions.new.testGetValue(),
1,
"tab_interactions.new was recorded"
);
info("Test that moving a tab within a group calls tab_interactions.reorder");
win.gBrowser.moveTabTo(group.tabs[0], { tabIndex: 3, isUserTriggered: true });
await BrowserTestUtils.waitForCondition(() => {
return Glean.tabgroup.tabInteractions.reorder.testGetValue() !== null;
}, "Wait for tab_interactions.reorder to be recorded");
Assert.equal(
Glean.tabgroup.tabInteractions.reorder.testGetValue(),
1,
"tab_interactions.reorder was recorded"
);
info(
"Test that duplicating a tab within a group calls tab_interactions.duplicate"
);
win.gBrowser.duplicateTab(group.tabs[0], true, { index: 2 });
await BrowserTestUtils.waitForCondition(() => {
return Glean.tabgroup.tabInteractions.duplicate.testGetValue() !== null;
}, "Wait for tab_interactions.duplicate to be recorded");
Assert.equal(
Glean.tabgroup.tabInteractions.duplicate.testGetValue(),
1,
"tab_interactions.duplicate was recorded"
);
info(
"Test that closing a tab using the tab's close button calls tab_interactions.close_tabstrip"
);
group.tabs.at(-1).querySelector(".tab-close-button").click();
await BrowserTestUtils.waitForCondition(() => {
return (
Glean.tabgroup.tabInteractions.close_tabstrip.testGetValue() !== null
);
}, "Wait for tab_interactions.close_tabstrip to be recorded");
Assert.equal(
Glean.tabgroup.tabInteractions.close_tabstrip.testGetValue(),
1,
"tab_interactions.close_tabstrip was recorded"
);
info(
"Test that closing a tab from the tab overflow menu calls tab_interactions.close_tabmenu"
);
await openTabsMenu();
win.document
.querySelector(".all-tabs-item.grouped .all-tabs-close-button")
.click();
await BrowserTestUtils.waitForCondition(() => {
return Glean.tabgroup.tabInteractions.close_tabmenu.testGetValue() !== null;
}, "Wait for tab_interactions.close_tabmenu to be recorded");
Assert.equal(
Glean.tabgroup.tabInteractions.close_tabmenu.testGetValue(),
1,
"tab_interactions.close_tabmenu was recorded"
);
await closeTabsMenu();
await removeTabGroup(group);
await resetTelemetry();
});