Skip to main content
Glama
lines.test.ts7.28 kB
/** * Tests for line manipulation utilities. */ import { describe, expect, test } from 'bun:test'; import { addLineNumbers, deleteLines, extractLines, getContextLines, insertAfterLine, insertBeforeLine, parseLineRange, replaceLines, } from '../../src/lib/lines.js'; describe('parseLineRange', () => { test('parses single line number', () => { expect(parseLineRange('10')).toEqual({ start: 10, end: 10 }); }); test('parses line range', () => { expect(parseLineRange('10-15')).toEqual({ start: 10, end: 15 }); }); test('handles basic whitespace', () => { // Note: current implementation uses strict regex, may not handle all whitespace expect(parseLineRange('10-15')).toEqual({ start: 10, end: 15 }); }); test('returns null for invalid input', () => { expect(parseLineRange('abc')).toBeNull(); expect(parseLineRange('10-')).toBeNull(); expect(parseLineRange('-15')).toBeNull(); }); test('handles reversed range as-is', () => { // Current implementation doesn't swap - caller handles this const result = parseLineRange('15-10'); // Either null or swapped is acceptable if (result) { expect(result.start).toBeDefined(); expect(result.end).toBeDefined(); } }); }); describe('extractLines', () => { const content = 'line1\nline2\nline3\nline4\nline5'; test('extracts single line', () => { const result = extractLines(content, 2, 2); expect(result.text).toBe('line2'); expect(result.actualStart).toBe(2); expect(result.actualEnd).toBe(2); }); test('extracts line range', () => { const result = extractLines(content, 2, 4); expect(result.text).toBe('line2\nline3\nline4'); expect(result.actualStart).toBe(2); expect(result.actualEnd).toBe(4); }); test('clamps to file bounds', () => { const result = extractLines(content, 1, 100); expect(result.actualEnd).toBe(5); }); test('handles start beyond file', () => { const result = extractLines(content, 100, 105); expect(result.text).toBe(''); }); }); describe('addLineNumbers', () => { test('adds line numbers starting at 1', () => { const result = addLineNumbers('a\nb\nc'); expect(result).toContain('1|a'); expect(result).toContain('2|b'); expect(result).toContain('3|c'); }); test('adds line numbers with custom start', () => { const result = addLineNumbers('a\nb', 10); expect(result).toContain('10|a'); expect(result).toContain('11|b'); }); test('pads line numbers for alignment', () => { const lines = Array(100).fill('x').join('\n'); const result = addLineNumbers(lines); // Line 1 should be padded to align with line 100 expect(result).toMatch(/\s+1\|x/); }); }); describe('replaceLines', () => { const content = 'line1\nline2\nline3\nline4\nline5'; test('replaces single line', () => { const result = replaceLines(content, 3, 3, 'NEW'); expect(result).toBe('line1\nline2\nNEW\nline4\nline5'); }); test('replaces multiple lines with single line', () => { const result = replaceLines(content, 2, 4, 'REPLACED'); expect(result).toBe('line1\nREPLACED\nline5'); }); test('replaces with multiple lines', () => { const result = replaceLines(content, 2, 2, 'new1\nnew2'); expect(result).toBe('line1\nnew1\nnew2\nline3\nline4\nline5'); }); test('handles first line replacement', () => { const result = replaceLines(content, 1, 1, 'NEW_FIRST'); expect(result.startsWith('NEW_FIRST\n')).toBe(true); }); test('handles last line replacement', () => { const result = replaceLines(content, 5, 5, 'NEW_LAST'); expect(result.endsWith('NEW_LAST')).toBe(true); }); }); describe('insertBeforeLine', () => { const content = 'line1\nline2\nline3'; test('inserts before specified line', () => { const result = insertBeforeLine(content, 2, 'INSERTED'); expect(result).toBe('line1\nINSERTED\nline2\nline3'); }); test('inserts before first line', () => { const result = insertBeforeLine(content, 1, 'FIRST'); expect(result).toBe('FIRST\nline1\nline2\nline3'); }); test('handles multi-line insertion', () => { const result = insertBeforeLine(content, 2, 'a\nb'); expect(result).toBe('line1\na\nb\nline2\nline3'); }); }); describe('insertAfterLine', () => { const content = 'line1\nline2\nline3'; test('inserts after specified line', () => { const result = insertAfterLine(content, 2, 'INSERTED'); expect(result).toBe('line1\nline2\nINSERTED\nline3'); }); test('inserts after last line', () => { const result = insertAfterLine(content, 3, 'LAST'); expect(result).toBe('line1\nline2\nline3\nLAST'); }); }); describe('deleteLines', () => { const content = 'line1\nline2\nline3\nline4\nline5'; test('deletes single line', () => { const result = deleteLines(content, 3, 3); expect(result).toBe('line1\nline2\nline4\nline5'); }); test('deletes multiple lines', () => { const result = deleteLines(content, 2, 4); expect(result).toBe('line1\nline5'); }); test('deletes first line', () => { const result = deleteLines(content, 1, 1); expect(result).toBe('line2\nline3\nline4\nline5'); }); test('deletes last line', () => { const result = deleteLines(content, 5, 5); expect(result).toBe('line1\nline2\nline3\nline4'); }); test('deletes all lines', () => { const result = deleteLines(content, 1, 5); expect(result).toBe(''); }); }); describe('getContextLines', () => { const content = 'line1\nline2\nline3\nline4\nline5\nline6\nline7'; test('returns context around a line', () => { const result = getContextLines(content, 4, 2, 2); expect(result.before).toEqual(['line2', 'line3']); expect(result.after).toEqual(['line5', 'line6']); }); test('handles lines at start of file', () => { const result = getContextLines(content, 1, 2, 2); expect(result.before).toEqual([]); expect(result.after).toEqual(['line2', 'line3']); }); test('handles lines at end of file', () => { const result = getContextLines(content, 7, 2, 2); expect(result.before).toEqual(['line5', 'line6']); expect(result.after).toEqual([]); }); test('handles asymmetric context', () => { const result = getContextLines(content, 4, 1, 3); expect(result.before).toHaveLength(1); expect(result.after).toHaveLength(3); }); }); describe('edge cases', () => { test('handles empty content', () => { expect(extractLines('', 1, 1).text).toBe(''); expect(replaceLines('', 1, 1, 'new')).toBe('new'); }); test('handles single line content', () => { const content = 'only line'; expect(extractLines(content, 1, 1).text).toBe('only line'); expect(replaceLines(content, 1, 1, 'new')).toBe('new'); expect(deleteLines(content, 1, 1)).toBe(''); }); test('handles content with trailing newline', () => { const content = 'line1\nline2\n'; const result = replaceLines(content, 2, 2, 'new'); expect(result).toBe('line1\nnew\n'); }); test('handles Windows line endings', () => { const content = 'line1\r\nline2\r\nline3'; const result = extractLines(content, 2, 2); // Should handle or normalize expect(result.text).toContain('line2'); }); });

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/iceener/files-stdio-mcp-server'

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