bbk-cli 1.1.2 → 1.2.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 (39) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +113 -132
  4. package/dist/cli/wrapper.d.ts +0 -1
  5. package/dist/cli/wrapper.d.ts.map +1 -1
  6. package/dist/cli/wrapper.js +19 -54
  7. package/dist/cli/wrapper.js.map +1 -1
  8. package/dist/commands/helpers.js +1 -1
  9. package/dist/commands/runner.d.ts.map +1 -1
  10. package/dist/commands/runner.js +22 -18
  11. package/dist/commands/runner.js.map +1 -1
  12. package/dist/config/constants.d.ts.map +1 -1
  13. package/dist/config/constants.js +37 -52
  14. package/dist/config/constants.js.map +1 -1
  15. package/dist/utils/arg-parser.d.ts.map +1 -1
  16. package/dist/utils/arg-parser.js +23 -8
  17. package/dist/utils/arg-parser.js.map +1 -1
  18. package/dist/utils/bitbucket-client.d.ts +24 -37
  19. package/dist/utils/bitbucket-client.d.ts.map +1 -1
  20. package/dist/utils/bitbucket-client.js +38 -52
  21. package/dist/utils/bitbucket-client.js.map +1 -1
  22. package/dist/utils/bitbucket-utils.d.ts +48 -68
  23. package/dist/utils/bitbucket-utils.d.ts.map +1 -1
  24. package/dist/utils/bitbucket-utils.js +100 -125
  25. package/dist/utils/bitbucket-utils.js.map +1 -1
  26. package/dist/utils/config-loader.d.ts +10 -29
  27. package/dist/utils/config-loader.d.ts.map +1 -1
  28. package/dist/utils/config-loader.js +277 -51
  29. package/dist/utils/config-loader.js.map +1 -1
  30. package/dist/utils/index.d.ts +1 -1
  31. package/dist/utils/index.d.ts.map +1 -1
  32. package/dist/utils/index.js +1 -1
  33. package/dist/utils/index.js.map +1 -1
  34. package/package.json +1 -3
  35. package/tests/integration/cli-integration.test.ts +96 -217
  36. package/tests/unit/cli/wrapper.test.ts +28 -137
  37. package/tests/unit/commands/runner.test.ts +69 -197
  38. package/tests/unit/utils/arg-parser.test.ts +53 -4
  39. package/tests/unit/utils/config-loader.test.ts +441 -106
@@ -1,4 +1,4 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest';
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
2
 
3
3
  import { wrapper } from '../../../src/cli/wrapper.js';
4
4
 
