goke 6.1.3 → 6.2.1
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/README.md +281 -3
- package/dist/__test__/index.test.js +347 -8
- package/dist/coerce.d.ts +15 -19
- package/dist/coerce.d.ts.map +1 -1
- package/dist/coerce.js +39 -33
- package/dist/goke.d.ts +25 -2
- package/dist/goke.d.ts.map +1 -1
- package/dist/goke.js +153 -83
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +26 -6
- package/src/__test__/index.test.ts +412 -9
- package/src/coerce.ts +44 -35
- package/src/goke.ts +169 -92
- package/src/index.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, test, expect } from 'vitest'
|
|
2
2
|
import goke, { createConsole } from '../index.js'
|
|
3
|
-
import type { GokeOutputStream } from '../index.js'
|
|
3
|
+
import type { GokeOutputStream, GokeOptions } from '../index.js'
|
|
4
4
|
import { coerceBySchema } from '../coerce.js'
|
|
5
5
|
import { z } from 'zod'
|
|
6
6
|
|
|
@@ -21,6 +21,142 @@ function createTestOutputStream(): GokeOutputStream & { lines: string[]; readonl
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Helper: creates a goke instance with exit overridden to a no-op.
|
|
26
|
+
* This prevents process.exit(1) from killing the test runner while
|
|
27
|
+
* still allowing the original error to propagate (the framework
|
|
28
|
+
* re-throws after calling exit when exit doesn't halt execution).
|
|
29
|
+
*
|
|
30
|
+
* Tests can still use .toThrow() to assert CLI errors normally.
|
|
31
|
+
*/
|
|
32
|
+
function gokeTestable(name = '', options?: Partial<GokeOptions>) {
|
|
33
|
+
return goke(name, {
|
|
34
|
+
...options,
|
|
35
|
+
exit: () => {},
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Strip stack trace lines for stable snapshots.
|
|
41
|
+
* Keeps the error message and help hint, removes all " at ..." lines
|
|
42
|
+
* and the blank line before them, since those contain machine-specific paths.
|
|
43
|
+
*/
|
|
44
|
+
function stripStackTrace(text: string): string {
|
|
45
|
+
return text
|
|
46
|
+
.split('\n')
|
|
47
|
+
.filter(line => !line.match(/^\s+at /))
|
|
48
|
+
.join('\n')
|
|
49
|
+
.replace(/\n{2,}/g, '\n')
|
|
50
|
+
.trim()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe('error formatting', () => {
|
|
54
|
+
test('unknown option prints formatted error to stderr', () => {
|
|
55
|
+
const stderr = createTestOutputStream()
|
|
56
|
+
const cli = goke('mycli', { stderr, exit: () => {} })
|
|
57
|
+
|
|
58
|
+
cli
|
|
59
|
+
.command('build', 'Build your app')
|
|
60
|
+
.option('--port <port>', 'Port')
|
|
61
|
+
.action(() => {})
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
cli.parse('node bin build --unknown'.split(' '))
|
|
65
|
+
} catch {}
|
|
66
|
+
|
|
67
|
+
expect(stripStackTrace(stderr.text)).toMatchInlineSnapshot(`"error: Unknown option \`--unknown\`"`)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('missing required option value prints formatted error to stderr', () => {
|
|
71
|
+
const stderr = createTestOutputStream()
|
|
72
|
+
const cli = goke('mycli', { stderr, exit: () => {} })
|
|
73
|
+
|
|
74
|
+
cli
|
|
75
|
+
.command('serve', 'Start server')
|
|
76
|
+
.option('--port <port>', 'Port')
|
|
77
|
+
.action(() => {})
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
cli.parse('node bin serve --port'.split(' '))
|
|
81
|
+
} catch {}
|
|
82
|
+
|
|
83
|
+
expect(stripStackTrace(stderr.text)).toMatchInlineSnapshot(`"error: option \`--port <port>\` value is missing"`)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('schema coercion error prints formatted error to stderr', () => {
|
|
87
|
+
const stderr = createTestOutputStream()
|
|
88
|
+
const cli = goke('mycli', { stderr, exit: () => {} })
|
|
89
|
+
|
|
90
|
+
cli.option('--port <port>', z.number().describe('Port'))
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
cli.parse('node bin --port abc'.split(' '))
|
|
94
|
+
} catch {}
|
|
95
|
+
|
|
96
|
+
expect(stripStackTrace(stderr.text)).toMatchInlineSnapshot(`"error: Invalid value for --port: expected number, got "abc""`)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test('error includes help hint when help is enabled', () => {
|
|
100
|
+
const stderr = createTestOutputStream()
|
|
101
|
+
const cli = goke('mycli', { stderr, exit: () => {} })
|
|
102
|
+
|
|
103
|
+
cli.help()
|
|
104
|
+
|
|
105
|
+
cli
|
|
106
|
+
.command('serve', 'Start server')
|
|
107
|
+
.option('--port <port>', 'Port')
|
|
108
|
+
.action(() => {})
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
cli.parse('node bin serve --port'.split(' '))
|
|
112
|
+
} catch {}
|
|
113
|
+
|
|
114
|
+
expect(stripStackTrace(stderr.text)).toMatchInlineSnapshot(`
|
|
115
|
+
"error: option \`--port <port>\` value is missing
|
|
116
|
+
Run "mycli serve --help" for usage information."
|
|
117
|
+
`)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('async action error prints formatted error to stderr', async () => {
|
|
121
|
+
const stderr = createTestOutputStream()
|
|
122
|
+
let exitCode: number | undefined
|
|
123
|
+
const cli = goke('mycli', { stderr, exit: (code) => { exitCode = code } })
|
|
124
|
+
|
|
125
|
+
cli
|
|
126
|
+
.command('deploy', 'Deploy app')
|
|
127
|
+
.action(async () => {
|
|
128
|
+
throw new Error('connection refused')
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
cli.parse('node bin deploy'.split(' '))
|
|
132
|
+
|
|
133
|
+
// Wait for the async rejection to be handled
|
|
134
|
+
await new Promise(resolve => setTimeout(resolve, 10))
|
|
135
|
+
|
|
136
|
+
expect(exitCode).toBe(1)
|
|
137
|
+
expect(stripStackTrace(stderr.text)).toMatchInlineSnapshot(`"error: connection refused"`)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
test('error output includes stack trace', () => {
|
|
141
|
+
const stderr = createTestOutputStream()
|
|
142
|
+
const cli = goke('mycli', { stderr, exit: () => {} })
|
|
143
|
+
|
|
144
|
+
cli
|
|
145
|
+
.command('build', 'Build app')
|
|
146
|
+
.action(() => {})
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
cli.parse('node bin build --unknown'.split(' '))
|
|
150
|
+
} catch {}
|
|
151
|
+
|
|
152
|
+
// Verify that stderr contains "error:" prefix and a stack trace with "at" lines
|
|
153
|
+
const text = stderr.text
|
|
154
|
+
expect(text).toContain('error:')
|
|
155
|
+
expect(text).toContain('Unknown option `--unknown`')
|
|
156
|
+
expect(text).toMatch(/at /)
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
24
160
|
test('double dashes', () => {
|
|
25
161
|
const cli = goke()
|
|
26
162
|
|
|
@@ -101,7 +237,7 @@ describe('schema-based options', () => {
|
|
|
101
237
|
})
|
|
102
238
|
|
|
103
239
|
test('schema throws on invalid number', () => {
|
|
104
|
-
const cli =
|
|
240
|
+
const cli = gokeTestable()
|
|
105
241
|
|
|
106
242
|
cli.option('--port <port>', z.number().describe('Port number'))
|
|
107
243
|
|
|
@@ -328,7 +464,7 @@ describe('typical CLI usage examples', () => {
|
|
|
328
464
|
|
|
329
465
|
describe('regression: oracle-found issues', () => {
|
|
330
466
|
test('required option with schema still throws when value missing', () => {
|
|
331
|
-
const cli =
|
|
467
|
+
const cli = gokeTestable()
|
|
332
468
|
let actionCalled = false
|
|
333
469
|
|
|
334
470
|
cli
|
|
@@ -344,7 +480,7 @@ describe('regression: oracle-found issues', () => {
|
|
|
344
480
|
})
|
|
345
481
|
|
|
346
482
|
test('repeated flags with non-array schema throws', () => {
|
|
347
|
-
const cli =
|
|
483
|
+
const cli = gokeTestable()
|
|
348
484
|
|
|
349
485
|
cli.option('--tag <tag>', z.string().describe('Tags'))
|
|
350
486
|
|
|
@@ -353,7 +489,7 @@ describe('regression: oracle-found issues', () => {
|
|
|
353
489
|
})
|
|
354
490
|
|
|
355
491
|
test('repeated flags with number schema throws', () => {
|
|
356
|
-
const cli =
|
|
492
|
+
const cli = gokeTestable()
|
|
357
493
|
|
|
358
494
|
cli.option('--id <id>', z.number().describe('ID'))
|
|
359
495
|
|
|
@@ -535,7 +671,7 @@ describe('edge cases: boolean flags + schema', () => {
|
|
|
535
671
|
})
|
|
536
672
|
|
|
537
673
|
test('invalid boolean string with boolean schema throws', () => {
|
|
538
|
-
const cli =
|
|
674
|
+
const cli = gokeTestable()
|
|
539
675
|
|
|
540
676
|
cli.option('--flag <flag>', z.boolean().describe('A flag'))
|
|
541
677
|
|
|
@@ -587,7 +723,7 @@ describe('edge cases: empty string values', () => {
|
|
|
587
723
|
})
|
|
588
724
|
|
|
589
725
|
test('empty string with number schema throws', () => {
|
|
590
|
-
const cli =
|
|
726
|
+
const cli = gokeTestable()
|
|
591
727
|
|
|
592
728
|
cli.option('--port <port>', z.number().describe('Port'))
|
|
593
729
|
|
|
@@ -653,7 +789,7 @@ describe('edge cases: short alias + schema', () => {
|
|
|
653
789
|
})
|
|
654
790
|
|
|
655
791
|
test('short alias repeated with non-array schema throws', () => {
|
|
656
|
-
const cli =
|
|
792
|
+
const cli = gokeTestable()
|
|
657
793
|
|
|
658
794
|
cli.option('-p, --port <port>', z.number().describe('Port'))
|
|
659
795
|
|
|
@@ -663,7 +799,7 @@ describe('edge cases: short alias + schema', () => {
|
|
|
663
799
|
})
|
|
664
800
|
|
|
665
801
|
test('throw on unknown options', () => {
|
|
666
|
-
const cli =
|
|
802
|
+
const cli = gokeTestable()
|
|
667
803
|
|
|
668
804
|
cli
|
|
669
805
|
.command('build [entry]', 'Build your app')
|
|
@@ -834,23 +970,32 @@ describe('space-separated subcommands', () => {
|
|
|
834
970
|
expect(stripAnsi(output)).toMatchInlineSnapshot(`
|
|
835
971
|
"mycli
|
|
836
972
|
|
|
973
|
+
|
|
837
974
|
Usage:
|
|
838
975
|
$ mycli <command> [options]
|
|
839
976
|
|
|
977
|
+
|
|
840
978
|
Commands:
|
|
841
979
|
mcp login <url> Login to MCP server
|
|
842
980
|
|
|
981
|
+
|
|
843
982
|
mcp logout Logout from MCP server
|
|
844
983
|
|
|
984
|
+
|
|
845
985
|
mcp status Show connection status
|
|
846
986
|
|
|
987
|
+
|
|
847
988
|
git remote add <name> <url> Add a git remote
|
|
848
989
|
|
|
990
|
+
|
|
849
991
|
git remote remove <name> Remove a git remote
|
|
850
992
|
|
|
993
|
+
|
|
851
994
|
build Build the project
|
|
995
|
+
|
|
852
996
|
--watch Watch mode
|
|
853
997
|
|
|
998
|
+
|
|
854
999
|
Options:
|
|
855
1000
|
-h, --help Display this message
|
|
856
1001
|
"
|
|
@@ -1100,6 +1245,113 @@ describe('many commands with root command (empty string)', () => {
|
|
|
1100
1245
|
expect(stdout.text).toContain('Stream logs for a deployment')
|
|
1101
1246
|
})
|
|
1102
1247
|
|
|
1248
|
+
test('root help with many commands renders examples section after options', () => {
|
|
1249
|
+
const stdout = createTestOutputStream()
|
|
1250
|
+
const cli = goke('deploy', { stdout })
|
|
1251
|
+
|
|
1252
|
+
cli
|
|
1253
|
+
.command('', 'Deploy the current project')
|
|
1254
|
+
.option('--env <env>', 'Target environment')
|
|
1255
|
+
.option('--dry-run', 'Preview without deploying')
|
|
1256
|
+
.example('# Deploy to staging first')
|
|
1257
|
+
.example('deploy --env staging --dry-run')
|
|
1258
|
+
|
|
1259
|
+
cli.command('init', 'Initialize a new project')
|
|
1260
|
+
cli.command('login', 'Authenticate with the server')
|
|
1261
|
+
cli.command('logout', 'Clear saved credentials')
|
|
1262
|
+
cli.command('status', 'Show deployment status')
|
|
1263
|
+
cli.command('logs <deploymentId>', 'Stream logs for a deployment')
|
|
1264
|
+
|
|
1265
|
+
cli.help()
|
|
1266
|
+
cli.parse(['node', 'bin', '--help'], { run: false })
|
|
1267
|
+
|
|
1268
|
+
expect(stdout.text).toMatchInlineSnapshot(`
|
|
1269
|
+
"deploy
|
|
1270
|
+
|
|
1271
|
+
|
|
1272
|
+
Usage:
|
|
1273
|
+
$ deploy [options]
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
Commands:
|
|
1277
|
+
deploy Deploy the current project
|
|
1278
|
+
|
|
1279
|
+
|
|
1280
|
+
init Initialize a new project
|
|
1281
|
+
|
|
1282
|
+
|
|
1283
|
+
login Authenticate with the server
|
|
1284
|
+
|
|
1285
|
+
|
|
1286
|
+
logout Clear saved credentials
|
|
1287
|
+
|
|
1288
|
+
|
|
1289
|
+
status Show deployment status
|
|
1290
|
+
|
|
1291
|
+
|
|
1292
|
+
logs <deploymentId> Stream logs for a deployment
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
Options:
|
|
1296
|
+
--env <env> Target environment
|
|
1297
|
+
--dry-run Preview without deploying
|
|
1298
|
+
-h, --help Display this message
|
|
1299
|
+
|
|
1300
|
+
|
|
1301
|
+
Examples:
|
|
1302
|
+
# Deploy to staging first
|
|
1303
|
+
deploy --env staging --dry-run
|
|
1304
|
+
"
|
|
1305
|
+
`)
|
|
1306
|
+
})
|
|
1307
|
+
|
|
1308
|
+
test('subcommand help renders command examples at the end', () => {
|
|
1309
|
+
const stdout = createTestOutputStream()
|
|
1310
|
+
const cli = goke('deploy', { stdout, columns: 80 })
|
|
1311
|
+
|
|
1312
|
+
cli.command('', 'Deploy the current project')
|
|
1313
|
+
cli.command('init', 'Initialize a new project')
|
|
1314
|
+
cli.command('login', 'Authenticate with the server')
|
|
1315
|
+
|
|
1316
|
+
cli
|
|
1317
|
+
.command('logs <deploymentId>', 'Stream logs for a deployment')
|
|
1318
|
+
.option('--follow', 'Follow log output')
|
|
1319
|
+
.option('--lines <n>', z.number().default(100).describe('Number of lines'))
|
|
1320
|
+
.example('# Stream last 200 lines for a deployment')
|
|
1321
|
+
.example('deploy logs dep_123 --lines 200')
|
|
1322
|
+
.example('# Keep following new log lines')
|
|
1323
|
+
.example('deploy logs dep_123 --follow')
|
|
1324
|
+
|
|
1325
|
+
cli.help()
|
|
1326
|
+
cli.parse(['node', 'bin', 'logs', '--help'], { run: false })
|
|
1327
|
+
|
|
1328
|
+
expect(stdout.text).toMatchInlineSnapshot(`
|
|
1329
|
+
"deploy
|
|
1330
|
+
|
|
1331
|
+
|
|
1332
|
+
Usage:
|
|
1333
|
+
$ deploy logs <deploymentId>
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
Options:
|
|
1337
|
+
--follow Follow log output
|
|
1338
|
+
--lines <n> Number of lines (default: 100)
|
|
1339
|
+
-h, --help Display this message
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
Description:
|
|
1343
|
+
Stream logs for a deployment
|
|
1344
|
+
|
|
1345
|
+
|
|
1346
|
+
Examples:
|
|
1347
|
+
# Stream last 200 lines for a deployment
|
|
1348
|
+
deploy logs dep_123 --lines 200
|
|
1349
|
+
# Keep following new log lines
|
|
1350
|
+
deploy logs dep_123 --follow
|
|
1351
|
+
"
|
|
1352
|
+
`)
|
|
1353
|
+
})
|
|
1354
|
+
|
|
1103
1355
|
test('root help labels default command with cli name and does not duplicate global options', () => {
|
|
1104
1356
|
const stdout = createTestOutputStream()
|
|
1105
1357
|
const cli = goke('deploy', { stdout })
|
|
@@ -1118,14 +1370,18 @@ describe('many commands with root command (empty string)', () => {
|
|
|
1118
1370
|
expect(stdout.text).toMatchInlineSnapshot(`
|
|
1119
1371
|
"deploy
|
|
1120
1372
|
|
|
1373
|
+
|
|
1121
1374
|
Usage:
|
|
1122
1375
|
$ deploy [options]
|
|
1123
1376
|
|
|
1377
|
+
|
|
1124
1378
|
Commands:
|
|
1125
1379
|
deploy Deploy the current project
|
|
1126
1380
|
|
|
1381
|
+
|
|
1127
1382
|
status Show deployment status
|
|
1128
1383
|
|
|
1384
|
+
|
|
1129
1385
|
Options:
|
|
1130
1386
|
--env <env> Target environment
|
|
1131
1387
|
--dry-run Preview without deploying
|
|
@@ -1156,26 +1412,32 @@ describe('many commands with root command (empty string)', () => {
|
|
|
1156
1412
|
expect(stdout.text).toMatchInlineSnapshot(`
|
|
1157
1413
|
"mycli
|
|
1158
1414
|
|
|
1415
|
+
|
|
1159
1416
|
Usage:
|
|
1160
1417
|
$ mycli <command> [options]
|
|
1161
1418
|
|
|
1419
|
+
|
|
1162
1420
|
Commands:
|
|
1163
1421
|
notion-search Perform a semantic search over
|
|
1164
1422
|
Notion workspace content and
|
|
1165
1423
|
connected integrations with
|
|
1166
1424
|
advanced filtering options, date
|
|
1167
1425
|
filters, and creator filters.
|
|
1426
|
+
|
|
1168
1427
|
--query <query> Natural language query text to
|
|
1169
1428
|
search for
|
|
1170
1429
|
--limit [limit] Maximum number of results to return
|
|
1171
1430
|
(default: 10)
|
|
1172
1431
|
|
|
1432
|
+
|
|
1173
1433
|
notion-fetch Retrieve a Notion page or database
|
|
1174
1434
|
by URL or ID and render the result
|
|
1175
1435
|
in enhanced markdown format for
|
|
1176
1436
|
terminal output.
|
|
1437
|
+
|
|
1177
1438
|
--id <id> Notion URL or UUID to fetch
|
|
1178
1439
|
|
|
1440
|
+
|
|
1179
1441
|
Options:
|
|
1180
1442
|
-h, --help Display this message
|
|
1181
1443
|
"
|
|
@@ -1197,20 +1459,28 @@ describe('many commands with root command (empty string)', () => {
|
|
|
1197
1459
|
expect(stdout.text).toMatchInlineSnapshot(`
|
|
1198
1460
|
"gtui
|
|
1199
1461
|
|
|
1462
|
+
|
|
1200
1463
|
Usage:
|
|
1201
1464
|
$ gtui <command> [options]
|
|
1202
1465
|
|
|
1466
|
+
|
|
1203
1467
|
Commands:
|
|
1204
1468
|
auth login Authenticate with Google (opens browser)
|
|
1205
1469
|
|
|
1470
|
+
|
|
1206
1471
|
auth logout Remove stored credentials
|
|
1472
|
+
|
|
1207
1473
|
--force Skip confirmation
|
|
1208
1474
|
|
|
1475
|
+
|
|
1209
1476
|
mail list List email threads
|
|
1477
|
+
|
|
1210
1478
|
--folder [folder] Folder to list
|
|
1211
1479
|
|
|
1480
|
+
|
|
1212
1481
|
attachment get <messageId> <attachmentId> Download an attachment
|
|
1213
1482
|
|
|
1483
|
+
|
|
1214
1484
|
Options:
|
|
1215
1485
|
-h, --help Display this message
|
|
1216
1486
|
"
|
|
@@ -1257,14 +1527,18 @@ describe('many commands with root command (empty string)', () => {
|
|
|
1257
1527
|
expect(stdout.text).toMatchInlineSnapshot(`
|
|
1258
1528
|
"mycli
|
|
1259
1529
|
|
|
1530
|
+
|
|
1260
1531
|
Usage:
|
|
1261
1532
|
$ mycli <command> [options]
|
|
1262
1533
|
|
|
1534
|
+
|
|
1263
1535
|
Commands:
|
|
1264
1536
|
notion-search Perform a semantic search over Notion workspace content and connected integrations with advanced filtering options, date filters, and creator filters.
|
|
1537
|
+
|
|
1265
1538
|
--query <query> Natural language query text to search for
|
|
1266
1539
|
--limit [limit] Maximum number of results to return (default: 10)
|
|
1267
1540
|
|
|
1541
|
+
|
|
1268
1542
|
Options:
|
|
1269
1543
|
-h, --help Display this message
|
|
1270
1544
|
"
|
|
@@ -1470,4 +1744,133 @@ describe('schema description and default extraction', () => {
|
|
|
1470
1744
|
|
|
1471
1745
|
expect(stdout.text).toContain('(default: 3000)')
|
|
1472
1746
|
})
|
|
1747
|
+
|
|
1748
|
+
test('deprecated options are hidden from help output', () => {
|
|
1749
|
+
const stdout = createTestOutputStream()
|
|
1750
|
+
const cli = goke('mycli', { stdout })
|
|
1751
|
+
|
|
1752
|
+
cli
|
|
1753
|
+
.command('serve', 'Start server')
|
|
1754
|
+
.option('--old <value>', z.string().meta({ deprecated: true, description: 'Old option' }))
|
|
1755
|
+
.option('--new <value>', z.string().describe('Normal option'))
|
|
1756
|
+
|
|
1757
|
+
cli.help()
|
|
1758
|
+
cli.parse(['node', 'bin', 'serve', '--help'], { run: false })
|
|
1759
|
+
|
|
1760
|
+
// Normal option should be visible
|
|
1761
|
+
expect(stdout.text).toContain('--new')
|
|
1762
|
+
expect(stdout.text).toContain('Normal option')
|
|
1763
|
+
// Deprecated option should be hidden
|
|
1764
|
+
expect(stdout.text).not.toContain('--old')
|
|
1765
|
+
expect(stdout.text).not.toContain('Old option')
|
|
1766
|
+
})
|
|
1767
|
+
|
|
1768
|
+
test('deprecated option still works for parsing (just hidden from help)', () => {
|
|
1769
|
+
const cli = gokeTestable('mycli')
|
|
1770
|
+
|
|
1771
|
+
let result: any = {}
|
|
1772
|
+
cli
|
|
1773
|
+
.command('serve', 'Start server')
|
|
1774
|
+
.option('--old <value>', z.string().meta({ deprecated: true, description: 'Old option' }))
|
|
1775
|
+
.action((options) => { result = options })
|
|
1776
|
+
|
|
1777
|
+
cli.parse(['node', 'bin', 'serve', '--old', 'legacy-value'])
|
|
1778
|
+
|
|
1779
|
+
// Deprecated option should still be parsed and usable
|
|
1780
|
+
expect(result.old).toBe('legacy-value')
|
|
1781
|
+
})
|
|
1782
|
+
|
|
1783
|
+
test('deprecated options hidden from global help', () => {
|
|
1784
|
+
const stdout = createTestOutputStream()
|
|
1785
|
+
const cli = goke('mycli', { stdout })
|
|
1786
|
+
|
|
1787
|
+
cli.option('--legacy [value]', z.string().meta({ deprecated: true, description: 'Deprecated global' }))
|
|
1788
|
+
cli.option('--current [value]', z.string().describe('Current option'))
|
|
1789
|
+
|
|
1790
|
+
cli.help()
|
|
1791
|
+
cli.parse(['node', 'bin', '--help'], { run: false })
|
|
1792
|
+
|
|
1793
|
+
expect(stdout.text).toContain('--current')
|
|
1794
|
+
expect(stdout.text).toContain('Current option')
|
|
1795
|
+
expect(stdout.text).not.toContain('--legacy')
|
|
1796
|
+
expect(stdout.text).not.toContain('Deprecated global')
|
|
1797
|
+
})
|
|
1798
|
+
})
|
|
1799
|
+
|
|
1800
|
+
describe('helpText()', () => {
|
|
1801
|
+
test('returns help string without printing', () => {
|
|
1802
|
+
const stdout = createTestOutputStream()
|
|
1803
|
+
const cli = goke('mycli', { stdout })
|
|
1804
|
+
|
|
1805
|
+
cli.command('serve', 'Start server')
|
|
1806
|
+
cli.option('--port <port>', 'Port number')
|
|
1807
|
+
cli.help()
|
|
1808
|
+
// parse a known command so help is not auto-triggered
|
|
1809
|
+
cli.parse(['node', 'bin', 'serve'], { run: false })
|
|
1810
|
+
|
|
1811
|
+
// reset stdout after parse
|
|
1812
|
+
stdout.lines.length = 0
|
|
1813
|
+
|
|
1814
|
+
const text = stripAnsi(cli.helpText())
|
|
1815
|
+
|
|
1816
|
+
expect(text).toContain('mycli')
|
|
1817
|
+
expect(text).toContain('serve')
|
|
1818
|
+
expect(text).toContain('Start server')
|
|
1819
|
+
expect(text).toContain('--port')
|
|
1820
|
+
// helpText() does not print to stdout
|
|
1821
|
+
expect(stdout.text).toBe('')
|
|
1822
|
+
})
|
|
1823
|
+
|
|
1824
|
+
test('returns same content as outputHelp', () => {
|
|
1825
|
+
const stdout = createTestOutputStream()
|
|
1826
|
+
const cli = goke('mycli', { stdout })
|
|
1827
|
+
|
|
1828
|
+
cli.command('build', 'Build project')
|
|
1829
|
+
cli.option('--watch [watch]', 'Watch mode')
|
|
1830
|
+
cli.help()
|
|
1831
|
+
// parse a known command so help is not auto-triggered
|
|
1832
|
+
cli.parse(['node', 'bin', 'build'], { run: false })
|
|
1833
|
+
|
|
1834
|
+
// reset stdout after parse
|
|
1835
|
+
stdout.lines.length = 0
|
|
1836
|
+
|
|
1837
|
+
const helpTextResult = stripAnsi(cli.helpText())
|
|
1838
|
+
cli.outputHelp()
|
|
1839
|
+
// outputHelp adds a trailing newline via console.log
|
|
1840
|
+
const outputHelpResult = stdout.text.replace(/\n$/, '')
|
|
1841
|
+
|
|
1842
|
+
expect(helpTextResult).toBe(outputHelpResult)
|
|
1843
|
+
})
|
|
1844
|
+
|
|
1845
|
+
test('returns subcommand help when command is matched', () => {
|
|
1846
|
+
const cli = goke('mycli')
|
|
1847
|
+
|
|
1848
|
+
cli.command('deploy <env>', 'Deploy to environment')
|
|
1849
|
+
.option('--force', 'Force deploy')
|
|
1850
|
+
|
|
1851
|
+
cli.help()
|
|
1852
|
+
cli.parse(['node', 'bin', 'deploy', '--help'], { run: false })
|
|
1853
|
+
|
|
1854
|
+
const text = stripAnsi(cli.helpText())
|
|
1855
|
+
|
|
1856
|
+
expect(text).toContain('deploy')
|
|
1857
|
+
expect(text).toContain('--force')
|
|
1858
|
+
expect(text).toContain('Force deploy')
|
|
1859
|
+
})
|
|
1860
|
+
|
|
1861
|
+
test('works without calling parse', () => {
|
|
1862
|
+
const cli = goke('mycli')
|
|
1863
|
+
|
|
1864
|
+
cli.command('test', 'Run tests')
|
|
1865
|
+
cli.option('--coverage', 'Enable coverage')
|
|
1866
|
+
cli.help()
|
|
1867
|
+
|
|
1868
|
+
// helpText() works even without parse
|
|
1869
|
+
const text = stripAnsi(cli.helpText())
|
|
1870
|
+
|
|
1871
|
+
expect(text).toContain('mycli')
|
|
1872
|
+
expect(text).toContain('test')
|
|
1873
|
+
expect(text).toContain('Run tests')
|
|
1874
|
+
expect(text).toContain('--coverage')
|
|
1875
|
+
})
|
|
1473
1876
|
})
|