603 lines
18 KiB
JavaScript
603 lines
18 KiB
JavaScript
'use strict';
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
|
|
function createEventEmitter() {
|
|
const listeners = new Set();
|
|
return {
|
|
addListener(listener) {
|
|
listeners.add(listener);
|
|
},
|
|
removeListener(listener) {
|
|
listeners.delete(listener);
|
|
},
|
|
hasListener(listener) {
|
|
return listeners.has(listener);
|
|
},
|
|
emit(...args) {
|
|
for (const listener of listeners) listener(...args);
|
|
},
|
|
};
|
|
}
|
|
|
|
// Mock chrome APIs before loading the module
|
|
const mockStorage = {};
|
|
globalThis.chrome = {
|
|
storage: {
|
|
local: {
|
|
get: async (keys) => {
|
|
const key = Array.isArray(keys) ? keys[0] : keys;
|
|
return { [key]: mockStorage[key] ?? undefined };
|
|
},
|
|
set: async (items) => {
|
|
Object.assign(mockStorage, items);
|
|
},
|
|
},
|
|
},
|
|
tabs: {
|
|
group: async (opts) => 0,
|
|
get: async () => ({ id: 0, groupId: -1, windowId: 0 }),
|
|
move: async () => {},
|
|
ungroup: async () => {},
|
|
query: async (opts) => opts?.groupId != null ? [] : [],
|
|
onAttached: createEventEmitter(),
|
|
onCreated: createEventEmitter(),
|
|
onDetached: createEventEmitter(),
|
|
onMoved: createEventEmitter(),
|
|
onRemoved: createEventEmitter(),
|
|
onUpdated: createEventEmitter(),
|
|
},
|
|
tabGroups: {
|
|
query: async () => [],
|
|
update: async () => {},
|
|
get: async () => null,
|
|
onCreated: createEventEmitter(),
|
|
onRemoved: createEventEmitter(),
|
|
onUpdated: createEventEmitter(),
|
|
},
|
|
};
|
|
|
|
require('./chrome-tab-groups-sync.js');
|
|
|
|
const {
|
|
loadChromeTabGroupsSetting,
|
|
saveChromeTabGroupsSetting,
|
|
syncChromeTabGroups,
|
|
collapseChromeTabGroupsInWindow,
|
|
syncChromeTabGroupExpansionForTab,
|
|
resetChromeGroupState,
|
|
isChromeTabGroupsEnabled,
|
|
getChromeGroupCount,
|
|
populateChromeGroupMap,
|
|
queryExistingChromeGroups,
|
|
setImportMode,
|
|
subscribeToChromeTabGroupChanges,
|
|
assignGroupColor,
|
|
getGroupTitle,
|
|
} = globalThis.TabOutChromeTabGroups;
|
|
|
|
test('assignGroupColor returns blue for session groups', () => {
|
|
assert.equal(assignGroupColor('__session_group__:g1', 0), 'blue');
|
|
assert.equal(assignGroupColor('__session_group__:g2', 5), 'blue');
|
|
});
|
|
|
|
test('assignGroupColor returns yellow for landing pages', () => {
|
|
assert.equal(assignGroupColor('__landing-pages__', 0), 'yellow');
|
|
assert.equal(assignGroupColor('__landing-pages__', 3), 'yellow');
|
|
});
|
|
|
|
test('assignGroupColor cycles through colors for domain groups', () => {
|
|
const colors = ['grey', 'red', 'green', 'pink', 'purple', 'cyan', 'orange'];
|
|
colors.forEach((expected, i) => {
|
|
assert.equal(assignGroupColor('example.com', i), expected);
|
|
});
|
|
// Cycles back
|
|
assert.equal(assignGroupColor('test.com', 7), colors[0]);
|
|
});
|
|
|
|
test('getGroupTitle returns friendly domain for regular groups', () => {
|
|
const group = { domain: 'github.com', tabs: [] };
|
|
const title = getGroupTitle(group);
|
|
assert.equal(typeof title, 'string');
|
|
assert.ok(title.length > 0);
|
|
});
|
|
|
|
test('getGroupTitle uses label for custom groups', () => {
|
|
const group = { domain: 'custom-key', label: 'Work', tabs: [] };
|
|
assert.equal(getGroupTitle(group), 'Work');
|
|
});
|
|
|
|
test('getGroupTitle returns Homepages for landing pages', () => {
|
|
const group = { domain: '__landing-pages__', tabs: [] };
|
|
assert.equal(getGroupTitle(group), 'Homepages');
|
|
});
|
|
|
|
test('loadChromeTabGroupsSetting returns false by default', async () => {
|
|
resetChromeGroupState();
|
|
delete mockStorage.chromeTabGroupsEnabled;
|
|
const result = await loadChromeTabGroupsSetting();
|
|
assert.equal(result, false);
|
|
assert.equal(isChromeTabGroupsEnabled(), false);
|
|
});
|
|
|
|
test('saveChromeTabGroupsSetting persists and updates cached state', async () => {
|
|
resetChromeGroupState();
|
|
const saved = await saveChromeTabGroupsSetting(true);
|
|
assert.equal(saved, true);
|
|
assert.equal(isChromeTabGroupsEnabled(), true);
|
|
assert.equal(mockStorage.chromeTabGroupsEnabled, true);
|
|
|
|
const loaded = await loadChromeTabGroupsSetting();
|
|
assert.equal(loaded, true);
|
|
});
|
|
|
|
test('saveChromeTabGroupsSetting toggles off correctly', async () => {
|
|
resetChromeGroupState();
|
|
await saveChromeTabGroupsSetting(true);
|
|
assert.equal(isChromeTabGroupsEnabled(), true);
|
|
await saveChromeTabGroupsSetting(false);
|
|
assert.equal(isChromeTabGroupsEnabled(), false);
|
|
assert.equal(mockStorage.chromeTabGroupsEnabled, false);
|
|
});
|
|
|
|
test('syncChromeTabGroups removes groups when disabled', async () => {
|
|
resetChromeGroupState();
|
|
await saveChromeTabGroupsSetting(false);
|
|
|
|
const groups = [
|
|
{ domain: 'github.com', tabs: [{ id: 1, windowId: 1, url: 'https://github.com' }] },
|
|
];
|
|
|
|
// Should not throw when disabled
|
|
await syncChromeTabGroups(groups);
|
|
assert.equal(getChromeGroupCount(), 0);
|
|
});
|
|
|
|
test('syncChromeTabGroups creates groups when enabled', async () => {
|
|
resetChromeGroupState();
|
|
let groupCallCount = 0;
|
|
let updateCallArgs = [];
|
|
|
|
globalThis.chrome.tabs.group = async (opts) => {
|
|
groupCallCount++;
|
|
return 100 + groupCallCount;
|
|
};
|
|
|
|
globalThis.chrome.tabGroups.update = async (id, opts) => {
|
|
updateCallArgs.push({ id, ...opts });
|
|
};
|
|
|
|
globalThis.chrome.tabGroups.query = async () => [];
|
|
globalThis.chrome.tabs.query = async (opts) => [];
|
|
|
|
await saveChromeTabGroupsSetting(true);
|
|
|
|
const groups = [
|
|
{ domain: 'github.com', tabs: [{ id: 1, windowId: 1, url: 'https://github.com' }] },
|
|
{ domain: 'example.com', tabs: [{ id: 2, windowId: 1, url: 'https://example.com' }] },
|
|
];
|
|
|
|
await syncChromeTabGroups(groups);
|
|
|
|
assert.equal(groupCallCount, 2);
|
|
assert.equal(updateCallArgs.length, 2);
|
|
assert.equal(updateCallArgs[0].collapsed, true);
|
|
assert.equal(updateCallArgs[1].collapsed, true);
|
|
assert.equal(updateCallArgs[0].color, 'grey');
|
|
assert.equal(updateCallArgs[1].color, 'red');
|
|
assert.equal(getChromeGroupCount(), 2);
|
|
});
|
|
|
|
test('syncChromeTabGroups handles tabs in different windows', async () => {
|
|
resetChromeGroupState();
|
|
let groupCallCount = 0;
|
|
|
|
globalThis.chrome.tabs.group = async (opts) => {
|
|
groupCallCount++;
|
|
return 200 + groupCallCount;
|
|
};
|
|
|
|
globalThis.chrome.tabGroups.update = async () => {};
|
|
globalThis.chrome.tabGroups.query = async () => [];
|
|
globalThis.chrome.tabs.query = async (opts) => [];
|
|
|
|
await saveChromeTabGroupsSetting(true);
|
|
|
|
const groups = [
|
|
{
|
|
domain: 'github.com',
|
|
tabs: [
|
|
{ id: 1, windowId: 1, url: 'https://github.com' },
|
|
{ id: 3, windowId: 2, url: 'https://github.com/other' },
|
|
],
|
|
},
|
|
];
|
|
|
|
await syncChromeTabGroups(groups);
|
|
|
|
// Should create 1 group for each window → 2 total chrome.tabs.group calls
|
|
assert.equal(groupCallCount, 2);
|
|
});
|
|
|
|
test('syncChromeTabGroups reorders tabs inside a chrome group to match desired order', async () => {
|
|
resetChromeGroupState();
|
|
const moveCalls = [];
|
|
|
|
globalThis.chrome.tabs.group = async () => 901;
|
|
globalThis.chrome.tabs.move = async (tabId, opts) => {
|
|
moveCalls.push({ tabId, ...opts });
|
|
};
|
|
globalThis.chrome.tabGroups.update = async () => {};
|
|
globalThis.chrome.tabGroups.query = async () => [];
|
|
globalThis.chrome.tabs.query = async (opts) => {
|
|
if (opts?.groupId === 901) {
|
|
return [
|
|
{ id: 2, groupId: 901, windowId: 1, index: 7 },
|
|
{ id: 1, groupId: 901, windowId: 1, index: 8 },
|
|
];
|
|
}
|
|
return [];
|
|
};
|
|
|
|
await saveChromeTabGroupsSetting(true);
|
|
|
|
await syncChromeTabGroups([
|
|
{
|
|
domain: 'github.com',
|
|
tabs: [
|
|
{ id: 1, windowId: 1, url: 'https://github.com/1' },
|
|
{ id: 2, windowId: 1, url: 'https://github.com/2' },
|
|
],
|
|
},
|
|
]);
|
|
|
|
assert.deepEqual(moveCalls, [
|
|
{ tabId: 1, windowId: 1, index: 7 },
|
|
{ tabId: 2, windowId: 1, index: 8 },
|
|
]);
|
|
});
|
|
|
|
test('syncChromeTabGroups reorders grouped tabs across groups to match desired group order in a window', async () => {
|
|
resetChromeGroupState();
|
|
const moveCalls = [];
|
|
|
|
globalThis.chrome.tabs.group = async (opts) => opts.groupId ?? 0;
|
|
globalThis.chrome.tabs.move = async (tabId, opts) => {
|
|
moveCalls.push({ tabId, ...opts });
|
|
};
|
|
globalThis.chrome.tabGroups.update = async () => {};
|
|
globalThis.chrome.tabGroups.query = async () => [
|
|
{ id: 501, title: 'GitHub', color: 'grey', windowId: 1 },
|
|
{ id: 502, title: 'Bilibili', color: 'red', windowId: 1 },
|
|
];
|
|
globalThis.chrome.tabs.query = async (opts) => {
|
|
if (opts?.groupId === 501) {
|
|
return [
|
|
{ id: 11, groupId: 501, windowId: 1, index: 8 },
|
|
];
|
|
}
|
|
if (opts?.groupId === 502) {
|
|
return [
|
|
{ id: 22, groupId: 502, windowId: 1, index: 5 },
|
|
];
|
|
}
|
|
if (opts?.windowId === 1) {
|
|
return [
|
|
{ id: 22, windowId: 1, index: 5 },
|
|
{ id: 11, windowId: 1, index: 8 },
|
|
];
|
|
}
|
|
return [];
|
|
};
|
|
|
|
await saveChromeTabGroupsSetting(true);
|
|
await populateChromeGroupMap([
|
|
{ virtualGroupKey: 'github.com', windowId: 1, chromeGroupId: 501 },
|
|
{ virtualGroupKey: 'bilibili.com', windowId: 1, chromeGroupId: 502 },
|
|
]);
|
|
|
|
await syncChromeTabGroups([
|
|
{ domain: 'github.com', tabs: [{ id: 11, windowId: 1, url: 'https://github.com' }] },
|
|
{ domain: 'bilibili.com', tabs: [{ id: 22, windowId: 1, url: 'https://bilibili.com' }] },
|
|
]);
|
|
|
|
assert.deepEqual(
|
|
moveCalls.slice(-2),
|
|
[
|
|
{ tabId: 11, windowId: 1, index: 5 },
|
|
{ tabId: 22, windowId: 1, index: 6 },
|
|
]
|
|
);
|
|
});
|
|
|
|
test('syncChromeTabGroups cleans up when disabled after being enabled', async () => {
|
|
resetChromeGroupState();
|
|
let ungroupCalls = [];
|
|
|
|
globalThis.chrome.tabs.ungroup = async (tabIds) => {
|
|
ungroupCalls.push(tabIds);
|
|
};
|
|
|
|
globalThis.chrome.tabs.group = async (opts) => 300;
|
|
globalThis.chrome.tabGroups.update = async () => {};
|
|
globalThis.chrome.tabGroups.query = async () => [];
|
|
globalThis.chrome.tabs.query = async (opts) => [];
|
|
|
|
await saveChromeTabGroupsSetting(true);
|
|
|
|
const groups = [
|
|
{ domain: 'test.com', tabs: [{ id: 10, windowId: 1, url: 'https://test.com' }] },
|
|
];
|
|
|
|
await syncChromeTabGroups(groups);
|
|
assert.ok(getChromeGroupCount() > 0);
|
|
|
|
// Now disable
|
|
await saveChromeTabGroupsSetting(false);
|
|
await syncChromeTabGroups(groups);
|
|
|
|
assert.equal(getChromeGroupCount(), 0);
|
|
});
|
|
|
|
test('syncChromeTabGroups handles empty tabs gracefully', async () => {
|
|
resetChromeGroupState();
|
|
await saveChromeTabGroupsSetting(true);
|
|
await syncChromeTabGroups([]);
|
|
assert.equal(getChromeGroupCount(), 0);
|
|
});
|
|
|
|
test('syncChromeTabGroups handles groups with no tabs', async () => {
|
|
resetChromeGroupState();
|
|
await saveChromeTabGroupsSetting(true);
|
|
await syncChromeTabGroups([{ domain: 'empty.com', tabs: [] }]);
|
|
assert.equal(getChromeGroupCount(), 0);
|
|
});
|
|
|
|
test('populateChromeGroupMap adds mappings to internal state', () => {
|
|
resetChromeGroupState();
|
|
populateChromeGroupMap([
|
|
{ virtualGroupKey: 'github.com', windowId: 1, chromeGroupId: 101 },
|
|
{ virtualGroupKey: 'github.com', windowId: 2, chromeGroupId: 102 },
|
|
]);
|
|
assert.equal(getChromeGroupCount(), 1);
|
|
});
|
|
|
|
test('queryExistingChromeGroups returns groups from chrome.tabGroups.query', async () => {
|
|
globalThis.chrome.tabGroups.query = async () => [
|
|
{ id: 1, title: 'Work', color: 'blue' },
|
|
{ id: 2, title: 'Research', color: 'red' },
|
|
];
|
|
const groups = await queryExistingChromeGroups();
|
|
assert.equal(groups.length, 2);
|
|
assert.equal(groups[0].title, 'Work');
|
|
});
|
|
|
|
test('syncChromeTabGroups reuses chromeGroupMap populated by populateChromeGroupMap', async () => {
|
|
resetChromeGroupState();
|
|
let lastGroupCall = null;
|
|
|
|
globalThis.chrome.tabs.group = async (opts) => {
|
|
lastGroupCall = opts;
|
|
return 500;
|
|
};
|
|
globalThis.chrome.tabGroups.update = async () => {};
|
|
globalThis.chrome.tabGroups.query = async () => [{ id: 500, title: 'GitHub', color: 'grey' }];
|
|
globalThis.chrome.tabs.query = async (opts) => [];
|
|
|
|
await saveChromeTabGroupsSetting(true);
|
|
|
|
// Pre-populate the mapping (simulating "pull from Chrome groups" scenario)
|
|
populateChromeGroupMap([
|
|
{ virtualGroupKey: 'github.com', windowId: 1, chromeGroupId: 500 },
|
|
]);
|
|
|
|
const groups = [
|
|
{ domain: 'github.com', tabs: [{ id: 10, windowId: 1, url: 'https://github.com' }] },
|
|
];
|
|
|
|
await syncChromeTabGroups(groups);
|
|
|
|
// Should have reused the existing group — called with groupId (reuse path), not tabIds (create path)
|
|
assert.equal(lastGroupCall.groupId, 500);
|
|
assert.deepEqual(lastGroupCall.tabIds, [10]);
|
|
});
|
|
|
|
test('syncChromeTabGroups only removes obsolete mappings in synced windows', async () => {
|
|
resetChromeGroupState();
|
|
const ungroupedTabIds = [];
|
|
|
|
globalThis.chrome.tabs.group = async (opts) => opts.groupId ?? 700;
|
|
globalThis.chrome.tabs.ungroup = async (tabIds) => {
|
|
ungroupedTabIds.push(...tabIds);
|
|
};
|
|
globalThis.chrome.tabGroups.update = async () => {};
|
|
globalThis.chrome.tabGroups.query = async () => [
|
|
{ id: 501, title: 'GitHub', color: 'grey', windowId: 1 },
|
|
{ id: 502, title: 'GitHub', color: 'grey', windowId: 2 },
|
|
{ id: 503, title: 'Docs', color: 'blue', windowId: 2 },
|
|
];
|
|
globalThis.chrome.tabs.query = async (opts) => {
|
|
if (opts?.groupId === 502) return [{ id: 202, windowId: 2 }];
|
|
if (opts?.groupId === 503) return [{ id: 203, windowId: 2 }];
|
|
return [];
|
|
};
|
|
|
|
await saveChromeTabGroupsSetting(true);
|
|
populateChromeGroupMap([
|
|
{ virtualGroupKey: 'github.com', windowId: 1, chromeGroupId: 501 },
|
|
{ virtualGroupKey: 'github.com', windowId: 2, chromeGroupId: 502 },
|
|
{ virtualGroupKey: 'docs.example', windowId: 2, chromeGroupId: 503 },
|
|
]);
|
|
|
|
await syncChromeTabGroups([
|
|
{ domain: 'github.com', tabs: [{ id: 101, windowId: 1, url: 'https://github.com' }] },
|
|
]);
|
|
|
|
assert.deepEqual(ungroupedTabIds, []);
|
|
assert.equal(getChromeGroupCount(), 2);
|
|
});
|
|
|
|
test('syncChromeTabGroups in import mode skips creating new groups for ungrouped tabs', async () => {
|
|
resetChromeGroupState();
|
|
let createCalls = 0;
|
|
let reuseCalls = 0;
|
|
|
|
globalThis.chrome.tabs.group = async (opts) => {
|
|
if (opts.groupId != null) {
|
|
reuseCalls++;
|
|
return opts.groupId;
|
|
}
|
|
createCalls++;
|
|
return 600 + createCalls;
|
|
};
|
|
globalThis.chrome.tabGroups.update = async () => {};
|
|
globalThis.chrome.tabGroups.query = async () => [{ id: 500, title: 'Work', color: 'blue' }];
|
|
globalThis.chrome.tabs.query = async (opts) => [];
|
|
|
|
await saveChromeTabGroupsSetting(true);
|
|
|
|
// Simulate import: populate chromeGroupMap with existing Chrome groups
|
|
populateChromeGroupMap([
|
|
{ virtualGroupKey: '__session_group__:g1', windowId: 1, chromeGroupId: 500 },
|
|
]);
|
|
|
|
// Enable import mode: only reuse, don't create
|
|
setImportMode(true);
|
|
|
|
const groups = [
|
|
{ domain: '__session_group__:g1', label: 'Work', tabs: [{ id: 1, windowId: 1, url: 'https://a.com' }] },
|
|
{ domain: 'github.com', tabs: [{ id: 5, windowId: 1, url: 'https://github.com' }] },
|
|
];
|
|
|
|
await syncChromeTabGroups(groups);
|
|
|
|
// Work group reused existing Chrome group
|
|
assert.equal(reuseCalls, 1);
|
|
// github.com was SKIPPED (import mode, no matching Chrome group)
|
|
assert.equal(createCalls, 0);
|
|
|
|
// Disable import mode and sync again — now github.com should get a new group
|
|
setImportMode(false);
|
|
await syncChromeTabGroups(groups);
|
|
assert.equal(createCalls, 1);
|
|
});
|
|
|
|
test('subscribeToChromeTabGroupChanges notifies on external Chrome group changes', async () => {
|
|
resetChromeGroupState();
|
|
await saveChromeTabGroupsSetting(true);
|
|
|
|
const events = [];
|
|
const unsubscribe = subscribeToChromeTabGroupChanges(event => {
|
|
events.push(event);
|
|
});
|
|
|
|
globalThis.chrome.tabGroups.onUpdated.emit(501, { title: 'Work' });
|
|
|
|
assert.equal(events.length, 1);
|
|
assert.equal(events[0].source, 'tabGroups.onUpdated');
|
|
|
|
unsubscribe();
|
|
});
|
|
|
|
test('subscribeToChromeTabGroupChanges ignores collapse-only group updates', async () => {
|
|
resetChromeGroupState();
|
|
await saveChromeTabGroupsSetting(true);
|
|
|
|
const events = [];
|
|
const unsubscribe = subscribeToChromeTabGroupChanges(event => {
|
|
events.push(event);
|
|
});
|
|
|
|
globalThis.chrome.tabGroups.onUpdated.emit(501, { collapsed: true });
|
|
|
|
assert.equal(events.length, 0);
|
|
|
|
unsubscribe();
|
|
});
|
|
|
|
test('subscribeToChromeTabGroupChanges notifies when a grouped tab is moved', async () => {
|
|
resetChromeGroupState();
|
|
await saveChromeTabGroupsSetting(true);
|
|
|
|
const events = [];
|
|
globalThis.chrome.tabs.get = async (tabId) => ({
|
|
id: tabId,
|
|
groupId: 501,
|
|
windowId: 1,
|
|
});
|
|
|
|
const unsubscribe = subscribeToChromeTabGroupChanges(event => {
|
|
events.push(event);
|
|
});
|
|
|
|
globalThis.chrome.tabs.onMoved.emit(42, { windowId: 1, fromIndex: 3, toIndex: 6 });
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
|
|
assert.equal(events.length, 1);
|
|
assert.equal(events[0].source, 'tabs.onMoved');
|
|
assert.equal(events[0].tabId, 42);
|
|
assert.equal(events[0].tab.groupId, 501);
|
|
|
|
unsubscribe();
|
|
});
|
|
|
|
test('syncChromeTabGroupExpansionForTab expands target group and collapses sibling groups in same window', async () => {
|
|
resetChromeGroupState();
|
|
await saveChromeTabGroupsSetting(true);
|
|
|
|
const updateCalls = [];
|
|
globalThis.chrome.tabGroups.query = async () => [
|
|
{ id: 101, windowId: 1, collapsed: true },
|
|
{ id: 102, windowId: 1, collapsed: false },
|
|
{ id: 103, windowId: 2, collapsed: false },
|
|
];
|
|
globalThis.chrome.tabGroups.update = async (id, opts) => {
|
|
updateCalls.push({ id, ...opts });
|
|
};
|
|
|
|
await syncChromeTabGroupExpansionForTab({ groupId: 101, windowId: 1 });
|
|
|
|
assert.deepEqual(updateCalls, [
|
|
{ id: 101, collapsed: false },
|
|
{ id: 102, collapsed: true },
|
|
]);
|
|
});
|
|
|
|
test('syncChromeTabGroupExpansionForTab skips work when Chrome sync is disabled', async () => {
|
|
resetChromeGroupState();
|
|
await saveChromeTabGroupsSetting(false);
|
|
|
|
let queryCount = 0;
|
|
globalThis.chrome.tabGroups.query = async () => {
|
|
queryCount++;
|
|
return [];
|
|
};
|
|
|
|
await syncChromeTabGroupExpansionForTab({ groupId: 101, windowId: 1 });
|
|
|
|
assert.equal(queryCount, 0);
|
|
});
|
|
|
|
test('collapseChromeTabGroupsInWindow collapses every expanded group in the target window', async () => {
|
|
resetChromeGroupState();
|
|
await saveChromeTabGroupsSetting(true);
|
|
|
|
const updateCalls = [];
|
|
globalThis.chrome.tabGroups.query = async () => [
|
|
{ id: 101, windowId: 1, collapsed: false },
|
|
{ id: 102, windowId: 1, collapsed: true },
|
|
{ id: 103, windowId: 2, collapsed: false },
|
|
];
|
|
globalThis.chrome.tabGroups.update = async (id, opts) => {
|
|
updateCalls.push({ id, ...opts });
|
|
};
|
|
|
|
await collapseChromeTabGroupsInWindow(1);
|
|
|
|
assert.deepEqual(updateCalls, [
|
|
{ id: 101, collapsed: true },
|
|
]);
|
|
});
|