Skip to main content
Glama

Lighthouse MCP

by mizchi
l2-unused-code.test.tsโ€ข9.47 kB
/** * Unit tests for L2 Unused Code Tool */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { executeL2UnusedCode, l2UnusedCodeTool } from '../../../src/tools/l2-unused-code'; import * as l1GetReport from '../../../src/tools/l1-get-report'; import * as l1Collect from '../../../src/tools/l1-collect-single'; import * as unusedCode from '../../../src/analyzers/unusedCode'; vi.mock('../../../src/tools/l1-get-report'); vi.mock('../../../src/tools/l1-collect-single'); vi.mock('../../../src/analyzers/unusedCode'); describe('L2 Unused Code Tool', () => { const mockReport = { audits: { 'unused-css-rules': { details: {}, }, 'unused-javascript': { details: {}, }, }, }; const mockUnusedCodeAnalysis = { totalWastedBytes: 250000, items: [ { url: 'https://example.com/style.css', unusedBytes: 75000, unusedPercent: 60, totalBytes: 125000, type: 'css', }, { url: 'https://example.com/vendor.js', unusedBytes: 125000, unusedPercent: 50, totalBytes: 250000, type: 'js', }, { url: 'https://example.com/app.js', unusedBytes: 50000, unusedPercent: 25, totalBytes: 200000, type: 'js', }, ], recommendations: [ 'Remove unused CSS from style.css', 'Tree-shake vendor.js to reduce bundle size', 'Consider code splitting for app.js', ], }; beforeEach(() => { vi.clearAllMocks(); }); describe('Tool Definition', () => { it('should have correct tool metadata', () => { expect(l2UnusedCodeTool.name).toBe('l2_unused_code'); expect(l2UnusedCodeTool.description).toContain('unused CSS and JavaScript'); expect(l2UnusedCodeTool.description).toContain('Layer 2'); }); it('should define proper schema', () => { const props = l2UnusedCodeTool.inputSchema.properties as any; expect(props.reportId.type).toBe('string'); expect(props.url.type).toBe('string'); expect(props.device.enum).toContain('mobile'); expect(props.threshold.type).toBe('number'); expect(props.threshold.default).toBe(1024); }); }); describe('executeL2UnusedCode', () => { it('should analyze unused code from report ID', async () => { vi.spyOn(l1GetReport, 'executeL1GetReport').mockResolvedValue({ reportId: 'test-report', data: mockReport as any, metadata: { url: 'https://example.com', device: 'mobile', categories: ['performance'], timestamp: Date.now(), }, }); vi.spyOn(unusedCode, 'analyzeUnusedCode').mockReturnValue(mockUnusedCodeAnalysis as any); const result = await executeL2UnusedCode({ reportId: 'test-report', }); expect(result.reportId).toBe('test-report'); expect(result.unusedCode.totalWastedBytes).toBe(250000); expect(result.unusedCode.files).toHaveLength(3); expect(result.unusedCode.files[0]).toEqual({ url: 'https://example.com/style.css', wastedBytes: 75000, wastedPercent: 60, totalBytes: 125000, type: 'css', }); expect(result.unusedCode.recommendations).toContain('Remove unused CSS from style.css'); expect(result.unusedCode.recommendations).toContain('Tree-shake vendor.js to reduce bundle size'); }); it('should collect and analyze by URL', async () => { vi.spyOn(l1Collect, 'executeL1Collect').mockResolvedValue({ reportId: 'new-report', url: 'https://example.com', device: 'desktop', categories: ['performance'], timestamp: Date.now(), cached: false, }); vi.spyOn(l1GetReport, 'executeL1GetReport').mockResolvedValue({ reportId: 'new-report', data: mockReport as any, metadata: { url: 'https://example.com', device: 'desktop', categories: ['performance'], timestamp: Date.now(), }, }); vi.spyOn(unusedCode, 'analyzeUnusedCode').mockReturnValue(mockUnusedCodeAnalysis as any); const result = await executeL2UnusedCode({ url: 'https://example.com', device: 'desktop', }); expect(l1Collect.executeL1Collect).toHaveBeenCalledWith({ url: 'https://example.com', device: 'desktop', categories: ['performance'], gather: false, }); expect(result.reportId).toBe('new-report'); }); it('should handle no unused code detected', async () => { vi.spyOn(l1GetReport, 'executeL1GetReport').mockResolvedValue({ reportId: 'test-report', data: mockReport as any, metadata: { url: 'https://example.com', device: 'mobile', categories: ['performance'], timestamp: Date.now(), }, }); vi.spyOn(unusedCode, 'analyzeUnusedCode').mockReturnValue(null as any); const result = await executeL2UnusedCode({ reportId: 'test-report', }); expect(result.unusedCode.totalWastedBytes).toBe(0); expect(result.unusedCode.totalWastedMs).toBe(0); expect(result.unusedCode.files).toHaveLength(0); expect(result.unusedCode.recommendations).toContain('No significant unused code detected'); }); it('should throw error when neither reportId nor url provided', async () => { await expect(executeL2UnusedCode({})).rejects.toThrow( 'Either reportId, url, or report is required' ); }); it('should correctly categorize CSS and JS files', async () => { const mixedFiles = { totalWastedBytes: 100000, items: [ { url: 'main.css', unusedBytes: 10000, unusedPercent: 20, totalBytes: 50000, type: 'css' }, { url: 'theme.css', unusedBytes: 15000, unusedPercent: 30, totalBytes: 50000, type: 'css' }, { url: 'app.js', unusedBytes: 25000, unusedPercent: 25, totalBytes: 100000, type: 'js' }, { url: 'vendor.js', unusedBytes: 50000, unusedPercent: 50, totalBytes: 100000, type: 'js' }, ], recommendations: [], }; vi.spyOn(l1GetReport, 'executeL1GetReport').mockResolvedValue({ reportId: 'test-report', data: mockReport as any, metadata: { url: 'https://example.com', device: 'mobile', categories: ['performance'], timestamp: Date.now(), }, }); vi.spyOn(unusedCode, 'analyzeUnusedCode').mockReturnValue(mixedFiles as any); const result = await executeL2UnusedCode({ reportId: 'test-report', }); const cssFiles = result.unusedCode.files.filter(f => f.type === 'css'); const jsFiles = result.unusedCode.files.filter(f => f.type === 'js'); expect(cssFiles).toHaveLength(2); expect(jsFiles).toHaveLength(2); }); it('should handle empty recommendations', async () => { const analysisWithoutRecs = { totalWastedBytes: 50000, items: [ { url: 'small.js', unusedBytes: 50000, unusedPercent: 10, totalBytes: 500000, type: 'js' }, ], recommendations: [], }; vi.spyOn(l1GetReport, 'executeL1GetReport').mockResolvedValue({ reportId: 'test-report', data: mockReport as any, metadata: { url: 'https://example.com', device: 'mobile', categories: ['performance'], timestamp: Date.now(), }, }); vi.spyOn(unusedCode, 'analyzeUnusedCode').mockReturnValue(analysisWithoutRecs as any); const result = await executeL2UnusedCode({ reportId: 'test-report', }); expect(result.unusedCode.recommendations).toHaveLength(0); }); it('should set totalWastedMs to 0 as not available', async () => { vi.spyOn(l1GetReport, 'executeL1GetReport').mockResolvedValue({ reportId: 'test-report', data: mockReport as any, metadata: { url: 'https://example.com', device: 'mobile', categories: ['performance'], timestamp: Date.now(), }, }); vi.spyOn(unusedCode, 'analyzeUnusedCode').mockReturnValue(mockUnusedCodeAnalysis as any); const result = await executeL2UnusedCode({ reportId: 'test-report', }); expect(result.unusedCode.totalWastedMs).toBe(0); }); it('should preserve all file metadata', async () => { vi.spyOn(l1GetReport, 'executeL1GetReport').mockResolvedValue({ reportId: 'test-report', data: mockReport as any, metadata: { url: 'https://example.com', device: 'mobile', categories: ['performance'], timestamp: Date.now(), }, }); vi.spyOn(unusedCode, 'analyzeUnusedCode').mockReturnValue(mockUnusedCodeAnalysis as any); const result = await executeL2UnusedCode({ reportId: 'test-report', threshold: 2048, }); result.unusedCode.files.forEach((file, index) => { const original = mockUnusedCodeAnalysis.items[index]; expect(file.url).toBe(original.url); expect(file.wastedBytes).toBe(original.unusedBytes); expect(file.wastedPercent).toBe(original.unusedPercent); expect(file.totalBytes).toBe(original.totalBytes); expect(file.type).toBe(original.type); }); }); }); });

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