UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

452 lines (389 loc) 14.5 kB
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { RoadmapWizard } from '../roadmap-wizard.js'; import { RoadmapManager } from '../roadmap-manager.js'; import { PrioritizationEngine } from '../prioritization-engine.js'; import { ConfigManager } from '../../../config/config-manager.js'; // Mock the dependencies vi.mock('../roadmap-manager.js'); vi.mock('../prioritization-engine.js'); describe('RoadmapWizard', () => { let wizard: RoadmapWizard; let mockRoadmapManager: any; let mockPrioritizationEngine: any; const sessionId = 'test-session-123'; beforeEach(() => { // Create mock instances mockRoadmapManager = { createRoadmap: vi.fn().mockResolvedValue({ id: 'roadmap-123', name: 'Test Roadmap', vision: 'Test Vision', timeHorizon: 'annual', owner: 'Test Owner', stakeholders: [], status: 'draft', themes: [], milestones: [], releases: [], createdAt: new Date(), updatedAt: new Date(), metrics: { featuresPlanned: 0, featuresCompleted: 0, initiativesActive: 0, velocityTrend: 'stable', onTimeDelivery: 100, valueDelivered: 0 } }), addTheme: vi.fn().mockResolvedValue({ id: 'theme-123', name: 'Test Theme', description: 'Test Description', objectives: ['Objective 1'], priority: 'must-have', timeframe: { startQuarter: 'Q1 2024', endQuarter: 'Q4 2024' }, status: 'planned', initiatives: [], metrics: { initiativesTotal: 0, initiativesCompleted: 0, featuresTotal: 0, featuresCompleted: 0, progressPercentage: 0, valueScore: 0 } }), createInitiative: vi.fn().mockImplementation((options) => Promise.resolve({ id: 'init-123', themeId: options.themeId, title: options.title, description: options.description, features: [], epicIds: [], value: options.estimatedValue, effort: options.estimatedEffort, risks: options.risks || [], dependencies: [], status: 'ideation' })), addFeature: vi.fn().mockImplementation((options) => Promise.resolve({ id: 'feature-123', initiativeId: options.initiativeId, name: options.name, description: options.description, userStories: [], priority: 0, businessValue: options.businessValue, technicalComplexity: options.technicalComplexity, status: 'proposed' })), getRoadmap: vi.fn().mockResolvedValue({ id: 'roadmap-123', name: 'Test Roadmap', themes: [] }), getThemeDetails: vi.fn().mockResolvedValue({ theme: { id: 'theme-123', name: 'Test Theme' }, initiatives: [], features: [] }) }; mockPrioritizationEngine = {}; wizard = new RoadmapWizard(mockRoadmapManager, mockPrioritizationEngine); }); describe('Roadmap Wizard', () => { it('should start roadmap wizard and return step 1 instructions', async () => { const result = await wizard.startRoadmapWizard(sessionId); expect(result).toContain('Product Roadmap Creation Wizard'); expect(result).toContain('Step 1/4: Basic Information'); expect(result).toContain('Product/Roadmap Name'); expect(result).toContain('Vision Statement'); expect(result).toContain('Time Horizon'); expect(result).toContain('Your Name/Role'); }); it('should progress through wizard steps', async () => { // Start wizard await wizard.startRoadmapWizard(sessionId); // Step 1 -> Step 2 const step2Result = await wizard.continueRoadmapWizard(sessionId, { name: 'My Product', vision: 'Amazing product vision', timeHorizon: 'annual', owner: 'John Doe' }); expect(step2Result).toContain('Step 2/4: Key Stakeholders'); expect(step2Result).toContain('stakeholders'); // Step 2 -> Step 3 const step3Result = await wizard.continueRoadmapWizard(sessionId, { stakeholders: ['Jane Smith', 'Bob Johnson'] }); expect(step3Result).toContain('Step 3/4: Strategic Context'); expect(step3Result).toContain('Primary Goal'); expect(step3Result).toContain('Key Challenges'); // Step 3 -> Step 4 const step4Result = await wizard.continueRoadmapWizard(sessionId, { goal: 'Customer satisfaction', challenges: ['High support volume', 'Poor UX'], metrics: ['NPS > 50', 'Support tickets -50%'] }); expect(step4Result).toContain('Step 4/4: Initial Themes'); expect(step4Result).toContain('strategic themes'); }); it('should complete wizard and create roadmap', async () => { // Progress through all steps await wizard.startRoadmapWizard(sessionId); await wizard.continueRoadmapWizard(sessionId, { name: 'My Product', vision: 'Amazing product vision', timeHorizon: 'annual', owner: 'John Doe' }); await wizard.continueRoadmapWizard(sessionId, { stakeholders: ['Jane Smith'] }); await wizard.continueRoadmapWizard(sessionId, { goal: 'Customer satisfaction', challenges: ['High support volume'], metrics: ['NPS > 50'] }); // Complete wizard with themes const result = await wizard.continueRoadmapWizard(sessionId, { themes: [ { name: 'Self-Service', description: 'Build self-service capabilities', priority: 'must-have', startQuarter: 'Q1 2024', endQuarter: 'Q4 2024' } ] }); expect(result).toContain('Roadmap Created Successfully!'); expect(result).toContain('roadmap-123'); expect(mockRoadmapManager.createRoadmap).toHaveBeenCalledWith({ name: 'My Product', vision: 'Amazing product vision', timeHorizon: 'annual', owner: 'John Doe', stakeholders: ['Jane Smith'] }); expect(mockRoadmapManager.addTheme).toHaveBeenCalled(); }); it('should handle "none" for stakeholders', async () => { await wizard.startRoadmapWizard(sessionId); await wizard.continueRoadmapWizard(sessionId, { name: 'My Product', vision: 'Amazing product vision', timeHorizon: 'annual', owner: 'John Doe' }); const result = await wizard.continueRoadmapWizard(sessionId, { stakeholders: 'none' }); expect(result).toContain('Step 3/4'); }); it('should return error for invalid session', async () => { const result = await wizard.continueRoadmapWizard('invalid-session', {}); expect(result).toContain('No active roadmap wizard found'); }); }); describe('Initiative Wizard', () => { it('should start initiative wizard', async () => { const result = await wizard.startInitiativeWizard( sessionId, 'roadmap-123', 'theme-123' ); expect(result).toContain('Initiative Creation Wizard'); expect(result).toContain('Step 1/5: Initiative Overview'); expect(result).toContain('Title'); expect(result).toContain('Description'); expect(result).toContain('Key Outcomes'); }); it('should progress through initiative wizard steps', async () => { // Start wizard await wizard.startInitiativeWizard(sessionId, 'roadmap-123', 'theme-123'); // Step 1 -> Step 2 const step2Result = await wizard.continueInitiativeWizard(sessionId, { title: 'Smart Search', description: 'AI-powered search implementation', outcomes: ['Instant answers', 'Reduced support'] }); expect(step2Result).toContain('Step 2/5: Business Value Assessment'); expect(step2Result).toContain('User Impact'); expect(step2Result).toContain('Revenue Impact'); // Step 2 -> Step 3 const step3Result = await wizard.continueInitiativeWizard(sessionId, { userImpact: 'high', revenueImpact: 500000, costSavings: 200000, strategicValue: 8 }); expect(step3Result).toContain('Step 3/5: Effort Estimation'); expect(step3Result).toContain('Development Weeks'); // Step 3 -> Step 4 const step4Result = await wizard.continueInitiativeWizard(sessionId, { developmentWeeks: 12, designWeeks: 4, qaWeeks: 3, confidence: 'medium' }); expect(step4Result).toContain('Step 4/5: Risk Assessment'); // Step 4 -> Step 5 const step5Result = await wizard.continueInitiativeWizard(sessionId, { risks: [{ description: 'Technical complexity', likelihood: 'medium', impact: 'high', mitigation: 'Technical spike' }] }); expect(step5Result).toContain('Step 5/5: Initial Features'); }); it('should complete initiative wizard and create initiative', async () => { await wizard.startInitiativeWizard(sessionId, 'roadmap-123', 'theme-123'); // Progress through all steps await wizard.continueInitiativeWizard(sessionId, { title: 'Smart Search', description: 'AI-powered search', outcomes: ['Instant answers'] }); await wizard.continueInitiativeWizard(sessionId, { userImpact: 'high', revenueImpact: 500000, costSavings: 200000, strategicValue: 8 }); await wizard.continueInitiativeWizard(sessionId, { developmentWeeks: 12, designWeeks: 4, qaWeeks: 3, confidence: 'medium' }); await wizard.continueInitiativeWizard(sessionId, { risks: 'none' }); const result = await wizard.continueInitiativeWizard(sessionId, { features: [ { name: 'Natural Language Search', description: 'NLP search capability', value: 85, complexity: 'high' } ] }); expect(result).toContain('Initiative Created Successfully!'); expect(result).toContain('Smart Search'); expect(mockRoadmapManager.createInitiative).toHaveBeenCalled(); expect(mockRoadmapManager.addFeature).toHaveBeenCalled(); }); it('should handle invalid roadmap or theme', async () => { mockRoadmapManager.getRoadmap.mockResolvedValueOnce(null); const result = await wizard.startInitiativeWizard( sessionId, 'invalid-roadmap', 'theme-123' ); expect(result).toContain('Invalid roadmap or theme ID'); }); }); describe('Quick Feature Wizard', () => { it('should start quick feature wizard', async () => { const result = await wizard.startQuickFeatureWizard( sessionId, 'roadmap-123', 'init-123' ); expect(result).toContain('Quick Feature Addition'); expect(result).toContain('Name'); expect(result).toContain('Description'); expect(result).toContain('Value'); expect(result).toContain('Complexity'); expect(result).toContain('Success Metrics'); }); it('should complete quick feature wizard', async () => { await wizard.startQuickFeatureWizard(sessionId, 'roadmap-123', 'init-123'); const result = await wizard.continueQuickFeatureWizard(sessionId, { name: 'Real-time Notifications', description: 'Push notifications for updates', value: 75, complexity: 'medium', metrics: ['User engagement +30%', 'Response time < 2s'] }); expect(result).toContain('Feature Added Successfully!'); expect(result).toContain('Real-time Notifications'); expect(mockRoadmapManager.addFeature).toHaveBeenCalledWith({ roadmapId: 'roadmap-123', initiativeId: 'init-123', name: 'Real-time Notifications', description: 'Push notifications for updates', businessValue: { score: 75, rationale: 'Added via quick wizard', metrics: ['User engagement +30%', 'Response time < 2s'] }, technicalComplexity: 'medium', targetRelease: undefined }); }); }); describe('Wizard State Management', () => { it('should track active wizards', async () => { await wizard.startRoadmapWizard('session-1'); await wizard.startInitiativeWizard('session-2', 'roadmap-123', 'theme-123'); const activeWizards = wizard.getActiveWizards(); expect(activeWizards).toContain('session-1'); expect(activeWizards).toContain('session-2'); }); it('should cancel wizard', async () => { await wizard.startRoadmapWizard(sessionId); const cancelled = wizard.cancelWizard(sessionId); expect(cancelled).toBe(true); const result = await wizard.continueRoadmapWizard(sessionId, {}); expect(result).toContain('No active roadmap wizard found'); }); it('should not cancel non-existent wizard', () => { const cancelled = wizard.cancelWizard('non-existent'); expect(cancelled).toBe(false); }); }); describe('Helper Methods', () => { it('should derive objectives from context', async () => { await wizard.startRoadmapWizard(sessionId); // Progress to step 4 await wizard.continueRoadmapWizard(sessionId, { name: 'My Product', vision: 'Vision', timeHorizon: 'annual', owner: 'Owner' }); await wizard.continueRoadmapWizard(sessionId, { stakeholders: [] }); await wizard.continueRoadmapWizard(sessionId, { goal: 'Revenue growth', challenges: ['Low conversion'], metrics: ['Revenue +50%'] }); await wizard.continueRoadmapWizard(sessionId, { themes: [{ name: 'Conversion Optimization', description: 'Improve conversion rates', priority: 'must-have' }] }); // Check that addTheme was called with derived objectives const addThemeCall = mockRoadmapManager.addTheme.mock.calls[0][0]; expect(addThemeCall.objectives).toContain('Drive Revenue growth through conversion optimization'); expect(addThemeCall.objectives).toContain('Address key challenge: Low conversion'); }); }); });