""" Theme & Styling Tests Tests for theme consistency and styling including: - Color system - Typography - Component styles - Dark/light theme switching - Style consistency """ class ThemeStylingTests: """Test suite for theme and styling.""" name = "Theme & Styling" icon = "🎨" description = "Tests theme consistency, styling, and dark/light mode switching" def __init__(self): self.tests = { 'color_system': self.test_color_system, 'typography_system': self.test_typography_system, 'theme_switching': self.test_theme_switching, 'button_styles': self.test_button_styles, 'input_styles': self.test_input_styles, 'table_styles': self.test_table_styles, 'scrollbar_styles': self.test_scrollbar_styles, 'global_stylesheet': self.test_global_stylesheet, 'component_consistency': self.test_component_consistency, 'accessibility_colors': self.test_accessibility_colors, } def test_color_system(self) -> dict: """Test color system completeness.""" try: from core.eu_styles import ( EU_DARK_COLORS, EU_LIGHT_COLORS, get_color, get_all_colors, EUTheme ) issues = [] # Check required color categories required_colors = [ 'bg_primary', 'bg_secondary', 'bg_tertiary', 'text_primary', 'text_secondary', 'text_muted', 'accent_orange', 'accent_teal', 'accent_blue', 'border_default', 'border_hover', 'border_focus', 'status_success', 'status_warning', 'status_error' ] # Check dark theme dark_missing = [c for c in required_colors if c not in EU_DARK_COLORS] if dark_missing: issues.append(f"Dark theme missing: {dark_missing}") # Check light theme light_missing = [c for c in required_colors if c not in EU_LIGHT_COLORS] if light_missing: issues.append(f"Light theme missing: {light_missing}") # Check color getter if not callable(get_color): issues.append("get_color is not callable") if not callable(get_all_colors): issues.append("get_all_colors is not callable") # Test get_color EUTheme.set_theme('dark') color = get_color('bg_primary') if not color or not isinstance(color, str): issues.append("get_color returned invalid value") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'error' } return { 'passed': True, 'message': f"Color system complete ({len(EU_DARK_COLORS)} dark, {len(EU_LIGHT_COLORS)} light colors)", 'severity': 'info' } except Exception as e: return { 'passed': False, 'message': f"Error checking color system: {e}", 'severity': 'error' } def test_typography_system(self) -> dict: """Test typography system.""" try: from core.eu_styles import EU_TYPOGRAPHY issues = [] # Check required typography fields required = [ 'font_family', 'font_mono', 'size_xs', 'size_sm', 'size_base', 'size_md', 'size_lg', 'size_xl', 'weight_normal', 'weight_medium', 'weight_semibold', 'weight_bold', 'line_tight', 'line_normal', 'line_relaxed' ] missing = [r for r in required if r not in EU_TYPOGRAPHY] if missing: issues.append(f"Missing typography fields: {missing}") # Check font family if 'font_family' in EU_TYPOGRAPHY: if 'Segoe UI' not in EU_TYPOGRAPHY['font_family']: issues.append("Primary font family may not be optimal for Windows") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'warning' } return { 'passed': True, 'message': f"Typography system complete ({len(EU_TYPOGRAPHY)} definitions)", 'severity': 'info' } except Exception as e: return { 'passed': False, 'message': f"Error checking typography: {e}", 'severity': 'error' } def test_theme_switching(self) -> dict: """Test theme switching functionality.""" try: from core.eu_styles import EUTheme, get_all_colors, EU_DARK_COLORS, EU_LIGHT_COLORS issues = [] # Check EUTheme class if not hasattr(EUTheme, 'set_theme'): issues.append("EUTheme.set_theme missing") if not hasattr(EUTheme, 'get_theme'): issues.append("EUTheme.get_theme missing") if not hasattr(EUTheme, 'is_dark'): issues.append("EUTheme.is_dark missing") # Test theme switching original = EUTheme.get_theme() EUTheme.set_theme('dark') if EUTheme.get_theme() != 'dark': issues.append("Failed to set dark theme") dark_colors = get_all_colors() if dark_colors != EU_DARK_COLORS: issues.append("Dark theme colors not returned correctly") EUTheme.set_theme('light') if EUTheme.get_theme() != 'light': issues.append("Failed to set light theme") light_colors = get_all_colors() if light_colors != EU_LIGHT_COLORS: issues.append("Light theme colors not returned correctly") # Restore EUTheme.set_theme(original) if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'error' } return { 'passed': True, 'message': "Theme switching working correctly", 'severity': 'info' } except Exception as e: return { 'passed': False, 'message': f"Error testing theme switching: {e}", 'severity': 'error' } def test_button_styles(self) -> dict: """Test button style generation.""" try: from core.eu_styles import get_button_style issues = [] # Check function exists if not callable(get_button_style): return { 'passed': False, 'message': "get_button_style not callable", 'severity': 'error' } # Test variants variants = ['primary', 'secondary', 'ghost', 'danger', 'success'] sizes = ['sm', 'md', 'lg'] for variant in variants: try: style = get_button_style(variant, 'md') if not style or not isinstance(style, str): issues.append(f"get_button_style('{variant}') returned invalid style") except Exception as e: issues.append(f"get_button_style('{variant}') failed: {e}") # Test sizes for size in sizes: try: style = get_button_style('primary', size) if not style or not isinstance(style, str): issues.append(f"get_button_style('primary', '{size}') returned invalid style") except Exception as e: issues.append(f"get_button_style('primary', '{size}') failed: {e}") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'warning' } return { 'passed': True, 'message': f"Button styles working for {len(variants)} variants and {len(sizes)} sizes", 'severity': 'info' } except Exception as e: return { 'passed': False, 'message': f"Error checking button styles: {e}", 'severity': 'error' } def test_input_styles(self) -> dict: """Test input field styles.""" try: from core.eu_styles import get_input_style, get_combo_style issues = [] # Check input style if not callable(get_input_style): issues.append("get_input_style not callable") else: style = get_input_style() if not style or 'QLineEdit' not in style: issues.append("get_input_style missing QLineEdit styling") # Check combo style if not callable(get_combo_style): issues.append("get_combo_style not callable") else: style = get_combo_style() if not style or 'QComboBox' not in style: issues.append("get_combo_style missing QComboBox styling") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'warning' } return { 'passed': True, 'message': "Input styles present", 'severity': 'info' } except Exception as e: return { 'passed': False, 'message': f"Error checking input styles: {e}", 'severity': 'error' } def test_table_styles(self) -> dict: """Test table widget styles.""" try: from core.eu_styles import get_table_style issues = [] # Check function if not callable(get_table_style): return { 'passed': False, 'message': "get_table_style not callable", 'severity': 'warning' } style = get_table_style() # Check for required elements required = ['QTableWidget', 'QHeaderView'] missing = [r for r in required if r not in style] if missing: issues.append(f"Table style missing: {missing}") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'warning' } return { 'passed': True, 'message': "Table styles present", 'severity': 'info' } except Exception as e: return { 'passed': False, 'message': f"Error checking table styles: {e}", 'severity': 'error' } def test_scrollbar_styles(self) -> dict: """Test scrollbar styles.""" try: from core.eu_styles import get_scrollbar_style issues = [] # Check function if not callable(get_scrollbar_style): return { 'passed': False, 'message': "get_scrollbar_style not callable", 'severity': 'warning' } style = get_scrollbar_style() # Check for vertical and horizontal if 'vertical' not in style: issues.append("Missing vertical scrollbar styling") if 'horizontal' not in style: issues.append("Missing horizontal scrollbar styling") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'warning' } return { 'passed': True, 'message': "Scrollbar styles present", 'severity': 'info' } except Exception as e: return { 'passed': False, 'message': f"Error checking scrollbar styles: {e}", 'severity': 'error' } def test_global_stylesheet(self) -> dict: """Test global stylesheet generation.""" try: from core.eu_styles import get_global_stylesheet issues = [] # Check function if not callable(get_global_stylesheet): return { 'passed': False, 'message': "get_global_stylesheet not callable", 'severity': 'error' } style = get_global_stylesheet() if not style or not isinstance(style, str): return { 'passed': False, 'message': "get_global_stylesheet returned invalid value", 'severity': 'error' } # Check for base styling required = ['QWidget', 'QMainWindow'] missing = [r for r in required if r not in style] if missing: issues.append(f"Global stylesheet missing: {missing}") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'warning' } return { 'passed': True, 'message': f"Global stylesheet generated ({len(style)} characters)", 'severity': 'info' } except Exception as e: return { 'passed': False, 'message': f"Error checking global stylesheet: {e}", 'severity': 'error' } def test_component_consistency(self) -> dict: """Test component style consistency.""" try: from core.eu_styles import ( get_button_style, get_input_style, get_table_style, get_card_style, get_panel_style, get_color ) issues = [] recommendations = [] # Get styles button = get_button_style('primary') input_style = get_input_style() table = get_table_style() # Check for common color usage bg_color = get_color('bg_secondary') # All components should use theme colors if bg_color not in input_style: recommendations.append("Input style may not use theme background color") # Check border radius consistency radii = [] for style in [button, input_style]: import re matches = re.findall(r'border-radius:\s*(\d+)px', style) radii.extend([int(m) for m in matches]) if radii and max(radii) - min(radii) > 8: recommendations.append("Consider more consistent border radius values") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'warning' } return { 'passed': True, 'message': "Component consistency checked", 'severity': 'info', 'recommendation': recommendations[0] if recommendations else None } except Exception as e: return { 'passed': False, 'message': f"Error checking consistency: {e}", 'severity': 'error' } def test_accessibility_colors(self) -> dict: """Test color accessibility.""" try: from core.eu_styles import EU_DARK_COLORS, EU_LIGHT_COLORS issues = [] recommendations = [] # Check contrast ratios (basic check) # WCAG AA requires 4.5:1 for normal text, 3:1 for large text dark_bg = EU_DARK_COLORS.get('bg_primary', '#0d1117') dark_text = EU_DARK_COLORS.get('text_primary', '#f0f6fc') light_bg = EU_LIGHT_COLORS.get('bg_primary', '#ffffff') light_text = EU_LIGHT_COLORS.get('text_primary', '#24292f') # Simple check - ensure colors are different if dark_bg == dark_text: issues.append("Dark theme background and text are identical") if light_bg == light_text: issues.append("Light theme background and text are identical") # Check for focus indicators if 'border_focus' not in EU_DARK_COLORS: recommendations.append("Consider adding border_focus for accessibility") # Check for disabled states if 'text_disabled' not in EU_DARK_COLORS: recommendations.append("Consider adding text_disabled state") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'error' } return { 'passed': True, 'message': "Color accessibility checked", 'severity': 'info', 'recommendation': recommendations[0] if recommendations else None } except Exception as e: return { 'passed': False, 'message': f"Error checking accessibility colors: {e}", 'severity': 'error' }