106 lines
4.6 KiB
TypeScript
106 lines
4.6 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import {
|
|
choiceQuestionGroups,
|
|
choiceSelectionIntent,
|
|
normalizeUnmatchedNote,
|
|
} from "../src/lib/birth-time-choice-question-model.ts";
|
|
import type { PublicDynamicChoiceQuestion } from "../src/lib/birth-time-dynamic-choice.ts";
|
|
import {
|
|
advanceDynamicBirthTimePreview,
|
|
dynamicBirthTimePreview,
|
|
} from "../src/lib/birth-time-dynamic-preview.ts";
|
|
|
|
const question: PublicDynamicChoiceQuestion = {
|
|
questionId: "education-shift",
|
|
prompt: "哪段时间更接近一次明显的学习方向变化?",
|
|
options: [
|
|
{ optionId: "early", label: "更接近 2007—2009 年", kind: "primary" },
|
|
{ optionId: "late", label: "更接近 2010—2012 年", kind: "primary" },
|
|
{ optionId: "unknown", label: "不记得", kind: "unknown" },
|
|
{ optionId: "unmatched", label: "都不符合", kind: "unmatched" },
|
|
],
|
|
};
|
|
|
|
test("click intent submits primary and unknown options immediately", () => {
|
|
const groups = choiceQuestionGroups(question);
|
|
|
|
assert.deepEqual(groups.primary.map((option) => option.optionId), ["early", "late"]);
|
|
assert.deepEqual(choiceSelectionIntent(groups.primary[0]), {
|
|
kind: "submit", optionId: "early", effective: true,
|
|
});
|
|
assert.deepEqual(choiceSelectionIntent(groups.unknown), {
|
|
kind: "submit", optionId: "unknown", effective: false,
|
|
});
|
|
});
|
|
|
|
test("unmatched requests clarification without creating scoring evidence", () => {
|
|
const groups = choiceQuestionGroups(question);
|
|
|
|
assert.deepEqual(choiceSelectionIntent(groups.unmatched), {
|
|
kind: "submit", optionId: "unmatched", effective: false,
|
|
});
|
|
assert.equal(normalizeUnmatchedNote(" 大约是在搬家之后 "), "大约是在搬家之后");
|
|
assert.equal(normalizeUnmatchedNote("甲".repeat(260)).length, 240);
|
|
});
|
|
|
|
test("preview choices expose real local interaction states without authentication", () => {
|
|
const start = dynamicBirthTimePreview();
|
|
const nextQuestion = advanceDynamicBirthTimePreview(start, { kind: "select", optionId: "earlier" });
|
|
const result = advanceDynamicBirthTimePreview(nextQuestion, { kind: "select", optionId: "school" });
|
|
const clarification = advanceDynamicBirthTimePreview(start, { kind: "select", optionId: "unmatched" });
|
|
const reframed = advanceDynamicBirthTimePreview(clarification, { kind: "reframe" });
|
|
const terminal = advanceDynamicBirthTimePreview(reframed, { kind: "finish" });
|
|
|
|
assert.equal(nextQuestion.nextAction.kind, "ask_dynamic_choice");
|
|
assert.equal(nextQuestion.progress.effectiveAnswerCount, 1);
|
|
assert.equal(result.nextAction.kind, "present_medium_result");
|
|
assert.equal(clarification.nextAction.kind, "clarify_unmatched_answer");
|
|
assert.equal(reframed.nextAction.kind, "ask_dynamic_choice");
|
|
assert.equal(terminal.nextAction.kind, "present_low_result");
|
|
});
|
|
|
|
test("preview unknown answers terminate instead of repeating the same question", () => {
|
|
const start = dynamicBirthTimePreview();
|
|
const secondQuestion = advanceDynamicBirthTimePreview(start, { kind: "select", optionId: "unknown" });
|
|
const terminal = advanceDynamicBirthTimePreview(secondQuestion, { kind: "select", optionId: "unknown-2" });
|
|
|
|
assert.equal(secondQuestion.nextAction.kind, "ask_dynamic_choice");
|
|
assert.equal(terminal.nextAction.kind, "present_low_result");
|
|
assert.equal(terminal.progress.answeredCount, 2);
|
|
});
|
|
|
|
test("preview unmatched reframing terminates when no different question remains", () => {
|
|
const start = dynamicBirthTimePreview();
|
|
const secondQuestion = advanceDynamicBirthTimePreview(start, { kind: "select", optionId: "earlier" });
|
|
const secondClarification = advanceDynamicBirthTimePreview(secondQuestion, {
|
|
kind: "select",
|
|
optionId: "unmatched-2",
|
|
});
|
|
const terminal = advanceDynamicBirthTimePreview(secondClarification, { kind: "reframe" });
|
|
|
|
assert.equal(secondClarification.nextAction.kind, "clarify_unmatched_answer");
|
|
assert.equal(terminal.nextAction.kind, "present_low_result");
|
|
assert.equal(terminal.progress.answeredCount, 2);
|
|
});
|
|
|
|
test("dynamic previews cover every visible result and recovery state", () => {
|
|
const expected = new Map([
|
|
["generating", "generate_dynamic_question"],
|
|
["question-retry", "retry_question_generation"],
|
|
["question", "ask_dynamic_choice"],
|
|
["clarification", "clarify_unmatched_answer"],
|
|
["scoring", "score_pending"],
|
|
["scoring-retry", "retry_scoring"],
|
|
["low", "present_low_result"],
|
|
["medium", "present_medium_result"],
|
|
["confirmation", "request_candidate_confirmation"],
|
|
["ready", "ready"],
|
|
["paused", "paused"],
|
|
] as const);
|
|
|
|
for (const [state, action] of expected) {
|
|
assert.equal(dynamicBirthTimePreview(state).nextAction.kind, action);
|
|
}
|
|
});
|