import { render, screen, within } from "@testing-library/react"; import AiPage, { metadata } from "./page"; it("uses a short search title: with the layout suffix it fits in 44 characters", () => { // o template de app/layout.tsx acrescenta " | Evandro.dev.br" const suffix = " | Evandro.dev.br"; expect(metadata.title).toBe("Revisão de app feito com IA"); expect(`${metadata.title}${suffix}`.length).toBeLessThanOrEqual(44); expect(metadata).toMatchObject({ description: expect.any(String), alternates: { canonical: "https://evandro.dev.br/ia" }, }); // as prévias de compartilhamento não passam pelo template: o título delas já leva o sufixo expect(metadata.openGraph?.title).toBe( `Revisão de app feito com IA${suffix}`, ); }); it("keeps the full AI content, with the cited incidents summarized only here", () => { render(); expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent( "Revisão de app feito com IA (Lovable, Bolt, Replit)", ); for (const heading of [ "Se a IA cria um sistema, por que contratar um desenvolvedor?", "O que casos documentados ensinam", "E a LGPD?", ]) { expect(screen.getByRole("heading", { name: heading })).toBeInTheDocument(); } for (const incident of [/Lovable:/, /Replit:/, /Base44:/]) { expect(screen.getByRole("heading", { name: incident })).toBeInTheDocument(); } expect( screen.getByRole("link", { name: "Reportagem da Semafor (em inglês)" }), ).toHaveAttribute("rel", "noopener noreferrer"); }); it("offers WhatsApp conversations about reviewing an AI-built system", () => { render(); const links = screen .getAllByRole("link") .filter((link) => link.getAttribute("href")?.startsWith("https://wa.me/")); expect(links.map((link) => link.textContent)).toEqual([ "Pedir a revisão do meu app", "Avaliar meu projeto", ]); for (const link of links) { const text = new URL(link.getAttribute("href") ?? "").searchParams.get( "text", ); // a mensagem tem de formar uma frase: "Quero conversar sobre a revisão de um aplicativo feito com IA." expect(text).toContain( "Quero conversar sobre a revisão de um aplicativo feito com IA.", ); } }); it("has breadcrumb navigation back to the home", () => { render(); const breadcrumb = screen.getByRole("navigation", { name: "Trilha de navegação", }); expect( within(breadcrumb).getByRole("link", { name: "Início" }), ).toHaveAttribute("href", "/"); expect( within(breadcrumb).getByText("Revisão de app feito com IA"), ).toHaveAttribute("aria-current", "page"); }); it("presents the review as an offer: what the person receives, with no price or deadline", () => { const { container } = render(); const offer = screen.getByRole("heading", { name: "O que você recebe na revisão", }); const section = offer.closest("section") as HTMLElement; const items = within(section) .getAllByRole("listitem") .map((item) => item.querySelector("h3")?.textContent); expect(items).toEqual([ "As falhas de acesso a dados", "O que dá para aproveitar", "O que é melhor refazer", "Um orçamento para pôr em produção", ]); expect(container.textContent).not.toMatch(/R\$|\d+ dias|em até \d+/); }); it("keeps the incident news collapsed in a
, closed by default", () => { const { container } = render(); const heading = screen.getByRole("heading", { name: "O que casos documentados ensinam", }); const details = heading.closest("details") as HTMLDetailsElement; expect(details).not.toBeNull(); expect(details.open).toBe(false); expect(heading.closest("summary")).not.toBeNull(); for (const incident of [/Lovable:/, /Replit:/, /Base44:/]) { expect(within(details).getByText(incident)).toBeInTheDocument(); } // só os incidentes ficam recolhidos: a oferta e a LGPD ficam à vista expect(container.querySelectorAll("details")).toHaveLength(1); expect( screen .getByRole("heading", { name: "O que você recebe na revisão" }) .closest("details"), ).toBeNull(); });