Open History to see the last commit
1import { assert, describe, it } from "@effect/vitest";
2
3import { parseElicitationForm } from "./elicitationForm.ts";
4
5describe("parseElicitationForm", () => {
6 for (const type of ["string", "array"] as const) {
7 it.each([
8 { values: [" padded ", "", "plain"], labels: ["padded", '""', "plain"] },
9 { values: [" a", "a", "b"], labels: ['" a"', '"a"', "b"] },
10 { values: [" a", "a", '"a"'], labels: ['" a"', '"a"', '"\\"a\\""'] },
11 ])(
12 `preserves ${type} enum values with distinct readable labels: $values`,
13 ({ values, labels }) => {
14 const form = parseElicitationForm(
15 {
16 type: "object",
17 properties: {
18 answer:
19 type === "array"
20 ? { type, items: { type: "string", enum: values } }
21 : { type, enum: values },
22 },
23 required: ["answer"],
24 },
25 "Choose",
26 );
27 assert(form !== undefined);
28 const question = form.questions[0];
29 assert(question !== undefined);
30 assert.deepStrictEqual(
31 question.options.map((option) => option.label),
32 labels,
33 );
34 assert.deepStrictEqual(
35 question.options.map((option) => option.value),
36 values,
37 );
38 assert.strictEqual(question.allowCustomAnswer, false);
39 assert.strictEqual(question.multiSelect, type === "array" ? true : undefined);
40 for (const value of values) {
41 const answer = type === "array" ? [value] : value;
42 assert.deepStrictEqual(form.content({ answer }), { answer });
43 }
44 },
45 );
46 }
47
48 it("asks each question once when a choice has a free-text companion field", () => {
49 // OMP's ask dialog follows each choice `q<n>` with a free-text `q<n>__other` field.
50 const ask = (question: string, header: string) => ({
51 type: "string",
52 title: question,
53 description: header,
54 oneOf: [
55 { const: "Yes", title: "Yes" },
56 { const: "No", title: "No" },
57 ],
58 });
59 const form = parseElicitationForm(
60 {
61 type: "object",
62 properties: {
63 q0: ask("Use Postgres for the new service?", "Database"),
64 q0__other: { type: "string", title: "Other (type your own)" },
65 q1: ask("Deploy to staging first?", "Deploy"),
66 q1__other: { type: "string", title: "Other (type your own)" },
67 },
68 },
69 "Answer 2 questions",
70 );
71 assert(form !== undefined);
72 assert.deepStrictEqual(
73 form.questions.map((question) =>
74 [question.header, question.question].some((text) => text.endsWith("?")),
75 ),
76 [true, true],
77 );
78 assert.deepStrictEqual(form.content({ q0: "Yes", q1: "Only on Fridays" }), {
79 q0: "Yes",
80 q1__other: "Only on Fridays",
81 });
82
83 // A multi-select answer can carry options and free text together, so both fields stay.
84 const multi = parseElicitationForm(
85 {
86 type: "object",
87 properties: {
88 q0: { type: "array", title: "Databases?", items: { anyOf: [{ const: "Postgres" }] } },
89 q0__other: { type: "string", title: "Other (type your own)" },
90 },
91 },
92 "Databases?",
93 );
94 assert(multi !== undefined);
95 assert.deepStrictEqual(multi.content({ q0: ["Postgres"], q0__other: "SQLite" }), {
96 q0: ["Postgres"],
97 q0__other: "SQLite",
98 });
99 });
100});