import { beforeEach, describe, it, expect, vi } from "vitest"; import React, { useEffect } from "react"; import { renderWithProviders as render, screen, waitFor, } from "../utils/test-utils"; import "@testing-library/jest-dom/vitest"; import { CommunityReviewScreen } from "../../app/(app)/create/screens/review/CommunityReviewScreen"; import { useCreateFlow } from "../../app/(app)/create/context/CreateFlowContext"; import { testRouter } from "../mocks/navigation"; import { readPendingCommunityAvatarFile } from "../../lib/create/pendingCommunityAvatarUpload"; vi.mock("../../lib/create/pendingCommunityAvatarUpload", () => ({ readPendingCommunityAvatarFile: vi.fn().mockResolvedValue(null), storePendingCommunityAvatarFile: vi.fn(), clearPendingCommunityAvatarFile: vi.fn(), })); function ReviewWithTitle({ title }: { title: string }) { const { state, updateState } = useCreateFlow(); const seededRef = React.useRef(false); useEffect(() => { if (seededRef.current) return; seededRef.current = true; updateState({ title }); }, [title, updateState]); if (state.title !== title) return null; return ; } function ReviewWithAnswers() { const { state, updateState } = useCreateFlow(); const seededRef = React.useRef(false); useEffect(() => { if (seededRef.current) return; seededRef.current = true; updateState({ title: "Garden Club", selectedOrganizationTypeIds: ["mutualAid"], selectedCommunitySizeIds: ["thirteenToOneHundred"], selectedMaturityIds: ["established"], }); }, [updateState]); if (state.title !== "Garden Club") return null; return ; } describe("CommunityReviewScreen", () => { beforeEach(() => { testRouter.replace.mockReset(); testRouter.push.mockReset(); vi.mocked(readPendingCommunityAvatarFile).mockReset(); vi.mocked(readPendingCommunityAvatarFile).mockResolvedValue(null); }); it("renders without crashing", () => { render(); expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument(); }); it("shows an empty state instead of a baked-in community name", () => { render(); expect( screen.getByRole("heading", { name: "No community in progress yet" }), ).toBeInTheDocument(); expect(screen.queryByText("Mutual Aid Mondays")).not.toBeInTheDocument(); expect( screen.queryByRole("heading", { name: "Your community is added - congrats!", }), ).not.toBeInTheDocument(); expect( screen.getByRole("link", { name: "Start creating" }), ).toHaveAttribute("href", "/create/informational"); }); it("renders HeaderLockup with expected title when a community name exists", () => { render(); expect( screen.getByRole("heading", { name: "Here is your community", }), ).toBeInTheDocument(); }); it("renders HeaderLockup with expected description when a community name exists", () => { render(); expect( screen.getByText( /Next you will write your Rule/i, ), ).toBeInTheDocument(); }); it("renders Rule with the community name from state", () => { render(); expect(screen.getByText("Garden Club")).toBeInTheDocument(); }); it("shows a guest logo staged in the browser on the rule card", async () => { const pending = new File([new Uint8Array([1, 2, 3])], "logo.png", { type: "image/png", }); vi.mocked(readPendingCommunityAvatarFile).mockResolvedValue(pending); Object.defineProperty(URL, "createObjectURL", { value: vi.fn(() => "blob:pending-avatar"), writable: true, configurable: true, }); Object.defineProperty(URL, "revokeObjectURL", { value: vi.fn(), writable: true, configurable: true, }); render(); await waitFor(() => { expect(screen.getByRole("img", { name: "Garden Club" })).toHaveAttribute( "src", "blob:pending-avatar", ); }); expect(readPendingCommunityAvatarFile).toHaveBeenCalled(); }); it("omits the Rule description when the user has not entered community context", () => { render(); expect( screen.queryByText( /Mutual Aid Monday is a grassroots community in Denver/i, ), ).not.toBeInTheDocument(); }); it("lists kind, size, and maturity with Edit controls", () => { render(); expect(screen.getByText("Kind")).toBeInTheDocument(); expect(screen.getByText("Mutual aid")).toBeInTheDocument(); expect(screen.getByText("Size")).toBeInTheDocument(); expect(screen.getByText("13 to 100 people")).toBeInTheDocument(); expect(screen.getByText("Maturity")).toBeInTheDocument(); expect(screen.getByText("Established")).toBeInTheDocument(); expect(screen.getAllByRole("button", { name: "Edit" })).toHaveLength(3); }); it("renders Rule as a button when a community name exists", () => { render(); const buttons = screen.getAllByRole("button"); expect(buttons.length).toBeGreaterThanOrEqual(1); expect(buttons.some((el) => el.textContent?.includes("Garden Club"))).toBe( true, ); }); }); /** * Seeds `pendingTemplateAction` into CreateFlowContext before the screen * under test mounts, so we can assert its mount-time redirect behavior. * Mirrors the flow `handleCustomizeTemplate` / `handleUseTemplateWithoutChanges` * create when the user picks a template before completing community stage. */ function ReviewWithPendingAction({ mode, }: { mode: "customize" | "useWithoutChanges"; }) { const { state, updateState } = useCreateFlow(); const seededRef = React.useRef(false); useEffect(() => { if (seededRef.current) return; seededRef.current = true; updateState({ title: "Neighborhood", pendingTemplateAction: { slug: "mutual-aid-mondays", mode }, }); }, [mode, updateState]); // Block the real screen from mounting until the seed landed — otherwise // its own `useEffect` reads an empty state on the first pass and bails. if (!state.pendingTemplateAction) return null; return ; } describe("CommunityReviewScreen — pendingTemplateAction redirect", () => { beforeEach(() => { testRouter.replace.mockReset(); testRouter.push.mockReset(); }); it("redirects to /create/core-values when mode === 'customize'", async () => { render(); await waitFor(() => { expect(testRouter.replace).toHaveBeenCalledWith("/create/core-values"); }); expect(testRouter.push).not.toHaveBeenCalled(); }); it("redirects Use without changes to community-name so identity is not skipped", async () => { render(); await waitFor(() => { expect(testRouter.replace).toHaveBeenCalledWith("/create/community-name"); }); expect(testRouter.push).not.toHaveBeenCalled(); }); it("does not redirect when no pendingTemplateAction is set", () => { render(); expect(testRouter.replace).not.toHaveBeenCalled(); }); });