cli-kiss 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +28 -13
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/Command.ts +75 -52
- package/src/lib/Operation.ts +16 -8
- package/src/lib/Option.ts +17 -12
- package/src/lib/Positional.ts +15 -8
- package/src/lib/Reader.ts +2 -5
- package/src/lib/Run.ts +32 -31
- package/src/lib/Type.ts +2 -2
- package/src/lib/Usage.ts +47 -41
- package/tests/unit.command.execute.ts +3 -2
- package/tests/unit.command.usage.ts +15 -20
- package/tests/{unit.runner.test1.ts → unit.runner.cycle.ts} +102 -68
|
@@ -16,12 +16,14 @@ import {
|
|
|
16
16
|
typeUrl,
|
|
17
17
|
} from "../src";
|
|
18
18
|
|
|
19
|
+
// TODO - unit test for color styling
|
|
20
|
+
|
|
19
21
|
it("run", async () => {
|
|
20
22
|
const rootUsage = [
|
|
21
|
-
"Root Description",
|
|
22
|
-
"",
|
|
23
23
|
"Usage: my-cli <REQUIRED1> <SUBCOMMAND>",
|
|
24
24
|
"",
|
|
25
|
+
"Root Description",
|
|
26
|
+
"",
|
|
25
27
|
"Positionals:",
|
|
26
28
|
" <REQUIRED1> Required1 positional description",
|
|
27
29
|
"",
|
|
@@ -35,10 +37,10 @@ it("run", async () => {
|
|
|
35
37
|
"",
|
|
36
38
|
].join("\n");
|
|
37
39
|
const subcommandUsage = [
|
|
38
|
-
"Subcommand Description",
|
|
39
|
-
"",
|
|
40
40
|
"Usage: my-cli <REQUIRED1> subcommand <REQUIRED2> [OPTIONAL] [VARIADICS]...",
|
|
41
41
|
"",
|
|
42
|
+
"Subcommand Description",
|
|
43
|
+
"",
|
|
42
44
|
"Positionals:",
|
|
43
45
|
" <REQUIRED1> Required1 positional description",
|
|
44
46
|
" <REQUIRED2> Required2 positional description",
|
|
@@ -53,9 +55,31 @@ it("run", async () => {
|
|
|
53
55
|
"",
|
|
54
56
|
].join("\n");
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
// Test that everything could work normally
|
|
59
|
+
await testCase(
|
|
60
|
+
["required1", "subcommand", "required2"],
|
|
61
|
+
["Has executed root command", "Has executed subcommand"],
|
|
62
|
+
[],
|
|
63
|
+
0,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Check that version flag takes precedence over execution
|
|
57
67
|
await testCase(["--version"], ["my-cli 1.0.0"], [], 0);
|
|
68
|
+
await testCase(["required1", "--version"], ["my-cli 1.0.0"], [], 0);
|
|
69
|
+
await testCase(
|
|
70
|
+
["required1", "subcommand", "--version"],
|
|
71
|
+
["my-cli 1.0.0"],
|
|
72
|
+
[],
|
|
73
|
+
0,
|
|
74
|
+
);
|
|
75
|
+
await testCase(
|
|
76
|
+
["required1", "subcommand", "required2", "--version"],
|
|
77
|
+
["my-cli 1.0.0"],
|
|
78
|
+
[],
|
|
79
|
+
0,
|
|
80
|
+
);
|
|
58
81
|
|
|
82
|
+
// Check that help flag takes precedence over execution
|
|
59
83
|
await testCase(["--help"], [rootUsage], [], 0);
|
|
60
84
|
await testCase(["required1", "--help"], [rootUsage], [], 0);
|
|
61
85
|
await testCase(
|
|
@@ -71,109 +95,129 @@ it("run", async () => {
|
|
|
71
95
|
0,
|
|
72
96
|
);
|
|
73
97
|
|
|
98
|
+
// Help takes precedence over version
|
|
99
|
+
await testCase(["--version", "--help"], [rootUsage], [], 0);
|
|
100
|
+
await testCase(["--help", "--version"], [rootUsage], [], 0);
|
|
101
|
+
|
|
102
|
+
// Test missing required inputs
|
|
74
103
|
await testCase(
|
|
75
104
|
[],
|
|
76
105
|
[],
|
|
77
|
-
[rootUsage, "Error:
|
|
106
|
+
[rootUsage, "Error: <REQUIRED1>: Is required, but was not provided"],
|
|
78
107
|
1,
|
|
79
108
|
);
|
|
80
109
|
await testCase(
|
|
81
110
|
["required1"],
|
|
82
111
|
[],
|
|
83
|
-
[rootUsage, "Error:
|
|
112
|
+
[rootUsage, "Error: <SUBCOMMAND>: Is required, but was not provided"],
|
|
84
113
|
1,
|
|
85
114
|
);
|
|
86
115
|
await testCase(
|
|
87
116
|
["required1", "subcommand"],
|
|
88
117
|
[],
|
|
89
|
-
[subcommandUsage, "Error:
|
|
118
|
+
[subcommandUsage, "Error: <REQUIRED2>: Is required, but was not provided"],
|
|
90
119
|
1,
|
|
91
120
|
);
|
|
121
|
+
|
|
122
|
+
// Test that flags become available when subcommand is known
|
|
92
123
|
await testCase(
|
|
93
|
-
["
|
|
124
|
+
["--url", "https://example.com"],
|
|
94
125
|
[],
|
|
95
|
-
[
|
|
96
|
-
subcommandUsage,
|
|
97
|
-
'Error: REQUIRED2: Unexpected value: "invalid" (expected: "required2"|"required2-bis")',
|
|
98
|
-
],
|
|
126
|
+
[rootUsage, "Error: --url: Unexpected unknown option"],
|
|
99
127
|
1,
|
|
100
128
|
);
|
|
101
|
-
|
|
102
129
|
await testCase(
|
|
103
|
-
["required1", "
|
|
130
|
+
["required1", "--url", "https://example.com"],
|
|
104
131
|
[],
|
|
105
|
-
[
|
|
132
|
+
[rootUsage, "Error: --url: Unexpected unknown option"],
|
|
106
133
|
1,
|
|
107
134
|
);
|
|
108
135
|
await testCase(
|
|
109
|
-
["required1", "subcommand", "
|
|
136
|
+
["required1", "subcommand", "--url", "https://example.com"],
|
|
110
137
|
[],
|
|
111
|
-
[
|
|
112
|
-
subcommandUsage,
|
|
113
|
-
"Error: Option parsing: --url: requires a value, but got end of input",
|
|
114
|
-
],
|
|
138
|
+
[subcommandUsage, "Error: <REQUIRED2>: Is required, but was not provided"],
|
|
115
139
|
1,
|
|
116
140
|
);
|
|
117
141
|
await testCase(
|
|
118
|
-
["required1", "subcommand", "required2", "--url", "
|
|
142
|
+
["required1", "subcommand", "required2", "--url", "https://example.com"],
|
|
143
|
+
["Has executed root command", "Has executed subcommand"],
|
|
119
144
|
[],
|
|
120
|
-
|
|
121
|
-
1,
|
|
145
|
+
0,
|
|
122
146
|
);
|
|
123
147
|
|
|
148
|
+
// Test option as flag parsing cases
|
|
124
149
|
await testCase(
|
|
125
|
-
["
|
|
150
|
+
["--flag", "--flag", "required1", "subcommand", "required2"],
|
|
126
151
|
[],
|
|
127
|
-
[
|
|
152
|
+
[subcommandUsage, "Error: --flag: Must not be set multiple times"],
|
|
128
153
|
1,
|
|
129
154
|
);
|
|
130
155
|
await testCase(
|
|
131
|
-
["
|
|
156
|
+
["--flag=42", "required1", "subcommand", "required2"],
|
|
132
157
|
[],
|
|
133
|
-
[subcommandUsage,
|
|
158
|
+
[subcommandUsage, 'Error: --flag: <BOOLEAN>: Invalid value: "42"'],
|
|
134
159
|
1,
|
|
135
160
|
);
|
|
136
161
|
await testCase(
|
|
137
|
-
["
|
|
162
|
+
["--flag=no", "required1", "subcommand", "required2"],
|
|
163
|
+
["Has executed root command", "Has executed subcommand"],
|
|
138
164
|
[],
|
|
165
|
+
0,
|
|
166
|
+
);
|
|
167
|
+
await testCase(
|
|
168
|
+
["--flag=yes", "required1", "subcommand", "required2"],
|
|
169
|
+
["Has executed root command", "Has executed subcommand"],
|
|
139
170
|
[],
|
|
140
171
|
0,
|
|
141
172
|
);
|
|
142
173
|
|
|
174
|
+
// Test option parsing errors
|
|
143
175
|
await testCase(
|
|
144
176
|
["--invalid", "required1", "subcommand", "required2"],
|
|
145
177
|
[],
|
|
146
|
-
[rootUsage,
|
|
178
|
+
[rootUsage, "Error: --invalid: Unexpected unknown option"],
|
|
147
179
|
1,
|
|
148
180
|
);
|
|
149
181
|
await testCase(
|
|
150
|
-
["
|
|
182
|
+
["required1", "subcommand", "required2", "--nope"],
|
|
151
183
|
[],
|
|
152
|
-
[
|
|
153
|
-
subcommandUsage,
|
|
154
|
-
"Error: Option value for: --flag: must not be set multiple times",
|
|
155
|
-
],
|
|
184
|
+
[subcommandUsage, "Error: --nope: Unexpected unknown option"],
|
|
156
185
|
1,
|
|
157
186
|
);
|
|
158
187
|
await testCase(
|
|
159
|
-
["
|
|
188
|
+
["required1", "subcommand", "required2", "--url"],
|
|
160
189
|
[],
|
|
161
|
-
[subcommandUsage,
|
|
190
|
+
[subcommandUsage, "Error: --url: requires a value, but got end of input"],
|
|
162
191
|
1,
|
|
163
192
|
);
|
|
193
|
+
|
|
194
|
+
// Test invalid input values type decoding errors
|
|
164
195
|
await testCase(
|
|
165
|
-
["
|
|
166
|
-
[],
|
|
196
|
+
["required1", "subcommand", "invalid"],
|
|
167
197
|
[],
|
|
168
|
-
|
|
198
|
+
[
|
|
199
|
+
subcommandUsage,
|
|
200
|
+
'Error: <REQUIRED2>: Unexpected value: "invalid" (expected: "required2"|"required2-bis")',
|
|
201
|
+
],
|
|
202
|
+
1,
|
|
169
203
|
);
|
|
170
204
|
await testCase(
|
|
171
|
-
["
|
|
205
|
+
["required1", "subcommand", "required2", "--single-value=44"],
|
|
172
206
|
[],
|
|
207
|
+
[
|
|
208
|
+
subcommandUsage,
|
|
209
|
+
'Error: --single-value: <NUMBER>: Unexpected value: "44" (expected: "42"|"43")',
|
|
210
|
+
],
|
|
211
|
+
1,
|
|
212
|
+
);
|
|
213
|
+
await testCase(
|
|
214
|
+
["required1", "subcommand", "required2", "--url", "not-a-url"],
|
|
173
215
|
[],
|
|
174
|
-
|
|
216
|
+
[subcommandUsage, "Error: --url: <URL>: TypeError: Invalid URL"],
|
|
217
|
+
1,
|
|
175
218
|
);
|
|
176
219
|
|
|
220
|
+
// Test option multiple value parsing cases
|
|
177
221
|
await testCase(
|
|
178
222
|
[
|
|
179
223
|
"required1",
|
|
@@ -183,7 +227,7 @@ it("run", async () => {
|
|
|
183
227
|
"--repeatable",
|
|
184
228
|
"43",
|
|
185
229
|
],
|
|
186
|
-
[],
|
|
230
|
+
["Has executed root command", "Has executed subcommand"],
|
|
187
231
|
[],
|
|
188
232
|
0,
|
|
189
233
|
);
|
|
@@ -197,19 +241,7 @@ it("run", async () => {
|
|
|
197
241
|
"43",
|
|
198
242
|
],
|
|
199
243
|
[],
|
|
200
|
-
[
|
|
201
|
-
subcommandUsage,
|
|
202
|
-
"Error: Option value for: --single-value: must not be set multiple times",
|
|
203
|
-
],
|
|
204
|
-
1,
|
|
205
|
-
);
|
|
206
|
-
await testCase(
|
|
207
|
-
["required1", "subcommand", "required2", "--single-value=44"],
|
|
208
|
-
[],
|
|
209
|
-
[
|
|
210
|
-
subcommandUsage,
|
|
211
|
-
'Error: --single-value: NUMBER: Unexpected value: "44" (expected: "42"|"43")',
|
|
212
|
-
],
|
|
244
|
+
[subcommandUsage, "Error: --single-value: Must not be set multiple times"],
|
|
213
245
|
1,
|
|
214
246
|
);
|
|
215
247
|
});
|
|
@@ -220,7 +252,16 @@ async function testCase(
|
|
|
220
252
|
expectStdErr: Array<string>,
|
|
221
253
|
expectExit: number,
|
|
222
254
|
) {
|
|
223
|
-
const
|
|
255
|
+
const onLogStdOut = makeMocked<string, void>([
|
|
256
|
+
null as unknown as void,
|
|
257
|
+
null as unknown as void,
|
|
258
|
+
]);
|
|
259
|
+
const onLogStdErr = makeMocked<string, void>([
|
|
260
|
+
null as unknown as void,
|
|
261
|
+
null as unknown as void,
|
|
262
|
+
]);
|
|
263
|
+
const onExit = makeMocked<number, never>([null as never]);
|
|
264
|
+
const cmd = commandWithSubcommands<null, void, void>(
|
|
224
265
|
{ description: "Root Description" },
|
|
225
266
|
operation(
|
|
226
267
|
{
|
|
@@ -250,7 +291,7 @@ async function testCase(
|
|
|
250
291
|
],
|
|
251
292
|
},
|
|
252
293
|
async () => {
|
|
253
|
-
|
|
294
|
+
onLogStdOut.call("Has executed root command");
|
|
254
295
|
},
|
|
255
296
|
),
|
|
256
297
|
{
|
|
@@ -284,20 +325,13 @@ async function testCase(
|
|
|
284
325
|
}),
|
|
285
326
|
],
|
|
286
327
|
},
|
|
287
|
-
async () => {
|
|
328
|
+
async () => {
|
|
329
|
+
onLogStdOut.call("Has executed subcommand");
|
|
330
|
+
},
|
|
288
331
|
),
|
|
289
332
|
),
|
|
290
333
|
},
|
|
291
334
|
);
|
|
292
|
-
const onLogStdOut = makeMocked<string, void>([
|
|
293
|
-
null as unknown as void,
|
|
294
|
-
null as unknown as void,
|
|
295
|
-
]);
|
|
296
|
-
const onLogStdErr = makeMocked<string, void>([
|
|
297
|
-
null as unknown as void,
|
|
298
|
-
null as unknown as void,
|
|
299
|
-
]);
|
|
300
|
-
const onExit = makeMocked<number, never>([null as never]);
|
|
301
335
|
await runAsCliAndExit("my-cli", args, null, cmd, {
|
|
302
336
|
buildVersion: "1.0.0",
|
|
303
337
|
onExit: onExit.call,
|