Files
community-rule/tests/pages/communication-methods.test.jsx
T
adilalloandCursor a820739d07 Open create-flow method modals with editable fields and tertiary default copy.
Seeded section text should look like the form default state, not locked primary, and Customize stays on the kebab instead of unlocking the body.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 12:59:53 -06:00

429 lines
14 KiB
React

import {
renderWithProviders as render,
screen,
cleanup,
within,
} from "../utils/test-utils";
import userEvent from "@testing-library/user-event";
import { describe, test, expect, afterEach } from "vitest";
import { useLayoutEffect } from "react";
import { CommunicationMethodsScreen } from "../../app/(app)/create/screens/card/CommunicationMethodsScreen";
import { useCreateFlow } from "../../app/(app)/create/context/CreateFlowContext";
const CUSTOM_POLICY_ID = "550e8400-e29b-41d4-a716-446655440000";
function CommunicationMethodsScreenWithState({ initial }) {
const { replaceState } = useCreateFlow();
useLayoutEffect(() => {
replaceState(initial);
}, [replaceState, initial]);
return <CommunicationMethodsScreen />;
}
afterEach(() => {
cleanup();
});
describe("Create flow communication-methods page", () => {
test("clicking a card opens the Create modal", async () => {
const user = userEvent.setup();
render(<CommunicationMethodsScreen />);
const signalCards = screen.getAllByRole("button", {
name: /Signal: Encrypted messaging/,
});
await user.click(signalCards[0]);
const dialog = screen.getByRole("dialog");
expect(dialog).toBeInTheDocument();
expect(within(dialog).getByText("Signal")).toBeInTheDocument();
expect(within(dialog).getByText("Add Platform")).toBeInTheDocument();
});
test("re-opening a selected method shows Save; Remove is in the kebab", async () => {
const user = userEvent.setup();
render(<CommunicationMethodsScreen />);
const signalCards = screen.getAllByRole("button", {
name: /Signal: Encrypted messaging/,
});
await user.click(signalCards[0]);
const dialog = screen.getByRole("dialog");
await user.click(within(dialog).getByRole("button", { name: "Add Platform" }));
await user.click(signalCards[0]);
const dialogAgain = screen.getByRole("dialog");
expect(
within(dialogAgain).queryByRole("button", { name: "Remove" }),
).not.toBeInTheDocument();
expect(
within(dialogAgain).queryByRole("button", { name: "Add Platform" }),
).not.toBeInTheDocument();
expect(
within(dialogAgain).getByRole("button", { name: "Save" }),
).toBeInTheDocument();
await user.click(within(dialogAgain).getByRole("button", { name: "More options" }));
expect(screen.getByRole("menuitem", { name: "Remove" })).toBeInTheDocument();
});
test("Remove from the kebab deselects the method", async () => {
const user = userEvent.setup();
render(<CommunicationMethodsScreen />);
const signalCards = screen.getAllByRole("button", {
name: /Signal: Encrypted messaging/,
});
await user.click(signalCards[0]);
await user.click(
within(screen.getByRole("dialog")).getByRole("button", {
name: "Add Platform",
}),
);
expect(signalCards[0]).toHaveTextContent("SELECTED");
await user.click(signalCards[0]);
await user.click(
within(screen.getByRole("dialog")).getByRole("button", {
name: "More options",
}),
);
await user.click(screen.getByRole("menuitem", { name: "Remove" }));
expect(signalCards[0]).not.toHaveTextContent("SELECTED");
});
test("kebab menu does not include Close", async () => {
const user = userEvent.setup();
render(<CommunicationMethodsScreen />);
const signalCards = screen.getAllByRole("button", {
name: /Signal: Encrypted messaging/,
});
await user.click(signalCards[0]);
const dialog = screen.getByRole("dialog");
await user.click(within(dialog).getByRole("button", { name: "More options" }));
expect(
screen.queryByRole("menuitem", { name: "Close" }),
).not.toBeInTheDocument();
});
test("unselected preset method fields are editable without Customize", async () => {
const user = userEvent.setup();
render(<CommunicationMethodsScreen />);
const signalCards = screen.getAllByRole("button", {
name: /Signal: Encrypted messaging/,
});
await user.click(signalCards[0]);
const dialog = screen.getByRole("dialog");
const textbox = within(dialog).getAllByRole("textbox")[0];
expect(textbox).not.toBeDisabled();
await user.click(within(dialog).getByRole("button", { name: "More options" }));
expect(screen.getByRole("menuitem", { name: "Customize" })).toBeInTheDocument();
});
test("meta-only custom policy opens with editable section fields", async () => {
const user = userEvent.setup();
render(
<CommunicationMethodsScreenWithState
initial={{
selectedCommunicationMethodIds: [CUSTOM_POLICY_ID],
customMethodCardMetaById: {
[CUSTOM_POLICY_ID]: { label: "My policy", supportText: "Desc" },
},
}}
/>,
);
await user.click(
screen.getAllByRole("button", { name: /My policy: Desc/ })[0],
);
const dialog = await screen.findByRole("dialog");
const textboxes = within(dialog).getAllByRole("textbox");
expect(textboxes.length).toBeGreaterThan(0);
for (const field of textboxes) {
expect(field).not.toBeDisabled();
}
expect(
within(dialog).queryByText("No custom fields yet."),
).not.toBeInTheDocument();
});
test("renders without error", () => {
render(<CommunicationMethodsScreen />);
expect(
screen.getByText(
"How should this community communicate with each-other?",
),
).toBeInTheDocument();
});
test("renders HeaderLockup and CardStack content", () => {
render(<CommunicationMethodsScreen />);
expect(
screen.getByText(/You can select multiple methods for different needs or/),
).toBeInTheDocument();
expect(screen.getByRole("button", { name: /^add$/i })).toBeInTheDocument();
expect(
screen.getByRole("button", { name: "See all communication approaches" }),
).toBeInTheDocument();
});
test("with a finalized custom policy, inline add link still opens the custom wizard", async () => {
const user = userEvent.setup();
render(
<CommunicationMethodsScreenWithState
initial={{
selectedCommunicationMethodIds: [CUSTOM_POLICY_ID],
customMethodCardMetaById: {
[CUSTOM_POLICY_ID]: { label: "My policy", supportText: "Desc" },
},
}}
/>,
);
expect(
screen.queryByRole("button", { name: "Remove policy" }),
).not.toBeInTheDocument();
const addButtons = screen.getAllByRole("button", { name: /^add$/i });
expect(addButtons.length).toBeGreaterThanOrEqual(1);
await user.click(addButtons[0]);
const dialog = await screen.findByRole("dialog");
expect(
within(dialog).getByText("What do you call your group's new policy?"),
).toBeInTheDocument();
});
test("toggle expands and shows Show less", async () => {
const user = userEvent.setup();
render(<CommunicationMethodsScreen />);
const toggle = screen.getByRole("button", {
name: "See all communication approaches",
});
await user.click(toggle);
expect(
screen.getByRole("button", { name: "Show less" }),
).toBeInTheDocument();
expect(
screen.getByText(
"What method should this community use to communicate with eachother?",
),
).toBeInTheDocument();
expect(
screen.getByRole("button", { name: /^add$/i }),
).toBeInTheDocument();
});
test("opening Create modal for custom policy shows saved field blocks editable", async () => {
const user = userEvent.setup();
const initial = {
selectedCommunicationMethodIds: [CUSTOM_POLICY_ID],
methodSectionsPinCommitted: { communication: true },
customMethodCardMetaById: {
[CUSTOM_POLICY_ID]: { label: "My policy", supportText: "Support copy" },
},
customMethodCardFieldBlocksById: {
[CUSTOM_POLICY_ID]: [
{
kind: "text",
id: "f1",
blockTitle: "Guidelines",
placeholderText: "Enter norms here",
},
],
},
};
render(<CommunicationMethodsScreenWithState initial={initial} />);
const policyTiles = screen.getAllByRole("button", {
name: /My policy: Support copy/,
});
await user.click(policyTiles[0]);
const dialog = screen.getByRole("dialog");
expect(within(dialog).getByText("Guidelines")).toBeInTheDocument();
const textboxesBefore = within(dialog).getAllByRole("textbox");
expect(textboxesBefore).toHaveLength(1);
const textarea = textboxesBefore[0];
expect(textarea).not.toBeDisabled();
expect(textarea).toHaveValue("Enter norms here");
});
test("opening Create modal for custom policy shows badge options as interactive chips", async () => {
const user = userEvent.setup();
const initial = {
selectedCommunicationMethodIds: [CUSTOM_POLICY_ID],
methodSectionsPinCommitted: { communication: true },
customMethodCardMetaById: {
[CUSTOM_POLICY_ID]: { label: "My policy", supportText: "Support copy" },
},
customMethodCardFieldBlocksById: {
[CUSTOM_POLICY_ID]: [
{
kind: "badges",
id: "b1",
blockTitle: "Choose channels",
options: ["Alpha", "Beta"],
},
],
},
};
render(<CommunicationMethodsScreenWithState initial={initial} />);
const policyTiles = screen.getAllByRole("button", {
name: /My policy: Support copy/,
});
await user.click(policyTiles[0]);
const dialog = screen.getByRole("dialog");
expect(within(dialog).getByText("Choose channels")).toBeInTheDocument();
const alpha = within(dialog).getByRole("button", {
name: /Deselect Alpha/,
});
const beta = within(dialog).getByRole("button", {
name: /Deselect Beta/,
});
expect(alpha).not.toBeDisabled();
expect(beta).not.toBeDisabled();
});
test("editing custom policy field blocks updates draft state after Save", async () => {
const user = userEvent.setup();
let latest = {};
function Probe({ initial }) {
const { replaceState, state } = useCreateFlow();
useLayoutEffect(() => {
replaceState(initial);
}, [replaceState, initial]);
useLayoutEffect(() => {
latest = state;
}, [state]);
return <CommunicationMethodsScreen />;
}
const initial = {
selectedCommunicationMethodIds: [CUSTOM_POLICY_ID],
customMethodCardMetaById: {
[CUSTOM_POLICY_ID]: { label: "My policy", supportText: "Support copy" },
},
customMethodCardFieldBlocksById: {
[CUSTOM_POLICY_ID]: [
{
kind: "text",
id: "f1",
blockTitle: "Guidelines",
placeholderText: "Original",
},
],
},
};
render(<Probe initial={initial} />);
const policyTiles = screen.getAllByRole("button", {
name: /My policy: Support copy/,
});
await user.click(policyTiles[0]);
const dialog = screen.getByRole("dialog");
const textarea = within(dialog).getByRole("textbox");
await user.clear(textarea);
await user.type(textarea, "Updated norms");
await user.click(within(dialog).getByRole("button", { name: "Save" }));
const row = latest.customMethodCardFieldBlocksById?.[CUSTOM_POLICY_ID]?.[0];
expect(row).toMatchObject({
kind: "text",
placeholderText: "Updated norms",
});
});
test("duplicate staged copy is unselected; closing modal drops ephemeral card; duplicate again works", async () => {
const user = userEvent.setup();
let latest = {};
function Probe({ initial }) {
const { replaceState, state } = useCreateFlow();
useLayoutEffect(() => {
replaceState(initial);
}, [replaceState, initial]);
useLayoutEffect(() => {
latest = state;
}, [state]);
return <CommunicationMethodsScreen />;
}
const initial = {
selectedCommunicationMethodIds: [CUSTOM_POLICY_ID],
customMethodCardMetaById: {
[CUSTOM_POLICY_ID]: { label: "My policy", supportText: "Support copy" },
},
customMethodCardFieldBlocksById: {
[CUSTOM_POLICY_ID]: [
{
kind: "text",
id: "f1",
blockTitle: "Guidelines",
placeholderText: "Enter norms here",
},
],
},
};
render(<Probe initial={initial} />);
const policyTiles = screen.getAllByRole("button", {
name: /My policy: Support copy/,
});
await user.click(policyTiles[0]);
const dialog = screen.getByRole("dialog");
await user.click(
within(dialog).getByRole("button", { name: "More options" }),
);
await user.click(screen.getByRole("menuitem", { name: "Duplicate" }));
const metaAfterDup = latest.customMethodCardMetaById ?? {};
const dupIds = Object.keys(metaAfterDup).filter(
(id) => id !== CUSTOM_POLICY_ID,
);
expect(dupIds).toHaveLength(1);
const dupId = dupIds[0];
expect(latest.selectedCommunicationMethodIds).toEqual([CUSTOM_POLICY_ID]);
expect(
within(dialog).getByRole("button", { name: "Add Platform" }),
).toBeInTheDocument();
expect(metaAfterDup[dupId].label.endsWith(" (copy)")).toBe(true);
expect(within(dialog).getByRole("heading", { level: 1 })).toHaveTextContent(
/^My policy \(copy\)$/,
);
expect(within(dialog).getByText("Support copy")).toBeInTheDocument();
expect(within(dialog).getByText("Guidelines")).toBeInTheDocument();
expect(within(dialog).getByRole("textbox")).toHaveValue("Enter norms here");
await user.click(
within(dialog).getByRole("button", { name: "Close dialog" }),
);
const metaAfterClose = latest.customMethodCardMetaById ?? {};
expect(metaAfterClose[dupId]).toBeUndefined();
expect(Object.keys(metaAfterClose)).toEqual([CUSTOM_POLICY_ID]);
await user.click(policyTiles[0]);
const dialog2 = screen.getByRole("dialog");
await user.click(
within(dialog2).getByRole("button", { name: "More options" }),
);
await user.click(screen.getByRole("menuitem", { name: "Duplicate" }));
const metaSecond = latest.customMethodCardMetaById ?? {};
const dupIds2 = Object.keys(metaSecond).filter(
(id) => id !== CUSTOM_POLICY_ID,
);
expect(dupIds2).toHaveLength(1);
});
});