Saem gsap, motion, clsx e tailwind-merge; entram server-only, @playwright/test e @axe-core/playwright. Suíte e2e (rotas, axe, teclado, navegação, sem JS, rolagem) rodável com pnpm test:e2e contra BASE_URL.
39 lines
1.4 KiB
TypeScript
39 lines
1.4 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import type { APIRequestContext } from "@playwright/test";
|
|
|
|
/** Caminhos (`/`, `/cases/…`) de todas as rotas do /sitemap.xml, sem o domínio de produção. */
|
|
export async function sitemapPaths(request: APIRequestContext) {
|
|
const response = await request.get("/sitemap.xml");
|
|
if (response.status() !== 200)
|
|
throw new Error(`/sitemap.xml respondeu ${response.status()}`);
|
|
const xml = await response.text();
|
|
const paths = [...xml.matchAll(/<loc>\s*([^<\s]+)\s*<\/loc>/g)].map(
|
|
([, loc]) => {
|
|
const { pathname } = new URL(loc);
|
|
return pathname === "" ? "/" : pathname;
|
|
},
|
|
);
|
|
return [...new Set(paths)];
|
|
}
|
|
|
|
/** Nomes dos projetos de cliente, como a home os lista (public/portfolio-data.json, kind "cliente"). */
|
|
export function clientProjectNames() {
|
|
const data = JSON.parse(
|
|
readFileSync(
|
|
join(__dirname, "..", "public", "portfolio-data.json"),
|
|
"utf8",
|
|
),
|
|
) as { projects: Array<{ title: string; kind?: string }> };
|
|
return data.projects
|
|
.filter((project) => project.kind === "cliente")
|
|
.map((project) => project.title);
|
|
}
|
|
|
|
/** Texto do cabeçalho da seção de contato da home. */
|
|
export const CONTACT_HEADING =
|
|
"Vamos conversar sobre o que sua empresa precisa.";
|
|
|
|
export const DESKTOP = { width: 1440, height: 900 } as const;
|
|
export const PHONE = { width: 390, height: 844 } as const;
|