Skip to main content
Glama

Visa Design System MCP Server

by MarySuneela
design-token-service.test.ts16.4 kB
/** * Unit tests for DesignTokenService class */ import type { DesignToken } from '../src/types/index.js'; // Mock DataManager jest.mock('../src/utils/data-manager.js'); import { DesignTokenService } from '../src/services/design-token-service.js'; import { DataManager } from '../src/utils/data-manager.js'; describe('DesignTokenService', () => { let designTokenService: DesignTokenService; let mockDataManager: jest.Mocked<DataManager>; let mockDesignTokens: DesignToken[]; beforeEach(() => { // Setup mock design tokens mockDesignTokens = [ { name: 'primary-blue', value: '#0066CC', category: 'color', description: 'Primary brand blue color', usage: ['buttons', 'links'], aliases: ['brand-blue', 'main-blue'] }, { name: 'secondary-gray', value: '#666666', category: 'color', description: 'Secondary gray color', usage: ['text', 'borders'], deprecated: false }, { name: 'old-red', value: '#FF0000', category: 'color', description: 'Deprecated red color', deprecated: true, aliases: ['danger-red'] }, { name: 'font-size-large', value: '18px', category: 'typography', description: 'Large font size for headings', usage: ['headings', 'titles'] }, { name: 'spacing-medium', value: 16, category: 'spacing', description: 'Medium spacing value', usage: ['margins', 'padding'] }, { name: 'elevation-high', value: '0 8px 16px rgba(0,0,0,0.2)', category: 'elevation', description: 'High elevation shadow', usage: ['modals', 'dropdowns'] } ]; // Setup mock DataManager mockDataManager = { getCachedData: jest.fn().mockReturnValue({ designTokens: mockDesignTokens, components: [], guidelines: [] }), initialize: jest.fn(), loadData: jest.fn(), validateData: jest.fn(), watchFiles: jest.fn(), stopWatching: jest.fn() } as any; (DataManager as jest.MockedClass<typeof DataManager>).mockImplementation(() => mockDataManager); // Create DesignTokenService instance designTokenService = new DesignTokenService(mockDataManager); }); afterEach(() => { jest.clearAllMocks(); }); describe('getTokens', () => { it('should return all tokens when no options provided', async () => { const tokens = await designTokenService.getTokens(); expect(tokens).toHaveLength(6); expect(tokens).toEqual(mockDesignTokens); expect(mockDataManager.getCachedData).toHaveBeenCalledTimes(1); }); it('should filter tokens by category', async () => { const tokens = await designTokenService.getTokens({ category: 'color' }); expect(tokens).toHaveLength(3); expect(tokens.every(token => token.category === 'color')).toBe(true); expect(tokens.map(t => t.name)).toEqual(['primary-blue', 'secondary-gray', 'old-red']); }); it('should filter tokens by deprecated status', async () => { const deprecatedTokens = await designTokenService.getTokens({ deprecated: true }); const activeTokens = await designTokenService.getTokens({ deprecated: false }); expect(deprecatedTokens).toHaveLength(1); expect(deprecatedTokens[0].name).toBe('old-red'); expect(activeTokens).toHaveLength(5); expect(activeTokens.every(token => token.deprecated !== true)).toBe(true); }); it('should filter tokens by usage', async () => { const tokens = await designTokenService.getTokens({ hasUsage: ['buttons'] }); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); }); it('should combine multiple filters', async () => { const tokens = await designTokenService.getTokens({ category: 'color', deprecated: false }); expect(tokens).toHaveLength(2); expect(tokens.map(t => t.name)).toEqual(['primary-blue', 'secondary-gray']); }); it('should throw error when no data available', async () => { mockDataManager.getCachedData.mockReturnValue(null); await expect(designTokenService.getTokens()).rejects.toMatchObject({ code: 'NO_DATA', message: 'No design token data available' }); }); }); describe('getToken', () => { it('should return specific token by name', async () => { const token = await designTokenService.getToken('primary-blue'); expect(token).toEqual(mockDesignTokens[0]); expect(token.name).toBe('primary-blue'); }); it('should be case insensitive', async () => { const token = await designTokenService.getToken('PRIMARY-BLUE'); expect(token).toEqual(mockDesignTokens[0]); expect(token.name).toBe('primary-blue'); }); it('should throw error for non-existent token', async () => { await expect(designTokenService.getToken('non-existent')).rejects.toMatchObject({ code: 'TOKEN_NOT_FOUND', message: 'Design token "non-existent" not found' }); }); it('should throw error for invalid name parameter', async () => { await expect(designTokenService.getToken('')).rejects.toMatchObject({ code: 'INVALID_NAME', message: 'Token name must be a non-empty string' }); await expect(designTokenService.getToken(null as any)).rejects.toMatchObject({ code: 'INVALID_NAME' }); }); it('should throw error when no data available', async () => { mockDataManager.getCachedData.mockReturnValue(null); await expect(designTokenService.getToken('primary-blue')).rejects.toMatchObject({ code: 'NO_DATA', message: 'No design token data available' }); }); }); describe('searchTokens', () => { it('should search tokens by name', async () => { const tokens = await designTokenService.searchTokens('blue'); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); }); it('should search tokens by value', async () => { const tokens = await designTokenService.searchTokens('#0066CC'); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); }); it('should search tokens by description', async () => { const tokens = await designTokenService.searchTokens('brand'); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); }); it('should search tokens by usage', async () => { const tokens = await designTokenService.searchTokens('buttons'); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); }); it('should search tokens by aliases', async () => { const tokens = await designTokenService.searchTokens('brand-blue'); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); }); it('should be case insensitive', async () => { const tokens = await designTokenService.searchTokens('BLUE'); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); }); it('should return multiple matching tokens', async () => { const tokens = await designTokenService.searchTokens('color'); expect(tokens.length).toBeGreaterThan(1); expect(tokens.every(token => token.description?.toLowerCase().includes('color') || token.usage?.some(usage => usage.toLowerCase().includes('color')) )).toBe(true); }); it('should return empty array for no matches', async () => { const tokens = await designTokenService.searchTokens('nonexistent'); expect(tokens).toHaveLength(0); }); it('should throw error for invalid query parameter', async () => { await expect(designTokenService.searchTokens('')).rejects.toMatchObject({ code: 'INVALID_QUERY', message: 'Search query must be a non-empty string' }); await expect(designTokenService.searchTokens(null as any)).rejects.toMatchObject({ code: 'INVALID_QUERY' }); }); it('should throw error when no data available', async () => { mockDataManager.getCachedData.mockReturnValue(null); await expect(designTokenService.searchTokens('blue')).rejects.toMatchObject({ code: 'NO_DATA', message: 'No design token data available' }); }); }); describe('getTokensByCategory', () => { it('should return tokens for valid category', async () => { const tokens = await designTokenService.getTokensByCategory('typography'); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('font-size-large'); expect(tokens[0].category).toBe('typography'); }); it('should return empty array for category with no tokens', async () => { const tokens = await designTokenService.getTokensByCategory('motion'); expect(tokens).toHaveLength(0); }); it('should throw error for invalid category parameter', async () => { await expect(designTokenService.getTokensByCategory('')).rejects.toMatchObject({ code: 'INVALID_CATEGORY', message: 'Category must be a valid token category' }); }); }); describe('getTokenCategories', () => { it('should return all available categories', async () => { const categories = await designTokenService.getTokenCategories(); expect(categories).toEqual(['color', 'elevation', 'spacing', 'typography']); expect(categories).toHaveLength(4); }); it('should return sorted categories', async () => { const categories = await designTokenService.getTokenCategories(); const sortedCategories = [...categories].sort(); expect(categories).toEqual(sortedCategories); }); }); describe('getTokenUsage', () => { it('should return usage array for token', async () => { const usage = await designTokenService.getTokenUsage('primary-blue'); expect(usage).toEqual(['buttons', 'links']); }); it('should return empty array for token without usage', async () => { // Add a token without usage for this test const tokenWithoutUsage: DesignToken = { name: 'test-token', value: '#000000', category: 'color' }; mockDataManager.getCachedData.mockReturnValue({ designTokens: [tokenWithoutUsage], components: [], guidelines: [] }); const usage = await designTokenService.getTokenUsage('test-token'); expect(usage).toEqual([]); }); }); describe('getTokenAliases', () => { it('should return aliases array for token', async () => { const aliases = await designTokenService.getTokenAliases('primary-blue'); expect(aliases).toEqual(['brand-blue', 'main-blue']); }); it('should return empty array for token without aliases', async () => { const aliases = await designTokenService.getTokenAliases('font-size-large'); expect(aliases).toEqual([]); }); }); describe('getDeprecatedTokens', () => { it('should return only deprecated tokens', async () => { const tokens = await designTokenService.getDeprecatedTokens(); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('old-red'); expect(tokens[0].deprecated).toBe(true); }); }); describe('getActiveTokens', () => { it('should return only non-deprecated tokens', async () => { const tokens = await designTokenService.getActiveTokens(); expect(tokens).toHaveLength(5); expect(tokens.every(token => token.deprecated !== true)).toBe(true); }); }); describe('findTokensByValue', () => { it('should find tokens by string value', async () => { const tokens = await designTokenService.findTokensByValue('#0066CC'); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); }); it('should find tokens by numeric value', async () => { const tokens = await designTokenService.findTokensByValue(16); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('spacing-medium'); }); it('should be case insensitive for string values', async () => { const tokens = await designTokenService.findTokensByValue('#0066cc'); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); }); it('should return empty array for no matches', async () => { const tokens = await designTokenService.findTokensByValue('#FFFFFF'); expect(tokens).toHaveLength(0); }); }); describe('getTokensWithUsage', () => { it('should return tokens with specific usage', async () => { const tokens = await designTokenService.getTokensWithUsage('buttons'); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); }); it('should return empty array for usage not found', async () => { const tokens = await designTokenService.getTokensWithUsage('nonexistent-usage'); expect(tokens).toHaveLength(0); }); it('should throw error for invalid usage parameter', async () => { await expect(designTokenService.getTokensWithUsage('')).rejects.toMatchObject({ code: 'INVALID_USAGE', message: 'Usage must be a non-empty string' }); }); }); describe('Error Handling', () => { it('should provide helpful error messages with suggestions', async () => { try { await designTokenService.getToken('nonexistent'); } catch (error: any) { expect(error.code).toBe('TOKEN_NOT_FOUND'); expect(error.message).toContain('Design token "nonexistent" not found'); expect(error.suggestions).toBeDefined(); expect(Array.isArray(error.suggestions)).toBe(true); expect(error.suggestions.length).toBeGreaterThan(0); } }); it('should handle data manager returning null', async () => { mockDataManager.getCachedData.mockReturnValue(null); const methods = [ () => designTokenService.getTokens(), () => designTokenService.getToken('test'), () => designTokenService.searchTokens('test'), () => designTokenService.findTokensByValue('test') ]; for (const method of methods) { await expect(method()).rejects.toMatchObject({ code: 'NO_DATA', message: 'No design token data available' }); } }); it('should validate parameter types', async () => { const invalidParams = [ () => designTokenService.getToken(123 as any), () => designTokenService.searchTokens(123 as any), () => designTokenService.getTokensByCategory(123 as any), () => designTokenService.getTokensWithUsage(123 as any) ]; for (const method of invalidParams) { await expect(method()).rejects.toHaveProperty('code'); } }); }); describe('Filter Logic', () => { it('should handle complex filter combinations', async () => { const tokens = await designTokenService.getTokens({ category: 'color', deprecated: false, hasUsage: ['buttons'] }); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); expect(tokens[0].category).toBe('color'); expect(tokens[0].deprecated).not.toBe(true); expect(tokens[0].usage).toContain('buttons'); }); it('should return empty array when filters match nothing', async () => { const tokens = await designTokenService.getTokens({ category: 'color', hasUsage: ['nonexistent-usage'] }); expect(tokens).toHaveLength(0); }); it('should handle partial usage matches', async () => { const tokens = await designTokenService.getTokens({ hasUsage: ['button'] // partial match for 'buttons' }); expect(tokens).toHaveLength(1); expect(tokens[0].name).toBe('primary-blue'); }); }); });

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/MarySuneela/mcp-vpds'

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