Skip to main content
Glama
cursor-profile.integration.test.ts12.3 kB
/** * @fileoverview Integration tests for CursorProfile * * These tests verify actual filesystem operations using addSlashCommands * and removeSlashCommands methods. Tests ensure that: * - Directory creation works correctly (files go to .cursor/commands/tm/) * - Files are written with correct content (no transformation) * - Commands can be added and removed * - User files are preserved during cleanup * - Empty directories are cleaned up */ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import * as fs from 'node:fs'; import * as path from 'node:path'; import * as os from 'node:os'; import { CursorProfile } from '../../src/slash-commands/profiles/cursor-profile.js'; import { staticCommand, dynamicCommand } from '../../src/slash-commands/factories.js'; describe('CursorProfile - Integration Tests', () => { let tempDir: string; let cursorProfile: CursorProfile; // Test commands created inline const testStaticCommand = staticCommand({ name: 'help', description: 'Show available commands', content: '# Help\n\nList of available Task Master commands.' }); const testDynamicCommand = dynamicCommand( 'goham', 'Start Working with Hamster Brief', '[brief-url]', '# Start Working\n\nBrief URL: $ARGUMENTS\n\nThis command helps you start working on a Hamster brief.' ); const testCommands = [testStaticCommand, testDynamicCommand]; beforeEach(() => { // Create temporary directory for each test tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cursor-profile-test-')); cursorProfile = new CursorProfile(); }); afterEach(() => { // Clean up temporary directory if (fs.existsSync(tempDir)) { fs.rmSync(tempDir, { recursive: true, force: true }); } }); describe('addSlashCommands', () => { it('should create the .cursor/commands/tm directory (nested structure)', () => { // Verify directory doesn't exist before const tmDir = path.join(tempDir, '.cursor', 'commands', 'tm'); expect(fs.existsSync(tmDir)).toBe(false); // Add commands cursorProfile.addSlashCommands(tempDir, testCommands); // Verify tm directory exists after (nested structure) expect(fs.existsSync(tmDir)).toBe(true); expect(fs.statSync(tmDir).isDirectory()).toBe(true); }); it('should write files with content unchanged (no transformation)', () => { cursorProfile.addSlashCommands(tempDir, testCommands); // Cursor supports nested commands, files go to .cursor/commands/tm/ const tmDir = path.join(tempDir, '.cursor', 'commands', 'tm'); // Verify static command (help.md) const helpPath = path.join(tmDir, 'help.md'); expect(fs.existsSync(helpPath)).toBe(true); const helpContent = fs.readFileSync(helpPath, 'utf-8'); expect(helpContent).toBe( '# Help\n\nList of available Task Master commands.' ); // Verify dynamic command (goham.md) const gohamPath = path.join(tmDir, 'goham.md'); expect(fs.existsSync(gohamPath)).toBe(true); const gohamContent = fs.readFileSync(gohamPath, 'utf-8'); expect(gohamContent).toBe( '# Start Working\n\nBrief URL: $ARGUMENTS\n\nThis command helps you start working on a Hamster brief.' ); // Verify $ARGUMENTS placeholder is NOT transformed expect(gohamContent).toContain('$ARGUMENTS'); }); it('should return success result with correct count', () => { const result = cursorProfile.addSlashCommands(tempDir, testCommands); expect(result.success).toBe(true); expect(result.count).toBe(2); // Path includes tm/ subdirectory for nested structure expect(result.directory).toBe( path.join(tempDir, '.cursor', 'commands', 'tm') ); expect(result.files).toEqual(['help.md', 'goham.md']); expect(result.error).toBeUndefined(); }); it('should overwrite existing files on re-run', () => { // Files go to .cursor/commands/tm/ const tmDir = path.join(tempDir, '.cursor', 'commands', 'tm'); // First run cursorProfile.addSlashCommands(tempDir, testCommands); const originalContent = fs.readFileSync( path.join(tmDir, 'help.md'), 'utf-8' ); expect(originalContent).toBe( '# Help\n\nList of available Task Master commands.' ); // Modify the content of test command const modifiedCommand = staticCommand({ name: 'help', description: 'Show available commands', content: '# Help - Updated\n\nThis is updated content.' }); // Second run with modified command const result = cursorProfile.addSlashCommands(tempDir, [modifiedCommand]); // Verify file was overwritten const updatedContent = fs.readFileSync( path.join(tmDir, 'help.md'), 'utf-8' ); expect(updatedContent).toBe( '# Help - Updated\n\nThis is updated content.' ); expect(result.success).toBe(true); expect(result.count).toBe(1); }); it('should handle commands with special characters in content', () => { const specialCommand = staticCommand({ name: 'special', description: 'Command with special characters', content: '# Special\n\n```bash\necho "Hello $USER"\n```\n\n- Item 1\n- Item 2\n\n**Bold** and *italic*' }); cursorProfile.addSlashCommands(tempDir, [specialCommand]); // Files go to .cursor/commands/tm/ const tmDir = path.join(tempDir, '.cursor', 'commands', 'tm'); const specialPath = path.join(tmDir, 'special.md'); const content = fs.readFileSync(specialPath, 'utf-8'); // Verify content is preserved exactly expect(content).toBe( '# Special\n\n```bash\necho "Hello $USER"\n```\n\n- Item 1\n- Item 2\n\n**Bold** and *italic*' ); }); }); describe('removeSlashCommands', () => { beforeEach(() => { // Add commands before testing removal cursorProfile.addSlashCommands(tempDir, testCommands); }); it('should remove only TaskMaster commands (preserve user files)', () => { // Files go to .cursor/commands/tm/ const tmDir = path.join(tempDir, '.cursor', 'commands', 'tm'); // Create a user's custom command file in the tm directory const userCommandPath = path.join(tmDir, 'custom-user-command.md'); fs.writeFileSync( userCommandPath, '# Custom User Command\n\nThis is a user-created command.' ); // Verify all files exist before removal expect(fs.existsSync(path.join(tmDir, 'help.md'))).toBe(true); expect(fs.existsSync(path.join(tmDir, 'goham.md'))).toBe(true); expect(fs.existsSync(userCommandPath)).toBe(true); // Remove TaskMaster commands const result = cursorProfile.removeSlashCommands(tempDir, testCommands); // Verify TaskMaster commands removed expect(fs.existsSync(path.join(tmDir, 'help.md'))).toBe(false); expect(fs.existsSync(path.join(tmDir, 'goham.md'))).toBe(false); // Verify user's custom file preserved expect(fs.existsSync(userCommandPath)).toBe(true); expect(fs.readFileSync(userCommandPath, 'utf-8')).toBe( '# Custom User Command\n\nThis is a user-created command.' ); // Verify result expect(result.success).toBe(true); expect(result.count).toBe(2); // File order is not guaranteed, so check both files are present expect(result.files).toHaveLength(2); expect(result.files).toContain('help.md'); expect(result.files).toContain('goham.md'); }); it('should remove empty tm directory after cleanup', () => { // Files go to .cursor/commands/tm/ const tmDir = path.join(tempDir, '.cursor', 'commands', 'tm'); // Verify directory exists with files expect(fs.existsSync(tmDir)).toBe(true); expect(fs.readdirSync(tmDir).length).toBe(2); // Remove all commands (should cleanup empty directory) const result = cursorProfile.removeSlashCommands( tempDir, testCommands, true ); // Verify tm directory removed expect(fs.existsSync(tmDir)).toBe(false); expect(result.success).toBe(true); expect(result.count).toBe(2); }); it('should not remove directory if removeEmptyDir is false', () => { // Files go to .cursor/commands/tm/ const tmDir = path.join(tempDir, '.cursor', 'commands', 'tm'); // Remove commands but keep directory const result = cursorProfile.removeSlashCommands( tempDir, testCommands, false ); // Verify directory still exists (but empty) expect(fs.existsSync(tmDir)).toBe(true); expect(fs.statSync(tmDir).isDirectory()).toBe(true); expect(fs.readdirSync(tmDir).length).toBe(0); expect(result.success).toBe(true); }); it('should handle removal when directory does not exist', () => { const nonExistentDir = path.join(tempDir, 'nonexistent'); // Remove commands from non-existent directory const result = cursorProfile.removeSlashCommands( nonExistentDir, testCommands ); // Should succeed with 0 count expect(result.success).toBe(true); expect(result.count).toBe(0); expect(result.files).toEqual([]); }); it('should be case-insensitive when matching command names', () => { // Files go to .cursor/commands/tm/ const tmDir = path.join(tempDir, '.cursor', 'commands', 'tm'); // Check if filesystem is case-sensitive (Linux) or case-insensitive (macOS/Windows) const testFile = path.join(tmDir, 'TEST-CASE.md'); fs.writeFileSync(testFile, 'test'); const isCaseSensitive = !fs.existsSync(path.join(tmDir, 'test-case.md')); fs.rmSync(testFile); // Create command with different casing from test commands const upperCaseFile = path.join(tmDir, 'HELP.md'); fs.writeFileSync(upperCaseFile, '# Upper case help'); // Remove using lowercase name const result = cursorProfile.removeSlashCommands(tempDir, testCommands); // help.md should always be removed expect(fs.existsSync(path.join(tmDir, 'help.md'))).toBe(false); if (isCaseSensitive) { // On case-sensitive filesystems, HELP.md is treated as different file expect(fs.existsSync(upperCaseFile)).toBe(true); expect(result.count).toBe(2); // help.md, goham.md // Clean up fs.rmSync(upperCaseFile); } else { // On case-insensitive filesystems (macOS/Windows), both should be removed // because the filesystem treats help.md and HELP.md as the same file expect(fs.existsSync(upperCaseFile)).toBe(false); expect(result.count).toBe(2); // help.md (which is the same as HELP.md), goham.md } }); }); describe('Profile configuration', () => { it('should have correct profile properties', () => { expect(cursorProfile.name).toBe('cursor'); expect(cursorProfile.displayName).toBe('Cursor'); expect(cursorProfile.commandsDir).toBe('.cursor/commands'); expect(cursorProfile.extension).toBe('.md'); expect(cursorProfile.supportsCommands).toBe(true); expect(cursorProfile.supportsNestedCommands).toBe(true); }); it('should generate correct filenames (no prefix for nested structure)', () => { // Cursor supports nested commands, so no tm- prefix expect(cursorProfile.getFilename('help')).toBe('help.md'); expect(cursorProfile.getFilename('goham')).toBe('goham.md'); }); it('should generate correct commands path with tm subdirectory', () => { // Path includes tm/ subdirectory for nested structure const expectedPath = path.join(tempDir, '.cursor', 'commands', 'tm'); expect(cursorProfile.getCommandsPath(tempDir)).toBe(expectedPath); }); }); describe('Round-trip operations', () => { it('should successfully add, remove, and re-add commands', () => { // Files go to .cursor/commands/tm/ const tmDir = path.join(tempDir, '.cursor', 'commands', 'tm'); // Add commands const addResult1 = cursorProfile.addSlashCommands(tempDir, testCommands); expect(addResult1.success).toBe(true); expect(fs.existsSync(path.join(tmDir, 'help.md'))).toBe(true); // Remove commands const removeResult = cursorProfile.removeSlashCommands( tempDir, testCommands ); expect(removeResult.success).toBe(true); expect(fs.existsSync(tmDir)).toBe(false); // Re-add commands const addResult2 = cursorProfile.addSlashCommands(tempDir, testCommands); expect(addResult2.success).toBe(true); expect(fs.existsSync(path.join(tmDir, 'help.md'))).toBe(true); // Verify content is still correct const content = fs.readFileSync(path.join(tmDir, 'help.md'), 'utf-8'); expect(content).toBe('# Help\n\nList of available Task Master commands.'); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/eyaltoledano/claude-task-master'

If you have feedback or need assistance with the MCP directory API, please join our Discord server