feat(contact): wire the form to a real endpoint at build time
The contact form could not be made to work by configuration. Two reasons, both fixed here. The endpoint never reached the browser. k8s/configmap.yaml carried PUBLIC_CONTACT_FORM_ENDPOINT, but the Deployment has no env/envFrom pointing at that ConfigMap, so it was dead config. Wiring it up would not have helped either: this is a static Astro build, so import.meta.env.PUBLIC_* is inlined when the image is built and the pod only serves pre-built HTML. Anything the browser needs has to arrive as a build arg. Add ARG/ENV to the Dockerfile and pass --build-arg from CI, sourced from a CONTACT_FORM_ENDPOINT Actions secret. Rewrite the ConfigMap as documentation of what the build expects, with an explicit warning not to add envFrom. Submission behaviour was also wrong for visitors. A native POST would have navigated them to the form provider's page; post in the background with fetch() and send them to our own /thank-you/ instead. On failure the form now stays put, re-enables the button and offers the phone and email fallback, rather than silently losing the inquiry. Add a honeypot (_gotcha, which Formspree treats as spam) positioned off-screen rather than display:none, so bots that skip hidden fields still fill it. Read data-endpoint at submit time rather than caching it at init, keeping the attribute the single source of truth. Tests: replace the old contact assertion, which faked a native GET navigation, with one that mocks the endpoint and exercises the real fetch path including the posted payload. Add a case for endpoint failure. typecheck 0/0/0, build 0/0, seo:lint clean on 20 pages, e2e 44 passed. The 4 remaining failures are the pre-existing stale nav assertions in docs/launch-checklist.md section 2.1, untouched here.
This commit is contained in:
1 parent
8a4c4bccec
commit
0f769d7bdd
6 files changed
+143
-20
No files matched your search
+35
-5
@@ -123,12 +123,42 @@ test("contact form validates required, invalid, and valid paths", async ({ page
|
||||
|
||||
await page.locator('input[name="email"]').fill("test@example.com");
|
||||
await page.locator('textarea[name="message"]').fill("This is a valid test inquiry for BlueCap Strategies.");
|
||||
await page.locator("form").evaluate((form) => {
|
||||
form.setAttribute("data-endpoint", "/thank-you/");
|
||||
form.setAttribute("action", "/thank-you/");
|
||||
form.setAttribute("method", "get");
|
||||
|
||||
// Exercise the real submit path: the form POSTs to the configured endpoint in the background
|
||||
// and only then sends the visitor to our own /thank-you/. Mock the endpoint so the test never
|
||||
// touches the live form provider.
|
||||
const endpoint = "https://forms.test.invalid/f/mock";
|
||||
let posted: string | null = null;
|
||||
await page.route(endpoint, async (route) => {
|
||||
posted = route.request().postData();
|
||||
await route.fulfill({ status: 200, contentType: "application/json", body: '{"ok":true}' });
|
||||
});
|
||||
await page.locator("form").evaluate((form, url) => {
|
||||
form.setAttribute("data-endpoint", url);
|
||||
}, endpoint);
|
||||
|
||||
await submit.click();
|
||||
await expect(page).toHaveURL(/\/thank-you\/(?:\?.*)?$/);
|
||||
await expect(page).toHaveURL(/\/thank-you\/$/);
|
||||
await expect(page.getByRole("heading", { name: "Thank you" })).toBeVisible();
|
||||
expect(posted, "form fields should reach the endpoint").toContain("test@example.com");
|
||||
});
|
||||
|
||||
test("contact form surfaces an error when the endpoint fails", async ({ page }) => {
|
||||
await page.goto("/contact/");
|
||||
const endpoint = "https://forms.test.invalid/f/mock";
|
||||
await page.route(endpoint, (route) => route.fulfill({ status: 500, body: "nope" }));
|
||||
await page.locator("form").evaluate((form, url) => {
|
||||
form.setAttribute("data-endpoint", url);
|
||||
}, endpoint);
|
||||
|
||||
await page.locator('input[name="name"]').fill("Test User");
|
||||
await page.locator('input[name="email"]').fill("test@example.com");
|
||||
await page.locator('textarea[name="message"]').fill("This is a valid test inquiry for BlueCap Strategies.");
|
||||
await page.getByRole("button", { name: "Submit" }).click();
|
||||
|
||||
// A failed send must not strand the visitor: it stays on the page, says so, and offers the
|
||||
// phone/email fallback rather than silently losing the inquiry.
|
||||
await expect(page).toHaveURL(/\/contact\/$/);
|
||||
await expect(page.locator("[data-form-status]")).toContainText("didn't send");
|
||||
await expect(page.getByRole("button", { name: "Submit" })).toBeEnabled();
|
||||
});
|
||||
Reference in new issue
Block a user