Files
dss/.dss/test_category_phase2.py
Digital Production Factory 276ed71f31 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
2025-12-09 18:45:48 -03:00

623 lines
26 KiB
Python

#!/usr/bin/env python3
"""
DSS Admin UI - Phase 2: Category-Based Testing
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Framework: Pytest-Playwright (Python-based)
Purpose: Validate component categories with specific interaction patterns
Coverage: Component interactions, data flows, category-specific behaviors
Test Categories:
- Tools: Input → Execute → Result validation
- Metrics: Chart rendering, data validation
- Layout: Navigation, sidebar, panels
- Admin: CRUD operations, permissions
Generated: 2025-12-08
Author: Gemini 3 Pro Expert Analysis
Status: Ready for Implementation
"""
import pytest
import json
import time
from pathlib import Path
from playwright.sync_api import sync_playwright, expect
import re
# ────────────────────────────────────────────────────────────────────────────
# Tools Category Tests
# ────────────────────────────────────────────────────────────────────────────
class TestToolsCategory:
"""Test all Tools components with input/output validation"""
TOOLS_COMPONENTS = [
'ds-metrics-panel',
'ds-console-viewer',
'ds-token-inspector',
'ds-figma-status',
'ds-activity-log',
'ds-visual-diff',
'ds-accessibility-report',
'ds-screenshot-gallery',
'ds-network-monitor',
'ds-test-results',
'ds-system-log',
]
@pytest.fixture(autouse=True)
def setup(self, page):
"""Setup for each test"""
self.page = page
self.page.goto("http://localhost:5173/")
self.page.wait_for_load_state("networkidle")
def test_metrics_panel_data_display(self):
"""Test: Metrics panel displays data correctly"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-metrics-panel', container);
// Wait for data to load
await new Promise(r => setTimeout(r, 1000));
return {
success: true,
hasContent: element.innerHTML.length > 0,
hasMetrics: element.querySelector('[data-metric]') !== null,
rendered: element.offsetHeight > 0
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Failed to load metrics panel: {result.get('error')}"
assert result['rendered'], "Metrics panel has zero height (not rendered)"
def test_console_viewer_functionality(self):
"""Test: Console viewer captures and displays logs"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-console-viewer', container);
// Simulate some console activity
console.log('Test log entry');
console.warn('Test warning');
console.error('Test error');
// Wait for capture
await new Promise(r => setTimeout(r, 500));
return {
success: true,
isElement: element.tagName !== undefined,
hasContent: element.innerHTML.length > 0
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Console viewer failed: {result.get('error')}"
assert result['isElement'], "Console viewer not properly created as element"
def test_token_inspector_rendering(self):
"""Test: Token inspector displays design tokens"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-token-inspector', container);
await new Promise(r => setTimeout(r, 1000));
return {
success: true,
rendered: element.offsetHeight > 0,
hasTokens: element.querySelector('[class*="token"]') !== null,
isEmpty: element.innerHTML.trim().length === 0
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Token inspector failed: {result.get('error')}"
assert not result['isEmpty'], "Token inspector rendered but has no content"
# ────────────────────────────────────────────────────────────────────────────
# Metrics Category Tests
# ────────────────────────────────────────────────────────────────────────────
class TestMetricsCategory:
"""Test Metrics components with data rendering validation"""
METRICS_COMPONENTS = [
'ds-frontpage',
'ds-metric-card',
'ds-metrics-dashboard',
]
@pytest.fixture(autouse=True)
def setup(self, page):
"""Setup for each test"""
self.page = page
self.page.goto("http://localhost:5173/")
self.page.wait_for_load_state("networkidle")
def test_metrics_dashboard_renders(self):
"""Test: Metrics dashboard renders with grid layout"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-metrics-dashboard', container);
await new Promise(r => setTimeout(r, 1000));
return {
success: true,
rendered: element.offsetHeight > 0,
hasGrid: element.classList.toString().includes('grid') || element.style.display !== 'none',
childCount: element.children.length,
isVisible: window.getComputedStyle(element).display !== 'none'
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Dashboard failed: {result.get('error')}"
assert result['rendered'], "Dashboard not rendered"
assert result['isVisible'], "Dashboard not visible"
def test_metric_card_data_display(self):
"""Test: Metric cards display numeric data"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
// Add sample data
const element = await hydrateComponent('ds-metric-card', container);
element.setAttribute('label', 'Test Metric');
element.setAttribute('value', '42');
element.setAttribute('unit', '%');
await new Promise(r => setTimeout(r, 500));
return {
success: true,
rendered: element.offsetHeight > 0,
hasLabel: element.textContent.includes('Test') || element.innerHTML.includes('Test'),
hasValue: element.textContent.includes('42') || element.innerHTML.includes('42')
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Metric card failed: {result.get('error')}"
def test_frontpage_initialization(self):
"""Test: Frontpage component initializes without errors"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-frontpage', container);
await new Promise(r => setTimeout(r, 1000));
return {
success: true,
isElement: element.tagName === 'DS-FRONTPAGE',
rendered: element.offsetHeight > 0
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Frontpage failed: {result.get('error')}"
# ────────────────────────────────────────────────────────────────────────────
# Layout Category Tests
# ────────────────────────────────────────────────────────────────────────────
class TestLayoutCategory:
"""Test Layout components with navigation and panel interaction"""
LAYOUT_COMPONENTS = [
'ds-shell',
'ds-panel',
'ds-activity-bar',
'ds-ai-chat-sidebar',
'ds-project-selector',
]
@pytest.fixture(autouse=True)
def setup(self, page):
"""Setup for each test"""
self.page = page
self.page.goto("http://localhost:5173/")
self.page.wait_for_load_state("networkidle")
def test_shell_component_core_layout(self):
"""Test: Shell component provides core layout structure"""
result = self.page.evaluate("""
() => {
const shell = document.querySelector('ds-shell');
return {
exists: shell !== null,
rendered: shell?.offsetHeight > 0,
hasChildren: shell?.children?.length > 0,
isMainElement: shell?.parentElement === document.body || shell?.parentElement?.tagName === 'BODY'
};
}
""")
assert result['exists'], "Shell component not found in DOM"
assert result['rendered'], "Shell component not rendered"
assert result['hasChildren'], "Shell has no child elements"
def test_activity_bar_navigation(self):
"""Test: Activity bar responds to navigation actions"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-activity-bar', container);
await new Promise(r => setTimeout(r, 500));
return {
success: true,
rendered: element.offsetHeight > 0,
hasItems: element.querySelectorAll('[role="button"], button').length > 0
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Activity bar failed: {result.get('error')}"
def test_project_selector_functionality(self):
"""Test: Project selector displays and responds to selection"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-project-selector', container);
await new Promise(r => setTimeout(r, 500));
return {
success: true,
rendered: element.offsetHeight > 0,
isSelectLike: element.querySelector('select') !== null ||
element.querySelector('[role="combobox"]') !== null ||
element.className.includes('select') ||
element.className.includes('selector')
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Project selector failed: {result.get('error')}"
# ────────────────────────────────────────────────────────────────────────────
# Admin Category Tests
# ────────────────────────────────────────────────────────────────────────────
class TestAdminCategory:
"""Test Admin components with CRUD and permission operations"""
ADMIN_COMPONENTS = [
'ds-admin-settings',
'ds-project-list',
'ds-user-settings',
]
@pytest.fixture(autouse=True)
def setup(self, page):
"""Setup for each test"""
self.page = page
self.page.goto("http://localhost:5173/")
self.page.wait_for_load_state("networkidle")
def test_admin_settings_form(self):
"""Test: Admin settings displays configuration form"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-admin-settings', container);
await new Promise(r => setTimeout(r, 500));
return {
success: true,
rendered: element.offsetHeight > 0,
hasForm: element.querySelector('form') !== null ||
element.querySelector('[role="form"]') !== null,
hasInputs: element.querySelectorAll('input, select, textarea').length > 0
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Admin settings failed: {result.get('error')}"
def test_project_list_crud_interface(self):
"""Test: Project list displays items and CRUD controls"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-project-list', container);
await new Promise(r => setTimeout(r, 1000));
return {
success: true,
rendered: element.offsetHeight > 0,
hasList: element.querySelector('[role="list"], ul, .list') !== null,
hasActions: element.querySelectorAll('[role="button"], button').length > 0
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Project list failed: {result.get('error')}"
def test_user_settings_profile(self):
"""Test: User settings displays profile management"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-user-settings', container);
await new Promise(r => setTimeout(r, 500));
return {
success: true,
rendered: element.offsetHeight > 0,
hasProfileElements: element.querySelectorAll('[class*="profile"], [data-profile]').length > 0 ||
element.querySelector('form') !== null
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"User settings failed: {result.get('error')}"
# ────────────────────────────────────────────────────────────────────────────
# UI Elements Tests
# ────────────────────────────────────────────────────────────────────────────
class TestUIElementsCategory:
"""Test basic UI element components"""
UI_COMPONENTS = [
'ds-button',
'ds-input',
'ds-card',
'ds-badge',
'ds-toast',
]
@pytest.fixture(autouse=True)
def setup(self, page):
"""Setup for each test"""
self.page = page
self.page.goto("http://localhost:5173/")
self.page.wait_for_load_state("networkidle")
def test_button_component_interactive(self):
"""Test: Button component is interactive"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-button', container);
element.textContent = 'Click Me';
return {
success: true,
rendered: element.offsetHeight > 0,
isButton: element.tagName === 'DS-BUTTON',
hasText: element.textContent.includes('Click')
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Button failed: {result.get('error')}"
def test_input_component_functional(self):
"""Test: Input component accepts and stores values"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-input', container);
// Try to set value
if (element.value !== undefined) {
element.value = 'test input';
} else if (element.querySelector('input')) {
element.querySelector('input').value = 'test input';
}
return {
success: true,
rendered: element.offsetHeight > 0,
isInput: element.tagName === 'DS-INPUT'
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Input failed: {result.get('error')}"
def test_card_component_layout(self):
"""Test: Card component provides container structure"""
result = self.page.evaluate("""
async () => {
try {
const { hydrateComponent } = await import('../js/config/component-registry.js');
const container = document.createElement('div');
document.body.appendChild(container);
const element = await hydrateComponent('ds-card', container);
element.innerHTML = '<h3>Card Title</h3><p>Card content</p>';
return {
success: true,
rendered: element.offsetHeight > 0,
isCard: element.tagName === 'DS-CARD',
hasContent: element.innerHTML.length > 0
};
} catch (err) {
return { success: false, error: err.message };
}
}
""")
assert result['success'], f"Card failed: {result.get('error')}"
# ────────────────────────────────────────────────────────────────────────────
# Test Configuration & Fixtures
# ────────────────────────────────────────────────────────────────────────────
@pytest.fixture(scope="session")
def browser_context():
"""Start Playwright browser session"""
with sync_playwright() as p:
browser = p.chromium.launch()
context = browser.new_context()
yield context
context.close()
browser.close()
@pytest.fixture
def page(browser_context):
"""Create new page for each test"""
page = browser_context.new_page()
yield page
page.close()
@pytest.fixture(scope="session", autouse=True)
def print_test_header():
"""Print header with test information"""
print("\n" + "="*80)
print("DSS Admin UI - Phase 2: Category-Based Testing")
print("="*80)
print("Framework: Pytest-Playwright")
print("Tests: Component-specific interaction validation")
print("="*80)
if __name__ == "__main__":
print("""
╔═══════════════════════════════════════════════════════════════════════════╗
║ ║
║ DSS Admin UI - Phase 2 Category Testing ║
║ pytest-playwright Component Interaction Validation ║
║ ║
║ To run all category tests: ║
║ $ pytest .dss/test_category_phase2.py -v ║
║ ║
║ To run specific category: ║
║ $ pytest .dss/test_category_phase2.py::TestToolsCategory -v ║
║ $ pytest .dss/test_category_phase2.py::TestMetricsCategory -v ║
║ $ pytest .dss/test_category_phase2.py::TestLayoutCategory -v ║
║ $ pytest .dss/test_category_phase2.py::TestAdminCategory -v ║
║ ║
║ To run with parallel execution: ║
║ $ pytest .dss/test_category_phase2.py -n auto -v ║
║ ║
║ To run with detailed reporting: ║
║ $ pytest .dss/test_category_phase2.py -v --tb=short ║
║ ║
╚═══════════════════════════════════════════════════════════════════════════╝
""")