Skip to main content
Glama
jest-matchers.test.ts11.1 kB
/** * Test suite for Custom Jest Matchers * Tests for AC 6.13, 6.14 - Jest integration and error diagnostics * * TDD methodology: Failing tests first for custom matchers */ import './jest-matchers'; // Load custom matchers // import { DynamicExpectedValueConstructor } from './dynamic-expected-value-constructor'; // Extend Jest matchers interface for TypeScript declare global { namespace jest { interface Matchers<R> { toMatchDynamicTemplate(template: string): R; toMatchDynamicPattern(pattern: string): R; toHaveValidTerminalHistory(): R; toContainCRLFLineEndings(): R; } } } describe('Custom Jest Matchers for Dynamic Expected Values', () => { // let constructor: DynamicExpectedValueConstructor; beforeAll(() => { // constructor = new DynamicExpectedValueConstructor(); }); describe('AC 6.13: toMatchDynamicTemplate matcher', () => { it('should match simple dynamic template', async () => { const currentUser = process.env.USER || require('os').userInfo().username; const actualOutput = currentUser; const template = '${USER}'; expect(actualOutput).toMatchDynamicTemplate(template); }); it('should match complex prompt template', async () => { const currentUser = process.env.USER || require('os').userInfo().username; const currentHost = require('os').hostname(); const currentDir = process.cwd(); const actualOutput = `[${currentUser}@${currentHost} ${currentDir}]$ `; const template = '[${USER}@${HOSTNAME} ${PWD}]$ '; expect(actualOutput).toMatchDynamicTemplate(template); }); it('should fail when template does not match', async () => { const actualOutput = 'different_user'; const template = '${USER}'; expect(() => { expect(actualOutput).toMatchDynamicTemplate(template); }).toThrow('Expected output to match dynamic template'); }); it('should provide clear error messages with resolved values', async () => { const actualOutput = 'wrong_output'; const template = '${USER}@${HOSTNAME}'; try { expect(actualOutput).toMatchDynamicTemplate(template); fail('Should have thrown an error'); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); expect(errorMessage).toContain('Template:'); expect(errorMessage).toContain('Resolved:'); expect(errorMessage).toContain('Actual:'); expect(errorMessage).toContain('wrong_output'); } }); it('should handle templates with arrays', async () => { const currentDir = process.cwd(); const files = require('fs').readdirSync(currentDir); const actualOutput = JSON.stringify(files); const template = '${LS_OUTPUT}'; expect(actualOutput).toMatchDynamicTemplate(template); }); it('should support custom variables in templates', async () => { const actualOutput = 'custom_value_test'; const template = '${CUSTOM_VAR}_test'; // This should fail since CUSTOM_VAR is not defined expect(() => { expect(actualOutput).toMatchDynamicTemplate(template); }).toThrow('Unknown template variable'); }); }); describe('AC 6.14: toMatchDynamicPattern matcher with regex', () => { it('should match volatile timestamp patterns', async () => { const actualOutput = 'Mon Dec 25 10:30:45 UTC 2023'; const pattern = '${pattern:/\\w{3} \\w{3}\\s+\\d{1,2} \\d{2}:\\d{2}:\\d{2} \\w{3} \\d{4}/}'; expect(actualOutput).toMatchDynamicPattern(pattern); }); it('should match date command output patterns', async () => { const actualOutput = 'Fri Jan 1 00:00:00 EST 2024'; const pattern = '${pattern:/\\w{3} \\w{3}\\s+\\d{1,2} \\d{2}:\\d{2}:\\d{2} \\w{3} \\d{4}/}'; expect(actualOutput).toMatchDynamicPattern(pattern); }); it('should match process ID patterns', async () => { const actualOutput = 'Process ID: 12345'; const pattern = 'Process ID: ${pattern:/\\d+/}'; expect(actualOutput).toMatchDynamicPattern(pattern); }); it('should fail when pattern does not match', async () => { const actualOutput = 'Invalid date format'; const pattern = '${pattern:/\\w{3} \\w{3}\\s+\\d{1,2} \\d{2}:\\d{2}:\\d{2} \\w{3} \\d{4}/}'; expect(() => { expect(actualOutput).toMatchDynamicPattern(pattern); }).toThrow('Expected output to match dynamic pattern'); }); it('should provide detailed error messages for pattern failures', async () => { const actualOutput = 'wrong format'; const pattern = 'Expected format: ${pattern:/\\d{4}-\\d{2}-\\d{2}/}'; try { expect(actualOutput).toMatchDynamicPattern(pattern); fail('Should have thrown an error'); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); expect(errorMessage).toContain('Pattern:'); expect(errorMessage).toContain('Resolved Pattern:'); expect(errorMessage).toContain('Actual:'); expect(errorMessage).toContain('wrong format'); } }); it('should handle mixed template and pattern syntax', async () => { const currentUser = process.env.USER || require('os').userInfo().username; const actualOutput = `User ${currentUser} logged in at 10:30:45`; const pattern = 'User ${USER} logged in at ${pattern:/\\d{2}:\\d{2}:\\d{2}/}'; expect(actualOutput).toMatchDynamicPattern(pattern); }); }); describe('AC 6.14: Error diagnostics with environmental context', () => { it('should include environment context in error messages', async () => { const actualOutput = 'unexpected_output'; const template = 'Expected: ${USER}@${HOSTNAME}'; try { expect(actualOutput).toMatchDynamicTemplate(template); fail('Should have thrown an error'); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); expect(errorMessage).toContain('Environment Context:'); expect(errorMessage).toContain('USER:'); expect(errorMessage).toContain('HOSTNAME:'); expect(errorMessage).toContain('PWD:'); } }); it('should highlight specific mismatches', async () => { const actualOutput = 'wrong_user@correct_host'; const template = '${USER}@${HOSTNAME}'; try { expect(actualOutput).toMatchDynamicTemplate(template); fail('Should have thrown an error'); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); expect(errorMessage).toContain('Mismatch detected'); expect(errorMessage).toContain('wrong_user'); } }); it('should provide guidance for fixing templates', async () => { const actualOutput = 'some output'; const template = '${NONEXISTENT_VAR}'; try { expect(actualOutput).toMatchDynamicTemplate(template); fail('Should have thrown an error'); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); expect(errorMessage).toContain('Unknown template variable'); expect(errorMessage).toContain('Available variables:'); } }); it('should show available template variables in error', async () => { const actualOutput = 'test'; const template = '${UNKNOWN}'; try { expect(actualOutput).toMatchDynamicTemplate(template); fail('Should have thrown an error'); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); expect(errorMessage).toContain('USER'); expect(errorMessage).toContain('PWD'); expect(errorMessage).toContain('HOSTNAME'); expect(errorMessage).toContain('HOME'); } }); }); describe('Integration with existing terminal history matchers', () => { it('should work alongside toHaveValidTerminalHistory', async () => { const terminalOutput = `[${process.env.USER || require('os').userInfo().username}@${require('os').hostname()} ${process.cwd()}]$ pwd\r\n${process.cwd()}\r\n[${process.env.USER || require('os').userInfo().username}@${require('os').hostname()} ${process.cwd()}]$ `; expect(terminalOutput).toHaveValidTerminalHistory(); expect(terminalOutput).toMatchDynamicPattern('[${USER}@${HOSTNAME} ${PWD}]$ pwd\\r\\n${PWD}\\r\\n[${USER}@${HOSTNAME} ${PWD}]$ '); }); it('should work alongside toContainCRLFLineEndings', async () => { const terminalOutput = 'Line 1\r\nLine 2\r\nLine 3\r\n'; expect(terminalOutput).toContainCRLFLineEndings(); expect(terminalOutput).toMatchDynamicPattern('Line 1\\r\\nLine 2\\r\\nLine 3\\r\\n'); }); it('should support chaining with other matchers', async () => { const terminalOutput = `Command output: ${process.cwd()}\r\nSecond line\r\n`; expect(terminalOutput).toContainCRLFLineEndings(); expect(terminalOutput).toMatchDynamicTemplate('Command output: ${PWD}\r\nSecond line\r\n'); }); }); describe('Performance considerations for matchers', () => { it('should resolve templates efficiently for large outputs', async () => { const largeOutput = Array(1000).fill(`User: ${process.env.USER || require('os').userInfo().username}`).join('\n'); const template = Array(1000).fill('User: ${USER}').join('\n'); const startTime = Date.now(); expect(largeOutput).toMatchDynamicTemplate(template); const endTime = Date.now(); expect(endTime - startTime).toBeLessThan(1000); // Should complete within 1 second }); it('should cache template resolution for repeated use', async () => { const actualOutput = process.env.USER || require('os').userInfo().username; const template = '${USER}'; // First call const startTime1 = Date.now(); expect(actualOutput).toMatchDynamicTemplate(template); const duration1 = Date.now() - startTime1; // Second call should be faster due to caching const startTime2 = Date.now(); expect(actualOutput).toMatchDynamicTemplate(template); const duration2 = Date.now() - startTime2; expect(duration2).toBeLessThanOrEqual(duration1); }); }); describe('Cross-platform compatibility in matchers', () => { it('should handle Windows path separators in templates', async () => { const template = '${PWD}'; const actualOutput = process.cwd(); expect(actualOutput).toMatchDynamicTemplate(template); }); it('should handle different username formats across platforms', async () => { const actualOutput = process.env.USER || require('os').userInfo().username; const template = '${USER}'; expect(actualOutput).toMatchDynamicTemplate(template); }); it('should handle hostname variations', async () => { const actualOutput = require('os').hostname(); const template = '${HOSTNAME}'; expect(actualOutput).toMatchDynamicTemplate(template); }); }); });

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