feat(servicos): ofertas no topo de /servicos, capa por assunto e diagramas

/servicos abre com as 3 ofertas e depois outros serviços; H1 de criação de sites e de engenharia com o nome da oferta; capa de cada serviço pelo assunto (SEV fora do próprio case cai de ~11 para 2 imagens); diagrama de fluxo em SVG nos serviços sem case; cases com o que o cliente passou a poder fazer e contato logo depois de Como ficou; FAQ num componente só; lastmod por rota; ProfessionalService com areaServed.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 19:14:46 -03:00
1 parent e63f3c2ebd
commit fc44a69fb3
41 files changed
+2486 -550

No files matched your search

@@ -43,18 +43,76 @@ describe("ServiceHeroCover", () => {
expect(container).toBeEmptyDOMElement();
});
it("path: case with a capture -> a link to the case around a cover that loads first", () => {
it("path: case with a capture -> a cover that loads first and a caption with the case name, the relevance and 'Ver case'", () => {
mockedGetCaseMedia.mockImplementation(getShowcaseAssets);
render(<ServiceHeroCover caseStudy={sev} />);
const { container } = render(
<ServiceHeroCover
caseStudy={sev}
relevance="Frase sobre a relação com o serviço."
/>,
);
const link = screen.getByRole("link", {
name: new RegExp(`^Ver o case ${sev.title}`),
name: `Ver case ${sev.title}`,
});
expect(link).toHaveAttribute("href", `/cases/${sev.id}`);
const image = screen.getByAltText(`Captura de tela do site ${sev.title}`);
expect(image).toHaveAttribute("loading", "eager");
expect(image).toHaveAttribute("fetchpriority", "high");
expect(image).toHaveAttribute("sizes", coverSizes.hero);
// a imagem também leva ao case, mas fica fora da leitura e da tabulação: o link da legenda já leva ao mesmo lugar
const coverLink = image.closest("a");
expect(coverLink).toHaveAttribute("aria-hidden", "true");
expect(coverLink).toHaveAttribute("tabindex", "-1");
const caption = container.querySelector("figcaption");
expect(caption).toHaveTextContent(sev.title);
expect(caption).toHaveTextContent("Frase sobre a relação com o serviço.");
expect(caption).toContainElement(link);
});
it("path: no relevance sentence -> the caption keeps the name and the button", () => {
mockedGetCaseMedia.mockImplementation(getShowcaseAssets);
const { container } = render(<ServiceHeroCover caseStudy={sev} />);
expect(container.querySelector("figcaption")).toHaveTextContent(
new RegExp(`^${sev.title}Ver case`),
);
expect(
screen.getByRole("link", { name: `Ver case ${sev.title}` }),
).toBeVisible();
});
it("path: mobile device -> the phone poster in a phone frame, a link to the case, no browser frame", () => {
mockedGetCaseMedia.mockImplementation(getShowcaseAssets);
const { container } = render(
<ServiceHeroCover caseStudy={sev} device="mobile" />,
);
expect(mockedGetCaseMedia).toHaveBeenCalledWith(sev.id, "mobile");
const link = screen.getByRole("link", {
name: `Ver case ${sev.title}`,
});
expect(link).toHaveAttribute("href", `/cases/${sev.id}`);
const screenElement = container.querySelector(".ds-phone-screen");
expect(screenElement).not.toBeNull();
expect(
decodeURIComponent(
(screenElement as HTMLElement).style.backgroundImage ?? "",
),
).toContain(`/videos/${sev.id}/mobile.webp`);
expect(container.querySelector(".ds-bframe")).toBeNull();
expect(container.querySelector("img")).toBeNull();
});
it("path: mobile device but no mobile capture -> renders nothing", () => {
mockedGetCaseMedia.mockImplementation((id, device) =>
device === "mobile" ? undefined : getShowcaseAssets(id, device),
);
const { container } = render(
<ServiceHeroCover caseStudy={sev} device="mobile" />,
);
expect(container).toBeEmptyDOMElement();
});
it("path: link -> pre-fetches the case only after the pointer reaches it", () => {