import { render, screen } from "@testing-library/react"; import CompanyLogos from "./CompanyLogos"; // Mock framer-motion with configurable useReducedMotion let mockReducedMotion = false; jest.mock("framer-motion", () => { const actual = jest.requireActual("framer-motion"); return { ...actual, useReducedMotion: () => mockReducedMotion, motion: { div: ({ children, animate, transition, ...props }: any) => { // Capture animation props for testing const animationProps = { "data-animate": JSON.stringify(animate), "data-transition": JSON.stringify(transition), }; return (
{children}
); }, }, }; }); describe("CompanyLogos Component - Logo Rendering", () => { beforeEach(() => { mockReducedMotion = false; }); it("should render all five company logos", () => { const { container } = render(); // In animated mode, logos are duplicated (10 total) const logoImages = container.querySelectorAll('img[alt*="logo"]'); expect(logoImages.length).toBe(10); // 5 logos x 2 (duplicated for infinite scroll) }); it("should render Apolo Energia logo", () => { render(); const apoloLogos = screen.getAllByAltText("Apolo Energia logo"); expect(apoloLogos.length).toBe(2); // Duplicated for infinite scroll expect(apoloLogos[0]).toHaveAttribute("src", "/logos/apolo.svg"); }); it("should render Julia Beauty Lashes logo", () => { render(); const juliaLogos = screen.getAllByAltText("Julia Beauty Lashes logo"); expect(juliaLogos.length).toBe(2); expect(juliaLogos[0]).toHaveAttribute("src", "/logos/julia.svg"); }); it("should render Innovo logo", () => { render(); const innovoLogos = screen.getAllByAltText("Innovo logo"); expect(innovoLogos.length).toBe(2); expect(innovoLogos[0]).toHaveAttribute("src", "/logos/innovo.svg"); }); it("should render TeraByteShop logo", () => { render(); const terabyteLogos = screen.getAllByAltText("TeraByteShop logo"); expect(terabyteLogos.length).toBe(2); expect(terabyteLogos[0]).toHaveAttribute("src", "/logos/terabyte.svg"); }); it("should render Pichau logo", () => { render(); const pichauLogos = screen.getAllByAltText("Pichau logo"); expect(pichauLogos.length).toBe(2); expect(pichauLogos[0]).toHaveAttribute("src", "/logos/pichau.svg"); }); it("should have alt text for all logo images", () => { const { container } = render(); const logoImages = container.querySelectorAll("img"); logoImages.forEach((img) => { const alt = img.getAttribute("alt"); expect(alt).toBeTruthy(); expect(alt?.length).toBeGreaterThan(0); }); }); it("should duplicate logo array for seamless infinite scroll", () => { const { container } = render(); // Each company should appear exactly twice const apoloLogos = screen.getAllByAltText("Apolo Energia logo"); const juliaLogos = screen.getAllByAltText("Julia Beauty Lashes logo"); const innovoLogos = screen.getAllByAltText("Innovo logo"); const terabyteLogos = screen.getAllByAltText("TeraByteShop logo"); const pichauLogos = screen.getAllByAltText("Pichau logo"); expect(apoloLogos.length).toBe(2); expect(juliaLogos.length).toBe(2); expect(innovoLogos.length).toBe(2); expect(terabyteLogos.length).toBe(2); expect(pichauLogos.length).toBe(2); }); }); describe("CompanyLogos Component - Animation Configuration", () => { beforeEach(() => { mockReducedMotion = false; }); it("should have horizontal scroll animation configuration", () => { const { container } = render(); const animatedElement = container.querySelector("[data-animate]"); expect(animatedElement).toBeInTheDocument(); const animateValue = animatedElement?.getAttribute("data-animate"); expect(animateValue).toBeTruthy(); const animateProps = JSON.parse(animateValue!); expect(animateProps.x).toEqual([0, "-50%"]); }); it("should have 30-second animation duration", () => { const { container } = render(); const animatedElement = container.querySelector("[data-transition]"); expect(animatedElement).toBeInTheDocument(); const transitionValue = animatedElement?.getAttribute("data-transition"); expect(transitionValue).toBeTruthy(); const transitionProps = JSON.parse(transitionValue!); expect(transitionProps.duration).toBe(30); }); it("should have infinite repeat configuration", () => { const { container } = render(); const animatedElement = container.querySelector("[data-transition]"); const transitionValue = animatedElement?.getAttribute("data-transition"); const transitionProps = JSON.parse(transitionValue!); // JSON.stringify converts Infinity to null, so we check for null expect(transitionProps.repeat).toBe(null); }); it("should use linear easing for smooth animation", () => { const { container } = render(); const animatedElement = container.querySelector("[data-transition]"); const transitionValue = animatedElement?.getAttribute("data-transition"); const transitionProps = JSON.parse(transitionValue!); expect(transitionProps.ease).toBe("linear"); }); it("should have flex layout with gap-sp-12 for logo spacing", () => { const { container } = render(); const animatedElement = container.querySelector("[data-animate]"); const classes = animatedElement?.className || ""; expect(classes).toContain("flex"); expect(classes).toContain("gap-sp-12"); }); it("should have items-center alignment", () => { const { container } = render(); const animatedElement = container.querySelector("[data-animate]"); const classes = animatedElement?.className || ""; expect(classes).toContain("items-center"); }); }); describe("CompanyLogos Component - Reduced Motion Fallback", () => { beforeEach(() => { mockReducedMotion = true; }); afterEach(() => { mockReducedMotion = false; }); it("should render static grid when reduced motion is enabled", () => { const { container } = render(); const staticGrid = container.querySelector(".grid"); expect(staticGrid).toBeInTheDocument(); }); it("should not have animated elements when reduced motion is enabled", () => { const { container } = render(); const animatedElements = container.querySelectorAll("[data-animate]"); expect(animatedElements.length).toBe(0); }); it("should render exactly 5 logos in static mode (no duplication)", () => { const { container } = render(); const logoImages = container.querySelectorAll('img[alt*="logo"]'); expect(logoImages.length).toBe(5); }); it("should have responsive grid columns in static mode", () => { const { container } = render(); const staticGrid = container.querySelector(".grid"); const classes = staticGrid?.className || ""; expect(classes).toContain("grid-cols-2"); expect(classes).toContain("sm:grid-cols-3"); expect(classes).toContain("md:grid-cols-5"); }); it("should have gap-sp-6 spacing in static grid", () => { const { container } = render(); const staticGrid = container.querySelector(".grid"); const classes = staticGrid?.className || ""; expect(classes).toContain("gap-sp-6"); }); it("should maintain all logo alt text in static mode", () => { const { container } = render(); const logoImages = container.querySelectorAll("img"); logoImages.forEach((img) => { const alt = img.getAttribute("alt"); expect(alt).toBeTruthy(); expect(alt?.length).toBeGreaterThan(0); }); }); }); describe("CompanyLogos Component - Missing Logo Image Handling", () => { beforeEach(() => { mockReducedMotion = false; }); it("should have onError handler for logo images", () => { const { container } = render(); const logoImages = container.querySelectorAll("img"); logoImages.forEach((img) => { // Check that the image element has an onError handler // In React, this is attached as a prop, so we verify the element exists expect(img).toBeInTheDocument(); }); }); it("should apply grayscale filter to logo images", () => { const { container } = render(); const logoImages = container.querySelectorAll("img"); logoImages.forEach((img) => { const classes = img.className; expect(classes).toContain("grayscale"); }); }); it("should have hover effect to remove grayscale", () => { const { container } = render(); const logoImages = container.querySelectorAll("img"); logoImages.forEach((img) => { const classes = img.className; expect(classes).toContain("hover:grayscale-0"); }); }); it("should have opacity transition on hover", () => { const { container } = render(); const logoImages = container.querySelectorAll("img"); logoImages.forEach((img) => { const classes = img.className; expect(classes).toContain("opacity-60"); expect(classes).toContain("hover:opacity-100"); }); }); it("should have transition-all for smooth hover effects", () => { const { container } = render(); const logoImages = container.querySelectorAll("img"); logoImages.forEach((img) => { const classes = img.className; expect(classes).toContain("transition-all"); }); }); }); describe("CompanyLogos Component - Responsive Layout", () => { beforeEach(() => { mockReducedMotion = false; }); it("should have section element as container", () => { const { container } = render(); const section = container.querySelector("section"); expect(section).toBeInTheDocument(); }); it("should have full width class", () => { const { container } = render(); const section = container.querySelector("section"); const classes = section?.className || ""; expect(classes).toContain("w-full"); }); it("should have overflow-hidden for animation containment", () => { const { container } = render(); const section = container.querySelector("section"); const classes = section?.className || ""; expect(classes).toContain("overflow-hidden"); }); it("should have border-b for visual separation", () => { const { container } = render(); const section = container.querySelector("section"); const classes = section?.className || ""; expect(classes).toContain("border-b"); }); it("should have background color class", () => { const { container } = render(); const section = container.querySelector("section"); const classes = section?.className || ""; expect(classes).toContain("bg-[var(--paper)]"); }); it("should have py-sp-8 vertical padding", () => { const { container } = render(); // In animated mode, padding is on inner div const innerDiv = container.querySelector(".py-sp-8"); expect(innerDiv).toBeInTheDocument(); }); it("should have responsive padding in static mode", () => { mockReducedMotion = true; const { container } = render(); const innerContainer = container.querySelector(".container"); const classes = innerContainer?.className || ""; expect(classes).toContain("px-sp-6"); expect(classes).toContain("py-sp-8"); expect(classes).toContain("md:px-sp-10"); expect(classes).toContain("lg:px-sp-16"); }); it("should have mx-auto for centering in static mode", () => { mockReducedMotion = true; const { container } = render(); const innerContainer = container.querySelector(".container"); const classes = innerContainer?.className || ""; expect(classes).toContain("mx-auto"); }); }); describe("CompanyLogos Component - Logo Sizing and Styling", () => { beforeEach(() => { mockReducedMotion = false; }); it("should have h-12 height for all logos", () => { const { container } = render(); const logoImages = container.querySelectorAll("img"); logoImages.forEach((img) => { const classes = img.className; expect(classes).toContain("h-12"); }); }); it("should have w-auto width for aspect ratio preservation", () => { const { container } = render(); const logoImages = container.querySelectorAll("img"); logoImages.forEach((img) => { const classes = img.className; expect(classes).toContain("w-auto"); }); }); it("should have object-contain for proper image scaling", () => { const { container } = render(); const logoImages = container.querySelectorAll("img"); logoImages.forEach((img) => { const classes = img.className; expect(classes).toContain("object-contain"); }); }); it("should have flex-shrink-0 on logo containers in animated mode", () => { const { container } = render(); const logoContainers = container.querySelectorAll("[data-animate] > div"); logoContainers.forEach((div) => { const classes = div.className; expect(classes).toContain("flex-shrink-0"); }); }); it("should center logos within their containers", () => { const { container } = render(); const logoContainers = container.querySelectorAll("[data-animate] > div"); logoContainers.forEach((div) => { const classes = div.className; expect(classes).toContain("flex"); expect(classes).toContain("items-center"); expect(classes).toContain("justify-center"); }); }); }); describe("CompanyLogos Component - Accessibility", () => { beforeEach(() => { mockReducedMotion = false; }); it("should use semantic section element", () => { const { container } = render(); const section = container.querySelector("section"); expect(section).toBeInTheDocument(); }); it("should have descriptive alt text for each logo", () => { render(); expect(screen.getAllByAltText("Apolo Energia logo")).toHaveLength(2); expect(screen.getAllByAltText("Julia Beauty Lashes logo")).toHaveLength(2); expect(screen.getAllByAltText("Innovo logo")).toHaveLength(2); expect(screen.getAllByAltText("TeraByteShop logo")).toHaveLength(2); expect(screen.getAllByAltText("Pichau logo")).toHaveLength(2); }); it("should respect reduced motion preference", () => { mockReducedMotion = true; const { container } = render(); // Should render static grid instead of animation const staticGrid = container.querySelector(".grid"); const animatedElements = container.querySelectorAll("[data-animate]"); expect(staticGrid).toBeInTheDocument(); expect(animatedElements.length).toBe(0); }); it("should maintain content visibility in both modes", () => { // Test animated mode mockReducedMotion = false; const { container: animatedContainer } = render(); const animatedLogos = animatedContainer.querySelectorAll('img[alt*="logo"]'); // Test static mode mockReducedMotion = true; const { container: staticContainer } = render(); const staticLogos = staticContainer.querySelectorAll('img[alt*="logo"]'); // Both modes should display logos (different counts due to duplication) expect(animatedLogos.length).toBeGreaterThan(0); expect(staticLogos.length).toBeGreaterThan(0); }); });