Skip to main content
Glama

Motion.dev MCP Server

patterns.test.ts14.9 kB
/** * Test suite for animation patterns library */ import { AnimationPatterns } from '../../../src/generation/templates/patterns.js'; import { Framework } from '../../../src/types/motion.js'; describe('AnimationPatterns', () => { let patterns: AnimationPatterns; beforeEach(() => { patterns = new AnimationPatterns(); }); describe('Pattern Management', () => { test('should load default patterns on initialization', () => { const allPatterns = patterns.getAllPatterns(); expect(allPatterns.size).toBeGreaterThan(0); expect(allPatterns.has('fade-in')).toBe(true); expect(allPatterns.has('slide-up')).toBe(true); expect(allPatterns.has('scale-in')).toBe(true); expect(allPatterns.has('bounce-in')).toBe(true); }); test('should get pattern by ID', () => { const fadePattern = patterns.getPattern('fade-in'); expect(fadePattern).toBeDefined(); expect(fadePattern?.id).toBe('fade-in'); expect(fadePattern?.category).toBe('entrance'); expect(fadePattern?.frameworks).toHaveProperty('react'); expect(fadePattern?.frameworks).toHaveProperty('vue'); expect(fadePattern?.frameworks).toHaveProperty('js'); }); test('should return undefined for non-existent pattern', () => { const nonExistent = patterns.getPattern('non-existent-pattern'); expect(nonExistent).toBeUndefined(); }); test('should get patterns by category', () => { const entrancePatterns = patterns.getPatternsByCategory('entrance'); const exitPatterns = patterns.getPatternsByCategory('exit'); const gesturePatterns = patterns.getPatternsByCategory('gesture'); expect(entrancePatterns.length).toBeGreaterThan(0); expect(exitPatterns.length).toBeGreaterThan(0); expect(gesturePatterns.length).toBeGreaterThan(0); expect(entrancePatterns.every(p => p.category === 'entrance')).toBe(true); expect(exitPatterns.every(p => p.category === 'exit')).toBe(true); expect(gesturePatterns.every(p => p.category === 'gesture')).toBe(true); }); test('should get patterns by framework', () => { const reactPatterns = patterns.getPatternsByFramework('react'); const vuePatterns = patterns.getPatternsByFramework('vue'); const jsPatterns = patterns.getPatternsByFramework('js'); expect(reactPatterns.length).toBeGreaterThan(0); expect(vuePatterns.length).toBeGreaterThan(0); expect(jsPatterns.length).toBeGreaterThan(0); // All patterns should support all frameworks expect(reactPatterns.length).toBe(vuePatterns.length); expect(vuePatterns.length).toBe(jsPatterns.length); }); }); describe('Code Generation', () => { test('should generate React component code from pattern', () => { const code = patterns.getPatternCode('fade-in', 'react', 'FadeComponent'); expect(code).toContain('import React from \'react\''); expect(code).toContain('import { motion } from \'framer-motion\''); expect(code).toContain('const FadeComponent'); expect(code).toContain('motion.div'); expect(code).toContain('initial'); expect(code).toContain('animate'); expect(code).toContain('export default FadeComponent'); }); test('should generate Vue component code from pattern', () => { const code = patterns.getPatternCode('slide-up', 'vue', 'SlideComponent'); expect(code).toContain('<template>'); expect(code).toContain('<script>'); expect(code).toContain('v-motion'); expect(code).toContain('SlideComponent'); }); test('should generate JavaScript function from pattern', () => { const code = patterns.getPatternCode('scale-in', 'js', 'scaleAnimation'); expect(code).toContain('import { animate } from \'motion\''); expect(code).toContain('function scaleAnimation'); expect(code).toContain('document.querySelector'); expect(code).toContain('animate('); }); test('should use default component name when not provided', () => { const reactCode = patterns.getPatternCode('fade-in', 'react'); const vueCode = patterns.getPatternCode('fade-in', 'vue'); const jsCode = patterns.getPatternCode('fade-in', 'js'); expect(reactCode).toContain('AnimatedComponent'); expect(vueCode).toContain('AnimatedComponent'); expect(jsCode).toContain('animateElement'); }); test('should throw error for invalid pattern ID', () => { expect(() => { patterns.getPatternCode('invalid-pattern', 'react'); }).toThrow('Pattern not found: invalid-pattern'); }); }); describe('Pattern Categories', () => { test('should have entrance patterns', () => { const entrancePatterns = patterns.getPatternsByCategory('entrance'); const expectedEntrance = ['fade-in', 'slide-up', 'slide-down', 'slide-left', 'slide-right', 'scale-in', 'bounce-in']; expectedEntrance.forEach(patternId => { expect(entrancePatterns.some(p => p.id === patternId)).toBe(true); }); }); test('should have exit patterns', () => { const exitPatterns = patterns.getPatternsByCategory('exit'); const expectedExit = ['fade-out', 'slide-out-up', 'slide-out-down', 'scale-out']; expectedExit.forEach(patternId => { expect(exitPatterns.some(p => p.id === patternId)).toBe(true); }); }); test('should have gesture patterns', () => { const gesturePatterns = patterns.getPatternsByCategory('gesture'); const expectedGesture = ['hover-lift', 'tap-scale', 'drag-rotation']; expectedGesture.forEach(patternId => { expect(gesturePatterns.some(p => p.id === patternId)).toBe(true); }); }); test('should have layout patterns', () => { const layoutPatterns = patterns.getPatternsByCategory('layout'); expect(layoutPatterns.some(p => p.id === 'layout-shift')).toBe(true); }); test('should have scroll patterns', () => { const scrollPatterns = patterns.getPatternsByCategory('scroll'); expect(scrollPatterns.some(p => p.id === 'scroll-reveal')).toBe(true); }); test('should have stagger patterns', () => { const staggerPatterns = patterns.getPatternsByCategory('stagger'); expect(staggerPatterns.some(p => p.id === 'stagger-children')).toBe(true); }); test('should have complex patterns', () => { const complexPatterns = patterns.getPatternsByCategory('complex'); expect(complexPatterns.some(p => p.id === 'morphing-shapes')).toBe(true); }); }); describe('Performance Analysis', () => { test('should provide performance metrics for patterns', () => { const pattern = patterns.getPattern('fade-in'); expect(pattern?.performance).toBeDefined(); expect(pattern?.performance.score).toBeGreaterThanOrEqual(0); expect(pattern?.performance.score).toBeLessThanOrEqual(100); expect(pattern?.performance.bundleSize).toBeGreaterThan(0); expect(pattern?.performance.renderComplexity).toMatch(/low|medium|high/); }); test('should analyze pattern performance', () => { const analysis = patterns.analyzePatternPerformance('bounce-in'); expect(analysis).toBeDefined(); expect(analysis.score).toBeGreaterThanOrEqual(0); expect(analysis.recommendations).toBeDefined(); expect(Array.isArray(analysis.recommendations)).toBe(true); }); test('should compare pattern performance', () => { const comparison = patterns.comparePatterns(['fade-in', 'bounce-in']); expect(comparison).toBeDefined(); expect(comparison.fastest).toBeDefined(); expect(comparison.smallest).toBeDefined(); expect(comparison.details).toHaveLength(2); }); }); describe('Accessibility Features', () => { test('should provide accessibility information for patterns', () => { const pattern = patterns.getPattern('fade-in'); expect(pattern?.accessibility).toBeDefined(); expect(pattern?.accessibility.respectsReducedMotion).toBeDefined(); expect(pattern?.accessibility.hasAltText).toBeDefined(); expect(pattern?.accessibility.score).toBeGreaterThanOrEqual(0); expect(pattern?.accessibility.score).toBeLessThanOrEqual(100); }); test('should validate pattern accessibility', () => { const validation = patterns.validateAccessibility('slide-up'); expect(validation).toBeDefined(); expect(validation.isAccessible).toBeDefined(); expect(validation.issues).toBeDefined(); expect(validation.suggestions).toBeDefined(); }); test('should get accessibility-compliant patterns', () => { const accessiblePatterns = patterns.getAccessiblePatterns(); expect(accessiblePatterns.length).toBeGreaterThan(0); expect(accessiblePatterns.every(p => p.accessibility.score >= 80)).toBe(true); }); }); describe('Custom Patterns', () => { test('should allow adding custom patterns', () => { const customPattern = { id: 'custom-spin', name: 'Custom Spin', category: 'entrance' as const, description: 'A custom spinning animation', performance: { score: 85, bundleSize: 512, renderComplexity: 'medium' as const }, accessibility: { respectsReducedMotion: true, hasAltText: true, score: 90 }, frameworks: { react: { code: 'const CustomSpin = () => <motion.div animate={{ rotate: 360 }}>Content</motion.div>;', imports: ['framer-motion'], dependencies: [] }, vue: { code: '<div v-motion :animate="{ rotate: 360 }">Content</div>', imports: ['@vueuse/motion'], dependencies: [] }, js: { code: 'animate(element, { rotate: 360 });', imports: ['motion'], dependencies: [] } } }; patterns.addPattern(customPattern); const added = patterns.getPattern('custom-spin'); expect(added).toBeDefined(); expect(added?.name).toBe('Custom Spin'); }); test('should validate custom pattern structure', () => { const invalidPattern = { id: 'invalid', // Missing required fields }; expect(() => { patterns.addPattern(invalidPattern as any); }).toThrow(); }); test('should update existing patterns', () => { const updatedPattern = { id: 'fade-in', name: 'Updated Fade In', category: 'entrance' as const, description: 'Updated fade animation', performance: { score: 95, bundleSize: 256, renderComplexity: 'low' as const }, accessibility: { respectsReducedMotion: true, hasAltText: true, score: 100 }, frameworks: { react: { code: 'const UpdatedFade = () => <motion.div>Updated</motion.div>;', imports: ['framer-motion'], dependencies: [] }, vue: { code: '<div>Updated Vue</div>', imports: [], dependencies: [] }, js: { code: '// Updated JS', imports: [], dependencies: [] } } }; patterns.updatePattern('fade-in', updatedPattern); const updated = patterns.getPattern('fade-in'); expect(updated?.name).toBe('Updated Fade In'); expect(updated?.performance.score).toBe(95); }); test('should remove patterns', () => { patterns.removePattern('fade-in'); const removed = patterns.getPattern('fade-in'); expect(removed).toBeUndefined(); }); }); describe('Framework-Specific Features', () => { test('should handle React-specific optimizations', () => { const reactCode = patterns.getPatternCode('stagger-children', 'react'); expect(reactCode).toContain('AnimatePresence'); expect(reactCode).toContain('staggerChildren'); expect(reactCode).toContain('delayChildren'); }); test('should handle Vue-specific directives', () => { const vueCode = patterns.getPatternCode('scroll-reveal', 'vue'); expect(vueCode).toContain('v-motion'); expect(vueCode).toContain('v-intersection'); }); test('should handle JavaScript-specific DOM operations', () => { const jsCode = patterns.getPatternCode('drag-rotation', 'js'); expect(jsCode).toContain('addEventListener'); expect(jsCode).toContain('getBoundingClientRect'); }); }); describe('Pattern Search and Filtering', () => { test('should search patterns by name or description', () => { const fadeResults = patterns.searchPatterns('fade'); const slideResults = patterns.searchPatterns('slide'); expect(fadeResults.length).toBeGreaterThan(0); expect(slideResults.length).toBeGreaterThan(0); expect(fadeResults.some(p => p.id.includes('fade'))).toBe(true); expect(slideResults.some(p => p.id.includes('slide'))).toBe(true); }); test('should filter patterns by performance score', () => { const highPerformance = patterns.filterByPerformance(90); expect(highPerformance.length).toBeGreaterThan(0); expect(highPerformance.every(p => p.performance.score >= 90)).toBe(true); }); test('should get recommended patterns for use case', () => { const entranceRecommendations = patterns.getRecommendedPatterns('entrance', { performance: 'high', accessibility: 'required', framework: 'react' }); expect(entranceRecommendations.length).toBeGreaterThan(0); expect(entranceRecommendations.every(p => p.category === 'entrance')).toBe(true); }); }); describe('Error Handling', () => { test('should handle invalid framework gracefully', () => { expect(() => { patterns.getPatternCode('fade-in', 'invalid' as Framework); }).toThrow('Unsupported framework: invalid'); }); test('should handle empty pattern database', () => { const emptyPatterns = new AnimationPatterns(); emptyPatterns.clearAll(); expect(emptyPatterns.getAllPatterns().size).toBe(0); expect(emptyPatterns.getPatternsByCategory('entrance')).toHaveLength(0); }); test('should validate pattern dependencies', () => { const pattern = patterns.getPattern('bounce-in'); const validation = patterns.validatePatternDependencies(pattern!); expect(validation.isValid).toBe(true); expect(validation.missingDependencies).toHaveLength(0); }); }); });

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/Abhishekrajpurohit/motion-dev-mcp'

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