import { test, expect } from '@playwright/test'; /** * Form Validation Tests * * Tests that all forms have labels, hints, validation, and disabled-state explanations */ test.describe('Form Validation', () => { test('Forms have labels for inputs', async ({ page }) => { await page.goto('/plans'); await page.waitForLoadState('networkidle'); // Find all form inputs const inputs = page.locator('input, select, textarea'); const inputCount = await inputs.count(); if (inputCount > 0) { for (let i = 0; i < Math.min(inputCount, 10); i++) { const input = inputs.nth(i); const isVisible = await input.isVisible(); if (isVisible) { // Check if input has an associated label const id = await input.getAttribute('id'); let hasLabel = false; if (id) { const label = page.locator(`label[for="${id}"]`); hasLabel = await label.count() > 0; } // Also check for aria-label or aria-labelledby const ariaLabel = await input.getAttribute('aria-label'); const ariaLabelledby = await input.getAttribute('aria-labelledby'); const hasAriaLabel = ariaLabel !== null || ariaLabelledby !== null; // Input should have some form of label expect(hasLabel || hasAriaLabel).toBeTruthy(); } } } }); test('Forms have hints or help text', async ({ page }) => { await page.goto('/plans'); await page.waitForLoadState('networkidle'); // Look for form hints const hints = page.locator('[class*="hint"], [class*="help"], small, .description'); const hintCount = await hints.count(); // Hints may not be present on all forms, but should exist in the codebase // This test verifies that hint components are available }); test('Forms show validation errors', async ({ page }) => { await page.goto('/plans'); await page.waitForLoadState('networkidle'); // Find required form inputs const requiredInputs = page.locator('input[required], select[required], textarea[required]'); const requiredCount = await requiredInputs.count(); if (requiredCount > 0) { // Try to submit form without filling required fields const submitButton = page.locator('button[type="submit"]'); const submitCount = await submitButton.count(); if (submitCount > 0) { await submitButton.first().click(); await page.waitForTimeout(500); // Check for validation error messages const errors = page.locator('[role="alert"], .error, .validation-error, [class*="error"]'); const errorCount = await errors.count(); // Validation errors should appear for required fields expect(errorCount).toBeGreaterThan(0); } } }); test('Disabled inputs have explanations', async ({ page }) => { await page.goto('/plans'); await page.waitForLoadState('networkidle'); // Find disabled inputs const disabledInputs = page.locator('input:disabled, select:disabled, textarea:disabled, button:disabled'); const disabledCount = await disabledInputs.count(); if (disabledCount > 0) { for (let i = 0; i < Math.min(disabledCount, 5); i++) { const input = disabledInputs.nth(i); // Check for aria-disabled or title attribute const ariaDisabled = await input.getAttribute('aria-disabled'); const title = await input.getAttribute('title'); const disabledTitle = await input.getAttribute('aria-label'); // Disabled inputs should have some explanation const hasExplanation = title !== null || disabledTitle !== null || ariaDisabled === 'true'; expect(hasExplanation).toBeTruthy(); } } }); test('Form fields have proper types', async ({ page }) => { await page.goto('/plans'); await page.waitForLoadState('networkidle'); // Check that number inputs have type="number" const numberInputs = page.locator('input[type="number"]'); const numberCount = await numberInputs.count(); // Check that email inputs have type="email" const emailInputs = page.locator('input[type="email"]'); const emailCount = await emailInputs.count(); // Check that password inputs have type="password" const passwordInputs = page.locator('input[type="password"]'); const passwordCount = await passwordInputs.count(); // These should exist for proper form validation // This test verifies that proper input types are used }); test('Forms have accessible error messages', async ({ page }) => { await page.goto('/plans'); await page.waitForLoadState('networkidle'); // Look for form validation errors const errors = page.locator('[role="alert"], .error'); const errorCount = await errors.count(); if (errorCount > 0) { for (let i = 0; i < Math.min(errorCount, 5); i++) { const error = errors.nth(i); // Check ARIA attributes const role = await error.getAttribute('role'); const ariaLive = await error.getAttribute('aria-live'); // Error messages should have proper ARIA attributes expect(role || ariaLive).toBeTruthy(); } } }); });