mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 10:45:22 +00:00
125 lines
4.1 KiB
TypeScript
125 lines
4.1 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { normalizeQuestions } from "../src/normalize.ts";
|
|
import { runRpcQuestions, type RpcQuestionUI } from "../src/rpc.ts";
|
|
|
|
function mockUi(responses: {
|
|
select?: Array<string | undefined>;
|
|
input?: Array<string | undefined>;
|
|
}): RpcQuestionUI & {
|
|
calls: Array<{ kind: "select" | "input"; title: string; options?: string[] }>;
|
|
notifications: Array<{ message: string; type?: "info" | "warning" | "error" }>;
|
|
} {
|
|
const selectResponses = [...(responses.select ?? [])];
|
|
const inputResponses = [...(responses.input ?? [])];
|
|
const calls: Array<{ kind: "select" | "input"; title: string; options?: string[] }> = [];
|
|
const notifications: Array<{ message: string; type?: "info" | "warning" | "error" }> = [];
|
|
return {
|
|
calls,
|
|
notifications,
|
|
async select(title, options) {
|
|
calls.push({ kind: "select", title, options });
|
|
assert.ok(selectResponses.length > 0, "unexpected select dialog");
|
|
return selectResponses.shift();
|
|
},
|
|
async input(title) {
|
|
calls.push({ kind: "input", title });
|
|
assert.ok(inputResponses.length > 0, "unexpected input dialog");
|
|
return inputResponses.shift();
|
|
},
|
|
notify(message, type) {
|
|
notifications.push({ message, type });
|
|
},
|
|
};
|
|
}
|
|
|
|
test("asks RPC questions sequentially and preserves stable option values", async () => {
|
|
const questions = normalizeQuestions([
|
|
{
|
|
id: "language",
|
|
label: "Language",
|
|
prompt: "Which language?",
|
|
type: "select",
|
|
options: [
|
|
{ value: "ts", label: "TypeScript", description: "Strict typing" },
|
|
{ value: "js", label: "JavaScript" },
|
|
],
|
|
},
|
|
{ id: "notes", prompt: "Anything else?", type: "text", required: false },
|
|
]);
|
|
const ui = mockUi({ select: ["2. JavaScript"], input: [" "] });
|
|
|
|
const result = await runRpcQuestions(ui, questions);
|
|
|
|
assert.equal(result.cancelled, false);
|
|
assert.deepEqual(result.answers, [
|
|
{
|
|
id: "language",
|
|
question: "Which language?",
|
|
type: "select",
|
|
answer: "JavaScript",
|
|
value: "js",
|
|
label: "JavaScript",
|
|
custom: false,
|
|
skipped: false,
|
|
},
|
|
{
|
|
id: "notes",
|
|
question: "Anything else?",
|
|
type: "text",
|
|
answer: "",
|
|
value: "",
|
|
label: "Skipped",
|
|
custom: false,
|
|
skipped: true,
|
|
},
|
|
]);
|
|
assert.match(ui.calls[0]?.title ?? "", /^\[1\/2\] Language:/);
|
|
assert.ok(ui.calls[0]?.options?.includes("1. TypeScript — Strict typing"));
|
|
});
|
|
|
|
test("supports custom RPC answers and repeats required blank input", async () => {
|
|
const questions = normalizeQuestions([{
|
|
id: "other",
|
|
prompt: "Choose or write one",
|
|
type: "select",
|
|
options: [{ value: "known", label: "Known" }],
|
|
}]);
|
|
const ui = mockUi({ select: ["Type something."], input: [" ", " Custom value "] });
|
|
|
|
const result = await runRpcQuestions(ui, questions);
|
|
|
|
assert.equal(result.cancelled, false);
|
|
assert.equal(result.answers[0]?.answer, "Custom value");
|
|
assert.equal(result.answers[0]?.custom, true);
|
|
assert.deepEqual(ui.notifications, [{ message: "An answer is required.", type: "warning" }]);
|
|
});
|
|
|
|
test("returns cancelled with partial answers when an RPC dialog is dismissed", async () => {
|
|
const questions = normalizeQuestions([
|
|
{ id: "name", prompt: "Name?", type: "text" },
|
|
{ id: "confirm", prompt: "Continue?", type: "select", options: [{ value: "yes", label: "Yes" }] },
|
|
]);
|
|
const ui = mockUi({ input: ["Ada"], select: [undefined] });
|
|
|
|
const result = await runRpcQuestions(ui, questions);
|
|
|
|
assert.equal(result.cancelled, true);
|
|
assert.equal(result.answers.length, 1);
|
|
assert.equal(result.answers[0]?.answer, "Ada");
|
|
});
|
|
|
|
test("does not open RPC dialogs after abort", async () => {
|
|
const questions = normalizeQuestions([{ id: "name", prompt: "Name?", type: "text" }]);
|
|
const ui = mockUi({});
|
|
const controller = new AbortController();
|
|
controller.abort();
|
|
|
|
const result = await runRpcQuestions(ui, questions, controller.signal);
|
|
|
|
assert.equal(result.cancelled, true);
|
|
assert.deepEqual(result.answers, []);
|
|
assert.deepEqual(ui.calls, []);
|
|
});
|