Hero: replace particle field with cobe globe (Capricorn view, multi-axis drag, reduced-motion safe), telemetry HUD with pointer parallax, particle text build effect, Space Grotesk display font. SEO: short titles with template, PT-BR accents, twitter cards on dynamic cases, escaped JSON-LD via lib/jsonld.ts, CSP report-only, 25 service FAQs answer-first. Design: fix TW4 layered reset killing spacing utilities, literal @theme tokens, mobile hero overflow, header overlap, empty case cards. Remove framer-motion (unified on motion/react), header version badge, phone number from hero CTA. Production build verified (24 static pages).
98 lines
3.1 KiB
TypeScript
98 lines
3.1 KiB
TypeScript
import { render, screen } from "@testing-library/react";
|
|
import Footer from "./Footer";
|
|
|
|
describe("Footer contact and navigation", () => {
|
|
it("renders the contact card and a mailto link", () => {
|
|
render(<Footer />);
|
|
|
|
expect(screen.getByText("Contato")).toBeInTheDocument();
|
|
expect(
|
|
screen.getByRole("heading", {
|
|
name: "Engenharia com menos ruído e mais clareza de execução",
|
|
}),
|
|
).toBeInTheDocument();
|
|
|
|
const emailLink = screen.getByRole("link", {
|
|
name: "[email protected]",
|
|
});
|
|
expect(emailLink).toHaveAttribute(
|
|
"href",
|
|
"mailto:[email protected]",
|
|
);
|
|
expect(emailLink).not.toHaveAttribute("target", "_blank");
|
|
});
|
|
|
|
it("renders quick links as same-tab navigation", () => {
|
|
render(<Footer />);
|
|
|
|
const servicesLink = screen.getByRole("link", { name: "Hub de serviços" });
|
|
expect(servicesLink).toHaveAttribute("href", "/servicos");
|
|
expect(servicesLink).not.toHaveAttribute("target", "_blank");
|
|
});
|
|
});
|
|
|
|
describe("Footer social links", () => {
|
|
it("renders each social link with an accessible name", () => {
|
|
render(<Footer />);
|
|
|
|
const socialLinks = Array.from(
|
|
document.querySelectorAll("footer a[aria-label]"),
|
|
);
|
|
|
|
expect(socialLinks.map((link) => link.getAttribute("aria-label"))).toEqual([
|
|
"Visit GitHub profile",
|
|
"Visit LinkedIn profile",
|
|
"Visit Instagram profile",
|
|
]);
|
|
expect(
|
|
new Set(socialLinks.map((link) => link.getAttribute("aria-label"))).size,
|
|
).toBe(socialLinks.length);
|
|
});
|
|
|
|
it("opens social links securely in a new tab", () => {
|
|
render(<Footer />);
|
|
|
|
const socialLinks = Array.from(
|
|
document.querySelectorAll("footer a[aria-label]"),
|
|
);
|
|
|
|
for (const link of socialLinks) {
|
|
expect(link).toHaveAttribute("target", "_blank");
|
|
expect(link).toHaveAttribute("rel", "noopener noreferrer");
|
|
expect(link.querySelector("svg")).toHaveAttribute("aria-hidden", "true");
|
|
}
|
|
|
|
expect(screen.getByLabelText("Visit GitHub profile")).toHaveAttribute(
|
|
"href",
|
|
"https://github.com/evandrodevbr",
|
|
);
|
|
expect(
|
|
screen.getByLabelText("Visit LinkedIn profile").getAttribute("href"),
|
|
).toContain("https://www.linkedin.com/in/evandrodevbr/");
|
|
});
|
|
});
|
|
|
|
describe("Footer structure", () => {
|
|
it("uses the current design-system shell and two-card grid", () => {
|
|
const { container } = render(<Footer />);
|
|
const footer = container.querySelector("footer");
|
|
|
|
expect(footer).toHaveClass("ds-section");
|
|
expect(footer?.querySelector(".ds-section-shell")).toBeInTheDocument();
|
|
expect(footer?.querySelector(".ds-grid-2")).toBeInTheDocument();
|
|
expect(footer?.querySelectorAll(":scope .ds-card")).toHaveLength(2);
|
|
expect(footer?.querySelectorAll("h3")).toHaveLength(2);
|
|
});
|
|
|
|
it("renders the current year without hardcoded copyright text", () => {
|
|
render(<Footer />);
|
|
|
|
const currentYear = new Date().getFullYear();
|
|
expect(
|
|
screen.getByText(
|
|
new RegExp(`Copyright ${currentYear} Evandro Fonseca Junior`),
|
|
),
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|