Skip to main content
Glama

Token Saver MCP

by jerry426
test-form-validation.ts8.28 kB
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' import type { ToolMetadata } from '../types' import { z } from 'zod' import { ensureCDPConnection } from '../../browser-functions' const logger = { info: console.error, error: console.error, warn: console.warn } export const metadata: ToolMetadata = { name: 'test_form_validation', title: 'Test Form Validation', category: 'helper', description: 'Complete form validation testing workflow with automatic error detection and accessibility checks', docs: { brief: 'Complete form validation testing workflow', testsAutomatically: [ 'Form field validation (client-side)', 'HTML5 validation constraints', 'Custom validation error messages', 'Form submission behavior', 'Success/error state handling', 'Page redirection after submit', 'Console errors during validation', ], savesTimeVsManual: [ 'No need to manually fill forms repeatedly', 'Automatically captures all validation states', 'Tests both valid and invalid scenarios', 'Detects validation edge cases', 'Verifies error message display', 'Checks form accessibility', ], integrationWorkflows: [ 'Validate form logic during development', 'Test validation rules after changes', 'Ensure proper error handling', 'Verify form accessibility features', 'Test user experience flows', 'Regression testing for form changes', ], exampleUsage: `test_form_validation({ url: "http://localhost:3000/signup", formSelector: "#registration-form", fields: { email: "invalid-email", password: "123", confirmPassword: "456" }, submitButtonSelector: "button[type='submit']" })`, }, } // Tool handler - single source of truth for execution export async function handler({ url, formSelector, fields, submitButtonSelector }: any): Promise<any> { try { const client = await ensureCDPConnection() await client.navigate(url) await client.waitForSelector(formSelector) // Fill form fields for (const [field, value] of Object.entries(fields)) { const selector = `${formSelector} [name="${field}"], ${formSelector} #${field}` await client.type(selector, String(value)) } // Clear previous console messages client.clearConsoleMessages() // Submit form const submitSelector = submitButtonSelector || `${formSelector} button[type="submit"], ${formSelector} input[type="submit"]` const result = await client.execute(` (() => { const form = document.querySelector('${formSelector}'); const submitBtn = document.querySelector('${submitSelector}'); const initialUrl = window.location.href; // Collect initial validation state const getValidationErrors = () => { const errors = {}; const errorElements = document.querySelectorAll('.error, .error-message, [class*="error"]'); errorElements.forEach(el => { const field = el.closest('[data-field]')?.dataset.field || el.getAttribute('for') || 'general'; errors[field] = el.textContent.trim(); }); // Check HTML5 validation const inputs = form.querySelectorAll('input, textarea, select'); inputs.forEach(input => { if (!input.validity.valid) { errors[input.name || input.id] = input.validationMessage; } }); return errors; }; // Check if form is valid before submit const isValid = form ? form.checkValidity() : false; // Trigger submit if (submitBtn) { submitBtn.click(); } else if (form) { form.requestSubmit(); } return { formFound: !!form, submitButtonFound: !!submitBtn, htmlValidation: isValid, initialErrors: getValidationErrors(), initialUrl: initialUrl }; })() `) // Wait for form processing await new Promise(resolve => setTimeout(resolve, 1500)) // Check post-submit state const postSubmitResult = await client.execute(` (() => { const currentUrl = window.location.href; const form = document.querySelector('${formSelector}'); // Look for success indicators const successElements = document.querySelectorAll('.success, .alert-success, [class*="success"]'); const successMessage = successElements.length > 0 ? Array.from(successElements).map(el => el.textContent.trim()).join(' ') : null; // Collect validation errors after submit const errors = {}; const errorElements = document.querySelectorAll('.error, .error-message, [class*="error"], .invalid-feedback'); errorElements.forEach(el => { if (el.offsetParent !== null) { // Only visible errors const field = el.closest('[data-field]')?.dataset.field || el.getAttribute('for') || el.previousElementSibling?.name || 'general'; errors[field] = el.textContent.trim(); } }); // Check for form field specific errors const inputs = form ? form.querySelectorAll('input, textarea, select') : []; inputs.forEach(input => { if (input.classList.contains('error') || input.classList.contains('invalid')) { errors[input.name || input.id] = 'Field has error'; } }); return { currentUrl, redirected: currentUrl !== '${result.initialUrl}', successMessage, validationErrors: errors, errorCount: Object.keys(errors).length, formStillVisible: !!form && form.offsetParent !== null }; })() `) // Get console errors const consoleErrors = client.getConsoleMessages ? client.getConsoleMessages('error') : [] return { content: [{ type: 'text', text: JSON.stringify({ success: true, form: { found: result.formFound, submitted: result.submitButtonFound, htmlValid: result.htmlValidation, }, validation: { hasErrors: postSubmitResult.errorCount > 0, errors: postSubmitResult.validationErrors, errorCount: postSubmitResult.errorCount, }, result: { redirected: postSubmitResult.redirected, successMessage: postSubmitResult.successMessage, formStillVisible: postSubmitResult.formStillVisible, newUrl: postSubmitResult.redirected ? postSubmitResult.currentUrl : null, }, console: { errors: consoleErrors.map((e: any) => e.text), }, }, null, 2), }], } } catch (error: any) { logger.error('Failed to test form validation:', error) return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message, }, null, 2), }], } } } export function register(server: McpServer) { server.registerTool( 'test_form_validation', { title: 'Test Form Validation', description: 'Test form validation by filling fields and attempting submission', inputSchema: { url: z.string().describe('URL of the form page'), formSelector: z.string().describe('CSS selector for the form'), fields: z.record(z.string()).describe('Field name/id to value mapping'), submitButtonSelector: z.string().optional().describe('Submit button selector (default: finds submit button)'), }, }, handler, // Use the exported handler ) }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jerry426/token-saver-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server