import assert from 'node:assert/strict'; import { test } from 'node:test'; import { buildCommand, parseTabIds } from '../nodes/BrowserCli/request'; test('tab:list maps to tabs.list', () => { assert.deepEqual(buildCommand('tab', 'list', {}), { command: 'tabs.list', args: {} }); }); test('client:list maps to clients.list', () => { assert.deepEqual(buildCommand('client', 'list', {}), { command: 'clients.list', args: {} }); }); test('tab:open sends navigate.open with background derived from focus', () => { const bg = buildCommand('tab', 'open', { url: 'https://example.com', focus: false }); assert.deepEqual(bg, { command: 'navigate.open', args: { url: 'https://example.com', focus: false, background: true }, }); const fg = buildCommand('tab', 'open', { url: 'https://example.com', focus: true }); assert.equal(fg.args.focus, true); assert.equal(fg.args.background, false, 'focused open sends background:false (matches SDK)'); }); test('tab:close supports ids, inactive, duplicates', () => { assert.deepEqual(buildCommand('tab', 'close', { mode: 'ids', tabIds: '12, 34' }), { command: 'tabs.close', args: { tabIds: [12, 34] }, }); assert.deepEqual(buildCommand('tab', 'close', { mode: 'inactive' }).args, { inactive: true }); assert.deepEqual(buildCommand('tab', 'close', { mode: 'duplicates' }).args, { duplicates: true }); }); test('dom:type sends dom.type with selector + text', () => { assert.deepEqual(buildCommand('dom', 'type', { selector: '#q', text: 'hello' }), { command: 'dom.type', args: { selector: '#q', text: 'hello' }, }); }); test('dom:eval includes tabId only when set', () => { assert.deepEqual(buildCommand('dom', 'eval', { code: 'return 1', tabId: 0 }).args, { code: 'return 1' }); assert.deepEqual(buildCommand('dom', 'eval', { code: 'return 1', tabId: 7 }).args, { code: 'return 1', tabId: 7 }); }); test('page:extractMarkdown omits empty selector', () => { assert.deepEqual(buildCommand('page', 'extractMarkdown', { selector: '' }).args, {}); assert.deepEqual(buildCommand('page', 'extractMarkdown', { selector: 'main' }).args, { selector: 'main' }); }); test('command:execute passes command and args through', () => { assert.deepEqual(buildCommand('command', 'execute', { command: 'tabs.query', args: { search: 'docs' } }), { command: 'tabs.query', args: { search: 'docs' }, }); }); test('tab read ops map to safe commands', () => { assert.deepEqual(buildCommand('tab', 'query', { search: 'docs' }), { command: 'tabs.query', args: { search: 'docs' } }); assert.deepEqual(buildCommand('tab', 'filter', { pattern: '*.dev/*' }), { command: 'tabs.filter', args: { pattern: '*.dev/*' } }); assert.deepEqual(buildCommand('tab', 'count', { pattern: '' }).args, {}, 'empty pattern is dropped'); assert.deepEqual(buildCommand('tab', 'get', { tabId: 0 }).args, {}, 'tabId 0 means active tab'); assert.deepEqual(buildCommand('tab', 'get', { tabId: 5 }), { command: 'tabs.status', args: { tabId: 5 } }); assert.deepEqual(buildCommand('tab', 'activeInWindow', { windowId: 3 }), { command: 'tabs.active_in_window', args: { windowId: 3 }, }); }); test('tab control ops map to navigate/tabs commands', () => { assert.deepEqual(buildCommand('tab', 'activate', { tabId: 7 }), { command: 'tabs.active', args: { tabId: 7 } }); assert.deepEqual(buildCommand('tab', 'navigateTo', { tabId: 7, url: 'https://x.dev' }), { command: 'navigate.to', args: { tabId: 7, url: 'https://x.dev' }, }); assert.deepEqual(buildCommand('tab', 'reload', { tabId: 0 }), { command: 'navigate.reload', args: {} }); assert.deepEqual(buildCommand('tab', 'back', { tabId: 0 }).command, 'navigate.back'); assert.deepEqual(buildCommand('tab', 'mute', { tabId: 2 }), { command: 'tabs.mute', args: { tabId: 2 } }); assert.deepEqual(buildCommand('tab', 'pin', { tabId: 0 }), { command: 'tabs.pin', args: {} }); }); test('tab move keeps index 0 but drops active tabId fallback for window', () => { assert.deepEqual(buildCommand('tab', 'move', { tabId: 4, windowId: 0, index: 0 }), { command: 'tabs.move', args: { tabId: 4, index: 0 }, }); assert.deepEqual(buildCommand('tab', 'move', { tabId: 4, windowId: 9, index: '' }).args, { tabId: 4, windowId: 9 }); }); test('tab rearrange ops default gentleMode and sort key', () => { assert.deepEqual(buildCommand('tab', 'dedupe', {}), { command: 'tabs.dedupe', args: { gentleMode: 'auto' } }); assert.deepEqual(buildCommand('tab', 'sort', { by: 'title' }), { command: 'tabs.sort', args: { by: 'title', gentleMode: 'auto' }, }); assert.deepEqual(buildCommand('tab', 'mergeWindows', {}).command, 'tabs.merge_windows'); }); test('tab screenshot includes quality only when set', () => { assert.deepEqual(buildCommand('tab', 'screenshot', { tabId: 0, format: 'png', quality: '' }).args, { format: 'png' }); assert.deepEqual(buildCommand('tab', 'screenshot', { tabId: 1, format: 'jpeg', quality: 80 }).args, { tabId: 1, format: 'jpeg', quality: 80, }); }); test('dom interaction ops map to dom.* commands', () => { assert.deepEqual(buildCommand('dom', 'attr', { selector: 'a', attr: 'href' }), { command: 'dom.attr', args: { selector: 'a', attr: 'href' }, }); assert.deepEqual(buildCommand('dom', 'select', { selector: '#s', value: 'v' }), { command: 'dom.select', args: { selector: '#s', value: 'v' }, }); assert.deepEqual(buildCommand('dom', 'key', { key: 'Enter', selector: '' }).args, { key: 'Enter' }); assert.deepEqual(buildCommand('dom', 'scroll', { selector: '', x: '', y: 500 }).args, { y: 500 }); assert.deepEqual(buildCommand('dom', 'exists', { selector: '#x' }), { command: 'dom.exists', args: { selector: '#x' } }); }); test('page extractJson sends selector', () => { assert.deepEqual(buildCommand('page', 'extractJson', { selector: 'script' }), { command: 'extract.json', args: { selector: 'script' }, }); }); test('group ops map to group.* commands', () => { assert.deepEqual(buildCommand('group', 'create', { name: 'Research' }), { command: 'group.open', args: { name: 'Research' } }); assert.deepEqual(buildCommand('group', 'tabs', { groupId: 3 }), { command: 'group.tabs', args: { groupId: 3 } }); assert.deepEqual(buildCommand('group', 'addTab', { group: 'Research', url: '' }).args, { group: 'Research' }); assert.deepEqual(buildCommand('group', 'move', { group: '5', direction: 'backward' }), { command: 'group.move', args: { group: '5', forward: false, backward: true }, }); assert.deepEqual(buildCommand('group', 'close', { groupId: 2, gentleMode: 'off' }).args, { groupId: 2, gentleMode: 'off' }); }); test('window ops map to windows.* commands', () => { assert.deepEqual(buildCommand('window', 'open', { url: '' }), { command: 'windows.open', args: {} }); assert.deepEqual(buildCommand('window', 'rename', { windowId: 1, name: 'Work' }), { command: 'windows.rename', args: { windowId: 1, name: 'Work' }, }); }); test('session ops map to session.* commands', () => { assert.deepEqual(buildCommand('session', 'save', { name: 'morning' }), { command: 'session.save', args: { name: 'morning' } }); assert.deepEqual(buildCommand('session', 'export', { name: '' }), { command: 'session.export', args: {} }); assert.deepEqual(buildCommand('session', 'diff', { nameA: 'a', nameB: 'b' }).args, { nameA: 'a', nameB: 'b' }); assert.deepEqual(buildCommand('session', 'autoSave', { enabled: false }), { command: 'session.auto_save', args: { enabled: false }, }); }); test('storage ops default to local and drop active tabId', () => { assert.deepEqual(buildCommand('storage', 'get', { key: 'token', storeType: 'local', tabId: 0 }), { command: 'storage.get', args: { key: 'token', type: 'local' }, }); assert.deepEqual(buildCommand('storage', 'set', { key: 'k', value: 'v', storeType: 'session', tabId: 4 }), { command: 'storage.set', args: { key: 'k', value: 'v', type: 'session', tabId: 4 }, }); assert.deepEqual(buildCommand('storage', 'get', { key: '', storeType: 'local', tabId: 0 }).args, { type: 'local' }); }); test('perf and extension ops map to safe/control commands', () => { assert.deepEqual(buildCommand('perf', 'status', {}), { command: 'perf.status', args: {} }); assert.deepEqual(buildCommand('extension', 'capabilities', {}), { command: 'extension.capabilities', args: {} }); assert.deepEqual(buildCommand('extension', 'reload', {}), { command: 'extension.reload', args: {} }); }); test('unknown operation throws', () => { assert.throws(() => buildCommand('tab', 'nope', {}), /Unsupported operation/); assert.throws(() => buildCommand('connection', 'health', {}), /Unsupported operation/); assert.throws(() => buildCommand('gateway', 'health', {}), /Unsupported operation/); }); test('parseTabIds accepts array, json, and delimited strings', () => { assert.deepEqual(parseTabIds([1, 2, 3]), [1, 2, 3]); assert.deepEqual(parseTabIds('[4, 5]'), [4, 5]); assert.deepEqual(parseTabIds('6, 7 8'), [6, 7, 8]); assert.deepEqual(parseTabIds(''), []); assert.deepEqual(parseTabIds(9), [9]); });