pnpm-catalog-updates 1.0.2 → 1.1.0

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.
Files changed (51) hide show
  1. package/README.md +15 -0
  2. package/dist/index.js +22031 -10684
  3. package/dist/index.js.map +1 -1
  4. package/package.json +7 -2
  5. package/src/cli/__tests__/commandRegistrar.test.ts +248 -0
  6. package/src/cli/commandRegistrar.ts +785 -0
  7. package/src/cli/commands/__tests__/aiCommand.test.ts +161 -0
  8. package/src/cli/commands/__tests__/analyzeCommand.test.ts +283 -0
  9. package/src/cli/commands/__tests__/checkCommand.test.ts +435 -0
  10. package/src/cli/commands/__tests__/graphCommand.test.ts +312 -0
  11. package/src/cli/commands/__tests__/initCommand.test.ts +317 -0
  12. package/src/cli/commands/__tests__/rollbackCommand.test.ts +400 -0
  13. package/src/cli/commands/__tests__/securityCommand.test.ts +467 -0
  14. package/src/cli/commands/__tests__/themeCommand.test.ts +166 -0
  15. package/src/cli/commands/__tests__/updateCommand.test.ts +720 -0
  16. package/src/cli/commands/__tests__/workspaceCommand.test.ts +286 -0
  17. package/src/cli/commands/aiCommand.ts +163 -0
  18. package/src/cli/commands/analyzeCommand.ts +219 -0
  19. package/src/cli/commands/checkCommand.ts +91 -98
  20. package/src/cli/commands/graphCommand.ts +475 -0
  21. package/src/cli/commands/initCommand.ts +64 -54
  22. package/src/cli/commands/rollbackCommand.ts +334 -0
  23. package/src/cli/commands/securityCommand.ts +165 -100
  24. package/src/cli/commands/themeCommand.ts +148 -0
  25. package/src/cli/commands/updateCommand.ts +215 -263
  26. package/src/cli/commands/workspaceCommand.ts +73 -0
  27. package/src/cli/constants/cliChoices.ts +93 -0
  28. package/src/cli/formatters/__tests__/__snapshots__/outputFormatter.test.ts.snap +557 -0
  29. package/src/cli/formatters/__tests__/ciFormatter.test.ts +526 -0
  30. package/src/cli/formatters/__tests__/outputFormatter.test.ts +448 -0
  31. package/src/cli/formatters/__tests__/progressBar.test.ts +709 -0
  32. package/src/cli/formatters/ciFormatter.ts +964 -0
  33. package/src/cli/formatters/colorUtils.ts +145 -0
  34. package/src/cli/formatters/outputFormatter.ts +615 -332
  35. package/src/cli/formatters/progressBar.ts +43 -52
  36. package/src/cli/formatters/versionFormatter.ts +132 -0
  37. package/src/cli/handlers/aiAnalysisHandler.ts +205 -0
  38. package/src/cli/handlers/changelogHandler.ts +113 -0
  39. package/src/cli/handlers/index.ts +9 -0
  40. package/src/cli/handlers/installHandler.ts +130 -0
  41. package/src/cli/index.ts +175 -726
  42. package/src/cli/interactive/InteractiveOptionsCollector.ts +387 -0
  43. package/src/cli/interactive/interactivePrompts.ts +189 -83
  44. package/src/cli/interactive/optionUtils.ts +89 -0
  45. package/src/cli/themes/colorTheme.ts +43 -16
  46. package/src/cli/utils/cliOutput.ts +118 -0
  47. package/src/cli/utils/commandHelpers.ts +249 -0
  48. package/src/cli/validators/commandValidator.ts +321 -336
  49. package/src/cli/validators/index.ts +37 -2
  50. package/src/cli/options/globalOptions.ts +0 -437
  51. package/src/cli/options/index.ts +0 -5
