Files
community-rule/tests/pages/decision-approaches.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

642 lines
20 KiB
React

import {
renderWithProviders as render,
screen,
cleanup,
within,
waitFor,
} from "../utils/test-utils";
import userEvent from "@testing-library/user-event";
import { describe, test, expect, afterEach } from "vitest";
import { useLayoutEffect } from "react";
import { DecisionApproachesScreen } from "../../app/(app)/create/screens/right-rail/DecisionApproachesScreen";
import { useCreateFlow } from "../../app/(app)/create/context/CreateFlowContext";
const CUSTOM_APPROACH_ID = "550e8400-e29b-41d4-a716-446655440000";
function DecisionApproachesScreenWithState({ initial }) {
const { replaceState } = useCreateFlow();
useLayoutEffect(() => {
replaceState(initial);
}, [replaceState, initial]);
return <DecisionApproachesScreen />;
}
afterEach(() => {
cleanup();
});
describe("Create flow decision-approaches page", () => {
test("renders without error", () => {
render(<DecisionApproachesScreen />);
expect(
screen.getByRole("heading", {
name: "How should this community make difficult decisions?",
}),
).toBeInTheDocument();
});
test("renders sidebar description with add link", () => {
render(<DecisionApproachesScreen />);
const addControl = screen.getByRole("button", {
name: /^Add$/i,
});
expect(addControl).toBeInTheDocument();
const description = addControl.parentElement;
expect(description).not.toBeNull();
expect(description?.textContent).toMatch(/Select as many as you need/);
expect(description?.textContent).toMatch(/new decision making approaches/);
});
test("with a finalized custom approach, sidebar still shows add (not Remove policy)", () => {
render(
<DecisionApproachesScreenWithState
initial={{
selectedDecisionApproachIds: [CUSTOM_APPROACH_ID],
customMethodCardMetaById: {
[CUSTOM_APPROACH_ID]: {
label: "My approach",
supportText: "Desc",
},
},
}}
/>,
);
expect(
screen.queryByRole("button", { name: "Remove policy" }),
).not.toBeInTheDocument();
const addControl = screen.getByRole("button", { name: /^Add$/i });
expect(addControl).toBeInTheDocument();
const description = addControl.parentElement;
expect(description?.textContent).toMatch(/Select as many as you need/);
expect(description?.textContent).toMatch(/new decision making approaches/);
});
test("renders message box with title and checkboxes", () => {
render(<DecisionApproachesScreen />);
const region = screen.getByRole("region", {
name: "Consider defining approaches to steward key resources:",
});
expect(region).toBeInTheDocument();
expect(
within(region).getByRole("checkbox", {
name: "Amend your CommunityRule",
}),
).toBeInTheDocument();
expect(
within(region).getByRole("checkbox", { name: "Steward finances" }),
).toBeInTheDocument();
expect(
within(region).getByRole("checkbox", { name: "Project level decisions" }),
).toBeInTheDocument();
expect(
within(region).getByRole("checkbox", {
name: "Discipline and member termination",
}),
).toBeInTheDocument();
});
test("renders card stack with See all decision approaches toggle", () => {
render(<DecisionApproachesScreen />);
expect(
screen.getByRole("button", { name: "See all decision approaches" }),
).toBeInTheDocument();
});
test("renders recommended approach cards", () => {
render(<DecisionApproachesScreen />);
expect(
screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
}),
).toBeInTheDocument();
expect(
screen.getByRole("button", {
name: /Do-ocracy: Decisions are made by those who take initiative/,
}),
).toBeInTheDocument();
expect(
screen.getByRole("button", {
name: /Consensus Decision-Making: All members must agree/,
}),
).toBeInTheDocument();
});
test("toggle expands and shows Show less", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
const toggle = screen.getByRole("button", {
name: "See all decision approaches",
});
await user.click(toggle);
expect(
screen.getByRole("button", { name: "Show less" }),
).toBeInTheDocument();
expect(screen.getAllByRole("button", { name: /^add$/i })).toHaveLength(2);
});
test("expanded view reveals additional non-recommended approaches", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
expect(
screen.queryByRole("button", { name: /^Sociocracy:/ }),
).not.toBeInTheDocument();
const toggle = screen.getByRole("button", {
name: "See all decision approaches",
});
await user.click(toggle);
expect(
screen.getByRole("button", { name: /^Sociocracy:/ }),
).toBeInTheDocument();
});
test("selecting an approach in the expanded list does not move it to the top", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
await user.click(
screen.getByRole("button", { name: "See all decision approaches" }),
);
const cardLabels = () =>
screen
.getAllByRole("button")
.map((el) => el.getAttribute("aria-label") || "")
.filter((label) => label.includes(": "));
const labelsBefore = cardLabels();
expect(labelsBefore[0]).toMatch(/^Lazy Consensus:/);
expect(labelsBefore.some((label) => label.startsWith("Sociocracy:"))).toBe(
true,
);
await user.click(screen.getByRole("button", { name: /^Sociocracy:/ }));
await user.click(
within(await screen.findByRole("dialog")).getByRole("button", {
name: "Add Approach",
}),
);
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
expect(
screen.getByRole("button", { name: "Show less" }),
).toBeInTheDocument();
expect(cardLabels()).toEqual(labelsBefore);
expect(
screen.getByRole("button", { name: /^Sociocracy:/ }),
).toHaveTextContent("SELECTED");
});
test("opens approach fields editable without Customize", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
await user.click(
screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
}),
);
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();
}
await user.click(
within(dialog).getByRole("button", { name: "More options" }),
);
expect(
screen.getByRole("menuitem", { name: "Customize" }),
).toBeInTheDocument();
});
test("clicking a card opens the create modal and confirming selects it", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
const card = screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
});
await user.click(card);
const dialog = await screen.findByRole("dialog");
expect(dialog).toBeInTheDocument();
const confirmButton = within(dialog).getByRole("button", {
name: "Add Approach",
});
await user.click(confirmButton);
expect(screen.getByText("SELECTED")).toBeInTheDocument();
});
test("re-opening a selected approach shows Save; Remove is in the kebab", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
const card = screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
});
await user.click(card);
const dialog = await screen.findByRole("dialog");
await user.click(
within(dialog).getByRole("button", {
name: "Add Approach",
}),
);
await user.click(card);
const dialogAgain = screen.getByRole("dialog");
expect(
within(dialogAgain).queryByRole("button", { name: "Remove" }),
).not.toBeInTheDocument();
expect(
within(dialogAgain).queryByRole("button", { name: "Add Approach" }),
).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("Save on a selected approach persists the edit and closes", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
const card = screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
});
await user.click(card);
const dialog = await screen.findByRole("dialog");
await user.click(
within(dialog).getByRole("button", { name: "Add Approach" }),
);
await user.click(card);
const dialogAgain = await screen.findByRole("dialog");
const principleField = within(dialogAgain).getByRole("textbox", {
name: /core principle/i,
});
await user.clear(principleField);
await user.type(principleField, "Edited principle");
await user.click(within(dialogAgain).getByRole("button", { name: "Save" }));
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
});
test("Remove from the kebab deselects the approach", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
const card = screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
});
await user.click(card);
await user.click(
within(await screen.findByRole("dialog")).getByRole("button", {
name: "Add Approach",
}),
);
expect(card).toHaveTextContent("SELECTED");
await user.click(card);
await user.click(
within(screen.getByRole("dialog")).getByRole("button", { name: "More options" }),
);
await user.click(screen.getByRole("menuitem", { name: "Remove" }));
expect(card).not.toHaveTextContent("SELECTED");
});
test("when editing a published rule, method modal kebab has no Duplicate", async () => {
const user = userEvent.setup();
render(
<DecisionApproachesScreenWithState
initial={{
editingPublishedRuleId: "published-rule-1",
selectedDecisionApproachIds: ["lazy-consensus"],
}}
/>,
);
const card = screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
});
await user.click(card);
const dialog = await screen.findByRole("dialog");
await user.click(
within(dialog).getByRole("button", { name: "More options" }),
);
expect(
screen.queryByRole("menuitem", { name: "Duplicate" }),
).not.toBeInTheDocument();
});
test("message box checkboxes are interactive", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
const amendCheckbox = screen.getByRole("checkbox", {
name: "Amend your CommunityRule",
});
expect(amendCheckbox).not.toBeChecked();
await user.click(amendCheckbox);
expect(amendCheckbox).toBeChecked();
});
test("each approach modal includes the key-resource applicable-scope chips", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
await user.click(
screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
}),
);
const dialog = await screen.findByRole("dialog");
expect(
within(dialog).getByRole("button", {
name: "Select Amend your CommunityRule",
}),
).toBeInTheDocument();
expect(
within(dialog).getByRole("button", { name: "Select Steward finances" }),
).toBeInTheDocument();
expect(
within(dialog).getByRole("button", {
name: "Select Project level decisions",
}),
).toBeInTheDocument();
expect(
within(dialog).getByRole("button", {
name: "Select Discipline and member termination",
}),
).toBeInTheDocument();
});
test("checking a key-resource box does not highlight that chip on an unselected approach", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
await user.click(
screen.getByRole("checkbox", { name: "Steward finances" }),
);
await user.click(
screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
}),
);
const lazyDialog = await screen.findByRole("dialog");
expect(
within(lazyDialog).getByRole("button", {
name: "Select Steward finances",
}),
).toBeInTheDocument();
});
test("applicable-scope chips stay on the approach they were chosen for", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
await user.click(
screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
}),
);
const lazyDialog = await screen.findByRole("dialog");
await user.click(
within(lazyDialog).getByRole("button", {
name: "Select Steward finances",
}),
);
expect(
screen.getByRole("checkbox", { name: "Steward finances" }),
).toBeChecked();
await user.click(
within(lazyDialog).getByRole("button", { name: "Add Approach" }),
);
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
await user.click(
screen.getByRole("button", {
name: /Do-ocracy: Decisions are made by those who take initiative/,
}),
);
const doocracyDialog = await screen.findByRole("dialog");
expect(
within(doocracyDialog).getByRole("button", {
name: "Select Steward finances",
}),
).toBeInTheDocument();
expect(
within(doocracyDialog).queryByRole("button", {
name: "Deselect Steward finances",
}),
).not.toBeInTheDocument();
expect(
screen.getByRole("checkbox", { name: "Steward finances" }),
).not.toBeChecked();
await user.click(
within(doocracyDialog).getByRole("button", { name: "Close dialog" }),
);
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
expect(
screen.getByRole("checkbox", { name: "Steward finances" }),
).toBeChecked();
await user.click(
screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
}),
);
const lazyAgain = await screen.findByRole("dialog");
expect(
within(lazyAgain).getByRole("button", {
name: "Deselect Steward finances",
}),
).toBeInTheDocument();
expect(
screen.getByRole("checkbox", { name: "Steward finances" }),
).toBeChecked();
});
test("checking a key-resource box with a modal open selects that chip on the open approach only", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
await user.click(
screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
}),
);
const lazyDialog = await screen.findByRole("dialog");
await user.click(
within(lazyDialog).getByRole("button", { name: "Add Approach" }),
);
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
await user.click(
screen.getByRole("button", {
name: /Do-ocracy: Decisions are made by those who take initiative/,
}),
);
const doocracyDialog = await screen.findByRole("dialog");
await user.click(
screen.getByRole("checkbox", { name: "Steward finances" }),
);
expect(
within(doocracyDialog).getByRole("button", {
name: "Deselect Steward finances",
}),
).toBeInTheDocument();
await user.click(
within(doocracyDialog).getByRole("button", { name: "Close dialog" }),
);
await user.click(await screen.findByRole("button", { name: "Discard" }));
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
await user.click(
screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
}),
);
const lazyAgain = await screen.findByRole("dialog");
expect(
within(lazyAgain).getByRole("button", {
name: "Select Steward finances",
}),
).toBeInTheDocument();
});
test("highlighting a key-resource chip on a second approach does not rewrite the first", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
await user.click(
screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
}),
);
const lazyDialog = await screen.findByRole("dialog");
await user.click(
within(lazyDialog).getByRole("button", {
name: "Select Steward finances",
}),
);
expect(
screen.getByRole("checkbox", { name: "Steward finances" }),
).toBeChecked();
await user.click(
within(lazyDialog).getByRole("button", { name: "Add Approach" }),
);
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
await user.click(
screen.getByRole("button", {
name: /Do-ocracy: Decisions are made by those who take initiative/,
}),
);
const doocracyDialog = await screen.findByRole("dialog");
await user.click(
within(doocracyDialog).getByRole("button", {
name: "Select Project level decisions",
}),
);
expect(
screen.getByRole("checkbox", { name: "Project level decisions" }),
).toBeChecked();
expect(
screen.getByRole("checkbox", { name: "Steward finances" }),
).not.toBeChecked();
expect(
within(doocracyDialog).getByRole("button", {
name: "Select Steward finances",
}),
).toBeInTheDocument();
await user.click(
within(doocracyDialog).getByRole("button", { name: "Add Approach" }),
);
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
expect(
screen.getByRole("checkbox", { name: "Steward finances" }),
).toBeChecked();
expect(
screen.getByRole("checkbox", { name: "Project level decisions" }),
).toBeChecked();
await user.click(
screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
}),
);
const lazyAgain = await screen.findByRole("dialog");
expect(
within(lazyAgain).getByRole("button", {
name: "Deselect Steward finances",
}),
).toBeInTheDocument();
expect(
within(lazyAgain).getByRole("button", {
name: "Select Project level decisions",
}),
).toBeInTheDocument();
await user.click(
within(lazyAgain).getByRole("button", { name: "Close dialog" }),
);
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
await user.click(
screen.getByRole("button", {
name: /Do-ocracy: Decisions are made by those who take initiative/,
}),
);
const doocracyAgain = await screen.findByRole("dialog");
expect(
within(doocracyAgain).getByRole("button", {
name: "Deselect Project level decisions",
}),
).toBeInTheDocument();
expect(
within(doocracyAgain).getByRole("button", {
name: "Select Steward finances",
}),
).toBeInTheDocument();
});
});