181 lines
5.3 KiB
JavaScript
181 lines
5.3 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import {
|
|
SOMETHING_ELSE_LABEL,
|
|
SOMETHING_ELSE_VALUE,
|
|
allQuestionsAnswered,
|
|
createAnsweredResult,
|
|
createCancelledResult,
|
|
createCustomAnswer,
|
|
createPredefinedAnswer,
|
|
nextTabAfterAnswer,
|
|
normalizeQuestions,
|
|
summarizeAnswers,
|
|
wrapPrefixedText,
|
|
} from "./question-core.mjs";
|
|
|
|
test("normalizeQuestions adds default labels and appends the Something else option", () => {
|
|
const [question] = normalizeQuestions([
|
|
{
|
|
id: "scope",
|
|
prompt: "Which scope fits best?",
|
|
options: [{ value: "small", label: "Small change" }],
|
|
},
|
|
]);
|
|
|
|
assert.equal(question.label, "Q1");
|
|
assert.deepEqual(question.options[0], { value: "small", label: "Small change" });
|
|
assert.deepEqual(question.options.at(-1), {
|
|
value: SOMETHING_ELSE_VALUE,
|
|
label: SOMETHING_ELSE_LABEL,
|
|
});
|
|
});
|
|
|
|
test("normalizeQuestions keeps provided labels and descriptions intact before the synthetic option", () => {
|
|
const [question] = normalizeQuestions([
|
|
{
|
|
id: "priority",
|
|
label: "Priority",
|
|
prompt: "Which priority?",
|
|
options: [
|
|
{ value: "p0", label: "P0", description: "Need this now" },
|
|
{ value: "p1", label: "P1" },
|
|
],
|
|
},
|
|
]);
|
|
|
|
assert.equal(question.label, "Priority");
|
|
assert.deepEqual(question.options.slice(0, 2), [
|
|
{ value: "p0", label: "P0", description: "Need this now" },
|
|
{ value: "p1", label: "P1" },
|
|
]);
|
|
assert.equal(question.options[2].label, SOMETHING_ELSE_LABEL);
|
|
});
|
|
|
|
test("answer helpers preserve machine values and summary lines distinguish predefined vs custom answers", () => {
|
|
const questions = normalizeQuestions([
|
|
{
|
|
id: "scope",
|
|
label: "Scope",
|
|
prompt: "Which scope fits best?",
|
|
options: [{ value: "small", label: "Small change" }],
|
|
},
|
|
{
|
|
id: "notes",
|
|
label: "Notes",
|
|
prompt: "Anything else?",
|
|
options: [{ value: "none", label: "No extra notes" }],
|
|
},
|
|
]);
|
|
|
|
const predefined = createPredefinedAnswer("scope", questions[0].options[0], 1);
|
|
const custom = createCustomAnswer("notes", "Needs to work with tmux");
|
|
|
|
assert.deepEqual(predefined, {
|
|
id: "scope",
|
|
value: "small",
|
|
label: "Small change",
|
|
wasCustom: false,
|
|
index: 1,
|
|
});
|
|
assert.deepEqual(custom, {
|
|
id: "notes",
|
|
value: "Needs to work with tmux",
|
|
label: "Needs to work with tmux",
|
|
wasCustom: true,
|
|
});
|
|
assert.deepEqual(summarizeAnswers(questions, [predefined, custom]), [
|
|
"Scope: user selected: 1. Small change",
|
|
"Notes: user wrote: Needs to work with tmux",
|
|
]);
|
|
});
|
|
|
|
test("createCancelledResult returns a structured cancelled payload", () => {
|
|
const questions = normalizeQuestions([
|
|
{
|
|
id: "scope",
|
|
prompt: "Which scope fits best?",
|
|
options: [{ value: "small", label: "Small change" }],
|
|
},
|
|
]);
|
|
|
|
assert.deepEqual(createCancelledResult(questions), {
|
|
questions,
|
|
answers: [],
|
|
cancelled: true,
|
|
});
|
|
});
|
|
|
|
test("createAnsweredResult keeps answers in question order", () => {
|
|
const questions = normalizeQuestions([
|
|
{
|
|
id: "scope",
|
|
label: "Scope",
|
|
prompt: "Which scope fits best?",
|
|
options: [{ value: "small", label: "Small change" }],
|
|
},
|
|
{
|
|
id: "notes",
|
|
label: "Notes",
|
|
prompt: "Anything else?",
|
|
options: [{ value: "none", label: "No extra notes" }],
|
|
},
|
|
]);
|
|
|
|
const second = createCustomAnswer("notes", "Custom note");
|
|
const first = createPredefinedAnswer("scope", questions[0].options[0], 1);
|
|
const result = createAnsweredResult(questions, [second, first]);
|
|
|
|
assert.equal(result.cancelled, false);
|
|
assert.deepEqual(result.answers.map((answer) => answer.id), ["scope", "notes"]);
|
|
});
|
|
|
|
test("allQuestionsAnswered only returns true when every question has an answer", () => {
|
|
const questions = normalizeQuestions([
|
|
{
|
|
id: "scope",
|
|
prompt: "Scope?",
|
|
options: [{ value: "small", label: "Small" }],
|
|
},
|
|
{
|
|
id: "priority",
|
|
prompt: "Priority?",
|
|
options: [{ value: "p1", label: "P1" }],
|
|
},
|
|
]);
|
|
|
|
const answers = new Map([
|
|
["scope", createPredefinedAnswer("scope", questions[0].options[0], 1)],
|
|
]);
|
|
|
|
assert.equal(allQuestionsAnswered(questions, answers), false);
|
|
answers.set("priority", createCustomAnswer("priority", "Ship this week"));
|
|
assert.equal(allQuestionsAnswered(questions, answers), true);
|
|
});
|
|
|
|
test("nextTabAfterAnswer advances through questions and then to the submit tab", () => {
|
|
assert.equal(nextTabAfterAnswer(0, 3), 1);
|
|
assert.equal(nextTabAfterAnswer(1, 3), 2);
|
|
assert.equal(nextTabAfterAnswer(2, 3), 3);
|
|
});
|
|
|
|
test("wrapPrefixedText wraps long prompts and keeps the prefix on continuation lines", () => {
|
|
assert.deepEqual(wrapPrefixedText("Pick the best rollout strategy for this change", 18, " "), [
|
|
" Pick the best",
|
|
" rollout strategy",
|
|
" for this change",
|
|
]);
|
|
});
|
|
|
|
test("wrapPrefixedText supports a different continuation prefix for wrapped option labels", () => {
|
|
assert.deepEqual(wrapPrefixedText("Very long option label", 16, "> 1. ", " "), [
|
|
"> 1. Very long",
|
|
" option",
|
|
" label",
|
|
]);
|
|
});
|
|
|
|
test("wrapPrefixedText breaks oversized words when there is no whitespace boundary", () => {
|
|
assert.deepEqual(wrapPrefixedText("supercalifragilistic", 8), ["supercal", "ifragili", "stic"]);
|
|
});
|