Skip to content

Commit 20ced79

Browse files
semsem
authored andcommitted
Fix import paths and mock issues in init.test.ts
- Update import paths to match actual service locations - Fix AIIntelligenceService mock structure - Update ErrorRecoveryService mock to use handleError method - Fix inquirer mock type casting issues - Update ProgressTracker.completeOperation assertions to match actual API
1 parent 4ea58aa commit 20ced79

File tree

1 file changed

+73
-80
lines changed

1 file changed

+73
-80
lines changed

src/cli/commands/__tests__/init.test.ts

Lines changed: 73 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,39 @@
11
import { Command } from 'commander';
22
import { initCommand } from '../init';
3-
import { AIService } from '../../services/aiService';
4-
import { ProgressTracker } from '../../services/progressTracker';
5-
import { ErrorRecoveryService } from '../../services/errorRecovery';
3+
import { AIIntelligenceService } from '../../../services/aiIntelligenceService';
4+
import { ProgressTracker } from '../../../services/progressTracker';
5+
import { ErrorRecoveryService } from '../../../services/errorRecoveryService';
66
import * as fs from 'fs-extra';
77
import * as path from 'path';
88
import { tmpdir } from 'os';
99

1010
// Mock dependencies
11-
jest.mock('../../services/aiService');
12-
jest.mock('../../services/progressTracker');
13-
jest.mock('../../services/errorRecovery');
11+
jest.mock('../../../services/aiIntelligenceService');
12+
jest.mock('../../../services/progressTracker');
13+
jest.mock('../../../services/errorRecoveryService');
1414
jest.mock('inquirer');
1515

16-
const MockAIService = AIService as jest.MockedClass<typeof AIService>;
16+
const MockAIService = AIIntelligenceService as jest.MockedClass<typeof AIIntelligenceService>;
1717
const MockProgressTracker = ProgressTracker as jest.MockedClass<typeof ProgressTracker>;
1818
const MockErrorRecoveryService = ErrorRecoveryService as jest.MockedClass<
1919
typeof ErrorRecoveryService
2020
>;
2121

2222
// Mock inquirer
23+
jest.mock('inquirer', () => ({
24+
prompt: jest.fn(),
25+
Separator: jest.fn()
26+
}));
2327
import inquirer from 'inquirer';
24-
jest.mock('inquirer');
28+
const mockPrompt = inquirer.prompt as unknown as jest.Mock;
2529

2630
// Mock console methods
2731
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation();
2832
const mockConsoleError = jest.spyOn(console, 'error').mockImplementation();
2933

