-
Notifications
You must be signed in to change notification settings - Fork 2
/
FakeInquirer.ts
133 lines (115 loc) · 3.12 KB
/
FakeInquirer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import type {
QuestionCollection,
Answers,
DistinctQuestion,
} from "inquirer";
import {
PromiseAllSequence
} from "./PromiseTypes";
import type {
PartialInquirer
} from "./PartialInquirer";
export class FakeAnswers {
readonly answer: unknown
readonly validatePass: unknown[];
readonly validateFail: unknown[];
constructor(
answer: unknown,
validatePass: unknown[] = [],
validateFail: unknown[] = []
)
{
this.answer = answer;
this.validatePass = validatePass.slice();
this.validateFail = validateFail.slice();
}
}
export default
class FakeInquirer extends Map<string, FakeAnswers>
implements PartialInquirer
{
async prompt<T extends Answers = Answers>(
questions: QuestionCollection<T>,
initialAnswers?: Partial<T>
): Promise<T>
{
if (!FakeInquirer.#isQuestionArray<T>(questions))
throw new Error("FakeInquirer only supports question arrays");
const answers = {...initialAnswers};
await PromiseAllSequence(
questions,
async question => this.#askQuestion(question, answers as T)
);
return answers as T;
}
static #isQuestionArray<
T extends Answers = Answers
>
(
questions: QuestionCollection<T>
) : questions is DistinctQuestion<T>[]
{
return Array.isArray(questions);
}
async #askQuestion<
T extends Answers = Answers
>
(
question: DistinctQuestion<T>,
answers: T
) : Promise<void>
{
if (!question.name)
throw new Error("No name for a question... help!");
const { name } = question;
if ((name in answers) && !(question as { askAnswered: boolean }).askAnswered)
return;
const fakeAnswers = this.get(name);
if (!fakeAnswers)
throw new Error(`No fake answers for question "${name}"!`);
if (question.validate) {
await PromiseAllSequence(fakeAnswers.validateFail, async answer => {
await this.#validateAnswer(question, name, answer, answers, false, false);
});
await PromiseAllSequence(fakeAnswers.validatePass, async answer => {
await this.#validateAnswer(question, name, answer, answers, true, false);
});
await this.#validateAnswer(
question, name, fakeAnswers.answer, answers, true, true
);
}
else if (
fakeAnswers.validateFail.length ||
fakeAnswers.validatePass.length
)
{
throw new Error(`Validation test answers are present for a question with no validate method: "${name}"`);
}
answers[name] = fakeAnswers.answer as T[string];
}
async #validateAnswer<
T extends Answers = Answers
>
(
question : DistinctQuestion<T>,
name: string,
answer: unknown,
answers: T,
shouldPass: boolean,
isFinal: boolean
) : Promise<void>
{
if (!question.validate)
throw new Error("assertion failure, we shouldn't get here");
const result = await question.validate(answer, answers);
if ((result === true) === shouldPass)
return;
throw new Error(
`validation should have ${
shouldPass ? "passed" : "failed"
} on question ${name} with ${
isFinal ? "final " : ""
}answer "${String(answer)}"`
);
}
}