feat(cases): prova certa por serviço, páginas internas com trabalho no topo

Diagnóstico sem case que não cabe; desktop com o GCC Installer como projeto próprio; IoT com o Twins Lab e a nota da Apolo. Mosaico de pôsteres em /servicos e /cases, faixa baixa em uma coluna no currículo, privacidade e laboratório; Laboratório como lista compacta; SEV começando por catálogo, pedido no WhatsApp e painel; Joinville e SC nos títulos; imagens do LCP com fetchPriority e sizes reais; pré-carregamento só no hover; 404 com Open Graph próprio.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 17:01:35 -03:00
1 parent 7b633f0f4a
commit de6af74a0c
37 files changed
+1062 -254

No files matched your search

+32 -12
View File
@@ -51,7 +51,8 @@ function readJsonLdEntities(container: HTMLElement): Record<string, unknown>[] {
/**
* Nenhum link para /#contact nem para o GitHub; o contato é o WhatsApp com o contexto do case.
* Case de cliente: "projeto parecido"; projeto de laboratório: "problema parecido".
* Case de cliente: "projeto parecido"; projeto de laboratório: "problema parecido" no botão. A mensagem é a mesma
* frase nos dois, a do cabeçalho: "um projeto parecido com o {título}".
*/
function expectContactAndNoDeadLinks(caseStudy: { id: string; title: string }) {
const hrefs = screen
@@ -69,9 +70,7 @@ function expectContactAndNoDeadLinks(caseStudy: { id: string; title: string }) {
const destination = new URL(cta.getAttribute("href") ?? "");
expect(destination.origin).toBe("https://wa.me");
expect(destination.searchParams.get("text")).toContain(
isClient
? `um projeto parecido com o da ${caseStudy.title}`
: `um problema parecido com o do projeto ${caseStudy.title}`,
`um projeto parecido com o ${caseStudy.title}`,
);
expect(cta).toHaveAttribute("target", "_blank");
expect(cta).toHaveAttribute("rel", expect.stringContaining("noopener"));
@@ -590,16 +589,36 @@ it("lists client cases with a cover first, then the laboratory, and keeps the sc
clientCaseIds.map((id) => `/cases/${id}`),
);
// laboratório: sem capa
const labLinks = screen.getAllByRole("link", { name: "Ver o projeto" });
// laboratório: lista compacta, uma linha por projeto (nome, o que é, "Projeto próprio" e o link), sem capa nem cartão
const labLinks = screen.getAllByRole("link", { name: /^Ver o projeto / });
expect(labLinks.map((link) => link.getAttribute("href"))).toEqual(
labCaseIds.map((id) => `/cases/${id}`),
);
for (const link of labLinks) {
const card = link.closest("article");
const labList = labLinks[0].closest("ul");
expect(labList?.querySelectorAll(":scope > li")).toHaveLength(
labCaseIds.length,
);
expect(labList?.querySelector("article, .ds-card, .ds-bframe")).toBeNull();
labLinks.forEach((link, index) => {
const row = link.closest("li");
const caseStudy = getCase(labCaseIds[index]);
expect(isBefore(labHeading, link)).toBe(true);
expect(card?.querySelector(".ds-bframe")).toBeNull();
}
expect(link).toHaveAccessibleName(`Ver o projeto ${caseStudy.title}`);
expect(
within(row as HTMLElement).getByRole("heading", {
level: 3,
name: caseStudy.title,
}),
).toBeVisible();
expect(
within(row as HTMLElement).getByText(caseStudy.subtitle),
).toBeVisible();
expect(
within(row as HTMLElement).getByText("Projeto próprio"),
).toBeVisible();
// sem o resumo longo do cartão antigo
expect(row).not.toHaveTextContent(caseStudy.summary);
});
// contato pelo WhatsApp, sem /#contact nem GitHub
const hrefs = screen
@@ -639,10 +658,11 @@ it("lists client cases with a cover first, then the laboratory, and keeps the sc
};
const visibleItems = [...clientLinks, ...labLinks].map((link, index) => {
const href = link.getAttribute("href");
const card = link.closest("article");
// cartão do cliente ou linha da lista do laboratório
const card = link.closest("article, li");
if (!card) throw new Error(`Missing visible card for ${href}`);
const expected = getCase((href ?? "").replace("/cases/", ""));
expect(card.querySelector(".ds-kicker")).toHaveTextContent(expected.title);
expect(card).toHaveTextContent(expected.title);
return {
"@type": "ListItem",
position: index + 1,