531 lines
18 KiB
Python
531 lines
18 KiB
Python
"""
|
|
Widget System Tests
|
|
|
|
Tests for the widget registry and floating widgets including:
|
|
- Widget registration
|
|
- Widget creation
|
|
- Positioning and resizing
|
|
- Opacity control
|
|
- Widget lifecycle
|
|
"""
|
|
|
|
|
|
class WidgetSystemTests:
|
|
"""Test suite for widget system."""
|
|
|
|
name = "Widget System"
|
|
icon = "🎨"
|
|
description = "Tests widget creation, positioning, resizing, opacity, and lifecycle"
|
|
|
|
def __init__(self):
|
|
self.tests = {
|
|
'registry_initialization': self.test_registry_initialization,
|
|
'widget_registration': self.test_widget_registration,
|
|
'widget_creation': self.test_widget_creation,
|
|
'widget_lookup': self.test_widget_lookup,
|
|
'widget_by_plugin': self.test_widget_by_plugin,
|
|
'widget_unregister': self.test_widget_unregister,
|
|
'widget_clear': self.test_widget_clear,
|
|
'widget_info_structure': self.test_widget_info_structure,
|
|
'overlay_widget_creation': self.test_overlay_widget_creation,
|
|
'widget_positioning': self.test_widget_positioning,
|
|
}
|
|
|
|
def test_registry_initialization(self) -> dict:
|
|
"""Test widget registry initialization."""
|
|
try:
|
|
from core.widget_registry import WidgetRegistry, get_widget_registry
|
|
|
|
# Test singleton pattern
|
|
reg1 = get_widget_registry()
|
|
reg2 = get_widget_registry()
|
|
|
|
if reg1 is not reg2:
|
|
return {
|
|
'passed': False,
|
|
'message': "WidgetRegistry singleton not working correctly",
|
|
'severity': 'error'
|
|
}
|
|
|
|
# Test that registry has _widgets dict
|
|
if not hasattr(reg1, '_widgets'):
|
|
return {
|
|
'passed': False,
|
|
'message': "Registry missing _widgets dictionary",
|
|
'severity': 'error'
|
|
}
|
|
|
|
return {
|
|
'passed': True,
|
|
'message': "WidgetRegistry singleton working correctly",
|
|
'severity': 'info'
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
'passed': False,
|
|
'message': f"Error initializing registry: {e}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
def test_widget_registration(self) -> dict:
|
|
"""Test widget registration."""
|
|
try:
|
|
from core.widget_registry import get_widget_registry
|
|
from PyQt6.QtWidgets import QLabel
|
|
|
|
registry = get_widget_registry()
|
|
|
|
# Test registration
|
|
test_widget_id = "_test_widget_123"
|
|
|
|
def create_test_widget():
|
|
return QLabel("Test")
|
|
|
|
registry.register_widget(
|
|
widget_id=test_widget_id,
|
|
name="Test Widget",
|
|
description="A test widget",
|
|
icon="🧪",
|
|
creator=create_test_widget,
|
|
plugin_id="test_plugin"
|
|
)
|
|
|
|
# Verify registration
|
|
if test_widget_id not in registry._widgets:
|
|
return {
|
|
'passed': False,
|
|
'message': "Widget not found after registration",
|
|
'severity': 'error'
|
|
}
|
|
|
|
# Cleanup
|
|
registry.unregister_widget(test_widget_id)
|
|
|
|
return {
|
|
'passed': True,
|
|
'message': "Widget registration working",
|
|
'severity': 'info'
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
'passed': False,
|
|
'message': f"Error registering widget: {e}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
def test_widget_creation(self) -> dict:
|
|
"""Test widget creation via registry."""
|
|
try:
|
|
from core.widget_registry import get_widget_registry
|
|
from PyQt6.QtWidgets import QLabel, QWidget
|
|
|
|
registry = get_widget_registry()
|
|
test_widget_id = "_test_creation_123"
|
|
|
|
def create_widget():
|
|
return QLabel("Test Widget")
|
|
|
|
registry.register_widget(
|
|
widget_id=test_widget_id,
|
|
name="Creation Test",
|
|
description="Testing widget creation",
|
|
icon="🔨",
|
|
creator=create_widget,
|
|
plugin_id="test"
|
|
)
|
|
|
|
# Test creation
|
|
widget = registry.create_widget(test_widget_id)
|
|
|
|
if widget is None:
|
|
registry.unregister_widget(test_widget_id)
|
|
return {
|
|
'passed': False,
|
|
'message': "create_widget returned None",
|
|
'severity': 'error'
|
|
}
|
|
|
|
if not isinstance(widget, QWidget):
|
|
registry.unregister_widget(test_widget_id)
|
|
return {
|
|
'passed': False,
|
|
'message': "Created widget is not a QWidget",
|
|
'severity': 'error'
|
|
}
|
|
|
|
# Cleanup
|
|
registry.unregister_widget(test_widget_id)
|
|
|
|
return {
|
|
'passed': True,
|
|
'message': "Widget creation working correctly",
|
|
'severity': 'info'
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
'passed': False,
|
|
'message': f"Error creating widget: {e}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
def test_widget_lookup(self) -> dict:
|
|
"""Test widget lookup by ID."""
|
|
try:
|
|
from core.widget_registry import get_widget_registry
|
|
from PyQt6.QtWidgets import QLabel
|
|
|
|
registry = get_widget_registry()
|
|
test_id = "_test_lookup_123"
|
|
|
|
registry.register_widget(
|
|
widget_id=test_id,
|
|
name="Lookup Test",
|
|
description="Testing lookup",
|
|
icon="🔍",
|
|
creator=lambda: QLabel("Test"),
|
|
plugin_id="test"
|
|
)
|
|
|
|
# Test get_widget
|
|
info = registry.get_widget(test_id)
|
|
|
|
if info is None:
|
|
registry.unregister_widget(test_id)
|
|
return {
|
|
'passed': False,
|
|
'message': "get_widget returned None for existing widget",
|
|
'severity': 'error'
|
|
}
|
|
|
|
if info.id != test_id:
|
|
registry.unregister_widget(test_id)
|
|
return {
|
|
'passed': False,
|
|
'message': f"Widget ID mismatch: expected {test_id}, got {info.id}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
# Test non-existent widget
|
|
non_existent = registry.get_widget("_non_existent_widget_999")
|
|
if non_existent is not None:
|
|
registry.unregister_widget(test_id)
|
|
return {
|
|
'passed': False,
|
|
'message': "get_widget should return None for non-existent widgets",
|
|
'severity': 'warning'
|
|
}
|
|
|
|
registry.unregister_widget(test_id)
|
|
|
|
return {
|
|
'passed': True,
|
|
'message': "Widget lookup working correctly",
|
|
'severity': 'info'
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
'passed': False,
|
|
'message': f"Error looking up widget: {e}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
def test_widget_by_plugin(self) -> dict:
|
|
"""Test getting widgets by plugin ID."""
|
|
try:
|
|
from core.widget_registry import get_widget_registry
|
|
from PyQt6.QtWidgets import QLabel
|
|
|
|
registry = get_widget_registry()
|
|
test_plugin_id = "_test_plugin_456"
|
|
|
|
# Register multiple widgets for same plugin
|
|
for i in range(3):
|
|
registry.register_widget(
|
|
widget_id=f"_test_wp_{i}",
|
|
name=f"Widget {i}",
|
|
description=f"Test widget {i}",
|
|
icon="📦",
|
|
creator=lambda: QLabel("Test"),
|
|
plugin_id=test_plugin_id
|
|
)
|
|
|
|
# Get widgets by plugin
|
|
widgets = registry.get_widgets_by_plugin(test_plugin_id)
|
|
|
|
if len(widgets) != 3:
|
|
# Cleanup
|
|
for i in range(3):
|
|
registry.unregister_widget(f"_test_wp_{i}")
|
|
return {
|
|
'passed': False,
|
|
'message': f"Expected 3 widgets, got {len(widgets)}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
# Cleanup
|
|
for i in range(3):
|
|
registry.unregister_widget(f"_test_wp_{i}")
|
|
|
|
return {
|
|
'passed': True,
|
|
'message': f"get_widgets_by_plugin returned {len(widgets)} widgets",
|
|
'severity': 'info'
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
'passed': False,
|
|
'message': f"Error getting widgets by plugin: {e}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
def test_widget_unregister(self) -> dict:
|
|
"""Test widget unregistration."""
|
|
try:
|
|
from core.widget_registry import get_widget_registry
|
|
from PyQt6.QtWidgets import QLabel
|
|
|
|
registry = get_widget_registry()
|
|
test_id = "_test_unregister_123"
|
|
|
|
registry.register_widget(
|
|
widget_id=test_id,
|
|
name="Unregister Test",
|
|
description="Testing unregistration",
|
|
icon="🗑️",
|
|
creator=lambda: QLabel("Test"),
|
|
plugin_id="test"
|
|
)
|
|
|
|
# Verify registration
|
|
if test_id not in registry._widgets:
|
|
return {
|
|
'passed': False,
|
|
'message': "Widget not registered",
|
|
'severity': 'error'
|
|
}
|
|
|
|
# Unregister
|
|
registry.unregister_widget(test_id)
|
|
|
|
# Verify unregistration
|
|
if test_id in registry._widgets:
|
|
return {
|
|
'passed': False,
|
|
'message': "Widget still in registry after unregister",
|
|
'severity': 'error'
|
|
}
|
|
|
|
# Verify get_widget returns None
|
|
info = registry.get_widget(test_id)
|
|
if info is not None:
|
|
return {
|
|
'passed': False,
|
|
'message': "get_widget returns widget after unregister",
|
|
'severity': 'error'
|
|
}
|
|
|
|
return {
|
|
'passed': True,
|
|
'message': "Widget unregistration working",
|
|
'severity': 'info'
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
'passed': False,
|
|
'message': f"Error unregistering widget: {e}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
def test_widget_clear(self) -> dict:
|
|
"""Test clearing all widgets."""
|
|
try:
|
|
from core.widget_registry import get_widget_registry
|
|
from PyQt6.QtWidgets import QLabel
|
|
|
|
registry = get_widget_registry()
|
|
|
|
# Register some widgets
|
|
for i in range(5):
|
|
registry.register_widget(
|
|
widget_id=f"_test_clear_{i}",
|
|
name=f"Clear Test {i}",
|
|
description="Testing clear",
|
|
icon="🧹",
|
|
creator=lambda: QLabel("Test"),
|
|
plugin_id="test"
|
|
)
|
|
|
|
# Clear
|
|
registry.clear()
|
|
|
|
# Verify
|
|
if len(registry._widgets) != 0:
|
|
return {
|
|
'passed': False,
|
|
'message': f"Registry not empty after clear: {len(registry._widgets)} widgets",
|
|
'severity': 'error'
|
|
}
|
|
|
|
all_widgets = registry.get_all_widgets()
|
|
if len(all_widgets) != 0:
|
|
return {
|
|
'passed': False,
|
|
'message': f"get_all_widgets not empty after clear",
|
|
'severity': 'error'
|
|
}
|
|
|
|
return {
|
|
'passed': True,
|
|
'message': "Registry clear working",
|
|
'severity': 'info'
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
'passed': False,
|
|
'message': f"Error clearing widgets: {e}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
def test_widget_info_structure(self) -> dict:
|
|
"""Test WidgetInfo dataclass structure."""
|
|
try:
|
|
from core.widget_registry import WidgetInfo
|
|
|
|
# Check required fields
|
|
required_fields = ['id', 'name', 'description', 'icon', 'creator', 'plugin_id']
|
|
|
|
# Create a test WidgetInfo
|
|
def dummy_creator():
|
|
return None
|
|
|
|
info = WidgetInfo(
|
|
id="test",
|
|
name="Test",
|
|
description="Test description",
|
|
icon="🧪",
|
|
creator=dummy_creator,
|
|
plugin_id="test_plugin"
|
|
)
|
|
|
|
missing = []
|
|
for field in required_fields:
|
|
if not hasattr(info, field):
|
|
missing.append(field)
|
|
|
|
if missing:
|
|
return {
|
|
'passed': False,
|
|
'message': f"WidgetInfo missing fields: {missing}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
return {
|
|
'passed': True,
|
|
'message': f"WidgetInfo structure correct with {len(required_fields)} fields",
|
|
'severity': 'info'
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
'passed': False,
|
|
'message': f"Error checking WidgetInfo: {e}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
def test_overlay_widget_creation(self) -> dict:
|
|
"""Test widget creation from overlay."""
|
|
try:
|
|
from core.overlay_window import OverlayWindow
|
|
|
|
issues = []
|
|
recommendations = []
|
|
|
|
# Check _add_registered_widget method
|
|
if not hasattr(OverlayWindow, '_add_registered_widget'):
|
|
issues.append("_add_registered_widget method missing")
|
|
|
|
# Check widget storage
|
|
if not hasattr(OverlayWindow, '_active_widgets'):
|
|
recommendations.append("Consider adding _active_widgets list to prevent GC")
|
|
|
|
# Check widgets tab
|
|
if not hasattr(OverlayWindow, '_create_widgets_tab'):
|
|
issues.append("_create_widgets_tab method missing")
|
|
|
|
if not hasattr(OverlayWindow, '_refresh_widgets_tab'):
|
|
issues.append("_refresh_widgets_tab method missing")
|
|
|
|
if issues:
|
|
return {
|
|
'passed': False,
|
|
'message': "; ".join(issues),
|
|
'severity': 'error',
|
|
'recommendation': recommendations[0] if recommendations else None
|
|
}
|
|
|
|
return {
|
|
'passed': True,
|
|
'message': "Overlay widget creation present",
|
|
'severity': 'info'
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
'passed': False,
|
|
'message': f"Error checking overlay widget creation: {e}",
|
|
'severity': 'error'
|
|
}
|
|
|
|
def test_widget_positioning(self) -> dict:
|
|
"""Test widget positioning features."""
|
|
try:
|
|
from core.overlay_window import OverlayWindow
|
|
from core.widget_system import WidgetConfig
|
|
|
|
issues = []
|
|
recommendations = []
|
|
|
|
# Check widget config
|
|
if 'WidgetConfig' in dir():
|
|
config = WidgetConfig()
|
|
if not hasattr(config, 'x') or not hasattr(config, 'y'):
|
|
issues.append("WidgetConfig missing position fields")
|
|
else:
|
|
recommendations.append("Consider adding WidgetConfig for widget settings")
|
|
|
|
# Check if widgets can be positioned
|
|
# In _add_registered_widget, widgets are positioned at screen center
|
|
import inspect
|
|
if hasattr(OverlayWindow, '_add_registered_widget'):
|
|
source = inspect.getsource(OverlayWindow._add_registered_widget)
|
|
if 'move(' not in source:
|
|
recommendations.append("Consider allowing custom widget positions")
|
|
|
|
if issues:
|
|
return {
|
|
'passed': False,
|
|
'message': "; ".join(issues),
|
|
'severity': 'warning'
|
|
}
|
|
|
|
return {
|
|
'passed': True,
|
|
'message': "Widget positioning features present",
|
|
'severity': 'info',
|
|
'recommendation': recommendations[0] if recommendations else None
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
'passed': False,
|
|
'message': f"Error checking widget positioning: {e}",
|
|
'severity': 'error'
|
|
}
|