b91b29d516
- Add UI resources and mappings for groups, windows, sessions, storage, performance, extension, and more tab/DOM/page actions. - Remove the synthetic Gateway Health operation so exposed operations match real browser-cli commands. - Document the expanded command/policy matrix and cover the new request mappings with tests. - Cap the node SVG icon at 60x60, bump the n8n package to 0.3.0, and advertise client protocol version 0.16.0.
190 lines
8.9 KiB
TypeScript
190 lines
8.9 KiB
TypeScript
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]);
|
|
});
|