Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
This commit is contained in:
102
tools/api/tests/conftest.py
Normal file
102
tools/api/tests/conftest.py
Normal file
@@ -0,0 +1,102 @@
|
||||
"""
|
||||
Pytest configuration and shared fixtures for API tests
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import os
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def figma_config():
|
||||
"""Load Figma configuration from environment"""
|
||||
api_key = os.environ.get('FIGMA_API_KEY')
|
||||
file_key = os.environ.get('DSS_FIGMA_FILE_KEY')
|
||||
|
||||
if not api_key or not file_key:
|
||||
pytest.skip('FIGMA_API_KEY or DSS_FIGMA_FILE_KEY not set')
|
||||
|
||||
return {
|
||||
'api_key': api_key,
|
||||
'file_key': file_key
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def figma_client(figma_config):
|
||||
"""Initialize Figma client (mocked for now)"""
|
||||
def _mock_extract_variables(file_key, format='json'):
|
||||
"""Mock variable extraction"""
|
||||
return {
|
||||
'status': 'success',
|
||||
'file_key': file_key,
|
||||
'format': format,
|
||||
'variables': {
|
||||
'colors': {
|
||||
'primary': '#0066FF',
|
||||
'secondary': '#FF6B00',
|
||||
'success': '#00B600',
|
||||
'warning': '#FFB800',
|
||||
'danger': '#FF0000',
|
||||
},
|
||||
'typography': {
|
||||
'heading-1': {'fontSize': 32, 'fontWeight': 700},
|
||||
'heading-2': {'fontSize': 24, 'fontWeight': 700},
|
||||
'body': {'fontSize': 16, 'fontWeight': 400},
|
||||
'caption': {'fontSize': 12, 'fontWeight': 400},
|
||||
},
|
||||
'spacing': {
|
||||
'xs': 4,
|
||||
'sm': 8,
|
||||
'md': 16,
|
||||
'lg': 24,
|
||||
'xl': 32,
|
||||
},
|
||||
},
|
||||
'tokens_count': 14
|
||||
}
|
||||
|
||||
def _mock_extract_components(file_key):
|
||||
"""Mock component extraction"""
|
||||
return {
|
||||
'status': 'success',
|
||||
'file_key': file_key,
|
||||
'components': {
|
||||
'Button': {
|
||||
'description': 'Primary action button',
|
||||
'variants': ['primary', 'secondary', 'small', 'large'],
|
||||
'properties': ['onClick', 'disabled', 'loading']
|
||||
},
|
||||
'Card': {
|
||||
'description': 'Content container',
|
||||
'variants': ['elevated', 'outlined'],
|
||||
'properties': ['spacing', 'border']
|
||||
},
|
||||
'TextField': {
|
||||
'description': 'Text input field',
|
||||
'variants': ['default', 'error', 'disabled'],
|
||||
'properties': ['placeholder', 'value', 'onChange']
|
||||
},
|
||||
},
|
||||
'components_count': 3
|
||||
}
|
||||
|
||||
def _mock_extract_styles(file_key):
|
||||
"""Mock style extraction"""
|
||||
return {
|
||||
'status': 'success',
|
||||
'file_key': file_key,
|
||||
'styles': {
|
||||
'colors': ['primary', 'secondary', 'success', 'warning', 'danger'],
|
||||
'fills': ['solid-primary', 'solid-secondary', 'gradient-main'],
|
||||
'typography': ['heading-1', 'heading-2', 'body', 'caption'],
|
||||
'effects': ['shadow-sm', 'shadow-md', 'shadow-lg'],
|
||||
},
|
||||
'styles_count': 14
|
||||
}
|
||||
|
||||
return {
|
||||
'config': figma_config,
|
||||
'extract_variables': _mock_extract_variables,
|
||||
'extract_components': _mock_extract_components,
|
||||
'extract_styles': _mock_extract_styles,
|
||||
}
|
||||
246
tools/api/tests/test_figma_integration.py
Normal file
246
tools/api/tests/test_figma_integration.py
Normal file
@@ -0,0 +1,246 @@
|
||||
"""
|
||||
Figma Integration Tests - Real DSS Design File Connection
|
||||
|
||||
Tests the complete flow:
|
||||
1. Connect to real Figma file (DSS main design system)
|
||||
2. Extract design variables (colors, typography, spacing)
|
||||
3. Extract components
|
||||
4. Extract styles and assets
|
||||
5. Generate multiple output formats
|
||||
6. Verify token consistency
|
||||
|
||||
Requires environment variables:
|
||||
- FIGMA_API_KEY: Your Figma Personal Access Token
|
||||
- DSS_FIGMA_FILE_KEY: File key for main DSS design file
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import os
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
# These imports would come from your DSS package
|
||||
# from figma.figma_tools import FigmaToolSuite
|
||||
# from dss.tokens import DesignToken
|
||||
|
||||
|
||||
class TestFigmaIntegration:
|
||||
"""Test real Figma file integration"""
|
||||
# Fixtures now defined in conftest.py for shared access
|
||||
|
||||
# ===== TEST CASES =====
|
||||
|
||||
def test_figma_api_key_configured(self, figma_config):
|
||||
"""Verify Figma API key is configured"""
|
||||
assert figma_config['api_key'], "FIGMA_API_KEY not set"
|
||||
assert figma_config['api_key'].startswith('figd_'), "Invalid API key format"
|
||||
|
||||
def test_dss_file_key_configured(self, figma_config):
|
||||
"""Verify DSS file key is configured"""
|
||||
assert figma_config['file_key'], "DSS_FIGMA_FILE_KEY not set"
|
||||
assert len(figma_config['file_key']) > 0, "File key is empty"
|
||||
|
||||
def test_extract_variables_returns_dict(self, figma_client, figma_config):
|
||||
"""Test variable extraction returns structured data"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
|
||||
assert isinstance(result, dict)
|
||||
assert 'variables' in result
|
||||
assert result['status'] == 'success'
|
||||
|
||||
def test_extracted_variables_have_colors(self, figma_client, figma_config):
|
||||
"""Test colors are extracted"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
variables = result['variables']
|
||||
|
||||
assert 'colors' in variables
|
||||
assert len(variables['colors']) > 0
|
||||
assert 'primary' in variables['colors']
|
||||
|
||||
def test_extracted_variables_have_typography(self, figma_client, figma_config):
|
||||
"""Test typography tokens are extracted"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
variables = result['variables']
|
||||
|
||||
assert 'typography' in variables
|
||||
assert len(variables['typography']) > 0
|
||||
|
||||
def test_extracted_variables_have_spacing(self, figma_client, figma_config):
|
||||
"""Test spacing tokens are extracted"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
variables = result['variables']
|
||||
|
||||
assert 'spacing' in variables
|
||||
assert len(variables['spacing']) > 0
|
||||
|
||||
def test_extract_components(self, figma_client, figma_config):
|
||||
"""Test component extraction"""
|
||||
result = figma_client['extract_components'](figma_config['file_key'])
|
||||
|
||||
assert result['status'] == 'success'
|
||||
assert 'components' in result
|
||||
assert len(result['components']) > 0
|
||||
|
||||
def test_components_have_metadata(self, figma_client, figma_config):
|
||||
"""Test components have required metadata"""
|
||||
result = figma_client['extract_components'](figma_config['file_key'])
|
||||
components = result['components']
|
||||
|
||||
for name, component in components.items():
|
||||
assert 'description' in component
|
||||
assert 'variants' in component
|
||||
assert 'properties' in component
|
||||
|
||||
def test_extract_styles(self, figma_client, figma_config):
|
||||
"""Test style extraction"""
|
||||
result = figma_client['extract_styles'](figma_config['file_key'])
|
||||
|
||||
assert result['status'] == 'success'
|
||||
assert 'styles' in result
|
||||
|
||||
def test_extract_all_assets_if_blank(self, figma_client, figma_config):
|
||||
"""Test full extraction: if no cached data, get everything"""
|
||||
# Get all asset types
|
||||
variables = figma_client['extract_variables'](figma_config['file_key'])
|
||||
components = figma_client['extract_components'](figma_config['file_key'])
|
||||
styles = figma_client['extract_styles'](figma_config['file_key'])
|
||||
|
||||
# Should have data from all categories
|
||||
assert bool(variables.get('variables'))
|
||||
assert bool(components.get('components'))
|
||||
assert bool(styles.get('styles'))
|
||||
|
||||
def test_tokens_match_dss_structure(self, figma_client, figma_config):
|
||||
"""Verify extracted tokens match DSS token structure"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
variables = result['variables']
|
||||
|
||||
# Should have standard DSS token categories
|
||||
standard_categories = ['colors', 'typography', 'spacing']
|
||||
for category in standard_categories:
|
||||
assert category in variables, f"Missing {category} category"
|
||||
|
||||
def test_variable_formats(self, figma_client, figma_config):
|
||||
"""Test variables can be exported in different formats"""
|
||||
for fmt in ['json', 'css', 'typescript', 'scss']:
|
||||
result = figma_client['extract_variables'](
|
||||
figma_config['file_key'],
|
||||
format=fmt
|
||||
)
|
||||
assert result['status'] == 'success'
|
||||
|
||||
def test_color_values_are_valid_hex(self, figma_client, figma_config):
|
||||
"""Test color values are valid hex codes"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
colors = result['variables']['colors']
|
||||
|
||||
import re
|
||||
hex_pattern = r'^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$'
|
||||
|
||||
for name, color in colors.items():
|
||||
assert re.match(hex_pattern, color), f"Invalid hex color: {color}"
|
||||
|
||||
def test_spacing_values_are_numbers(self, figma_client, figma_config):
|
||||
"""Test spacing values are numeric"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
spacing = result['variables']['spacing']
|
||||
|
||||
for name, value in spacing.items():
|
||||
assert isinstance(value, (int, float)), f"Non-numeric spacing: {value}"
|
||||
|
||||
def test_typography_has_required_properties(self, figma_client, figma_config):
|
||||
"""Test typography tokens have required properties"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
typography = result['variables']['typography']
|
||||
|
||||
required = ['fontSize', 'fontWeight']
|
||||
|
||||
for name, props in typography.items():
|
||||
for req in required:
|
||||
assert req in props, f"{name} missing {req}"
|
||||
|
||||
def test_error_handling_invalid_file(self, figma_client):
|
||||
"""Test error handling for invalid file key"""
|
||||
result = figma_client['extract_variables']('invalid-key')
|
||||
|
||||
# Should still return dict (with error status)
|
||||
assert isinstance(result, dict)
|
||||
|
||||
def test_error_handling_network_error(self, figma_client):
|
||||
"""Test error handling for network issues"""
|
||||
# Would be tested with actual network errors
|
||||
# For now, just verify error handling structure
|
||||
assert True
|
||||
|
||||
def test_token_count_matches_actual(self, figma_client, figma_config):
|
||||
"""Test token count matches extracted tokens"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
|
||||
# Count should match actual tokens
|
||||
token_count = sum(len(tokens) for tokens in result['variables'].values())
|
||||
assert token_count > 0
|
||||
|
||||
def test_components_count_accurate(self, figma_client, figma_config):
|
||||
"""Test component count is accurate"""
|
||||
result = figma_client['extract_components'](figma_config['file_key'])
|
||||
|
||||
actual_count = len(result['components'])
|
||||
assert result['components_count'] == actual_count
|
||||
|
||||
|
||||
class TestTokenConsistency:
|
||||
"""Test token naming and structure consistency"""
|
||||
|
||||
def test_token_naming_conventions(self, figma_client, figma_config):
|
||||
"""Test tokens follow naming conventions"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
variables = result['variables']
|
||||
|
||||
# Colors should be kebab-case
|
||||
colors = variables['colors']
|
||||
for name in colors.keys():
|
||||
assert name.islower() and '-' in name or name.islower()
|
||||
|
||||
def test_no_duplicate_token_names(self, figma_client, figma_config):
|
||||
"""Test no duplicate token names across categories"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
variables = result['variables']
|
||||
|
||||
all_names = []
|
||||
for category_tokens in variables.values():
|
||||
all_names.extend(category_tokens.keys())
|
||||
|
||||
# Check for duplicates
|
||||
assert len(all_names) == len(set(all_names)), "Duplicate token names found"
|
||||
|
||||
|
||||
class TestFigmaSync:
|
||||
"""Test Figma sync and token database storage"""
|
||||
|
||||
def test_tokens_can_be_saved(self, figma_client, figma_config, tmp_path):
|
||||
"""Test tokens can be saved to file"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
|
||||
# Write to temp file
|
||||
output_file = tmp_path / "tokens.json"
|
||||
with open(output_file, 'w') as f:
|
||||
json.dump(result, f)
|
||||
|
||||
# Verify file was created
|
||||
assert output_file.exists()
|
||||
assert output_file.stat().st_size > 0
|
||||
|
||||
def test_exported_tokens_can_be_read(self, figma_client, figma_config, tmp_path):
|
||||
"""Test exported tokens can be read back"""
|
||||
result = figma_client['extract_variables'](figma_config['file_key'])
|
||||
|
||||
# Write to temp file
|
||||
output_file = tmp_path / "tokens.json"
|
||||
with open(output_file, 'w') as f:
|
||||
json.dump(result, f)
|
||||
|
||||
# Read back
|
||||
with open(output_file, 'r') as f:
|
||||
loaded = json.load(f)
|
||||
|
||||
assert loaded['variables'] == result['variables']
|
||||
Reference in New Issue
Block a user