import { Geist_Mono, Space_Grotesk } from "next/font/google";
import localFont from "next/font/local";
import { renderToStaticMarkup } from "react-dom/server";
import { caseStudies, serviceHubLinks, servicePages } from "@/lib/site-content";
import RootLayout, { metadata } from "./layout";
/**
* Metadados do layout (título e sufixo, canônico) e o que ele monta em todas as páginas: o cabeçalho, o rodapé e o botão
* flutuante do WhatsApp. Cabeçalho e botão recebem os nomes dos serviços e dos cases, lidos de lib/site-content.ts no servidor.
*/
const mockHeaderProps = jest.fn();
const mockFloatingProps = jest.fn();
const mockFooterProps = jest.fn();
jest.mock("next/font/google", () => ({
Geist_Mono: jest.fn(() => ({ variable: "font-mono-var" })),
Space_Grotesk: jest.fn(() => ({ variable: "font-display-var" })),
}));
jest.mock("next/font/local", () => ({ __esModule: true, default: jest.fn() }));
jest.mock("./globals.css", () => ({}));
jest.mock("./components/cinematic/CinematicOverlays", () => ({
__esModule: true,
default: () => null,
}));
jest.mock("./components/GoogleAnalytics", () => ({
__esModule: true,
default: () => null,
}));
jest.mock("./components/Header", () => ({
Header: (props: unknown) => {
mockHeaderProps(props);
return ;
},
}));
jest.mock("./components/Footer", () => ({
__esModule: true,
default: (props: unknown) => {
mockFooterProps(props);
return ;
},
}));
jest.mock("./components/FloatingWhatsApp", () => ({
__esModule: true,
default: (props: unknown) => {
mockFloatingProps(props);
return
;
},
}));
// as fontes são pedidas ao carregar o layout (uma vez, na importação): lê-se o que foi pedido antes de qualquer
// jest.clearAllMocks
const monoFontOptions = jest.mocked(Geist_Mono).mock.calls[0]?.[0];
const displayFontOptions = jest.mocked(Space_Grotesk).mock.calls[0]?.[0];
const localFontCalls = jest.mocked(localFont).mock.calls.length;
beforeEach(() => {
jest.clearAllMocks();
});
describe("metadata", () => {
it("ends every page title with ' | Evandro.dev.br' and gives the home its own default title", () => {
expect(metadata.title).toEqual({
default: "Sites e sistemas em Joinville e SC | Evandro.dev.br",
template: "%s | Evandro.dev.br",
});
});
it("keeps the home title, suffix included, within 60 characters", () => {
const title = metadata.title as { default: string };
expect(title.default.length).toBeLessThanOrEqual(60);
});
it("does not set a canonical in the layout: only pages that know their own address do", () => {
// o 404 e as páginas sem canônico próprio não podem herdar o "/" da home
expect(metadata.alternates).toBeUndefined();
});
it("describes the offer without inventing numbers or promises", () => {
// o posicionamento do hero: sites e sistemas sob medida (os casos de WhatsApp e planilha ficam nos cartões de oferta)
expect(metadata.description).toMatch(/^Sites e sistemas sob medida/);
expect(metadata.description).not.toMatch(/R\$|garantia|\d+ dias/);
});
});
describe("what the layout mounts", () => {
function markup() {
return renderToStaticMarkup(
conteúdo
,
);
}
it("mounts the floating WhatsApp button for the whole site, outside the home", () => {
const html = markup();
expect(html).toContain('data-testid="floating-whatsapp"');
expect(html.indexOf("conteúdo")).toBeLessThan(
html.indexOf('data-testid="floating-whatsapp"'),
);
});
it("mounts the footer once for the whole site, after the page content, with the service links", () => {
const html = markup();
expect(html.match(/data-testid="footer"/g)).toHaveLength(1);
expect(html.indexOf("conteúdo")).toBeLessThan(
html.indexOf('data-testid="footer"'),
);
expect(mockFooterProps).toHaveBeenCalledWith({
serviceLinks: serviceHubLinks,
});
expect(serviceHubLinks.length).toBeGreaterThan(0);
});
it("gives the footer wrapper and the content the ids the mobile menu makes inert", () => {
const html = markup();
expect(html).toMatch(
/]*>(?:(?!<\/div>).)*conteúdo/,
);
expect(html).toMatch(
/