Skip to main content
Glama
registry.test.ts12.8 kB
/** * Tests for Builder Adapter Registry * * @package WP_Navigator_MCP */ import { describe, it, expect, beforeEach } from 'vitest'; import { AdapterRegistry } from './registry.js'; import type { BuilderAdapter, PageData, BuilderDetectionResult, ConversionResult, ConversionOptions, } from './types.js'; import type { NeutralLayout } from '../layout/types.js'; /** * Create a mock adapter for testing */ function createMockAdapter( name: string, options: { supported?: boolean; detectConfidence?: number; detectResult?: boolean; } = {} ): BuilderAdapter { const { supported = true, detectConfidence = 0.9, detectResult = true } = options; return { name, displayName: `${name.charAt(0).toUpperCase()}${name.slice(1)} Builder`, supported, version: { adapter: '1.0.0' }, capabilities: { canExtract: true, canApply: true, canDetect: true, supportsNesting: true, supportsResponsive: true, supportsAnimations: false, supportedElements: ['paragraph', 'heading', 'image'], }, detect(_page: PageData): BuilderDetectionResult { return { detected: detectResult, confidence: detectConfidence, method: 'content', }; }, extractLayout(_page: PageData, _options?: ConversionOptions): ConversionResult<NeutralLayout> { return { data: { layout_version: '1.0', source: { builder: name }, elements: [], }, success: true, warnings: [], }; }, extractLayoutFromContent(_content: string, _options?: ConversionOptions): ConversionResult<NeutralLayout> { return { data: { layout_version: '1.0', source: { builder: name }, elements: [], }, success: true, warnings: [], }; }, applyLayout(_layout: NeutralLayout, _options?: ConversionOptions): ConversionResult<string> { return { data: '', success: true, warnings: [], }; }, }; } /** * Create mock page data for testing */ function createMockPageData(content: string = ''): PageData { return { id: 1, slug: 'test-page', title: { rendered: 'Test Page' }, content: { rendered: content, raw: content }, status: 'publish', }; } describe('AdapterRegistry', () => { let registry: AdapterRegistry; beforeEach(() => { registry = new AdapterRegistry(); }); describe('register', () => { it('should register an adapter', () => { const adapter = createMockAdapter('gutenberg'); registry.register(adapter); expect(registry.has('gutenberg')).toBe(true); expect(registry.get('gutenberg')).toBe(adapter); }); it('should register with custom priority', () => { registry.register(createMockAdapter('gutenberg'), { priority: 100 }); registry.register(createMockAdapter('elementor'), { priority: 50 }); const all = registry.getAll(); expect(all[0].name).toBe('gutenberg'); // Higher priority first expect(all[1].name).toBe('elementor'); }); it('should throw on duplicate registration', () => { registry.register(createMockAdapter('gutenberg')); expect(() => registry.register(createMockAdapter('gutenberg'))).toThrow( "Adapter 'gutenberg' is already registered" ); }); it('should register disabled by default with option', () => { registry.register(createMockAdapter('experimental'), { enabled: false }); expect(registry.has('experimental')).toBe(true); expect(registry.getAll()).toHaveLength(0); // Not in enabled list }); }); describe('unregister', () => { it('should unregister an adapter', () => { registry.register(createMockAdapter('gutenberg')); expect(registry.unregister('gutenberg')).toBe(true); expect(registry.has('gutenberg')).toBe(false); }); it('should return false for non-existent adapter', () => { expect(registry.unregister('nonexistent')).toBe(false); }); }); describe('get', () => { it('should return adapter by name', () => { const adapter = createMockAdapter('gutenberg'); registry.register(adapter); expect(registry.get('gutenberg')).toBe(adapter); }); it('should return undefined for unknown adapter', () => { expect(registry.get('unknown')).toBeUndefined(); }); }); describe('setEnabled', () => { it('should enable/disable adapters', () => { registry.register(createMockAdapter('gutenberg')); expect(registry.getAll()).toHaveLength(1); registry.setEnabled('gutenberg', false); expect(registry.getAll()).toHaveLength(0); registry.setEnabled('gutenberg', true); expect(registry.getAll()).toHaveLength(1); }); }); describe('getAll', () => { it('should return all enabled adapters sorted by priority', () => { registry.register(createMockAdapter('low'), { priority: 10 }); registry.register(createMockAdapter('high'), { priority: 100 }); registry.register(createMockAdapter('medium'), { priority: 50 }); const all = registry.getAll(); expect(all.map((a) => a.name)).toEqual(['high', 'medium', 'low']); }); it('should exclude disabled adapters', () => { registry.register(createMockAdapter('enabled'), { enabled: true }); registry.register(createMockAdapter('disabled'), { enabled: false }); const all = registry.getAll(); expect(all).toHaveLength(1); expect(all[0].name).toBe('enabled'); }); }); describe('getSupported', () => { it('should return only supported adapters', () => { registry.register(createMockAdapter('supported', { supported: true })); registry.register(createMockAdapter('unsupported', { supported: false })); const supported = registry.getSupported(); expect(supported).toHaveLength(1); expect(supported[0].name).toBe('supported'); }); }); describe('getNames', () => { it('should return all adapter names', () => { registry.register(createMockAdapter('gutenberg')); registry.register(createMockAdapter('elementor')); registry.register(createMockAdapter('divi')); const names = registry.getNames(); expect(names).toContain('gutenberg'); expect(names).toContain('elementor'); expect(names).toContain('divi'); }); }); describe('detectBuilder', () => { it('should detect builder with highest confidence', () => { registry.register(createMockAdapter('low', { detectConfidence: 0.5 })); registry.register(createMockAdapter('high', { detectConfidence: 0.95 })); registry.register(createMockAdapter('medium', { detectConfidence: 0.7 })); const result = registry.detectBuilder(createMockPageData()); expect(result).not.toBeNull(); expect(result!.adapter.name).toBe('high'); expect(result!.detection.confidence).toBe(0.95); }); it('should respect minimum confidence threshold', () => { registry.register(createMockAdapter('low', { detectConfidence: 0.3 })); const result = registry.detectBuilder(createMockPageData(), 0.5); expect(result).toBeNull(); }); it('should return null when no builder detected', () => { registry.register(createMockAdapter('none', { detectResult: false })); const result = registry.detectBuilder(createMockPageData()); expect(result).toBeNull(); }); it('should skip unsupported adapters', () => { registry.register(createMockAdapter('unsupported', { supported: false, detectConfidence: 1.0 })); registry.register(createMockAdapter('supported', { supported: true, detectConfidence: 0.6 })); const result = registry.detectBuilder(createMockPageData()); expect(result).not.toBeNull(); expect(result!.adapter.name).toBe('supported'); }); it('should handle adapter detection errors gracefully', () => { const throwingAdapter = createMockAdapter('throwing'); throwingAdapter.detect = () => { throw new Error('Detection failed'); }; registry.register(throwingAdapter); registry.register(createMockAdapter('working')); const result = registry.detectBuilder(createMockPageData()); expect(result).not.toBeNull(); expect(result!.adapter.name).toBe('working'); }); }); describe('detectAllBuilders', () => { it('should return all matching builders sorted by confidence', () => { registry.register(createMockAdapter('low', { detectConfidence: 0.4 })); registry.register(createMockAdapter('high', { detectConfidence: 0.9 })); registry.register(createMockAdapter('medium', { detectConfidence: 0.6 })); const results = registry.detectAllBuilders(createMockPageData(), 0.3); expect(results).toHaveLength(3); expect(results[0].adapter.name).toBe('high'); expect(results[1].adapter.name).toBe('medium'); expect(results[2].adapter.name).toBe('low'); }); it('should filter by minimum confidence', () => { registry.register(createMockAdapter('low', { detectConfidence: 0.3 })); registry.register(createMockAdapter('high', { detectConfidence: 0.9 })); const results = registry.detectAllBuilders(createMockPageData(), 0.5); expect(results).toHaveLength(1); expect(results[0].adapter.name).toBe('high'); }); }); describe('clear', () => { it('should remove all adapters', () => { registry.register(createMockAdapter('one')); registry.register(createMockAdapter('two')); registry.register(createMockAdapter('three')); registry.clear(); expect(registry.getAll()).toHaveLength(0); expect(registry.getNames()).toHaveLength(0); }); }); describe('getStats', () => { it('should return correct statistics', () => { registry.register(createMockAdapter('enabled1', { supported: true }), { enabled: true }); registry.register(createMockAdapter('enabled2', { supported: false }), { enabled: true }); registry.register(createMockAdapter('disabled', { supported: true }), { enabled: false }); const stats = registry.getStats(); expect(stats.total).toBe(3); expect(stats.enabled).toBe(2); expect(stats.supported).toBe(1); }); }); }); describe('BuilderAdapter Interface', () => { it('should have all required properties', () => { const adapter = createMockAdapter('test'); // Identification expect(adapter.name).toBe('test'); expect(adapter.displayName).toBeTruthy(); expect(typeof adapter.supported).toBe('boolean'); // Version expect(adapter.version).toBeDefined(); expect(adapter.version.adapter).toBeTruthy(); // Capabilities expect(adapter.capabilities).toBeDefined(); expect(typeof adapter.capabilities.canExtract).toBe('boolean'); expect(typeof adapter.capabilities.canApply).toBe('boolean'); expect(typeof adapter.capabilities.canDetect).toBe('boolean'); expect(Array.isArray(adapter.capabilities.supportedElements)).toBe(true); // Methods expect(typeof adapter.detect).toBe('function'); expect(typeof adapter.extractLayout).toBe('function'); expect(typeof adapter.extractLayoutFromContent).toBe('function'); expect(typeof adapter.applyLayout).toBe('function'); }); it('should implement detect correctly', () => { const adapter = createMockAdapter('test'); const result = adapter.detect(createMockPageData()); expect(result).toHaveProperty('detected'); expect(result).toHaveProperty('confidence'); expect(result).toHaveProperty('method'); expect(typeof result.detected).toBe('boolean'); expect(typeof result.confidence).toBe('number'); expect(result.confidence).toBeGreaterThanOrEqual(0); expect(result.confidence).toBeLessThanOrEqual(1); }); it('should implement extractLayout correctly', () => { const adapter = createMockAdapter('test'); const result = adapter.extractLayout(createMockPageData()); expect(result).toHaveProperty('data'); expect(result).toHaveProperty('success'); expect(result).toHaveProperty('warnings'); expect(result.data).toHaveProperty('layout_version'); expect(result.data).toHaveProperty('source'); expect(result.data).toHaveProperty('elements'); }); it('should implement applyLayout correctly', () => { const adapter = createMockAdapter('test'); const layout = { layout_version: '1.0' as const, source: { builder: 'test' }, elements: [], }; const result = adapter.applyLayout(layout); expect(result).toHaveProperty('data'); expect(result).toHaveProperty('success'); expect(result).toHaveProperty('warnings'); expect(typeof result.data).toBe('string'); }); });

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/littlebearapps/wp-navigator-mcp'

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