import { expect, test } from "@playwright/test"; const expectedPages = [ "/", "/about-us/", "/contact/", "/services/", "/focus-areas/", "/insights/", "/market-intelligence-opportunity-analysis/", "/develop-and-deploy-catalytic-capital/", "/stakeholder-engagement-partnership-development/", "/sustainable-technology-incubation/", "/sustainable-business-model-innovation/", "/project-management-implementation/", "/impact-assessment-reporting/", "/policy-advocacy-development/", "/the-economics-of-pfas-remediation/", "/structuring-marine-conservation-finance/", "/sustainable-living-resource-utilization/", "/economic-development-for-island-and-coastal-communities/", ]; test.describe("page rendering", () => { for (const path of expectedPages) { test(`${path} renders meaningful HTML without horizontal overflow`, async ({ page }) => { const response = await page.goto(path); expect(response?.status(), path).toBeLessThan(400); await expect(page.locator("h1")).toBeVisible(); await expect(page.locator("main")).toContainText(/BlueCap|water|capital|aquatic|PFAS|marine/i); const overflow = await page.evaluate( () => document.documentElement.scrollWidth - document.documentElement.clientWidth, ); expect(overflow, `${path} has horizontal overflow`).toBeLessThanOrEqual(1); }); } }); test("all internal href links resolve", async ({ page, request, baseURL }) => { const found = new Set(); for (const path of expectedPages) { await page.goto(path); const links = await page.locator("a[href]").evaluateAll((anchors) => anchors.map((anchor) => (anchor as HTMLAnchorElement).href), ); for (const href of links) { if (href.startsWith("mailto:") || href.startsWith("tel:")) continue; const url = new URL(href); if (url.origin !== baseURL) continue; found.add(`${url.pathname}${url.search}`); } } for (const href of found) { const response = await request.get(href); expect(response.status(), href).toBeLessThan(400); } }); test("desktop navigation and CTA links are clickable", async ({ page }) => { await page.setViewportSize({ width: 1440, height: 900 }); await page.goto("/"); await page.getByRole("link", { name: "Explore services" }).click(); await expect(page).toHaveURL(/\/services\/$/); await expect(page.getByRole("heading", { name: /Advisory services/ })).toBeVisible(); await page.goto("/"); await page.getByRole("link", { name: "Request a consultation" }).first().click(); await expect(page).toHaveURL(/\/contact\/$/); await expect(page.getByRole("heading", { name: /Get in touch/ })).toBeVisible(); }); test("service and focus-area learn more links are wired to detail pages", async ({ page }) => { for (const path of ["/services/", "/focus-areas/"]) { await page.goto(path); const learnMoreLinks = page.getByRole("link", { name: "Learn more" }); const count = await learnMoreLinks.count(); expect(count, `${path} should expose Learn more links`).toBeGreaterThan(0); for (let index = 0; index < count; index += 1) { const href = await learnMoreLinks.nth(index).getAttribute("href"); expect(href, `${path} Learn more ${index + 1}`).toMatch(/^\/.+\/$/); const response = await page.request.get(href!); expect(response.status(), href ?? "").toBeLessThan(400); } } }); test("mobile menu opens and navigates", async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); await page.goto("/"); await page.locator(".mobile-nav summary").click(); await expect(page.locator(".mobile-nav nav")).toBeVisible(); await page.locator(".mobile-nav").getByRole("link", { name: "Focus Areas" }).click(); await expect(page).toHaveURL(/\/focus-areas\/$/); await expect(page.getByRole("heading", { name: /Focused expertise/ })).toBeVisible(); }); test("contact form validates required, invalid, and valid paths", async ({ page }) => { await page.goto("/contact/"); const submit = page.getByRole("button", { name: "Submit" }); await submit.click(); const emptyValidation = await page.locator('input[name="name"]').evaluate((input) => { return (input as HTMLInputElement).validationMessage.length > 0; }); expect(emptyValidation).toBe(true); await expect(page).toHaveURL(/\/contact\/$/); await page.locator('input[name="name"]').fill("Test User"); await page.locator('input[name="email"]').fill("not-an-email"); await page.locator('textarea[name="message"]').fill("Too short"); await submit.click(); const invalidEmail = await page.locator('input[name="email"]').evaluate((input) => { return !(input as HTMLInputElement).validity.valid; }); expect(invalidEmail).toBe(true); 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."); // 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.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(); });