Home mais curta (hero, vitrine de clientes, sobre, serviços, IA resumida, depoimentos e contato), faixa de clientes e telas reais no hero, sem números do GitHub. Barra de cookies fina, WhatsApp fixo no celular, formulário com e-mail opcional e alternativa por e-mail, página /ia, eventos de e-mail, redes e currículo e conversão do Ads opcional por variável. Rodapé em 3 colunas. Remove componentes órfãos (LighthouseMetrics, Skills, Experience, contagem do GitHub).
204 lines
6.2 KiB
TypeScript
204 lines
6.2 KiB
TypeScript
import { render, screen, within } from "@testing-library/react";
|
|
import { useReducedMotion } from "@/app/hooks/useReducedMotion";
|
|
import { primaryButton, secondaryButtonLight } from "./button-styles";
|
|
import { WorkModalities } from "./WorkModalities";
|
|
|
|
/**
|
|
* Testes de caminhos-base de WorkModalities e TierCard: movimento normal x reduzido, plano em destaque
|
|
* (botão e borda) x demais planos, e serviços do hub ausentes x presentes.
|
|
* O `motion/react` é trocado por elementos simples que registram as props de animação.
|
|
*/
|
|
|
|
const mockSeen = jest.fn();
|
|
|
|
jest.mock("motion/react", () => {
|
|
const React = require("react");
|
|
const cache = new Map<string, unknown>();
|
|
const motion = new Proxy(
|
|
{},
|
|
{
|
|
get: (_target, tag: string) => {
|
|
if (!cache.has(tag)) {
|
|
cache.set(
|
|
tag,
|
|
({
|
|
children,
|
|
variants,
|
|
whileHover,
|
|
whileTap,
|
|
transition,
|
|
initial: _initial,
|
|
animate: _animate,
|
|
whileInView: _whileInView,
|
|
viewport: _viewport,
|
|
...props
|
|
}: Record<string, unknown> & { children?: unknown }) => {
|
|
mockSeen(tag, { variants, whileHover, whileTap, transition });
|
|
return React.createElement(tag, props, children);
|
|
},
|
|
);
|
|
}
|
|
return cache.get(tag);
|
|
},
|
|
},
|
|
);
|
|
return { motion };
|
|
});
|
|
|
|
jest.mock("@/app/hooks/useReducedMotion", () => ({
|
|
useReducedMotion: jest.fn(),
|
|
}));
|
|
|
|
jest.mock("./Section", () => ({
|
|
Section: ({ children, id }: { children?: React.ReactNode; id?: string }) => (
|
|
<section id={id}>{children}</section>
|
|
),
|
|
}));
|
|
|
|
jest.mock("./animations/DrawLine", () => ({ DrawLine: () => null }));
|
|
|
|
type Seen = {
|
|
variants?: {
|
|
hidden: Record<string, unknown>;
|
|
visible: { transition: Record<string, unknown> };
|
|
};
|
|
whileHover?: unknown;
|
|
whileTap?: unknown;
|
|
};
|
|
|
|
const serviceLinks = [
|
|
{
|
|
href: "/servicos/criacao-de-sites",
|
|
label: "Sites profissionais",
|
|
description: "Presença digital para a empresa.",
|
|
},
|
|
{
|
|
href: "/servicos/tauri-rust",
|
|
label: "Aplicativos de desktop",
|
|
description: "Programas rápidos para o computador.",
|
|
},
|
|
];
|
|
|
|
function seen(tag: string): Seen[] {
|
|
return mockSeen.mock.calls
|
|
.filter(([name]) => name === tag)
|
|
.map(([, props]) => props as Seen);
|
|
}
|
|
|
|
beforeEach(() => {
|
|
mockSeen.mockClear();
|
|
});
|
|
|
|
describe("WorkModalities: movimento", () => {
|
|
it("path: normal motion -> tiers rise in, with hover lift and press feedback", () => {
|
|
jest.mocked(useReducedMotion).mockReturnValue(false);
|
|
|
|
render(<WorkModalities />);
|
|
|
|
const items = seen("div").filter(
|
|
(props) => props.variants?.hidden?.opacity === 0,
|
|
);
|
|
expect(items).toHaveLength(3);
|
|
for (const item of items) {
|
|
expect(item.variants?.hidden).toEqual({
|
|
opacity: 0,
|
|
y: 20,
|
|
clipPath: "inset(100% 0 0 0)",
|
|
});
|
|
expect(item.variants?.visible.transition).toEqual({
|
|
duration: 0.75,
|
|
ease: [0.16, 1, 0.3, 1],
|
|
});
|
|
}
|
|
const cards = seen("article");
|
|
expect(cards).toHaveLength(3);
|
|
for (const card of cards) {
|
|
expect(card.whileHover).toEqual({
|
|
y: -4,
|
|
boxShadow: "0 24px 50px rgba(0, 0, 0, 0.08)",
|
|
});
|
|
expect(card.whileTap).toEqual({ scale: 0.99 });
|
|
}
|
|
});
|
|
|
|
it("path: reduced motion -> tiers only fade, with no hover lift and no press feedback", () => {
|
|
jest.mocked(useReducedMotion).mockReturnValue(true);
|
|
|
|
render(<WorkModalities />);
|
|
|
|
const items = seen("div").filter(
|
|
(props) => props.variants?.hidden?.opacity === 0,
|
|
);
|
|
expect(items).toHaveLength(3);
|
|
for (const item of items) {
|
|
expect(item.variants?.hidden).toEqual({ opacity: 0 });
|
|
expect(item.variants?.visible.transition).toEqual({ duration: 0.25 });
|
|
}
|
|
for (const card of seen("article")) {
|
|
expect(card.whileHover).toBeUndefined();
|
|
expect(card.whileTap).toBeUndefined();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("WorkModalities: plano em destaque", () => {
|
|
it("path: the featured tier has the filled button and the thick border; the others have outline buttons", () => {
|
|
jest.mocked(useReducedMotion).mockReturnValue(false);
|
|
|
|
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", () => {
|
|
jest.mocked(useReducedMotion).mockReturnValue(false);
|
|
|
|
render(<WorkModalities />);
|
|
|
|
expect(
|
|
screen.getByRole("link", { name: "Conhecer todos os serviços" }),
|
|
).toHaveAttribute("href", "/servicos");
|
|
expect(
|
|
screen.queryByRole("link", { name: /Sites profissionais/ }),
|
|
).toBeNull();
|
|
});
|
|
|
|
it("path: serviceLinks given -> one card per service, in order, linking to its page", () => {
|
|
jest.mocked(useReducedMotion).mockReturnValue(false);
|
|
|
|
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(
|
|
serviceLinks.map(({ href }) => href),
|
|
);
|
|
expect(
|
|
within(links[0]).getByText("Sites profissionais"),
|
|
).toBeInTheDocument();
|
|
expect(
|
|
within(links[1]).getByText("Programas rápidos para o computador."),
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|