Files
claude-task-master/tests/e2e/tests/commands/remove-task.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

325 lines
13 KiB
JavaScript

/**
* E2E tests for remove-task command
* Tests task removal functionality
*/
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
import { mkdtempSync, existsSync, readFileSync, rmSync, writeFileSync, mkdirSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';
describe('task-master remove-task', () => {
let testDir;
let helpers;
beforeEach(async () => {
// Create test directory
testDir = mkdtempSync(join(tmpdir(), 'task-master-remove-task-'));
// Initialize test helpers
const context = global.createTestContext('remove-task');
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);
// Ensure tasks.json exists (bug workaround)
const tasksJsonPath = join(testDir, '.taskmaster/tasks/tasks.json');
if (!existsSync(tasksJsonPath)) {
mkdirSync(join(testDir, '.taskmaster/tasks'), { recursive: true });
writeFileSync(tasksJsonPath, JSON.stringify({ master: { tasks: [] } }));
}
});
afterEach(() => {
// Clean up test directory
if (testDir && existsSync(testDir)) {
rmSync(testDir, { recursive: true, force: true });
}
});
describe('Basic task removal', () => {
it('should remove a single task', async () => {
// Create a task
const task = await helpers.taskMaster('add-task', ['--title', 'Task to remove', '--description', 'This will be removed'], { cwd: testDir });
const taskId = helpers.extractTaskId(task.stdout);
// Remove the task
const result = await helpers.taskMaster('remove-task', ['--id', taskId], { cwd: testDir });
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain('Successfully removed task');
expect(result.stdout).toContain(taskId);
// Verify task is gone
const listResult = await helpers.taskMaster('list', [], { cwd: testDir });
expect(listResult.stdout).not.toContain('Task to remove');
});
it('should remove task with confirmation prompt bypassed', async () => {
// Create a task
const task = await helpers.taskMaster('add-task', ['--title', 'Task to force remove', '--description', 'Will be removed with force'], { cwd: testDir });
const taskId = helpers.extractTaskId(task.stdout);
// Remove with force flag
const result = await helpers.taskMaster('remove-task', ['--id', taskId, '--force'], { cwd: testDir });
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain('Successfully removed task');
});
it('should remove multiple tasks', async () => {
// Create multiple tasks
const task1 = await helpers.taskMaster('add-task', ['--title', 'First task', '--description', 'To be removed'], { cwd: testDir });
const taskId1 = helpers.extractTaskId(task1.stdout);
const task2 = await helpers.taskMaster('add-task', ['--title', 'Second task', '--description', 'Also to be removed'], { cwd: testDir });
const taskId2 = helpers.extractTaskId(task2.stdout);
const task3 = await helpers.taskMaster('add-task', ['--title', 'Third task', '--description', 'Will remain'], { cwd: testDir });
const taskId3 = helpers.extractTaskId(task3.stdout);
// Remove first two tasks
const result = await helpers.taskMaster('remove-task', ['--id', `${taskId1},${taskId2}`, '--force'], { cwd: testDir });
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain('Successfully removed');
// Verify correct tasks were removed
const listResult = await helpers.taskMaster('list', [], { cwd: testDir });
expect(listResult.stdout).not.toContain('First task');
expect(listResult.stdout).not.toContain('Second task');
expect(listResult.stdout).toContain('Third task');
});
});
describe('Error handling', () => {
it('should fail when removing non-existent task', async () => {
const result = await helpers.taskMaster('remove-task', ['--id', '999', '--force'], {
cwd: testDir,
allowFailure: true
});
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toContain('not found');
});
it('should fail when task ID is not provided', async () => {
const result = await helpers.taskMaster('remove-task', [], {
cwd: testDir,
allowFailure: true
});
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toContain('required');
});
it('should handle invalid task ID format', async () => {
const result = await helpers.taskMaster('remove-task', ['--id', 'invalid-id', '--force'], {
cwd: testDir,
allowFailure: true
});
expect(result.exitCode).not.toBe(0);
});
});
describe('Task with dependencies', () => {
it('should warn when removing task that others depend on', async () => {
// Create dependent tasks
const task1 = await helpers.taskMaster('add-task', ['--title', 'Base task', '--description', 'Others depend on this'], { cwd: testDir });
const taskId1 = helpers.extractTaskId(task1.stdout);
const task2 = await helpers.taskMaster('add-task', ['--title', 'Dependent task', '--description', 'Depends on base'], { cwd: testDir });
const taskId2 = helpers.extractTaskId(task2.stdout);
// Add dependency
await helpers.taskMaster('add-dependency', ['--id', taskId2, '--depends-on', taskId1], { cwd: testDir });
// Try to remove base task
const result = await helpers.taskMaster('remove-task', ['--id', taskId1, '--force'], { cwd: testDir });
// Should either warn or update dependent tasks
expect(result).toHaveExitCode(0);
});
it('should handle removing task with dependencies', async () => {
// Create tasks with dependency chain
const task1 = await helpers.taskMaster('add-task', ['--title', 'Dependency 1', '--description', 'First dep'], { cwd: testDir });
const taskId1 = helpers.extractTaskId(task1.stdout);
const task2 = await helpers.taskMaster('add-task', ['--title', 'Main task', '--description', 'Has dependencies'], { cwd: testDir });
const taskId2 = helpers.extractTaskId(task2.stdout);
// Add dependency
await helpers.taskMaster('add-dependency', ['--id', taskId2, '--depends-on', taskId1], { cwd: testDir });
// Remove the main task (with dependencies)
const result = await helpers.taskMaster('remove-task', ['--id', taskId2, '--force'], { cwd: testDir });
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain('Successfully removed task');
// Dependency task should still exist
const listResult = await helpers.taskMaster('list', [], { cwd: testDir });
expect(listResult.stdout).toContain('Dependency 1');
expect(listResult.stdout).not.toContain('Main task');
});
});
describe('Task with subtasks', () => {
it('should remove task and all its subtasks', async () => {
// Create parent task
const parent = await helpers.taskMaster('add-task', ['--title', 'Parent task', '--description', 'Has subtasks'], { cwd: testDir });
const parentId = helpers.extractTaskId(parent.stdout);
// Expand to create subtasks
await helpers.taskMaster('expand', ['-i', parentId, '-n', '3'], {
cwd: testDir,
timeout: 60000
});
// Remove parent task
const result = await helpers.taskMaster('remove-task', ['--id', parentId, '--force'], { cwd: testDir });
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain('Successfully removed task');
// Verify parent and subtasks are gone
const listResult = await helpers.taskMaster('list', [], { cwd: testDir });
expect(listResult.stdout).not.toContain('Parent task');
expect(listResult.stdout).not.toContain(`${parentId}.1`);
expect(listResult.stdout).not.toContain(`${parentId}.2`);
expect(listResult.stdout).not.toContain(`${parentId}.3`);
});
it('should remove only subtask when specified', async () => {
// Create parent task with subtasks
const parent = await helpers.taskMaster('add-task', ['--title', 'Parent with subtasks', '--description', 'Parent task'], { cwd: testDir });
const parentId = helpers.extractTaskId(parent.stdout);
// Expand to create subtasks
await helpers.taskMaster('expand', ['-i', parentId, '-n', '3'], {
cwd: testDir,
timeout: 60000
});
// Remove only one subtask
const result = await helpers.taskMaster('remove-task', ['--id', `${parentId}.2`, '--force'], { cwd: testDir });
expect(result).toHaveExitCode(0);
// Verify parent and other subtasks still exist
const showResult = await helpers.taskMaster('show', [parentId], { cwd: testDir });
expect(showResult.stdout).toContain('Parent with subtasks');
expect(showResult.stdout).toContain(`${parentId}.1`);
expect(showResult.stdout).not.toContain(`${parentId}.2`);
expect(showResult.stdout).toContain(`${parentId}.3`);
});
});
describe('Tag context', () => {
it('should remove task from specific tag', async () => {
// Create tag and add tasks
await helpers.taskMaster('add-tag', ['feature'], { cwd: testDir });
// Add task to master
const masterTask = await helpers.taskMaster('add-task', ['--title', 'Master task', '--description', 'In master'], { cwd: testDir });
const masterId = helpers.extractTaskId(masterTask.stdout);
// Add task to feature tag
await helpers.taskMaster('use-tag', ['feature'], { cwd: testDir });
const featureTask = await helpers.taskMaster('add-task', ['--title', 'Feature task', '--description', 'In feature'], { cwd: testDir });
const featureId = helpers.extractTaskId(featureTask.stdout);
// Remove task from feature tag
const result = await helpers.taskMaster('remove-task', ['--id', featureId, '--tag', 'feature', '--force'], { cwd: testDir });
expect(result).toHaveExitCode(0);
// Verify only feature task was removed
await helpers.taskMaster('use-tag', ['master'], { cwd: testDir });
const masterList = await helpers.taskMaster('list', [], { cwd: testDir });
expect(masterList.stdout).toContain('Master task');
await helpers.taskMaster('use-tag', ['feature'], { cwd: testDir });
const featureList = await helpers.taskMaster('list', [], { cwd: testDir });
expect(featureList.stdout).not.toContain('Feature task');
});
});
describe('Status considerations', () => {
it('should remove tasks in different statuses', async () => {
// Create tasks with different statuses
const pendingTask = await helpers.taskMaster('add-task', ['--title', 'Pending task', '--description', 'Status: pending'], { cwd: testDir });
const pendingId = helpers.extractTaskId(pendingTask.stdout);
const inProgressTask = await helpers.taskMaster('add-task', ['--title', 'In progress task', '--description', 'Status: in-progress'], { cwd: testDir });
const inProgressId = helpers.extractTaskId(inProgressTask.stdout);
await helpers.taskMaster('set-status', ['--id', inProgressId, '--status', 'in-progress'], { cwd: testDir });
const doneTask = await helpers.taskMaster('add-task', ['--title', 'Done task', '--description', 'Status: done'], { cwd: testDir });
const doneId = helpers.extractTaskId(doneTask.stdout);
await helpers.taskMaster('set-status', ['--id', doneId, '--status', 'done'], { cwd: testDir });
// Remove all tasks
const result = await helpers.taskMaster('remove-task', ['--id', `${pendingId},${inProgressId},${doneId}`, '--force'], { cwd: testDir });
expect(result).toHaveExitCode(0);
// Verify all are removed
const listResult = await helpers.taskMaster('list', ['--all'], { cwd: testDir });
expect(listResult.stdout).not.toContain('Pending task');
expect(listResult.stdout).not.toContain('In progress task');
expect(listResult.stdout).not.toContain('Done task');
});
it('should warn when removing in-progress task', async () => {
// Create in-progress task
const task = await helpers.taskMaster('add-task', ['--title', 'Active task', '--description', 'Currently being worked on'], { cwd: testDir });
const taskId = helpers.extractTaskId(task.stdout);
await helpers.taskMaster('set-status', ['--id', taskId, '--status', 'in-progress'], { cwd: testDir });
// Remove without force (if interactive prompt is supported)
const result = await helpers.taskMaster('remove-task', ['--id', taskId, '--force'], { cwd: testDir });
// Should succeed with force flag
expect(result).toHaveExitCode(0);
});
});
describe('Output options', () => {
it('should support quiet mode', async () => {
const task = await helpers.taskMaster('add-task', ['--title', 'Quiet removal', '--description', 'Remove quietly'], { cwd: testDir });
const taskId = helpers.extractTaskId(task.stdout);
// Remove with quiet flag if supported
const result = await helpers.taskMaster('remove-task', ['--id', taskId, '--force', '-q'], { cwd: testDir });
expect(result).toHaveExitCode(0);
// Output should be minimal or empty
});
it('should show detailed output in verbose mode', async () => {
const task = await helpers.taskMaster('add-task', ['--title', 'Verbose removal', '--description', 'Remove with details'], { cwd: testDir });
const taskId = helpers.extractTaskId(task.stdout);
// Remove with verbose flag if supported
const result = await helpers.taskMaster('remove-task', ['--id', taskId, '--force'], { cwd: testDir });
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain('Successfully removed task');
});
});
});