format
This commit is contained in:
+8
-2
@@ -383,7 +383,7 @@ async function handleNavigate(
|
|||||||
async function handleClick(command: ClickCommand, browser: BrowserManager): Promise<Response> {
|
async function handleClick(command: ClickCommand, browser: BrowserManager): Promise<Response> {
|
||||||
// Support both refs (@e1) and regular selectors
|
// Support both refs (@e1) and regular selectors
|
||||||
const locator = browser.getLocator(command.selector);
|
const locator = browser.getLocator(command.selector);
|
||||||
|
|
||||||
await locator.click({
|
await locator.click({
|
||||||
button: command.button,
|
button: command.button,
|
||||||
clickCount: command.clickCount,
|
clickCount: command.clickCount,
|
||||||
@@ -449,7 +449,13 @@ async function handleScreenshot(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleSnapshot(
|
async function handleSnapshot(
|
||||||
command: Command & { action: 'snapshot'; interactive?: boolean; maxDepth?: number; compact?: boolean; selector?: string },
|
command: Command & {
|
||||||
|
action: 'snapshot';
|
||||||
|
interactive?: boolean;
|
||||||
|
maxDepth?: number;
|
||||||
|
compact?: boolean;
|
||||||
|
selector?: string;
|
||||||
|
},
|
||||||
browser: BrowserManager
|
browser: BrowserManager
|
||||||
): Promise<Response<SnapshotData>> {
|
): Promise<Response<SnapshotData>> {
|
||||||
// Use enhanced snapshot with refs and optional filtering
|
// Use enhanced snapshot with refs and optional filtering
|
||||||
|
|||||||
+3
-1
@@ -123,7 +123,9 @@ describe('BrowserManager', () => {
|
|||||||
it('should set cookie with domain', async () => {
|
it('should set cookie with domain', async () => {
|
||||||
const page = browser.getPage();
|
const page = browser.getPage();
|
||||||
const context = page.context();
|
const context = page.context();
|
||||||
await context.addCookies([{ name: 'domainCookie', value: 'domainValue', domain: 'example.com', path: '/' }]);
|
await context.addCookies([
|
||||||
|
{ name: 'domainCookie', value: 'domainValue', domain: 'example.com', path: '/' },
|
||||||
|
]);
|
||||||
const cookies = await context.cookies();
|
const cookies = await context.cookies();
|
||||||
const testCookie = cookies.find((c) => c.name === 'domainCookie');
|
const testCookie = cookies.find((c) => c.name === 'domainCookie');
|
||||||
expect(testCookie?.value).toBe('domainValue');
|
expect(testCookie?.value).toBe('domainValue');
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@ export class BrowserManager {
|
|||||||
if (!refData) return null;
|
if (!refData) return null;
|
||||||
|
|
||||||
const page = this.getPage();
|
const page = this.getPage();
|
||||||
|
|
||||||
// Parse the selector and create locator
|
// Parse the selector and create locator
|
||||||
if (refData.name) {
|
if (refData.name) {
|
||||||
return page.getByRole(refData.role as any, { name: refData.name });
|
return page.getByRole(refData.role as any, { name: refData.name });
|
||||||
|
|||||||
+4
-2
@@ -33,7 +33,7 @@ export function getSession(): string {
|
|||||||
function getPortForSession(session: string): number {
|
function getPortForSession(session: string): number {
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
for (let i = 0; i < session.length; i++) {
|
for (let i = 0; i < session.length; i++) {
|
||||||
hash = ((hash << 5) - hash) + session.charCodeAt(i);
|
hash = (hash << 5) - hash + session.charCodeAt(i);
|
||||||
hash |= 0;
|
hash |= 0;
|
||||||
}
|
}
|
||||||
// Port range 49152-65535 (dynamic/private ports)
|
// Port range 49152-65535 (dynamic/private ports)
|
||||||
@@ -90,7 +90,9 @@ export function isDaemonRunning(session?: string): boolean {
|
|||||||
* Get connection info for the current session
|
* Get connection info for the current session
|
||||||
* Returns { type: 'unix', path: string } or { type: 'tcp', port: number }
|
* Returns { type: 'unix', path: string } or { type: 'tcp', port: number }
|
||||||
*/
|
*/
|
||||||
export function getConnectionInfo(session?: string): { type: 'unix'; path: string } | { type: 'tcp'; port: number } {
|
export function getConnectionInfo(
|
||||||
|
session?: string
|
||||||
|
): { type: 'unix'; path: string } | { type: 'tcp'; port: number } {
|
||||||
const sess = session ?? currentSession;
|
const sess = session ?? currentSession;
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
return { type: 'tcp', port: getPortForSession(sess) };
|
return { type: 'tcp', port: getPortForSession(sess) };
|
||||||
|
|||||||
+22
-12
@@ -252,7 +252,9 @@ describe('parseCommand', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should parse storage_get with specific key', () => {
|
it('should parse storage_get with specific key', () => {
|
||||||
const result = parseCommand(cmd({ id: '1', action: 'storage_get', type: 'local', key: 'mykey' }));
|
const result = parseCommand(
|
||||||
|
cmd({ id: '1', action: 'storage_get', type: 'local', key: 'mykey' })
|
||||||
|
);
|
||||||
expect(result.success).toBe(true);
|
expect(result.success).toBe(true);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
expect(result.command.key).toBe('mykey');
|
expect(result.command.key).toBe('mykey');
|
||||||
@@ -426,14 +428,16 @@ describe('parseCommand', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should parse snapshot with all options', () => {
|
it('should parse snapshot with all options', () => {
|
||||||
const result = parseCommand(cmd({
|
const result = parseCommand(
|
||||||
id: '1',
|
cmd({
|
||||||
action: 'snapshot',
|
id: '1',
|
||||||
interactive: true,
|
action: 'snapshot',
|
||||||
compact: true,
|
interactive: true,
|
||||||
maxDepth: 5,
|
compact: true,
|
||||||
selector: '.content',
|
maxDepth: 5,
|
||||||
}));
|
selector: '.content',
|
||||||
|
})
|
||||||
|
);
|
||||||
expect(result.success).toBe(true);
|
expect(result.success).toBe(true);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
expect(result.command.interactive).toBe(true);
|
expect(result.command.interactive).toBe(true);
|
||||||
@@ -487,7 +491,9 @@ describe('parseCommand', () => {
|
|||||||
|
|
||||||
describe('scroll', () => {
|
describe('scroll', () => {
|
||||||
it('should parse scroll command', () => {
|
it('should parse scroll command', () => {
|
||||||
const result = parseCommand(cmd({ id: '1', action: 'scroll', direction: 'down', amount: 300 }));
|
const result = parseCommand(
|
||||||
|
cmd({ id: '1', action: 'scroll', direction: 'down', amount: 300 })
|
||||||
|
);
|
||||||
expect(result.success).toBe(true);
|
expect(result.success).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -521,7 +527,9 @@ describe('parseCommand', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should parse geolocation', () => {
|
it('should parse geolocation', () => {
|
||||||
const result = parseCommand(cmd({ id: '1', action: 'geolocation', latitude: 37.7749, longitude: -122.4194 }));
|
const result = parseCommand(
|
||||||
|
cmd({ id: '1', action: 'geolocation', latitude: 37.7749, longitude: -122.4194 })
|
||||||
|
);
|
||||||
expect(result.success).toBe(true);
|
expect(result.success).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -572,7 +580,9 @@ describe('parseCommand', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should parse dialog accept with prompt text', () => {
|
it('should parse dialog accept with prompt text', () => {
|
||||||
const result = parseCommand(cmd({ id: '1', action: 'dialog', response: 'accept', promptText: 'hello' }));
|
const result = parseCommand(
|
||||||
|
cmd({ id: '1', action: 'dialog', response: 'accept', promptText: 'hello' })
|
||||||
|
);
|
||||||
expect(result.success).toBe(true);
|
expect(result.success).toBe(true);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
expect(result.command.promptText).toBe('hello');
|
expect(result.command.promptText).toBe('hello');
|
||||||
|
|||||||
+18
-21
@@ -173,10 +173,10 @@ function processAriaTree(ariaTree: string, refs: RefMap, options: SnapshotOption
|
|||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const match = line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);
|
const match = line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);
|
||||||
if (!match) continue;
|
if (!match) continue;
|
||||||
|
|
||||||
const [, , role, name, suffix] = match;
|
const [, , role, name, suffix] = match;
|
||||||
const roleLower = role.toLowerCase();
|
const roleLower = role.toLowerCase();
|
||||||
|
|
||||||
if (INTERACTIVE_ROLES.has(roleLower)) {
|
if (INTERACTIVE_ROLES.has(roleLower)) {
|
||||||
const ref = nextRef();
|
const ref = nextRef();
|
||||||
refs[ref] = {
|
refs[ref] = {
|
||||||
@@ -184,12 +184,12 @@ function processAriaTree(ariaTree: string, refs: RefMap, options: SnapshotOption
|
|||||||
role: roleLower,
|
role: roleLower,
|
||||||
name,
|
name,
|
||||||
};
|
};
|
||||||
|
|
||||||
let enhanced = `- ${role}`;
|
let enhanced = `- ${role}`;
|
||||||
if (name) enhanced += ` "${name}"`;
|
if (name) enhanced += ` "${name}"`;
|
||||||
enhanced += ` [ref=${ref}]`;
|
enhanced += ` [ref=${ref}]`;
|
||||||
if (suffix && suffix.includes('[')) enhanced += suffix;
|
if (suffix && suffix.includes('[')) enhanced += suffix;
|
||||||
|
|
||||||
result.push(enhanced);
|
result.push(enhanced);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,11 +223,7 @@ function getIndentLevel(line: string): number {
|
|||||||
/**
|
/**
|
||||||
* Process a single line: add ref if needed, filter if requested
|
* Process a single line: add ref if needed, filter if requested
|
||||||
*/
|
*/
|
||||||
function processLine(
|
function processLine(line: string, refs: RefMap, options: SnapshotOptions): string | null {
|
||||||
line: string,
|
|
||||||
refs: RefMap,
|
|
||||||
options: SnapshotOptions
|
|
||||||
): string | null {
|
|
||||||
const depth = getIndentLevel(line);
|
const depth = getIndentLevel(line);
|
||||||
|
|
||||||
// Check max depth
|
// Check max depth
|
||||||
@@ -302,27 +298,27 @@ function processLine(
|
|||||||
function compactTree(tree: string): string {
|
function compactTree(tree: string): string {
|
||||||
const lines = tree.split('\n');
|
const lines = tree.split('\n');
|
||||||
const result: string[] = [];
|
const result: string[] = [];
|
||||||
|
|
||||||
// Simple pass: keep lines that have content or refs
|
// Simple pass: keep lines that have content or refs
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i];
|
const line = lines[i];
|
||||||
|
|
||||||
// Always keep lines with refs
|
// Always keep lines with refs
|
||||||
if (line.includes('[ref=')) {
|
if (line.includes('[ref=')) {
|
||||||
result.push(line);
|
result.push(line);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep lines with text content (after :)
|
// Keep lines with text content (after :)
|
||||||
if (line.includes(':') && !line.endsWith(':')) {
|
if (line.includes(':') && !line.endsWith(':')) {
|
||||||
result.push(line);
|
result.push(line);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this structural element has children with refs
|
// Check if this structural element has children with refs
|
||||||
const currentIndent = getIndentLevel(line);
|
const currentIndent = getIndentLevel(line);
|
||||||
let hasRelevantChildren = false;
|
let hasRelevantChildren = false;
|
||||||
|
|
||||||
for (let j = i + 1; j < lines.length; j++) {
|
for (let j = i + 1; j < lines.length; j++) {
|
||||||
const childIndent = getIndentLevel(lines[j]);
|
const childIndent = getIndentLevel(lines[j]);
|
||||||
if (childIndent <= currentIndent) break;
|
if (childIndent <= currentIndent) break;
|
||||||
@@ -331,12 +327,12 @@ function compactTree(tree: string): string {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasRelevantChildren) {
|
if (hasRelevantChildren) {
|
||||||
result.push(line);
|
result.push(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.join('\n');
|
return result.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,17 +355,18 @@ export function parseRef(arg: string): string | null {
|
|||||||
/**
|
/**
|
||||||
* Get snapshot statistics
|
* Get snapshot statistics
|
||||||
*/
|
*/
|
||||||
export function getSnapshotStats(tree: string, refs: RefMap): {
|
export function getSnapshotStats(
|
||||||
|
tree: string,
|
||||||
|
refs: RefMap
|
||||||
|
): {
|
||||||
lines: number;
|
lines: number;
|
||||||
chars: number;
|
chars: number;
|
||||||
tokens: number;
|
tokens: number;
|
||||||
refs: number;
|
refs: number;
|
||||||
interactive: number;
|
interactive: number;
|
||||||
} {
|
} {
|
||||||
const interactive = Object.values(refs).filter(r =>
|
const interactive = Object.values(refs).filter((r) => INTERACTIVE_ROLES.has(r.role)).length;
|
||||||
INTERACTIVE_ROLES.has(r.role)
|
|
||||||
).length;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lines: tree.split('\n').length,
|
lines: tree.split('\n').length,
|
||||||
chars: tree.length,
|
chars: tree.length,
|
||||||
|
|||||||
Reference in New Issue
Block a user