Compare commits
1 Commits
fix/claude
...
chore/pimp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74a1abc3b2 |
@@ -1,12 +0,0 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Add compact mode --compact / -c flag to the `tm list` CLI command
|
||||
|
||||
- outputs tasks in a minimal, git-style one-line format. This reduces verbose output from ~30+ lines of dashboards and tables to just 1 line per task, making it much easier to quickly scan available tasks.
|
||||
- Git-style format: ID STATUS TITLE (PRIORITY) → DEPS
|
||||
- Color-coded status, priority, and dependencies
|
||||
- Smart title truncation and dependency abbreviation
|
||||
- Subtask support with indentation
|
||||
- Full backward compatibility with existing list options
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"extension": minor
|
||||
---
|
||||
|
||||
Display current task ID on task details page
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Remove `clear` Taskmaster claude code commands since they were too close to the claude-code clear command
|
||||
93
.claude/commands/tm/clear-subtasks/clear-all-subtasks.md
Normal file
93
.claude/commands/tm/clear-subtasks/clear-all-subtasks.md
Normal file
@@ -0,0 +1,93 @@
|
||||
Clear all subtasks from all tasks globally.
|
||||
|
||||
## Global Subtask Clearing
|
||||
|
||||
Remove all subtasks across the entire project. Use with extreme caution.
|
||||
|
||||
## Execution
|
||||
|
||||
```bash
|
||||
task-master clear-subtasks --all
|
||||
```
|
||||
|
||||
## Pre-Clear Analysis
|
||||
|
||||
1. **Project-Wide Summary**
|
||||
```
|
||||
Global Subtask Summary
|
||||
━━━━━━━━━━━━━━━━━━━━
|
||||
Total parent tasks: 12
|
||||
Total subtasks: 47
|
||||
- Completed: 15
|
||||
- In-progress: 8
|
||||
- Pending: 24
|
||||
|
||||
Work at risk: ~120 hours
|
||||
```
|
||||
|
||||
2. **Critical Warnings**
|
||||
- In-progress subtasks that will lose work
|
||||
- Completed subtasks with valuable history
|
||||
- Complex dependency chains
|
||||
- Integration test results
|
||||
|
||||
## Double Confirmation
|
||||
|
||||
```
|
||||
⚠️ DESTRUCTIVE OPERATION WARNING ⚠️
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
This will remove ALL 47 subtasks from your project
|
||||
Including 8 in-progress and 15 completed subtasks
|
||||
|
||||
This action CANNOT be undone
|
||||
|
||||
Type 'CLEAR ALL SUBTASKS' to confirm:
|
||||
```
|
||||
|
||||
## Smart Safeguards
|
||||
|
||||
- Require explicit confirmation phrase
|
||||
- Create automatic backup
|
||||
- Log all removed data
|
||||
- Option to export first
|
||||
|
||||
## Use Cases
|
||||
|
||||
Valid reasons for global clear:
|
||||
- Project restructuring
|
||||
- Major pivot in approach
|
||||
- Starting fresh breakdown
|
||||
- Switching to different task organization
|
||||
|
||||
## Process
|
||||
|
||||
1. Full project analysis
|
||||
2. Create backup file
|
||||
3. Show detailed impact
|
||||
4. Require confirmation
|
||||
5. Execute removal
|
||||
6. Generate summary report
|
||||
|
||||
## Alternative Suggestions
|
||||
|
||||
Before clearing all:
|
||||
- Export subtasks to file
|
||||
- Clear only pending subtasks
|
||||
- Clear by task category
|
||||
- Archive instead of delete
|
||||
|
||||
## Post-Clear Report
|
||||
|
||||
```
|
||||
Global Subtask Clear Complete
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Removed: 47 subtasks from 12 tasks
|
||||
Backup saved: .taskmaster/backup/subtasks-20240115.json
|
||||
Parent tasks updated: 12
|
||||
Time estimates adjusted: Yes
|
||||
|
||||
Next steps:
|
||||
- Review updated task list
|
||||
- Re-expand complex tasks as needed
|
||||
- Check project timeline
|
||||
```
|
||||
86
.claude/commands/tm/clear-subtasks/clear-subtasks.md
Normal file
86
.claude/commands/tm/clear-subtasks/clear-subtasks.md
Normal file
@@ -0,0 +1,86 @@
|
||||
Clear all subtasks from a specific task.
|
||||
|
||||
Arguments: $ARGUMENTS (task ID)
|
||||
|
||||
Remove all subtasks from a parent task at once.
|
||||
|
||||
## Clearing Subtasks
|
||||
|
||||
Bulk removal of all subtasks from a parent task.
|
||||
|
||||
## Execution
|
||||
|
||||
```bash
|
||||
task-master clear-subtasks --id=<task-id>
|
||||
```
|
||||
|
||||
## Pre-Clear Analysis
|
||||
|
||||
1. **Subtask Summary**
|
||||
- Number of subtasks
|
||||
- Completion status of each
|
||||
- Work already done
|
||||
- Dependencies affected
|
||||
|
||||
2. **Impact Assessment**
|
||||
- Data that will be lost
|
||||
- Dependencies to be removed
|
||||
- Effect on project timeline
|
||||
- Parent task implications
|
||||
|
||||
## Confirmation Required
|
||||
|
||||
```
|
||||
Clear Subtasks Confirmation
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Parent Task: #5 "Implement user authentication"
|
||||
Subtasks to remove: 4
|
||||
- #5.1 "Setup auth framework" (done)
|
||||
- #5.2 "Create login form" (in-progress)
|
||||
- #5.3 "Add validation" (pending)
|
||||
- #5.4 "Write tests" (pending)
|
||||
|
||||
⚠️ This will permanently delete all subtask data
|
||||
Continue? (y/n)
|
||||
```
|
||||
|
||||
## Smart Features
|
||||
|
||||
- Option to convert to standalone tasks
|
||||
- Backup task data before clearing
|
||||
- Preserve completed work history
|
||||
- Update parent task appropriately
|
||||
|
||||
## Process
|
||||
|
||||
1. List all subtasks for confirmation
|
||||
2. Check for in-progress work
|
||||
3. Remove all subtasks
|
||||
4. Update parent task
|
||||
5. Clean up dependencies
|
||||
|
||||
## Alternative Options
|
||||
|
||||
Suggest alternatives:
|
||||
- Convert important subtasks to tasks
|
||||
- Keep completed subtasks
|
||||
- Archive instead of delete
|
||||
- Export subtask data first
|
||||
|
||||
## Post-Clear
|
||||
|
||||
- Show updated parent task
|
||||
- Recalculate time estimates
|
||||
- Update task complexity
|
||||
- Suggest next steps
|
||||
|
||||
## Example
|
||||
|
||||
```
|
||||
/project:tm/clear-subtasks 5
|
||||
→ Found 4 subtasks to remove
|
||||
→ Warning: Subtask #5.2 is in-progress
|
||||
→ Cleared all subtasks from task #5
|
||||
→ Updated parent task estimates
|
||||
→ Suggestion: Consider re-expanding with better breakdown
|
||||
```
|
||||
@@ -53,11 +53,6 @@ export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
|
||||
refreshComplexityAfterAI
|
||||
} = useTaskDetails({ taskId, sendMessage, tasks: allTasks });
|
||||
|
||||
const displayId =
|
||||
isSubtask && parentTask
|
||||
? `${parentTask.id}.${currentTask?.id}`
|
||||
: currentTask?.id;
|
||||
|
||||
const handleStatusChange = async (newStatus: TaskMasterTask['status']) => {
|
||||
if (!currentTask) return;
|
||||
|
||||
@@ -65,7 +60,10 @@ export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
|
||||
await sendMessage({
|
||||
type: 'updateTaskStatus',
|
||||
data: {
|
||||
taskId: displayId,
|
||||
taskId:
|
||||
isSubtask && parentTask
|
||||
? `${parentTask.id}.${currentTask.id}`
|
||||
: currentTask.id,
|
||||
newStatus: newStatus
|
||||
}
|
||||
});
|
||||
@@ -137,7 +135,7 @@ export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<span className="text-vscode-foreground">
|
||||
#{displayId} {currentTask.title}
|
||||
{currentTask.title}
|
||||
</span>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
@@ -154,9 +152,9 @@ export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Task ID and title */}
|
||||
{/* Task title */}
|
||||
<h1 className="text-2xl font-bold tracking-tight text-vscode-foreground">
|
||||
#{displayId} {currentTask.title}
|
||||
{currentTask.title}
|
||||
</h1>
|
||||
|
||||
{/* Description */}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Available Models as of August 11, 2025
|
||||
# Available Models as of August 8, 2025
|
||||
|
||||
## Main Models
|
||||
|
||||
|
||||
@@ -1753,7 +1753,6 @@ function registerCommands(programInstance) {
|
||||
)
|
||||
.option('-s, --status <status>', 'Filter by status')
|
||||
.option('--with-subtasks', 'Show subtasks for each task')
|
||||
.option('-c, --compact', 'Display tasks in compact one-line format')
|
||||
.option('--tag <tag>', 'Specify tag context for task operations')
|
||||
.action(async (options) => {
|
||||
// Initialize TaskMaster
|
||||
@@ -1771,12 +1770,10 @@ function registerCommands(programInstance) {
|
||||
|
||||
const statusFilter = options.status;
|
||||
const withSubtasks = options.withSubtasks || false;
|
||||
const compact = options.compact || false;
|
||||
const tag = taskMaster.getCurrentTag();
|
||||
// Show current tag context
|
||||
displayCurrentTagIndicator(tag);
|
||||
|
||||
if (!compact) {
|
||||
console.log(
|
||||
chalk.blue(`Listing tasks from: ${taskMaster.getTasksPath()}`)
|
||||
);
|
||||
@@ -1786,14 +1783,13 @@ function registerCommands(programInstance) {
|
||||
if (withSubtasks) {
|
||||
console.log(chalk.blue('Including subtasks in listing'));
|
||||
}
|
||||
}
|
||||
|
||||
await listTasks(
|
||||
taskMaster.getTasksPath(),
|
||||
statusFilter,
|
||||
taskMaster.getComplexityReportPath(),
|
||||
withSubtasks,
|
||||
compact ? 'compact' : 'text',
|
||||
'text',
|
||||
{ projectRoot: taskMaster.getProjectRoot(), tag }
|
||||
);
|
||||
});
|
||||
|
||||
@@ -294,11 +294,6 @@ function listTasks(
|
||||
});
|
||||
}
|
||||
|
||||
// For compact output, return minimal one-line format
|
||||
if (outputFormat === 'compact') {
|
||||
return renderCompactOutput(filteredTasks, withSubtasks);
|
||||
}
|
||||
|
||||
// ... existing code for text output ...
|
||||
|
||||
// Calculate status breakdowns as percentages of total
|
||||
@@ -967,98 +962,4 @@ function generateMarkdownOutput(data, filteredTasks, stats) {
|
||||
return markdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format dependencies for compact output with truncation and coloring
|
||||
* @param {Array} dependencies - Array of dependency IDs
|
||||
* @returns {string} - Formatted dependency string with arrow prefix
|
||||
*/
|
||||
function formatCompactDependencies(dependencies) {
|
||||
if (!dependencies || dependencies.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (dependencies.length > 5) {
|
||||
const visible = dependencies.slice(0, 5).join(',');
|
||||
const remaining = dependencies.length - 5;
|
||||
return ` → ${chalk.cyan(visible)}${chalk.gray('... (+' + remaining + ' more)')}`;
|
||||
} else {
|
||||
return ` → ${chalk.cyan(dependencies.join(','))}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a single task in compact one-line format
|
||||
* @param {Object} task - Task object
|
||||
* @param {number} maxTitleLength - Maximum title length before truncation
|
||||
* @returns {string} - Formatted task line
|
||||
*/
|
||||
function formatCompactTask(task, maxTitleLength = 50) {
|
||||
const status = task.status || 'pending';
|
||||
const priority = task.priority || 'medium';
|
||||
const title = truncate(task.title || 'Untitled', maxTitleLength);
|
||||
|
||||
// Use colored status from existing function
|
||||
const coloredStatus = getStatusWithColor(status, true);
|
||||
|
||||
// Color priority based on level
|
||||
const priorityColors = {
|
||||
high: chalk.red,
|
||||
medium: chalk.yellow,
|
||||
low: chalk.gray
|
||||
};
|
||||
const priorityColor = priorityColors[priority] || chalk.white;
|
||||
|
||||
// Format dependencies using shared helper
|
||||
const depsText = formatCompactDependencies(task.dependencies);
|
||||
|
||||
return `${chalk.cyan(task.id)} ${coloredStatus} ${chalk.white(title)} ${priorityColor('(' + priority + ')')}${depsText}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a subtask in compact format with indentation
|
||||
* @param {Object} subtask - Subtask object
|
||||
* @param {string|number} parentId - Parent task ID
|
||||
* @param {number} maxTitleLength - Maximum title length before truncation
|
||||
* @returns {string} - Formatted subtask line
|
||||
*/
|
||||
function formatCompactSubtask(subtask, parentId, maxTitleLength = 47) {
|
||||
const status = subtask.status || 'pending';
|
||||
const title = truncate(subtask.title || 'Untitled', maxTitleLength);
|
||||
|
||||
// Use colored status from existing function
|
||||
const coloredStatus = getStatusWithColor(status, true);
|
||||
|
||||
// Format dependencies using shared helper
|
||||
const depsText = formatCompactDependencies(subtask.dependencies);
|
||||
|
||||
return ` ${chalk.cyan(parentId + '.' + subtask.id)} ${coloredStatus} ${chalk.dim(title)}${depsText}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render complete compact output
|
||||
* @param {Array} filteredTasks - Tasks to display
|
||||
* @param {boolean} withSubtasks - Whether to include subtasks
|
||||
* @returns {void} - Outputs directly to console
|
||||
*/
|
||||
function renderCompactOutput(filteredTasks, withSubtasks) {
|
||||
if (filteredTasks.length === 0) {
|
||||
console.log('No tasks found');
|
||||
return;
|
||||
}
|
||||
|
||||
const output = [];
|
||||
|
||||
filteredTasks.forEach((task) => {
|
||||
output.push(formatCompactTask(task));
|
||||
|
||||
if (withSubtasks && task.subtasks && task.subtasks.length > 0) {
|
||||
task.subtasks.forEach((subtask) => {
|
||||
output.push(formatCompactSubtask(subtask, task.id));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
console.log(output.join('\n'));
|
||||
}
|
||||
|
||||
export default listTasks;
|
||||
|
||||
@@ -1430,20 +1430,6 @@ function ensureTagMetadata(tagObj, opts = {}) {
|
||||
return tagObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip ANSI color codes from a string
|
||||
* Useful for testing, logging to files, or when clean text output is needed
|
||||
* @param {string} text - The text that may contain ANSI color codes
|
||||
* @returns {string} - The text with ANSI color codes removed
|
||||
*/
|
||||
function stripAnsiCodes(text) {
|
||||
if (typeof text !== 'string') {
|
||||
return text;
|
||||
}
|
||||
// Remove ANSI escape sequences (color codes, cursor movements, etc.)
|
||||
return text.replace(/\x1b\[[0-9;]*m/g, '');
|
||||
}
|
||||
|
||||
// Export all utility functions and configuration
|
||||
export {
|
||||
LOG_LEVELS,
|
||||
@@ -1481,6 +1467,5 @@ export {
|
||||
markMigrationForNotice,
|
||||
flattenTasksWithSubtasks,
|
||||
ensureTagMetadata,
|
||||
stripAnsiCodes,
|
||||
normalizeTaskIds
|
||||
};
|
||||
|
||||
@@ -22,10 +22,7 @@ jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
|
||||
),
|
||||
addComplexityToTask: jest.fn(),
|
||||
readComplexityReport: jest.fn(() => null),
|
||||
getTagAwareFilePath: jest.fn((tag, path) => '/mock/tagged/report.json'),
|
||||
stripAnsiCodes: jest.fn((text) =>
|
||||
text ? text.replace(/\x1b\[[0-9;]*m/g, '') : text
|
||||
)
|
||||
getTagAwareFilePath: jest.fn((tag, path) => '/mock/tagged/report.json')
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({
|
||||
@@ -48,13 +45,8 @@ jest.unstable_mockModule(
|
||||
);
|
||||
|
||||
// Import the mocked modules
|
||||
const {
|
||||
readJSON,
|
||||
log,
|
||||
readComplexityReport,
|
||||
addComplexityToTask,
|
||||
stripAnsiCodes
|
||||
} = await import('../../../../../scripts/modules/utils.js');
|
||||
const { readJSON, log, readComplexityReport, addComplexityToTask } =
|
||||
await import('../../../../../scripts/modules/utils.js');
|
||||
const { displayTaskList } = await import(
|
||||
'../../../../../scripts/modules/ui.js'
|
||||
);
|
||||
@@ -592,140 +584,4 @@ describe('listTasks', () => {
|
||||
expect(taskIds).toContain(5); // review task
|
||||
});
|
||||
});
|
||||
|
||||
describe('Compact output format', () => {
|
||||
test('should output compact format when outputFormat is compact', async () => {
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
||||
const tasksPath = 'tasks/tasks.json';
|
||||
|
||||
await listTasks(tasksPath, null, null, false, 'compact', {
|
||||
tag: 'master'
|
||||
});
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalled();
|
||||
const output = consoleSpy.mock.calls.map((call) => call[0]).join('\n');
|
||||
// Strip ANSI color codes for testing
|
||||
const cleanOutput = stripAnsiCodes(output);
|
||||
|
||||
// Should contain compact format elements: ID status title (priority) [→ dependencies]
|
||||
expect(cleanOutput).toContain('1 done Setup Project (high)');
|
||||
expect(cleanOutput).toContain(
|
||||
'2 pending Implement Core Features (high) → 1'
|
||||
);
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('should format single task compactly', async () => {
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
||||
const tasksPath = 'tasks/tasks.json';
|
||||
|
||||
await listTasks(tasksPath, null, null, false, 'compact', {
|
||||
tag: 'master'
|
||||
});
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalled();
|
||||
const output = consoleSpy.mock.calls.map((call) => call[0]).join('\n');
|
||||
|
||||
// Should be compact (no verbose headers)
|
||||
expect(output).not.toContain('Project Dashboard');
|
||||
expect(output).not.toContain('Progress:');
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('should handle compact format with subtasks', async () => {
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
||||
const tasksPath = 'tasks/tasks.json';
|
||||
|
||||
await listTasks(
|
||||
tasksPath,
|
||||
null,
|
||||
null,
|
||||
true, // withSubtasks = true
|
||||
'compact',
|
||||
{ tag: 'master' }
|
||||
);
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalled();
|
||||
const output = consoleSpy.mock.calls.map((call) => call[0]).join('\n');
|
||||
// Strip ANSI color codes for testing
|
||||
const cleanOutput = stripAnsiCodes(output);
|
||||
|
||||
// Should handle both tasks and subtasks
|
||||
expect(cleanOutput).toContain('1 done Setup Project (high)');
|
||||
expect(cleanOutput).toContain('3.1 done Create Header Component');
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('should handle empty task list in compact format', async () => {
|
||||
readJSON.mockReturnValue({ tasks: [] });
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
||||
const tasksPath = 'tasks/tasks.json';
|
||||
|
||||
await listTasks(tasksPath, null, null, false, 'compact', {
|
||||
tag: 'master'
|
||||
});
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith('No tasks found');
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('should format dependencies correctly with shared helper', async () => {
|
||||
// Create mock tasks with various dependency scenarios
|
||||
const tasksWithDeps = {
|
||||
tasks: [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Task with no dependencies',
|
||||
status: 'pending',
|
||||
priority: 'medium',
|
||||
dependencies: []
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Task with few dependencies',
|
||||
status: 'pending',
|
||||
priority: 'high',
|
||||
dependencies: [1, 3]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Task with many dependencies',
|
||||
status: 'pending',
|
||||
priority: 'low',
|
||||
dependencies: [1, 2, 4, 5, 6, 7, 8, 9]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
readJSON.mockReturnValue(tasksWithDeps);
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
||||
const tasksPath = 'tasks/tasks.json';
|
||||
|
||||
await listTasks(tasksPath, null, null, false, 'compact', {
|
||||
tag: 'master'
|
||||
});
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalled();
|
||||
const output = consoleSpy.mock.calls.map((call) => call[0]).join('\n');
|
||||
// Strip ANSI color codes for testing
|
||||
const cleanOutput = stripAnsiCodes(output);
|
||||
|
||||
// Should format tasks correctly with compact output including priority
|
||||
expect(cleanOutput).toContain(
|
||||
'1 pending Task with no dependencies (medium)'
|
||||
);
|
||||
expect(cleanOutput).toContain('Task with few dependencies');
|
||||
expect(cleanOutput).toContain('Task with many dependencies');
|
||||
// Should show dependencies with arrow when they exist
|
||||
expect(cleanOutput).toMatch(/2.*→.*1,3/);
|
||||
// Should truncate many dependencies with "+X more" format
|
||||
expect(cleanOutput).toMatch(/3.*→.*1,2,4,5,6.*\(\+\d+ more\)/);
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Tests for the stripAnsiCodes utility function
|
||||
*/
|
||||
import { jest } from '@jest/globals';
|
||||
|
||||
// Import the module under test
|
||||
const { stripAnsiCodes } = await import('../../scripts/modules/utils.js');
|
||||
|
||||
describe('stripAnsiCodes', () => {
|
||||
test('should remove ANSI color codes from text', () => {
|
||||
const textWithColors = '\x1b[31mRed text\x1b[0m \x1b[32mGreen text\x1b[0m';
|
||||
const result = stripAnsiCodes(textWithColors);
|
||||
expect(result).toBe('Red text Green text');
|
||||
});
|
||||
|
||||
test('should handle text without ANSI codes', () => {
|
||||
const plainText = 'This is plain text';
|
||||
const result = stripAnsiCodes(plainText);
|
||||
expect(result).toBe('This is plain text');
|
||||
});
|
||||
|
||||
test('should handle empty string', () => {
|
||||
const result = stripAnsiCodes('');
|
||||
expect(result).toBe('');
|
||||
});
|
||||
|
||||
test('should handle complex ANSI sequences', () => {
|
||||
// Test with various ANSI escape sequences
|
||||
const complexText =
|
||||
'\x1b[1;31mBold red\x1b[0m \x1b[4;32mUnderlined green\x1b[0m \x1b[33;46mYellow on cyan\x1b[0m';
|
||||
const result = stripAnsiCodes(complexText);
|
||||
expect(result).toBe('Bold red Underlined green Yellow on cyan');
|
||||
});
|
||||
|
||||
test('should handle non-string input gracefully', () => {
|
||||
expect(stripAnsiCodes(null)).toBe(null);
|
||||
expect(stripAnsiCodes(undefined)).toBe(undefined);
|
||||
expect(stripAnsiCodes(123)).toBe(123);
|
||||
expect(stripAnsiCodes({})).toEqual({});
|
||||
});
|
||||
|
||||
test('should handle real chalk output patterns', () => {
|
||||
// Test patterns similar to what chalk produces
|
||||
const chalkLikeText =
|
||||
'1 \x1b[32m✓ done\x1b[39m Setup Project \x1b[31m(high)\x1b[39m';
|
||||
const result = stripAnsiCodes(chalkLikeText);
|
||||
expect(result).toBe('1 ✓ done Setup Project (high)');
|
||||
});
|
||||
|
||||
test('should handle multiline text with ANSI codes', () => {
|
||||
const multilineText =
|
||||
'\x1b[31mLine 1\x1b[0m\n\x1b[32mLine 2\x1b[0m\n\x1b[33mLine 3\x1b[0m';
|
||||
const result = stripAnsiCodes(multilineText);
|
||||
expect(result).toBe('Line 1\nLine 2\nLine 3');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user