UNPKG

@iservu-inc/adf-cli

Version:

CLI tool for AgentDevFramework - AI-assisted development framework with multi-provider AI support

437 lines (353 loc) 15.3 kB
const fs = require('fs-extra'); const path = require('path'); const VSCodeGenerator = require('../lib/generators/vscode-generator'); const TEST_PROJECT_PATH = path.join(__dirname, 'test-project-vscode'); const TEST_SESSION_PATH = path.join(TEST_PROJECT_PATH, '.adf', 'sessions', 'test-session'); describe('VSCodeGenerator', () => { beforeEach(async () => { // Clean up test directories await fs.remove(TEST_PROJECT_PATH); await fs.ensureDir(TEST_PROJECT_PATH); await fs.ensureDir(TEST_SESSION_PATH); await fs.ensureDir(path.join(TEST_SESSION_PATH, 'outputs')); }); afterEach(async () => { // Clean up after tests await fs.remove(TEST_PROJECT_PATH); }); describe('PRP Framework', () => { it('should generate VS Code configurations from PRP output', async () => { // Create mock PRP output const prpContent = `# Product Requirement Prompt (PRP) ## 1. Goal Definition Build a React dashboard that displays real-time analytics from PostgreSQL database. ## 2. Business Justification This will help users make data-driven decisions and improve productivity. ## 3. Contextual Intelligence ### Technology Stack - Frontend: React 18, TypeScript - Backend: Node.js, Express - Database: PostgreSQL ## 4. Implementation Blueprint ### File Structure - src/components/Dashboard/ - src/api/analytics/ ### Core Logic 1. Fetch data from analytics API 2. Process and aggregate 3. Render charts ## 5. Validation ### Success Criteria - Dashboard loads in <2s - All charts render correctly `; await fs.writeFile( path.join(TEST_SESSION_PATH, 'outputs', 'prp.md'), prpContent, 'utf-8' ); // Create metadata await fs.writeJson(path.join(TEST_SESSION_PATH, '_metadata.json'), { framework: 'rapid', projectName: 'Test Analytics Dashboard' }); // Generate VS Code configs const generator = new VSCodeGenerator(TEST_SESSION_PATH, TEST_PROJECT_PATH, 'rapid'); const generated = await generator.generate(); // Verify .github/copilot-instructions.md was created const copilotPath = path.join(TEST_PROJECT_PATH, '.github', 'copilot-instructions.md'); expect(await fs.pathExists(copilotPath)).toBe(true); const copilotContent = await fs.readFile(copilotPath, 'utf-8'); expect(copilotContent).toContain('Copilot Instructions for Test Analytics Dashboard'); expect(copilotContent).toContain('Project Overview'); expect(copilotContent).toContain('React dashboard'); expect(copilotContent).toContain('Tech Stack'); expect(copilotContent).toContain('Implementation Blueprint'); expect(copilotContent).toContain('Success Criteria'); expect(copilotContent).toContain('When Generating Code'); expect(copilotContent).toContain('.adf/sessions/test-session/outputs/prp.md'); // Verify .vscode/settings.json was created const settingsPath = path.join(TEST_PROJECT_PATH, '.vscode', 'settings.json'); expect(await fs.pathExists(settingsPath)).toBe(true); const settingsContent = await fs.readFile(settingsPath, 'utf-8'); const settings = JSON.parse(settingsContent); expect(settings['github.copilot.chat.modes']).toBeDefined(); expect(settings['github.copilot.chat.modes'].architect).toBeDefined(); expect(settings['github.copilot.chat.modes'].implementer).toBeDefined(); expect(settings['github.copilot.chat.modes'].reviewer).toBeDefined(); // Verify architect mode expect(settings['github.copilot.chat.modes'].architect.instructions).toContain('architecture'); expect(settings['github.copilot.chat.modes'].architect.context).toContain('.adf/sessions/test-session/outputs/prp.md'); // Verify generated structure expect(generated.copilot).toHaveLength(1); expect(generated.vscode).toHaveLength(1); }); }); describe('Balanced Framework', () => { it('should generate VS Code configurations from Balanced outputs', async () => { // Create mock outputs const constitutionContent = `# Constitution ## Core Principles 1. User privacy is paramount 2. Performance over features ## Constraints - No third-party analytics - WCAG 2.1 AA compliance `; const specificationContent = `# Specification ## Overview A comprehensive user management system. ## Purpose Manage users across multiple tenants. ## Architecture Microservices architecture with API gateway. `; const planContent = `# Technical Plan ## Technology Stack - React 18 - Node.js 20 - PostgreSQL 15 ## Code Style - Use TypeScript strict mode - Follow Airbnb style guide ## Coding Standards - Write tests first - Document public APIs ## Testing - Unit tests with Jest - E2E tests with Playwright `; await fs.writeFile( path.join(TEST_SESSION_PATH, 'outputs', 'constitution.md'), constitutionContent, 'utf-8' ); await fs.writeFile( path.join(TEST_SESSION_PATH, 'outputs', 'specification.md'), specificationContent, 'utf-8' ); await fs.writeFile( path.join(TEST_SESSION_PATH, 'outputs', 'plan.md'), planContent, 'utf-8' ); await fs.writeJson(path.join(TEST_SESSION_PATH, '_metadata.json'), { framework: 'balanced', projectName: 'User Management System' }); // Generate VS Code configs const generator = new VSCodeGenerator(TEST_SESSION_PATH, TEST_PROJECT_PATH, 'balanced'); await generator.generate(); // Verify .github/copilot-instructions.md const copilotPath = path.join(TEST_PROJECT_PATH, '.github', 'copilot-instructions.md'); const copilotContent = await fs.readFile(copilotPath, 'utf-8'); expect(copilotContent).toContain('User Management System'); expect(copilotContent).toContain('Core Principles'); expect(copilotContent).toContain('User privacy is paramount'); expect(copilotContent).toContain('Constraints (Non-Negotiable)'); expect(copilotContent).toContain('WCAG 2.1 AA compliance'); expect(copilotContent).toContain('Microservices architecture'); expect(copilotContent).toContain('Code Standards'); expect(copilotContent).toContain('Testing Requirements'); expect(copilotContent).toContain('constitution.md'); expect(copilotContent).toContain('specification.md'); expect(copilotContent).toContain('plan.md'); // Verify .vscode/settings.json has balanced-specific context const settingsContent = await fs.readFile( path.join(TEST_PROJECT_PATH, '.vscode', 'settings.json'), 'utf-8' ); const settings = JSON.parse(settingsContent); const architectContext = settings['github.copilot.chat.modes'].architect.context.join(' '); const implementerContext = settings['github.copilot.chat.modes'].implementer.context.join(' '); const reviewerContext = settings['github.copilot.chat.modes'].reviewer.context.join(' '); expect(architectContext).toContain('specification.md'); expect(architectContext).toContain('plan.md'); expect(implementerContext).toContain('specification.md'); expect(implementerContext).toContain('tasks.md'); expect(reviewerContext).toContain('constitution.md'); }); }); describe('BMAD Framework', () => { it('should generate VS Code configurations from BMAD outputs', async () => { // Create mock outputs const prdContent = `# Product Requirements Document ## Executive Summary A complete e-commerce platform for small businesses. ## Goals and Objectives - Enable online sales - Provide analytics ## Technical Requirements - RESTful API - Payment gateway integration - Secure checkout flow ## Security - PCI DSS compliance - Data encryption at rest and in transit ## Performance - Sub-100ms response times - Handle 10,000 concurrent users `; const architectureContent = `# System Architecture ## System Overview Modular monolith architecture with separate domains. ## Architecture Overview Clean architecture with domain-driven design. ## Components - Order Management - Inventory System - Payment Processing `; await fs.writeFile( path.join(TEST_SESSION_PATH, 'outputs', 'prd.md'), prdContent, 'utf-8' ); await fs.writeFile( path.join(TEST_SESSION_PATH, 'outputs', 'architecture.md'), architectureContent, 'utf-8' ); await fs.writeJson(path.join(TEST_SESSION_PATH, '_metadata.json'), { framework: 'comprehensive', projectName: 'E-commerce Platform' }); // Generate VS Code configs const generator = new VSCodeGenerator(TEST_SESSION_PATH, TEST_PROJECT_PATH, 'comprehensive'); await generator.generate(); // Verify .github/copilot-instructions.md const copilotPath = path.join(TEST_PROJECT_PATH, '.github', 'copilot-instructions.md'); const copilotContent = await fs.readFile(copilotPath, 'utf-8'); expect(copilotContent).toContain('E-commerce Platform'); expect(copilotContent).toContain('Product Overview'); expect(copilotContent).toContain('e-commerce platform'); expect(copilotContent).toContain('Goals and Objectives'); expect(copilotContent).toContain('Enable online sales'); expect(copilotContent).toContain('Technical Requirements'); expect(copilotContent).toContain('System Architecture'); expect(copilotContent).toContain('Security Requirements'); expect(copilotContent).toContain('PCI DSS compliance'); expect(copilotContent).toContain('Performance Considerations'); expect(copilotContent).toContain('Sub-100ms response times'); expect(copilotContent).toContain('prd.md'); expect(copilotContent).toContain('architecture.md'); // Verify .vscode/settings.json has BMAD-specific context const settingsContent = await fs.readFile( path.join(TEST_PROJECT_PATH, '.vscode', 'settings.json'), 'utf-8' ); const settings = JSON.parse(settingsContent); const architectContext = settings['github.copilot.chat.modes'].architect.context.join(' '); const implementerContext = settings['github.copilot.chat.modes'].implementer.context.join(' '); expect(architectContext).toContain('architecture.md'); expect(architectContext).toContain('prd.md'); expect(implementerContext).toContain('stories.md'); }); }); describe('Settings.json Enhancement', () => { it('should preserve existing settings when enhancing .vscode/settings.json', async () => { // Create existing settings.json const existingSettings = { 'editor.formatOnSave': true, 'editor.tabSize': 2, 'files.exclude': { '**/.git': true } }; await fs.ensureDir(path.join(TEST_PROJECT_PATH, '.vscode')); await fs.writeJson( path.join(TEST_PROJECT_PATH, '.vscode', 'settings.json'), existingSettings, { spaces: 2 } ); // Create PRP const prpContent = '# PRP\n\n## 1. Goal Definition\nTest project'; await fs.writeFile( path.join(TEST_SESSION_PATH, 'outputs', 'prp.md'), prpContent, 'utf-8' ); // Generate VS Code configs const generator = new VSCodeGenerator(TEST_SESSION_PATH, TEST_PROJECT_PATH, 'rapid'); await generator.generate(); // Verify existing settings are preserved const settingsContent = await fs.readFile( path.join(TEST_PROJECT_PATH, '.vscode', 'settings.json'), 'utf-8' ); const settings = JSON.parse(settingsContent); expect(settings['editor.formatOnSave']).toBe(true); expect(settings['editor.tabSize']).toBe(2); expect(settings['files.exclude']).toEqual({ '**/.git': true }); expect(settings['github.copilot.chat.modes']).toBeDefined(); }); }); describe('Custom Chat Modes', () => { it('should create architect, implementer, and reviewer modes', async () => { const prpContent = '# PRP\n\n## 1. Goal Definition\nTest project'; await fs.writeFile( path.join(TEST_SESSION_PATH, 'outputs', 'prp.md'), prpContent, 'utf-8' ); const generator = new VSCodeGenerator(TEST_SESSION_PATH, TEST_PROJECT_PATH, 'rapid'); await generator.generate(); const settingsContent = await fs.readFile( path.join(TEST_PROJECT_PATH, '.vscode', 'settings.json'), 'utf-8' ); const settings = JSON.parse(settingsContent); const modes = settings['github.copilot.chat.modes']; // Verify all three modes exist expect(modes).toHaveProperty('architect'); expect(modes).toHaveProperty('implementer'); expect(modes).toHaveProperty('reviewer'); // Verify each mode has instructions and context expect(modes.architect).toHaveProperty('instructions'); expect(modes.architect).toHaveProperty('context'); expect(Array.isArray(modes.architect.context)).toBe(true); expect(modes.implementer).toHaveProperty('instructions'); expect(modes.implementer).toHaveProperty('context'); expect(Array.isArray(modes.implementer.context)).toBe(true); expect(modes.reviewer).toHaveProperty('instructions'); expect(modes.reviewer).toHaveProperty('context'); expect(Array.isArray(modes.reviewer.context)).toBe(true); }); }); describe('Template Variables', () => { it('should replace session ID in paths', async () => { const prpContent = '# PRP\n\n## 1. Goal Definition\nTest project'; await fs.writeFile( path.join(TEST_SESSION_PATH, 'outputs', 'prp.md'), prpContent, 'utf-8' ); const generator = new VSCodeGenerator(TEST_SESSION_PATH, TEST_PROJECT_PATH, 'rapid'); await generator.generate(); const copilotContent = await fs.readFile( path.join(TEST_PROJECT_PATH, '.github', 'copilot-instructions.md'), 'utf-8' ); // Should contain the actual session ID, not a template variable expect(copilotContent).toContain('test-session'); expect(copilotContent).not.toContain('{SESSION_ID}'); }); it('should include ADF CLI version', async () => { const prpContent = '# PRP\n\n## 1. Goal Definition\nTest'; await fs.writeFile( path.join(TEST_SESSION_PATH, 'outputs', 'prp.md'), prpContent, 'utf-8' ); const generator = new VSCodeGenerator(TEST_SESSION_PATH, TEST_PROJECT_PATH, 'rapid'); await generator.generate(); const copilotContent = await fs.readFile( path.join(TEST_PROJECT_PATH, '.github', 'copilot-instructions.md'), 'utf-8' ); expect(copilotContent).toContain('Generated by'); expect(copilotContent).toContain('ADF CLI'); }); }); });