Skip to main content
Glama
real-browser-terminal-validation.test.ts6.26 kB
/** * Real Browser Terminal Validation Test * * This test validates the terminal behavior by connecting to the real server * and testing WebSocket-based terminal interactions that simulate browser usage. * Tests the critical fix for double echo issues. */ import WebSocket from 'ws'; describe('Real Browser Terminal Validation', () => { let webSocket: WebSocket; const serverPort = 8094; const testTimeoutMs = 30000; beforeEach(() => { // Create WebSocket connection to running server webSocket = new WebSocket(`ws://localhost:${serverPort}`); }); afterEach(() => { if (webSocket) { webSocket.close(); } }); /** * Critical test: Validate real-time terminal interaction * Tests that SSH session works properly without double echo */ test('should handle real terminal session without double echo', async () => { return new Promise<void>((resolve, reject) => { const responses: string[] = []; // let connectionEstablished = false; let sessionCreated = false; let commandExecuted = false; const timeout = setTimeout(() => { reject(new Error('Test timeout - real terminal validation failed')); }, testTimeoutMs); webSocket.on('open', () => { console.log('✅ WebSocket connection established'); // connectionEstablished = true; // Create SSH session const connectMessage = { type: 'terminal_input', sessionName: 'real-test-session', command: 'ssh_connect {"name": "real-test-session", "host": "localhost", "username": "jsbattig", "keyFilePath": "~/.ssh/id_ed25519"}', timestamp: new Date().toISOString() }; webSocket.send(JSON.stringify(connectMessage)); }); webSocket.on('message', (data) => { const response = data.toString(); responses.push(response); console.log('Received:', response.substring(0, 100) + (response.length > 100 ? '...' : '')); try { const message = JSON.parse(response); if (message.type === 'terminal_output' && message.data) { // Check for connection success if (!sessionCreated && message.data.includes('successfully')) { sessionCreated = true; console.log('✅ SSH session created successfully'); // Execute a simple command const commandMessage = { type: 'terminal_input', sessionName: 'real-test-session', command: 'ssh_exec {"sessionName": "real-test-session", "command": "echo testing123"}', timestamp: new Date().toISOString() }; webSocket.send(JSON.stringify(commandMessage)); } // Check for command execution success if (sessionCreated && !commandExecuted && message.data.includes('testing123')) { commandExecuted = true; console.log('✅ Command executed successfully'); // Validate no double echo const allResponses = responses.join(' '); // Count occurrences of our test string const testStringCount = (allResponses.match(/testing123/g) || []).length; if (testStringCount <= 2) { // Command + result = acceptable console.log(`✅ Double echo validation passed: "testing123" appears ${testStringCount} times`); clearTimeout(timeout); resolve(); } else { clearTimeout(timeout); reject(new Error(`Double echo detected: "testing123" appears ${testStringCount} times (expected ≤ 2)`)); } } } } catch (error) { // Non-JSON response - that's ok for some messages } }); webSocket.on('error', (error) => { clearTimeout(timeout); reject(new Error(`WebSocket error: ${error.message}`)); }); webSocket.on('close', () => { if (!commandExecuted) { clearTimeout(timeout); reject(new Error('WebSocket closed before test completion')); } }); }); }, testTimeoutMs); /** * Validate proper CRLF line ending handling */ test('should handle CRLF line endings properly', async () => { return new Promise<void>((resolve, reject) => { const responses: string[] = []; // let crlfFound = false; const timeout = setTimeout(() => { reject(new Error('Test timeout - CRLF validation failed')); }, testTimeoutMs); webSocket.on('open', () => { // Create SSH session and execute command const connectMessage = { type: 'terminal_input', sessionName: 'crlf-test-session', command: 'ssh_connect {"name": "crlf-test-session", "host": "localhost", "username": "jsbattig", "keyFilePath": "~/.ssh/id_ed25519"}', timestamp: new Date().toISOString() }; webSocket.send(JSON.stringify(connectMessage)); }); webSocket.on('message', (data) => { const response = data.toString(); responses.push(response); try { const message = JSON.parse(response); if (message.type === 'terminal_output' && message.data) { // Check for CRLF line endings (critical for xterm.js compatibility) if (message.data.includes('\\r\\n')) { // crlfFound = true; console.log('✅ CRLF line endings found in terminal output'); clearTimeout(timeout); resolve(); } } } catch (error) { // Non-JSON response - check raw content if (response.includes('\\r\\n')) { // crlfFound = true; console.log('✅ CRLF line endings found in raw response'); clearTimeout(timeout); resolve(); } } }); webSocket.on('error', (error) => { clearTimeout(timeout); reject(new Error(`WebSocket error: ${error.message}`)); }); }); }, testTimeoutMs); });

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/LightspeedDMS/ssh-mcp'

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