- Introduced DrawLine component for drawing animations. - Added LazyRevealImage component for lazy loading images with reveal animations. - Implemented TextDiffuseReveal for animated text reveals with staggered effects. - Created TopProgressBar for a dynamic loading indicator. - Added PointCloud mock for testing animations. - Developed useReducedMotion hook to handle user preferences for reduced motion. - Centralized animation configurations in config.ts for easier management. - Defined animation variants in variants.ts for consistent animation effects. - Established AnimationProvider to supply global animation settings. - Created tests for AnimationProvider and useReducedMotion hook. - Added SVG assets for branding and UI elements. - Documented frontend review plan and hero circuit board animation plan.
324 lines
10 KiB
TypeScript
324 lines
10 KiB
TypeScript
import { render, screen } from "@testing-library/react";
|
|
import Footer from "./Footer";
|
|
|
|
describe("Footer Component - Contact Information", () => {
|
|
it("should render contact section heading", () => {
|
|
render(<Footer />);
|
|
|
|
const contactHeading = screen.getByText("Contato direto");
|
|
expect(contactHeading).toBeInTheDocument();
|
|
expect(contactHeading.tagName).toBe("H3");
|
|
});
|
|
|
|
it("should render email address", () => {
|
|
render(<Footer />);
|
|
|
|
const emailLink = screen.getByText("[email protected]");
|
|
expect(emailLink).toBeInTheDocument();
|
|
expect(emailLink.tagName).toBe("A");
|
|
});
|
|
|
|
it("should have mailto link for email", () => {
|
|
render(<Footer />);
|
|
|
|
const emailLink = screen.getByText("[email protected]");
|
|
expect(emailLink).toHaveAttribute("href", "mailto:[email protected]");
|
|
});
|
|
|
|
it("should apply correct styling classes to email link", () => {
|
|
render(<Footer />);
|
|
|
|
const emailLink = screen.getByText("[email protected]");
|
|
const classes = emailLink.className;
|
|
|
|
expect(classes).toContain("text-[12px]");
|
|
expect(classes).toContain("hover:text-[var(--slash)]");
|
|
});
|
|
});
|
|
|
|
describe("Footer Component - Social Links", () => {
|
|
it("should render social links section heading", () => {
|
|
render(<Footer />);
|
|
|
|
const connectHeading = screen.getByText("Redes profissionais");
|
|
expect(connectHeading).toBeInTheDocument();
|
|
expect(connectHeading.tagName).toBe("H3");
|
|
});
|
|
|
|
it("should render GitHub social link", () => {
|
|
render(<Footer />);
|
|
|
|
const githubLink = screen.getByLabelText("Visit GitHub profile");
|
|
expect(githubLink).toBeInTheDocument();
|
|
expect(githubLink.tagName).toBe("A");
|
|
});
|
|
|
|
it("should render LinkedIn social link", () => {
|
|
render(<Footer />);
|
|
|
|
const linkedinLink = screen.getByLabelText("Visit LinkedIn profile");
|
|
expect(linkedinLink).toBeInTheDocument();
|
|
expect(linkedinLink.tagName).toBe("A");
|
|
});
|
|
|
|
it("should have correct href for GitHub link", () => {
|
|
render(<Footer />);
|
|
|
|
const githubLink = screen.getByLabelText("Visit GitHub profile");
|
|
expect(githubLink).toHaveAttribute(
|
|
"href",
|
|
"https://github.com/evandrodevbr",
|
|
);
|
|
});
|
|
|
|
it("should have correct href for LinkedIn link", () => {
|
|
render(<Footer />);
|
|
|
|
const linkedinLink = screen.getByLabelText("Visit LinkedIn profile");
|
|
expect(linkedinLink).toHaveAttribute(
|
|
"href",
|
|
"https://linkedin.com/in/evandrodevbr",
|
|
);
|
|
});
|
|
|
|
it('should open social links in new tab with target="_blank"', () => {
|
|
render(<Footer />);
|
|
|
|
const githubLink = screen.getByLabelText("Visit GitHub profile");
|
|
const linkedinLink = screen.getByLabelText("Visit LinkedIn profile");
|
|
|
|
expect(githubLink).toHaveAttribute("target", "_blank");
|
|
expect(linkedinLink).toHaveAttribute("target", "_blank");
|
|
});
|
|
|
|
it('should have rel="noopener noreferrer" for security', () => {
|
|
render(<Footer />);
|
|
|
|
const githubLink = screen.getByLabelText("Visit GitHub profile");
|
|
const linkedinLink = screen.getByLabelText("Visit LinkedIn profile");
|
|
|
|
expect(githubLink).toHaveAttribute("rel", "noopener noreferrer");
|
|
expect(linkedinLink).toHaveAttribute("rel", "noopener noreferrer");
|
|
});
|
|
|
|
it("should have aria-label for accessibility", () => {
|
|
render(<Footer />);
|
|
|
|
const githubLink = screen.getByLabelText("Visit GitHub profile");
|
|
const linkedinLink = screen.getByLabelText("Visit LinkedIn profile");
|
|
|
|
expect(githubLink).toHaveAttribute("aria-label", "Visit GitHub profile");
|
|
expect(linkedinLink).toHaveAttribute(
|
|
"aria-label",
|
|
"Visit LinkedIn profile",
|
|
);
|
|
});
|
|
|
|
it("should render SVG icons for social links", () => {
|
|
render(<Footer />);
|
|
|
|
const githubLink = screen.getByLabelText("Visit GitHub profile");
|
|
const linkedinLink = screen.getByLabelText("Visit LinkedIn profile");
|
|
|
|
const githubSvg = githubLink.querySelector("svg");
|
|
const linkedinSvg = linkedinLink.querySelector("svg");
|
|
|
|
expect(githubSvg).toBeInTheDocument();
|
|
expect(linkedinSvg).toBeInTheDocument();
|
|
});
|
|
|
|
it('should have aria-hidden="true" on SVG icons', () => {
|
|
render(<Footer />);
|
|
|
|
const githubLink = screen.getByLabelText("Visit GitHub profile");
|
|
const linkedinLink = screen.getByLabelText("Visit LinkedIn profile");
|
|
|
|
const githubSvg = githubLink.querySelector("svg");
|
|
const linkedinSvg = linkedinLink.querySelector("svg");
|
|
|
|
expect(githubSvg).toHaveAttribute("aria-hidden", "true");
|
|
expect(linkedinSvg).toHaveAttribute("aria-hidden", "true");
|
|
});
|
|
});
|
|
|
|
describe("Footer Component - Copyright", () => {
|
|
it("should render copyright section heading", () => {
|
|
render(<Footer />);
|
|
|
|
const copyrightHeading = screen.getByText("Informacoes legais");
|
|
expect(copyrightHeading).toBeInTheDocument();
|
|
expect(copyrightHeading.tagName).toBe("H3");
|
|
});
|
|
|
|
it("should display current year in copyright text", () => {
|
|
render(<Footer />);
|
|
|
|
const currentYear = new Date().getFullYear();
|
|
const copyrightText = screen.getByText(
|
|
new RegExp(`© ${currentYear} Evandro Fonseca Junior`),
|
|
);
|
|
|
|
expect(copyrightText).toBeInTheDocument();
|
|
});
|
|
|
|
it("should update copyright year dynamically", () => {
|
|
// Test that the component uses new Date().getFullYear() dynamically
|
|
// by verifying it shows the current year (not a hardcoded value)
|
|
const currentYear = new Date().getFullYear();
|
|
|
|
render(<Footer />);
|
|
|
|
// Verify the copyright includes the current year
|
|
const copyrightText = screen.getByText(
|
|
new RegExp(`© ${currentYear} Evandro Fonseca Junior`),
|
|
);
|
|
expect(copyrightText).toBeInTheDocument();
|
|
|
|
// Verify it's not hardcoded to a specific year like 2023 or 2024
|
|
// by checking that the year matches the actual current year
|
|
expect(currentYear).toBeGreaterThanOrEqual(2024);
|
|
});
|
|
|
|
it("should apply correct styling to copyright text", () => {
|
|
render(<Footer />);
|
|
|
|
const currentYear = new Date().getFullYear();
|
|
const copyrightText = screen.getByText(
|
|
new RegExp(`© ${currentYear} Evandro Fonseca Junior`),
|
|
);
|
|
const classes = copyrightText.className;
|
|
|
|
expect(classes).toContain("text-[12px]");
|
|
expect(classes).toContain("text-[rgba(255,255,255,0.5)]");
|
|
});
|
|
});
|
|
|
|
describe("Footer Component - Responsive Layout", () => {
|
|
beforeEach(() => {
|
|
// Mock window.matchMedia for responsive tests
|
|
Object.defineProperty(window, "matchMedia", {
|
|
writable: true,
|
|
value: jest.fn().mockImplementation((query) => ({
|
|
matches: false,
|
|
media: query,
|
|
onchange: null,
|
|
addListener: jest.fn(),
|
|
removeListener: jest.fn(),
|
|
addEventListener: jest.fn(),
|
|
removeEventListener: jest.fn(),
|
|
dispatchEvent: jest.fn(),
|
|
})),
|
|
});
|
|
});
|
|
|
|
it("should have responsive grid layout classes", () => {
|
|
const { container } = render(<Footer />);
|
|
|
|
const gridContainer = container.querySelector(".grid");
|
|
expect(gridContainer).toBeInTheDocument();
|
|
|
|
const classes = gridContainer?.className || "";
|
|
expect(classes).toContain("grid-cols-1");
|
|
expect(classes).toContain("md:grid-cols-2");
|
|
expect(classes).toContain("lg:grid-cols-3");
|
|
});
|
|
|
|
it("should have responsive padding classes on container", () => {
|
|
const { container } = render(<Footer />);
|
|
|
|
const innerContainer = container.querySelector(".ds-section-shell");
|
|
expect(innerContainer).toBeInTheDocument();
|
|
|
|
// The ds-section class handles responsive padding via CSS
|
|
const footer = container.querySelector("footer");
|
|
const classes = footer?.className || "";
|
|
expect(classes).toContain("ds-section");
|
|
});
|
|
|
|
it("should have responsive gap classes on grid", () => {
|
|
const { container } = render(<Footer />);
|
|
|
|
const gridContainer = container.querySelector(".grid");
|
|
const classes = gridContainer?.className || "";
|
|
|
|
expect(classes).toContain("gap-sp-6");
|
|
});
|
|
|
|
it("should have responsive column span for copyright section", () => {
|
|
const { container } = render(<Footer />);
|
|
|
|
// Find the copyright section by its heading
|
|
const copyrightHeading = screen.getByText("Informacoes legais");
|
|
const copyrightSection = copyrightHeading.closest(".ds-card");
|
|
|
|
expect(copyrightSection).toBeInTheDocument();
|
|
const classes = copyrightSection?.className || "";
|
|
|
|
expect(classes).toContain("md:col-span-2");
|
|
expect(classes).toContain("lg:col-span-1");
|
|
});
|
|
});
|
|
|
|
describe("Footer Component - Design System Consistency", () => {
|
|
it("should use design system kicker class for section labels", () => {
|
|
render(<Footer />);
|
|
|
|
const kickers = document.querySelectorAll(".ds-kicker");
|
|
expect(kickers.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("should have ds-section class on footer element", () => {
|
|
const { container } = render(<Footer />);
|
|
|
|
const footer = container.querySelector("footer");
|
|
expect(footer).toBeInTheDocument();
|
|
|
|
const classes = footer?.className || "";
|
|
expect(classes).toContain("ds-section");
|
|
});
|
|
|
|
it("should have ds-section-dark class for dark background", () => {
|
|
const { container } = render(<Footer />);
|
|
|
|
const footer = container.querySelector("footer");
|
|
const classes = footer?.className || "";
|
|
|
|
expect(classes).toContain("ds-section-dark");
|
|
});
|
|
|
|
it("should use design system shell class for container", () => {
|
|
const { container } = render(<Footer />);
|
|
|
|
const shell = container.querySelector(".ds-section-shell");
|
|
expect(shell).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("Footer Component - Structure", () => {
|
|
it("should render as footer semantic element", () => {
|
|
const { container } = render(<Footer />);
|
|
|
|
const footer = container.querySelector("footer");
|
|
expect(footer).toBeInTheDocument();
|
|
});
|
|
|
|
it("should have three main sections", () => {
|
|
render(<Footer />);
|
|
|
|
const contactHeading = screen.getByText("Contato direto");
|
|
const connectHeading = screen.getByText("Redes profissionais");
|
|
const copyrightHeading = screen.getByText("Informacoes legais");
|
|
|
|
expect(contactHeading).toBeInTheDocument();
|
|
expect(connectHeading).toBeInTheDocument();
|
|
expect(copyrightHeading).toBeInTheDocument();
|
|
});
|
|
|
|
it("should have design system shell container", () => {
|
|
const { container } = render(<Footer />);
|
|
|
|
const innerContainer = container.querySelector(".ds-section-shell");
|
|
expect(innerContainer).toBeInTheDocument();
|
|
});
|
|
});
|