Template navigation and review/complete cleanup
This commit is contained in:
@@ -113,10 +113,10 @@ describe("Templates page (/templates)", () => {
|
||||
).toBe(
|
||||
JSON.stringify({ "1": { meaning: "stale", signals: "stale" } }),
|
||||
);
|
||||
// No `?fromFlow=1` on the outbound review-template URL — the marker
|
||||
// only disambiguates /templates' own click behavior.
|
||||
// In-flow picks also pass `?fromFlow=1` on the template review URL so
|
||||
// footer Back on `/create/review-template/…` returns to `/create/review`.
|
||||
expect(testRouter.push).toHaveBeenCalledWith(
|
||||
"/create/review-template/consensus",
|
||||
"/create/review-template/consensus?fromFlow=1",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { applyFinalReviewChipEditPatch } from "../../lib/create/applyFinalReviewChipEditPatch";
|
||||
import type { CreateFlowState } from "../../app/(app)/create/types";
|
||||
import type { FinalReviewChipEditPatch } from "../../app/(app)/create/components/FinalReviewChipEditModal";
|
||||
|
||||
describe("applyFinalReviewChipEditPatch", () => {
|
||||
it("creates the coreValueDetailsByChipId record when missing", () => {
|
||||
const patch: FinalReviewChipEditPatch = {
|
||||
groupKey: "coreValues",
|
||||
overrideKey: "accessibility",
|
||||
value: { meaning: "Be welcoming.", signals: "Captions on videos." },
|
||||
};
|
||||
|
||||
const result = applyFinalReviewChipEditPatch({}, patch);
|
||||
|
||||
expect(result).toEqual({
|
||||
coreValueDetailsByChipId: {
|
||||
accessibility: {
|
||||
meaning: "Be welcoming.",
|
||||
signals: "Captions on videos.",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("merges into the existing record without dropping siblings", () => {
|
||||
const state: CreateFlowState = {
|
||||
communicationMethodDetailsById: {
|
||||
signal: {
|
||||
corePrinciple: "Stay async-first.",
|
||||
logisticsAdmin: "Daily check-ins.",
|
||||
codeOfConduct: "Be kind.",
|
||||
},
|
||||
},
|
||||
};
|
||||
const patch: FinalReviewChipEditPatch = {
|
||||
groupKey: "communication",
|
||||
overrideKey: "in-person-meetings",
|
||||
value: {
|
||||
corePrinciple: "Meet weekly.",
|
||||
logisticsAdmin: "Hybrid format.",
|
||||
codeOfConduct: "Listen actively.",
|
||||
},
|
||||
};
|
||||
|
||||
const result = applyFinalReviewChipEditPatch(state, patch);
|
||||
|
||||
expect(result.communicationMethodDetailsById).toEqual({
|
||||
signal: {
|
||||
corePrinciple: "Stay async-first.",
|
||||
logisticsAdmin: "Daily check-ins.",
|
||||
codeOfConduct: "Be kind.",
|
||||
},
|
||||
"in-person-meetings": {
|
||||
corePrinciple: "Meet weekly.",
|
||||
logisticsAdmin: "Hybrid format.",
|
||||
codeOfConduct: "Listen actively.",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("overwrites the same key when the user re-saves it", () => {
|
||||
const state: CreateFlowState = {
|
||||
membershipMethodDetailsById: {
|
||||
"open-access": {
|
||||
eligibility: "Anyone",
|
||||
joiningProcess: "Sign up",
|
||||
expectations: "Old expectations",
|
||||
},
|
||||
},
|
||||
};
|
||||
const patch: FinalReviewChipEditPatch = {
|
||||
groupKey: "membership",
|
||||
overrideKey: "open-access",
|
||||
value: {
|
||||
eligibility: "Anyone over 18",
|
||||
joiningProcess: "Sign up + intro call",
|
||||
expectations: "New expectations",
|
||||
},
|
||||
};
|
||||
|
||||
const result = applyFinalReviewChipEditPatch(state, patch);
|
||||
|
||||
expect(result.membershipMethodDetailsById?.["open-access"]).toEqual({
|
||||
eligibility: "Anyone over 18",
|
||||
joiningProcess: "Sign up + intro call",
|
||||
expectations: "New expectations",
|
||||
});
|
||||
});
|
||||
|
||||
it("routes decisionApproaches to its dedicated state field", () => {
|
||||
const patch: FinalReviewChipEditPatch = {
|
||||
groupKey: "decisionApproaches",
|
||||
overrideKey: "lazy-consensus",
|
||||
value: {
|
||||
corePrinciple: "Silence implies assent.",
|
||||
applicableScope: ["budget"],
|
||||
selectedApplicableScope: ["budget"],
|
||||
stepByStepInstructions: "Propose. Wait 72h.",
|
||||
consensusLevel: 0.66,
|
||||
objectionsDeadlocks: "Escalate to vote.",
|
||||
},
|
||||
};
|
||||
|
||||
const result = applyFinalReviewChipEditPatch({}, patch);
|
||||
|
||||
expect(Object.keys(result)).toEqual(["decisionApproachDetailsById"]);
|
||||
});
|
||||
|
||||
it("routes conflictManagement to its dedicated state field", () => {
|
||||
const patch: FinalReviewChipEditPatch = {
|
||||
groupKey: "conflictManagement",
|
||||
overrideKey: "peer-mediation",
|
||||
value: {
|
||||
corePrinciple: "Restore trust.",
|
||||
applicableScope: ["interpersonal"],
|
||||
selectedApplicableScope: ["interpersonal"],
|
||||
processProtocol: "Pair the parties with a neutral facilitator.",
|
||||
restorationFallbacks: "Council escalation.",
|
||||
},
|
||||
};
|
||||
|
||||
const result = applyFinalReviewChipEditPatch({}, patch);
|
||||
|
||||
expect(Object.keys(result)).toEqual(["conflictManagementDetailsById"]);
|
||||
});
|
||||
});
|
||||
@@ -112,6 +112,20 @@ describe("createFlowStateSchema", () => {
|
||||
expect(r.success).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts templateReviewBackSlug", () => {
|
||||
const r = createFlowStateSchema.safeParse({
|
||||
templateReviewBackSlug: "mutual-aid-mondays",
|
||||
});
|
||||
expect(r.success).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts templateReviewEntryFromCreateFlow", () => {
|
||||
const r = createFlowStateSchema.safeParse({
|
||||
templateReviewEntryFromCreateFlow: true,
|
||||
});
|
||||
expect(r.success).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects core value detail strings that are too long", () => {
|
||||
const r = createFlowStateSchema.safeParse({
|
||||
coreValueDetailsByChipId: {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
FLOW_STEP_ORDER,
|
||||
buildTemplateReviewHref,
|
||||
getNextStep,
|
||||
getPreviousStep,
|
||||
isValidStep,
|
||||
getStepIndex,
|
||||
resolveCreateFlowBackTarget,
|
||||
} from "../../app/(app)/create/utils/flowSteps";
|
||||
|
||||
describe("flowSteps", () => {
|
||||
@@ -72,4 +74,33 @@ describe("flowSteps", () => {
|
||||
expect(getNextStep("review", opts)).toBe("core-values");
|
||||
expect(getPreviousStep("communication-methods", opts)).toBe("core-values");
|
||||
});
|
||||
|
||||
it("resolveCreateFlowBackTarget returns template review when use-without slug is set on confirm-stakeholders", () => {
|
||||
expect(
|
||||
resolveCreateFlowBackTarget(
|
||||
"confirm-stakeholders",
|
||||
undefined,
|
||||
"mutual-aid-mondays",
|
||||
),
|
||||
).toEqual({ kind: "templateReview", slug: "mutual-aid-mondays" });
|
||||
});
|
||||
|
||||
it("resolveCreateFlowBackTarget falls back to linear previous when slug is absent", () => {
|
||||
expect(
|
||||
resolveCreateFlowBackTarget("confirm-stakeholders", undefined, undefined),
|
||||
).toEqual({ kind: "step", step: "conflict-management" });
|
||||
});
|
||||
|
||||
it("resolveCreateFlowBackTarget ignores whitespace-only slug", () => {
|
||||
expect(
|
||||
resolveCreateFlowBackTarget("confirm-stakeholders", undefined, " "),
|
||||
).toEqual({ kind: "step", step: "conflict-management" });
|
||||
});
|
||||
|
||||
it("buildTemplateReviewHref encodes slug and optional fromFlow query", () => {
|
||||
expect(buildTemplateReviewHref("a/b")).toBe("/create/review-template/a%2Fb");
|
||||
expect(buildTemplateReviewHref("mutual-aid", { fromCreateWizard: true })).toBe(
|
||||
"/create/review-template/mutual-aid?fromFlow=1",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user