Files
claude-task-master/tests/e2e/tests/commands/remove-dependency.test.js
Ralph Khreish 2577c95e65 feat(e2e): implement whole test suite
- some elements and tests still broken, but did the 80%
2025-07-19 00:18:37 +03:00

259 lines
8.2 KiB
JavaScript

import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
import { mkdtempSync, existsSync, readFileSync, rmSync, writeFileSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { tmpdir } from 'os';
describe('task-master remove-dependency command', () => {
let testDir;
let helpers;
let tasksPath;
beforeEach(async () => {
// Create test directory
testDir = mkdtempSync(join(tmpdir(), 'task-master-remove-dependency-command-'));
// Initialize test helpers
const context = global.createTestContext('remove-dependency command');
helpers = context.helpers;
// Copy .env file if it exists
const mainEnvPath = join(process.cwd(), '.env');
const testEnvPath = join(testDir, '.env');
if (existsSync(mainEnvPath)) {
const envContent = readFileSync(mainEnvPath, 'utf8');
writeFileSync(testEnvPath, envContent);
}
// Initialize task-master project
const initResult = await helpers.taskMaster('init', ['-y'], {
cwd: testDir
});
expect(initResult).toHaveExitCode(0);
// Set up tasks path
tasksPath = join(testDir, '.taskmaster', 'tasks', 'tasks.json');
// Create test tasks with dependencies
const testTasks = {
master: {
tasks: [
{
id: 1,
description: 'Task 1 - Independent',
status: 'pending',
priority: 'high',
dependencies: [],
subtasks: []
},
{
id: 2,
description: 'Task 2 - Depends on 1',
status: 'pending',
priority: 'medium',
dependencies: [1],
subtasks: []
},
{
id: 3,
description: 'Task 3 - Depends on 1 and 2',
status: 'pending',
priority: 'low',
dependencies: [1, 2],
subtasks: [
{
id: 1,
description: 'Subtask 3.1',
status: 'pending',
priority: 'medium',
dependencies: ['1', '2']
}
]
},
{
id: 4,
description: 'Task 4 - Complex dependencies',
status: 'pending',
priority: 'high',
dependencies: [1, 2, 3],
subtasks: []
}
]
}
};
// Ensure .taskmaster directory exists
mkdirSync(dirname(tasksPath), { recursive: true });
writeFileSync(tasksPath, JSON.stringify(testTasks, null, 2));
});
afterEach(() => {
// Clean up test directory
if (testDir && existsSync(testDir)) {
rmSync(testDir, { recursive: true, force: true });
}
});
it('should remove a dependency from a task', async () => {
// Run remove-dependency command
const result = await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-i', '2', '-d', '1'], { cwd: testDir });
// Verify success
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain('Removing dependency');
expect(result.stdout).toContain('from task 2');
// Read updated tasks
const updatedTasks = JSON.parse(readFileSync(tasksPath, 'utf8'));
const task2 = updatedTasks.master.tasks.find(t => t.id === 2);
// Verify dependency was removed
expect(task2.dependencies).toEqual([]);
});
it('should remove one dependency while keeping others', async () => {
// Run remove-dependency command to remove dependency 1 from task 3
const result = await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-i', '3', '-d', '1'], { cwd: testDir });
// Verify success
expect(result).toHaveExitCode(0);
// Read updated tasks
const updatedTasks = JSON.parse(readFileSync(tasksPath, 'utf8'));
const task3 = updatedTasks.master.tasks.find(t => t.id === 3);
// Verify only dependency 1 was removed, dependency 2 remains
expect(task3.dependencies).toEqual([2]);
});
it('should handle removing all dependencies from a task', async () => {
// Remove all dependencies from task 4 one by one
await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-i', '4', '-d', '1'], { cwd: testDir });
await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-i', '4', '-d', '2'], { cwd: testDir });
const result = await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-i', '4', '-d', '3'], { cwd: testDir });
expect(result).toHaveExitCode(0);
// Read updated tasks
const updatedTasks = JSON.parse(readFileSync(tasksPath, 'utf8'));
const task4 = updatedTasks.master.tasks.find(t => t.id === 4);
// Verify all dependencies were removed
expect(task4.dependencies).toEqual([]);
});
it('should handle subtask dependencies', async () => {
// Run remove-dependency command for subtask
const result = await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-i', '3.1', '-d', '1'], { cwd: testDir });
// Verify success
expect(result).toHaveExitCode(0);
// Read updated tasks
const updatedTasks = JSON.parse(readFileSync(tasksPath, 'utf8'));
const task3 = updatedTasks.master.tasks.find(t => t.id === 3);
const subtask = task3.subtasks.find(s => s.id === 1);
// Verify subtask dependency was removed
expect(subtask.dependencies).toEqual(['2']);
});
it('should fail when required parameters are missing', async () => {
// Run without --id
const result1 = await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-d', '1'], { cwd: testDir, allowFailure: true });
expect(result1.exitCode).not.toBe(0);
expect(result1.stderr).toContain('Error');
expect(result1.stderr).toContain('Both --id and --depends-on are required');
// Run without --depends-on
const result2 = await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-i', '2'], { cwd: testDir, allowFailure: true });
expect(result2.exitCode).not.toBe(0);
expect(result2.stderr).toContain('Error');
expect(result2.stderr).toContain('Both --id and --depends-on are required');
});
it('should handle removing non-existent dependency', async () => {
// Try to remove a dependency that doesn't exist
const result = await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-i', '1', '-d', '999'], { cwd: testDir });
// Should succeed (no-op)
expect(result).toHaveExitCode(0);
// Task should remain unchanged
const updatedTasks = JSON.parse(readFileSync(tasksPath, 'utf8'));
const task1 = updatedTasks.master.tasks.find(t => t.id === 1);
expect(task1.dependencies).toEqual([]);
});
it('should handle non-existent task', async () => {
// Try to remove dependency from non-existent task
const result = await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-i', '999', '-d', '1'], { cwd: testDir, allowFailure: true });
// Should fail gracefully
expect(result.exitCode).not.toBe(0);
// The command might succeed gracefully or show error - let's just check it doesn't crash
if (result.stderr) {
expect(result.stderr.length).toBeGreaterThan(0);
}
});
it('should work with tag option', async () => {
// Create tasks with different tags
const multiTagTasks = {
master: {
tasks: [{
id: 1,
description: 'Master task',
dependencies: [2]
}]
},
feature: {
tasks: [{
id: 1,
description: 'Feature task',
dependencies: [2, 3]
}]
}
};
writeFileSync(tasksPath, JSON.stringify(multiTagTasks, null, 2));
// Remove dependency from feature tag
const result = await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-i', '1', '-d', '2', '--tag', 'feature'], { cwd: testDir });
expect(result).toHaveExitCode(0);
// Verify only feature tag was affected
const updatedTasks = JSON.parse(readFileSync(tasksPath, 'utf8'));
expect(updatedTasks.master.tasks[0].dependencies).toEqual([2]);
expect(updatedTasks.feature.tasks[0].dependencies).toEqual([3]);
});
it('should handle mixed dependency types', async () => {
// Create task with mixed dependency types (numbers and strings)
const mixedTasks = {
master: {
tasks: [{
id: 5,
description: 'Task with mixed deps',
dependencies: [1, '2', 3, '4.1'],
subtasks: []
}]
}
};
writeFileSync(tasksPath, JSON.stringify(mixedTasks, null, 2));
// Remove string dependency
const result = await helpers.taskMaster('remove-dependency', ['-f', tasksPath, '-i', '5', '-d', '4.1'], { cwd: testDir });
expect(result).toHaveExitCode(0);
// Verify correct dependency was removed
const updatedTasks = JSON.parse(readFileSync(tasksPath, 'utf8'));
const task5 = updatedTasks.master.tasks.find(t => t.id === 5);
expect(task5.dependencies).toEqual([1, '2', 3]);
});
});