import React from "react"; import { FindKinetic } from "./index"; import { FindKineticProps } from "./types"; import { render, screen } from "@testing-library/react"; // Mock dependencies jest.mock("@shared/components/link", () => ({ Link: ({ href, children, className, }: { href: string; children: React.ReactNode; className?: string; }) => ( {children} ), })); jest.mock("@shared/components/text", () => ({ Text: ({ as: Tag = "span", children, className }: any) => ( {children} ), })); jest.mock("@shared/next", () => ({ NextImage: ({ src, alt, width, height, }: { src: string; alt: string; width: number; height: number; }) => {alt}, })); const mockList = [ { name: "New York", code: "NY" }, { name: "California", code: "CA" }, { name: "Texas", code: "TX" }, { name: "Florida", code: "FL" }, { name: "Illinois", code: "IL" }, { name: "Ohio", code: "OH" }, { name: "Georgia", code: "GA" }, ]; const defaultProps: FindKineticProps = { title: "Find Kinetic Near You", subTitle: "Available in many locations", description: "Check availability in your area.", list: mockList, image: "https://example.com/image.png", }; describe("FindKinetic", () => { describe("Rendering", () => { it("renders with default props", () => { render(); expect(screen.getByText("Find Kinetic Near You")).toBeInTheDocument(); }); it("renders nothing when no props are provided", () => { const { container } = render(); expect(container.firstChild).toBeInTheDocument(); }); it("renders title when provided", () => { render(); expect(screen.getByText("Test Title")).toBeInTheDocument(); }); it("does not render title when not provided", () => { render(); expect(screen.queryByText("Test Title")).not.toBeInTheDocument(); }); it("renders subtitle when provided", () => { render(); expect(screen.getByText("Subtitle Text")).toBeInTheDocument(); }); it("does not render subtitle when not provided", () => { render(); expect(screen.queryByText("Subtitle Text")).not.toBeInTheDocument(); }); it("renders description when provided", () => { render(); expect(screen.getByText("Desc text here")).toBeInTheDocument(); }); it("does not render description when not provided", () => { render(); expect(screen.queryByText("Desc text here")).not.toBeInTheDocument(); }); }); describe("Heading levels", () => { it("renders title as h2 when enableHeading is false", () => { render(); const el = screen.getByText("Heading Test"); expect(el.tagName).toBe("H2"); }); it("renders title as h1 when enableHeading is true", () => { render(); const el = screen.getByText("Heading Test"); expect(el.tagName).toBe("H1"); }); it("renders subtitle as h3 when enableHeading is false", () => { render(); const el = screen.getByText("Sub Heading"); expect(el.tagName).toBe("H3"); }); it("renders subtitle as h2 when enableHeading is true", () => { render(); const el = screen.getByText("Sub Heading"); expect(el.tagName).toBe("H2"); }); }); describe("Background themes", () => { it("applies white background by default", () => { const { container } = render(); expect(container.firstChild).toHaveClass("bg-white"); }); it.each([ ["blue", "bg-[#07B2E2]"], ["green", "bg-[#26B170]"], ["yellow", "bg-[#F5FF1E]"], ["purple", "bg-[#931D69]"], ["white", "bg-white"], ["navy", "bg-[#00002D]"], ] as const)("applies %s background class", (bg, expectedClass) => { const { container } = render( ); expect(container.firstChild).toHaveClass(expectedClass); }); }); describe("Color theme", () => { it("applies dark text color by default", () => { const { container } = render(); expect(container.firstChild).toHaveClass("text-text"); }); it("applies light text color when color is light", () => { const { container } = render( ); expect(container.firstChild).toHaveClass("text-white"); }); it("applies dark text color when color is dark", () => { const { container } = render( ); expect(container.firstChild).toHaveClass("text-text"); }); }); describe("Max width", () => { it("applies max-w-120 class when maxWidth is true (default)", () => { const { container } = render(); const innerDiv = container.firstChild?.firstChild as HTMLElement; expect(innerDiv).toHaveClass("max-w-120"); }); it("does not apply max-w-120 class when maxWidth is false", () => { const { container } = render( ); const innerDiv = container.firstChild?.firstChild as HTMLElement; expect(innerDiv).not.toHaveClass("max-w-120"); }); }); describe("Location directory list", () => { it("renders location links from list", () => { render(); // Each item appears in both mobile (2-col) and desktop (3-col) lists const nyLinks = screen.getAllByText("New York"); expect(nyLinks.length).toBeGreaterThanOrEqual(2); }); it("generates correct href with lowercase code", () => { render(); const links = screen.getAllByText("New York"); links.forEach(link => { expect(link.closest("a")).toHaveAttribute("href", "/local/ny"); }); }); it("does not append a trailing slash by default", () => { render(); const links = screen.getAllByText("Alabama"); links.forEach(link => { expect(link.closest("a")).toHaveAttribute("href", "/local/al"); }); }); it("appends a trailing slash when addTrailingSlash is true", () => { render( ); const links = screen.getAllByText("Alabama"); links.forEach(link => { expect(link.closest("a")).toHaveAttribute("href", "/local/al/"); }); }); it("does not produce a protocol-relative href when localPathPrefix is '/'", () => { render( ); const links = screen.getAllByText("Alabama"); links.forEach(link => { expect(link.closest("a")).toHaveAttribute("href", "/al"); expect(link.closest("a")?.getAttribute("href")).not.toMatch(/^\/\//); }); }); it("sorts list items alphabetically by name", () => { const unsortedList = [ { name: "Texas", code: "TX" }, { name: "Alabama", code: "AL" }, { name: "Michigan", code: "MI" }, ]; render(); const listItems = screen.getAllByRole("listitem"); const texts = listItems.map(li => li.textContent); // In both grids, Alabama should come before Michigan, Michigan before Texas expect(texts[0]).toBe("Alabama"); }); it("does not render lists when list is empty", () => { render(); expect(screen.queryAllByRole("list")).toHaveLength(0); }); it("does not render lists when list is not provided", () => { render(); expect(screen.queryAllByRole("list")).toHaveLength(0); }); it("renders two column layout for mobile, three for tablet and four for desktop", () => { const { container } = render(); const lists = container.querySelectorAll("ul"); expect(lists).toHaveLength(3); expect(lists[0]).toHaveClass("grid-cols-2"); expect(lists[1]).toHaveClass("md:grid-cols-3"); expect(lists[2]).toHaveClass("xl:grid-cols-4"); }); it("rearranges items in column-major order for 2-col layout", () => { const list = [ { name: "A", code: "A" }, { name: "B", code: "B" }, { name: "C", code: "C" }, { name: "D", code: "D" }, ]; const { container } = render(); // Mobile list (2-col): sorted [A, B, C, D], 2 rows, column-major: A, C, B, D const mobileList = container.querySelector( "ul.grid-cols-2" ) as HTMLElement; const items = mobileList?.querySelectorAll("li"); expect(items?.[0]?.textContent).toBe("A"); expect(items?.[1]?.textContent).toBe("C"); expect(items?.[2]?.textContent).toBe("B"); expect(items?.[3]?.textContent).toBe("D"); }); }); describe("Image", () => { it("renders image when provided", () => { render(); const img = screen.getByAltText("image"); expect(img).toHaveAttribute("src", "https://example.com/img.png"); expect(img).toHaveAttribute("width", "472"); }); it("does not render image when not provided", () => { render(); expect(screen.queryByAltText("image")).not.toBeInTheDocument(); }); }); });