Skip to main content
Glama
analyze-webpage-tool.test.ts12.2 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { registerAnalyzeWebpageTool } from '../../src/tools/analyze-webpage.js'; import { Config } from '../../src/config/index.js'; import { OpenRouterClient } from '../../src/utils/openrouter-client.js'; import { ImageProcessor } from '../../src/utils/image-processor.js'; import { Logger } from '../../src/utils/logger.js'; // Mock dependencies vi.mock('../../src/config/index.js'); vi.mock('../../src/utils/openrouter-client.js'); vi.mock('../../src/utils/image-processor.js'); vi.mock('../../src/utils/logger.js'); const MockedConfig = vi.mocked(Config); const MockedOpenRouterClient = vi.mocked(OpenRouterClient); const MockedImageProcessor = vi.mocked(ImageProcessor); const MockedLogger = vi.mocked(Logger); describe('registerAnalyzeWebpageTool', () => { let mockServer: any; let mockConfig: any; let mockOpenRouterClient: any; let mockImageProcessor: any; let mockLogger: any; beforeEach(() => { vi.clearAllMocks(); // Create mocks mockServer = { setRequestHandler: vi.fn(), }; mockConfig = { getOpenRouterConfig: vi.fn(() => ({ apiKey: 'test-api-key', model: 'test-model', })), getServerConfig: vi.fn(() => ({ maxImageSize: 10485760, })), }; mockOpenRouterClient = { analyzeImage: vi.fn(), }; mockImageProcessor = { processImage: vi.fn(), isValidImageType: vi.fn(() => true), }; mockLogger = { getInstance: vi.fn(() => ({ info: vi.fn(), debug: vi.fn(), warn: vi.fn(), error: vi.fn(), })), }; // Setup mock returns MockedConfig.getInstance = vi.fn(() => mockConfig); MockedOpenRouterClient.getInstance = vi.fn(() => mockOpenRouterClient); MockedImageProcessor.getInstance = vi.fn(() => mockImageProcessor); MockedLogger.getInstance = mockLogger.getInstance; // Register the tool registerAnalyzeWebpageTool(mockServer); }); afterEach(() => { vi.resetAllMocks(); }); describe('tool registration', () => { it('should register the tool handler', () => { expect(mockServer.setRequestHandler).toHaveBeenCalledWith( expect.anything(), expect.any(Function) ); }); }); describe('tool handler', () => { let toolHandler: any; beforeEach(() => { // Get the registered handler const handlerCall = mockServer.setRequestHandler.mock.calls[0]; toolHandler = handlerCall[1]; }); it('should successfully analyze webpage screenshot with default settings', async () => { const mockProcessedImage = { data: 'base64screenshot', mimeType: 'image/png', size: 5000, }; const mockAnalysisResult = { success: true, analysis: '{"layout": "responsive", "interactive_elements": ["button", "form"]}', model: 'test-model', }; mockImageProcessor.processImage.mockResolvedValue(mockProcessedImage); mockOpenRouterClient.analyzeImage.mockResolvedValue(mockAnalysisResult); const request = { params: { name: 'analyze_webpage_screenshot', arguments: { type: 'file', data: '/path/to/screenshot.png', }, }, }; const result = await toolHandler(request); expect(mockOpenRouterClient.analyzeImage).toHaveBeenCalledWith( 'base64screenshot', 'image/png', expect.stringContaining('Analyze this webpage screenshot and provide detailed information about its structure, content, and design'), { format: 'json', maxTokens: 4000, } ); expect(result).toEqual({ content: [ { type: 'text', text: '{"layout": "responsive", "interactive_elements": ["button", "form"]}', }, ], }); }); it('should analyze webpage with specific focus area', async () => { const mockProcessedImage = { data: 'base64screenshot', mimeType: 'image/jpeg', size: 4000, }; const mockAnalysisResult = { success: true, analysis: '{"navigation_elements": ["menu", "breadcrumbs"], "structure": "hierarchical"}', model: 'test-model', }; mockImageProcessor.processImage.mockResolvedValue(mockProcessedImage); mockOpenRouterClient.analyzeImage.mockResolvedValue(mockAnalysisResult); const request = { params: { name: 'analyze_webpage_screenshot', arguments: { type: 'file', data: '/path/to/screenshot.jpg', focusArea: 'navigation', includeAccessibility: false, format: 'json', maxTokens: 3000, }, }, }; const result = await toolHandler(request); expect(mockOpenRouterClient.analyzeImage).toHaveBeenCalledWith( 'base64screenshot', 'image/jpeg', expect.stringContaining('Focus specifically on navigation elements, menus, breadcrumbs, and user pathways'), { format: 'json', maxTokens: 3000, } ); expect(result).toEqual({ content: [ { type: 'text', text: '{"navigation_elements": ["menu", "breadcrumbs"], "structure": "hierarchical"}', }, ], }); }); it('should analyze webpage with accessibility analysis enabled', async () => { const mockProcessedImage = { data: 'base64screenshot', mimeType: 'image/png', size: 6000, }; const mockAnalysisResult = { success: true, analysis: '{"accessibility_issues": ["missing_alt_text", "poor_color_contrast"], "score": 0.7}', model: 'test-model', }; mockImageProcessor.processImage.mockResolvedValue(mockProcessedImage); mockOpenRouterClient.analyzeImage.mockResolvedValue(mockAnalysisResult); const request = { params: { name: 'analyze_webpage_screenshot', arguments: { type: 'base64', data: 'base64data', mimeType: 'image/png', focusArea: 'accessibility', includeAccessibility: true, format: 'json', }, }, }; const result = await toolHandler(request); expect(mockOpenRouterClient.analyzeImage).toHaveBeenCalledWith( 'base64screenshot', 'image/png', expect.stringContaining('accessibility'), { format: 'json', maxTokens: 4000, } ); expect(result).toEqual({ content: [ { type: 'text', text: '{"accessibility_issues": ["missing_alt_text", "poor_color_contrast"], "score": 0.7}', }, ], }); }); it('should analyze webpage with different focus areas', async () => { const focusAreas = ['layout', 'content', 'forms', 'interactive']; const mockProcessedImage = { data: 'base64screenshot', mimeType: 'image/png', size: 5000, }; mockImageProcessor.processImage.mockResolvedValue(mockProcessedImage); mockOpenRouterClient.analyzeImage.mockResolvedValue({ success: true, analysis: 'Analysis complete', model: 'test-model', }); for (const focusArea of focusAreas) { const request = { params: { name: 'analyze_webpage_screenshot', arguments: { type: 'file', data: '/path/to/screenshot.png', focusArea, }, }, }; await toolHandler(request); expect(mockOpenRouterClient.analyzeImage).toHaveBeenCalledWith( 'base64screenshot', 'image/png', expect.stringContaining(focusArea), { format: 'json', maxTokens: 4000 } ); } }); it('should handle text format output', async () => { const mockProcessedImage = { data: 'base64screenshot', mimeType: 'image/png', size: 5000, }; const mockAnalysisResult = { success: true, analysis: 'The webpage has a clean layout with clear navigation hierarchy.', model: 'test-model', }; mockImageProcessor.processImage.mockResolvedValue(mockProcessedImage); mockOpenRouterClient.analyzeImage.mockResolvedValue(mockAnalysisResult); const request = { params: { name: 'analyze_webpage_screenshot', arguments: { type: 'file', data: '/path/to/screenshot.png', format: 'text', }, }, }; const result = await toolHandler(request); expect(mockOpenRouterClient.analyzeImage).toHaveBeenCalledWith( 'base64screenshot', 'image/png', expect.stringContaining('Analyze this webpage screenshot and provide detailed information about its structure, content, and design'), { format: 'text', maxTokens: 4000, } ); expect(result).toEqual({ content: [ { type: 'text', text: 'The webpage has a clean layout with clear navigation hierarchy.', }, ], }); }); it('should handle URL input for webpage screenshots', async () => { const mockProcessedImage = { data: 'base64fromurl', mimeType: 'image/webp', size: 7000, }; const mockAnalysisResult = { success: true, analysis: '{"webpage_type": "e-commerce", "conversion_elements": ["add_to_cart", "checkout"]}', model: 'test-model', }; mockImageProcessor.processImage.mockResolvedValue(mockProcessedImage); mockOpenRouterClient.analyzeImage.mockResolvedValue(mockAnalysisResult); const request = { params: { name: 'analyze_webpage_screenshot', arguments: { type: 'url', data: 'https://example.com/screenshot.webp', focusArea: 'interactive', }, }, }; const result = await toolHandler(request); expect(mockImageProcessor.processImage).toHaveBeenCalledWith({ type: 'url', data: 'https://example.com/screenshot.webp', mimeType: undefined, }); expect(result).toEqual({ content: [ { type: 'text', text: '{"webpage_type": "e-commerce", "conversion_elements": ["add_to_cart", "checkout"]}', }, ], }); }); it('should reject unknown tool names', async () => { const request = { params: { name: 'unknown_webpage_tool', arguments: {}, }, }; await expect(toolHandler(request)).rejects.toThrow('Unknown tool: unknown_webpage_tool'); }); it('should reject requests without arguments', async () => { const request = { params: { name: 'analyze_webpage_screenshot', }, }; await expect(toolHandler(request)).rejects.toThrow('Arguments are required'); }); it('should handle analysis errors gracefully', async () => { const mockProcessedImage = { data: 'base64screenshot', mimeType: 'image/png', size: 5000, }; mockImageProcessor.processImage.mockResolvedValue(mockProcessedImage); mockOpenRouterClient.analyzeImage.mockResolvedValue({ success: false, error: 'Webpage analysis failed', }); const request = { params: { name: 'analyze_webpage_screenshot', arguments: { type: 'file', data: '/path/to/screenshot.png', }, }, }; const result = await toolHandler(request); expect(result).toEqual({ content: [ { type: 'text', text: 'Error: Webpage analysis failed', }, ], isError: true, }); }); }); });

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/JonathanJude/openrouter-image-mcp'

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