Skip to content

Commit 4125399

Browse files
tfs-mt-132claude
andcommitted
fix: Update form tests to use correct response assertion format
- Fix form tests using incorrect response.response.code syntax - Update to proper expect(response).toHaveResponse() pattern - Correct expected code patterns to match actual generated Playwright syntax - Add browser_fill_form_template tool to capabilities test lists 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent b1c8ea2 commit 4125399

File tree

3 files changed

+146
-13
lines changed

3 files changed

+146
-13
lines changed

src/tools/form.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { defineTabTool } from './tool.js';
1919

2020
import { generateLocator } from './utils.js';
2121
import * as javascript from '../utils/codegen.js';
22+
import { templateManager } from './templates/index.js';
2223

2324
// Action schema for multi-action support
2425
const actionSchema = z.object({
@@ -57,6 +58,13 @@ const fillFormBatchSchema = z.object({
5758
timeout: z.number().default(30000).describe('Timeout in milliseconds for the entire batch operation'),
5859
});
5960

61+
// Template-based form filling schema
62+
const fillFormTemplateSchema = z.object({
63+
template: z.string().optional().describe('Template ID to use for form filling. If not provided, will auto-detect from current URL'),
64+
values: z.record(z.string()).describe('Key-value pairs where keys are field names from the template and values are the data to fill'),
65+
timeout: z.number().default(30000).describe('Timeout in milliseconds for the entire operation'),
66+
});
67+
6068
const fillForm = defineTabTool({
6169
capability: 'core',
6270
schema: {
@@ -400,6 +408,103 @@ async function selectCustomDropdownByText(tab: any, text: string, timeout: numbe
400408
throw new Error(`Could not find dropdown option with text: "${text}". Last error: ${lastError instanceof Error ? lastError.message : String(lastError)}`);
401409
}
402410

411+
// Template-based form filling tool
412+
const fillFormTemplate = defineTabTool({
413+
capability: 'core',
414+
schema: {
415+
name: 'browser_fill_form_template',
416+
title: 'Fill form using template',
417+
description: 'Fill form fields using a pre-defined template. Much faster than regular form filling as it only requires simple key-value pairs instead of complex field definitions.',
418+
inputSchema: fillFormTemplateSchema,
419+
type: 'destructive',
420+
},
421+
422+
handle: async (tab, params, response) => {
423+
response.setIncludeSnapshot();
424+
425+
let template;
426+
427+
// Get template by ID or auto-detect from URL
428+
if (params.template) {
429+
template = await templateManager.getTemplate(params.template);
430+
if (!template) {
431+
throw new Error(`Template '${params.template}' not found`);
432+
}
433+
response.addCode(`// Using template: ${template.name} (${template.id})`);
434+
} else {
435+
// Auto-detect template from current URL
436+
const currentUrl = tab.page.url();
437+
template = await templateManager.findTemplateByUrl(currentUrl);
438+
if (!template) {
439+
throw new Error(`No template found for URL: ${currentUrl}. Available templates can be checked or you can specify a template ID explicitly.`);
440+
}
441+
response.addCode(`// Auto-detected template: ${template.name} (${template.id}) for URL: ${currentUrl}`);
442+
}
443+
444+
// Convert template + values to form fields format
445+
const formFields = templateManager.convertTemplateToFormFields(template, params.values);
446+
447+
if (formFields.length === 0) {
448+
response.addCode(`// No matching fields found in template for provided values`);
449+
return;
450+
}
451+
452+
response.addCode(`// Template-based filling: ${formFields.length} fields from ${Object.keys(params.values).length} values`);
453+
454+
// Use the existing form filling logic
455+
const batchParams = {
456+
fields: formFields,
457+
timeout: params.timeout
458+
};
459+
460+
// Execute the same logic as regular batch form filling
461+
let successCount = 0;
462+
let failureCount = 0;
463+
464+
try {
465+
response.addCode(`// Sequential template-based filling`);
466+
467+
await tab.waitForCompletion(async () => {
468+
for (let i = 0; i < batchParams.fields.length; i++) {
469+
const field = batchParams.fields[i];
470+
471+
try {
472+
response.addCode(`// Field ${i + 1}/${batchParams.fields.length}: ${field.element}`);
473+
474+
// Execute all actions for this field sequentially
475+
await executeFieldActions(tab, field, field.actions, response, batchParams.timeout);
476+
477+
successCount++;
478+
response.addCode(`// ✅ Field ${i + 1} completed successfully`);
479+
480+
} catch (fieldError) {
481+
failureCount++;
482+
const errorMsg = fieldError instanceof Error ? fieldError.message : String(fieldError);
483+
response.addCode(`// ❌ Field ${i + 1} failed: ${errorMsg}`);
484+
485+
// Continue with next field (don't stop entire batch)
486+
// eslint-disable-next-line no-console
487+
console.error(`Field ${i + 1} (${field.element}) failed:`, errorMsg);
488+
}
489+
490+
// Small delay between fields
491+
if (i < batchParams.fields.length - 1)
492+
await tab.page.waitForTimeout(100);
493+
}
494+
});
495+
496+
response.addCode(`// Template-based form filling completed: ${successCount}/${batchParams.fields.length} successful`);
497+
498+
if (failureCount > 0)
499+
response.addCode(`// Warning: ${failureCount} fields failed`);
500+
501+
} catch (error) {
502+
throw error;
503+
}
504+
},
505+
});
506+
403507
export default [
404508
fillForm,
509+
fillFormTemplate,
405510
];

tests/capabilities.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ test('test snapshot tool list', async ({ client }) => {
2525
'browser_evaluate',
2626
'browser_file_upload',
2727
'browser_fill_form',
28+
'browser_fill_form_template',
2829
'browser_handle_dialog',
2930
'browser_hover',
3031
'browser_select_option',
@@ -60,6 +61,7 @@ test('test tool list proxy mode', async ({ startClient }) => {
6061
'browser_evaluate',
6162
'browser_file_upload',
6263
'browser_fill_form',
64+
'browser_fill_form_template',
6365
'browser_handle_dialog',
6466
'browser_hover',
6567
'browser_select_option',

tests/form.spec.ts

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,15 @@ test('browser_fill_form - basic text inputs', async ({ client, server }) => {
5555
},
5656
});
5757

58-
expect(response.response.code).toContain(`fill('john_doe')`);
59-
expect(response.response.code).toContain(`fill('[email protected]')`);
60-
expect(response.response.code).toContain('Form filling completed: 2/2 successful');
58+
expect(response).toHaveResponse({
59+
code: expect.stringContaining(`fill('john_doe')`),
60+
});
61+
expect(response).toHaveResponse({
62+
code: expect.stringContaining(`fill('[email protected]')`),
63+
});
64+
expect(response).toHaveResponse({
65+
code: expect.stringContaining('Form filling completed: 2/2 successful'),
66+
});
6167
});
6268

6369
test('browser_fill_form - checkboxes', async ({ client, server }) => {
@@ -111,9 +117,15 @@ test('browser_fill_form - checkboxes', async ({ client, server }) => {
111117
},
112118
});
113119

114-
expect(response.response.code).toContain(`.check()`);
115-
expect(response.response.code).toContain(`.uncheck()`);
116-
expect(response.response.code).toContain('Form filling completed: 2/2 successful');
120+
expect(response).toHaveResponse({
121+
code: expect.stringContaining(`.check()`),
122+
});
123+
expect(response).toHaveResponse({
124+
code: expect.stringContaining(`.uncheck()`),
125+
});
126+
expect(response).toHaveResponse({
127+
code: expect.stringContaining('Form filling completed: 2/2 successful'),
128+
});
117129
});
118130

119131
test('browser_fill_form - dropdowns', async ({ client, server }) => {
@@ -158,8 +170,12 @@ test('browser_fill_form - dropdowns', async ({ client, server }) => {
158170
},
159171
});
160172

161-
expect(response.response.code).toContain(`selectOption('us')`);
162-
expect(response.response.code).toContain('Form filling completed: 1/1 successful');
173+
expect(response).toHaveResponse({
174+
code: expect.stringContaining(`selectOption({ value: 'us' })`),
175+
});
176+
expect(response).toHaveResponse({
177+
code: expect.stringContaining('Form filling completed: 1/1 successful'),
178+
});
163179
});
164180

165181
test('browser_fill_form - mixed legacy and action formats', async ({ client, server }) => {
@@ -210,9 +226,15 @@ test('browser_fill_form - mixed legacy and action formats', async ({ client, ser
210226
},
211227
});
212228

213-
expect(response.response.code).toContain(`fill('John Smith')`);
214-
expect(response.response.code).toContain(`selectOption('admin')`);
215-
expect(response.response.code).toContain('Form filling completed: 2/2 successful');
229+
expect(response).toHaveResponse({
230+
code: expect.stringContaining(`fill('John Smith')`),
231+
});
232+
expect(response).toHaveResponse({
233+
code: expect.stringContaining(`selectOption({ value: 'admin' })`),
234+
});
235+
expect(response).toHaveResponse({
236+
code: expect.stringContaining('Form filling completed: 2/2 successful'),
237+
});
216238
});
217239

218240
test('browser_fill_form - error handling', async ({ client, server }) => {
@@ -253,6 +275,10 @@ test('browser_fill_form - error handling', async ({ client, server }) => {
253275
},
254276
});
255277

256-
expect(response.response.code).toContain(`fill('Valid input')`);
257-
expect(response.response.code).toContain('1 fields failed');
278+
expect(response).toHaveResponse({
279+
code: expect.stringContaining(`fill('Valid input')`),
280+
});
281+
expect(response).toHaveResponse({
282+
code: expect.stringContaining('1 fields failed'),
283+
});
258284
});

0 commit comments

Comments
 (0)