Files
2026-10-14 22:54:27 -03:00

110 lines
3.9 KiB
TypeScript

import { readFileSync } from "node:fs";
import { join } from "node:path";
import { type APIRequestContext, expect, type Page } 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)];
}
let devServer: Promise<boolean> | undefined;
/**
* O `BASE_URL` é um servidor de desenvolvimento (`next dev`)? O HTML dele traz o cliente de recarga a quente (HMR) e o
* React Refresh, que o build de produção não tem. Serve para a única coisa que só existe em um dos dois: avisos do
* React que só o desenvolvimento emite. (A barra de cookies é a mesma nos dois, e o gtag só carrega em produção.)
*/
export function isDevServer(request: APIRequestContext) {
devServer ??= request
.get("/")
.then((response) => response.text())
.then((html) => /hmr-client|react-refresh/.test(html))
.catch((error) => {
devServer = undefined;
throw error;
});
return devServer;
}
/** 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;
/** Opções de `test.use` do computador. */
export const DESKTOP_USE = { viewport: DESKTOP } as const;
/**
* Opções de `test.use` de um celular de verdade: a janela de 390x844 mais `isMobile` (viewport de celular, `<meta
* viewport>` valendo) e `hasTouch` (toque, `tap()`), e não só uma janela estreita.
*/
export const PHONE_USE = {
viewport: PHONE,
isMobile: true,
hasTouch: true,
deviceScaleFactor: 2,
} as const;
/** Chave do consentimento no localStorage (`CONSENT_STORAGE_KEY` em lib/analytics.ts). */
export const CONSENT_STORAGE_KEY = "evandro.analytics-consent.v1";
/** Os controles da barra de cookies, na ordem do Tab. */
export const COOKIE_BAR_CONTROLS = [
"Política de privacidade",
"Aceitar",
"Recusar",
] as const;
/**
* Deixa o consentimento já salvo no navegador (como quem aceitou ou recusou numa visita anterior): chamar antes do
* `page.goto`. Com ele a barra de cookies não abre. "denied" por padrão, para o gtag não carregar em produção.
*/
export async function saveConsent(
page: Page,
value: "granted" | "denied" = "denied",
) {
await page.addInitScript(
([key, stored]) => window.localStorage.setItem(key, stored),
[CONSENT_STORAGE_KEY, value],
);
}
/**
* A barra de cookies da primeira visita (id `cookie-bar`, `COOKIE_BAR_ID` em lib/analytics.ts), fixa na base da tela,
* antes do cabeçalho no documento. Ela é a mesma em qualquer servidor, de desenvolvimento ou de produção (só o
* carregamento do gtag depende do ambiente): se não aparecer, o teste reprova. Devolve o localizador da barra.
*/
export async function cookieBar(page: Page) {
const bar = page.getByRole("dialog", { name: "Cookies e privacidade" });
await expect(
bar,
"a barra de cookies não apareceu na primeira visita",
).toBeVisible({ timeout: 10_000 });
return bar;
}