Initial commit: BlueCap Strategies Astro site
Static Astro 5 site for BlueCap Strategies with exact live-site content parity, deployed to k3s via GitOps (ArgoCD). - Page copy in Markdown content collections (services, focus-areas, pages, insights) with a Zod SEO schema enforced at build time - Homepage + About restored to exact live copy; real live-site imagery - Build-time SEO linter (scripts/seo-lint.mjs) and Playwright e2e suite - Multi-stage Dockerfile (nginx serves dist/) and Kustomize manifests (k8s/) - Per-agent robots.txt; config-driven PostHog + Umami analytics scaffold - ADRs and engineering docs under docs/ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
cd91c4b97c
92 files changed
+10246
No files matched your search
@@ -0,0 +1,134 @@
|
||||
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<string>();
|
||||
|
||||
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.");
|
||||
await page.locator("form").evaluate((form) => {
|
||||
form.setAttribute("data-endpoint", "/thank-you/");
|
||||
form.setAttribute("action", "/thank-you/");
|
||||
form.setAttribute("method", "get");
|
||||
});
|
||||
await submit.click();
|
||||
await expect(page).toHaveURL(/\/thank-you\/(?:\?.*)?$/);
|
||||
await expect(page.getByRole("heading", { name: "Thank you" })).toBeVisible();
|
||||
});
|
||||
Reference in new issue
Block a user