Skip to main content
Glama

Lighthouse MCP

by mizchi
l3-pattern-insights.test.tsโ€ข12.4 kB
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { executeL3PatternInsights, l3PatternInsightsTool } from '../../../src/tools/l3-pattern-insights'; import { LighthouseDatabase } from '../../../src/core/database'; vi.mock('../../../src/core/database'); describe('L3 Pattern Insights', () => { let mockDb: any; const mockCrawlResults = [ { id: 1, url: 'https://example-slow.com', timestamp: new Date().toISOString(), device: 'mobile' as const, performance_score: 0.25, accessibility_score: null, best_practices_score: null, seo_score: null, pwa_score: null, fcp: 4500, lcp: 8000, cls: 0.3, tbt: 1200, si: 5000, tti: 7000, report_json: '{}', error: null, created_at: new Date().toISOString() }, { id: 2, url: 'https://example-fast.com', timestamp: new Date().toISOString(), device: 'mobile' as const, performance_score: 0.90, accessibility_score: null, best_practices_score: null, seo_score: null, pwa_score: null, fcp: 1000, lcp: 1800, cls: 0.02, tbt: 100, si: 1500, tti: 2000, report_json: '{}', error: null, created_at: new Date().toISOString() }, { id: 3, url: 'https://example-medium.com', timestamp: new Date().toISOString(), device: 'mobile' as const, performance_score: 0.60, accessibility_score: null, best_practices_score: null, seo_score: null, pwa_score: null, fcp: 2500, lcp: 3500, cls: 0.1, tbt: 400, si: 3000, tti: 4000, report_json: '{}', error: null, created_at: new Date().toISOString() }, { id: 4, url: 'https://example-poor.com', timestamp: new Date().toISOString(), device: 'mobile' as const, performance_score: 0.30, accessibility_score: null, best_practices_score: null, seo_score: null, pwa_score: null, fcp: 4000, lcp: 6500, cls: 0.28, tbt: 900, si: 4800, tti: 6500, report_json: '{}', error: null, created_at: new Date().toISOString() }, { id: 5, url: 'https://example-average.com', timestamp: new Date().toISOString(), device: 'mobile' as const, performance_score: 0.55, accessibility_score: null, best_practices_score: null, seo_score: null, pwa_score: null, fcp: 2200, lcp: 3200, cls: 0.12, tbt: 450, si: 2900, tti: 3800, report_json: '{}', error: null, created_at: new Date().toISOString() } ]; beforeEach(() => { vi.clearAllMocks(); mockDb = { getAllCrawlResults: vi.fn(() => [...mockCrawlResults]), close: vi.fn() }; vi.mocked(LighthouseDatabase).mockImplementation(() => mockDb); }); afterEach(() => { vi.clearAllMocks(); }); describe('executeL3PatternInsights', () => { it('should analyze performance patterns', async () => { // Add more results to meet minSamples const extendedResults = [ ...mockCrawlResults, ...mockCrawlResults.map((r, i) => ({ ...r, id: r.id + 10, url: r.url + '-copy' })) ]; mockDb.getAllCrawlResults.mockReturnValue(extendedResults); const result = await executeL3PatternInsights({ minSamples: 5 }); expect(result.patterns).toBeDefined(); expect(result.patterns.length).toBeGreaterThan(0); // Should detect slow LCP pattern const slowLCPPattern = result.patterns.find(p => p.pattern.includes('Largest Contentful Paint')); expect(slowLCPPattern).toBeDefined(); expect(slowLCPPattern?.impact).toBe('critical'); expect(slowLCPPattern?.frequency).toBeGreaterThan(0); }); it('should identify high CLS pattern', async () => { const extendedResults = [ ...mockCrawlResults, ...mockCrawlResults.map((r, i) => ({ ...r, id: r.id + 10, url: r.url + '-copy' })) ]; mockDb.getAllCrawlResults.mockReturnValue(extendedResults); const result = await executeL3PatternInsights({}); const clsPattern = result.patterns.find(p => p.pattern.includes('Cumulative Layout Shift')); expect(clsPattern).toBeDefined(); expect(clsPattern?.affectedSites.length).toBeGreaterThan(0); }); it('should analyze category insights', async () => { const extendedResults = [ ...mockCrawlResults, ...mockCrawlResults.map((r, i) => ({ ...r, id: r.id + 10, url: r.url + '-copy' })) ]; mockDb.getAllCrawlResults.mockReturnValue(extendedResults); const result = await executeL3PatternInsights({}); expect(result.categoryInsights).toBeDefined(); expect(result.categoryInsights.length).toBeGreaterThan(0); // Category insights might not be implemented yet if (result.categoryInsights.length > 0) { const ecommerceInsight = result.categoryInsights.find(i => i.category === 'E-commerce'); if (ecommerceInsight) { expect(ecommerceInsight.averageScore).toBeLessThan(0.5); expect(ecommerceInsight.commonIssues.length).toBeGreaterThan(0); } } }); it('should calculate global trends', async () => { const extendedResults = [ ...mockCrawlResults, ...mockCrawlResults.map((r, i) => ({ ...r, id: r.id + 10, url: r.url + '-copy' })) ]; mockDb.getAllCrawlResults.mockReturnValue(extendedResults); const result = await executeL3PatternInsights({}); expect(result.globalTrends).toBeDefined(); expect(result.globalTrends.avgPerformanceScore).toBeGreaterThan(0); expect(result.globalTrends.avgPerformanceScore).toBeLessThanOrEqual(1); expect(result.globalTrends.avgLCP).toBeGreaterThan(0); expect(result.globalTrends.avgFCP).toBeGreaterThan(0); expect(result.globalTrends.avgCLS).toBeGreaterThan(0); expect(result.globalTrends.avgTBT).toBeGreaterThan(0); expect(result.globalTrends.mostCommonIssues.length).toBeGreaterThan(0); }); it('should generate recommendations', async () => { const extendedResults = [ ...mockCrawlResults, ...mockCrawlResults.map((r, i) => ({ ...r, id: r.id + 10, url: r.url + '-copy' })) ]; mockDb.getAllCrawlResults.mockReturnValue(extendedResults); const result = await executeL3PatternInsights({}); expect(result.recommendations).toBeDefined(); expect(result.recommendations.immediate).toBeDefined(); expect(result.recommendations.strategic).toBeDefined(); expect(result.recommendations.immediate.length).toBeGreaterThan(0); }); it('should filter by time range', async () => { const now = new Date(); const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000); const twoHoursAgo = new Date(now.getTime() - 2 * 60 * 60 * 1000); const timeFilteredResults = mockCrawlResults.map((r, i) => ({ ...r, timestamp: i < 3 ? oneHourAgo.toISOString() : twoHoursAgo.toISOString() })); // Add more results to meet minSamples const extendedResults = [ ...timeFilteredResults, ...timeFilteredResults.map((r, i) => ({ ...r, id: r.id + 10, url: r.url + '-copy', timestamp: oneHourAgo.toISOString() })), ...timeFilteredResults.map((r, i) => ({ ...r, id: r.id + 20, url: r.url + '-copy2', timestamp: oneHourAgo.toISOString() })) ]; mockDb.getAllCrawlResults.mockReturnValue(extendedResults); const result = await executeL3PatternInsights({ timeRange: { start: new Date(now.getTime() - 90 * 60 * 1000), end: now }, minSamples: 5 }); expect(result.patterns).toBeDefined(); // Should have analyzed some results expect(result.summary).toContain('Analyzed'); expect(result.summary).toMatch(/\d+ sites/); }); it('should filter by categories', async () => { const extendedResults = [ ...mockCrawlResults, ...mockCrawlResults.map((r, i) => ({ ...r, id: r.id + 10, url: r.url + '-copy' })) ]; mockDb.getAllCrawlResults.mockReturnValue(extendedResults); // Since categories are not available in CrawlResult, // filtering by category will only work with 'Other' const result = await executeL3PatternInsights({ categories: ['Other'], minSamples: 2 }); // Should complete without error expect(result).toBeDefined(); expect(result.patterns).toBeDefined(); }); it('should handle insufficient samples', async () => { mockDb.getAllCrawlResults.mockReturnValue([mockCrawlResults[0]]); await expect( executeL3PatternInsights({ minSamples: 10 }) ).rejects.toThrow('Insufficient data'); }); it('should generate summary correctly', async () => { const extendedResults = [ ...mockCrawlResults, ...mockCrawlResults.map((r, i) => ({ ...r, id: r.id + 10, url: r.url + '-copy' })) ]; mockDb.getAllCrawlResults.mockReturnValue(extendedResults); const result = await executeL3PatternInsights({}); expect(result.summary).toBeDefined(); expect(result.summary).toContain('Analyzed'); expect(result.summary).toContain('sites'); expect(result.summary).toContain('categories'); expect(result.summary).toContain('Average performance score'); }); it('should identify third-party impact pattern', async () => { const extendedResults = [ ...mockCrawlResults, ...mockCrawlResults.map((r, i) => ({ ...r, id: r.id + 10, url: r.url + '-copy' })) ]; mockDb.getAllCrawlResults.mockReturnValue(extendedResults); const result = await executeL3PatternInsights({}); // Third-party pattern might not be detected with current mock data const thirdPartyPattern = result.patterns.find(p => p.pattern.toLowerCase().includes('third')); if (thirdPartyPattern) { expect(thirdPartyPattern.recommendation.toLowerCase()).toMatch(/(lazy|defer|optimize)/); } }); it('should detect trends in categories', async () => { const extendedResults = [ ...mockCrawlResults, ...mockCrawlResults.map((r, i) => ({ ...r, id: r.id + 10, url: r.url + '-copy' })) ]; mockDb.getAllCrawlResults.mockReturnValue(extendedResults); const result = await executeL3PatternInsights({}); // Category insights might not be implemented if (result.categoryInsights.length > 0) { const newsInsight = result.categoryInsights.find(i => i.category === 'News & Media'); if (newsInsight?.trends) { expect(typeof newsInsight.trends.improving).toBe('boolean'); expect(typeof newsInsight.trends.rateOfChange).toBe('number'); } } }); }); describe('MCP Tool Definition', () => { it('should have correct tool metadata', () => { expect(l3PatternInsightsTool.name).toBe('l3_pattern_insights'); expect(l3PatternInsightsTool.description).toContain('Analyze performance patterns'); expect(l3PatternInsightsTool.description).toContain('Layer 3'); }); it('should have valid input schema', () => { const schema = l3PatternInsightsTool.inputSchema; expect(schema.type).toBe('object'); expect(schema.properties.minSamples).toBeDefined(); expect(schema.properties.categories).toBeDefined(); expect(schema.properties.timeRange).toBeDefined(); }); it('should execute and format output correctly', async () => { const extendedResults = [ ...mockCrawlResults, ...mockCrawlResults.map((r, i) => ({ ...r, id: r.id + 10, url: r.url + '-copy' })) ]; mockDb.getAllCrawlResults.mockReturnValue(extendedResults); const result = await l3PatternInsightsTool.execute({}); expect(result.type).toBe('text'); expect(result.text).toContain('# Performance Pattern Analysis'); expect(result.text).toContain('## Executive Summary'); expect(result.text).toContain('## Global Performance Metrics'); expect(result.text).toContain('## Critical Performance Patterns'); expect(result.text).toContain('## Strategic Recommendations'); }); }); });

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/mizchi/lighthouse-mcp'

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