import { render, screen } from "@testing-library/react"; import { getCaseMedia } from "@/app/cases/_components/case-media"; import { type Device, getShowcaseAssets } from "@/lib/project-showcase"; import { getServicePage } from "@/lib/site-content"; import ServicePage from "./page"; /** * Testes de caminhos-base da escolha da capa do topo em ServicePage (complemento de page.test.tsx), com as capturas * gravadas controladas pelo teste (getCaseMedia trocado por um dublê que só "tem" o que o teste diz). * * A capa vale quando o serviço tem `heroCase` E existe captura gravada do aparelho pedido (heroCase.device); senão * entra o `diagram` do serviço, se tiver; sem os dois, o topo vira uma coluna só e mais baixa. * Caminhos: capa de computador | capa de celular | sem captura do computador | só captura do outro aparelho | * case da capa que não existe | serviço de esquema, com ou sem captura de qualquer case. * Quando a capa vale, a legenda dela cita o case e o case não ganha cartão em "Trabalhos parecidos". */ jest.mock("next/navigation", () => ({ notFound: jest.fn(() => { throw new Error("NEXT_NOT_FOUND"); }), usePathname: () => "/servicos", })); jest.mock("@/app/cases/_components/case-media", () => ({ ...jest.requireActual("@/app/cases/_components/case-media"), getCaseMedia: jest.fn(), })); type Capture = readonly [id: string, device: Device]; /** Só as capturas listadas existem no disco. */ function capturesOnDisk(captures: readonly Capture[]) { jest .mocked(getCaseMedia) .mockImplementation((id, device) => captures.some( ([knownId, knownDevice]) => knownId === id && knownDevice === device, ) ? getShowcaseAssets(id, device) : undefined, ); } async function renderService(slug: string) { const ui = await ServicePage({ params: Promise.resolve({ slug }) }); const view = render(ui); const hero = view.container.querySelector("section") as HTMLElement; return { ...view, hero, twoColumns: hero .querySelector(".ds-section-shell") ?.classList.contains("grid"), compact: hero.className.includes("md:!pb-sp-12"), caption: hero.querySelector("figure figcaption"), cards: Array.from( view.container.querySelectorAll("#casos-relacionados article"), ).map((card) => card.querySelector(".ds-kicker")?.textContent), }; } afterEach(() => { jest.mocked(getCaseMedia).mockReset(); }); describe("ServicePage: escolha da capa do topo", () => { it("path: desktop cover with its capture on disk -> browser frame in the hero, the caption cites the case and the case gets no card", async () => { capturesOnDisk([["lavacao-047", "desktop"]]); const view = await renderService("criacao-de-sites"); expect(view.hero.querySelector(".ds-bframe")).not.toBeNull(); expect(view.twoColumns).toBe(true); expect(view.compact).toBe(false); expect(view.caption).toHaveTextContent("Lavação 047"); expect(view.cards).not.toContain("Lavação 047"); expect(view.cards).toEqual(["SC Plus Aquicultura", "AS Serviços"]); }); it("path: mobile cover with its capture on disk -> phone frame in the hero, no browser frame", async () => { capturesOnDisk([["sev-modaintima", "mobile"]]); const view = await renderService("engenharia-de-software"); expect(view.hero.querySelector(".ds-phone-screen")).not.toBeNull(); expect(view.hero.querySelector(".ds-bframe")).toBeNull(); expect(view.twoColumns).toBe(true); expect(view.caption).toHaveTextContent("SEV Moda Íntima"); expect(view.cards).toEqual(["Lavação 047"]); }); it("path: no capture on disk -> no cover, a lower one-column hero, and the case that would be the cover gets its card back", async () => { capturesOnDisk([]); const view = await renderService("criacao-de-sites"); expect(view.hero.querySelector("figure")).toBeNull(); expect(view.twoColumns).toBe(false); expect(view.compact).toBe(true); expect(screen.queryByRole("img")).toBeNull(); expect(view.cards).toEqual([ "Lavação 047", "SC Plus Aquicultura", "AS Serviços", ]); }); it("path: only the OTHER device was captured (mobile cover, desktop capture) -> no cover, the check follows the device the service asks for", async () => { capturesOnDisk([["sev-modaintima", "desktop"]]); const view = await renderService("engenharia-de-software"); expect(view.hero.querySelector("figure")).toBeNull(); expect(view.compact).toBe(true); expect(view.cards).toEqual(["SEV Moda Íntima", "Lavação 047"]); expect(getCaseMedia).toHaveBeenCalledWith("sev-modaintima", "mobile"); }); it("path: the cover case does not exist -> same as no capture, no crash", async () => { capturesOnDisk([["nao-existe", "desktop"]]); const service = getServicePage("consultoria-nextjs"); if (!service?.heroCase) throw new Error("Missing hero case"); const original = service.heroCase; service.heroCase = { id: "nao-existe", device: "desktop" }; try { const view = await renderService("consultoria-nextjs"); expect(view.hero.querySelector("figure")).toBeNull(); expect(view.compact).toBe(true); } finally { service.heroCase = original; } }); it("path: diagram service -> the diagram, never a cover, even with every capture on disk", async () => { capturesOnDisk([ ["twins-lab", "desktop"], ["twins-lab", "mobile"], ["lavacao-047", "desktop"], ]); const view = await renderService("integracao-hardware-software-iot"); expect( screen.getByRole("img", { name: /dispositivo envia as leituras/ }), ).toBeVisible(); expect(view.hero.querySelector(".ds-bframe")).toBeNull(); expect(view.twoColumns).toBe(true); expect(view.compact).toBe(false); }); it("path: a service with a cover AND the capture missing never falls back to a diagram it does not have", async () => { capturesOnDisk([]); const service = getServicePage("consultoria-nextjs"); const view = await renderService("consultoria-nextjs"); expect(service?.diagram).toBeUndefined(); expect(screen.queryByRole("img", { name: /Esquema|esquema/ })).toBeNull(); expect(view.compact).toBe(true); }); });