UNPKG

apple-hig-mcp

Version:

High-performance MCP server providing instant access to Apple's Human Interface Guidelines via hybrid static/dynamic content delivery

191 lines 8.81 kB
import { HIGToolProvider } from '../tools.js'; import { CrawleeHIGService } from '../services/crawlee-hig.service.js'; import { HIGCache } from '../cache.js'; import { HIGResourceProvider } from '../resources.js'; describe('HIGToolProvider', () => { let cache; let crawleeService; let resourceProvider; let toolProvider; beforeEach(() => { cache = new HIGCache(60); crawleeService = new CrawleeHIGService(cache); resourceProvider = new HIGResourceProvider(crawleeService, cache); toolProvider = new HIGToolProvider(crawleeService, cache, resourceProvider); }); afterEach(async () => { await crawleeService.teardown(); cache.clear(); }); describe('Search Guidelines', () => { test('should search guidelines successfully', async () => { const mockSearchResults = [ { id: 'ios-button', title: 'iOS Button', url: 'https://example.com/button', platform: 'iOS', relevanceScore: 1.0, snippet: 'Button design guidelines', type: 'section' } ]; jest.spyOn(crawleeService, 'searchContent').mockResolvedValue(mockSearchResults); jest.spyOn(resourceProvider, 'getResource').mockResolvedValue({ uri: 'hig://ios', name: 'iOS Guidelines', description: 'iOS design guidelines', mimeType: 'text/markdown', content: 'Comprehensive button design guidelines for iOS' }); const result = await toolProvider.searchGuidelines({ query: 'button', platform: 'iOS', limit: 10 }); expect(result.results).toHaveLength(1); expect(result.query).toBe('button'); expect(result.filters.platform).toBe('iOS'); expect(result.total).toBe(1); }); test('should handle search errors gracefully with fallback', async () => { jest.spyOn(crawleeService, 'searchContent').mockRejectedValue(new Error('Search failed')); const result = await toolProvider.searchGuidelines({ query: 'button' }); // Should return fallback results instead of throwing expect(result.results.length).toBeGreaterThan(0); expect(result.query).toBe('button'); }); }); describe('Get Component Spec', () => { test('should get component specification', async () => { const mockSearchResults = [ { id: 'ios-button', title: 'iOS Button', url: 'https://example.com/button', platform: 'iOS', relevanceScore: 1.0, snippet: 'Button specifications', type: 'section' } ]; const mockSections = [ { id: 'ios-button', title: 'iOS Button', url: 'https://example.com/button', platform: 'iOS', category: 'visual-design' } ]; const mockSectionWithContent = { ...mockSections[0], content: 'Button height: 44pt\nButton color: blue\nPadding: 16pt\n- Use clear labels\n- Make buttons accessible', lastUpdated: new Date() }; jest.spyOn(crawleeService, 'searchContent').mockResolvedValue(mockSearchResults); jest.spyOn(crawleeService, 'discoverSections').mockResolvedValue(mockSections); jest.spyOn(crawleeService, 'fetchSectionContent').mockResolvedValue(mockSectionWithContent); const result = await toolProvider.getComponentSpec({ componentName: 'Button', platform: 'iOS' }); expect(result.component).toBeTruthy(); expect(result.component?.title).toBe('Buttons'); // Uses fallback implementation expect(result.component?.platforms).toContain('iOS'); expect(result.component?.specifications).toBeDefined(); expect(result.platforms).toContain('iOS'); }); test('should return null for non-existent component', async () => { jest.spyOn(crawleeService, 'searchContent').mockResolvedValue([]); const result = await toolProvider.getComponentSpec({ componentName: 'NonExistentComponent' }); expect(result.component).toBeNull(); expect(result.relatedComponents).toEqual([]); expect(result.platforms).toEqual([]); }); }); describe('Get Design Tokens', () => { test('should get design tokens for button component', async () => { const result = await toolProvider.getDesignTokens({ component: 'Button', platform: 'iOS', tokenType: 'all' }); expect(result.component).toBe('Button'); expect(result.platform).toBe('iOS'); expect(result.tokens).toBeDefined(); expect(result.tokens.colors).toBeDefined(); expect(result.tokens.spacing).toBeDefined(); expect(result.tokens.typography).toBeDefined(); expect(result.tokens.dimensions).toBeDefined(); }); test('should get specific token type', async () => { const result = await toolProvider.getDesignTokens({ component: 'Button', platform: 'iOS', tokenType: 'colors' }); expect(result.tokens.colors).toBeDefined(); expect(result.tokens.spacing).toBeUndefined(); }); }); describe('Get Accessibility Requirements', () => { test('should get accessibility requirements for button', async () => { const result = await toolProvider.getAccessibilityRequirements({ component: 'Button', platform: 'iOS' }); expect(result.component).toBe('Button'); expect(result.platform).toBe('iOS'); expect(result.requirements).toBeDefined(); expect(result.requirements.minimumTouchTarget).toBeDefined(); expect(result.requirements.contrastRatio).toBeDefined(); expect(result.requirements.voiceOverSupport).toBeDefined(); expect(result.requirements.keyboardNavigation).toBeDefined(); expect(result.requirements.wcagCompliance).toBeDefined(); }); test('should get accessibility requirements for navigation', async () => { const result = await toolProvider.getAccessibilityRequirements({ component: 'Navigation Bar', platform: 'iOS' }); expect(result.requirements.voiceOverSupport).toContain('Navigation bar trait'); expect(result.requirements.keyboardNavigation).toContain('Tab navigation through interactive elements'); }); }); describe('Helper Methods', () => { test('should extract specifications from content', () => { const content = 'Button height: 44pt\nMinimum touch target: 44pt\nButton text color: blue'; // Access private method for testing const extractMethod = toolProvider.extractSpecifications.bind(toolProvider); const specs = extractMethod(content); expect(specs).toBeDefined(); expect(specs.height || specs.minimumSize || specs.touchTarget).toBeDefined(); }); test('should extract guidelines from content', () => { const content = ` Guidelines: - Use clear, descriptive labels - Make buttons large enough to tap 1. Ensure proper contrast 2. Support accessibility features `; const extractMethod = toolProvider.extractGuidelines.bind(toolProvider); const guidelines = extractMethod(content); expect(guidelines.length).toBeGreaterThan(0); expect(guidelines).toContain('Use clear, descriptive labels'); expect(guidelines).toContain('Ensure proper contrast'); }); test('should extract examples from content', () => { const content = 'For example: Primary buttons, Secondary buttons. Such as: Save, Cancel buttons.'; const extractMethod = toolProvider.extractExamples.bind(toolProvider); const examples = extractMethod(content); expect(examples.length).toBeGreaterThan(0); }); }); }); //# sourceMappingURL=tools.test.js.map