Skip to main content
Glama
portel-dev

NCP - Natural Context Provider

by portel-dev
output-formatter.test.ts11.5 kB
/** * Unit tests for OutputFormatter service * Tests format detection, auto-formatting, and markdown rendering */ import { OutputFormatter } from '../../src/services/output-formatter'; describe('OutputFormatter', () => { beforeEach(() => { // Reset to default configuration - disable colors for test environment OutputFormatter.configure({ noColor: true, emoji: true }); }); describe('format detection', () => { test('should detect primitive types', () => { expect(OutputFormatter.detectFormat('string')).toBe(OutputFormatter.FORMAT_TYPES.PRIMITIVE); expect(OutputFormatter.detectFormat(42)).toBe(OutputFormatter.FORMAT_TYPES.PRIMITIVE); expect(OutputFormatter.detectFormat(true)).toBe(OutputFormatter.FORMAT_TYPES.PRIMITIVE); }); test('should detect empty values as NONE', () => { expect(OutputFormatter.detectFormat(null)).toBe(OutputFormatter.FORMAT_TYPES.NONE); expect(OutputFormatter.detectFormat(undefined)).toBe(OutputFormatter.FORMAT_TYPES.NONE); expect(OutputFormatter.detectFormat('')).toBe(OutputFormatter.FORMAT_TYPES.NONE); }); test('should detect primitive arrays as LIST', () => { const data = ['apple', 'banana', 'cherry']; expect(OutputFormatter.detectFormat(data)).toBe(OutputFormatter.FORMAT_TYPES.LIST); const numbers = [1, 2, 3]; expect(OutputFormatter.detectFormat(numbers)).toBe(OutputFormatter.FORMAT_TYPES.LIST); }); test('should detect object arrays as TABLE', () => { const data = [ { name: 'Alice', age: 30 }, { name: 'Bob', age: 25 } ]; expect(OutputFormatter.detectFormat(data)).toBe(OutputFormatter.FORMAT_TYPES.TABLE); }); test('should detect flat objects as TABLE', () => { const data = { name: 'Alice', age: 30, email: 'alice@example.com' }; expect(OutputFormatter.detectFormat(data)).toBe(OutputFormatter.FORMAT_TYPES.TABLE); }); test('should detect nested objects as TREE', () => { const data = { user: { name: 'Alice', address: { city: 'Portland', zip: '97201' } } }; expect(OutputFormatter.detectFormat(data)).toBe(OutputFormatter.FORMAT_TYPES.TREE); }); test('should handle empty arrays', () => { expect(OutputFormatter.detectFormat([])).toBe(OutputFormatter.FORMAT_TYPES.LIST); }); }); describe('auto-formatting', () => { test('should format primitives as-is', () => { expect(OutputFormatter.formatAuto('hello')).toBe('hello'); expect(OutputFormatter.formatAuto(42)).toBe('42'); expect(OutputFormatter.formatAuto(true)).toBe('true'); }); test('should format empty values', () => { const result = OutputFormatter.formatAuto(null); expect(result.toLowerCase()).toContain('empty'); }); test('should format arrays as lists', () => { const data = ['item1', 'item2', 'item3']; const result = OutputFormatter.formatAuto(data); expect(result).toContain('item1'); expect(result).toContain('item2'); expect(result).toContain('item3'); }); test('should format object arrays as tables', () => { const data = [ { id: 1, name: 'Tool 1' }, { id: 2, name: 'Tool 2' } ]; const result = OutputFormatter.formatAuto(data); expect(result).toContain('Tool 1'); expect(result).toContain('Tool 2'); expect(result).toContain('id'); expect(result).toContain('name'); }); test('should format nested objects as trees', () => { const data = { config: { database: { host: 'localhost' } } }; const result = OutputFormatter.formatAuto(data); expect(result).toContain('config'); expect(result).toContain('database'); expect(result).toContain('localhost'); }); }); describe('markdown detection', () => { test('should detect markdown headings', () => { expect(OutputFormatter.isMarkdown('# Heading 1')).toBe(true); expect(OutputFormatter.isMarkdown('## Heading 2')).toBe(true); expect(OutputFormatter.isMarkdown('### Heading 3')).toBe(true); }); test('should detect bold markdown', () => { expect(OutputFormatter.isMarkdown('**bold text**')).toBe(true); }); test('should detect italic markdown', () => { expect(OutputFormatter.isMarkdown('*italic text*')).toBe(true); }); test('should detect code blocks', () => { expect(OutputFormatter.isMarkdown('```typescript\ncode here\n```')).toBe(true); }); test('should detect inline code', () => { expect(OutputFormatter.isMarkdown('Use `const x = 5` in code')).toBe(true); }); test('should detect links', () => { expect(OutputFormatter.isMarkdown('[Click here](https://example.com)')).toBe(true); }); test('should detect unordered lists', () => { expect(OutputFormatter.isMarkdown('- Item 1\n- Item 2')).toBe(true); expect(OutputFormatter.isMarkdown('* Item 1\n* Item 2')).toBe(true); expect(OutputFormatter.isMarkdown('+ Item 1\n+ Item 2')).toBe(true); }); test('should detect ordered lists', () => { expect(OutputFormatter.isMarkdown('1. First\n2. Second')).toBe(true); }); test('should detect blockquotes', () => { expect(OutputFormatter.isMarkdown('> A quote')).toBe(true); }); test('should not detect non-markdown text', () => { expect(OutputFormatter.isMarkdown('Just plain text')).toBe(false); expect(OutputFormatter.isMarkdown('192.168.1.1')).toBe(false); }); }); describe('markdown rendering', () => { test('should render headings', () => { const text = '# Main Heading'; const result = OutputFormatter.renderMarkdown(text); expect(result).toBeTruthy(); expect(result).toContain('Main Heading'); }); test('should render bold text', () => { const text = 'This is **bold text** here'; const result = OutputFormatter.renderMarkdown(text); expect(result).toContain('bold text'); // When noColor is true, markdown markers may be preserved expect(result).toBeTruthy(); }); test('should render inline code', () => { const text = 'Use `const x = 5` in code'; const result = OutputFormatter.renderMarkdown(text); expect(result).toContain('const x = 5'); }); test('should render code blocks', () => { const text = '```javascript\nconst x = 5;\n```'; const result = OutputFormatter.renderMarkdown(text); expect(result).toContain('const x = 5'); }); test('should render lists', () => { const text = '- Item 1\n- Item 2\n- Item 3'; const result = OutputFormatter.renderMarkdown(text); expect(result).toContain('Item 1'); expect(result).toContain('Item 2'); expect(result).toContain('Item 3'); }); test('should render links', () => { const text = '[Click here](https://example.com)'; const result = OutputFormatter.renderMarkdown(text); expect(result).toContain('Click here'); expect(result).toContain('https://example.com'); }); }); describe('formatString', () => { test('should detect and render markdown', () => { const text = '# Header\nSome content'; const result = OutputFormatter.formatString(text); expect(result).toBeTruthy(); expect(result).toContain('Header'); }); test('should pass through non-markdown text', () => { const text = 'Plain text without any markdown'; const result = OutputFormatter.formatString(text); expect(result).toBe(text); }); }); describe('status badges', () => { test('should create status badges', () => { const badge = OutputFormatter.statusBadge('HEALTHY'); expect(badge).toContain('HEALTHY'); const withLabel = OutputFormatter.statusBadge('HEALTHY', 'System Status'); expect(withLabel).toContain('HEALTHY'); expect(withLabel).toContain('System Status'); }); test('should create status rows for tables', () => { const row = OutputFormatter.statusRow('Database', 'HEALTHY', 'Connected'); expect(row).toContain('Database'); expect(row).toContain('HEALTHY'); expect(row).toContain('Connected'); }); test('should support all status types', () => { const statuses: Array<keyof typeof OutputFormatter.STATUS> = [ 'HEALTHY', 'UNHEALTHY', 'DEGRADED', 'UNKNOWN', 'DISABLED', 'RUNNING', 'PAUSED', 'FAILED', 'PENDING', 'COMPLETED', 'ACTIVE', 'INACTIVE' ]; statuses.forEach(status => { const badge = OutputFormatter.statusBadge(status); expect(badge).toBeTruthy(); expect(badge.length).toBeGreaterThan(0); }); }); }); describe('configuration', () => { test('should disable colors when configured', () => { OutputFormatter.configure({ noColor: true }); const message = OutputFormatter.success('Test'); // Test that it returns a string (actual color codes depend on chalk mock) expect(typeof message).toBe('string'); expect(message).toContain('Test'); }); test('should disable emoji when configured', () => { OutputFormatter.configure({ emoji: false }); const message = OutputFormatter.success('Test'); expect(message).toContain('Test'); }); test('should enable emoji by default', () => { OutputFormatter.configure({ emoji: true }); const message = OutputFormatter.success('Test'); expect(message).toContain('Test'); }); }); describe('status messages', () => { test('should create success messages', () => { const message = OutputFormatter.success('Operation completed'); expect(message).toContain('Operation completed'); }); test('should create error messages', () => { const message = OutputFormatter.error('Something went wrong'); expect(message).toContain('Something went wrong'); }); test('should create warning messages', () => { const message = OutputFormatter.warning('Be careful'); expect(message).toContain('Be careful'); }); test('should create info messages', () => { const message = OutputFormatter.info('Information'); expect(message).toContain('Information'); }); }); describe('complex data structures', () => { test('should handle deeply nested objects', () => { const data = { level1: { level2: { level3: { level4: { value: 'deep' } } } } }; const result = OutputFormatter.formatAuto(data); expect(result).toContain('deep'); }); test('should handle mixed arrays', () => { const data = [ { type: 'string', value: 'text' }, { type: 'number', value: 42 }, { type: 'boolean', value: true } ]; const result = OutputFormatter.formatAuto(data); expect(result).toContain('text'); expect(result).toContain('42'); }); test('should handle large data sets', () => { const data = Array.from({ length: 100 }, (_, i) => ({ id: i, name: `Item ${i}`, value: Math.random() })); const result = OutputFormatter.formatAuto(data); expect(result).toContain('id'); expect(result).toContain('name'); }); }); });

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/portel-dev/ncp'

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