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();
});
});