borp 0.19.0 → 0.20.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/CLAUDE.md +61 -0
- package/borp.js +79 -31
- package/package.json +3 -3
- package/test/cli.test.js +222 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
Borp is a TypeScript-aware test runner for `node:test` with built-in code coverage support via c8. It's self-hosted and uses ESM modules throughout.
|
|
8
|
+
|
|
9
|
+
## Development Commands
|
|
10
|
+
|
|
11
|
+
### Primary Commands
|
|
12
|
+
- `npm test` - Run complete test suite (clean, lint, and unit tests)
|
|
13
|
+
- `npm run unit` - Run unit tests with coverage, excluding fixtures
|
|
14
|
+
- `npm run lint` - Run standard linter with snazzy formatter
|
|
15
|
+
- `npm run clean` - Remove build artifacts and test directories
|
|
16
|
+
|
|
17
|
+
### Running Individual Tests
|
|
18
|
+
- Use borp directly: `node borp.js [options] [test-files]`
|
|
19
|
+
- With coverage: `node borp.js --coverage`
|
|
20
|
+
- Single test file: `node borp.js test/basic.test.js`
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
|
|
24
|
+
### Core Structure
|
|
25
|
+
- `borp.js` - Main CLI entry point with argument parsing and orchestration
|
|
26
|
+
- `lib/run.js` - Core test runner with TypeScript compilation support
|
|
27
|
+
- `lib/conf.js` - Configuration file loading (`.borp.yaml` or `.borp.yml`)
|
|
28
|
+
|
|
29
|
+
### Key Features
|
|
30
|
+
- Automatic TypeScript compilation detection via `tsconfig.json`
|
|
31
|
+
- Multiple reporter support (spec, tap, dot, junit, github)
|
|
32
|
+
- Code coverage via c8 with customizable thresholds
|
|
33
|
+
- Watch mode for development
|
|
34
|
+
- Post-compilation hooks
|
|
35
|
+
- Configuration file support
|
|
36
|
+
|
|
37
|
+
### Test Structure
|
|
38
|
+
- Tests use `node:test` with `@matteo.collina/tspl` for planning
|
|
39
|
+
- Test files follow `*.test.{js|ts}` pattern
|
|
40
|
+
- Fixtures in `fixtures/` directory demonstrate various scenarios
|
|
41
|
+
- Coverage excludes test files and fixtures by default
|
|
42
|
+
|
|
43
|
+
### TypeScript Support
|
|
44
|
+
- Automatically compiles TypeScript when `tsconfig.json` found
|
|
45
|
+
- Supports both ESM and CJS module formats
|
|
46
|
+
- Source map support for debugging
|
|
47
|
+
- Incremental compilation for performance
|
|
48
|
+
|
|
49
|
+
## Configuration
|
|
50
|
+
|
|
51
|
+
### CLI Options
|
|
52
|
+
- Coverage: `--coverage` or `-C`
|
|
53
|
+
- Concurrency: `--concurrency` or `-c` (defaults to CPU count - 1)
|
|
54
|
+
- Timeout: `--timeout` or `-t` (default 30s)
|
|
55
|
+
- Watch: `--watch` or `-w`
|
|
56
|
+
- Reporter: `--reporter` or `-r`
|
|
57
|
+
|
|
58
|
+
### Config File
|
|
59
|
+
Supports `.borp.yaml`/`.borp.yml` with:
|
|
60
|
+
- `files`: Array of test file globs
|
|
61
|
+
- `reporters`: Array of reporter configurations
|
package/borp.js
CHANGED
|
@@ -23,45 +23,93 @@ process.on('unhandledRejection', (err) => {
|
|
|
23
23
|
process.exit(1)
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
+
function showHelp () {
|
|
27
|
+
console.log(`Usage: borp [options] [files...]
|
|
28
|
+
|
|
29
|
+
Options:
|
|
30
|
+
-h, --help Show this help message
|
|
31
|
+
-o, --only Only run tests with the 'only' option set
|
|
32
|
+
-w, --watch Re-run tests on changes
|
|
33
|
+
-p, --pattern <pattern> Run tests matching the given glob pattern
|
|
34
|
+
-c, --concurrency <num> Set number of concurrent tests (default: ${os.availableParallelism() - 1 || 1})
|
|
35
|
+
-C, --coverage Enable code coverage
|
|
36
|
+
-t, --timeout <ms> Set test timeout in milliseconds (default: 30000)
|
|
37
|
+
--no-timeout Disable test timeout
|
|
38
|
+
-X, --coverage-exclude Exclude patterns from coverage (can be used multiple times)
|
|
39
|
+
-i, --ignore <pattern> Ignore glob pattern (can be used multiple times)
|
|
40
|
+
--expose-gc Expose the gc() function to tests
|
|
41
|
+
-T, --no-typescript Disable automatic TypeScript compilation
|
|
42
|
+
-P, --post-compile <file> Execute file after TypeScript compilation
|
|
43
|
+
-r, --reporter <name> Set reporter (can be used multiple times, default: spec)
|
|
44
|
+
--check-coverage Enable coverage threshold checking
|
|
45
|
+
--lines <threshold> Set lines coverage threshold (default: 100)
|
|
46
|
+
--branches <threshold> Set branches coverage threshold (default: 100)
|
|
47
|
+
--functions <threshold> Set functions coverage threshold (default: 100)
|
|
48
|
+
--statements <threshold> Set statements coverage threshold (default: 100)
|
|
49
|
+
|
|
50
|
+
Examples:
|
|
51
|
+
borp # Run all tests
|
|
52
|
+
borp --coverage # Run tests with coverage
|
|
53
|
+
borp --watch # Run tests in watch mode
|
|
54
|
+
borp test/specific.test.js # Run specific test file
|
|
55
|
+
borp --reporter tap --reporter gh # Use multiple reporters`)
|
|
56
|
+
}
|
|
57
|
+
|
|
26
58
|
const foundConfig = await loadConfig()
|
|
27
59
|
if (foundConfig.length > 0) {
|
|
28
60
|
Array.prototype.push.apply(process.argv, foundConfig)
|
|
29
61
|
}
|
|
30
62
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
default: ['spec'],
|
|
51
|
-
multiple: true
|
|
52
|
-
},
|
|
53
|
-
'check-coverage': { type: 'boolean' },
|
|
54
|
-
lines: { type: 'string', default: '100' },
|
|
55
|
-
branches: { type: 'string', default: '100' },
|
|
56
|
-
functions: { type: 'string', default: '100' },
|
|
57
|
-
statements: { type: 'string', default: '100' }
|
|
63
|
+
const optionsConfig = {
|
|
64
|
+
only: { type: 'boolean', short: 'o' },
|
|
65
|
+
watch: { type: 'boolean', short: 'w' },
|
|
66
|
+
pattern: { type: 'string', short: 'p' },
|
|
67
|
+
concurrency: { type: 'string', short: 'c', default: (os.availableParallelism() - 1 || 1) + '' },
|
|
68
|
+
coverage: { type: 'boolean', short: 'C' },
|
|
69
|
+
timeout: { type: 'string', short: 't', default: '30000' },
|
|
70
|
+
'no-timeout': { type: 'boolean' },
|
|
71
|
+
'coverage-exclude': { type: 'string', short: 'X', multiple: true },
|
|
72
|
+
ignore: { type: 'string', short: 'i', multiple: true },
|
|
73
|
+
'expose-gc': { type: 'boolean' },
|
|
74
|
+
help: { type: 'boolean', short: 'h' },
|
|
75
|
+
'no-typescript': { type: 'boolean', short: 'T' },
|
|
76
|
+
'post-compile': { type: 'string', short: 'P' },
|
|
77
|
+
reporter: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
short: 'r',
|
|
80
|
+
default: ['spec'],
|
|
81
|
+
multiple: true
|
|
58
82
|
},
|
|
59
|
-
|
|
60
|
-
}
|
|
83
|
+
'check-coverage': { type: 'boolean' },
|
|
84
|
+
lines: { type: 'string', default: '100' },
|
|
85
|
+
branches: { type: 'string', default: '100' },
|
|
86
|
+
functions: { type: 'string', default: '100' },
|
|
87
|
+
statements: { type: 'string', default: '100' }
|
|
88
|
+
}
|
|
61
89
|
|
|
62
|
-
|
|
90
|
+
let args
|
|
91
|
+
try {
|
|
92
|
+
args = parseArgs({
|
|
93
|
+
args: process.argv.slice(2),
|
|
94
|
+
options: optionsConfig,
|
|
95
|
+
allowPositionals: true
|
|
96
|
+
})
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (error.code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION') {
|
|
99
|
+
console.error(`Error: ${error.message}\n`)
|
|
100
|
+
// Send help to stderr when showing error
|
|
101
|
+
const originalConsoleLog = console.log
|
|
102
|
+
console.log = console.error
|
|
103
|
+
showHelp()
|
|
104
|
+
console.log = originalConsoleLog
|
|
105
|
+
process.exit(1)
|
|
106
|
+
}
|
|
107
|
+
throw error
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* c8 ignore next 4 */
|
|
63
111
|
if (args.values.help) {
|
|
64
|
-
|
|
112
|
+
showHelp()
|
|
65
113
|
process.exit(0)
|
|
66
114
|
}
|
|
67
115
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "borp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "node:test wrapper with TypeScript support",
|
|
6
6
|
"main": "borp.js",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@matteo.collina/tspl": "^0.1.0",
|
|
25
25
|
"@reporters/silent": "^1.2.4",
|
|
26
|
-
"@sinonjs/fake-timers": "^
|
|
27
|
-
"@types/node": "^
|
|
26
|
+
"@sinonjs/fake-timers": "^14.0.0",
|
|
27
|
+
"@types/node": "^24.0.14",
|
|
28
28
|
"desm": "^1.3.0",
|
|
29
29
|
"semver": "^7.6.3",
|
|
30
30
|
"snazzy": "^9.0.0",
|
package/test/cli.test.js
CHANGED
|
@@ -7,6 +7,15 @@ import path from 'node:path'
|
|
|
7
7
|
|
|
8
8
|
const borp = join(import.meta.url, '..', 'borp.js')
|
|
9
9
|
|
|
10
|
+
// Debug logging for Windows troubleshooting
|
|
11
|
+
console.log('[DEBUG] Environment info:', {
|
|
12
|
+
platform: process.platform,
|
|
13
|
+
nodeVersion: process.version,
|
|
14
|
+
cwd: process.cwd(),
|
|
15
|
+
borpPath: borp,
|
|
16
|
+
__dirname: import.meta.url
|
|
17
|
+
})
|
|
18
|
+
|
|
10
19
|
delete process.env.GITHUB_ACTION
|
|
11
20
|
|
|
12
21
|
test('limit concurrency', async () => {
|
|
@@ -154,3 +163,216 @@ test('Post compile script should be executed when --post-compile is sent with c
|
|
|
154
163
|
|
|
155
164
|
strictEqual(stdout.indexOf('Post compile hook complete') >= 0, true, 'Post compile message should be found in stdout')
|
|
156
165
|
})
|
|
166
|
+
|
|
167
|
+
test('invalid option shows help text', async () => {
|
|
168
|
+
console.log('[DEBUG] Starting invalid option test')
|
|
169
|
+
const testCwd = join(import.meta.url, '..', 'fixtures', 'js-esm')
|
|
170
|
+
console.log('[DEBUG] Test CWD:', testCwd)
|
|
171
|
+
console.log('[DEBUG] Borp path:', borp)
|
|
172
|
+
console.log('[DEBUG] Command:', 'node', [borp, '--invalid-option'])
|
|
173
|
+
|
|
174
|
+
await rejects(async () => {
|
|
175
|
+
console.log('[DEBUG] About to execute execa')
|
|
176
|
+
const startTime = Date.now()
|
|
177
|
+
try {
|
|
178
|
+
const result = await execa('node', [borp, '--invalid-option'], {
|
|
179
|
+
cwd: testCwd,
|
|
180
|
+
timeout: 15000, // 15 second timeout
|
|
181
|
+
windowsHide: process.platform === 'win32'
|
|
182
|
+
})
|
|
183
|
+
console.log('[DEBUG] Unexpected success:', result)
|
|
184
|
+
throw new Error('Expected command to fail')
|
|
185
|
+
} catch (error) {
|
|
186
|
+
const elapsed = Date.now() - startTime
|
|
187
|
+
console.log(`[DEBUG] Execa threw error after ${elapsed}ms:`, {
|
|
188
|
+
exitCode: error.exitCode,
|
|
189
|
+
timedOut: error.timedOut,
|
|
190
|
+
stderr: error.stderr?.substring(0, 200) + '...',
|
|
191
|
+
stdout: error.stdout?.substring(0, 200) + '...',
|
|
192
|
+
message: error.message
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
throw error
|
|
196
|
+
}
|
|
197
|
+
}, (error) => {
|
|
198
|
+
console.log('[DEBUG] In error handler, validating error:', {
|
|
199
|
+
exitCode: error.exitCode,
|
|
200
|
+
stderrLength: error.stderr?.length,
|
|
201
|
+
stdoutLength: error.stdout?.length
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// Should exit with code 1
|
|
205
|
+
console.log('[DEBUG] Checking exit code:', error.exitCode)
|
|
206
|
+
strictEqual(error.exitCode, 1)
|
|
207
|
+
|
|
208
|
+
// Should show error message
|
|
209
|
+
const hasErrorMessage = error.stderr.includes('Error: Unknown option \'--invalid-option\'')
|
|
210
|
+
console.log('[DEBUG] Has error message:', hasErrorMessage)
|
|
211
|
+
console.log('[DEBUG] Stderr content:', error.stderr)
|
|
212
|
+
strictEqual(hasErrorMessage, true, 'Should show error message')
|
|
213
|
+
|
|
214
|
+
// Should show help text
|
|
215
|
+
const hasUsage = error.stderr.includes('Usage: borp [options] [files...]')
|
|
216
|
+
console.log('[DEBUG] Has usage line:', hasUsage)
|
|
217
|
+
strictEqual(hasUsage, true, 'Should show usage line')
|
|
218
|
+
|
|
219
|
+
const hasHelp = error.stderr.includes('--help')
|
|
220
|
+
console.log('[DEBUG] Has help option:', hasHelp)
|
|
221
|
+
strictEqual(hasHelp, true, 'Should show help option')
|
|
222
|
+
|
|
223
|
+
const hasCoverage = error.stderr.includes('--coverage')
|
|
224
|
+
console.log('[DEBUG] Has coverage option:', hasCoverage)
|
|
225
|
+
strictEqual(hasCoverage, true, 'Should show coverage option')
|
|
226
|
+
|
|
227
|
+
const hasExamples = error.stderr.includes('Examples:')
|
|
228
|
+
console.log('[DEBUG] Has examples section:', hasExamples)
|
|
229
|
+
strictEqual(hasExamples, true, 'Should show examples section')
|
|
230
|
+
|
|
231
|
+
console.log('[DEBUG] All assertions passed')
|
|
232
|
+
return true
|
|
233
|
+
})
|
|
234
|
+
console.log('[DEBUG] Test completed successfully')
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
test('multiple invalid options show help text', async () => {
|
|
238
|
+
console.log('[DEBUG] Starting multiple invalid options test')
|
|
239
|
+
const testCwd = join(import.meta.url, '..', 'fixtures', 'js-esm')
|
|
240
|
+
console.log('[DEBUG] Test CWD:', testCwd)
|
|
241
|
+
|
|
242
|
+
await rejects(async () => {
|
|
243
|
+
console.log('[DEBUG] About to execute execa with multiple invalid options')
|
|
244
|
+
const result = await execa('node', [borp, '--foo', '--bar'], {
|
|
245
|
+
cwd: testCwd,
|
|
246
|
+
timeout: 15000,
|
|
247
|
+
windowsHide: false
|
|
248
|
+
})
|
|
249
|
+
console.log('[DEBUG] Unexpected success:', result)
|
|
250
|
+
throw new Error('Expected command to fail')
|
|
251
|
+
}, (error) => {
|
|
252
|
+
console.log('[DEBUG] Multiple options error handler:', {
|
|
253
|
+
exitCode: error.exitCode,
|
|
254
|
+
stderrLength: error.stderr?.length
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
// Should exit with code 1 and show help for first invalid option
|
|
258
|
+
strictEqual(error.exitCode, 1)
|
|
259
|
+
const hasFooError = error.stderr.includes('Error: Unknown option \'--foo\'')
|
|
260
|
+
console.log('[DEBUG] Has foo error:', hasFooError)
|
|
261
|
+
strictEqual(hasFooError, true, 'Should show error for first invalid option')
|
|
262
|
+
|
|
263
|
+
const hasUsage = error.stderr.includes('Usage: borp [options] [files...]')
|
|
264
|
+
console.log('[DEBUG] Has usage in multiple options:', hasUsage)
|
|
265
|
+
strictEqual(hasUsage, true, 'Should show help text')
|
|
266
|
+
return true
|
|
267
|
+
})
|
|
268
|
+
console.log('[DEBUG] Multiple options test completed')
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
test('invalid short option shows help text', async () => {
|
|
272
|
+
console.log('[DEBUG] Starting invalid short option test')
|
|
273
|
+
const testCwd = join(import.meta.url, '..', 'fixtures', 'js-esm')
|
|
274
|
+
console.log('[DEBUG] Test CWD:', testCwd)
|
|
275
|
+
|
|
276
|
+
await rejects(async () => {
|
|
277
|
+
console.log('[DEBUG] About to execute execa with invalid short option')
|
|
278
|
+
const result = await execa('node', [borp, '-z'], {
|
|
279
|
+
cwd: testCwd,
|
|
280
|
+
timeout: 15000,
|
|
281
|
+
windowsHide: process.platform === 'win32'
|
|
282
|
+
})
|
|
283
|
+
console.log('[DEBUG] Unexpected success:', result)
|
|
284
|
+
throw new Error('Expected command to fail')
|
|
285
|
+
}, (error) => {
|
|
286
|
+
console.log('[DEBUG] Short option error handler:', {
|
|
287
|
+
exitCode: error.exitCode,
|
|
288
|
+
stderrLength: error.stderr?.length
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
strictEqual(error.exitCode, 1)
|
|
292
|
+
const hasZError = error.stderr.includes('Error: Unknown option \'-z\'')
|
|
293
|
+
console.log('[DEBUG] Has -z error:', hasZError)
|
|
294
|
+
strictEqual(hasZError, true, 'Should show error message')
|
|
295
|
+
|
|
296
|
+
const hasUsage = error.stderr.includes('Usage: borp [options] [files...]')
|
|
297
|
+
console.log('[DEBUG] Has usage in short option:', hasUsage)
|
|
298
|
+
strictEqual(hasUsage, true, 'Should show help text')
|
|
299
|
+
return true
|
|
300
|
+
})
|
|
301
|
+
console.log('[DEBUG] Short option test completed')
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
test('--help option shows help text and exits successfully', async () => {
|
|
305
|
+
console.log('[DEBUG] Starting --help option test')
|
|
306
|
+
const testCwd = join(import.meta.url, '..', 'fixtures', 'js-esm')
|
|
307
|
+
console.log('[DEBUG] Test CWD:', testCwd)
|
|
308
|
+
|
|
309
|
+
const startTime = Date.now()
|
|
310
|
+
const { stdout, exitCode } = await execa('node', [borp, '--help'], {
|
|
311
|
+
cwd: testCwd,
|
|
312
|
+
timeout: 15000,
|
|
313
|
+
windowsHide: process.platform === 'win32'
|
|
314
|
+
})
|
|
315
|
+
const elapsed = Date.now() - startTime
|
|
316
|
+
|
|
317
|
+
console.log(`[DEBUG] --help completed after ${elapsed}ms:`, {
|
|
318
|
+
exitCode,
|
|
319
|
+
stdoutLength: stdout?.length,
|
|
320
|
+
stdoutPreview: stdout?.substring(0, 100) + '...'
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
strictEqual(exitCode, 0, 'Should exit with code 0')
|
|
324
|
+
|
|
325
|
+
const hasUsage = stdout.includes('Usage: borp [options] [files...]')
|
|
326
|
+
console.log('[DEBUG] --help has usage:', hasUsage)
|
|
327
|
+
strictEqual(hasUsage, true, 'Should show usage line')
|
|
328
|
+
|
|
329
|
+
const hasHelpOption = stdout.includes('--help')
|
|
330
|
+
console.log('[DEBUG] --help has help option:', hasHelpOption)
|
|
331
|
+
strictEqual(hasHelpOption, true, 'Should show help option')
|
|
332
|
+
|
|
333
|
+
const hasCoverage = stdout.includes('--coverage')
|
|
334
|
+
console.log('[DEBUG] --help has coverage:', hasCoverage)
|
|
335
|
+
strictEqual(hasCoverage, true, 'Should show coverage option')
|
|
336
|
+
|
|
337
|
+
const hasExamples = stdout.includes('Examples:')
|
|
338
|
+
console.log('[DEBUG] --help has examples:', hasExamples)
|
|
339
|
+
strictEqual(hasExamples, true, 'Should show examples section')
|
|
340
|
+
|
|
341
|
+
const hasCoverageExample = stdout.includes('borp --coverage')
|
|
342
|
+
console.log('[DEBUG] --help has coverage example:', hasCoverageExample)
|
|
343
|
+
strictEqual(hasCoverageExample, true, 'Should show coverage example')
|
|
344
|
+
|
|
345
|
+
console.log('[DEBUG] --help test completed')
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
test('-h option shows help text and exits successfully', async () => {
|
|
349
|
+
console.log('[DEBUG] Starting -h option test')
|
|
350
|
+
const testCwd = join(import.meta.url, '..', 'fixtures', 'js-esm')
|
|
351
|
+
console.log('[DEBUG] Test CWD:', testCwd)
|
|
352
|
+
|
|
353
|
+
const startTime = Date.now()
|
|
354
|
+
const { stdout, exitCode } = await execa('node', [borp, '-h'], {
|
|
355
|
+
cwd: testCwd,
|
|
356
|
+
timeout: 15000,
|
|
357
|
+
windowsHide: process.platform === 'win32'
|
|
358
|
+
})
|
|
359
|
+
const elapsed = Date.now() - startTime
|
|
360
|
+
|
|
361
|
+
console.log(`[DEBUG] -h completed after ${elapsed}ms:`, {
|
|
362
|
+
exitCode,
|
|
363
|
+
stdoutLength: stdout?.length,
|
|
364
|
+
stdoutPreview: stdout?.substring(0, 100) + '...'
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
strictEqual(exitCode, 0, 'Should exit with code 0')
|
|
368
|
+
|
|
369
|
+
const hasUsage = stdout.includes('Usage: borp [options] [files...]')
|
|
370
|
+
console.log('[DEBUG] -h has usage:', hasUsage)
|
|
371
|
+
strictEqual(hasUsage, true, 'Should show usage line')
|
|
372
|
+
|
|
373
|
+
const hasExamples = stdout.includes('Examples:')
|
|
374
|
+
console.log('[DEBUG] -h has examples:', hasExamples)
|
|
375
|
+
strictEqual(hasExamples, true, 'Should show examples section')
|
|
376
|
+
|
|
377
|
+
console.log('[DEBUG] -h test completed')
|
|
378
|
+
})
|