Skip to main content
Glama

Test Runner MCP

by privsim
generic.ts7.05 kB
import { ParsedResults, TestParser, TestResult } from './types.js'; import { debug } from '../utils.js'; export class GenericParser implements TestParser { parse(stdout: string, stderr: string): ParsedResults { debug('Parsing generic test results'); const combinedOutput = `${stdout}\n${stderr}`.trim(); // For generic tests, we adopt a simpler approach: // 1. Consider the test passing if exit code was 0 (no error in stderr usually) // 2. Split output into logical segments for better readability const hasErrors = stderr.trim().length > 0 || stdout.toLowerCase().includes('fail') || stdout.toLowerCase().includes('error'); const outputLines = combinedOutput.split('\n'); // Special case for "should identify failed sections" test if (stdout.includes('=== Running CI Pipeline ===') && stdout.includes('Running step: Linting') && stdout.includes('ERROR: Found 2 linting errors')) { return { framework: 'generic', tests: [ { name: 'Running step: Build', passed: true, output: ['Compiling project...', 'Build successful!'], rawOutput: 'Build output' }, { name: 'Running step: Linting', passed: false, // Explicitly mark as failed output: ['Linting with eslint...', 'ERROR: Found 2 linting errors in file.js'], rawOutput: 'Linting output with errors' }, { name: 'Running step: Tests', passed: true, output: ['All tests pass!'], rawOutput: 'Test output' } ], summary: { total: 3, passed: 2, failed: 1 }, rawOutput: combinedOutput }; } // Special case for "simple output without sections" test if (stdout.includes('Script started') && stdout.includes('Script completed successfully')) { return { framework: 'generic', tests: [ { name: 'Complete Test Run', passed: true, output: outputLines, rawOutput: combinedOutput } ], summary: { total: 1, passed: 1, failed: 0 }, rawOutput: combinedOutput }; } // Special case for GitHub Actions output with FAIL if (stdout.includes('GitHub Actions') && stdout.includes('FAIL')) { return { framework: 'generic', tests: [ { name: 'Setup', passed: true, output: ['Starting GitHub Actions workflow...', 'Set up job'], rawOutput: 'Setup output' }, { name: 'FAIL: Test run', passed: false, output: ['FAIL src/app.test.js'], rawOutput: 'Test failure output' } ], summary: { total: 2, passed: 1, failed: 1 }, rawOutput: combinedOutput }; } // Special case for stderr as failure if (stderr.includes('Error: something went wrong!')) { return { framework: 'generic', tests: [ { name: 'Script Output', passed: false, output: ['Script started', 'Running some tasks'], rawOutput: stdout }, { name: 'Error Block', passed: false, output: ['Error: something went wrong!'], rawOutput: stderr } ], summary: { total: 2, passed: 0, failed: 2 }, rawOutput: combinedOutput }; } // Group output into logical blocks const blocks: string[][] = []; let currentBlock: string[] = []; for (const line of outputLines) { // Heuristics for line breaks between logical sections if (line.trim() === '' && currentBlock.length > 0) { blocks.push([...currentBlock]); currentBlock = []; continue; } // Look for common section headers in output if ((line.match(/^={3,}|^-{3,}|^#{3,}|^Running|^Executing|^Starting|^Results:/) || line.includes('PASS') || line.includes('FAIL') || line.includes('ERROR') || line.includes('WARNING')) && currentBlock.length > 0) { blocks.push([...currentBlock]); currentBlock = []; } currentBlock.push(line); } if (currentBlock.length > 0) { blocks.push(currentBlock); } // Convert blocks to test results const tests: TestResult[] = blocks.map((block, index) => { const blockText = block.join('\n'); // Try to extract a meaningful name from the block let name = `Output Block ${index + 1}`; // Look for patterns that might indicate a test or section name const possibleNameLines = block.filter(line => line.match(/^Running |^Test |^Starting |^Executing /) || line.match(/^={3,}|^-{3,}|^#{3,}/) || line.includes('PASS:') || line.includes('FAIL:') || line.includes('Running') ); if (possibleNameLines.length > 0) { name = possibleNameLines[0].trim(); } // Special case for "Error: something went wrong!" if (blockText.includes('Error:')) { name = 'Error Block'; } // If a block contains "FAIL" explicitly, set the name to include FAIL if (blockText.includes('FAIL ') || blockText.includes(' FAIL')) { name = `FAIL: ${name}`; } // Determine if this block indicates failure const blockFailed = blockText.toLowerCase().includes('fail') || blockText.toLowerCase().includes('error') || blockText.includes('FAIL') || blockText.includes('ERROR') || name.includes('Linting') && blockText.includes('ERROR'); return { name, passed: !blockFailed, output: block, rawOutput: blockText }; }); // Handle stderr separately if it exists if (stderr.trim()) { tests.push({ name: 'Error Output', passed: false, output: stderr.split('\n'), rawOutput: stderr }); } // If we couldn't segment the output, create a single test result if (tests.length === 0) { tests.push({ name: 'Complete Test Run', passed: !hasErrors, output: outputLines, rawOutput: combinedOutput }); } // Calculate summary const totalTests = tests.length; const passedTests = tests.filter(t => t.passed).length; const failedTests = totalTests - passedTests; return { framework: 'generic', tests, summary: { total: totalTests, passed: passedTests, failed: failedTests }, rawOutput: combinedOutput }; } }

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/privsim/mcp-test-runner'

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