@@ -0,0 +1,709 @@
1
+ /**
2
+ * ProgressBar Unit Tests
3
+ *
4
+ * Tests for the ProgressBar, MultiStepProgress, PercentageProgressBar,
5
+ * and BatchProgressManager classes.
6
+ */
7
+
8
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
9
+ import {
10
+ BatchProgressManager,
11
+ MultiStepProgress,
12
+ PercentageProgressBar,
13
+ ProgressBar,
14
+ } from '../progressBar.js'
15
+
16
+ // Mock @pcu/utils t() function
17
+ vi.mock('@pcu/utils', () => ({
18
+ t: (key: string) => {
19
+ const translations: Record<string, string> = {
20
+ 'progress.processing': 'Processing...',
21
+ 'progress.completed': 'completed',
22
+ 'progress.failed': 'failed',
23
+ 'progress.success': 'Success',
24
+ 'progress.error': 'Error',
25
+ 'progress.warning': 'Warning',
26
+ 'progress.info': 'Info',
27
+ 'progress.steps': 'Steps',
28
+ 'progress.allStepsCompleted': 'All steps completed',
29
+ 'progress.overallProgress': 'Overall progress',
30
+ }
31
+ return translations[key] || key
32
+ },
33
+ }))
34
+
35
+ // Mock chalk to return plain strings for testing
36
+ vi.mock('chalk', () => {
37
+ const createChalkMock = (): unknown => {
38
+ const handler: ProxyHandler<unknown> = {
39
+ get: (_target, prop) => {
40
+ if (
41
+ prop === 'bold' ||
42
+ prop === 'gray' ||
43
+ prop === 'green' ||
44
+ prop === 'red' ||
45
+ prop === 'yellow' ||
46
+ prop === 'blue' ||
47
+ prop === 'cyan' ||
48
+ prop === 'magenta' ||
49
+ prop === 'white'
50
+ ) {
51
+ return createChalkMock()
52
+ }
53
+ return (text: string) => text
54
+ },
55
+ apply: (_target, _thisArg, args) => args[0],
56
+ }
57
+ return new Proxy(() => {}, handler)
58
+ }
59
+ return { default: createChalkMock() }
60
+ })
61
+
62
+ describe('ProgressBar', () => {
63
+ let consoleLogSpy: ReturnType<typeof vi.spyOn>
64
+ let stdoutWriteSpy: ReturnType<typeof vi.spyOn>
65
+
66
+ beforeEach(() => {
67
+ consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
68
+ stdoutWriteSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true)
69
+ vi.useFakeTimers()
70
+ })
71
+
72
+ afterEach(() => {
73
+ consoleLogSpy.mockRestore()
74
+ stdoutWriteSpy.mockRestore()
75
+ vi.useRealTimers()
76
+ })
77
+
78
+ describe('constructor', () => {
79
+ it('should create with default options', () => {
80
+ const bar = new ProgressBar()
81
+ expect(bar).toBeDefined()
82
+ })
83
+
84
+ it('should create with custom text', () => {
85
+ const bar = new ProgressBar({ text: 'Custom text' })
86
+ expect(bar).toBeDefined()
87
+ })
88
+
89
+ it('should create with custom total', () => {
90
+ const bar = new ProgressBar({ total: 100 })
91
+ expect(bar).toBeDefined()
92
+ })
93
+
94
+ it('should create with custom style', () => {
95
+ const styles = ['default', 'gradient', 'fancy', 'minimal', 'rainbow', 'neon'] as const
96
+ for (const style of styles) {
97
+ const bar = new ProgressBar({ style })
98
+ expect(bar).toBeDefined()
99
+ }
100
+ })
101
+
102
+ it('should create with showSpeed option', () => {
103
+ const bar = new ProgressBar({ showSpeed: false })
104
+ expect(bar).toBeDefined()
105
+ })
106
+ })
107
+
108
+ describe('start', () => {
109
+ it('should start the progress bar', () => {
110
+ const bar = new ProgressBar({ total: 10 })
111
+ bar.start()
112
+ expect(consoleLogSpy).toHaveBeenCalled()
113
+ })
114
+
115
+ it('should start with custom text', () => {
116
+ const bar = new ProgressBar({ total: 10 })
117
+ bar.start('Starting...')
118
+ expect(consoleLogSpy).toHaveBeenCalled()
119
+ })
120
+
121
+ it('should create percentage bar even without total', () => {
122
+ const bar = new ProgressBar()
123
+ bar.start()
124
+ expect(consoleLogSpy).toHaveBeenCalled()
125
+ })
126
+ })
127
+
128
+ describe('update', () => {
129
+ it('should update progress with text', () => {
130
+ const bar = new ProgressBar({ total: 10 })
131
+ bar.start()
132
+ bar.update('Updating...', 5)
133
+ // Update should call stdout.write for progress bar updates
134
+ expect(stdoutWriteSpy).toHaveBeenCalled()
135
+ })
136
+
137
+ it('should update progress with current and total', () => {
138
+ const bar = new ProgressBar({ total: 10 })
139
+ bar.start()
140
+ bar.update('Updating...', 5, 20)
141
+ expect(stdoutWriteSpy).toHaveBeenCalled()
142
+ })
143
+ })
144
+
145
+ describe('increment', () => {
146
+ it('should increment progress by 1', () => {
147
+ const bar = new ProgressBar({ total: 10 })
148
+ bar.start()
149
+ bar.increment()
150
+ expect(stdoutWriteSpy).toHaveBeenCalled()
151
+ })
152
+
153
+ it('should increment progress by custom amount', () => {
154
+ const bar = new ProgressBar({ total: 10 })
155
+ bar.start()
156
+ bar.increment(5)
157
+ expect(stdoutWriteSpy).toHaveBeenCalled()
158
+ })
159
+
160
+ it('should increment progress with text', () => {
161
+ const bar = new ProgressBar({ total: 10 })
162
+ bar.start()
163
+ bar.increment(1, 'Step 1 complete')
164
+ expect(stdoutWriteSpy).toHaveBeenCalled()
165
+ })
166
+ })
167
+
168
+ describe('succeed', () => {
169
+ it('should mark as succeeded', () => {
170
+ const bar = new ProgressBar({ total: 10 })
171
+ bar.start()
172
+ bar.succeed()
173
+ expect(consoleLogSpy).toHaveBeenCalled()
174
+ })
175
+
176
+ it('should mark as succeeded with custom text', () => {
177
+ const bar = new ProgressBar({ total: 10 })
178
+ bar.start()
179
+ bar.succeed('All done!')
180
+ expect(consoleLogSpy).toHaveBeenCalled()
181
+ })
182
+
183
+ it('should not throw when called without starting', () => {
184
+ const bar = new ProgressBar({ total: 10 })
185
+ expect(() => bar.succeed()).not.toThrow()
186
+ })
187
+ })
188
+
189
+ describe('fail', () => {
190
+ it('should mark as failed', () => {
191
+ const bar = new ProgressBar({ total: 10 })
192
+ bar.start()
193
+ bar.fail()
194
+ expect(consoleLogSpy).toHaveBeenCalled()
195
+ })
196
+
197
+ it('should mark as failed with custom text', () => {
198
+ const bar = new ProgressBar({ total: 10 })
199
+ bar.start()
200
+ bar.fail('Something went wrong')
201
+ expect(consoleLogSpy).toHaveBeenCalled()
202
+ })
203
+ })
204
+
205
+ describe('warn', () => {
206
+ it('should mark as warning', () => {
207
+ const bar = new ProgressBar({ total: 10 })
208
+ bar.start()
209
+ bar.warn()
210
+ expect(consoleLogSpy).toHaveBeenCalled()
211
+ })
212
+
213
+ it('should mark as warning with custom text', () => {
214
+ const bar = new ProgressBar({ total: 10 })
215
+ bar.start()
216
+ bar.warn('Be careful')
217
+ expect(consoleLogSpy).toHaveBeenCalled()
218
+ })
219
+ })
220
+
221
+ describe('info', () => {
222
+ it('should mark as info', () => {
223
+ const bar = new ProgressBar({ total: 10 })
224
+ bar.start()
225
+ bar.info()
226
+ expect(consoleLogSpy).toHaveBeenCalled()
227
+ })
228
+
229
+ it('should mark as info with custom text', () => {
230
+ const bar = new ProgressBar({ total: 10 })
231
+ bar.start()
232
+ bar.info('FYI')
233
+ expect(consoleLogSpy).toHaveBeenCalled()
234
+ })
235
+ })
236
+
237
+ describe('stop', () => {
238
+ it('should stop the progress bar', () => {
239
+ const bar = new ProgressBar({ total: 10 })
240
+ bar.start()
241
+ bar.stop()
242
+ // Should not throw
243
+ expect(true).toBe(true)
244
+ })
245
+
246
+ it('should not throw when called without starting', () => {
247
+ const bar = new ProgressBar({ total: 10 })
248
+ expect(() => bar.stop()).not.toThrow()
249
+ })
250
+ })
251
+
252
+ describe('style variations', () => {
253
+ const styles = ['default', 'gradient', 'fancy', 'minimal', 'rainbow', 'neon'] as const
254
+
255
+ for (const style of styles) {
256
+ describe(`${style} style`, () => {
257
+ it('should render success message', () => {
258
+ const bar = new ProgressBar({ style, total: 10 })
259
+ bar.start()
260
+ bar.succeed('Done')
261
+ expect(consoleLogSpy).toHaveBeenCalled()
262
+ })
263
+
264
+ it('should render failure message', () => {
265
+ const bar = new ProgressBar({ style, total: 10 })
266
+ bar.start()
267
+ bar.fail('Failed')
268
+ expect(consoleLogSpy).toHaveBeenCalled()
269
+ })
270
+
271
+ it('should render warning message', () => {
272
+ const bar = new ProgressBar({ style, total: 10 })
273
+ bar.start()
274
+ bar.warn('Warning')
275
+ expect(consoleLogSpy).toHaveBeenCalled()
276
+ })
277
+
278
+ it('should render info message', () => {
279
+ const bar = new ProgressBar({ style, total: 10 })
280
+ bar.start()
281
+ bar.info('Info')
282
+ expect(consoleLogSpy).toHaveBeenCalled()
283
+ })
284
+ })
285
+ }
286
+ })
287
+
288
+ describe('elapsed time formatting', () => {
289
+ it('should format milliseconds', () => {
290
+ const bar = new ProgressBar({ total: 10 })
291
+ bar.start()
292
+ vi.advanceTimersByTime(500)
293
+ bar.succeed()
294
+ expect(consoleLogSpy).toHaveBeenCalled()
295
+ })
296
+
297
+ it('should format seconds', () => {
298
+ const bar = new ProgressBar({ total: 10 })
299
+ bar.start()
300
+ vi.advanceTimersByTime(5000)
301
+ bar.succeed()
302
+ expect(consoleLogSpy).toHaveBeenCalled()
303
+ })
304
+
305
+ it('should format minutes', () => {
306
+ const bar = new ProgressBar({ total: 10 })
307
+ bar.start()
308
+ vi.advanceTimersByTime(65000)
309
+ bar.succeed()
310
+ expect(consoleLogSpy).toHaveBeenCalled()
311
+ })
312
+ })
313
+
314
+ describe('static factory methods', () => {
315
+ it('should create multi-step progress', () => {
316
+ const progress = ProgressBar.createMultiStep(['Step 1', 'Step 2'])
317
+ expect(progress).toBeInstanceOf(MultiStepProgress)
318
+ })
319
+
320
+ it('should create gradient progress bar', () => {
321
+ const bar = ProgressBar.createGradient()
322
+ expect(bar).toBeInstanceOf(ProgressBar)
323
+ })
324
+
325
+ it('should create fancy progress bar', () => {
326
+ const bar = ProgressBar.createFancy()
327
+ expect(bar).toBeInstanceOf(ProgressBar)
328
+ })
329
+
330
+ it('should create minimal progress bar', () => {
331
+ const bar = ProgressBar.createMinimal()
332
+ expect(bar).toBeInstanceOf(ProgressBar)
333
+ })
334
+
335
+ it('should create rainbow progress bar', () => {
336
+ const bar = ProgressBar.createRainbow()
337
+ expect(bar).toBeInstanceOf(ProgressBar)
338
+ })
339
+
340
+ it('should create neon progress bar', () => {
341
+ const bar = ProgressBar.createNeon()
342
+ expect(bar).toBeInstanceOf(ProgressBar)
343
+ })
344
+
345
+ it('should pass options to factory methods', () => {
346
+ const bar = ProgressBar.createGradient({ total: 50, text: 'Custom' })
347
+ expect(bar).toBeInstanceOf(ProgressBar)
348
+ })
349
+ })
350
+ })
351
+
352
+ describe('MultiStepProgress', () => {
353
+ let consoleLogSpy: ReturnType<typeof vi.spyOn>
354
+
355
+ beforeEach(() => {
356
+ consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
357
+ })
358
+
359
+ afterEach(() => {
360
+ consoleLogSpy.mockRestore()
361
+ })
362
+
363
+ describe('constructor', () => {
364
+ it('should create with steps', () => {
365
+ const progress = new MultiStepProgress(['Step 1', 'Step 2', 'Step 3'])
366
+ expect(progress).toBeDefined()
367
+ })
368
+
369
+ it('should create with empty steps', () => {
370
+ const progress = new MultiStepProgress([])
371
+ expect(progress).toBeDefined()
372
+ })
373
+ })
374
+
375
+ describe('start', () => {
376
+ it('should render initial steps', () => {
377
+ const progress = new MultiStepProgress(['Step 1', 'Step 2'])
378
+ progress.start()
379
+ expect(consoleLogSpy).toHaveBeenCalled()
380
+ })
381
+ })
382
+
383
+ describe('next', () => {
384
+ it('should advance to next step', () => {
385
+ const progress = new MultiStepProgress(['Step 1', 'Step 2'])
386
+ progress.start()
387
+ progress.next()
388
+ expect(consoleLogSpy).toHaveBeenCalled()
389
+ })
390
+
391
+ it('should advance with custom text', () => {
392
+ const progress = new MultiStepProgress(['Step 1', 'Step 2'])
393
+ progress.start()
394
+ progress.next('Custom step text')
395
+ expect(consoleLogSpy).toHaveBeenCalled()
396
+ })
397
+
398
+ it('should handle advancing past all steps', () => {
399
+ const progress = new MultiStepProgress(['Step 1'])
400
+ progress.start()
401
+ progress.next()
402
+ progress.next() // Should not throw
403
+ expect(consoleLogSpy).toHaveBeenCalled()
404
+ })
405
+ })
406
+
407
+ describe('complete', () => {
408
+ it('should show completion message', () => {
409
+ const progress = new MultiStepProgress(['Step 1', 'Step 2'])
410
+ progress.start()
411
+ progress.next()
412
+ progress.next()
413
+ progress.complete()
414
+ expect(consoleLogSpy).toHaveBeenCalled()
415
+ })
416
+ })
417
+ })
418
+
419
+ describe('PercentageProgressBar', () => {
420
+ let consoleLogSpy: ReturnType<typeof vi.spyOn>
421
+ let stdoutWriteSpy: ReturnType<typeof vi.spyOn>
422
+
423
+ beforeEach(() => {
424
+ consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
425
+ stdoutWriteSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true)
426
+ vi.useFakeTimers()
427
+ })
428
+
429
+ afterEach(() => {
430
+ consoleLogSpy.mockRestore()
431
+ stdoutWriteSpy.mockRestore()
432
+ vi.useRealTimers()
433
+ })
434
+
435
+ describe('constructor', () => {
436
+ it('should create with default width', () => {
437
+ const bar = new PercentageProgressBar()
438
+ expect(bar).toBeDefined()
439
+ })
440
+
441
+ it('should create with custom width', () => {
442
+ const bar = new PercentageProgressBar(60)
443
+ expect(bar).toBeDefined()
444
+ })
445
+
446
+ it('should create with options', () => {
447
+ const bar = new PercentageProgressBar(40, {
448
+ style: 'gradient',
449
+ showStats: true,
450
+ multiLine: true,
451
+ })
452
+ expect(bar).toBeDefined()
453
+ })
454
+ })
455
+
456
+ describe('start', () => {
457
+ it('should start with total and text', () => {
458
+ const bar = new PercentageProgressBar()
459
+ bar.start(100, 'Processing...')
460
+ expect(consoleLogSpy).toHaveBeenCalled()
461
+ })
462
+ })
463
+
464
+ describe('update', () => {
465
+ it('should update current value', () => {
466
+ const bar = new PercentageProgressBar()
467
+ bar.start(100, 'Processing...')
468
+ bar.update(50)
469
+ expect(stdoutWriteSpy).toHaveBeenCalled()
470
+ })
471
+
472
+ it('should update with text', () => {
473
+ const bar = new PercentageProgressBar()
474
+ bar.start(100, 'Processing...')
475
+ bar.update(50, 'Halfway there')
476
+ expect(stdoutWriteSpy).toHaveBeenCalled()
477
+ })
478
+ })
479
+
480
+ describe('increment', () => {
481
+ it('should increment by 1', () => {
482
+ const bar = new PercentageProgressBar()
483
+ bar.start(100, 'Processing...')
484
+ bar.increment()
485
+ expect(stdoutWriteSpy).toHaveBeenCalled()
486
+ })
487
+
488
+ it('should increment by custom amount', () => {
489
+ const bar = new PercentageProgressBar()
490
+ bar.start(100, 'Processing...')
491
+ bar.increment(10)
492
+ expect(stdoutWriteSpy).toHaveBeenCalled()
493
+ })
494
+
495
+ it('should increment with text', () => {
496
+ const bar = new PercentageProgressBar()
497
+ bar.start(100, 'Processing...')
498
+ bar.increment(1, 'Step complete')
499
+ expect(stdoutWriteSpy).toHaveBeenCalled()
500
+ })
501
+ })
502
+
503
+ describe('complete', () => {
504
+ it('should complete the progress bar', () => {
505
+ const bar = new PercentageProgressBar()
506
+ bar.start(100, 'Processing...')
507
+ bar.update(50)
508
+ bar.complete()
509
+ expect(stdoutWriteSpy).toHaveBeenCalled()
510
+ })
511
+
512
+ it('should complete with custom text', () => {
513
+ const bar = new PercentageProgressBar()
514
+ bar.start(100, 'Processing...')
515
+ bar.complete('All done!')
516
+ expect(stdoutWriteSpy).toHaveBeenCalled()
517
+ })
518
+ })
519
+
520
+ describe('style variations', () => {
521
+ const styles = ['gradient', 'fancy', 'minimal', 'blocks', 'default'] as const
522
+
523
+ for (const style of styles) {
524
+ it(`should render ${style} style`, () => {
525
+ const bar = new PercentageProgressBar(40, { style })
526
+ bar.start(100, 'Processing...')
527
+ bar.update(50)
528
+ expect(stdoutWriteSpy).toHaveBeenCalled()
529
+ })
530
+ }
531
+ })
532
+
533
+ describe('single line mode', () => {
534
+ it('should render in single line mode', () => {
535
+ const bar = new PercentageProgressBar(40, { multiLine: false })
536
+ bar.start(100, 'Processing...')
537
+ bar.update(50)
538
+ expect(stdoutWriteSpy).toHaveBeenCalled()
539
+ })
540
+ })
541
+
542
+ describe('speed calculation', () => {
543
+ it('should show speed when showStats is enabled', () => {
544
+ const bar = new PercentageProgressBar(40, { showStats: true })
545
+ bar.start(100, 'Processing...')
546
+ vi.advanceTimersByTime(1000)
547
+ bar.update(10)
548
+ expect(stdoutWriteSpy).toHaveBeenCalled()
549
+ })
550
+ })
551
+
552
+ describe('percentage coloring', () => {
553
+ it('should color percentage based on progress', () => {
554
+ const bar = new PercentageProgressBar()
555
+ bar.start(100, 'Processing...')
556
+
557
+ // Test different percentage thresholds
558
+ bar.update(10) // < 25%
559
+ bar.update(30) // < 50%
560
+ bar.update(60) // < 75%
561
+ bar.update(80) // < 100%
562
+ bar.update(100) // 100%
563
+
564
+ expect(stdoutWriteSpy).toHaveBeenCalled()
565
+ })
566
+ })
567
+
568
+ describe('static factory methods', () => {
569
+ it('should create gradient bar', () => {
570
+ const bar = PercentageProgressBar.createGradient()
571
+ expect(bar).toBeInstanceOf(PercentageProgressBar)
572
+ })
573
+
574
+ it('should create fancy bar', () => {
575
+ const bar = PercentageProgressBar.createFancy()
576
+ expect(bar).toBeInstanceOf(PercentageProgressBar)
577
+ })
578
+
579
+ it('should create minimal bar', () => {
580
+ const bar = PercentageProgressBar.createMinimal()
581
+ expect(bar).toBeInstanceOf(PercentageProgressBar)
582
+ })
583
+
584
+ it('should create blocks bar', () => {
585
+ const bar = PercentageProgressBar.createBlocks()
586
+ expect(bar).toBeInstanceOf(PercentageProgressBar)
587
+ })
588
+
589
+ it('should create with custom width', () => {
590
+ const bar = PercentageProgressBar.createGradient(60)
591
+ expect(bar).toBeInstanceOf(PercentageProgressBar)
592
+ })
593
+ })
594
+ })
595
+
596
+ describe('BatchProgressManager', () => {
597
+ let consoleLogSpy: ReturnType<typeof vi.spyOn>
598
+
599
+ beforeEach(() => {
600
+ consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
601
+ vi.spyOn(process.stdout, 'write').mockImplementation(() => true)
602
+ })
603
+
604
+ afterEach(() => {
605
+ consoleLogSpy.mockRestore()
606
+ vi.restoreAllMocks()
607
+ })
608
+
609
+ describe('createBar', () => {
610
+ it('should create a progress bar with id', () => {
611
+ const manager = new BatchProgressManager()
612
+ const bar = manager.createBar('test-bar')
613
+ expect(bar).toBeInstanceOf(ProgressBar)
614
+ })
615
+
616
+ it('should create a progress bar with options', () => {
617
+ const manager = new BatchProgressManager()
618
+ const bar = manager.createBar('test-bar', { total: 100, style: 'gradient' })
619
+ expect(bar).toBeInstanceOf(ProgressBar)
620
+ })
621
+ })
622
+
623
+ describe('getBar', () => {
624
+ it('should get existing bar by id', () => {
625
+ const manager = new BatchProgressManager()
626
+ const bar = manager.createBar('test-bar')
627
+ const retrieved = manager.getBar('test-bar')
628
+ expect(retrieved).toBe(bar)
629
+ })
630
+
631
+ it('should return undefined for non-existent bar', () => {
632
+ const manager = new BatchProgressManager()
633
+ const retrieved = manager.getBar('non-existent')
634
+ expect(retrieved).toBeUndefined()
635
+ })
636
+ })
637
+
638
+ describe('setTotal', () => {
639
+ it('should set total operations', () => {
640
+ const manager = new BatchProgressManager()
641
+ manager.setTotal(10)
642
+ // No direct assertion, but should not throw
643
+ expect(true).toBe(true)
644
+ })
645
+ })
646
+
647
+ describe('updateOverall', () => {
648
+ it('should update overall progress', () => {
649
+ const manager = new BatchProgressManager()
650
+ manager.setTotal(10)
651
+ manager.updateOverall('Processing batch...')
652
+ expect(consoleLogSpy).toHaveBeenCalled()
653
+ })
654
+
655
+ it('should calculate percentage correctly', () => {
656
+ const manager = new BatchProgressManager()
657
+ manager.setTotal(10)
658
+ manager.completeOperation()
659
+ manager.completeOperation()
660
+ manager.updateOverall('Processing...')
661
+ expect(consoleLogSpy).toHaveBeenCalled()
662
+ })
663
+ })
664
+
665
+ describe('completeOperation', () => {
666
+ it('should increment completed operations', () => {
667
+ const manager = new BatchProgressManager()
668
+ manager.setTotal(10)
669
+ manager.completeOperation()
670
+ expect(true).toBe(true)
671
+ })
672
+
673
+ it('should update overall with text', () => {
674
+ const manager = new BatchProgressManager()
675
+ manager.setTotal(10)
676
+ manager.completeOperation('Operation 1 complete')
677
+ expect(consoleLogSpy).toHaveBeenCalled()
678
+ })
679
+ })
680
+
681
+ describe('cleanup', () => {
682
+ it('should cleanup all bars', () => {
683
+ const manager = new BatchProgressManager()
684
+ const bar1 = manager.createBar('bar1', { total: 10 })
685
+ const bar2 = manager.createBar('bar2', { total: 10 })
686
+
687
+ bar1.start()
688
+ bar2.start()
689
+
690
+ manager.cleanup()
691
+
692
+ // Should clear the bars map
693
+ expect(manager.getBar('bar1')).toBeUndefined()
694
+ expect(manager.getBar('bar2')).toBeUndefined()
695
+ })
696
+ })
697
+
698
+ describe('multiple bars', () => {
699
+ it('should manage multiple bars independently', () => {
700
+ const manager = new BatchProgressManager()
701
+ const bar1 = manager.createBar('bar1', { total: 10 })
702
+ const bar2 = manager.createBar('bar2', { total: 20 })
703
+
704
+ expect(manager.getBar('bar1')).toBe(bar1)
705
+ expect(manager.getBar('bar2')).toBe(bar2)
706
+ expect(bar1).not.toBe(bar2)
707
+ })
708
+ })
709
+ })