feat: mark recommended ask options

This commit is contained in:
Erik Kotsiuba
2026-08-12 14:18:59 +02:00
parent 4de596ae54
commit bd5f4520a3
20 changed files with 231 additions and 20 deletions
+75 -6
View File
@@ -9,13 +9,14 @@ import {
} from "../src/state/transitions.ts";
import { renderQuestionScreen } from "../src/ui/render-question.ts";
function mockTheme() {
function mockTheme(onColor?: (color: string, text: string) => void) {
return {
fg(color: string, text: string) {
return `<${color}>${text}</${color}>`;
onColor?.(color, text);
return onColor ? text : `<${color}>${text}</${color}>`;
},
bg(color: string, text: string) {
return `{${color}}${text}{/${color}}`;
return onColor ? text : `{${color}}${text}{/${color}}`;
},
bold(text: string) {
return text;
@@ -60,6 +61,56 @@ test("custom option stays labeled before selection", () => {
assert(!lines.some((line) => line.includes("Type your answer...")));
});
test("standard options show recommendation metadata without changing answers", () => {
let state = createInitialState({
questions: [
{
id: "q1",
prompt: "Pick one",
options: [
{
value: "a",
label: "Option A",
description: "Best fit for the stated constraints",
recommended: true,
},
{ value: "b", label: "Option B" },
],
},
],
});
const lines: string[] = [];
const calls: Array<{ color: string; text: string }> = [];
renderQuestionScreen({
editor: mockEditor(),
lines,
options: getRenderableOptions(state.questions[0]),
question: state.questions[0],
state,
theme: mockTheme((color, text) => calls.push({ color, text })),
width: 80,
});
assert(lines.some((line) => line.includes("(recommended) | Best fit")));
assert(
calls.some(
(call) => call.color === "warning" && call.text === "(recommended)"
)
);
assert(
calls.some(
(call) =>
call.color === "muted" &&
call.text === " | Best fit for the stated constraints"
)
);
assert.equal(state.answers.q1, undefined);
state = applyNumberShortcut(state, 1);
assert.equal(state.answers.q1.selected[0]?.label, "Option A");
});
test("selected custom option keeps its label and renders editor below", () => {
let state = createInitialState({
questions: [
@@ -230,30 +281,48 @@ test("freeform-only question renders label without numbering or pointer and sepa
assert(!lines.some((line) => line.includes("❯")));
});
test("preview questions also show the custom answer option", () => {
test("preview questions show custom and recommended options", () => {
const state = createInitialState({
questions: [
{
id: "q1",
prompt: "Pick one",
type: "preview",
options: [{ value: "a", label: "A", preview: "Preview A" }],
options: [
{
value: "a",
label: "A",
preview: "Preview A",
recommended: true,
},
],
},
],
});
const lines: string[] = [];
const calls: Array<{ color: string; text: string }> = [];
renderQuestionScreen({
editor: mockEditor(),
lines,
options: getRenderableOptions(state.questions[0]),
question: state.questions[0],
state,
theme: mockTheme(),
theme: mockTheme((color, text) => calls.push({ color, text })),
width: 80,
});
assert(lines.some((line) => line.includes("Type your own")));
assert(lines.some((line) => line.trim() === "(recommended)"));
assert(
calls.some(
(call) => call.color === "warning" && call.text === "(recommended)"
)
);
assert(
!calls.some((call) => call.color === "muted" && call.text.startsWith(" |"))
);
assert.equal(state.answers.q1, undefined);
});
test("preview custom option reuses the normal inline editor", () => {