import { describe, expect, it } from "vite-plus/test"; import { buildPendingUserInputAnswers, countAnsweredPendingUserInputQuestions, derivePendingUserInputProgress, findFirstUnansweredPendingUserInputQuestionIndex, resolvePendingUserInputAnswer, setPendingUserInputCustomAnswer, togglePendingUserInputOptionSelection,} from "./pendingUserInput"; const singleSelectQuestion = { id: "scope", header: "Scope", question: "What should the plan target first?", options: [ { label: "Orchestration-first", description: "Focus on orchestration first", }, ], multiSelect: false,} as const; const multiSelectQuestion = { id: "areas", header: "Areas", question: "Which areas should this change cover?", options: [ { label: "Server", description: "Server", }, { label: "Web", description: "Web", }, ], multiSelect: true,} as const; describe("resolvePendingUserInputAnswer", () => { it("prefers a custom answer over selected options", () => { expect( resolvePendingUserInputAnswer(singleSelectQuestion, { selectedOptionLabels: ["Orchestration-first"], customAnswer: "Keep the existing envelope for one release", }), ).toBe("Keep the existing envelope for one release"); }); it("falls back to the selected option for single-select questions", () => { expect( resolvePendingUserInputAnswer(singleSelectQuestion, { selectedOptionLabels: ["Orchestration-first"], }), ).toBe("Orchestration-first"); }); it("returns all selected labels for multi-select questions", () => { expect( resolvePendingUserInputAnswer(multiSelectQuestion, { selectedOptionLabels: ["Server", "Web"], }), ).toEqual(["Server", "Web"]); }); it("clears the preset selection when a custom answer is entered", () => { expect( setPendingUserInputCustomAnswer( { selectedOptionLabels: ["Server", "Web"], }, "doesn't matter", ), ).toEqual({ customAnswer: "doesn't matter", }); });}); describe("togglePendingUserInputOptionSelection", () => { it("toggles options for multi-select questions", () => { expect(togglePendingUserInputOptionSelection(multiSelectQuestion, undefined, "Server")).toEqual( { customAnswer: "", selectedOptionLabels: ["Server"], }, ); expect( togglePendingUserInputOptionSelection( multiSelectQuestion, { selectedOptionLabels: ["Server", "Web"], }, "Server", ), ).toEqual({ customAnswer: "", selectedOptionLabels: ["Web"], }); });}); describe("buildPendingUserInputAnswers", () => { it("returns a canonical answer map for complete prompts", () => { expect( buildPendingUserInputAnswers( [ singleSelectQuestion, { id: "compat", header: "Compat", question: "How strict should compatibility be?", options: [ { label: "Keep current envelope", description: "Preserve current wire format", }, ], multiSelect: false, }, ], { scope: { selectedOptionLabels: ["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 arrays for answered multi-select prompts", () => { expect( buildPendingUserInputAnswers([multiSelectQuestion], { areas: { selectedOptionLabels: ["Server", "Web"], }, }), ).toEqual({ areas: ["Server", "Web"], }); }); it("returns null when any question is unanswered", () => { expect(buildPendingUserInputAnswers([singleSelectQuestion], {})).toBeNull(); });}); describe("pending user input question progress", () => { const questions = [ singleSelectQuestion, { id: "compat", header: "Compat", question: "How strict should compatibility be?", options: [ { label: "Keep current envelope", description: "Preserve current wire format", }, ], multiSelect: false, }, ] as const; it("counts only answered questions", () => { expect( countAnsweredPendingUserInputQuestions(questions, { scope: { selectedOptionLabels: ["Orchestration-first"], }, }), ).toBe(1); }); it("finds the first unanswered question", () => { expect( findFirstUnansweredPendingUserInputQuestionIndex(questions, { scope: { selectedOptionLabels: ["Orchestration-first"], }, }), ).toBe(1); }); it("returns the last question index when all answers are complete", () => { expect( findFirstUnansweredPendingUserInputQuestionIndex(questions, { scope: { selectedOptionLabels: ["Orchestration-first"], }, compat: { customAnswer: "Keep it for one release window", }, }), ).toBe(1); }); it("derives the active question and advancement state", () => { expect( derivePendingUserInputProgress( questions, { scope: { selectedOptionLabels: ["Orchestration-first"], }, }, 0, ), ).toMatchObject({ questionIndex: 0, activeQuestion: questions[0], selectedOptionLabels: ["Orchestration-first"], customAnswer: "", resolvedAnswer: "Orchestration-first", answeredQuestionCount: 1, isLastQuestion: false, isComplete: false, canAdvance: true, }); }); it("treats multi-select questions as answered when they have selected options", () => { expect( derivePendingUserInputProgress( [multiSelectQuestion], { areas: { selectedOptionLabels: ["Server", "Web"], }, }, 0, ), ).toMatchObject({ selectedOptionLabels: ["Server", "Web"], resolvedAnswer: ["Server", "Web"], canAdvance: true, isComplete: true, }); });});
import { describe, expect, it } from "vite-plus/test";
import {
buildPendingUserInputAnswers,
countAnsweredPendingUserInputQuestions,
derivePendingUserInputProgress,
findFirstUnansweredPendingUserInputQuestionIndex,
resolvePendingUserInputAnswer,
setPendingUserInputCustomAnswer,
togglePendingUserInputOptionSelection,
} from "./pendingUserInput";
const singleSelectQuestion = {
id: "scope",
header: "Scope",
question: "What should the plan target first?",
options: [
{
label: "Orchestration-first",
description: "Focus on orchestration first",
},
],
multiSelect: false,
} as const;
const multiSelectQuestion = {
id: "areas",
header: "Areas",
question: "Which areas should this change cover?",
options: [
{
label: "Server",
description: "Server",
},
{
label: "Web",
description: "Web",
},
],
multiSelect: true,
} as const;
describe("resolvePendingUserInputAnswer", () => {
it("prefers a custom answer over selected options", () => {
expect(
resolvePendingUserInputAnswer(singleSelectQuestion, {
selectedOptionLabels: ["Orchestration-first"],
customAnswer: "Keep the existing envelope for one release",
}),
).toBe("Keep the existing envelope for one release");
});
it("falls back to the selected option for single-select questions", () => {
expect(
resolvePendingUserInputAnswer(singleSelectQuestion, {
selectedOptionLabels: ["Orchestration-first"],
}),
).toBe("Orchestration-first");
});
it("returns all selected labels for multi-select questions", () => {
expect(
resolvePendingUserInputAnswer(multiSelectQuestion, {
selectedOptionLabels: ["Server", "Web"],
}),
).toEqual(["Server", "Web"]);
});
it("clears the preset selection when a custom answer is entered", () => {
expect(
setPendingUserInputCustomAnswer(
{
selectedOptionLabels: ["Server", "Web"],
},
"doesn't matter",
),
).toEqual({
customAnswer: "doesn't matter",
});
});
});
describe("togglePendingUserInputOptionSelection", () => {
it("toggles options for multi-select questions", () => {
expect(togglePendingUserInputOptionSelection(multiSelectQuestion, undefined, "Server")).toEqual(
{
customAnswer: "",
selectedOptionLabels: ["Server"],
},
);
expect(
togglePendingUserInputOptionSelection(
multiSelectQuestion,
{
selectedOptionLabels: ["Server", "Web"],
},
"Server",
),
).toEqual({
customAnswer: "",
selectedOptionLabels: ["Web"],
});
});
});
describe("buildPendingUserInputAnswers", () => {
it("returns a canonical answer map for complete prompts", () => {
expect(
buildPendingUserInputAnswers(
[
singleSelectQuestion,
{
id: "compat",
header: "Compat",
question: "How strict should compatibility be?",
options: [
{
label: "Keep current envelope",
description: "Preserve current wire format",
},
],
multiSelect: false,
},
],
{
scope: {
selectedOptionLabels: ["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 arrays for answered multi-select prompts", () => {
expect(
buildPendingUserInputAnswers([multiSelectQuestion], {
areas: {
selectedOptionLabels: ["Server", "Web"],
},
}),
).toEqual({
areas: ["Server", "Web"],
});
});
it("returns null when any question is unanswered", () => {
expect(buildPendingUserInputAnswers([singleSelectQuestion], {})).toBeNull();
});
});
describe("pending user input question progress", () => {
const questions = [
singleSelectQuestion,
{
id: "compat",
header: "Compat",
question: "How strict should compatibility be?",
options: [
{
label: "Keep current envelope",
description: "Preserve current wire format",
},
],
multiSelect: false,
},
] as const;
it("counts only answered questions", () => {
expect(
countAnsweredPendingUserInputQuestions(questions, {
scope: {
selectedOptionLabels: ["Orchestration-first"],
},
}),
).toBe(1);
});
it("finds the first unanswered question", () => {
expect(
findFirstUnansweredPendingUserInputQuestionIndex(questions, {
scope: {
selectedOptionLabels: ["Orchestration-first"],
},
}),
).toBe(1);
});
it("returns the last question index when all answers are complete", () => {
expect(
findFirstUnansweredPendingUserInputQuestionIndex(questions, {
scope: {
selectedOptionLabels: ["Orchestration-first"],
},
compat: {
customAnswer: "Keep it for one release window",
},
}),
).toBe(1);
});
it("derives the active question and advancement state", () => {
expect(
derivePendingUserInputProgress(
questions,
{
scope: {
selectedOptionLabels: ["Orchestration-first"],
},
},
0,
),
).toMatchObject({
questionIndex: 0,
activeQuestion: questions[0],
selectedOptionLabels: ["Orchestration-first"],
customAnswer: "",
resolvedAnswer: "Orchestration-first",
answeredQuestionCount: 1,
isLastQuestion: false,
isComplete: false,
canAdvance: true,
});
});
it("treats multi-select questions as answered when they have selected options", () => {
expect(
derivePendingUserInputProgress(
[multiSelectQuestion],
{
areas: {
selectedOptionLabels: ["Server", "Web"],
},
},
0,
),
).toMatchObject({
selectedOptionLabels: ["Server", "Web"],
resolvedAnswer: ["Server", "Web"],
canAdvance: true,
isComplete: true,
});
});
});