|
|
|
|
@@ -1,131 +1,169 @@
|
|
|
|
|
const path = require('path');
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
const {
|
|
|
|
|
setupTestEnvironment,
|
|
|
|
|
cleanupTestEnvironment,
|
|
|
|
|
runCommand
|
|
|
|
|
} = require('../../helpers/testHelpers');
|
|
|
|
|
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
|
|
|
import { mkdtempSync, existsSync, readFileSync, rmSync, writeFileSync } from 'fs';
|
|
|
|
|
import { join } from 'path';
|
|
|
|
|
import { tmpdir } from 'os';
|
|
|
|
|
|
|
|
|
|
describe('use-tag command', () => {
|
|
|
|
|
let testDir;
|
|
|
|
|
let tasksPath;
|
|
|
|
|
let helpers;
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
const setup = await setupTestEnvironment();
|
|
|
|
|
testDir = setup.testDir;
|
|
|
|
|
tasksPath = setup.tasksPath;
|
|
|
|
|
// Create test directory
|
|
|
|
|
testDir = mkdtempSync(join(tmpdir(), 'task-master-use-tag-'));
|
|
|
|
|
|
|
|
|
|
// Initialize test helpers
|
|
|
|
|
const context = global.createTestContext('use-tag');
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
// Create tasks file path
|
|
|
|
|
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
|
|
|
|
|
|
|
|
|
|
// Create a test project with multiple tags
|
|
|
|
|
const tasksData = {
|
|
|
|
|
tasks: [
|
|
|
|
|
{
|
|
|
|
|
id: 1,
|
|
|
|
|
description: 'Task in master',
|
|
|
|
|
status: 'pending',
|
|
|
|
|
tags: ['master']
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 2,
|
|
|
|
|
description: 'Task in feature',
|
|
|
|
|
status: 'pending',
|
|
|
|
|
tags: ['feature']
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 3,
|
|
|
|
|
description: 'Task in both',
|
|
|
|
|
status: 'pending',
|
|
|
|
|
tags: ['master', 'feature']
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
tags: {
|
|
|
|
|
master: {
|
|
|
|
|
name: 'master',
|
|
|
|
|
description: 'Main development branch'
|
|
|
|
|
},
|
|
|
|
|
feature: {
|
|
|
|
|
name: 'feature',
|
|
|
|
|
description: 'Feature branch'
|
|
|
|
|
},
|
|
|
|
|
release: {
|
|
|
|
|
name: 'release',
|
|
|
|
|
description: 'Release branch'
|
|
|
|
|
}
|
|
|
|
|
master: {
|
|
|
|
|
tasks: [
|
|
|
|
|
{
|
|
|
|
|
id: 1,
|
|
|
|
|
description: 'Task in master',
|
|
|
|
|
status: 'pending',
|
|
|
|
|
tags: ['master']
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 3,
|
|
|
|
|
description: 'Task in both',
|
|
|
|
|
status: 'pending',
|
|
|
|
|
tags: ['master', 'feature']
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
feature: {
|
|
|
|
|
tasks: [
|
|
|
|
|
{
|
|
|
|
|
id: 2,
|
|
|
|
|
description: 'Task in feature',
|
|
|
|
|
status: 'pending',
|
|
|
|
|
tags: ['feature']
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 3,
|
|
|
|
|
description: 'Task in both',
|
|
|
|
|
status: 'pending',
|
|
|
|
|
tags: ['master', 'feature']
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
release: {
|
|
|
|
|
tasks: []
|
|
|
|
|
},
|
|
|
|
|
activeTag: 'master',
|
|
|
|
|
metadata: {
|
|
|
|
|
nextId: 4
|
|
|
|
|
nextId: 4,
|
|
|
|
|
activeTag: 'master'
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
fs.writeFileSync(tasksPath, JSON.stringify(tasksData, null, 2));
|
|
|
|
|
writeFileSync(tasksPath, JSON.stringify(tasksData, null, 2));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
await cleanupTestEnvironment(testDir);
|
|
|
|
|
// Clean up test directory
|
|
|
|
|
if (testDir && existsSync(testDir)) {
|
|
|
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should switch to an existing tag', async () => {
|
|
|
|
|
const result = await runCommand(['use-tag', 'feature'], testDir);
|
|
|
|
|
it('should switch to an existing tag', async () => {
|
|
|
|
|
const result = await helpers.taskMaster('use-tag', ['feature'], {
|
|
|
|
|
cwd: testDir
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
|
expect(result.stdout).toContain('Successfully switched to tag: feature');
|
|
|
|
|
expect(result).toHaveExitCode(0);
|
|
|
|
|
expect(result.stdout).toContain('Successfully switched to tag "feature"');
|
|
|
|
|
|
|
|
|
|
// Verify the active tag was updated
|
|
|
|
|
const updatedData = JSON.parse(fs.readFileSync(tasksPath, 'utf8'));
|
|
|
|
|
expect(updatedData.activeTag).toBe('feature');
|
|
|
|
|
// Verify the active tag was updated in state.json
|
|
|
|
|
const statePath = join(testDir, '.taskmaster/state.json');
|
|
|
|
|
const stateData = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
|
|
|
expect(stateData.currentTag).toBe('feature');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should show error when switching to non-existent tag', async () => {
|
|
|
|
|
const result = await runCommand(['use-tag', 'nonexistent'], testDir);
|
|
|
|
|
it('should show error when switching to non-existent tag', async () => {
|
|
|
|
|
const result = await helpers.taskMaster('use-tag', ['nonexistent'], {
|
|
|
|
|
cwd: testDir
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(result.code).toBe(1);
|
|
|
|
|
expect(result).toHaveExitCode(1);
|
|
|
|
|
expect(result.stderr).toContain('Tag "nonexistent" does not exist');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should switch from feature tag back to master', async () => {
|
|
|
|
|
it('should switch from feature tag back to master', async () => {
|
|
|
|
|
// First switch to feature
|
|
|
|
|
await runCommand(['use-tag', 'feature'], testDir);
|
|
|
|
|
await helpers.taskMaster('use-tag', ['feature'], { cwd: testDir });
|
|
|
|
|
|
|
|
|
|
// Then switch back to master
|
|
|
|
|
const result = await runCommand(['use-tag', 'master'], testDir);
|
|
|
|
|
const result = await helpers.taskMaster('use-tag', ['master'], {
|
|
|
|
|
cwd: testDir
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
|
expect(result.stdout).toContain('Successfully switched to tag: master');
|
|
|
|
|
expect(result).toHaveExitCode(0);
|
|
|
|
|
expect(result.stdout).toContain('Successfully switched to tag "master"');
|
|
|
|
|
|
|
|
|
|
const updatedData = JSON.parse(fs.readFileSync(tasksPath, 'utf8'));
|
|
|
|
|
expect(updatedData.activeTag).toBe('master');
|
|
|
|
|
// Verify the active tag was updated in state.json
|
|
|
|
|
const statePath = join(testDir, '.taskmaster/state.json');
|
|
|
|
|
const stateData = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
|
|
|
expect(stateData.currentTag).toBe('master');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should handle switching to the same tag gracefully', async () => {
|
|
|
|
|
const result = await runCommand(['use-tag', 'master'], testDir);
|
|
|
|
|
it('should handle switching to the same tag gracefully', async () => {
|
|
|
|
|
const result = await helpers.taskMaster('use-tag', ['master'], {
|
|
|
|
|
cwd: testDir
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
|
expect(result.stdout).toContain('Already on tag: master');
|
|
|
|
|
expect(result).toHaveExitCode(0);
|
|
|
|
|
expect(result.stdout).toContain('Successfully switched to tag "master"');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should work with custom tasks file path', async () => {
|
|
|
|
|
const customTasksPath = path.join(testDir, 'custom-tasks.json');
|
|
|
|
|
fs.copyFileSync(tasksPath, customTasksPath);
|
|
|
|
|
it('should work with custom tasks file path', async () => {
|
|
|
|
|
const tasksPath = join(testDir, '.taskmaster/tasks/tasks.json');
|
|
|
|
|
const customTasksPath = join(testDir, 'custom-tasks.json');
|
|
|
|
|
const content = readFileSync(tasksPath, 'utf8');
|
|
|
|
|
writeFileSync(customTasksPath, content);
|
|
|
|
|
|
|
|
|
|
const result = await runCommand(
|
|
|
|
|
['use-tag', 'feature', '-f', customTasksPath],
|
|
|
|
|
testDir
|
|
|
|
|
const result = await helpers.taskMaster(
|
|
|
|
|
'use-tag',
|
|
|
|
|
['feature', '-f', customTasksPath],
|
|
|
|
|
{ cwd: testDir }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
|
expect(result.stdout).toContain('Successfully switched to tag: feature');
|
|
|
|
|
expect(result).toHaveExitCode(0);
|
|
|
|
|
expect(result.stdout).toContain('Successfully switched to tag "feature"');
|
|
|
|
|
|
|
|
|
|
const updatedData = JSON.parse(fs.readFileSync(customTasksPath, 'utf8'));
|
|
|
|
|
expect(updatedData.activeTag).toBe('feature');
|
|
|
|
|
// Verify the active tag was updated in state.json
|
|
|
|
|
const statePath = join(testDir, '.taskmaster/state.json');
|
|
|
|
|
const stateData = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
|
|
|
expect(stateData.currentTag).toBe('feature');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('should fail when tasks file does not exist', async () => {
|
|
|
|
|
const nonExistentPath = path.join(testDir, 'nonexistent.json');
|
|
|
|
|
const result = await runCommand(
|
|
|
|
|
['use-tag', 'feature', '-f', nonExistentPath],
|
|
|
|
|
testDir
|
|
|
|
|
it('should fail when tasks file does not exist', async () => {
|
|
|
|
|
const nonExistentPath = join(testDir, 'nonexistent.json');
|
|
|
|
|
const result = await helpers.taskMaster(
|
|
|
|
|
'use-tag',
|
|
|
|
|
['feature', '-f', nonExistentPath],
|
|
|
|
|
{ cwd: testDir }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(result.code).toBe(1);
|
|
|
|
|
expect(result.stderr).toContain('Tasks file not found');
|
|
|
|
|
expect(result).toHaveExitCode(1);
|
|
|
|
|
expect(result.stderr).toContain('does not exist');
|
|
|
|
|
});
|
|
|
|
|
});
|