EU-Utility/plugins/ui_test_suite/test_suite_plugin.py

581 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
UI Test Suite Plugin - Main Plugin Class
Tests and validates all EU-Utility UI components and user flows.
"""
from PyQt6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
QTabWidget, QScrollArea, QFrame, QTextEdit, QProgressBar,
QCheckBox, QGridLayout, QSplitter, QListWidget, QListWidgetItem,
QGroupBox, QMessageBox
)
from PyQt6.QtCore import Qt, QTimer, pyqtSignal
from PyQt6.QtGui import QColor
from core.base_plugin import BasePlugin
from core.eu_styles import (
get_color, get_all_colors, get_button_style, get_global_stylesheet,
EU_TYPOGRAPHY, EU_SIZES
)
from core.widget_registry import get_widget_registry
class UITestSuitePlugin(BasePlugin):
"""
UI/UX Validation Specialist Test Suite
Tests covered:
- Overlay window (tabs, navigation, plugin display)
- Activity Bar (layouts, dragging, drawer, pinned plugins)
- Widget system (creation, positioning, resizing, opacity)
- Settings UI (all settings categories)
- Plugin Store UI (install, uninstall, dependencies)
- Theme/styling consistency
"""
name = "UI Test Suite"
description = "Comprehensive UI/UX validation and testing framework"
version = "1.0.0"
author = "UI/UX Validation Specialist"
icon = "test_tube"
def __init__(self):
super().__init__()
self.test_results = []
self.current_test = None
def get_ui(self) -> QWidget:
"""Return the test suite UI."""
return TestSuiteUI(self)
def register_widgets(self):
"""Register test widgets to widget registry."""
registry = get_widget_registry()
registry.register_widget(
widget_id="ui_test_overlay_validator",
name="Overlay Validator",
description="Real-time overlay window validation widget",
icon="🔍",
creator=lambda: OverlayValidatorWidget(),
plugin_id="ui_test_suite"
)
registry.register_widget(
widget_id="ui_test_theme_checker",
name="Theme Consistency Checker",
description="Checks theme consistency across components",
icon="🎨",
creator=lambda: ThemeCheckerWidget(),
plugin_id="ui_test_suite"
)
registry.register_widget(
widget_id="ui_test_accessibility_auditor",
name="Accessibility Auditor",
description="Validates accessibility features",
icon="",
creator=lambda: AccessibilityAuditorWidget(),
plugin_id="ui_test_suite"
)
def on_enable(self):
"""Called when plugin is enabled."""
self.register_widgets()
print("[UI Test Suite] Enabled - Test widgets registered")
def on_disable(self):
"""Called when plugin is disabled."""
registry = get_widget_registry()
registry.unregister_widget("ui_test_overlay_validator")
registry.unregister_widget("ui_test_theme_checker")
registry.unregister_widget("ui_test_accessibility_auditor")
class TestSuiteUI(QWidget):
"""Main test suite UI."""
test_completed = pyqtSignal(str, bool, str) # test_name, passed, message
def __init__(self, plugin, parent=None):
super().__init__(parent)
self.plugin = plugin
self.c = get_all_colors()
self._setup_ui()
self._setup_test_modules()
def _setup_ui(self):
"""Setup the test suite UI."""
layout = QVBoxLayout(self)
layout.setSpacing(16)
layout.setContentsMargins(20, 20, 20, 20)
# Header
header = self._create_header()
layout.addWidget(header)
# Main content splitter
splitter = QSplitter(Qt.Orientation.Horizontal)
# Left: Test modules list
left_panel = self._create_test_modules_panel()
splitter.addWidget(left_panel)
# Right: Test execution and results
right_panel = self._create_results_panel()
splitter.addWidget(right_panel)
splitter.setSizes([300, 600])
layout.addWidget(splitter, 1)
# Apply styles
self.setStyleSheet(get_global_stylesheet())
def _create_header(self) -> QFrame:
"""Create header section."""
header = QFrame()
header.setStyleSheet(f"""
QFrame {{
background-color: {self.c['bg_secondary']};
border-radius: {EU_SIZES['radius_lg']};
border: 1px solid {self.c['border_default']};
}}
""")
layout = QHBoxLayout(header)
layout.setSpacing(16)
layout.setContentsMargins(16, 12, 16, 12)
# Title
title = QLabel("🧪 UI/UX Test Suite")
title.setStyleSheet(f"""
color: {self.c['text_primary']};
font-size: {EU_TYPOGRAPHY['size_2xl']};
font-weight: {EU_TYPOGRAPHY['weight_bold']};
""")
layout.addWidget(title)
# Stats
self.stats_label = QLabel("Ready to run tests")
self.stats_label.setStyleSheet(f"color: {self.c['text_secondary']};")
layout.addWidget(self.stats_label)
layout.addStretch()
# Global actions
run_all_btn = QPushButton("▶ Run All Tests")
run_all_btn.setStyleSheet(get_button_style('primary'))
run_all_btn.clicked.connect(self._run_all_tests)
layout.addWidget(run_all_btn)
clear_btn = QPushButton("🗑 Clear Results")
clear_btn.setStyleSheet(get_button_style('ghost'))
clear_btn.clicked.connect(self._clear_results)
layout.addWidget(clear_btn)
return header
def _create_test_modules_panel(self) -> QWidget:
"""Create left panel with test modules."""
panel = QWidget()
layout = QVBoxLayout(panel)
layout.setSpacing(12)
layout.setContentsMargins(0, 0, 0, 0)
# Label
label = QLabel("Test Categories")
label.setStyleSheet(f"""
color: {self.c['text_muted']};
font-size: {EU_TYPOGRAPHY['size_xs']};
font-weight: {EU_TYPOGRAPHY['weight_bold']};
text-transform: uppercase;
""")
layout.addWidget(label)
# Test modules list
self.test_modules_list = QListWidget()
self.test_modules_list.setStyleSheet(f"""
QListWidget {{
background-color: {self.c['bg_secondary']};
border: 1px solid {self.c['border_default']};
border-radius: {EU_SIZES['radius_md']};
padding: 8px;
}}
QListWidget::item {{
padding: 10px;
border-radius: {EU_SIZES['radius_sm']};
margin: 2px 0;
}}
QListWidget::item:selected {{
background-color: {self.c['bg_selected']};
border-left: 3px solid {self.c['accent_orange']};
}}
QListWidget::item:hover {{
background-color: {self.c['bg_hover']};
}}
""")
self.test_modules_list.itemClicked.connect(self._on_module_selected)
layout.addWidget(self.test_modules_list)
return panel
def _create_results_panel(self) -> QWidget:
"""Create right panel with test execution and results."""
panel = QWidget()
layout = QVBoxLayout(panel)
layout.setSpacing(12)
layout.setContentsMargins(0, 0, 0, 0)
# Tab widget for different views
self.tabs = QTabWidget()
self.tabs.setStyleSheet(f"""
QTabBar::tab {{
padding: 10px 20px;
background-color: {self.c['bg_tertiary']};
color: {self.c['text_secondary']};
}}
QTabBar::tab:selected {{
background-color: {self.c['accent_orange']};
color: white;
}}
""")
# Console output
self.console = QTextEdit()
self.console.setReadOnly(True)
self.console.setStyleSheet(f"""
QTextEdit {{
background-color: {self.c['bg_primary']};
color: {self.c['text_primary']};
border: 1px solid {self.c['border_default']};
border-radius: {EU_SIZES['radius_md']};
font-family: {EU_TYPOGRAPHY['font_mono']};
font-size: 12px;
padding: 12px;
}}
""")
self.tabs.addTab(self.console, "📝 Console")
# Results table
self.results_list = QListWidget()
self.results_list.setStyleSheet(f"""
QListWidget {{
background-color: {self.c['bg_primary']};
border: 1px solid {self.c['border_default']};
border-radius: {EU_SIZES['radius_md']};
}}
QListWidget::item {{
padding: 12px;
border-bottom: 1px solid {self.c['border_default']};
}}
""")
self.tabs.addTab(self.results_list, "📊 Results")
# Issues/Bugs tab
self.issues_text = QTextEdit()
self.issues_text.setReadOnly(True)
self.issues_text.setStyleSheet(f"""
QTextEdit {{
background-color: {self.c['bg_primary']};
color: {self.c['text_primary']};
border: 1px solid {self.c['border_default']};
border-radius: {EU_SIZES['radius_md']};
padding: 12px;
}}
""")
self.tabs.addTab(self.issues_text, "🐛 Issues Found")
layout.addWidget(self.tabs)
# Current test progress
self.progress_bar = QProgressBar()
self.progress_bar.setStyleSheet(f"""
QProgressBar {{
background-color: {self.c['bg_tertiary']};
border-radius: 4px;
height: 8px;
}}
QProgressBar::chunk {{
background-color: {self.c['accent_orange']};
border-radius: 4px;
}}
""")
self.progress_bar.setVisible(False)
layout.addWidget(self.progress_bar)
return panel
def _setup_test_modules(self):
"""Setup available test modules."""
from .test_modules import (
OverlayWindowTests,
ActivityBarTests,
WidgetSystemTests,
SettingsUITests,
PluginStoreTests,
ThemeStylingTests,
UserFlowTests,
AccessibilityTests,
PerformanceTests
)
self.test_modules = {
'overlay': OverlayWindowTests(),
'activity_bar': ActivityBarTests(),
'widgets': WidgetSystemTests(),
'settings': SettingsUITests(),
'plugin_store': PluginStoreTests(),
'theme': ThemeStylingTests(),
'user_flows': UserFlowTests(),
'accessibility': AccessibilityTests(),
'performance': PerformanceTests(),
}
# Add to list
for key, module in self.test_modules.items():
item = QListWidgetItem(f"{module.icon} {module.name}")
item.setData(Qt.ItemDataRole.UserRole, key)
self.test_modules_list.addItem(item)
def _on_module_selected(self, item: QListWidgetItem):
"""Handle module selection."""
module_key = item.data(Qt.ItemDataRole.UserRole)
module = self.test_modules.get(module_key)
if module:
self._log(f"Selected test module: {module.name}")
self._log(f"Description: {module.description}")
self._log(f"Tests available: {len(module.tests)}")
def _run_all_tests(self):
"""Run all test modules."""
self._clear_results()
self._log("=" * 60)
self._log("STARTING FULL UI TEST SUITE")
self._log("=" * 60)
total_tests = sum(len(m.tests) for m in self.test_modules.values())
self.progress_bar.setMaximum(total_tests)
self.progress_bar.setValue(0)
self.progress_bar.setVisible(True)
current = 0
for key, module in self.test_modules.items():
self._log(f"\n📦 Running: {module.name}")
self._log("-" * 40)
for test_name, test_func in module.tests.items():
current += 1
self.progress_bar.setValue(current)
self._stats_label.setText(f"Running... {current}/{total_tests}")
try:
result = test_func()
self._add_result(module.name, test_name, result)
except Exception as e:
self._add_result(module.name, test_name, {
'passed': False,
'message': f"Exception: {str(e)}"
})
self.progress_bar.setVisible(False)
self._log("\n" + "=" * 60)
self._log("TEST SUITE COMPLETE")
self._log("=" * 60)
self._update_stats()
def _add_result(self, module_name: str, test_name: str, result: dict):
"""Add a test result."""
passed = result.get('passed', False)
message = result.get('message', '')
severity = result.get('severity', 'error' if not passed else 'info')
# Log to console
status = "✅ PASS" if passed else "❌ FAIL"
self._log(f"{status} | {module_name}.{test_name}: {message}")
# Add to results list
icon = "" if passed else "⚠️" if severity == 'warning' else ""
item_text = f"{icon} {module_name} {test_name}\n {message}"
item = QListWidgetItem(item_text)
if passed:
item.setForeground(QColor(self.c['accent_green']))
elif severity == 'warning':
item.setForeground(QColor(self.c['accent_gold']))
else:
item.setForeground(QColor(self.c['accent_red']))
self.results_list.addItem(item)
# Track issues
if not passed:
self.plugin.test_results.append({
'module': module_name,
'test': test_name,
'message': message,
'severity': severity,
'recommendation': result.get('recommendation', '')
})
# Add to issues tab
issue_text = f"""
🐛 Issue Found
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Module: {module_name}
Test: {test_name}
Severity: {severity.upper()}
Message: {message}
"""
if result.get('recommendation'):
issue_text += f"Recommendation: {result['recommendation']}\n"
self.issues_text.append(issue_text)
def _log(self, message: str):
"""Log message to console."""
self.console.append(message)
def _clear_results(self):
"""Clear all results."""
self.console.clear()
self.results_list.clear()
self.issues_text.clear()
self.plugin.test_results.clear()
self.stats_label.setText("Ready to run tests")
def _update_stats(self):
"""Update statistics display."""
total = len(self.plugin.test_results)
passed = sum(1 for r in self.plugin.test_results if r.get('passed'))
failed = total - passed
self.stats_label.setText(f"Results: {passed} passed, {failed} failed")
# Test Widget Classes
class OverlayValidatorWidget(QFrame):
"""Widget for real-time overlay validation."""
def __init__(self, parent=None):
super().__init__(parent)
self.c = get_all_colors()
self._setup_ui()
def _setup_ui(self):
layout = QVBoxLayout(self)
title = QLabel("🔍 Overlay Validator")
title.setStyleSheet(f"font-weight: bold; color: {self.c['accent_orange']};")
layout.addWidget(title)
# Validation checks
checks = [
"Window positioning",
"Theme consistency",
"Animation smoothness",
"Keyboard navigation",
"Z-order (always on top)",
]
for check in checks:
lbl = QLabel(f"{check}")
lbl.setStyleSheet(f"color: {self.c['text_secondary']};")
layout.addWidget(lbl)
validate_btn = QPushButton("Validate Now")
validate_btn.setStyleSheet(get_button_style('primary', 'sm'))
layout.addWidget(validate_btn)
class ThemeCheckerWidget(QFrame):
"""Widget for theme consistency checking."""
def __init__(self, parent=None):
super().__init__(parent)
self.c = get_all_colors()
self._setup_ui()
def _setup_ui(self):
layout = QVBoxLayout(self)
title = QLabel("🎨 Theme Checker")
title.setStyleSheet(f"font-weight: bold; color: {self.c['accent_teal']};")
layout.addWidget(title)
# Color samples
colors_frame = QFrame()
colors_layout = QGridLayout(colors_frame)
color_samples = [
('Primary', self.c['accent_orange']),
('Secondary', self.c['accent_teal']),
('Background', self.c['bg_secondary']),
('Text', self.c['text_primary']),
]
for i, (name, color) in enumerate(color_samples):
sample = QFrame()
sample.setFixedSize(30, 30)
sample.setStyleSheet(f"background-color: {color}; border-radius: 4px;")
colors_layout.addWidget(sample, 0, i)
lbl = QLabel(name)
lbl.setStyleSheet(f"color: {self.c['text_secondary']}; font-size: 10px;")
lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
colors_layout.addWidget(lbl, 1, i)
layout.addWidget(colors_frame)
check_btn = QPushButton("Check Consistency")
check_btn.setStyleSheet(get_button_style('secondary', 'sm'))
layout.addWidget(check_btn)
class AccessibilityAuditorWidget(QFrame):
"""Widget for accessibility validation."""
def __init__(self, parent=None):
super().__init__(parent)
self.c = get_all_colors()
self._setup_ui()
def _setup_ui(self):
layout = QVBoxLayout(self)
title = QLabel("♿ Accessibility Auditor")
title.setStyleSheet(f"font-weight: bold; color: {self.c['accent_blue']};")
layout.addWidget(title)
# Accessibility checks
checks_frame = QFrame()
checks_layout = QVBoxLayout(checks_frame)
checks = [
("Keyboard navigation", True),
("Screen reader labels", True),
("Color contrast", True),
("Focus indicators", False),
]
for check, status in checks:
row = QHBoxLayout()
lbl = QLabel(check)
lbl.setStyleSheet(f"color: {self.c['text_secondary']};")
row.addWidget(lbl)
status_lbl = QLabel("" if status else "")
status_lbl.setStyleSheet(
f"color: {self.c['accent_green'] if status else self.c['accent_red']};"
)
row.addWidget(status_lbl)
checks_layout.addLayout(row)
layout.addWidget(checks_frame)
audit_btn = QPushButton("Run Audit")
audit_btn.setStyleSheet(get_button_style('primary', 'sm'))
layout.addWidget(audit_btn)