import { test, expect } from '@playwright/test'; test.describe('Contact Form Tests', () => { test.beforeEach(async ({ page }) => { await page.goto('/contact'); }); test('contact form is visible', async ({ page }) => { // The form might be within the page content const form = page.locator('form').first(); await expect(form).toBeVisible(); }); test('all form fields are present', async ({ page }) => { // Check required fields exist await expect(page.locator('input[name="name"], input[id="name"], input[placeholder*="name" i]').first()).toBeVisible(); await expect(page.locator('input[name="email"], input[id="email"], input[type="email"]').first()).toBeVisible(); await expect(page.locator('textarea[name="message"], textarea[id="message"], textarea').first()).toBeVisible(); }); test('form validates required fields', async ({ page }) => { // Try to submit empty form const submitButton = page.locator('button[type="submit"], input[type="submit"]').first(); await submitButton.click(); // Check for validation - either HTML5 validation or custom error messages const nameInput = page.locator('input[name="name"], input[id="name"], input[placeholder*="name" i]').first(); const isInvalid = await nameInput.evaluate((el: HTMLInputElement) => !el.checkValidity()); // Form should show some validation indicator expect(isInvalid || await page.locator('[class*="error"], [class*="invalid"], .text-red').count() > 0).toBeTruthy(); }); test('email validation works', async ({ page }) => { const emailInput = page.locator('input[name="email"], input[id="email"], input[type="email"]').first(); // Enter invalid email await emailInput.fill('invalid-email'); await emailInput.blur(); // Check for error indication const hasError = await emailInput.evaluate((el: HTMLInputElement) => !el.checkValidity()) || await page.locator('[class*="error"], [class*="invalid"]').count() > 0; expect(hasError).toBeTruthy(); // Clear and enter valid email await emailInput.fill('valid@email.com'); const isValid = await emailInput.evaluate((el: HTMLInputElement) => el.checkValidity()); expect(isValid).toBeTruthy(); }); test('form fields accept input', async ({ page }) => { const nameInput = page.locator('input[name="name"], input[id="name"], input[placeholder*="name" i]').first(); const emailInput = page.locator('input[name="email"], input[id="email"], input[type="email"]').first(); const messageInput = page.locator('textarea[name="message"], textarea[id="message"], textarea').first(); await nameInput.fill('John Doe'); await emailInput.fill('john@example.com'); await messageInput.fill('This is a test message for the contact form.'); await expect(nameInput).toHaveValue('John Doe'); await expect(emailInput).toHaveValue('john@example.com'); await expect(messageInput).toHaveValue('This is a test message for the contact form.'); }); test('phone field accepts valid formats', async ({ page }) => { const phoneInput = page.locator('input[name="phone"], input[id="phone"], input[type="tel"]').first(); if (await phoneInput.isVisible()) { // Test different phone formats const validPhones = ['(555) 123-4567', '555-123-4567', '+1 555 123 4567']; for (const phone of validPhones) { await phoneInput.fill(phone); await expect(phoneInput).toHaveValue(phone); await phoneInput.clear(); } } }); test('subject dropdown works', async ({ page }) => { const subjectSelect = page.locator('select[name="subject"], select[id="subject"]').first(); if (await subjectSelect.isVisible()) { // Get all options const options = await subjectSelect.locator('option').all(); expect(options.length).toBeGreaterThan(1); // Select an option await subjectSelect.selectOption({ index: 1 }); const selectedValue = await subjectSelect.inputValue(); expect(selectedValue).not.toBe(''); } }); test('honeypot field is hidden', async ({ page }) => { // Look for common honeypot field names const honeypotLocator = page.locator('input[name="website"], input[name="url"], input[name="honeypot"], input[name="bot"]'); const count = await honeypotLocator.count(); if (count > 0) { const honeypot = honeypotLocator.first(); // Honeypot should be hidden from users (various techniques) const isHidden = await honeypot.evaluate((el) => { const style = window.getComputedStyle(el); const parentStyle = el.parentElement ? window.getComputedStyle(el.parentElement) : null; const rect = el.getBoundingClientRect(); // Check various hiding techniques return style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0' || el.offsetParent === null || rect.left < -1000 || // Off-screen positioning (like -left-[9999px]) rect.top < -1000 || (parentStyle && (parentStyle.display === 'none' || parentStyle.visibility === 'hidden' || parseFloat(parentStyle.left) < -1000)); }); expect(isHidden).toBeTruthy(); } else { // No honeypot field found - that's fine, test passes expect(true).toBeTruthy(); } }); }); test.describe('Contact Form Accessibility', () => { test('form fields have labels', async ({ page }) => { await page.goto('/contact'); const inputs = await page.locator('input:not([type="hidden"]):not([type="submit"]), textarea, select').all(); for (const input of inputs) { const id = await input.getAttribute('id'); const ariaLabel = await input.getAttribute('aria-label'); const placeholder = await input.getAttribute('placeholder'); // Check if input has associated label if (id) { const label = page.locator(`label[for="${id}"]`); const hasLabel = await label.count() > 0 || ariaLabel || placeholder; expect(hasLabel).toBeTruthy(); } } }); test('submit button is accessible', async ({ page }) => { await page.goto('/contact'); const submitButton = page.locator('button[type="submit"], input[type="submit"]').first(); await expect(submitButton).toBeVisible(); // Button should be focusable await submitButton.focus(); await expect(submitButton).toBeFocused(); }); });