@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
JavaScript
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 = `
Build a React dashboard that displays real-time analytics from PostgreSQL database.
This will help users make data-driven decisions and improve productivity.
- Frontend: React 18, TypeScript
- Backend: Node.js, Express
- Database: PostgreSQL
- src/components/Dashboard/
- src/api/analytics/
1. Fetch data from analytics API
2. Process and aggregate
3. Render charts
- 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 = `
1. User privacy is paramount
2. Performance over features
- No third-party analytics
- WCAG 2.1 AA compliance
`;
const specificationContent = `
A comprehensive user management system.
Manage users across multiple tenants.
Microservices architecture with API gateway.
`;
const planContent = `
- React 18
- Node.js 20
- PostgreSQL 15
- Use TypeScript strict mode
- Follow Airbnb style guide
- Write tests first
- Document public APIs
- 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 = `
A complete e-commerce platform for small businesses.
- Enable online sales
- Provide analytics
- RESTful API
- Payment gateway integration
- Secure checkout flow
- PCI DSS compliance
- Data encryption at rest and in transit
- Sub-100ms response times
- Handle 10,000 concurrent users
`;
const architectureContent = `
Modular monolith architecture with separate domains.
Clean architecture with domain-driven design.
- 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');
});
});
});