feat(servicos): topo, entregáveis e cases por serviço, assunto próprio no WhatsApp e títulos curtos

This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 22:54:18 -03:00
1 parent cf360041bd
commit 2fd8b063d9
33 files changed
+1578 -300

No files matched your search

+31 -15
View File
@@ -1,5 +1,5 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
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";
@@ -14,9 +14,10 @@ const mockFloatingProps = jest.fn();
const mockFooterProps = jest.fn();
jest.mock("next/font/google", () => ({
Geist_Mono: () => ({ variable: "font-mono-var" }),
Space_Grotesk: () => ({ variable: "font-display-var" }),
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,
@@ -47,6 +48,12 @@ jest.mock("./components/FloatingWhatsApp", () => ({
},
}));
// 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();
});
@@ -119,12 +126,12 @@ describe("what the layout mounts", () => {
expect(html.match(/id="site-footer"/g)).toHaveLength(1);
});
it("gives the header and the button the service and case names, and only names", () => {
it("gives the header and the button the whatsappSubject of each service and the case names, and only those", () => {
markup();
const expected = {
services: Object.fromEntries(
servicePages.map((service) => [service.slug, service.navLabel]),
servicePages.map((service) => [service.slug, service.whatsappSubject]),
),
cases: Object.fromEntries(
caseStudies.map((caseStudy) => [caseStudy.id, caseStudy.title]),
@@ -135,9 +142,19 @@ describe("what the layout mounts", () => {
whatsAppContexts: expected,
});
expect(Object.keys(expected.services).length).toBeGreaterThan(0);
// a mensagem lê "Quero conversar sobre {assunto}.": nenhum assunto de serviço começa em maiúscula
for (const subject of Object.values(expected.services)) {
expect(subject).toMatch(/^[a-zà-ú]/);
}
expect(Object.keys(expected.cases).length).toBeGreaterThan(0);
});
it("keeps the smooth anchors but opens each new page at the top: the html carries data-scroll-behavior, which makes Next turn the smooth scroll off only during the route change", () => {
// o atributo é o que o Next lê para desligar a rolagem suave só durante a troca de rota (o e2e navigation.spec.ts
// confere no navegador que a página nova abre em scrollY 0, sem deslizar)
expect(markup()).toMatch(/<html[^>]* data-scroll-behavior="smooth"/);
});
it("hides the JavaScript-only parts of the page when scripts are off", () => {
expect(markup()).toContain(".js-only{display:none!important}");
});
@@ -145,18 +162,17 @@ describe("what the layout mounts", () => {
describe("fonts", () => {
it("does not preload Geist Mono (only small labels use it), while the display font keeps its preload", () => {
const source = readFileSync(join(__dirname, "layout.tsx"), "utf8");
const mono = source.match(/Geist_Mono\(\{[^}]*\}\)/)?.[0] ?? "";
expect(mono).toMatch(/preload:\s*false/);
const display = source.match(/Space_Grotesk\(\{[^}]*\}\)/)?.[0] ?? "";
expect(display).not.toMatch(/preload:\s*false/);
expect(monoFontOptions).toEqual(
expect.objectContaining({ preload: false }),
);
expect(displayFontOptions).toBeDefined();
expect(displayFontOptions).not.toEqual(
expect.objectContaining({ preload: false }),
);
});
it("no longer loads the unused Monument Extended font", () => {
const source = readFileSync(join(__dirname, "layout.tsx"), "utf8");
expect(source).not.toMatch(/Monument|next\/font\/local/i);
expect(localFontCalls).toBe(0);
expect(markupClassName()).not.toMatch(/monument/i);
});