Skip to main content
Glama
cli.test.ts12.7 kB
import { describe, test, expect, beforeEach, afterEach } from 'vitest'; import { exec } from 'child_process'; import { promisify } from 'util'; import { writeFileSync, existsSync, readFileSync, mkdirSync, rmSync } from 'fs'; const execAsync = promisify(exec); describe('CLI Commands', () => { const testDir = 'test-cli'; beforeEach(() => { // Clean up any existing test files and directories if (existsSync(testDir)) { rmSync(testDir, { recursive: true, force: true }); } // Create test directory structure mkdirSync(testDir, { recursive: true }); mkdirSync(`${testDir}/nested`, { recursive: true }); // Create test files with sample content writeFileSync( `${testDir}/test-search.js`, `function searchExample() { const value = 42; let result = value * 2; return result; } export function anotherFunction() { console.log('test'); }` ); writeFileSync( `${testDir}/test-refactor.js`, `const oldVariable = 'test'; const anotherOld = 123; let someVar = 'keep this'; const finalOld = true;` ); writeFileSync( `${testDir}/nested/deep-file.ts`, `export function deepFunction(): string { const data = { key: 'value' }; return JSON.stringify(data); }` ); }); afterEach(() => { // Clean up test files and directories after each test if (existsSync(testDir)) { rmSync(testDir, { recursive: true, force: true }); } }); describe('search command', () => { test('should find basic function patterns', async () => { const { stdout } = await execAsync( `npm run cli -- search -p "function.*\\(" -f "${testDir}/**/*.js"` ); expect(stdout).toContain('Search results:'); expect(stdout).toContain(`${testDir}/test-search.js`); expect(stdout).toContain('line: 1'); expect(stdout).toContain('line: 7'); }); test('should find patterns with file filtering', async () => { const { stdout } = await execAsync( `npm run cli -- search -p "export.*function" -f "${testDir}/**/*.{js,ts}"` ); expect(stdout).toContain('Search results:'); expect(stdout).toContain(`${testDir}/test-search.js`); expect(stdout).toContain(`${testDir}/nested/deep-file.ts`); }); test('should show "no matches" when pattern not found', async () => { const { stdout } = await execAsync( `npm run cli -- search -p "nonexistent.*pattern" -f "${testDir}/**/*.js"` ); expect(stdout).toContain('No matches found for the given pattern'); }); test('should display matched content with --print option', async () => { const { stdout } = await execAsync( `npm run cli -- search -p "const.*=" -f "${testDir}/test-refactor.js" --print` ); expect(stdout).toContain(`=== ${testDir}/test-refactor.js ===`); expect(stdout).toContain("1:const oldVariable = 'test';"); expect(stdout).toContain('2:const anotherOld = 123;'); expect(stdout).toContain('4:const finalOld = true;'); }); test('should work with context filtering', async () => { const { stdout } = await execAsync( `npm run cli -- search -p "function" -c "export" -f "${testDir}/**/*.js"` ); expect(stdout).toContain('Search results:'); expect(stdout).toContain(`${testDir}/test-search.js`); // Should only find the export function, not the regular function expect(stdout).toContain('line: 7'); expect(stdout).not.toContain('line: 1'); }); test('should display capture groups with --print option', async () => { const { stdout } = await execAsync( `npm run cli -- search -p "function (\\w+)\\(" -f "${testDir}/**/*.js" --print` ); expect(stdout).toContain(`=== ${testDir}/test-search.js ===`); expect(stdout).toContain('1:function searchExample() {'); expect(stdout).toContain(' └─ Captured: [searchExample]'); expect(stdout).toContain('7:export function anotherFunction() {'); expect(stdout).toContain(' └─ Captured: [anotherFunction]'); }); test('should display multiple capture groups', async () => { const { stdout } = await execAsync( `npm run cli -- search -p "(export )?function (\\w+)\\(" -f "${testDir}/**/*.js" --print` ); expect(stdout).toContain(`=== ${testDir}/test-search.js ===`); expect(stdout).toContain('1:function searchExample() {'); expect(stdout).toContain(' └─ Captured: [searchExample]'); expect(stdout).toContain('7:export function anotherFunction() {'); expect(stdout).toContain(' └─ Captured: [export , anotherFunction]'); }); test('should display matched text with --matched option', async () => { const { stdout } = await execAsync( `npm run cli -- search -p "function (\\w+)\\(" -f "${testDir}/**/*.js" --matched` ); expect(stdout).toContain(`=== ${testDir}/test-search.js ===`); expect(stdout).toContain('1: function searchExample('); expect(stdout).toContain(' └─ Captured: [searchExample]'); expect(stdout).toContain('7: function anotherFunction('); expect(stdout).toContain(' └─ Captured: [anotherFunction]'); }); test('should display matched text with multiple capture groups using --matched', async () => { const { stdout } = await execAsync( `npm run cli -- search -p "(export )?function (\\w+)\\(" -f "${testDir}/**/*.js" --matched` ); expect(stdout).toContain(`=== ${testDir}/test-search.js ===`); expect(stdout).toContain('1: function searchExample('); expect(stdout).toContain(' └─ Captured: [searchExample]'); expect(stdout).toContain('7: export function anotherFunction('); expect(stdout).toContain(' └─ Captured: [export , anotherFunction]'); }); }); describe('refactor command', () => { test('should perform basic refactoring with dry-run', async () => { const { stdout } = await execAsync( `npm run cli -- refactor -s "const (\\w+) = " -r "let \\$1 = " -f "${testDir}/test-refactor.js" --dry-run` ); expect(stdout).toContain('Refactoring completed:'); expect(stdout).toContain( `${testDir}/test-refactor.js: 3 replacements (dry run)` ); expect(stdout).toContain('Total: 3 replacements in 1 files'); // File should not be modified in dry-run const content = readFileSync(`${testDir}/test-refactor.js`, 'utf-8'); expect(content).toContain('const oldVariable'); }); test('should perform actual refactoring without dry-run', async () => { const { stdout } = await execAsync( `npm run cli -- refactor -s "const (\\w+) = " -r "let \\$1 = " -f "${testDir}/test-refactor.js"` ); expect(stdout).toContain('Refactoring completed:'); expect(stdout).toContain(`${testDir}/test-refactor.js: 3 replacements`); expect(stdout).not.toContain('(dry run)'); // File should be modified const content = readFileSync(`${testDir}/test-refactor.js`, 'utf-8'); expect(content).toContain('let oldVariable'); expect(content).toContain('let anotherOld'); expect(content).toContain('let finalOld'); expect(content).toContain("let someVar = 'keep this';"); // This shouldn't match the pattern }); test('should show matched content with --print option', async () => { const { stdout } = await execAsync( `npm run cli -- refactor -s "const (\\w+)" -r "let \\$1" -f "${testDir}/test-refactor.js" --print --dry-run` ); expect(stdout).toContain(`=== ${testDir}/test-refactor.js ===`); expect(stdout).toContain("1:const oldVariable = 'test';"); expect(stdout).toContain(' - const oldVariable → let oldVariable'); expect(stdout).toContain('2:const anotherOld = 123;'); expect(stdout).toContain(' - const anotherOld → let anotherOld'); }); test('should work with context filtering', async () => { // Add a file with imports to test context filtering writeFileSync( `${testDir}/test-context.js`, `import legacy_sdk from 'old-package'; const legacy_sdk = 'local variable'; console.log(legacy_sdk);` ); const { stdout } = await execAsync( `npm run cli -- refactor -s "legacy_sdk" -r "new_sdk" -c "import" -f "${testDir}/test-context.js" --dry-run` ); expect(stdout).toContain('Refactoring completed:'); expect(stdout).toContain(`${testDir}/test-context.js:`); expect(stdout).toContain('replacements (dry run)'); // Note: Context filtering may match more than expected due to context window }); test('should show "no matches" when pattern not found', async () => { const { stdout } = await execAsync( `npm run cli -- refactor -s "nonexistent.*pattern" -r "replacement" -f "${testDir}/**/*.js" --dry-run` ); expect(stdout).toContain('No matches found for the given pattern'); }); test('should handle multiple files', async () => { // Create additional test file writeFileSync( `${testDir}/test-multi.js`, `const multiVar = 'test'; const anotherMulti = 456;` ); const { stdout } = await execAsync( `npm run cli -- refactor -s "const (\\w+) = " -r "let \\$1 = " -f "${testDir}/*.js" --dry-run` ); expect(stdout).toContain('Refactoring completed:'); expect(stdout).toContain( `${testDir}/test-refactor.js: 3 replacements (dry run)` ); expect(stdout).toContain( `${testDir}/test-multi.js: 2 replacements (dry run)` ); expect(stdout).toContain('Total:'); expect(stdout).toContain('replacements in'); expect(stdout).toContain('files'); }); }); describe('error handling', () => { test('should handle missing required arguments in search', async () => { try { await execAsync(`npm run cli -- search -f "${testDir}/**/*.js"`); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error.stderr).toContain('required option'); expect(error.stderr).toContain('pattern'); } }); test('should handle missing required arguments in refactor', async () => { try { await execAsync( `npm run cli -- refactor -s "pattern" -f "${testDir}/**/*.js"` ); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error.stderr).toContain('required option'); expect(error.stderr).toContain('replace'); } }); test('should handle invalid regex patterns gracefully', async () => { try { await execAsync( `npm run cli -- search -p "[invalid regex" -f "${testDir}/**/*.js"` ); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error.stderr).toContain('Error during search'); } }); test('should handle non-existent file patterns', async () => { const { stdout } = await execAsync( `npm run cli -- search -p "function" -f "non-existent/**/*.js"` ); expect(stdout).toContain('No matches found for the given pattern'); }); }); describe('help and version', () => { test('should show help', async () => { const { stdout } = await execAsync('npm run cli -- --help'); expect(stdout).toContain('Usage: refactor-mcp'); expect(stdout).toContain('CLI tool for code refactoring and searching'); expect(stdout).toContain('search'); expect(stdout).toContain('refactor'); }); test('should show search command help', async () => { const { stdout } = await execAsync('npm run cli -- search --help'); expect(stdout).toContain('Search for code patterns using regex'); expect(stdout).toContain('--pattern'); expect(stdout).toContain('--context'); expect(stdout).toContain('--files'); expect(stdout).toContain('--print'); }); test('should show refactor command help', async () => { const { stdout } = await execAsync('npm run cli -- refactor --help'); expect(stdout).toContain('Refactor code by replacing search pattern'); expect(stdout).toContain('--search'); expect(stdout).toContain('--replace'); expect(stdout).toContain('--context'); expect(stdout).toContain('--files'); expect(stdout).toContain('--dry-run'); expect(stdout).toContain('--print'); }); test('should show version', async () => { const { stdout } = await execAsync('npm run cli -- --version'); expect(stdout).toContain('1.2.0'); }); }); });

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/myuon/refactor-mcp'

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