chore: parse response in tests (#796)

This commit is contained in:
Pavel Feldman
2025-07-30 12:47:22 -07:00
committed by GitHub
parent 65d99fe595
commit 4df162aff5
33 changed files with 442 additions and 365 deletions

View File

@@ -93,8 +93,8 @@ export class Context {
if (!this._tabs.length) {
return [
'### No open tabs',
'Use the "browser_navigate" tool to navigate to a page first.',
'### Open tabs',
'No open tabs. Use the "browser_navigate" tool to navigate to a page first.',
'',
];
}

View File

@@ -88,12 +88,12 @@ export function createServer(backend: ServerBackend, runHeartbeat: boolean): Ser
}
const errorResult = (...messages: string[]) => ({
content: [{ type: 'text', text: messages.join('\n') }],
content: [{ type: 'text', text: '### Result\n' + messages.join('\n') }],
isError: true,
});
const tool = tools.find(tool => tool.name === request.params.name) as ToolSchema<any>;
if (!tool)
return errorResult(`Tool "${request.params.name}" not found`);
return errorResult(`Error: Tool "${request.params.name}" not found`);
try {
return await backend.callTool(tool, tool.inputSchema.parse(request.params.arguments || {}));

View File

@@ -28,6 +28,7 @@ export class Response {
readonly toolName: string;
readonly toolArgs: Record<string, any>;
private _isError: boolean | undefined;
constructor(context: Context, toolName: string, toolArgs: Record<string, any>) {
this._context = context;
@@ -39,6 +40,11 @@ export class Response {
this._result.push(result);
}
addError(error: string) {
this._result.push(`Error: ${error}`);
this._isError = true;
}
result() {
return this._result.join('\n');
}
@@ -77,7 +83,7 @@ export class Response {
return this._snapshot;
}
async serialize(): Promise<{ content: (TextContent | ImageContent)[] }> {
async serialize(): Promise<{ content: (TextContent | ImageContent)[], isError?: boolean }> {
const response: string[] = [];
// Start with command result.
@@ -116,6 +122,6 @@ ${this._code.join('\n')}
content.push({ type: 'image', data: image.data.toString('base64'), mimeType: image.contentType });
}
return { content };
return { content, isError: this._isError };
}
}

View File

@@ -49,7 +49,6 @@ const resize = defineTabTool({
},
handle: async (tab, params, response) => {
response.addCode(`// Resize browser window to ${params.width}x${params.height}`);
response.addCode(`await page.setViewportSize({ width: ${params.width}, height: ${params.height} });`);
await tab.waitForCompletion(async () => {

View File

@@ -67,11 +67,9 @@ const type = defineTabTool({
await tab.waitForCompletion(async () => {
if (params.slowly) {
response.setIncludeSnapshot();
response.addCode(`// Press "${params.text}" sequentially into "${params.element}"`);
response.addCode(`await page.${await generateLocator(locator)}.pressSequentially(${javascript.quote(params.text)});`);
await locator.pressSequentially(params.text);
} else {
response.addCode(`// Fill "${params.text}" into "${params.element}"`);
response.addCode(`await page.${await generateLocator(locator)}.fill(${javascript.quote(params.text)});`);
await locator.fill(params.text);
}

View File

@@ -35,7 +35,6 @@ const navigate = defineTool({
await tab.navigate(params.url);
response.setIncludeSnapshot();
response.addCode(`// Navigate to ${params.url}`);
response.addCode(`await page.goto('${params.url}');`);
},
});
@@ -53,7 +52,6 @@ const goBack = defineTabTool({
handle: async (tab, params, response) => {
await tab.page.goBack();
response.setIncludeSnapshot();
response.addCode(`// Navigate back`);
response.addCode(`await page.goBack();`);
},
});
@@ -70,7 +68,6 @@ const goForward = defineTabTool({
handle: async (tab, params, response) => {
await tab.page.goForward();
response.setIncludeSnapshot();
response.addCode(`// Navigate forward`);
response.addCode(`await page.goForward();`);
},
});

View File

@@ -37,7 +37,6 @@ const pdf = defineTabTool({
handle: async (tab, params, response) => {
const fileName = await outputFile(tab.context.config, params.filename ?? `page-${new Date().toISOString()}.pdf`);
response.addCode(`// Save page as ${fileName}`);
response.addCode(`await page.pdf(${javascript.formatObject({ path: fileName })});`);
response.addResult(`Saved page as ${fileName}`);
await tab.page.pdf({ path: fileName });

View File

@@ -63,13 +63,11 @@ const click = defineTabTool({
const button = params.button;
const buttonAttr = button ? `{ button: '${button}' }` : '';
if (params.doubleClick) {
response.addCode(`// Double click ${params.element}`);
if (params.doubleClick)
response.addCode(`await page.${await generateLocator(locator)}.dblclick(${buttonAttr});`);
} else {
response.addCode(`// Click ${params.element}`);
else
response.addCode(`await page.${await generateLocator(locator)}.click(${buttonAttr});`);
}
await tab.waitForCompletion(async () => {
if (params.doubleClick)
@@ -151,7 +149,6 @@ const selectOption = defineTabTool({
response.setIncludeSnapshot();
const locator = await tab.refLocator(params);
response.addCode(`// Select options [${params.values.join(', ')}] in ${params.element}`);
response.addCode(`await page.${await generateLocator(locator)}.selectOption(${javascript.formatObject(params.values)});`);
await tab.waitForCompletion(async () => {

View File

@@ -60,10 +60,11 @@ export function defineTabTool<Input extends z.Schema>(tool: TabTool<Input>): Too
const tab = context.currentTabOrDie();
const modalStates = tab.modalStates().map(state => state.type);
if (tool.clearsModalState && !modalStates.includes(tool.clearsModalState))
throw new Error(`The tool "${tool.schema.name}" can only be used when there is related modal state present.\n` + tab.modalStatesMarkdown().join('\n'));
if (!tool.clearsModalState && modalStates.length)
throw new Error(`Tool "${tool.schema.name}" does not handle the modal state.\n` + tab.modalStatesMarkdown().join('\n'));
return tool.handle(tab, params, response);
response.addError(`The tool "${tool.schema.name}" can only be used when there is related modal state present.\n` + tab.modalStatesMarkdown().join('\n'));
else if (!tool.clearsModalState && modalStates.length)
response.addError(`Tool "${tool.schema.name}" does not handle the modal state.\n` + tab.modalStatesMarkdown().join('\n'));
else
return tool.handle(tab, params, response);
},
};
}