Files
portifolio-evandrodevbr/app/components/WorkModalities.paths.test.tsx
T
Evandro Fonseca Junior 10f170abf4 feat(home): posicionamento com prova, menu sem recarregar e WhatsApp em todo o site
H1 'Sites e sistemas que trazem pedidos pelo WhatsApp e tiram sua operação da planilha.'; HeroWork maior com a SEV; menu Cases, Serviços, Como trabalho, Sobre e IA com next/link e pré-carregamento no hover; menu do celular em tela cheia; WhatsApp flutuante e no rodapé com a mensagem da rota; rodapé em todas as páginas; FAQ da home; /ia como oferta de revisão de app feito com IA; formulário com noscript, validação do mailto e plano B do pop-up; cartões sem foco indevido; sem a fonte Monument.
2026-10-14 14:15:25 -03:00

212 lines
7.5 KiB
TypeScript

import { render, screen, within } from "@testing-library/react";
import { primaryButton, secondaryButtonLight } from "./button-styles";
import { WorkModalities } from "./WorkModalities";
/**
* Testes de caminhos-base de WorkModalities e TierCard: a entrada dos planos (Reveal, CSS), o toque e o passe do
* mouse (classes cv-press e cv-lift, sem foco nos cartões), plano em destaque (botão e borda) x demais planos,
* primeiro passo de cada plano, e serviços do hub (só os três principais).
* Nada de animação em JavaScript: o movimento reduzido é tratado pelo CSS (globals.css e conversion.css).
*/
jest.mock("./Section", () => ({
Section: ({ children, id }: { children?: React.ReactNode; id?: string }) => (
<section id={id}>{children}</section>
),
}));
jest.mock("./animations/DrawLine", () => ({ DrawLine: () => null }));
/** O hub inteiro, fora da ordem da home: a home mostra só sites, sistemas web e integrações, nesta ordem. */
const serviceLinks = [
{
href: "/servicos/tauri-rust",
label: "Aplicativos de desktop",
description: "Programas rápidos para o computador.",
},
{
href: "/servicos/integracao-hardware-software-iot",
label: "Integração de equipamentos e sistemas",
description: "Máquinas e sistemas trocando dados.",
},
{
href: "/servicos/consultoria-nextjs",
label: "Melhorias em sites e sistemas",
description: "Evolução do que já existe.",
},
{
href: "/servicos/criacao-de-sites",
label: "Sites profissionais",
description: "Presença digital para a empresa.",
},
{
href: "/servicos/engenharia-de-software",
label: "Sistemas web e aplicativos mobile",
description: "Sistemas sob medida.",
},
{
href: "/servicos/arquitetura-de-software",
label: "Diagnóstico e arquitetura",
description: "Avaliação técnica.",
},
];
describe("WorkModalities: movimento", () => {
it("path: tiers enter through Reveal -> same rise, same duration and a growing delay, no JavaScript animation props", () => {
const { container } = render(<WorkModalities />);
const reveals = [...container.querySelectorAll<HTMLElement>(".ds-reveal")];
expect(reveals).toHaveLength(3);
expect(
reveals.map((element) => element.style.getPropertyValue("--reveal-y")),
).toEqual(["20px", "20px", "20px"]);
expect(
reveals.map((element) =>
element.style.getPropertyValue("--reveal-duration"),
),
).toEqual(["0.75s", "0.75s", "0.75s"]);
expect(
reveals.map((element) =>
Number.parseFloat(element.style.getPropertyValue("--reveal-delay")),
),
).toEqual([0.08, 0.16, 0.24]);
// o estado escondido vive no CSS: sem opacidade nem transformação inline
for (const element of reveals) {
expect(element.style.opacity).toBe("");
expect(element.style.transform).toBe("");
}
});
it("path: the cards are not focus stops and keep no outline-none (touch and hover feedback come from CSS)", () => {
const { container } = render(<WorkModalities />);
const cards = container.querySelectorAll("article");
expect(cards).toHaveLength(3);
for (const card of cards) {
expect(card).not.toHaveAttribute("tabindex");
expect(card.className).not.toContain("outline-none");
expect(card).toHaveClass("cv-press", "cv-lift");
}
});
it("path: each tier sits alone in its own reveal wrapper, which can shrink in the grid", () => {
const { container } = render(<WorkModalities />);
for (const card of container.querySelectorAll("article")) {
const wrapper = card.closest(".ds-reveal");
expect(wrapper).toHaveClass("min-w-0");
expect(wrapper?.querySelectorAll("article")).toHaveLength(1);
}
});
});
describe("WorkModalities: plano em destaque", () => {
it("path: the featured tier has the filled button and the thick border; the others have outline buttons", () => {
const { container } = render(<WorkModalities />);
const featured = screen.getByRole("link", {
name: "Conversar sobre meu sistema",
});
expect(featured.className).toContain(primaryButton);
expect(featured.closest("article")).toHaveStyle({ borderWidth: "2px" });
expect(featured.closest("article")).toHaveClass("border-[var(--slash)]");
for (const name of ["Avaliar meu projeto", "Planejar a próxima etapa"]) {
const other = screen.getByRole("link", { name });
expect(other.className).toContain(secondaryButtonLight);
expect(other.className).not.toContain(primaryButton);
expect(other.closest("article")).toHaveStyle({ borderWidth: "1px" });
expect(other.closest("article")).not.toHaveClass("border-[var(--slash)]");
}
expect(container.querySelectorAll("article")).toHaveLength(3);
});
});
describe("WorkModalities: serviços do hub", () => {
it("path: no serviceLinks prop -> no service cards, the 'all services' link stays", () => {
render(<WorkModalities />);
expect(
screen.getByRole("link", { name: "Ver todos os serviços" }),
).toHaveAttribute("href", "/servicos");
expect(
screen.queryByRole("link", { name: /Sites profissionais/ }),
).toBeNull();
});
it("path: serviceLinks given -> only the three main services (sites, sistemas web, integrações), in that order", () => {
const { container } = render(
<WorkModalities serviceLinks={serviceLinks} />,
);
const grid = container.querySelector(
"[data-reel-stagger='left']",
) as HTMLElement;
const links = within(grid).getAllByRole("link");
expect(links.map((link) => link.getAttribute("href"))).toEqual([
"/servicos/criacao-de-sites",
"/servicos/engenharia-de-software",
"/servicos/integracao-hardware-software-iot",
]);
expect(
within(links[0]).getByText("Sites profissionais"),
).toBeInTheDocument();
expect(
within(links[1]).getByText("Sistemas sob medida."),
).toBeInTheDocument();
expect(
screen.getByRole("link", { name: "Ver todos os serviços" }),
).toHaveAttribute("href", "/servicos");
});
it("path: a main service missing from the hub -> it is skipped, the others stay", () => {
const { container } = render(
<WorkModalities
serviceLinks={serviceLinks.filter(
({ href }) => !href.endsWith("/engenharia-de-software"),
)}
/>,
);
const grid = container.querySelector(
"[data-reel-stagger='left']",
) as HTMLElement;
expect(
within(grid)
.getAllByRole("link")
.map((link) => link.getAttribute("href")),
).toEqual([
"/servicos/criacao-de-sites",
"/servicos/integracao-hardware-software-iot",
]);
});
});
describe("WorkModalities: primeiro passo", () => {
it("path: each tier states a concrete first step, with no price, deadline or contract promise", () => {
const { container } = render(<WorkModalities />);
const articles = Array.from(container.querySelectorAll("article"));
expect(articles).toHaveLength(3);
for (const article of articles) {
expect(within(article).getByText("Primeiro passo")).toBeInTheDocument();
}
expect(
screen.getAllByText(
/Primeira conversa para entender o problema\. Depois, escopo e proposta por escrito\./,
),
).toHaveLength(1);
expect(
screen.getByText(/Você me mostra o que já existe\./),
).toBeInTheDocument();
for (const gone of [
"Proposta por projeto",
"Escopo de análise definido",
"Etapas acordadas",
]) {
expect(screen.queryByText(gone)).toBeNull();
}
expect(container.textContent).not.toMatch(/R\$|em contrato|prazo de \d/);
});
});