import { describe, expect, it } from "vitest"; import { buildPendingUserInputAnswers, countAnsweredPendingUserInputQuestions, derivePendingUserInputProgress, findFirstUnansweredPendingUserInputQuestionIndex, resolvePendingUserInputAnswer, setPendingUserInputCustomAnswer,} from "./pendingUserInput"; describe("resolvePendingUserInputAnswer", () => { it("prefers a custom answer over a selected option", () => { expect( resolvePendingUserInputAnswer({ selectedOptionLabel: "Keep current envelope", customAnswer: "Keep the existing envelope for one release", }), ).toBe("Keep the existing envelope for one release"); }); it("falls back to the selected option", () => { expect( resolvePendingUserInputAnswer({ selectedOptionLabel: "Scaffold only", }), ).toBe("Scaffold only"); }); it("clears the preset selection when a custom answer is entered", () => { expect( setPendingUserInputCustomAnswer( { selectedOptionLabel: "Preserve existing tags", }, "doesn't matter", ), ).toEqual({ selectedOptionLabel: undefined, customAnswer: "doesn't matter", }); });}); describe("buildPendingUserInputAnswers", () => { it("returns a canonical answer map for complete prompts", () => { expect( buildPendingUserInputAnswers( [ { id: "scope", header: "Scope", question: "What should the plan target first?", options: [ { label: "Orchestration-first", description: "Focus on orchestration first", }, ], }, { id: "compat", header: "Compat", question: "How strict should compatibility be?", options: [ { label: "Keep current envelope", description: "Preserve current wire format", }, ], }, ], { scope: { selectedOptionLabel: "Orchestration-first", }, compat: { customAnswer: "Keep the current envelope for one release window", }, }, ), ).toEqual({ scope: "Orchestration-first", compat: "Keep the current envelope for one release window", }); }); it("returns null when any question is unanswered", () => { expect( buildPendingUserInputAnswers( [ { id: "scope", header: "Scope", question: "What should the plan target first?", options: [ { label: "Orchestration-first", description: "Focus on orchestration first", }, ], }, ], {}, ), ).toBeNull(); });}); describe("pending user input question progress", () => { const questions = [ { id: "scope", header: "Scope", question: "What should the plan target first?", options: [ { label: "Orchestration-first", description: "Focus on orchestration first", }, ], }, { id: "compat", header: "Compat", question: "How strict should compatibility be?", options: [ { label: "Keep current envelope", description: "Preserve current wire format", }, ], }, ] as const; it("counts only answered questions", () => { expect( countAnsweredPendingUserInputQuestions(questions, { scope: { selectedOptionLabel: "Orchestration-first", }, }), ).toBe(1); }); it("finds the first unanswered question", () => { expect( findFirstUnansweredPendingUserInputQuestionIndex(questions, { scope: { selectedOptionLabel: "Orchestration-first", }, }), ).toBe(1); }); it("returns the last question index when all answers are complete", () => { expect( findFirstUnansweredPendingUserInputQuestionIndex(questions, { scope: { selectedOptionLabel: "Orchestration-first", }, compat: { customAnswer: "Keep it for one release window", }, }), ).toBe(1); }); it("derives the active question and advancement state", () => { expect( derivePendingUserInputProgress( questions, { scope: { selectedOptionLabel: "Orchestration-first", }, }, 0, ), ).toMatchObject({ questionIndex: 0, activeQuestion: questions[0], selectedOptionLabel: "Orchestration-first", customAnswer: "", resolvedAnswer: "Orchestration-first", answeredQuestionCount: 1, isLastQuestion: false, isComplete: false, canAdvance: true, }); });});
import { describe, expect, it } from "vitest";
import {
buildPendingUserInputAnswers,
countAnsweredPendingUserInputQuestions,
derivePendingUserInputProgress,
findFirstUnansweredPendingUserInputQuestionIndex,
resolvePendingUserInputAnswer,
setPendingUserInputCustomAnswer,
} from "./pendingUserInput";
describe("resolvePendingUserInputAnswer", () => {
it("prefers a custom answer over a selected option", () => {
expect(
resolvePendingUserInputAnswer({
selectedOptionLabel: "Keep current envelope",
customAnswer: "Keep the existing envelope for one release",
}),
).toBe("Keep the existing envelope for one release");
});
it("falls back to the selected option", () => {
expect(
resolvePendingUserInputAnswer({
selectedOptionLabel: "Scaffold only",
}),
).toBe("Scaffold only");
});
it("clears the preset selection when a custom answer is entered", () => {
expect(
setPendingUserInputCustomAnswer(
{
selectedOptionLabel: "Preserve existing tags",
},
"doesn't matter",
),
).toEqual({
selectedOptionLabel: undefined,
customAnswer: "doesn't matter",
});
});
});
describe("buildPendingUserInputAnswers", () => {
it("returns a canonical answer map for complete prompts", () => {
expect(
buildPendingUserInputAnswers(
[
{
id: "scope",
header: "Scope",
question: "What should the plan target first?",
options: [
{
label: "Orchestration-first",
description: "Focus on orchestration first",
},
],
},
{
id: "compat",
header: "Compat",
question: "How strict should compatibility be?",
options: [
{
label: "Keep current envelope",
description: "Preserve current wire format",
},
],
},
],
{
scope: {
selectedOptionLabel: "Orchestration-first",
},
compat: {
customAnswer: "Keep the current envelope for one release window",
},
},
),
).toEqual({
scope: "Orchestration-first",
compat: "Keep the current envelope for one release window",
});
});
it("returns null when any question is unanswered", () => {
expect(
buildPendingUserInputAnswers(
[
{
id: "scope",
header: "Scope",
question: "What should the plan target first?",
options: [
{
label: "Orchestration-first",
description: "Focus on orchestration first",
},
],
},
],
{},
),
).toBeNull();
});
});
describe("pending user input question progress", () => {
const questions = [
{
id: "scope",
header: "Scope",
question: "What should the plan target first?",
options: [
{
label: "Orchestration-first",
description: "Focus on orchestration first",
},
],
},
{
id: "compat",
header: "Compat",
question: "How strict should compatibility be?",
options: [
{
label: "Keep current envelope",
description: "Preserve current wire format",
},
],
},
] as const;
it("counts only answered questions", () => {
expect(
countAnsweredPendingUserInputQuestions(questions, {
scope: {
selectedOptionLabel: "Orchestration-first",
},
}),
).toBe(1);
});
it("finds the first unanswered question", () => {
expect(
findFirstUnansweredPendingUserInputQuestionIndex(questions, {
scope: {
selectedOptionLabel: "Orchestration-first",
},
}),
).toBe(1);
});
it("returns the last question index when all answers are complete", () => {
expect(
findFirstUnansweredPendingUserInputQuestionIndex(questions, {
scope: {
selectedOptionLabel: "Orchestration-first",
},
compat: {
customAnswer: "Keep it for one release window",
},
}),
).toBe(1);
});
it("derives the active question and advancement state", () => {
expect(
derivePendingUserInputProgress(
questions,
{
scope: {
selectedOptionLabel: "Orchestration-first",
},
},
0,
),
).toMatchObject({
questionIndex: 0,
activeQuestion: questions[0],
selectedOptionLabel: "Orchestration-first",
customAnswer: "",
resolvedAnswer: "Orchestration-first",
answeredQuestionCount: 1,
isLastQuestion: false,
isComplete: false,
canAdvance: true,
});
});
});