Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test runs only with pattern: os != 'android'
- Manifest: browser/components/tabbrowser/test/xpcshell/smarttabgrouping/xpcshell.toml
/* Any copyright is dedicated to the Public Domain.
const { SmartTabGroupingManager } = ChromeUtils.importESModule(
"moz-src:///browser/components/tabbrowser/SmartTabGrouping.sys.mjs"
);
// Simple helper to construct tab-like objects for tests.
function makeTab(id, { pinned = false, hasURL = true } = {}) {
return {
id,
label: `Tab ${id}`,
pinned,
linkedBrowser: hasURL
: null,
};
}
add_task(
function test_tabs_to_process_includes_group_first_and_respects_limit() {
const manager = new SmartTabGroupingManager();
const groupTab1 = makeTab("group-1");
const groupTab2 = makeTab("group-2");
const other1 = makeTab("other-1");
const other2 = makeTab("other-2");
const other3 = makeTab("other-3");
const tabsInGroup = [groupTab1, groupTab2];
const allTabs = [groupTab1, other1, groupTab2, other2, other3];
// Limit 4: should be [group tabs] first, then fill with window tabs.
let resultIds = manager
.getTabsToProcess(tabsInGroup, allTabs, 4)
.map(t => t.id);
Assert.deepEqual(
resultIds,
["group-1", "group-2", "other-1", "other-2"],
"Group tabs should come first, then window tabs, up to the limit"
);
// Limit 2: should only include the group tabs.
resultIds = manager
.getTabsToProcess(tabsInGroup, allTabs, 2)
.map(t => t.id);
Assert.deepEqual(
resultIds,
["group-1", "group-2"],
"When limit equals number of group tabs, only group tabs are returned"
);
}
);
add_task(
function test_tabs_to_process_limits_anchor_tabs_to_max_nn_grouped_tabs() {
const manager = new SmartTabGroupingManager();
// More than MAX_NN_GROUPED_TABS (4) tabs in the group.
const groupTabs = [];
for (let i = 0; i < 6; i++) {
groupTabs.push(makeTab(`group-${i}`));
}
const extraTab = makeTab("other");
const allTabs = [...groupTabs, extraTab];
const resultIds = manager
.getTabsToProcess(groupTabs, allTabs, 10)
.map(t => t.id);
Assert.deepEqual(
resultIds,
["group-0", "group-1", "group-2", "other"],
"Only the first MAX_NN_GROUPED_TABS group tabs should be kept, then window tabs"
);
}
);
add_task(function test_tabs_to_process_deduplicates_group_tabs_in_all_tabs() {
const manager = new SmartTabGroupingManager();
const groupTab1 = makeTab("group-1");
const groupTab2 = makeTab("group-2");
const other1 = makeTab("other-1");
// group tabs also appear in allTabs
const tabsInGroup = [groupTab1, groupTab2];
const allTabs = [groupTab1, groupTab2, other1];
const resultIds = manager
.getTabsToProcess(tabsInGroup, allTabs, 10)
.map(t => t.id);
Assert.deepEqual(
resultIds,
["group-1", "group-2", "other-1"],
"Tabs already in the group should not be duplicated when iterating allTabs"
);
});
add_task(function test_tabs_to_process_more_non_group_tabs_than_limit() {
const manager = new SmartTabGroupingManager();
const groupTab1 = makeTab("group-1");
const groupTab2 = makeTab("group-2");
const tabsInGroup = [groupTab1, groupTab2];
// 10 non-group tabs
const nonGroupTabs = [];
for (let i = 0; i < 10; i++) {
nonGroupTabs.push(makeTab(`other-${i}`));
}
const allTabs = [groupTab1, ...nonGroupTabs, groupTab2];
// Limit 6 => 2 group tabs + first 4 non-group tabs (in allTabs order)
const resultIds = manager
.getTabsToProcess(tabsInGroup, allTabs, 6)
.map(t => t.id);
Assert.deepEqual(
resultIds,
["group-1", "group-2", "other-0", "other-1", "other-2", "other-3"],
"When there are more non-group tabs than the limit, only the first ones (by window order) are included after the group tabs"
);
});
add_task(function test_tabs_to_process_with_no_group_tabs() {
const manager = new SmartTabGroupingManager();
const allTabs = [];
for (let i = 0; i < 8; i++) {
allTabs.push(makeTab(`tab-${i}`));
}
const tabsInGroup = [];
const resultIds = manager
.getTabsToProcess(tabsInGroup, allTabs, 5)
.map(t => t.id);
Assert.deepEqual(
resultIds,
["tab-0", "tab-1", "tab-2", "tab-3", "tab-4"],
"When there are no group tabs, we should just take up to the limit from window tabs"
);
});
add_task(function test_tabs_to_process_uses_default_max_limit() {
const manager = new SmartTabGroupingManager();
// 350 non-group tabs, all valid
const allTabs = [];
for (let i = 0; i < 350; i++) {
allTabs.push(makeTab(`tab-${i}`));
}
const tabsInGroup = [];
// Use default max_limit_to_process (should be 100)
const result = manager.getTabsToProcess(tabsInGroup, allTabs);
const resultIds = result.map(t => t.id);
Assert.equal(
resultIds.length,
300,
"Default max limit should cap the number of processed tabs to 300"
);
Assert.deepEqual(
resultIds.slice(0, 5),
["tab-0", "tab-1", "tab-2", "tab-3", "tab-4"],
"Default behavior should preserve window order for the first tabs"
);
});
add_task(function test_tabs_to_process_all_tabs_filtered_out() {
const manager = new SmartTabGroupingManager();
// All pinned
const pinned1 = makeTab("pinned-1", { pinned: true });
const pinned2 = makeTab("pinned-2", { pinned: true });
// All missing URL
const noUrl1 = makeTab("no-url-1", { hasURL: false });
const noUrl2 = makeTab("no-url-2", { hasURL: false });
const tabsInGroup = [pinned1];
const allTabs = [pinned1, pinned2, noUrl1, noUrl2];
const result = manager.getTabsToProcess(tabsInGroup, allTabs, 10);
Assert.deepEqual(
result,
[],
"If all tabs are filtered out (pinned or no URL), we should return an empty list"
);
});
add_task(function test_tabs_to_process_group_tabs_not_in_allTabs() {
const manager = new SmartTabGroupingManager();
const externalGroupTab = makeTab("external-group"); // not in allTabs
const other1 = makeTab("other-1");
const other2 = makeTab("other-2");
const tabsInGroup = [externalGroupTab];
const allTabs = [other1, other2];
const resultIds = manager
.getTabsToProcess(tabsInGroup, allTabs, 3)
.map(t => t.id);
Assert.deepEqual(
resultIds,
["external-group", "other-1", "other-2"],
"Tabs in the group should still be included even if they don't appear in allTabs"
);
});