UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

315 lines (275 loc) 8.88 kB
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { KanbanDataStore } from '../store.js'; import { ConfigManager } from '../../../config/config-manager.js'; import { Task, KanbanBoard } from '../types.js'; vi.mock('../../../config/config-manager.js'); describe('Kanban Enhanced Features Tests', () => { let store: KanbanDataStore; let mockConfigManager: vi.Mocked<ConfigManager>; let board: KanbanBoard; beforeEach(async () => { mockConfigManager = new ConfigManager() as vi.Mocked<ConfigManager>; mockConfigManager.getStorageManager = vi.fn().mockReturnValue({ loadData: vi.fn().mockResolvedValue(null), saveData: vi.fn().mockResolvedValue(undefined), ensureStorageDirectories: vi.fn().mockResolvedValue(undefined) }); store = new KanbanDataStore(mockConfigManager); await store.init(); board = store.createBoard('Test Board'); }); describe('Acceptance Criteria Management', () => { it('should add task with acceptance criteria', () => { const task = store.addTask('Test Board', { title: 'Implement feature', column: 'To Do', acceptanceCriteria: [ { id: 'ac-1', description: 'Feature works correctly', status: 'pending' }, { id: 'ac-2', description: 'Tests are passing', status: 'pending' } ] }); expect(task).toBeDefined(); expect(task!.acceptanceCriteria).toHaveLength(2); expect(task!.acceptanceCriteria[0].status).toBe('pending'); }); it('should update acceptance criterion status', () => { const task = store.addTask('Test Board', { title: 'Test task', column: 'To Do', acceptanceCriteria: [ { id: 'ac-1', description: 'Criterion 1', status: 'pending' } ] }); const criterion = task!.acceptanceCriteria[0]; criterion.status = 'completed'; criterion.verifiedBy = 'tester'; criterion.verifiedAt = new Date(); const success = store.updateTask('Test Board', task!.id, { acceptanceCriteria: [criterion] }); expect(success).toBe(true); const updatedTask = board.tasks.find(t => t.id === task!.id); expect(updatedTask!.acceptanceCriteria[0].status).toBe('completed'); expect(updatedTask!.acceptanceCriteria[0].verifiedBy).toBe('tester'); }); }); describe('Task Dependencies', () => { it('should add task with dependencies', () => { const blockerTask = store.addTask('Test Board', { title: 'Blocker task', column: 'To Do' }); const dependentTask = store.addTask('Test Board', { title: 'Dependent task', column: 'To Do', dependencies: [ { taskId: blockerTask!.id, type: 'depends_on' } ] }); expect(dependentTask!.dependencies).toHaveLength(1); expect(dependentTask!.dependencies[0].taskId).toBe(blockerTask!.id); expect(dependentTask!.dependencies[0].type).toBe('depends_on'); }); it('should handle circular dependencies check', () => { const task1 = store.addTask('Test Board', { title: 'Task 1', column: 'To Do' }); const task2 = store.addTask('Test Board', { title: 'Task 2', column: 'To Do', dependencies: [{ taskId: task1!.id, type: 'depends_on' }] }); // This would need validation logic in the actual implementation // For now, we just verify the structure is correct expect(task2!.dependencies[0].taskId).toBe(task1!.id); }); }); describe('Time Tracking', () => { it('should add task with time estimate', () => { const task = store.addTask('Test Board', { title: 'Timed task', column: 'To Do', timeTracking: { estimated: 8 } }); expect(task!.timeTracking).toBeDefined(); expect(task!.timeTracking!.estimated).toBe(8); }); it('should track time spent on task', () => { const task = store.addTask('Test Board', { title: 'Task', column: 'To Do', timeTracking: { estimated: 8 } }); const success = store.updateTask('Test Board', task!.id, { timeTracking: { estimated: 8, spent: 3, logs: [{ date: new Date(), hours: 3, description: 'Initial work' }] } }); expect(success).toBe(true); const updatedTask = board.tasks.find(t => t.id === task!.id); expect(updatedTask!.timeTracking!.spent).toBe(3); expect(updatedTask!.timeTracking!.logs).toHaveLength(1); }); }); describe('Tags Management', () => { it('should use tags instead of labels', () => { const task = store.addTask('Test Board', { title: 'Tagged task', column: 'To Do', tags: ['frontend', 'urgent'] }); expect(task!.tags).toEqual(['frontend', 'urgent']); }); it('should support legacy labels property', () => { const task = store.addTask('Test Board', { title: 'Legacy task', column: 'To Do', labels: ['backend', 'bug'] }); // Should convert labels to tags expect(task!.tags).toEqual(['backend', 'bug']); }); }); describe('Comments and Collaboration', () => { it('should add comments to task', () => { const task = store.addTask('Test Board', { title: 'Task with comments', column: 'To Do', comments: [] }); const comment = { id: 'comment-1', author: 'developer', content: 'Working on this now', createdAt: new Date() }; const success = store.updateTask('Test Board', task!.id, { comments: [comment] }); expect(success).toBe(true); const updatedTask = board.tasks.find(t => t.id === task!.id); expect(updatedTask!.comments).toHaveLength(1); expect(updatedTask!.comments[0].content).toBe('Working on this now'); }); it('should track watchers on task', () => { const task = store.addTask('Test Board', { title: 'Watched task', column: 'To Do', watchers: ['user1', 'user2'] }); expect(task!.watchers).toEqual(['user1', 'user2']); }); }); describe('Subtasks', () => { it('should add subtasks to main task', () => { const task = store.addTask('Test Board', { title: 'Main task', column: 'To Do', subtasks: [ { id: 'subtask-1', title: 'Setup environment', completed: false }, { id: 'subtask-2', title: 'Write tests', completed: false } ] }); expect(task!.subtasks).toHaveLength(2); expect(task!.subtasks[0].title).toBe('Setup environment'); }); it('should update subtask completion', () => { const task = store.addTask('Test Board', { title: 'Task', column: 'To Do', subtasks: [{ id: 'subtask-1', title: 'Subtask', completed: false }] }); const success = store.updateTask('Test Board', task!.id, { subtasks: [{ id: 'subtask-1', title: 'Subtask', completed: true, completedAt: new Date() }] }); expect(success).toBe(true); const updatedTask = board.tasks.find(t => t.id === task!.id); expect(updatedTask!.subtasks[0].completed).toBe(true); }); }); describe('Attachments', () => { it('should add attachments to task', () => { const task = store.addTask('Test Board', { title: 'Task with files', column: 'To Do', attachments: [ { id: 'attach-1', filename: 'design.pdf', url: '/files/design.pdf', uploadedBy: 'designer', uploadedAt: new Date() } ] }); expect(task!.attachments).toHaveLength(1); expect(task!.attachments[0].filename).toBe('design.pdf'); }); }); describe('Custom Fields', () => { it('should support custom fields', () => { const task = store.addTask('Test Board', { title: 'Task with custom data', column: 'To Do', customFields: [ { key: 'client', value: 'ACME Corp' }, { key: 'invoice_number', value: 'INV-2024-001' } ] }); expect(task!.customFields).toHaveLength(2); expect(task!.customFields.find(f => f.key === 'client')?.value).toBe('ACME Corp'); }); }); });