import { test, expect } from '@playwright/test'; /** * E2E Form Interactions Test Suite * Deep testing of contact form with various scenarios */ test.describe('Contact Form - Complete Interaction Flow', () => { test.beforeEach(async ({ page }) => { await page.goto('/contact'); }); test('successful form submission with all fields', async ({ page }) => { // Fill all fields including optional ones await page.locator('input[name="name"]').fill('Alice Johnson'); await page.locator('#contact-form input[name="email"]').fill('alice.johnson@company.com'); await page.locator('input[name="phone"]').fill('+1 (555) 987-6543'); await page.locator('select[name="subject"]').selectOption('cloud-services'); await page.locator('textarea[name="message"]').fill('I need help migrating our infrastructure to the cloud. We currently run on-premise servers and want to move to AWS.'); // Submit const submitButton = page.locator('#contact-form button[type="submit"]'); await submitButton.click(); // Verify loading state await expect(submitButton).toBeDisabled(); // Verify success or API response (rate limiting may occur during testing) await page.waitForFunction(() => { const successState = document.getElementById('success-state'); const toastAlert = document.querySelector('#toast-container [role="alert"]'); const successVisible = successState && !successState.classList.contains('hidden'); const toastVisible = toastAlert && (toastAlert as HTMLElement).offsetParent !== null; return successVisible || toastVisible; }, { timeout: 10000 }); }); test('form validation - empty submission', async ({ page }) => { // Try to submit without filling anything await page.locator('#contact-form button[type="submit"]').click(); // Check for validation errors const nameInput = page.locator('input[name="name"]'); const emailInput = page.locator('#contact-form input[name="email"]'); const nameInvalid = await nameInput.evaluate((el: HTMLInputElement) => !el.checkValidity()); const emailInvalid = await emailInput.evaluate((el: HTMLInputElement) => !el.checkValidity()); expect(nameInvalid || await page.locator('[class*="error"]').count() > 0).toBeTruthy(); }); test('form validation - invalid email format', async ({ page }) => { await page.locator('input[name="name"]').fill('Test User'); await page.locator('#contact-form input[name="email"]').fill('invalid-email'); await page.locator('select[name="subject"]').selectOption('consulting'); await page.locator('textarea[name="message"]').fill('This is a test message.'); await page.locator('#contact-form button[type="submit"]').click(); // Should show email error const emailError = page.locator('[data-error="email"]'); await expect(emailError).toBeVisible(); }); test('form validation - name too short', async ({ page }) => { await page.locator('input[name="name"]').fill('A'); await page.locator('#contact-form input[name="email"]').fill('valid@email.com'); await page.locator('select[name="subject"]').selectOption('web-development'); await page.locator('textarea[name="message"]').fill('Test message content here.'); await page.locator('#contact-form button[type="submit"]').click(); // Should show name error const nameError = page.locator('[data-error="name"]'); await expect(nameError).toBeVisible(); }); test('form validation - message too short', async ({ page }) => { await page.locator('input[name="name"]').fill('Valid Name'); await page.locator('#contact-form input[name="email"]').fill('valid@email.com'); await page.locator('select[name="subject"]').selectOption('ai-ml'); await page.locator('textarea[name="message"]').fill('Short'); await page.locator('#contact-form button[type="submit"]').click(); // Should show message error const messageError = page.locator('[data-error="message"]'); await expect(messageError).toBeVisible(); }); test('form validation - real-time email correction', async ({ page }) => { const emailInput = page.locator('#contact-form input[name="email"]'); // Type invalid email await emailInput.fill('bad-email'); await emailInput.blur(); // Should show error await page.waitForTimeout(200); const hasError = await page.locator('[data-error="email"]').isVisible(); // Correct the email await emailInput.fill('good@email.com'); await emailInput.blur(); // Error should disappear await page.waitForTimeout(200); const errorGone = await page.locator('[data-error="email"]').isHidden(); expect(errorGone).toBeTruthy(); }); test('honeypot protection - bot detection', async ({ page }) => { // Fill form normally await page.locator('input[name="name"]').fill('Bot User'); await page.locator('#contact-form input[name="email"]').fill('bot@spam.com'); await page.locator('select[name="subject"]').selectOption('other'); await page.locator('textarea[name="message"]').fill('This is spam content.'); // Fill honeypot field (bots do this) await page.evaluate(() => { const honeypot = document.querySelector('input[name="website"]') as HTMLInputElement; if (honeypot) honeypot.value = 'http://spam.com'; }); await page.locator('#contact-form button[type="submit"]').click(); // Should show success (silent fail - honeypot triggers server-side reject but shows success to bot) await page.waitForFunction(() => { const successState = document.getElementById('success-state'); const toastAlert = document.querySelector('#toast-container [role="alert"]'); const successVisible = successState && !successState.classList.contains('hidden'); const toastVisible = toastAlert && (toastAlert as HTMLElement).offsetParent !== null; return successVisible || toastVisible; }, { timeout: 10000 }); }); test('phone field validation - various formats', async ({ page }) => { await page.locator('input[name="name"]').fill('Phone Tester'); await page.locator('#contact-form input[name="email"]').fill('phone@test.com'); await page.locator('select[name="subject"]').selectOption('support'); await page.locator('textarea[name="message"]').fill('Testing phone number formats.'); const phoneInput = page.locator('input[name="phone"]'); const validFormats = [ '+1 555-123-4567', '(555) 123-4567', '555.123.4567', '+1 (555) 123-4567' ]; for (const format of validFormats) { await phoneInput.fill(format); await expect(phoneInput).toHaveValue(format); } }); test('subject dropdown - all options selectable', async ({ page }) => { const subjectSelect = page.locator('select[name="subject"]'); const options = await subjectSelect.locator('option').all(); expect(options.length).toBeGreaterThan(1); // Try selecting each option for (let i = 1; i < options.length; i++) { const value = await options[i].getAttribute('value'); if (value) { await subjectSelect.selectOption(value); expect(await subjectSelect.inputValue()).toBe(value); } } }); test('form field focus states and keyboard navigation', async ({ page }) => { // Click on name field first to establish focus context, then verify tab order const nameInput = page.locator('input[name="name"]'); await nameInput.click(); await expect(nameInput).toBeFocused(); await page.keyboard.press('Tab'); await expect(page.locator('#contact-form input[name="email"]')).toBeFocused(); await page.keyboard.press('Tab'); await expect(page.locator('input[name="phone"]')).toBeFocused(); await page.keyboard.press('Tab'); await expect(page.locator('select[name="subject"]')).toBeFocused(); // Tab through budget radio buttons (optional field between subject and message) // There may be multiple radio buttons - tab until message textarea is focused for (let i = 0; i < 10; i++) { await page.keyboard.press('Tab'); const isMessageFocused = await page.locator('textarea[name="message"]').evaluate( el => document.activeElement === el ); if (isMessageFocused) break; } await expect(page.locator('textarea[name="message"]')).toBeFocused(); }); test('form persistence during session', async ({ page }) => { // Fill form partially await page.locator('input[name="name"]').fill('Partial Fill'); await page.locator('#contact-form input[name="email"]').fill('partial@test.com'); // Navigate away await page.goto('/services'); await expect(page).toHaveURL('/services'); // Navigate back await page.goto('/contact'); // Form should be empty (no persistence - expected behavior) await expect(page.locator('input[name="name"]')).toHaveValue(''); }); test('multiple rapid submissions prevented', async ({ page }) => { // Fill form await page.locator('input[name="name"]').fill('Rapid Submitter'); await page.locator('#contact-form input[name="email"]').fill('rapid@test.com'); await page.locator('select[name="subject"]').selectOption('web-development'); await page.locator('textarea[name="message"]').fill('Testing rapid submission prevention.'); const submitButton = page.locator('#contact-form button[type="submit"]'); // Click submit await submitButton.click(); // Button should be disabled immediately await expect(submitButton).toBeDisabled(); // Try clicking again (should not work) await submitButton.click({ force: true }); // Still disabled await expect(submitButton).toBeDisabled(); }); }); test.describe('Contact Form - Accessibility', () => { test('form labels associated with inputs', async ({ page }) => { await page.goto('/contact'); const fields = [ { id: 'name', label: 'Full Name' }, { id: 'email', label: 'Email Address' }, { id: 'phone', label: 'Phone' }, { id: 'subject', label: 'Service Needed' }, { id: 'message', label: 'Message' } ]; for (const field of fields) { const label = page.locator(`label[for="${field.id}"]`); await expect(label).toBeVisible(); const labelText = await label.textContent(); expect(labelText).toContain(field.label); } }); test('error messages are announced', async ({ page }) => { await page.goto('/contact'); // Submit empty form await page.locator('#contact-form button[type="submit"]').click(); // Check that error messages exist and are associated const errorMessages = page.locator('[data-error]'); expect(await errorMessages.count()).toBeGreaterThan(0); }); test('submit button has proper state', async ({ page }) => { await page.goto('/contact'); const submitButton = page.locator('#contact-form button[type="submit"]'); // Initially enabled await expect(submitButton).toBeEnabled(); // Fill and submit await page.locator('input[name="name"]').fill('Test User'); await page.locator('#contact-form input[name="email"]').fill('test@example.com'); await page.locator('select[name="subject"]').selectOption('consulting'); await page.locator('textarea[name="message"]').fill('Test message content.'); await submitButton.click(); // Disabled during submission await expect(submitButton).toBeDisabled(); }); }); test.describe('Contact Form - Mobile Experience', () => { test.use({ viewport: { width: 375, height: 667 } }); test('mobile form is fully usable', async ({ page }) => { await page.goto('/contact'); // All fields should be visible and clickable on mobile await page.locator('input[name="name"]').click(); await page.locator('input[name="name"]').fill('Mobile User'); await page.locator('#contact-form input[name="email"]').click(); await page.locator('#contact-form input[name="email"]').fill('mobile@test.com'); await page.locator('textarea[name="message"]').click(); await page.locator('textarea[name="message"]').fill('Testing mobile form interaction.'); // Submit button should be fully visible and clickable const submitButton = page.locator('#contact-form button[type="submit"]'); await submitButton.scrollIntoViewIfNeeded(); await expect(submitButton).toBeVisible(); }); test('mobile keyboard types are appropriate', async ({ page }) => { await page.goto('/contact'); // Email field should trigger email keyboard const emailInput = page.locator('#contact-form input[name="email"]'); expect(await emailInput.getAttribute('type')).toBe('email'); // Phone field should trigger tel keyboard const phoneInput = page.locator('input[name="phone"]'); expect(await phoneInput.getAttribute('type')).toBe('tel'); }); });