import { describe, expect, it } from "vitest"; import { buildMethodCardWizardInitialValues, coreValueDetailsFromWizardFieldBlocks, facetDetailsToWizardFieldBlocks, overlayFacetPrefillValues, } from "../../lib/create/methodCardWizardPrefill"; import type { CustomMethodCardFieldBlock } from "../../lib/create/customMethodCardFieldBlocks"; const cardId = "signal"; const blocks: CustomMethodCardFieldBlock[] = [ { kind: "text", id: "b1", blockTitle: "Notes", placeholderText: "…" }, ]; const commHeadings = { corePrinciple: "Core Principle & Scope", logisticsAdmin: "Logistics, Admin & Norms", codeOfConduct: "Code of Conduct", }; describe("buildMethodCardWizardInitialValues", () => { it("uses fallback title and description when meta is missing", () => { expect( buildMethodCardWizardInitialValues({ cardId, fallbackTitle: "Signal", fallbackDescription: "Encrypted messaging.", meta: {}, persistedBlocks: {}, draftFieldBlocks: null, }), ).toEqual({ title: "Signal", description: "Encrypted messaging.", fieldBlocks: [], }); }); it("maps facet body fields onto wizard blocks when none exist yet", () => { expect( buildMethodCardWizardInitialValues({ cardId, fallbackTitle: "Signal", fallbackDescription: "Encrypted messaging.", meta: {}, persistedBlocks: {}, draftFieldBlocks: null, facetPrefill: { group: "communication", draft: { corePrinciple: "Privacy first.", logisticsAdmin: "Admins steward access.", codeOfConduct: "No leaks.", }, headings: commHeadings, }, }).fieldBlocks, ).toEqual([ { kind: "text", id: "facet-corePrinciple", blockTitle: "Core Principle & Scope", placeholderText: "Privacy first.", }, { kind: "text", id: "facet-logisticsAdmin", blockTitle: "Logistics, Admin & Norms", placeholderText: "Admins steward access.", }, { kind: "text", id: "facet-codeOfConduct", blockTitle: "Code of Conduct", placeholderText: "No leaks.", }, ]); }); it("prefers persisted meta title and description over fallbacks", () => { expect( buildMethodCardWizardInitialValues({ cardId, fallbackTitle: "Signal", fallbackDescription: "Encrypted messaging.", meta: { [cardId]: { label: "Our Signal", supportText: "Private ops." }, }, persistedBlocks: { [cardId]: blocks }, draftFieldBlocks: null, }), ).toEqual({ title: "Our Signal", description: "Private ops.", fieldBlocks: blocks, }); }); it("overlays current facet field values onto matching persisted blocks", () => { const persisted: CustomMethodCardFieldBlock[] = [ { kind: "text", id: "facet-corePrinciple", blockTitle: "Core Principle & Scope", placeholderText: "Stale principle", }, { kind: "text", id: "extra-notes", blockTitle: "Notes", placeholderText: "keep", }, ]; expect( buildMethodCardWizardInitialValues({ cardId, fallbackTitle: "Signal", fallbackDescription: "Encrypted messaging.", meta: {}, persistedBlocks: { [cardId]: persisted }, draftFieldBlocks: null, facetPrefill: { group: "communication", draft: { corePrinciple: "Updated principle", logisticsAdmin: "Updated logistics", codeOfConduct: "Updated coc", }, headings: commHeadings, }, }).fieldBlocks, ).toEqual([ { kind: "text", id: "facet-corePrinciple", blockTitle: "Core Principle & Scope", placeholderText: "Updated principle", }, { kind: "text", id: "extra-notes", blockTitle: "Notes", placeholderText: "keep", }, { kind: "text", id: "facet-logisticsAdmin", blockTitle: "Logistics, Admin & Norms", placeholderText: "Updated logistics", }, { kind: "text", id: "facet-codeOfConduct", blockTitle: "Code of Conduct", placeholderText: "Updated coc", }, ]); }); it("prefers in-modal field-block drafts over persisted blocks", () => { const draft: CustomMethodCardFieldBlock[] = [ { kind: "proportion", id: "p1", blockTitle: "Share", defaultPercent: 40 }, ]; expect( buildMethodCardWizardInitialValues({ cardId, fallbackTitle: "Signal", fallbackDescription: "Encrypted messaging.", meta: { [cardId]: { label: "Our Signal", supportText: "Private ops." }, }, persistedBlocks: { [cardId]: blocks }, draftFieldBlocks: draft, }).fieldBlocks, ).toEqual(draft); }); }); describe("facetDetailsToWizardFieldBlocks", () => { it("returns no blocks when facet copy is empty", () => { expect( facetDetailsToWizardFieldBlocks({ group: "communication", draft: { corePrinciple: " ", logisticsAdmin: "", codeOfConduct: "", }, headings: commHeadings, }), ).toEqual([]); }); it("maps decision scope chips to a badge block", () => { const blocksOut = facetDetailsToWizardFieldBlocks({ group: "decisionApproaches", draft: { corePrinciple: "Momentum.", applicableScope: ["Daily Operations"], selectedApplicableScope: [], stepByStepInstructions: "Post a deadline.", consensusLevel: 100, objectionsDeadlocks: "Any member can block.", }, headings: { corePrinciple: "Core Principle", applicableScope: "Applicable Scope", stepByStepInstructions: "Step-by-Step Instructions", consensusLevel: "Consensus Level", objectionsDeadlocks: "Objections & Deadlocks", }, }); expect(blocksOut.find((b) => b.kind === "badges")).toMatchObject({ kind: "badges", blockTitle: "Applicable Scope", options: ["Daily Operations"], }); expect(blocksOut.find((b) => b.kind === "proportion")).toMatchObject({ defaultPercent: 100, }); }); it("omits a consensus proportion when the draft has no consensus level", () => { const blocksOut = facetDetailsToWizardFieldBlocks({ group: "decisionApproaches", draft: { corePrinciple: "Momentum.", applicableScope: [], selectedApplicableScope: [], stepByStepInstructions: "Post a deadline.", objectionsDeadlocks: "", }, headings: { corePrinciple: "Core Principle", applicableScope: "Applicable Scope", stepByStepInstructions: "Step-by-Step Instructions", consensusLevel: "Consensus Level", objectionsDeadlocks: "Objections & Deadlocks", }, }); expect(blocksOut.find((b) => b.kind === "proportion")).toBeUndefined(); }); it("does not inject a seeded 75% proportion onto empty custom drafts", () => { const next = overlayFacetPrefillValues([], { group: "decisionApproaches", draft: { corePrinciple: "", applicableScope: [], selectedApplicableScope: [], stepByStepInstructions: "", consensusLevel: 75, objectionsDeadlocks: "", }, headings: { corePrinciple: "Core Principle", applicableScope: "Applicable Scope", stepByStepInstructions: "Step-by-Step Instructions", consensusLevel: "Consensus Level", objectionsDeadlocks: "Objections & Deadlocks", }, }); expect(next).toEqual([]); }); it("maps core value meaning and signals onto text blocks", () => { expect( facetDetailsToWizardFieldBlocks({ group: "coreValues", draft: { meaning: "Everyone can join.", signals: "Inaccessible venues.", }, headings: { meaning: "What does this value mean to your group?", signals: "Signals of Violation", }, }), ).toEqual([ { kind: "text", id: "facet-meaning", blockTitle: "What does this value mean to your group?", placeholderText: "Everyone can join.", }, { kind: "text", id: "facet-signals", blockTitle: "Signals of Violation", placeholderText: "Inaccessible venues.", }, ]); }); }); describe("coreValueDetailsFromWizardFieldBlocks", () => { it("reads meaning and signals from facet block ids", () => { expect( coreValueDetailsFromWizardFieldBlocks( [ { kind: "text", id: "facet-meaning", blockTitle: "Meaning", placeholderText: "Updated meaning", }, { kind: "text", id: "facet-signals", blockTitle: "Signals", placeholderText: "Updated signals", }, ], { meaning: "old m", signals: "old s", supportText: "keep" }, ), ).toEqual({ meaning: "Updated meaning", signals: "Updated signals", supportText: "keep", }); }); });