Ask the six optional setup questions after size so each tick can change suggested cards, keeping the answers on the draft and off the published Rule.
This commit is contained in:
1 parent
b4bfa157f8
commit
bdaf1818d9
23 files changed
+399
-82
No files matched your search
@@ -0,0 +1,90 @@
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import { useLayoutEffect } from "react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { SetupQuestionsScreen } from "../../app/(app)/create/screens/select/SetupQuestionsScreen";
|
||||
import { useCreateFlow } from "../../app/(app)/create/context/CreateFlowContext";
|
||||
import type { CreateFlowState } from "../../app/(app)/create/types";
|
||||
import { TRAIT_KEYS } from "../../lib/create/deckTypes";
|
||||
import messages from "../../messages/en/index";
|
||||
import {
|
||||
cleanup,
|
||||
renderWithProviders as render,
|
||||
screen,
|
||||
} from "../utils/test-utils";
|
||||
|
||||
const copy = messages.create.community.setupQuestions;
|
||||
|
||||
function ScreenWithState({ initial }: { initial: CreateFlowState }) {
|
||||
const { replaceState } = useCreateFlow();
|
||||
useLayoutEffect(() => {
|
||||
replaceState(initial);
|
||||
}, [replaceState, initial]);
|
||||
return <SetupQuestionsScreen />;
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
describe("Create flow setup-questions page", () => {
|
||||
it("renders the six questions off by default with a help line under each", () => {
|
||||
render(<SetupQuestionsScreen />);
|
||||
|
||||
expect(
|
||||
screen.getByRole("heading", { name: copy.title }),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText(copy.description)).toBeInTheDocument();
|
||||
|
||||
for (const question of copy.questions) {
|
||||
const checkbox = screen.getByRole("checkbox", { name: question.label });
|
||||
expect(checkbox).toHaveAttribute("aria-checked", "false");
|
||||
expect(screen.getByText(question.help)).toBeInTheDocument();
|
||||
}
|
||||
expect(copy.questions).toHaveLength(6);
|
||||
expect(copy.questions.map((question) => question.id)).toEqual([
|
||||
...TRAIT_KEYS,
|
||||
]);
|
||||
});
|
||||
|
||||
it("ticks a question from the box or the help line", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<SetupQuestionsScreen />);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("checkbox", { name: copy.questions[2].label }),
|
||||
);
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: copy.questions[2].label }),
|
||||
).toHaveAttribute("aria-checked", "true");
|
||||
|
||||
await user.click(screen.getByText(copy.questions[0].help));
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: copy.questions[0].label }),
|
||||
).toHaveAttribute("aria-checked", "true");
|
||||
});
|
||||
|
||||
it("restores ticked answers from the draft", () => {
|
||||
render(
|
||||
<ScreenWithState
|
||||
initial={{ setupTraits: { employer: true, sponsor: false } }}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: copy.questions[2].label }),
|
||||
).toHaveAttribute("aria-checked", "true");
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: copy.questions[3].label }),
|
||||
).toHaveAttribute("aria-checked", "false");
|
||||
});
|
||||
|
||||
it("does not ask about police, immigration enforcement, or protest", () => {
|
||||
const blob = `${copy.title} ${copy.description} ${copy.questions
|
||||
.map((question) => `${question.label} ${question.help}`)
|
||||
.join(" ")}`.toLowerCase();
|
||||
expect(blob).not.toMatch(/police/);
|
||||
expect(blob).not.toMatch(/immigration/);
|
||||
expect(blob).not.toMatch(/protest/);
|
||||
});
|
||||
});
|
||||
@@ -22,6 +22,17 @@ describe("buildPublishPayload", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("omits setup answers from the published Rule", () => {
|
||||
const r = buildPublishPayload({
|
||||
title: "Oak Park Commons",
|
||||
setupTraits: { network: true, employer: true, risky: false },
|
||||
});
|
||||
expect(r.ok).toBe(true);
|
||||
if (!r.ok) return;
|
||||
expect(r.document).not.toHaveProperty("setupTraits");
|
||||
expect(JSON.stringify(r)).not.toContain("setupTraits");
|
||||
});
|
||||
|
||||
it("returns title and fallback Overview section when no sections", () => {
|
||||
const r = buildPublishPayload({ title: "Oak Park Commons" });
|
||||
expect(r.ok).toBe(true);
|
||||
|
||||
@@ -58,6 +58,9 @@ describe("createFlowLayoutTokens", () => {
|
||||
expect(getCreateFlowContentMaxClass({ step: "community-structure" })).toBe(
|
||||
CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS,
|
||||
);
|
||||
expect(getCreateFlowContentMaxClass({ step: "setup-questions" })).toBe(
|
||||
CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS,
|
||||
);
|
||||
expect(
|
||||
getCreateFlowContentMaxClass({ step: "communication-methods" }),
|
||||
).toBe(CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS);
|
||||
|
||||
@@ -17,7 +17,10 @@ describe("getProportionBarProgressForCreateFlowStep", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("maps the rest of Create Community onto part 1, skipping setup at 1-5", () => {
|
||||
it("maps the rest of Create Community onto part 1, with setup questions at 1-5", () => {
|
||||
expect(getProportionBarProgressForCreateFlowStep("setup-questions")).toBe(
|
||||
"1-5",
|
||||
);
|
||||
expect(getProportionBarProgressForCreateFlowStep("community-upload")).toBe(
|
||||
"1-6",
|
||||
);
|
||||
|
||||
@@ -69,6 +69,28 @@ describe("createFlowStateSchema", () => {
|
||||
expect(r.success).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts setup-questions as currentStep and true or false setupTraits", () => {
|
||||
const r = createFlowStateSchema.safeParse({
|
||||
currentStep: "setup-questions",
|
||||
setupTraits: {
|
||||
network: true,
|
||||
nonvoters: false,
|
||||
employer: true,
|
||||
sponsor: false,
|
||||
records: false,
|
||||
risky: true,
|
||||
},
|
||||
});
|
||||
expect(r.success).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects non-boolean setupTraits values", () => {
|
||||
const r = createFlowStateSchema.safeParse({
|
||||
setupTraits: { network: "yes" },
|
||||
});
|
||||
expect(r.success).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects title that is too long", () => {
|
||||
const r = createFlowStateSchema.safeParse({ title: "x".repeat(600) });
|
||||
expect(r.success).toBe(false);
|
||||
|
||||
@@ -26,6 +26,11 @@ describe("create footer messages", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("exposes Continue and Skip for the setup questions step", () => {
|
||||
expect(messages.create.footer.continue).toBe("Continue");
|
||||
expect(messages.create.footer.skip).toBe("Skip");
|
||||
});
|
||||
|
||||
it("exposes Skip and Skip for now for optional community steps", () => {
|
||||
expect(messages.create.footer.skip).toBe("Skip");
|
||||
expect(messages.create.footer.skipForNow).toBe("Skip for now");
|
||||
|
||||
@@ -69,6 +69,14 @@ describe("flowSteps", () => {
|
||||
expect(getNextStep("community-context")).toBe("community-size");
|
||||
});
|
||||
|
||||
it("places setup-questions after community-size and before the logo upload", () => {
|
||||
expect(getStepIndex("setup-questions")).toBe(5);
|
||||
expect(getNextStep("community-size")).toBe("setup-questions");
|
||||
expect(getNextStep("setup-questions")).toBe("community-upload");
|
||||
expect(getPreviousStep("community-upload")).toBe("setup-questions");
|
||||
expect(getPreviousStep("setup-questions")).toBe("community-size");
|
||||
});
|
||||
|
||||
it("skipCommunitySave bridges upload → review and review → upload", () => {
|
||||
const opts = { skipCommunitySave: true } as const;
|
||||
expect(getNextStep("community-upload", opts)).toBe("review");
|
||||
@@ -77,7 +85,8 @@ describe("flowSteps", () => {
|
||||
|
||||
it("skipCommunitySave does not change steps outside the save segment", () => {
|
||||
const opts = { skipCommunitySave: true } as const;
|
||||
expect(getNextStep("community-size", opts)).toBe("community-upload");
|
||||
expect(getNextStep("community-size", opts)).toBe("setup-questions");
|
||||
expect(getNextStep("setup-questions", opts)).toBe("community-upload");
|
||||
expect(getNextStep("review", opts)).toBe("core-values");
|
||||
expect(getPreviousStep("communication-methods", opts)).toBe("core-values");
|
||||
});
|
||||
@@ -86,6 +95,8 @@ describe("flowSteps", () => {
|
||||
const opts = { useWithoutChangesIdentityOnly: true } as const;
|
||||
expect(getNextStep("community-name", opts)).toBe("community-context");
|
||||
expect(getNextStep("community-context", opts)).toBe("community-upload");
|
||||
expect(getNextStep("community-size", opts)).toBe("community-upload");
|
||||
expect(getNextStep("setup-questions", opts)).toBe("community-upload");
|
||||
expect(getNextStep("community-upload", opts)).toBe("confirm-stakeholders");
|
||||
expect(getNextStep("confirm-stakeholders", opts)).toBe("final-review");
|
||||
expect(getPreviousStep("community-context", opts)).toBe("community-name");
|
||||
@@ -199,6 +210,7 @@ describe("flowSteps", () => {
|
||||
expect(shouldOfferCreateFlowSaveAndExit("informational")).toBe(false);
|
||||
expect(shouldOfferCreateFlowSaveAndExit("community-name")).toBe(false);
|
||||
expect(shouldOfferCreateFlowSaveAndExit("community-structure")).toBe(false);
|
||||
expect(shouldOfferCreateFlowSaveAndExit("setup-questions")).toBe(false);
|
||||
expect(shouldOfferCreateFlowSaveAndExit("community-upload")).toBe(false);
|
||||
expect(shouldOfferCreateFlowSaveAndExit("community-save")).toBe(true);
|
||||
expect(shouldOfferCreateFlowSaveAndExit("review")).toBe(true);
|
||||
@@ -244,6 +256,7 @@ describe("flowSteps", () => {
|
||||
expect(createFlowStepUsesSelectSplitScroll("community-structure")).toBe(
|
||||
true,
|
||||
);
|
||||
expect(createFlowStepUsesSelectSplitScroll("setup-questions")).toBe(true);
|
||||
expect(createFlowStepUsesSelectSplitScroll("communication-methods")).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
@@ -19,6 +19,13 @@ describe("maturity and chip aliases", () => {
|
||||
expect(answers.mainKind).toBe("workersCoop");
|
||||
});
|
||||
|
||||
it("passes ticked setup answers through as traits", () => {
|
||||
const answers = groupAnswersFromCreateFlowState({
|
||||
setupTraits: { employer: true, records: false },
|
||||
});
|
||||
expect(answers.traits).toEqual({ employer: true, records: false });
|
||||
});
|
||||
|
||||
it("loads Enterprise and Larger organization as Established", () => {
|
||||
const fromId = migrateLegacyCreateFlowState({
|
||||
selectedMaturityIds: ["4"],
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { TRAIT_KEYS } from "../../lib/create/deckTypes";
|
||||
import {
|
||||
EMPTY_SETUP_TRAITS,
|
||||
hasTickedSetupTrait,
|
||||
setupTraitsFromTickedIds,
|
||||
tickedSetupTraitIds,
|
||||
} from "../../lib/create/setupTraits";
|
||||
|
||||
describe("setupTraits", () => {
|
||||
it("starts with every question off", () => {
|
||||
expect(TRAIT_KEYS.every((key) => EMPTY_SETUP_TRAITS[key] === false)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(hasTickedSetupTrait(undefined)).toBe(false);
|
||||
expect(hasTickedSetupTrait(EMPTY_SETUP_TRAITS)).toBe(false);
|
||||
expect(tickedSetupTraitIds(undefined)).toEqual([]);
|
||||
});
|
||||
|
||||
it("round-trips ticked ids as true or false on every trait", () => {
|
||||
const traits = setupTraitsFromTickedIds(["employer", "records"]);
|
||||
expect(traits).toEqual({
|
||||
network: false,
|
||||
nonvoters: false,
|
||||
employer: true,
|
||||
sponsor: false,
|
||||
records: true,
|
||||
risky: false,
|
||||
});
|
||||
expect(tickedSetupTraitIds(traits)).toEqual(["employer", "records"]);
|
||||
expect(hasTickedSetupTrait(traits)).toBe(true);
|
||||
});
|
||||
|
||||
it("ignores ids that are not setup questions", () => {
|
||||
expect(setupTraitsFromTickedIds(["employer", "police"])).toEqual({
|
||||
...EMPTY_SETUP_TRAITS,
|
||||
employer: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user