UNPKG

@casoon/auditmysite

Version:

A comprehensive command-line tool for automated accessibility, security, performance, and SEO testing using Playwright and pa11y, based on sitemap URLs

159 lines 9.81 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ValidationFormValidationTest = void 0; const base_test_1 = require("../base-test"); class ValidationFormValidationTest extends base_test_1.BaseAccessibilityTest { constructor() { super(...arguments); this.name = 'Validation Form Validation Test'; this.description = 'Checks form validation patterns, rules, and structure (patterns, constraints, validation messages)'; this.category = 'validation'; this.priority = 'high'; this.standards = ['WCAG 2.1 AA', 'WCAG 2.2 AA', 'Section 508']; } async run(context) { const { page } = context; try { const result = await this.evaluateOnPage(page, () => { const issues = []; const warnings = []; // Check all form inputs for proper validation const formInputs = document.querySelectorAll('input, select, textarea'); formInputs.forEach(input => { const inputElement = input; // Check for proper input types (only for HTMLInputElement) if (inputElement instanceof HTMLInputElement) { if (inputElement.type === 'email' && !inputElement.pattern) { warnings.push(`Email input "${inputElement.id || inputElement.name}" lacks pattern validation`); } if (inputElement.type === 'tel' && !inputElement.pattern) { warnings.push(`Phone input "${inputElement.id || inputElement.name}" lacks pattern validation`); } if (inputElement.type === 'url' && !inputElement.pattern) { warnings.push(`URL input "${inputElement.id || inputElement.name}" lacks pattern validation`); } // Check for number inputs if (inputElement.type === 'number') { if (inputElement.min && inputElement.max) { if (parseFloat(inputElement.min) >= parseFloat(inputElement.max)) { issues.push(`Invalid number range for "${inputElement.id || inputElement.name}"`); } } } // Check for date inputs if (inputElement.type === 'date') { if (inputElement.min && inputElement.max) { const minDate = new Date(inputElement.min); const maxDate = new Date(inputElement.max); if (minDate >= maxDate) { issues.push(`Invalid date range for "${inputElement.id || inputElement.name}"`); } } } } // Check for required field validation patterns (not label associations - that's handled by FormLabelTest) if (inputElement.required) { // Only check validation-specific aspects, not label associations if (inputElement instanceof HTMLInputElement) { const type = inputElement.type; if (type === 'email' && !inputElement.pattern) { warnings.push(`Required email field "${inputElement.id || inputElement.name}" lacks email validation pattern`); } if (type === 'tel' && !inputElement.pattern) { warnings.push(`Required phone field "${inputElement.id || inputElement.name}" lacks phone validation pattern`); } } } // Check for min/max length (only for input and textarea) if ((inputElement instanceof HTMLInputElement || inputElement instanceof HTMLTextAreaElement)) { if (inputElement.minLength && inputElement.maxLength) { if (inputElement.minLength >= inputElement.maxLength) { issues.push(`Invalid length constraints for "${inputElement.id || inputElement.name}"`); } } } }); // Check for form validation patterns and structure const forms = document.querySelectorAll('form'); forms.forEach(form => { const formElement = form; // Check if form has validation patterns const hasClientValidation = formElement.querySelector('[pattern], [required], [minlength], [maxlength], [min], [max]'); if (!hasClientValidation) { warnings.push('Form lacks client-side validation attributes'); } // Check for form submission handling const submitButtons = formElement.querySelectorAll('button[type="submit"], input[type="submit"]'); if (submitButtons.length === 0) { issues.push('Form lacks submit button'); } // Note: Reset functionality is checked in ValidationErrorHandlingTest for error recovery context }); // Check for validation message structure and patterns (not accessibility - that's handled by ValidationErrorHandlingTest) const validationMessages = document.querySelectorAll('[data-validation], [data-error], .validation-message'); validationMessages.forEach(message => { const targetId = message.getAttribute('data-for') || message.getAttribute('data-target'); if (targetId) { const target = document.getElementById(targetId); if (!target) { issues.push(`Validation message references non-existent element: ${targetId}`); } // Note: aria-describedby association is checked in ValidationErrorHandlingTest } }); // Check for real-time validation const realTimeValidation = document.querySelectorAll('[data-validate-on-input], [data-validate-on-blur]'); if (realTimeValidation.length === 0) { warnings.push('No real-time validation detected (consider adding for better UX)'); } // Check for validation feedback timing const instantValidation = document.querySelectorAll('[data-validate-instantly]'); if (instantValidation.length > 0) { warnings.push('Instant validation may be disruptive to users (consider delayed validation)'); } // Check for validation error recovery const errorRecovery = document.querySelectorAll('[data-error-recovery], .error-recovery'); if (errorRecovery.length === 0 && validationMessages.length > 0) { warnings.push('No error recovery mechanisms detected'); } // Check for validation success feedback const successMessages = document.querySelectorAll('.success, .valid, [data-success]'); if (successMessages.length === 0 && formInputs.length > 0) { warnings.push('No success feedback patterns detected'); } // Check for validation error structure (accessibility aspects handled by ValidationErrorHandlingTest) const validationErrors = document.querySelectorAll('.error, .invalid, [aria-invalid="true"]'); validationErrors.forEach(error => { const errorText = error.textContent?.trim(); if (!errorText || errorText.length < 5) { issues.push('Validation error message too brief or empty'); } // Note: Screen reader visibility is checked in ValidationErrorHandlingTest }); // Check for validation consistency const emailInputs = document.querySelectorAll('input[type="email"]'); const emailPatterns = new Set(); emailInputs.forEach(input => { const pattern = input.getAttribute('pattern'); if (pattern) emailPatterns.add(pattern); }); if (emailPatterns.size > 1) { warnings.push('Inconsistent email validation patterns detected'); } // Check for validation performance const complexPatterns = document.querySelectorAll('[pattern*=".*"], [pattern*=".+"]'); if (complexPatterns.length > 5) { warnings.push('Many complex validation patterns may impact performance'); } return { issues, warnings }; }); return this.createResult(result.issues.length === 0, result.issues.length + result.warnings.length, result.issues, result.warnings); } catch (error) { return this.createErrorResult(`Validation form validation test failed: ${error}`); } } } exports.ValidationFormValidationTest = ValidationFormValidationTest; //# sourceMappingURL=validation-form-validation-test.js.map