chore: testes de ponta a ponta no CI e limpeza do repositório

Job e2e separado no CI; testes de teclado, rolagem, sem JS, axe no celular e menu do celular; limites de metadados derivados do sitemap; sai @react-three/fiber e @react-three/drei; pastas de ferramentas (.augment, .kiro, .claude/settings.local.json) fora do Git.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 17:01:37 -03:00
1 parent 6ac87824f3
commit 1a60795107
57 files changed
+625 -12090

No files matched your search

+46 -10
View File
@@ -1,11 +1,44 @@
import { expect, test } from "@playwright/test";
import { expect, type Locator, test } from "@playwright/test";
import { CONTACT_HEADING, clientProjectNames, DESKTOP } from "./helpers";
/** (e) Com JavaScript desligado, a home mostra o texto do contato e o nome dos clientes. */
/**
* (e) Com JavaScript desligado, a home mostra o texto do contato e o nome dos clientes: no HTML, visíveis e com
* opacidade calculada acima de 0,5 (um texto "visível" mas a 0,1 de opacidade, à espera de uma animação que não
* roda sem JavaScript, não serve a quem lê).
*/
const MIN_OPACITY = 0.5;
test.use({ viewport: DESKTOP, javaScriptEnabled: false });
test("sem JavaScript a home mostra o contato e os nomes dos 4 clientes", async ({
/** Opacidade efetiva: o produto das opacidades calculadas do elemento e de todos os seus ancestrais. */
function effectiveOpacity(locator: Locator) {
return locator.evaluate((element) => {
let opacity = 1;
for (let node: Element | null = element; node; node = node.parentElement)
opacity *= Number.parseFloat(getComputedStyle(node).opacity);
return opacity;
});
}
/**
* Leva o texto ao meio da janela, como quem lê rolando (a passagem entre seções é um fade ligado à rolagem, em CSS
* puro: antes de a seção chegar à janela o conteúdo espera a 0, e isso não depende de JavaScript) e espera a
* opacidade passar de MIN_OPACITY: as animações de entrada em CSS levam um instante para terminar.
*/
async function expectReadable(locator: Locator, what: string) {
await locator.evaluate((element) =>
element.scrollIntoView({ block: "center", behavior: "instant" }),
);
await expect
.poll(() => effectiveOpacity(locator), {
message: `opacidade de ${what}`,
timeout: 5_000,
})
.toBeGreaterThan(MIN_OPACITY);
}
test("sem JavaScript a home mostra o contato e os nomes dos 4 clientes, legíveis", async ({
page,
}) => {
const clients = clientProjectNames();
@@ -14,14 +47,17 @@ test("sem JavaScript a home mostra o contato e os nomes dos 4 clientes", async (
const response = await page.goto("/", { waitUntil: "load" });
expect(response?.status()).toBe(200);
await expect(
page.getByRole("heading", { level: 2, name: CONTACT_HEADING }),
).toBeVisible();
const heading = page.getByRole("heading", {
level: 2,
name: CONTACT_HEADING,
});
await expect(heading).toBeVisible();
await expectReadable(heading, "o texto do contato");
for (const name of clients) {
await expect(
page.getByText(name, { exact: false }).first(),
`cliente "${name}"`,
).toBeVisible();
const text = page.getByText(name, { exact: false }).first();
await expect(text, `cliente "${name}"`).toBeVisible();
await expectReadable(text, `o cliente "${name}"`);
}
// o texto está no HTML entregue, não só na tela: vale também para quem lê sem renderizar
const html = await (await page.request.get("/")).text();