Files
chrome-use/src/protocol.test.ts
T
2026-01-11 23:27:49 -06:00

612 lines
19 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { parseCommand } from './protocol.js';
// Helper to create command JSON string
const cmd = (obj: object) => JSON.stringify(obj);
describe('parseCommand', () => {
describe('navigation', () => {
it('should parse navigate command', () => {
const result = parseCommand(cmd({ id: '1', action: 'navigate', url: 'https://example.com' }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.action).toBe('navigate');
expect(result.command.url).toBe('https://example.com');
}
});
it('should reject navigate without url', () => {
const result = parseCommand(cmd({ id: '1', action: 'navigate' }));
expect(result.success).toBe(false);
});
it('should parse back command', () => {
const result = parseCommand(cmd({ id: '1', action: 'back' }));
expect(result.success).toBe(true);
});
it('should parse forward command', () => {
const result = parseCommand(cmd({ id: '1', action: 'forward' }));
expect(result.success).toBe(true);
});
it('should parse reload command', () => {
const result = parseCommand(cmd({ id: '1', action: 'reload' }));
expect(result.success).toBe(true);
});
});
describe('click', () => {
it('should parse click command', () => {
const result = parseCommand(cmd({ id: '1', action: 'click', selector: '#btn' }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.action).toBe('click');
expect(result.command.selector).toBe('#btn');
}
});
it('should reject click without selector', () => {
const result = parseCommand(cmd({ id: '1', action: 'click' }));
expect(result.success).toBe(false);
});
});
describe('type', () => {
it('should parse type command', () => {
const result = parseCommand(
cmd({ id: '1', action: 'type', selector: '#input', text: 'hello' })
);
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.action).toBe('type');
expect(result.command.selector).toBe('#input');
expect(result.command.text).toBe('hello');
}
});
});
describe('fill', () => {
it('should parse fill command', () => {
const result = parseCommand(
cmd({ id: '1', action: 'fill', selector: '#input', value: 'hello' })
);
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.action).toBe('fill');
expect(result.command.value).toBe('hello');
}
});
});
describe('wait', () => {
it('should parse wait with selector', () => {
const result = parseCommand(cmd({ id: '1', action: 'wait', selector: '#loading' }));
expect(result.success).toBe(true);
});
it('should parse wait with timeout', () => {
const result = parseCommand(cmd({ id: '1', action: 'wait', timeout: 5000 }));
expect(result.success).toBe(true);
});
it('should parse wait with text', () => {
const result = parseCommand(cmd({ id: '1', action: 'wait', text: 'Welcome' }));
expect(result.success).toBe(true);
});
});
describe('screenshot', () => {
it('should parse screenshot command', () => {
const result = parseCommand(cmd({ id: '1', action: 'screenshot', path: 'test.png' }));
expect(result.success).toBe(true);
});
it('should parse screenshot with fullPage', () => {
const result = parseCommand(cmd({ id: '1', action: 'screenshot', fullPage: true }));
expect(result.success).toBe(true);
});
});
describe('cookies', () => {
it('should parse cookies_get', () => {
const result = parseCommand(cmd({ id: '1', action: 'cookies_get' }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.action).toBe('cookies_get');
}
});
it('should parse cookies_get with urls filter', () => {
const result = parseCommand(
cmd({ id: '1', action: 'cookies_get', urls: ['https://example.com'] })
);
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.urls).toEqual(['https://example.com']);
}
});
it('should parse cookies_set with minimal cookie', () => {
const result = parseCommand(
cmd({
id: '1',
action: 'cookies_set',
cookies: [{ name: 'session', value: 'abc123' }],
})
);
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.action).toBe('cookies_set');
expect(result.command.cookies).toHaveLength(1);
expect(result.command.cookies[0].name).toBe('session');
expect(result.command.cookies[0].value).toBe('abc123');
}
});
it('should parse cookies_set with full cookie options', () => {
const result = parseCommand(
cmd({
id: '1',
action: 'cookies_set',
cookies: [
{
name: 'auth',
value: 'token123',
domain: 'example.com',
path: '/',
expires: Date.now() / 1000 + 3600,
httpOnly: true,
secure: true,
sameSite: 'Strict',
},
],
})
);
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.cookies[0].httpOnly).toBe(true);
expect(result.command.cookies[0].secure).toBe(true);
expect(result.command.cookies[0].sameSite).toBe('Strict');
}
});
it('should parse cookies_set with multiple cookies', () => {
const result = parseCommand(
cmd({
id: '1',
action: 'cookies_set',
cookies: [
{ name: 'cookie1', value: 'value1' },
{ name: 'cookie2', value: 'value2' },
],
})
);
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.cookies).toHaveLength(2);
}
});
it('should reject cookies_set without cookies array', () => {
const result = parseCommand(cmd({ id: '1', action: 'cookies_set' }));
expect(result.success).toBe(false);
});
it('should accept cookies_set with empty cookies array', () => {
// Empty array is technically valid (no-op)
const result = parseCommand(cmd({ id: '1', action: 'cookies_set', cookies: [] }));
expect(result.success).toBe(true);
});
it('should reject cookies_set with cookie missing name', () => {
const result = parseCommand(
cmd({ id: '1', action: 'cookies_set', cookies: [{ value: 'test' }] })
);
expect(result.success).toBe(false);
});
it('should reject cookies_set with cookie missing value', () => {
const result = parseCommand(
cmd({ id: '1', action: 'cookies_set', cookies: [{ name: 'test' }] })
);
expect(result.success).toBe(false);
});
it('should reject cookies_set with invalid sameSite value', () => {
const result = parseCommand(
cmd({
id: '1',
action: 'cookies_set',
cookies: [{ name: 'test', value: 'val', sameSite: 'Invalid' }],
})
);
expect(result.success).toBe(false);
});
it('should parse cookies_clear', () => {
const result = parseCommand(cmd({ id: '1', action: 'cookies_clear' }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.action).toBe('cookies_clear');
}
});
});
describe('storage', () => {
it('should parse storage_get for localStorage', () => {
const result = parseCommand(cmd({ id: '1', action: 'storage_get', type: 'local' }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.action).toBe('storage_get');
expect(result.command.type).toBe('local');
}
});
it('should parse storage_get for sessionStorage', () => {
const result = parseCommand(cmd({ id: '1', action: 'storage_get', type: 'session' }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.type).toBe('session');
}
});
it('should parse storage_get with specific key', () => {
const result = parseCommand(cmd({ id: '1', action: 'storage_get', type: 'local', key: 'mykey' }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.key).toBe('mykey');
}
});
it('should parse storage_set', () => {
const result = parseCommand(
cmd({
id: '1',
action: 'storage_set',
type: 'local',
key: 'test',
value: 'value',
})
);
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.action).toBe('storage_set');
expect(result.command.key).toBe('test');
expect(result.command.value).toBe('value');
}
});
it('should reject storage_set without key', () => {
const result = parseCommand(
cmd({
id: '1',
action: 'storage_set',
type: 'local',
value: 'value',
})
);
expect(result.success).toBe(false);
});
it('should reject storage_set without value', () => {
const result = parseCommand(
cmd({
id: '1',
action: 'storage_set',
type: 'local',
key: 'test',
})
);
expect(result.success).toBe(false);
});
it('should parse storage_clear for localStorage', () => {
const result = parseCommand(cmd({ id: '1', action: 'storage_clear', type: 'local' }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.action).toBe('storage_clear');
expect(result.command.type).toBe('local');
}
});
it('should parse storage_clear for sessionStorage', () => {
const result = parseCommand(cmd({ id: '1', action: 'storage_clear', type: 'session' }));
expect(result.success).toBe(true);
});
it('should reject storage_get without type', () => {
const result = parseCommand(cmd({ id: '1', action: 'storage_get' }));
expect(result.success).toBe(false);
});
it('should reject storage_get with invalid type', () => {
const result = parseCommand(cmd({ id: '1', action: 'storage_get', type: 'invalid' }));
expect(result.success).toBe(false);
});
});
describe('semantic locators', () => {
it('should parse getbyrole', () => {
const result = parseCommand(
cmd({
id: '1',
action: 'getbyrole',
role: 'button',
subaction: 'click',
})
);
expect(result.success).toBe(true);
});
it('should parse getbytext', () => {
const result = parseCommand(
cmd({
id: '1',
action: 'getbytext',
text: 'Submit',
subaction: 'click',
})
);
expect(result.success).toBe(true);
});
it('should parse getbylabel', () => {
const result = parseCommand(
cmd({
id: '1',
action: 'getbylabel',
label: 'Email',
subaction: 'fill',
value: 'test@test.com',
})
);
expect(result.success).toBe(true);
});
});
describe('tabs', () => {
it('should parse tab_new', () => {
const result = parseCommand(cmd({ id: '1', action: 'tab_new' }));
expect(result.success).toBe(true);
});
it('should parse tab_list', () => {
const result = parseCommand(cmd({ id: '1', action: 'tab_list' }));
expect(result.success).toBe(true);
});
it('should parse tab_switch', () => {
const result = parseCommand(cmd({ id: '1', action: 'tab_switch', index: 0 }));
expect(result.success).toBe(true);
});
it('should parse tab_close', () => {
const result = parseCommand(cmd({ id: '1', action: 'tab_close' }));
expect(result.success).toBe(true);
});
});
describe('snapshot', () => {
it('should parse basic snapshot command', () => {
const result = parseCommand(cmd({ id: '1', action: 'snapshot' }));
expect(result.success).toBe(true);
});
it('should parse snapshot with interactive filter', () => {
const result = parseCommand(cmd({ id: '1', action: 'snapshot', interactive: true }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.interactive).toBe(true);
}
});
it('should parse snapshot with compact filter', () => {
const result = parseCommand(cmd({ id: '1', action: 'snapshot', compact: true }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.compact).toBe(true);
}
});
it('should parse snapshot with maxDepth', () => {
const result = parseCommand(cmd({ id: '1', action: 'snapshot', maxDepth: 3 }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.maxDepth).toBe(3);
}
});
it('should parse snapshot with selector scope', () => {
const result = parseCommand(cmd({ id: '1', action: 'snapshot', selector: '#main' }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.selector).toBe('#main');
}
});
it('should parse snapshot with all options', () => {
const result = parseCommand(cmd({
id: '1',
action: 'snapshot',
interactive: true,
compact: true,
maxDepth: 5,
selector: '.content',
}));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.interactive).toBe(true);
expect(result.command.compact).toBe(true);
expect(result.command.maxDepth).toBe(5);
expect(result.command.selector).toBe('.content');
}
});
});
describe('launch', () => {
it('should parse launch command', () => {
const result = parseCommand(cmd({ id: '1', action: 'launch' }));
expect(result.success).toBe(true);
});
it('should parse launch with headless false', () => {
const result = parseCommand(cmd({ id: '1', action: 'launch', headless: false }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.headless).toBe(false);
}
});
});
describe('mouse actions', () => {
it('should parse mousemove', () => {
const result = parseCommand(cmd({ id: '1', action: 'mousemove', x: 100, y: 200 }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.x).toBe(100);
expect(result.command.y).toBe(200);
}
});
it('should parse mousedown', () => {
const result = parseCommand(cmd({ id: '1', action: 'mousedown', button: 'left' }));
expect(result.success).toBe(true);
});
it('should parse mouseup', () => {
const result = parseCommand(cmd({ id: '1', action: 'mouseup', button: 'left' }));
expect(result.success).toBe(true);
});
it('should parse wheel', () => {
const result = parseCommand(cmd({ id: '1', action: 'wheel', deltaX: 0, deltaY: 100 }));
expect(result.success).toBe(true);
});
});
describe('scroll', () => {
it('should parse scroll command', () => {
const result = parseCommand(cmd({ id: '1', action: 'scroll', direction: 'down', amount: 300 }));
expect(result.success).toBe(true);
});
it('should parse scrollintoview', () => {
const result = parseCommand(cmd({ id: '1', action: 'scrollintoview', selector: '#element' }));
expect(result.success).toBe(true);
});
});
describe('element state', () => {
it('should parse isvisible', () => {
const result = parseCommand(cmd({ id: '1', action: 'isvisible', selector: '#btn' }));
expect(result.success).toBe(true);
});
it('should parse isenabled', () => {
const result = parseCommand(cmd({ id: '1', action: 'isenabled', selector: '#btn' }));
expect(result.success).toBe(true);
});
it('should parse ischecked', () => {
const result = parseCommand(cmd({ id: '1', action: 'ischecked', selector: '#checkbox' }));
expect(result.success).toBe(true);
});
});
describe('viewport and settings', () => {
it('should parse viewport', () => {
const result = parseCommand(cmd({ id: '1', action: 'viewport', width: 1920, height: 1080 }));
expect(result.success).toBe(true);
});
it('should parse geolocation', () => {
const result = parseCommand(cmd({ id: '1', action: 'geolocation', latitude: 37.7749, longitude: -122.4194 }));
expect(result.success).toBe(true);
});
it('should parse offline', () => {
const result = parseCommand(cmd({ id: '1', action: 'offline', offline: true }));
expect(result.success).toBe(true);
});
});
describe('trace', () => {
it('should parse trace_start', () => {
const result = parseCommand(cmd({ id: '1', action: 'trace_start' }));
expect(result.success).toBe(true);
});
it('should parse trace_stop', () => {
const result = parseCommand(cmd({ id: '1', action: 'trace_stop', path: 'trace.zip' }));
expect(result.success).toBe(true);
});
});
describe('console and errors', () => {
it('should parse console', () => {
const result = parseCommand(cmd({ id: '1', action: 'console' }));
expect(result.success).toBe(true);
});
it('should parse console with clear', () => {
const result = parseCommand(cmd({ id: '1', action: 'console', clear: true }));
expect(result.success).toBe(true);
});
it('should parse errors', () => {
const result = parseCommand(cmd({ id: '1', action: 'errors' }));
expect(result.success).toBe(true);
});
});
describe('dialog', () => {
it('should parse dialog accept', () => {
const result = parseCommand(cmd({ id: '1', action: 'dialog', response: 'accept' }));
expect(result.success).toBe(true);
});
it('should parse dialog dismiss', () => {
const result = parseCommand(cmd({ id: '1', action: 'dialog', response: 'dismiss' }));
expect(result.success).toBe(true);
});
it('should parse dialog accept with prompt text', () => {
const result = parseCommand(cmd({ id: '1', action: 'dialog', response: 'accept', promptText: 'hello' }));
expect(result.success).toBe(true);
if (result.success) {
expect(result.command.promptText).toBe('hello');
}
});
});
describe('frame', () => {
it('should parse frame command', () => {
const result = parseCommand(cmd({ id: '1', action: 'frame', selector: '#iframe' }));
expect(result.success).toBe(true);
});
it('should parse mainframe', () => {
const result = parseCommand(cmd({ id: '1', action: 'mainframe' }));
expect(result.success).toBe(true);
});
});
describe('invalid commands', () => {
it('should reject unknown action', () => {
const result = parseCommand(cmd({ id: '1', action: 'unknown' }));
expect(result.success).toBe(false);
});
it('should reject missing id', () => {
const result = parseCommand(cmd({ action: 'click', selector: '#btn' }));
expect(result.success).toBe(false);
});
it('should reject invalid JSON', () => {
const result = parseCommand('not json');
expect(result.success).toBe(false);
});
});
});