@@ -64,6 +64,13 @@ vi.mock('../../../src/utils/index.js', () => ({
64
64
 
65
65
  const originalEnv = process.env;
66
66
 
67
+ const mockConfig = {
68
+ email: 'user@example.com',
69
+ apiToken: 'test_token',
70
+ defaultWorkspace: 'myworkspace',
71
+ defaultFormat: 'json' as const,
72
+ };
73
+
67
74
  describe('cli/wrapper', () => {
68
75
  beforeEach(() => {
69
76
  vi.clearAllMocks();
@@ -103,11 +110,7 @@ describe('cli/wrapper', () => {
103
110
  describe('connect', () => {
104
111
  it('should load config successfully', async () => {
105
112
  const { loadConfig } = await import('../../../src/utils/index.js');
106
- vi.mocked(loadConfig).mockReturnValue({
107
- profiles: { cloud: { email: 'test@test.com', apiToken: 'token123' } },
108
- defaultProfile: 'cloud',
109
- defaultFormat: 'json',
110
- });
113
+ vi.mocked(loadConfig).mockReturnValue(mockConfig);
111
114
 
112
115
  const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
113
116
 
@@ -119,22 +122,16 @@ describe('cli/wrapper', () => {
119
122
  consoleLogSpy.mockRestore();
120
123
  });
121
124
 
122
- it('should set default profile and format', async () => {
125
+ it('should set default format', async () => {
123
126
  const { loadConfig } = await import('../../../src/utils/index.js');
124
- vi.mocked(loadConfig).mockReturnValue({
125
- profiles: { cloud: { email: 'test@test.com', apiToken: 'token123' } },
126
- defaultProfile: 'cloud',
127
- defaultFormat: 'toon',
128
- });
127
+ vi.mocked(loadConfig).mockReturnValue(mockConfig);
129
128
 
130
129
  const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
131
130
 
132
131
  await cli.connect();
133
132
 
134
133
  // @ts-expect-error - accessing private property for testing
135
- expect(cli.currentProfile).toBe('cloud');
136
- // @ts-expect-error - accessing private property for testing
137
- expect(cli.currentFormat).toBe('toon');
134
+ expect(cli.currentFormat).toBe('json');
138
135
 
139
136
  consoleLogSpy.mockRestore();
140
137
  });
@@ -156,60 +153,18 @@ describe('cli/wrapper', () => {
156
153
  // Expected
157
154
  }
158
155
 
159
- expect(consoleErrorSpy).toHaveBeenCalledWith('Failed to load configuration:', 'Config file not found');
160
- expect(consoleErrorSpy).toHaveBeenCalledWith('\nMake sure:');
161
- expect(consoleErrorSpy).toHaveBeenCalledWith('1. .claude/bitbucket-config.local.md exists');
156
+ expect(consoleErrorSpy).toHaveBeenCalledWith('Config file not found');
162
157
  expect(exitSpy).toHaveBeenCalledWith(1);
163
158
 
164
159
  consoleErrorSpy.mockRestore();
165
160
  exitSpy.mockRestore();
166
161
  });
167
-
168
- it('should use CLAUDE_PROJECT_ROOT if set', async () => {
169
- process.env.CLAUDE_PROJECT_ROOT = '/custom/root';
170
- const { loadConfig } = await import('../../../src/utils/index.js');
171
- vi.mocked(loadConfig).mockReturnValue({
172
- profiles: { cloud: { email: 'test@test.com', apiToken: 'token123' } },
173
- defaultProfile: 'cloud',
174
- defaultFormat: 'json',
175
- });
176
-
177
- const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
178
-
179
- await cli.connect();
180
-
181
- expect(loadConfig).toHaveBeenCalledWith('/custom/root');
182
-
183
- consoleLogSpy.mockRestore();
184
- });
185
-
186
- it('should use process.cwd() if CLAUDE_PROJECT_ROOT not set', async () => {
187
- delete process.env.CLAUDE_PROJECT_ROOT;
188
- const { loadConfig } = await import('../../../src/utils/index.js');
189
- vi.mocked(loadConfig).mockReturnValue({
190
- profiles: { cloud: { email: 'test@test.com', apiToken: 'token123' } },
191
- defaultProfile: 'cloud',
192
- defaultFormat: 'json',
193
- });
194
-
195
- const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
196
-
197
- await cli.connect();
198
-
199
- expect(loadConfig).toHaveBeenCalledWith(process.cwd());
200
-
201
- consoleLogSpy.mockRestore();
202
- });
203
162
  });
204
163
 
205
164
  describe('handleCommand', () => {
206
165
  beforeEach(async () => {
207
166
  const { loadConfig } = await import('../../../src/utils/index.js');
208
- vi.mocked(loadConfig).mockReturnValue({
209
- profiles: { cloud: { host: 'https://test.atlassian.net', email: 'test@test.com', apiToken: 'token' } },
210
- defaultProfile: 'cloud',
211
- defaultFormat: 'json',
212
- });
167
+ vi.mocked(loadConfig).mockReturnValue(mockConfig);
213
168
  const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
214
169
  await cli.connect();
215
170
  consoleLogSpy.mockRestore();
@@ -299,28 +254,6 @@ describe('cli/wrapper', () => {
299
254
  consoleSpy.mockRestore();
300
255
  });
301
256
 
302
- it('should switch profile to valid profile', async () => {
303
- const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
304
-
305
- await cli['handleCommand']('profile cloud');
306
-
307
- expect(consoleLogSpy).toHaveBeenCalledWith('Switched to profile: cloud');
308
- expect(mockRlInterface.prompt).toHaveBeenCalled();
309
-
310
- consoleLogSpy.mockRestore();
311
- });
312
-
313
- it('should show error for invalid profile', async () => {
314
- const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
315
-
316
- await cli['handleCommand']('profile nonexistent');
317
-
318
- expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('ERROR:'));
319
- expect(mockRlInterface.prompt).toHaveBeenCalled();
320
-
321
- consoleErrorSpy.mockRestore();
322
- });
323
-
324
257
  it('should switch format to valid format', async () => {
325
258
  const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
326
259
 
@@ -343,18 +276,6 @@ describe('cli/wrapper', () => {
343
276
  consoleErrorSpy.mockRestore();
344
277
  });
345
278
 
346
- it('should list available profiles', async () => {
347
- const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
348
-
349
- await cli['handleCommand']('profiles');
350
-
351
- expect(consoleLogSpy).toHaveBeenCalledWith('\nAvailable profiles:');
352
- expect(consoleLogSpy).toHaveBeenCalledWith('1. cloud (current)');
353
- expect(mockRlInterface.prompt).toHaveBeenCalled();
354
-
355
- consoleLogSpy.mockRestore();
356
- });
357
-
358
279
  it('should show command detail with -h flag', async () => {
359
280
  const { printCommandDetail } = await import('../../../src/commands/index.js');
360
281
 
@@ -392,11 +313,7 @@ describe('cli/wrapper', () => {
392
313
  describe('runCommand', () => {
393
314
  beforeEach(async () => {
394
315
  const { loadConfig } = await import('../../../src/utils/index.js');
395
- vi.mocked(loadConfig).mockReturnValue({
396
- profiles: { cloud: { email: 'test@test.com', apiToken: 'token123' } },
397
- defaultProfile: 'cloud',
398
- defaultFormat: 'json',
399
- });
316
+ vi.mocked(loadConfig).mockReturnValue(mockConfig);
400
317
  const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
401
318
  await cli.connect();
402
319
  consoleLogSpy.mockRestore();
@@ -409,7 +326,7 @@ describe('cli/wrapper', () => {
409
326
 
410
327
  await cli['runCommand']('list-repositories', '{"workspace":"myworkspace"}');
411
328
 
412
- expect(listRepositories).toHaveBeenCalledWith('cloud', 'myworkspace', 'json');
329
+ expect(listRepositories).toHaveBeenCalledWith('myworkspace', 'json');
413
330
 
414
331
  consoleLogSpy.mockRestore();
415
332
  });
@@ -421,7 +338,7 @@ describe('cli/wrapper', () => {
421
338
 
422
339
  await cli['runCommand']('get-repository', '{"workspace":"myworkspace","repoSlug":"my-repo"}');
423
340
 
424
- expect(getRepository).toHaveBeenCalledWith('cloud', 'myworkspace', 'my-repo', 'json');
341
+ expect(getRepository).toHaveBeenCalledWith('myworkspace', 'my-repo', 'json');
425
342
 
426
343
  consoleLogSpy.mockRestore();
427
344
  });
@@ -444,7 +361,7 @@ describe('cli/wrapper', () => {
444
361
 
445
362
  await cli['runCommand']('list-pullrequests', '{"workspace":"myworkspace","repoSlug":"my-repo","state":"OPEN"}');
446
363
 
447
- expect(listPullRequests).toHaveBeenCalledWith('cloud', 'myworkspace', 'my-repo', 'OPEN', 'json');
364
+ expect(listPullRequests).toHaveBeenCalledWith('myworkspace', 'my-repo', 'OPEN', 'json');
448
365
 
449
366
  consoleLogSpy.mockRestore();
450
367
  });
@@ -456,7 +373,7 @@ describe('cli/wrapper', () => {
456
373
 
457
374
  await cli['runCommand']('get-issue', '{"workspace":"myworkspace","repoSlug":"my-repo","issueId":"123"}');
458
375
 
459
- expect(getIssue).toHaveBeenCalledWith('cloud', 'myworkspace', 'my-repo', '123', 'json');
376
+ expect(getIssue).toHaveBeenCalledWith('myworkspace', 'my-repo', '123', 'json');
460
377
 
461
378
  consoleLogSpy.mockRestore();
462
379
  });
@@ -483,7 +400,6 @@ describe('cli/wrapper', () => {
483
400
  );
484
401
 
485
402
  expect(createIssue).toHaveBeenCalledWith(
486
- 'cloud',
487
403
  'myworkspace',
488
404
  'my-repo',
489
405
  'Bug report',
@@ -514,7 +430,7 @@ describe('cli/wrapper', () => {
514
430
 
515
431
  await cli['runCommand']('list-branches', '{"workspace":"myworkspace","repoSlug":"my-repo"}');
516
432
 
517
- expect(listBranches).toHaveBeenCalledWith('cloud', 'myworkspace', 'my-repo', undefined, undefined, 'json');
433
+ expect(listBranches).toHaveBeenCalledWith('myworkspace', 'my-repo', undefined, undefined, 'json');
518
434
 
519
435
  consoleLogSpy.mockRestore();
520
436
  });
@@ -537,7 +453,7 @@ describe('cli/wrapper', () => {
537
453
 
538
454
  await cli['runCommand']('test-connection', '{}');
539
455
 
540
- expect(testConnection).toHaveBeenCalledWith('cloud');
456
+ expect(testConnection).toHaveBeenCalledWith();
541
457
 
542
458
  consoleLogSpy.mockRestore();
543
459
  });
@@ -549,19 +465,7 @@ describe('cli/wrapper', () => {
549
465
 
550
466
  await cli['runCommand']('get-user', '{"userId":"5b10a2844c20165700ede21g"}');
551
467
 
552
- expect(getUser).toHaveBeenCalledWith('cloud', '5b10a2844c20165700ede21g', 'json');
553
-
554
- consoleLogSpy.mockRestore();
555
- });
556
-
557
- it('should use profile from args if provided', async () => {
558
- const { listRepositories } = await import('../../../src/utils/index.js');
559
- vi.mocked(listRepositories).mockResolvedValue({ success: true, result: '{}' });
560
- const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
561
-
562
- await cli['runCommand']('list-repositories', '{"workspace":"myworkspace","profile":"staging"}');
563
-
564
- expect(listRepositories).toHaveBeenCalledWith('staging', 'myworkspace', 'json');
468
+ expect(getUser).toHaveBeenCalledWith('5b10a2844c20165700ede21g', 'json');
565
469
 
566
470
  consoleLogSpy.mockRestore();
567
471
  });
@@ -573,19 +477,19 @@ describe('cli/wrapper', () => {
573
477
 
574
478
  await cli['runCommand']('list-repositories', '{"workspace":"myworkspace","format":"toon"}');
575
479
 
576
- expect(listRepositories).toHaveBeenCalledWith('cloud', 'myworkspace', 'toon');
480
+ expect(listRepositories).toHaveBeenCalledWith('myworkspace', 'toon');
577
481
 
578
482
  consoleLogSpy.mockRestore();
579
483
  });
580
484
 
581
- it('should use current profile and format by default', async () => {
485
+ it('should use current format by default', async () => {
582
486
  const { listRepositories } = await import('../../../src/utils/index.js');
583
487
  vi.mocked(listRepositories).mockResolvedValue({ success: true, result: '{}' });
584
488
  const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
585
489
 
586
490
  await cli['runCommand']('list-repositories', '{"workspace":"myworkspace"}');
587
491
 
588
- expect(listRepositories).toHaveBeenCalledWith('cloud', 'myworkspace', 'json');
492
+ expect(listRepositories).toHaveBeenCalledWith('myworkspace', 'json');
589
493
 
590
494
  consoleLogSpy.mockRestore();
591
495
  });
@@ -643,11 +547,7 @@ describe('cli/wrapper', () => {
643
547
  describe('printHelp', () => {
644
548
  beforeEach(async () => {
645
549
  const { loadConfig } = await import('../../../src/utils/index.js');
646
- vi.mocked(loadConfig).mockReturnValue({
647
- profiles: { cloud: { email: 'test@test.com', apiToken: 'token123' } },
648
- defaultProfile: 'cloud',
649
- defaultFormat: 'json',
650
- });
550
+ vi.mocked(loadConfig).mockReturnValue(mockConfig);
651
551
  const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
652
552
  await cli.connect();
653
553
  consoleLogSpy.mockRestore();
@@ -659,7 +559,6 @@ describe('cli/wrapper', () => {
659
559
  cli['printHelp']();
660
560
 
661
561
  expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Bitbucket CLI v0.0.0'));
662
- expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Profile: cloud'));
663
562
  expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Format: json'));
664
563
 
665
564
  consoleLogSpy.mockRestore();
@@ -669,11 +568,7 @@ describe('cli/wrapper', () => {
669
568
  describe('start', () => {
670
569
  beforeEach(async () => {
671
570
  const { loadConfig } = await import('../../../src/utils/index.js');
672
- vi.mocked(loadConfig).mockReturnValue({
673
- profiles: { cloud: { email: 'test@test.com', apiToken: 'token123' } },
674
- defaultProfile: 'cloud',
675
- defaultFormat: 'json',
676
- });
571
+ vi.mocked(loadConfig).mockReturnValue(mockConfig);
677
572
  });
678
573
 
679
574
  it('should setup readline event handlers', async () => {
@@ -699,11 +594,7 @@ describe('cli/wrapper', () => {
699
594
  describe('disconnect', () => {
700
595
  beforeEach(async () => {
701
596
  const { loadConfig } = await import('../../../src/utils/index.js');
702
- vi.mocked(loadConfig).mockReturnValue({
703
- profiles: { cloud: { email: 'test@test.com', apiToken: 'token123' } },
704
- defaultProfile: 'cloud',
705
- defaultFormat: 'json',
706
- });
597
+ vi.mocked(loadConfig).mockReturnValue(mockConfig);
707
598
  });
708
599
 
709
600
  it('should clear clients and close readline', async () => {