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

1014 lines
27 KiB
JavaScript

/**
* Comprehensive E2E tests for move command
* Tests moving tasks and subtasks to different positions
*/
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('move command', () => {
let testDir;
let helpers;
beforeEach(async () => {
// Create test directory
testDir = mkdtempSync(join(tmpdir(), 'task-master-move-'));
// Initialize test helpers
const context = global.createTestContext('move');
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('Moving tasks to different positions', () => {
it('should move a task to a new ID', async () => {
// Create test tasks
const task1 = await helpers.taskMaster(
'add-task',
['--title', 'Task 1', '--description', 'First task'],
{ cwd: testDir }
);
const taskId1 = helpers.extractTaskId(task1.stdout);
await helpers.taskMaster(
'add-task',
['--title', 'Task 2', '--description', 'Second task'],
{ cwd: testDir }
);
// Move task 1 to position 3
const result = await helpers.taskMaster(
'move',
['--from', taskId1, '--to', '3'],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain(`Successfully moved task/subtask ${taskId1} to 3`);
// Verify the move
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
expect(tasks.master.tasks.find(t => t.id === 3)).toBeDefined();
expect(tasks.master.tasks.find(t => t.id === 3).title).toBe('Task 1');
expect(tasks.master.tasks.find(t => t.id === parseInt(taskId1))).toBeUndefined();
});
it('should handle moving to an existing task ID', async () => {
// Create test tasks
await helpers.taskMaster(
'add-task',
['--title', 'Task 1', '--description', 'First task'],
{ cwd: testDir }
);
const task2 = await helpers.taskMaster(
'add-task',
['--title', 'Task 2', '--description', 'Second task'],
{ cwd: testDir }
);
const taskId2 = helpers.extractTaskId(task2.stdout);
// Try to move task 1 to task 2's position
const result = await helpers.taskMaster(
'move',
['--from', '1', '--to', taskId2],
{ cwd: testDir, allowFailure: true }
);
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toContain('already exists');
});
it('should handle moving to the same position', async () => {
// Create a task
const task = await helpers.taskMaster(
'add-task',
['--title', 'Task 1', '--description', 'First task'],
{ cwd: testDir }
);
const taskId = helpers.extractTaskId(task.stdout);
// Move task to same position
const result = await helpers.taskMaster(
'move',
['--from', taskId, '--to', taskId],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain(`Skipping ${taskId} -> ${taskId} (same ID)`);
});
it('should update dependencies when moving a task', async () => {
// Create dependency task
const task1 = await helpers.taskMaster(
'add-task',
['--title', 'Dependency', '--description', 'Will be depended on'],
{ cwd: testDir }
);
const taskId1 = helpers.extractTaskId(task1.stdout);
// Create task that depends on it
await helpers.taskMaster(
'add-task',
[
'--title',
'Dependent task',
'--description',
'Depends on task 1',
'--dependencies',
taskId1
],
{ cwd: testDir }
);
// Move dependency task to new ID
const result = await helpers.taskMaster(
'move',
['--from', taskId1, '--to', '5'],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Verify dependencies were updated
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
const dependentTask = tasks.master.tasks.find(t => t.title === 'Dependent task');
expect(dependentTask.dependencies).toContain(5);
expect(dependentTask.dependencies).not.toContain(parseInt(taskId1));
});
});
describe('Moving subtasks within parent', () => {
let parentTaskId;
beforeEach(async () => {
// Create parent task
const parentResult = await helpers.taskMaster(
'add-task',
['--title', 'Parent task', '--description', 'Has subtasks'],
{ cwd: testDir }
);
parentTaskId = helpers.extractTaskId(parentResult.stdout);
// Add subtasks
await helpers.taskMaster(
'add-subtask',
[
'--parent',
parentTaskId,
'--title',
'Subtask 1',
'--description',
'First subtask'
],
{ cwd: testDir }
);
await helpers.taskMaster(
'add-subtask',
[
'--parent',
parentTaskId,
'--title',
'Subtask 2',
'--description',
'Second subtask'
],
{ cwd: testDir }
);
await helpers.taskMaster(
'add-subtask',
[
'--parent',
parentTaskId,
'--title',
'Subtask 3',
'--description',
'Third subtask'
],
{ cwd: testDir }
);
});
it('should move subtask within the same parent', async () => {
const fromId = `${parentTaskId}.1`;
const toId = `${parentTaskId}.3`;
const result = await helpers.taskMaster(
'move',
['--from', fromId, '--to', toId],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain(`Successfully moved task/subtask ${fromId} to ${toId}`);
// Verify the move
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
const parent = tasks.master.tasks.find(t => t.id === parseInt(parentTaskId));
// Subtask 1 should now be after subtask 3
const subtaskTitles = parent.subtasks.map(st => st.title);
expect(subtaskTitles.indexOf('Subtask 1')).toBeGreaterThan(subtaskTitles.indexOf('Subtask 3'));
});
it('should move subtask to first position', async () => {
const fromId = `${parentTaskId}.3`;
const toId = `${parentTaskId}.1`;
const result = await helpers.taskMaster(
'move',
['--from', fromId, '--to', toId],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Verify the move
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
const parent = tasks.master.tasks.find(t => t.id === parseInt(parentTaskId));
// Subtask 3 should now be before subtask 1
const subtaskTitles = parent.subtasks.map(st => st.title);
expect(subtaskTitles.indexOf('Subtask 3')).toBeLessThan(subtaskTitles.indexOf('Subtask 1'));
});
it('should handle moving to non-existent subtask position', async () => {
const fromId = `${parentTaskId}.1`;
const toId = `${parentTaskId}.99`; // Non-existent position
const result = await helpers.taskMaster(
'move',
['--from', fromId, '--to', toId],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Should move to end when position doesn't exist
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
const parent = tasks.master.tasks.find(t => t.id === parseInt(parentTaskId));
// Subtask 1 should be at the end
const lastSubtask = parent.subtasks[parent.subtasks.length - 1];
expect(lastSubtask.title).toBe('Subtask 1');
});
});
describe('Moving subtasks between parents', () => {
let parentTaskId1, parentTaskId2;
beforeEach(async () => {
// Create two parent tasks
const parent1 = await helpers.taskMaster(
'add-task',
['--title', 'Parent 1', '--description', 'First parent'],
{ cwd: testDir }
);
parentTaskId1 = helpers.extractTaskId(parent1.stdout);
const parent2 = await helpers.taskMaster(
'add-task',
['--title', 'Parent 2', '--description', 'Second parent'],
{ cwd: testDir }
);
parentTaskId2 = helpers.extractTaskId(parent2.stdout);
// Add subtasks to parent 1
await helpers.taskMaster(
'add-subtask',
[
'--parent',
parentTaskId1,
'--title',
'Subtask A',
'--description',
'From parent 1'
],
{ cwd: testDir }
);
await helpers.taskMaster(
'add-subtask',
[
'--parent',
parentTaskId1,
'--title',
'Subtask B',
'--description',
'From parent 1'
],
{ cwd: testDir }
);
// Add subtasks to parent 2
await helpers.taskMaster(
'add-subtask',
[
'--parent',
parentTaskId2,
'--title',
'Subtask X',
'--description',
'From parent 2'
],
{ cwd: testDir }
);
});
it('should move subtask from one parent to another', async () => {
const fromId = `${parentTaskId1}.1`; // Subtask A
const toId = `${parentTaskId2}.2`; // After Subtask X
const result = await helpers.taskMaster(
'move',
['--from', fromId, '--to', toId],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Verify the move
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
const parent1 = tasks.master.tasks.find(t => t.id === parseInt(parentTaskId1));
const parent2 = tasks.master.tasks.find(t => t.id === parseInt(parentTaskId2));
// Parent 1 should have one less subtask
expect(parent1.subtasks.length).toBe(1);
expect(parent1.subtasks.find(st => st.title === 'Subtask A')).toBeUndefined();
// Parent 2 should have the moved subtask
expect(parent2.subtasks.length).toBe(2);
expect(parent2.subtasks.find(st => st.title === 'Subtask A')).toBeDefined();
});
it('should handle moving to empty parent', async () => {
// Create parent with no subtasks
const parent3 = await helpers.taskMaster(
'add-task',
['--title', 'Parent 3', '--description', 'Empty parent'],
{ cwd: testDir }
);
const parentTaskId3 = helpers.extractTaskId(parent3.stdout);
const fromId = `${parentTaskId1}.1`;
const toId = `${parentTaskId3}.1`;
const result = await helpers.taskMaster(
'move',
['--from', fromId, '--to', toId],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Verify the move
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
const parent3Task = tasks.master.tasks.find(t => t.id === parseInt(parentTaskId3));
expect(parent3Task.subtasks).toBeDefined();
expect(parent3Task.subtasks.length).toBe(1);
expect(parent3Task.subtasks[0].title).toBe('Subtask A');
});
});
describe('Converting between tasks and subtasks', () => {
it('should convert subtask to task', async () => {
// Create parent with subtask
const parent = await helpers.taskMaster(
'add-task',
['--title', 'Parent task', '--description', 'Has subtask'],
{ cwd: testDir }
);
const parentId = helpers.extractTaskId(parent.stdout);
await helpers.taskMaster(
'add-subtask',
[
'--parent',
parentId,
'--title',
'Subtask to promote',
'--description',
'Will become a task'
],
{ cwd: testDir }
);
// Move subtask to task
const fromId = `${parentId}.1`;
const toId = '10';
const result = await helpers.taskMaster(
'move',
['--from', fromId, '--to', toId],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain(`Converted subtask ${fromId} to task ${toId}`);
// Verify conversion
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
// Should exist as task
const newTask = tasks.master.tasks.find(t => t.id === 10);
expect(newTask).toBeDefined();
expect(newTask.title).toBe('Subtask to promote');
// Should not exist as subtask anymore
const parentTask = tasks.master.tasks.find(t => t.id === parseInt(parentId));
expect(parentTask.subtasks.length).toBe(0);
});
it('should convert task to subtask', async () => {
// Create regular task
const task = await helpers.taskMaster(
'add-task',
['--title', 'Task to demote', '--description', 'Will become subtask'],
{ cwd: testDir }
);
const taskId = helpers.extractTaskId(task.stdout);
// Create parent task
const parent = await helpers.taskMaster(
'add-task',
['--title', 'Parent task', '--description', 'Will receive subtask'],
{ cwd: testDir }
);
const parentId = helpers.extractTaskId(parent.stdout);
// Move task to subtask
const toId = `${parentId}.1`;
const result = await helpers.taskMaster(
'move',
['--from', taskId, '--to', toId],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain(`Converted task ${taskId} to subtask ${toId}`);
// Verify conversion
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
// Should not exist as task
const oldTask = tasks.master.tasks.find(t => t.id === parseInt(taskId));
expect(oldTask).toBeUndefined();
// Should exist as subtask
const parentTask = tasks.master.tasks.find(t => t.id === parseInt(parentId));
expect(parentTask.subtasks).toBeDefined();
expect(parentTask.subtasks.length).toBe(1);
expect(parentTask.subtasks[0].title).toBe('Task to demote');
});
it('should handle task with subtasks when converting to subtask', async () => {
// Create task with subtasks
const task = await helpers.taskMaster(
'add-task',
['--title', 'Task with subtasks', '--description', 'Has children'],
{ cwd: testDir }
);
const taskId = helpers.extractTaskId(task.stdout);
await helpers.taskMaster(
'add-subtask',
[
'--parent',
taskId,
'--title',
'Child subtask',
'--description',
'Subtask of task'
],
{ cwd: testDir }
);
// Create parent task
const parent = await helpers.taskMaster(
'add-task',
['--title', 'New parent', '--description', 'Will receive task'],
{ cwd: testDir }
);
const parentId = helpers.extractTaskId(parent.stdout);
// Move task with subtasks to become subtask
const toId = `${parentId}.1`;
const result = await helpers.taskMaster(
'move',
['--from', taskId, '--to', toId],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Verify the task's subtasks are preserved (or handled appropriately)
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
const parentTask = tasks.master.tasks.find(t => t.id === parseInt(parentId));
// The converted subtask should exist
expect(parentTask.subtasks[0].title).toBe('Task with subtasks');
});
});
describe('Batch moving multiple tasks', () => {
it('should move multiple tasks at once', async () => {
// Create test tasks
await helpers.taskMaster(
'add-task',
['--title', 'Task 1', '--description', 'First'],
{ cwd: testDir }
);
await helpers.taskMaster(
'add-task',
['--title', 'Task 2', '--description', 'Second'],
{ cwd: testDir }
);
await helpers.taskMaster(
'add-task',
['--title', 'Task 3', '--description', 'Third'],
{ cwd: testDir }
);
// Move multiple tasks
const result = await helpers.taskMaster(
'move',
['--from', '1,2', '--to', '10,11'],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain('Moving multiple tasks');
expect(result.stdout).toContain('Successfully moved task/subtask 1 to 10');
expect(result.stdout).toContain('Successfully moved task/subtask 2 to 11');
// Verify moves
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
expect(tasks.master.tasks.find(t => t.id === 10)).toBeDefined();
expect(tasks.master.tasks.find(t => t.id === 11)).toBeDefined();
expect(tasks.master.tasks.find(t => t.id === 1)).toBeUndefined();
expect(tasks.master.tasks.find(t => t.id === 2)).toBeUndefined();
});
it('should handle mismatched source and destination counts', async () => {
const result = await helpers.taskMaster(
'move',
['--from', '1,2,3', '--to', '10,11'],
{ cwd: testDir, allowFailure: true }
);
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toContain('number of source and destination IDs must match');
});
it('should skip same ID moves in batch', async () => {
// Create tasks
await helpers.taskMaster(
'add-task',
['--title', 'Task 1', '--description', 'First'],
{ cwd: testDir }
);
await helpers.taskMaster(
'add-task',
['--title', 'Task 2', '--description', 'Second'],
{ cwd: testDir }
);
// Move with one same ID
const result = await helpers.taskMaster(
'move',
['--from', '1,2', '--to', '1,3'],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
expect(result.stdout).toContain('Skipping 1 -> 1 (same ID)');
expect(result.stdout).toContain('Successfully moved task/subtask 2 to 3');
});
});
describe('Tag support', () => {
beforeEach(async () => {
// Create a new tag
await helpers.taskMaster(
'add-tag',
['feature-branch', '--description', 'Feature work'],
{ cwd: testDir }
);
// Add tasks to feature tag
await helpers.taskMaster(
'add-task',
[
'--title',
'Feature task 1',
'--description',
'In feature branch',
'--tag',
'feature-branch'
],
{ cwd: testDir }
);
await helpers.taskMaster(
'add-task',
[
'--title',
'Feature task 2',
'--description',
'Also in feature branch',
'--tag',
'feature-branch'
],
{ cwd: testDir }
);
});
it('should move tasks within a specific tag', async () => {
const result = await helpers.taskMaster(
'move',
['--from', '1', '--to', '3', '--tag', 'feature-branch'],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Verify move in correct tag
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
expect(tasks['feature-branch'].tasks.find(t => t.id === 3)).toBeDefined();
expect(tasks['feature-branch'].tasks.find(t => t.id === 3).title).toBe('Feature task 1');
});
it('should respect current tag when no tag specified', async () => {
// Switch to feature tag
await helpers.taskMaster('use-tag', ['feature-branch'], { cwd: testDir });
const result = await helpers.taskMaster(
'move',
['--from', '2', '--to', '4'],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Verify move happened in feature tag
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
expect(tasks['feature-branch'].tasks.find(t => t.id === 4)).toBeDefined();
expect(tasks['feature-branch'].tasks.find(t => t.id === 4).title).toBe('Feature task 2');
});
});
describe('Error handling', () => {
it('should handle missing --from parameter', async () => {
const result = await helpers.taskMaster(
'move',
['--to', '5'],
{ cwd: testDir, allowFailure: true }
);
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toContain('Both --from and --to parameters are required');
});
it('should handle missing --to parameter', async () => {
const result = await helpers.taskMaster(
'move',
['--from', '1'],
{ cwd: testDir, allowFailure: true }
);
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toContain('Both --from and --to parameters are required');
});
it('should handle non-existent source task', async () => {
const result = await helpers.taskMaster(
'move',
['--from', '999', '--to', '1000'],
{ cwd: testDir, allowFailure: true }
);
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toContain('not found');
});
it('should handle non-existent source subtask', async () => {
// Create parent task
const parent = await helpers.taskMaster(
'add-task',
['--title', 'Parent', '--description', 'Parent task'],
{ cwd: testDir }
);
const parentId = helpers.extractTaskId(parent.stdout);
const result = await helpers.taskMaster(
'move',
['--from', `${parentId}.99`, '--to', `${parentId}.1`],
{ cwd: testDir, allowFailure: true }
);
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toContain('not found');
});
it('should handle non-existent parent task', async () => {
const result = await helpers.taskMaster(
'move',
['--from', '999.1', '--to', '1000.1'],
{ cwd: testDir, allowFailure: true }
);
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toContain('not found');
});
it('should handle invalid task file', async () => {
const invalidPath = join(testDir, 'invalid.json');
writeFileSync(invalidPath, '{ invalid json }');
const result = await helpers.taskMaster(
'move',
['--from', '1', '--to', '2', '--file', invalidPath],
{ cwd: testDir, allowFailure: true }
);
expect(result.exitCode).not.toBe(0);
});
});
describe('Edge cases', () => {
it('should handle moving first task', async () => {
// Create multiple tasks
await helpers.taskMaster(
'add-task',
['--title', 'First task', '--description', 'Task 1'],
{ cwd: testDir }
);
await helpers.taskMaster(
'add-task',
['--title', 'Second task', '--description', 'Task 2'],
{ cwd: testDir }
);
const result = await helpers.taskMaster(
'move',
['--from', '1', '--to', '3'],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Verify order
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
const taskIds = tasks.master.tasks.map(t => t.id);
expect(taskIds.indexOf(3)).toBeGreaterThan(taskIds.indexOf(2));
});
it('should handle moving last task', async () => {
// Create multiple tasks
await helpers.taskMaster(
'add-task',
['--title', 'First task', '--description', 'Task 1'],
{ cwd: testDir }
);
await helpers.taskMaster(
'add-task',
['--title', 'Second task', '--description', 'Task 2'],
{ cwd: testDir }
);
await helpers.taskMaster(
'add-task',
['--title', 'Third task', '--description', 'Task 3'],
{ cwd: testDir }
);
const result = await helpers.taskMaster(
'move',
['--from', '3', '--to', '0'],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Task should be moved with new ID
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
expect(tasks.master.tasks.find(t => t.id === 0)).toBeDefined();
expect(tasks.master.tasks.find(t => t.id === 0).title).toBe('Third task');
});
it('should preserve task properties when moving', async () => {
// Create task with all properties
const task = await helpers.taskMaster(
'add-task',
[
'--title',
'Complex task',
'--description',
'Has all properties',
'--priority',
'high',
'--details',
'Detailed information',
'--test-strategy',
'Unit tests required'
],
{ cwd: testDir }
);
const taskId = helpers.extractTaskId(task.stdout);
// Set status
await helpers.taskMaster(
'set-status',
['--id', taskId, '--status', 'in-progress'],
{ cwd: testDir }
);
// Move the task
const result = await helpers.taskMaster(
'move',
['--from', taskId, '--to', '10'],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Verify all properties preserved
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
const movedTask = tasks.master.tasks.find(t => t.id === 10);
expect(movedTask.title).toBe('Complex task');
expect(movedTask.description).toBe('Has all properties');
expect(movedTask.priority).toBe('high');
expect(movedTask.details).toBe('Detailed information');
expect(movedTask.testStrategy).toBe('Unit tests required');
expect(movedTask.status).toBe('in-progress');
});
it('should handle moving with subtask dependencies', async () => {
// Create parent with subtasks that have dependencies
const parent = await helpers.taskMaster(
'add-task',
['--title', 'Parent', '--description', 'Has dependent subtasks'],
{ cwd: testDir }
);
const parentId = helpers.extractTaskId(parent.stdout);
await helpers.taskMaster(
'add-subtask',
[
'--parent',
parentId,
'--title',
'Subtask 1',
'--description',
'First'
],
{ cwd: testDir }
);
await helpers.taskMaster(
'add-subtask',
[
'--parent',
parentId,
'--title',
'Subtask 2',
'--description',
'Depends on 1',
'--dependencies',
`${parentId}.1`
],
{ cwd: testDir }
);
// Move parent task to new ID
const result = await helpers.taskMaster(
'move',
['--from', parentId, '--to', '10'],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Verify subtask dependencies were updated
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
const tasks = helpers.readJson(tasksPath);
const movedParent = tasks.master.tasks.find(t => t.id === 10);
expect(movedParent.subtasks[1].dependencies).toContain('10.1');
expect(movedParent.subtasks[1].dependencies).not.toContain(`${parentId}.1`);
});
});
describe('Performance', () => {
it('should handle moving tasks efficiently with many tasks', async () => {
// Create many tasks
const promises = [];
for (let i = 1; i <= 20; i++) {
promises.push(
helpers.taskMaster(
'add-task',
['--title', `Task ${i}`, '--description', `Description ${i}`],
{ cwd: testDir }
)
);
}
await Promise.all(promises);
const startTime = Date.now();
const result = await helpers.taskMaster(
'move',
['--from', '10', '--to', '25'],
{ cwd: testDir }
);
const endTime = Date.now();
expect(result).toHaveExitCode(0);
// Should complete within reasonable time (2 seconds)
expect(endTime - startTime).toBeLessThan(2000);
});
});
describe('File generation', () => {
it('should regenerate task files after move', async () => {
// Create task
await helpers.taskMaster(
'add-task',
['--title', 'Task to move', '--description', 'Will be moved'],
{ cwd: testDir }
);
const result = await helpers.taskMaster(
'move',
['--from', '1', '--to', '5'],
{ cwd: testDir }
);
expect(result).toHaveExitCode(0);
// Check if task file was regenerated
const taskFilePath = join(testDir, '.taskmaster/tasks/5.md');
expect(existsSync(taskFilePath)).toBe(true);
// Old task file should be removed
const oldTaskFilePath = join(testDir, '.taskmaster/tasks/1.md');
expect(existsSync(oldTaskFilePath)).toBe(false);
});
});
});