3034
describe('Init Command', () => {
3135
let tempDir: string;
32-
let mockAIService: jest.Mocked<AIService>;
36+
let mockAIService: jest.Mocked<AIIntelligenceService>;
3337
let mockProgressTracker: jest.Mocked<ProgressTracker>;
3438
let mockErrorRecovery: jest.Mocked<ErrorRecoveryService>;
3539

@@ -40,22 +44,20 @@ describe('Init Command', () => {
4044
// Setup mocks
4145
mockAIService = {
4246
isAIEnabled: jest.fn().mockReturnValue(true),
43-
analyzeProject: jest.fn(),
47+
getAIStatus: jest.fn().mockReturnValue('AI Ready'),
4448
generateSmartDefaults: jest.fn(),
45-
getProjectSuggestions: jest.fn(),
4649
} as any;
4750

4851
mockProgressTracker = {
4952
startOperation: jest.fn().mockResolvedValue('test-operation-id'),
50-
addStep: jest.fn(),
53+
addStep: jest.fn().mockResolvedValue('step-id'),
54+
completeStep: jest.fn(),
5155
completeOperation: jest.fn(),
5256
getProgress: jest.fn(),
5357
} as any;
5458

5559
mockErrorRecovery = {
56-
recoverFromError: jest.fn(),
57-
analyzeError: jest.fn(),
58-
getRecoveryActions: jest.fn(),
60+
handleError: jest.fn(),
5961
} as any;
6062

6163
MockAIService.mockImplementation(() => mockAIService);
@@ -75,7 +77,7 @@ describe('Init Command', () => {
7577
describe('basic initialization', () => {
7678
it('should run interactive setup wizard', async () => {
7779
// Mock inquirer responses
78-
inquirer.prompt
80+
mockPrompt
7981
.mockResolvedValueOnce({
8082
projectName: 'test-project',
8183
projectType: 'web',
@@ -103,15 +105,12 @@ describe('Init Command', () => {
103105
});
104106

105107
mockAIService.generateSmartDefaults.mockResolvedValue({
106-
projectType: 'web',
107-
targetIDEs: ['claude'],
108-
techStack: { frontend: 'react', backend: 'nodejs' },
108+
suggestions: [],
109109
confidence: 85,
110+
reasoning: 'AI analysis complete',
111+
optimizations: [],
110112
});
111113

112-
mockAIService.getProjectSuggestions.mockResolvedValue([
113-
{ type: 'tech', message: 'Consider using TypeScript', confidence: 90 },
114-
]);
115114

116115
const program = new Command();
117116
program.addCommand(initCommand);
@@ -128,15 +127,19 @@ describe('Init Command', () => {
128127
);
129128

130129
expect(mockProgressTracker.addStep).toHaveBeenCalledWith('Interactive setup wizard');
131-
expect(mockProgressTracker.completeOperation).toHaveBeenCalledWith(true, expect.any(Object));
130+
expect(mockProgressTracker.completeOperation).toHaveBeenCalledWith(
131+
'test-operation-id',
132+
'completed',
133+
expect.any(Object)
134+
);
132135
});
133136

134137
it('should handle --quick option with AI defaults', async () => {
135138
mockAIService.generateSmartDefaults.mockResolvedValue({
136-
projectType: 'fullstack',
137-
targetIDEs: ['claude'],
138-
techStack: { frontend: 'nextjs', backend: 'fastapi' },
139+
suggestions: [],
139140
confidence: 95,
141+
reasoning: 'AI analysis complete',
142+
optimizations: [],
140143
});
141144

142145
const program = new Command();
@@ -151,7 +154,7 @@ describe('Init Command', () => {
151154

152155
it('should handle --no-ai option', async () => {
153156
// Mock non-AI responses
154-
inquirer.prompt.mockResolvedValue({
157+
mockPrompt.mockResolvedValue({
155158
projectName: 'no-ai-project',
156159
projectType: 'api',
157160
targetIDEs: ['cursor'],
@@ -164,7 +167,7 @@ describe('Init Command', () => {
164167
await program.parseAsync(['node', 'test', 'init', '--no-ai'], { from: 'user' });
165168

166169
expect(mockAIService.generateSmartDefaults).not.toHaveBeenCalled();
167-
expect(mockAIService.getProjectSuggestions).not.toHaveBeenCalled();
170+
expect(mockAIService.generateSmartDefaults).not.toHaveBeenCalled();
168171
expect(mockProgressTracker.startOperation).toHaveBeenCalledWith(
169172
'init',
170173
'Project initialization',
@@ -179,7 +182,7 @@ describe('Init Command', () => {
179182
it('should handle --output option', async () => {
180183
const outputDir = path.join(tempDir, 'custom-output');
181184

182-
inquirer.prompt.mockResolvedValue({
185+
mockPrompt.mockResolvedValue({
183186
projectName: 'output-test',
184187
projectType: 'web',
185188
targetIDEs: ['claude'],
@@ -196,7 +199,7 @@ describe('Init Command', () => {
196199
});
197200

198201
it('should handle --ide option', async () => {
199-
inquirer.prompt.mockResolvedValue({
202+
mockPrompt.mockResolvedValue({
200203
projectName: 'ide-test',
201204
projectType: 'web',
202205
confirmed: true,
@@ -241,7 +244,7 @@ describe('Init Command', () => {
241244

242245
describe('file generation', () => {
243246
it('should generate CLAUDE.md file', async () => {
244-
inquirer.prompt.mockResolvedValue({
247+
mockPrompt.mockResolvedValue({
245248
projectName: 'file-gen-test',
246249
projectType: 'web',
247250
description: 'Test project for file generation',
@@ -264,7 +267,7 @@ describe('Init Command', () => {
264267
});
265268

266269
it('should generate PRPs directory', async () => {
267-
inquirer.prompt.mockResolvedValue({
270+
mockPrompt.mockResolvedValue({
268271
projectName: 'prp-test',
269272
projectType: 'fullstack',
270273
targetIDEs: ['claude'],
@@ -283,7 +286,7 @@ describe('Init Command', () => {
283286
});
284287

285288
it('should generate .claude directory for Claude IDE', async () => {
286-
inquirer.prompt.mockResolvedValue({
289+
mockPrompt.mockResolvedValue({
287290
projectName: 'claude-dir-test',
288291
projectType: 'web',
289292
targetIDEs: ['claude'],
@@ -306,39 +309,34 @@ describe('Init Command', () => {
306309
describe('error handling', () => {
307310
it('should handle setup wizard errors with recovery', async () => {
308311
const error = new Error('Permission denied');
309-
inquirer.prompt.mockRejectedValue(error);
312+
mockPrompt.mockRejectedValue(error);
310313

311-
mockErrorRecovery.recoverFromError.mockResolvedValue({
312-
attempted: 1,
313-
successful: 1,
314-
actions: [],
315-
manualActions: [],
316-
});
317314

318315
const program = new Command();
319316
program.addCommand(initCommand);
320317

321318
await program.parseAsync(['node', 'test', 'init'], { from: 'user' });
322319

323-
expect(mockErrorRecovery.recoverFromError).toHaveBeenCalledWith(error, tempDir);
320+
expect(mockErrorRecovery.handleError).toHaveBeenCalled();
324321
expect(mockProgressTracker.completeOperation).toHaveBeenCalledWith(
325-
false,
326-
undefined,
327-
expect.stringContaining('Permission denied')
322+
'test-operation-id',
323+
'failed',
324+
expect.objectContaining({
325+
errors: expect.arrayContaining([expect.stringContaining('Permission denied')])
326+
})
328327
);
329328
});
330329

331330
it('should handle file generation errors', async () => {
332-
inquirer.prompt.mockResolvedValue({
331+
mockPrompt.mockResolvedValue({
333332
projectName: 'error-test',
334333
projectType: 'web',
335334
targetIDEs: ['claude'],
336335
confirmed: true,
337336
});
338337

339338
// Mock file system error
340-
const originalWriteFile = fs.writeFile;
341-
jest.spyOn(fs, 'writeFile').mockRejectedValue(new Error('Disk full'));
339+
(fs.writeFile as any) = jest.fn().mockRejectedValue(new Error('Disk full'));
342340

343341
const program = new Command();
344342
program.addCommand(initCommand);
@@ -350,13 +348,13 @@ describe('Init Command', () => {
350348
);
351349

352350
// Restore original function
353-
(fs.writeFile as jest.Mock).mockImplementation(originalWriteFile);
351+
jest.restoreAllMocks();
354352
});
355353

356354
it('should handle AI service errors gracefully', async () => {
357355
mockAIService.generateSmartDefaults.mockRejectedValue(new Error('AI service unavailable'));
358356

359-
inquirer.prompt.mockResolvedValue({
357+
mockPrompt.mockResolvedValue({
360358
projectName: 'ai-error-test',
361359
projectType: 'web',
362360
targetIDEs: ['claude'],
@@ -369,13 +367,17 @@ describe('Init Command', () => {
369367
await program.parseAsync(['node', 'test', 'init'], { from: 'user' });
370368

371369
// Should continue with fallback behavior
372-
expect(mockProgressTracker.completeOperation).toHaveBeenCalledWith(true, expect.any(Object));
370+
expect(mockProgressTracker.completeOperation).toHaveBeenCalledWith(
371+
'test-operation-id',
372+
'completed',
373+
expect.any(Object)
374+
);
373375
});
374376
});
375377

376378
describe('progress tracking', () => {
377379
it('should track all major steps', async () => {
378-
inquirer.prompt.mockResolvedValue({
380+
mockPrompt.mockResolvedValue({
379381
projectName: 'progress-test',
380382
projectType: 'fullstack',
381383
targetIDEs: ['claude', 'cursor'],
@@ -401,7 +403,7 @@ describe('Init Command', () => {
401403
});
402404

403405
it('should include metadata in progress tracking', async () => {
404-
inquirer.prompt.mockResolvedValue({
406+
mockPrompt.mockResolvedValue({
405407
projectName: 'metadata-test',
406408
projectType: 'api',
407409
targetIDEs: ['claude'],
@@ -415,19 +417,19 @@ describe('Init Command', () => {
415417
await program.parseAsync(['node', 'test', 'init'], { from: 'user' });
416418

417419
expect(mockProgressTracker.completeOperation).toHaveBeenCalledWith(
418-
true,
420+
'test-operation-id',
421+
'completed',
419422
expect.objectContaining({
420423
filesGenerated: expect.any(Number),
421-
ideConfigurations: expect.any(Array),
422-
features: expect.any(Array),
424+
targetIDEs: expect.any(Array)
423425
})
424426
);
425427
});
426428
});
427429

428430
describe('validation', () => {
429431
it('should validate project name input', async () => {
430-
inquirer.prompt
432+
mockPrompt
431433
.mockResolvedValueOnce({
432434
projectName: '', // Invalid empty name
433435
projectType: 'web',
@@ -445,11 +447,15 @@ describe('Init Command', () => {
445447
await program.parseAsync(['node', 'test', 'init'], { from: 'user' });
446448

447449
// Should eventually succeed with valid name
448-
expect(mockProgressTracker.completeOperation).toHaveBeenCalledWith(true, expect.any(Object));
450+
expect(mockProgressTracker.completeOperation).toHaveBeenCalledWith(
451+
'test-operation-id',
452+
'completed',
453+
expect.any(Object)
454+
);
449455
});
450456

451457
it('should validate IDE selection', async () => {
452-
inquirer.prompt.mockResolvedValue({
458+
mockPrompt.mockResolvedValue({
453459
projectName: 'ide-validation-test',
454460
projectType: 'web',
455461
targetIDEs: ['claude', 'invalid-ide'], // Invalid IDE should be filtered
@@ -473,16 +479,8 @@ describe('Init Command', () => {
473479

474480
describe('AI integration', () => {
475481
it('should use AI suggestions when available', async () => {
476-
mockAIService.getProjectSuggestions.mockResolvedValue([
477-
{
478-
type: 'tech',
479-
message: 'Consider using TypeScript for better type safety',
480-
confidence: 90,
481-
},
482-
{ type: 'config', message: 'Enable ESLint for code quality', confidence: 85 },
483-
]);
484-
485-
inquirer.prompt.mockResolvedValue({
482+
483+
mockPrompt.mockResolvedValue({
486484
projectName: 'ai-suggestions-test',
487485
projectType: 'web',
488486
targetIDEs: ['claude'],
@@ -494,22 +492,13 @@ describe('Init Command', () => {
494492

495493
await program.parseAsync(['node', 'test', 'init'], { from: 'user' });
496494

497-
expect(mockAIService.getProjectSuggestions).toHaveBeenCalledWith(
498-
expect.objectContaining({
499-
name: 'ai-suggestions-test',
500-
type: 'web',
501-
})
502-
);
503-
504-
expect(mockConsoleLog).toHaveBeenCalledWith(
505-
expect.stringContaining('Consider using TypeScript')
506-
);
495+
expect(mockAIService.generateSmartDefaults).toHaveBeenCalled();
507496
});
508497

509498
it('should fall back gracefully when AI is disabled', async () => {
510499
mockAIService.isAIEnabled.mockReturnValue(false);
511500

512-
inquirer.prompt.mockResolvedValue({
501+
mockPrompt.mockResolvedValue({
513502
projectName: 'no-ai-fallback-test',
514503
projectType: 'web',
515504
targetIDEs: ['claude'],
@@ -522,7 +511,11 @@ describe('Init Command', () => {
522511
await program.parseAsync(['node', 'test', 'init'], { from: 'user' });
523512

524513
expect(mockAIService.generateSmartDefaults).not.toHaveBeenCalled();
525-
expect(mockProgressTracker.completeOperation).toHaveBeenCalledWith(true, expect.any(Object));
514+
expect(mockProgressTracker.completeOperation).toHaveBeenCalledWith(
515+
'test-operation-id',
516+
'completed',
517+
expect.any(Object)
518+
);
526519
});
527520
});
528521
});

0 commit comments

Comments
 (0)