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.
@@ -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
- await testCase(["required1", "subcommand", "required2"], [], [], 0);
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: Missing required positional argument: REQUIRED1"],
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: Missing required positional argument: SUBCOMMAND"],
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: Missing required positional argument: REQUIRED2"],
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
- ["required1", "subcommand", "invalid"],
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", "subcommand", "required2", "--nope"],
130
+ ["required1", "--url", "https://example.com"],
104
131
  [],
105
- [subcommandUsage, "Error: Unknown option: --nope"],
132
+ [rootUsage, "Error: --url: Unexpected unknown option"],
106
133
  1,
107
134
  );
108
135
  await testCase(
109
- ["required1", "subcommand", "required2", "--url"],
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", "not-a-url"],
142
+ ["required1", "subcommand", "required2", "--url", "https://example.com"],
143
+ ["Has executed root command", "Has executed subcommand"],
119
144
  [],
120
- [subcommandUsage, "Error: --url: URL: TypeError: Invalid URL"],
121
- 1,
145
+ 0,
122
146
  );
123
147
 
148
+ // Test option as flag parsing cases
124
149
  await testCase(
125
- ["required1", "--url", "https://example.com"],
150
+ ["--flag", "--flag", "required1", "subcommand", "required2"],
126
151
  [],
127
- [rootUsage, `Error: Unknown option: --url`],
152
+ [subcommandUsage, "Error: --flag: Must not be set multiple times"],
128
153
  1,
129
154
  );
130
155
  await testCase(
131
- ["required1", "subcommand", "--url", "https://example.com"],
156
+ ["--flag=42", "required1", "subcommand", "required2"],
132
157
  [],
133
- [subcommandUsage, "Error: Missing required positional argument: REQUIRED2"],
158
+ [subcommandUsage, 'Error: --flag: <BOOLEAN>: Invalid value: "42"'],
134
159
  1,
135
160
  );
136
161
  await testCase(
137
- ["required1", "subcommand", "required2", "--url", "https://example.com"],
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, `Error: Unknown option: --invalid`],
178
+ [rootUsage, "Error: --invalid: Unexpected unknown option"],
147
179
  1,
148
180
  );
149
181
  await testCase(
150
- ["--flag", "--flag", "required1", "subcommand", "required2"],
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
- ["--flag=42", "required1", "subcommand", "required2"],
188
+ ["required1", "subcommand", "required2", "--url"],
160
189
  [],
161
- [subcommandUsage, 'Error: --flag: BOOLEAN: Invalid value: "42"'],
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
- ["--flag=no", "required1", "subcommand", "required2"],
166
- [],
196
+ ["required1", "subcommand", "invalid"],
167
197
  [],
168
- 0,
198
+ [
199
+ subcommandUsage,
200
+ 'Error: <REQUIRED2>: Unexpected value: "invalid" (expected: "required2"|"required2-bis")',
201
+ ],
202
+ 1,
169
203
  );
170
204
  await testCase(
171
- ["--flag=yes", "required1", "subcommand", "required2"],
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
- 0,
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 cmd = commandWithSubcommands<null, null, void>(
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
- return null;
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,