import assert from "node:assert/strict"; import test from "node:test"; import { renderResultText } from "../src/result.ts"; import { createInitialState } from "../src/state/create.ts"; import { summarizeResult, toAskResult } from "../src/state/result.ts"; import { applyNumberShortcut, enterOptionNoteMode, enterQuestionNoteMode, moveTab, saveNote, submitCustomAnswer, } from "../src/state/transitions.ts"; test("summarizeResult formats selected answers", () => { let state = createInitialState({ questions: [ { id: "style", label: "Style", prompt: "How should I frame it?", options: [ { value: "minimal", label: "Minimal" }, { value: "rich", label: "Rich" }, ], }, ], }); state = applyNumberShortcut(state, 2); const result = toAskResult(state); assert.equal(summarizeResult(result), "Style: Rich"); assert.equal(renderResultText(result), "✓ Style: Rich"); }); test("summaries include custom text answers", () => { let state = createInitialState({ questions: [ { id: "notes", label: "Notes", prompt: "Anything else?", options: [{ value: "none", label: "None" }], }, ], }); state = applyNumberShortcut(state, 2); state = submitCustomAnswer(state, "Please include examples"); const result = toAskResult(state); assert.equal(summarizeResult(result), "Notes: Please include examples"); assert.equal( renderResultText(result), "✓ Notes: (wrote) Please include examples" ); }); test("multi-select summaries include selected options and custom text", () => { let state = createInitialState({ questions: [ { id: "frameworks", label: "Frameworks", prompt: "Which frameworks?", type: "multi", options: [ { value: "react", label: "React" }, { value: "vue", label: "Vue" }, ], }, ], }); state = applyNumberShortcut(state, 1); state = applyNumberShortcut(state, 3); state = submitCustomAnswer(state, "SolidStart"); const result = toAskResult(state); assert.deepEqual(result.answers.frameworks, { values: ["react", "SolidStart"], labels: ["React", "SolidStart"], indices: [1], customText: "SolidStart", note: undefined, optionNotes: undefined, }); assert.equal(summarizeResult(result), "Frameworks: React, SolidStart"); assert.equal(renderResultText(result), "✓ Frameworks: React, SolidStart"); }); test("summaries and result text include question and selected option notes", () => { let state = createInitialState({ questions: [ { id: "framework", label: "Framework", prompt: "Pick a framework", options: [ { value: "react", label: "React" }, { value: "vue", label: "Vue" }, ], }, ], }); state = enterQuestionNoteMode(state, "framework"); state = saveNote(state, "Need SSR support"); state = enterOptionNoteMode(state, "framework", "vue"); state = saveNote(state, "Team has prior experience"); state = applyNumberShortcut(state, 2); const result = toAskResult(state); assert.equal( summarizeResult(result), [ "Framework: Vue", "Framework note: Need SSR support", "Framework / Vue note: Team has prior experience", ].join("\n") ); assert.equal( renderResultText(result), [ "✓ Framework: Vue", " note: Need SSR support", " Vue note: Team has prior experience", ].join("\n") ); }); test("unselected option notes are omitted from the submitted result", () => { let state = createInitialState({ questions: [ { id: "framework", label: "Framework", prompt: "Pick a framework", options: [ { value: "react", label: "React" }, { value: "vue", label: "Vue" }, ], }, ], }); state = enterOptionNoteMode(state, "framework", "react"); state = saveNote(state, "Only for legacy projects"); state = applyNumberShortcut(state, 2); const result = toAskResult(state); assert.equal(result.answers.framework.optionNotes, undefined); assert.equal(renderResultText(result), "✓ Framework: Vue"); }); test("question-note-only results still render note content", () => { const result = { title: "Interview", cancelled: false, mode: "submit" as const, questions: [ { id: "scope", label: "Scope", prompt: "What should we focus on?", type: "single" as const, }, ], answers: { scope: { values: [], labels: [], indices: [], note: "Prefer incremental changes", }, }, }; assert.equal(renderResultText(result), " note: Prefer incremental changes"); }); test("elaborate results include unselected notes in summary and render output", () => { let state = createInitialState({ questions: [ { id: "framework", label: "Framework", prompt: "Pick a framework", options: [ { value: "react", label: "React" }, { value: "vue", label: "Vue" }, ], }, ], }); state = enterQuestionNoteMode(state, "framework"); state = saveNote(state, "Need SSR support"); state = enterOptionNoteMode(state, "framework", "react"); state = saveNote(state, "Legacy codebase"); state = { ...state, mode: "elaborate" }; const result = toAskResult(state); assert.equal(result.mode, "elaborate"); assert.deepEqual(result.answers, {}); assert.deepEqual(result.continuation, { affectedQuestionIds: ["framework"], preservedAnswers: {}, questionStates: { framework: { status: "needs_clarification" }, }, strategy: "refine_only", }); assert.deepEqual(result.elaboration, { instruction: "First answer the user's noted clarification directly and concisely using the provided question and option context. Do not treat these notes as final answers. Then re-ask only the affected questions if a choice is still needed afterward. Do not jump straight to a follow-up question unless the note is already resolved.", nextAction: "clarify_then_reask", items: [ { target: { kind: "question" }, question: { id: "framework", label: "Framework", prompt: "Pick a framework", type: "single", options: [ { value: "react", label: "React" }, { value: "vue", label: "Vue" }, ], }, answered: false, answer: undefined, note: "Need SSR support", }, { target: { kind: "option", optionValue: "react", }, question: { id: "framework", label: "Framework", prompt: "Pick a framework", type: "single", options: [ { value: "react", label: "React" }, { value: "vue", label: "Vue" }, ], }, option: { value: "react", label: "React" }, selected: false, answered: false, answer: undefined, note: "Legacy codebase", }, ], }); assert.equal( summarizeResult(result), [ 'User asked to elaborate on question "Pick a framework" with note "Need SSR support"', 'User asked to elaborate on question "Pick a framework" option "React" with note "Legacy codebase"', ].join("\n") ); assert.equal( renderResultText(result), [ 'User asked to elaborate on question "Pick a framework" with note "Need SSR support"', 'User asked to elaborate on question "Pick a framework" option "React" with note "Legacy codebase"', ].join("\n") ); }); test("elaborate results keep committed answers alongside elaboration context", () => { let state = createInitialState({ questions: [ { id: "framework", label: "Framework", prompt: "Pick a framework", options: [ { value: "react", label: "React" }, { value: "vue", label: "Vue" }, ], }, ], }); state = applyNumberShortcut(state, 2); state = enterQuestionNoteMode(state, "framework"); state = saveNote(state, "Compare tradeoffs"); state = { ...state, mode: "elaborate" }; const result = toAskResult(state); assert.deepEqual(result.continuation, { affectedQuestionIds: ["framework"], preservedAnswers: {}, questionStates: { framework: { status: "needs_clarification" }, }, strategy: "refine_only", }); assert.deepEqual(result.answers, { framework: { values: ["vue"], labels: ["Vue"], indices: [2], customText: undefined, note: "Compare tradeoffs", optionNotes: undefined, }, }); assert.deepEqual(result.elaboration?.items[0], { target: { kind: "question" }, question: { id: "framework", label: "Framework", prompt: "Pick a framework", type: "single", options: [ { value: "react", label: "React" }, { value: "vue", label: "Vue" }, ], }, answered: true, answer: { values: ["vue"], labels: ["Vue"], indices: [2], customText: undefined, note: "Compare tradeoffs", optionNotes: undefined, }, note: "Compare tradeoffs", }); assert.equal( summarizeResult(result), 'User asked to elaborate on question "Pick a framework" after current answer "Vue" with note "Compare tradeoffs"' ); assert.equal( renderResultText(result), 'User asked to elaborate on question "Pick a framework" after current answer "Vue" with note "Compare tradeoffs"' ); }); test("elaboration summary includes custom answers in note context", () => { let state = createInitialState({ questions: [ { id: "framework", label: "Framework", prompt: "Pick a framework", options: [ { value: "react", label: "React" }, { value: "vue", label: "Vue" }, ], }, ], }); state = applyNumberShortcut(state, 3); state = submitCustomAnswer(state, "SolidStart"); state = enterQuestionNoteMode(state, "framework"); state = saveNote(state, "Compare tradeoffs"); state = { ...state, mode: "elaborate" }; const result = toAskResult(state); assert.equal( summarizeResult(result), 'User asked to elaborate on question "Pick a framework" after current answer "SolidStart" with note "Compare tradeoffs"' ); assert.equal( renderResultText(result), 'User asked to elaborate on question "Pick a framework" after current answer "SolidStart" with note "Compare tradeoffs"' ); assert.deepEqual(result.elaboration?.items[0]?.answer, { values: ["SolidStart"], labels: ["SolidStart"], indices: [], customText: "SolidStart", note: "Compare tradeoffs", optionNotes: undefined, }); }); test("elaboration summary includes answered custom text even without notes", () => { let state = createInitialState({ questions: [ { id: "demo_goal", label: "Goal", prompt: "What kind of ask_user showcase would you like?", options: [ { value: "simple-demo", label: "Simple demo" }, { value: "code-example", label: "Code example" }, ], }, ], }); state = applyNumberShortcut(state, 3); state = submitCustomAnswer(state, "teeeel meee whyyyyy"); state = { ...state, mode: "elaborate" }; const result = toAskResult(state); assert.equal( summarizeResult(result), 'User asked to elaborate on question "What kind of ask_user showcase would you like?" after current answer "teeeel meee whyyyyy"' ); assert.equal( renderResultText(result), 'User asked to elaborate on question "What kind of ask_user showcase would you like?" after current answer "teeeel meee whyyyyy"' ); assert.deepEqual(result.elaboration?.items, []); }); test("elaborate continuation preserves unrelated committed answers", () => { let state = createInitialState({ questions: [ { id: "goal", label: "Goal", prompt: "Pick goal", options: [ { value: "fast", label: "Fast" }, { value: "safe", label: "Safe" }, ], }, { id: "constraints", label: "Constraints", prompt: "Pick constraints", type: "multi", options: [ { value: "small", label: "Small diff" }, { value: "tests", label: "Add tests" }, ], }, ], }); state = enterQuestionNoteMode(state, "goal"); state = saveNote(state, "Explain tradeoff"); state = moveTab(state, 1); state = applyNumberShortcut(state, 1); state = applyNumberShortcut(state, 2); state = { ...state, mode: "elaborate" }; const result = toAskResult(state); assert.deepEqual(result.continuation, { affectedQuestionIds: ["goal"], preservedAnswers: { constraints: { values: ["small", "tests"], labels: ["Small diff", "Add tests"], indices: [1, 2], customText: undefined, note: undefined, optionNotes: undefined, }, }, questionStates: { goal: { status: "needs_clarification" }, constraints: { status: "answered" }, }, strategy: "refine_only", }); }); test("invalid input result renders as invalid input", () => { const result = { title: "Interview", cancelled: true, mode: "submit" as const, questions: [], answers: {}, error: { kind: "invalid_input" as const, issues: [], }, }; assert.equal(renderResultText(result), "Invalid input"); }); test("cancelled result renders as cancelled", () => { const result = { title: "Interview", cancelled: true, mode: "submit" as const, questions: [], answers: {}, }; assert.equal(renderResultText(result), "Cancelled"); });