feat(homepage): expand inline lead form to name/email/phone/type/message
Ping Search Engines / Notify Search Engines (push) Successful in 3s
E2E Test Suite / Smoke Tests (P0) (push) Has been cancelled
E2E Test Suite / Critical User Journeys (push) Has been cancelled
E2E Test Suite / API Integration Tests (push) Has been cancelled
E2E Test Suite / Form Interaction Tests (push) Has been cancelled
E2E Test Suite / Destructive & Chaos Tests (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (chromium) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (firefox) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (webkit) (push) Has been cancelled
E2E Test Suite / Mobile Device Tests (push) Has been cancelled
E2E Test Suite / Security Header Tests (push) Has been cancelled
E2E Test Suite / Test Report Summary (push) Has been cancelled
Deploy to Production / Pre-Deploy Tests (push) Failing after 10m58s
Deploy to Production / Build & Verify (push) Failing after 16m37s
Deploy to Production / Deploy to Fly.io (push) Failing after 14m3s
Deploy to Production / Deploy to VPS (PM2) (push) Failing after 14m3s
Deploy to Production / Deploy to Render (push) Failing after 14m55s
Deploy to Production / Deploy to Railway (push) Failing after 14m55s
Deploy to Production / Post-Deploy Verification (push) Failing after 13m32s
Deploy to Production / Notify on Failure (push) Failing after 13m51s
Ping Search Engines / Notify Search Engines (push) Successful in 3s
E2E Test Suite / Smoke Tests (P0) (push) Has been cancelled
E2E Test Suite / Critical User Journeys (push) Has been cancelled
E2E Test Suite / API Integration Tests (push) Has been cancelled
E2E Test Suite / Form Interaction Tests (push) Has been cancelled
E2E Test Suite / Destructive & Chaos Tests (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (chromium) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (firefox) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (webkit) (push) Has been cancelled
E2E Test Suite / Mobile Device Tests (push) Has been cancelled
E2E Test Suite / Security Header Tests (push) Has been cancelled
E2E Test Suite / Test Report Summary (push) Has been cancelled
Deploy to Production / Pre-Deploy Tests (push) Failing after 10m58s
Deploy to Production / Build & Verify (push) Failing after 16m37s
Deploy to Production / Deploy to Fly.io (push) Failing after 14m3s
Deploy to Production / Deploy to VPS (PM2) (push) Failing after 14m3s
Deploy to Production / Deploy to Render (push) Failing after 14m55s
Deploy to Production / Deploy to Railway (push) Failing after 14m55s
Deploy to Production / Post-Deploy Verification (push) Failing after 13m32s
Deploy to Production / Notify on Failure (push) Failing after 13m51s
Rework the homepage InlineLeadForm from a single combined "email or phone" contact field to an explicit 5-field layout — name, email (type=email), phone (type=tel), project type and an optional message — so each field gets the correct mobile keyboard and leads carry a real email + phone separately. - Required fields kept to name + email + project type for a low-friction mobile flow; phone and message are optional. - Email uses type=email + inputmode=email; phone uses type=tel + inputmode=tel for the numeric keypad. Message is an optional textarea; when blank the client synthesizes a >=10 char summary so the /api/contact validator passes. - Still posts to the same /api/contact endpoint tagged source=homepage-inline, delivering leads exactly the same way the /contact page does (no page nav). - Update inline-lead-form.spec.ts to the new field set and keyboards. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
870075b33f
commit
5a6db9c20f
@@ -6,7 +6,10 @@ import { test, expect } from '@playwright/test';
|
||||
* Covers:
|
||||
* - The form is mounted on the homepage between FAQ/tech-stack and the
|
||||
* final CTA band.
|
||||
* - Filling all three fields and submitting calls /api/contact and shows
|
||||
* - It exposes the compact 5-field layout (name, email, phone, project type,
|
||||
* message) with the correct mobile keyboard hints per field. Only name,
|
||||
* email and project type are required; phone and message are optional.
|
||||
* - Filling the required fields and submitting calls /api/contact and shows
|
||||
* an in-place thank-you state without a full-page navigation.
|
||||
* - The submission is tagged `source: 'homepage-inline'` so leads can be
|
||||
* segmented downstream.
|
||||
@@ -15,7 +18,7 @@ import { test, expect } from '@playwright/test';
|
||||
*/
|
||||
|
||||
test.describe('Inline Lead Form (homepage)', () => {
|
||||
test('form is visible on the homepage and has the three required fields', async ({ page }) => {
|
||||
test('form is visible on the homepage with the compact field set and mobile keyboards', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
const form = page.getByTestId('inline-lead-form');
|
||||
@@ -23,8 +26,10 @@ test.describe('Inline Lead Form (homepage)', () => {
|
||||
await expect(form).toBeVisible();
|
||||
|
||||
await expect(page.locator('#inline-lead-name')).toBeVisible();
|
||||
await expect(page.locator('#inline-lead-contact')).toBeVisible();
|
||||
await expect(page.locator('#inline-lead-email')).toBeVisible();
|
||||
await expect(page.locator('#inline-lead-phone')).toBeVisible();
|
||||
await expect(page.locator('#inline-lead-project-type')).toBeVisible();
|
||||
await expect(page.locator('#inline-lead-message')).toBeVisible();
|
||||
|
||||
// Touch-target sanity: the submit button must be at least 48px tall.
|
||||
const submit = page.locator('#inline-lead-submit');
|
||||
@@ -32,9 +37,17 @@ test.describe('Inline Lead Form (homepage)', () => {
|
||||
expect(submitBox).not.toBeNull();
|
||||
expect(submitBox!.height).toBeGreaterThanOrEqual(48);
|
||||
|
||||
// Correct mobile-friendly attributes on the contact field.
|
||||
await expect(page.locator('#inline-lead-contact')).toHaveAttribute('autocomplete', 'email tel');
|
||||
await expect(page.locator('#inline-lead-contact')).toHaveAttribute('inputmode', 'email');
|
||||
// Correct mobile keyboard per field.
|
||||
await expect(page.locator('#inline-lead-email')).toHaveAttribute('type', 'email');
|
||||
await expect(page.locator('#inline-lead-email')).toHaveAttribute('inputmode', 'email');
|
||||
await expect(page.locator('#inline-lead-email')).toHaveAttribute('autocomplete', 'email');
|
||||
await expect(page.locator('#inline-lead-phone')).toHaveAttribute('type', 'tel');
|
||||
await expect(page.locator('#inline-lead-phone')).toHaveAttribute('inputmode', 'tel');
|
||||
await expect(page.locator('#inline-lead-phone')).toHaveAttribute('autocomplete', 'tel');
|
||||
|
||||
// Phone + message are optional (no `required` attribute).
|
||||
await expect(page.locator('#inline-lead-phone')).not.toHaveAttribute('required', /.*/);
|
||||
await expect(page.locator('#inline-lead-message')).not.toHaveAttribute('required', /.*/);
|
||||
});
|
||||
|
||||
test('blocks submit when required fields are empty', async ({ page }) => {
|
||||
@@ -57,12 +70,12 @@ test.describe('Inline Lead Form (homepage)', () => {
|
||||
|
||||
// The blank-field errors should surface and the network should NOT be hit.
|
||||
await expect(page.locator('#inline-lead-name-error')).toBeVisible();
|
||||
await expect(page.locator('#inline-lead-contact-error')).toBeVisible();
|
||||
await expect(page.locator('#inline-lead-email-error')).toBeVisible();
|
||||
await expect(page.locator('#inline-lead-project-type-error')).toBeVisible();
|
||||
expect(apiCalled).toBe(false);
|
||||
});
|
||||
|
||||
test('fills and submits the form successfully with an email contact', async ({ page }) => {
|
||||
test('fills the required fields and submits successfully', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
const form = page.getByTestId('inline-lead-form');
|
||||
await form.scrollIntoViewIfNeeded();
|
||||
@@ -85,7 +98,7 @@ test.describe('Inline Lead Form (homepage)', () => {
|
||||
});
|
||||
|
||||
await page.locator('#inline-lead-name').fill('Inline Lead Tester');
|
||||
await page.locator('#inline-lead-contact').fill('inline-lead@example.com');
|
||||
await page.locator('#inline-lead-email').fill('inline-lead@example.com');
|
||||
await page.locator('#inline-lead-project-type').selectOption('Web App');
|
||||
await page.locator('#inline-lead-submit').click();
|
||||
|
||||
@@ -103,11 +116,14 @@ test.describe('Inline Lead Form (homepage)', () => {
|
||||
expect(captured.name).toBe('Inline Lead Tester');
|
||||
expect(captured.email).toBe('inline-lead@example.com');
|
||||
expect(captured.subject).toBe('web-development');
|
||||
// Message is synthesized when left blank so the backend's >=10 char rule passes.
|
||||
expect(typeof captured.message).toBe('string');
|
||||
expect(captured.message.length).toBeGreaterThanOrEqual(10);
|
||||
// Honeypot must be empty for a real user submission.
|
||||
expect(captured.website ?? '').toBe('');
|
||||
});
|
||||
|
||||
test('routes a phone-shaped contact value into the phone field', async ({ page }) => {
|
||||
test('sends the phone and a typed message through their own fields', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
const form = page.getByTestId('inline-lead-form');
|
||||
await form.scrollIntoViewIfNeeded();
|
||||
@@ -127,17 +143,19 @@ test.describe('Inline Lead Form (homepage)', () => {
|
||||
});
|
||||
|
||||
await page.locator('#inline-lead-name').fill('Phone Lead');
|
||||
await page.locator('#inline-lead-contact').fill('+91 95614 17403');
|
||||
await page.locator('#inline-lead-email').fill('phone-lead@example.com');
|
||||
await page.locator('#inline-lead-phone').fill('+91 95614 17403');
|
||||
await page.locator('#inline-lead-project-type').selectOption('Government / GeM');
|
||||
await page.locator('#inline-lead-message').fill('We need a GeM-ready procurement portal.');
|
||||
await page.locator('#inline-lead-submit').click();
|
||||
|
||||
await expect(page.getByTestId('inline-lead-success')).toBeVisible();
|
||||
|
||||
expect(captured).toBeTruthy();
|
||||
// The real email and phone are sent as-is in their own fields.
|
||||
expect(captured.email).toBe('phone-lead@example.com');
|
||||
expect(captured.phone).toBe('+91 95614 17403');
|
||||
// The client synthesizes a placeholder email so the backend's strict email
|
||||
// validator accepts phone-only contacts. The real phone is what matters.
|
||||
expect(captured.email).toMatch(/^lead-\d+@homepage\.workroot\.in$/);
|
||||
expect(captured.message).toBe('We need a GeM-ready procurement portal.');
|
||||
expect(captured.subject).toBe('government-systems');
|
||||
expect(captured.source).toBe('homepage-inline');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user