import { renderWithProviders as render, screen, cleanup, fireEvent, } from "../utils/test-utils"; import userEvent from "@testing-library/user-event"; import { vi, describe, test, expect, afterEach } from "vitest"; import CardStack from "../../app/components/cards/CardStack"; const SAMPLE_CARDS = [ { id: "1", label: "Option A", supportText: "Description A", recommended: true, }, { id: "2", label: "Option B", supportText: "Description B", recommended: false, }, { id: "3", label: "Option C", supportText: "Description C", recommended: true, }, ]; afterEach(() => { cleanup(); }); describe("CardStack Component", () => { test("renders header when title is provided", () => { render( , ); expect( screen.getByText("How should this community communicate?"), ).toBeInTheDocument(); expect(screen.getByText("Pick one or more.")).toBeInTheDocument(); }); test("renders up to 5 recommended cards in compact (grid) mode", () => { render(); expect(screen.getAllByText("Option A").length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText("Option C").length).toBeGreaterThanOrEqual(1); expect(screen.queryByText("Option B")).not.toBeInTheDocument(); }); test("renders all cards in expanded (list) mode", () => { render(); expect(screen.getAllByText("Option A").length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText("Option B").length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText("Option C").length).toBeGreaterThanOrEqual(1); }); test("hides helper icons on compact, expanded, and single-stack cards", () => { const { unmount } = render( , ); expect(screen.queryByText("?")).not.toBeInTheDocument(); unmount(); const expanded = render(); expect(screen.queryByText("?")).not.toBeInTheDocument(); expanded.unmount(); render(); expect(screen.queryByText("?")).not.toBeInTheDocument(); }); test("expanded tiles use the compact 142px CardSelection height", () => { render(); const tiles = screen.getAllByRole("button", { name: "Option A: Description A", }); expect(tiles.length).toBeGreaterThan(0); for (const tile of tiles) { expect(tile.className).toMatch(/142px/); expect(tile).toHaveClass("flex-col"); expect(tile).not.toHaveClass("flex-row"); } }); test("shows See all toggle when hasMore is true", () => { render(); expect( screen.getByRole("button", { name: "See all communication approaches" }), ).toBeInTheDocument(); }); test("does not show toggle when hasMore is false", () => { render(); expect( screen.queryByRole("button", { name: "See all communication approaches", }), ).not.toBeInTheDocument(); }); test("toggle expands when clicked", async () => { const user = userEvent.setup(); render(); const toggle = screen.getByRole("button", { name: "See all communication approaches", }); await user.click(toggle); expect( screen.getByRole("button", { name: "Show less" }), ).toBeInTheDocument(); }); test("single-stack See all / Show less is centered, not end-aligned", () => { render( , ); const toggle = screen.getByRole("button", { name: "See all communication approaches", }); expect(toggle.className).toMatch(/\bself-center\b/); expect(toggle.className).not.toMatch(/\bself-end\b/); }); test("calls onCardSelect when a card is clicked", () => { const onCardSelect = vi.fn(); render(); const cardButtons = screen.getAllByRole("button", { name: "Option A: Description A", }); fireEvent.click(cardButtons[0]); expect(onCardSelect).toHaveBeenCalledWith("1"); }); test("renders with selectedId", () => { render(); expect(screen.getAllByText("SELECTED").length).toBeGreaterThanOrEqual(1); }); test("compact single-stack shows seven recommended cards and hides the tag", () => { const cards = Array.from({ length: 8 }, (_, index) => ({ id: String(index), label: `Card ${index}`, supportText: `Desc ${index}`, recommended: true, })); render( , ); expect( screen.getAllByRole("button", { name: /Card \d: Desc \d/ }), ).toHaveLength(7); expect(screen.queryByText("RECOMMENDED")).not.toBeInTheDocument(); }); test("See all search filters the expanded single-stack", () => { render( , ); const search = screen.getByRole("searchbox", { name: "Search this deck" }); fireEvent.change(search, { target: { value: "Option B" } }); expect( screen.getByRole("button", { name: "Option B: Description B" }), ).toBeInTheDocument(); expect( screen.queryByRole("button", { name: "Option A: Description A" }), ).not.toBeInTheDocument(); }); test("selected compact card keeps catalog copy and hides the alternative", () => { render( , ); expect( screen.getByRole("button", { name: "Option A: Catalog copy" }), ).toBeInTheDocument(); expect(screen.queryByText(/Fits a worker co-op/)).not.toBeInTheDocument(); expect( screen.queryByRole("button", { name: "Option B" }), ).not.toBeInTheDocument(); }); test("unselected compact card shows the why line", () => { render( , ); expect( screen.getByRole("button", { name: "Option A: Fits a worker co-op" }), ).toBeInTheDocument(); }); test("compact alternative row opens the named card", () => { const onAlternativeSelect = vi.fn(); render( , ); fireEvent.click(screen.getByRole("button", { name: "Option B" })); expect(onAlternativeSelect).toHaveBeenCalledWith("2"); }); test("calls onCardSelect when re-clicking an already selected card", () => { const onCardSelect = vi.fn(); render( , ); const cardButtons = screen.getAllByRole("button", { name: "Option A: Description A", }); fireEvent.click(cardButtons[0]); expect(onCardSelect).toHaveBeenCalledWith("1"); expect(screen.queryByRole("button", { name: "Remove policy" })).not.toBeInTheDocument(); }); });