""" User Flow Tests Tests for user workflows including: - First-time user experience - Plugin installation workflow - Widget creation and management - Settings changes - Hotkey functionality - Window positioning and persistence """ class UserFlowTests: """Test suite for user flows.""" name = "User Flows" icon = "👤" description = "Tests first-time experience, plugin installation, widget creation, and settings workflows" def __init__(self): self.tests = { 'first_time_experience': self.test_first_time_experience, 'plugin_install_workflow': self.test_plugin_install_workflow, 'widget_creation_workflow': self.test_widget_creation_workflow, 'settings_change_workflow': self.test_settings_change_workflow, 'hotkey_functionality': self.test_hotkey_functionality, 'window_positioning_persistence': self.test_window_positioning_persistence, 'overlay_toggle_flow': self.test_overlay_toggle_flow, 'tab_navigation_flow': self.test_tab_navigation_flow, 'plugin_drawer_flow': self.test_plugin_drawer_flow, 'error_recovery': self.test_error_recovery, } def test_first_time_experience(self) -> dict: """Test first-time user experience.""" try: from core.ui.dashboard_view import DashboardView from core.overlay_window import OverlayWindow issues = [] recommendations = [] # Check dashboard welcome widget if not hasattr(DashboardView, '_add_builtin_widgets'): issues.append("Dashboard missing _add_builtin_widgets") if not hasattr(DashboardView, '_create_widget_frame'): issues.append("Dashboard missing _create_widget_frame") # Check for welcome content import inspect if hasattr(DashboardView, '_add_builtin_widgets'): source = inspect.getsource(DashboardView._add_builtin_widgets) welcome_terms = ['welcome', 'install', 'plugin', 'store', 'get started'] has_welcome = any(term in source.lower() for term in welcome_terms) if not has_welcome: recommendations.append("Consider adding more prominent welcome guidance") # Check for placeholder when no plugins if not hasattr(OverlayWindow, '_create_placeholder'): recommendations.append("Consider adding placeholder for empty plugin state") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'warning', 'recommendation': recommendations[0] if recommendations else None } return { 'passed': True, 'message': "First-time experience features present", 'severity': 'info', 'recommendation': recommendations[0] if recommendations else None } except Exception as e: return { 'passed': False, 'message': f"Error checking first-time experience: {e}", 'severity': 'error' } def test_plugin_install_workflow(self) -> dict: """Test plugin installation workflow.""" try: from core.overlay_window import OverlayWindow from core.plugin_store import PluginStoreUI issues = [] recommendations = [] # Check settings dialog opens if not hasattr(OverlayWindow, '_open_settings'): issues.append("_open_settings method missing") # Check plugin store tab exists if not hasattr(OverlayWindow, '_create_plugin_store_tab'): issues.append("_create_plugin_store_tab missing") # Check store UI if not hasattr(PluginStoreUI, 'install_plugin'): issues.append("PluginStoreUI.install_plugin missing") # Check for feedback after install import inspect if hasattr(PluginStoreUI, 'install_plugin'): source = inspect.getsource(PluginStoreUI.install_plugin) if 'progress' not in source.lower(): recommendations.append("Consider showing progress during installation") if 'success' not in source.lower() and 'complete' not in source.lower(): recommendations.append("Consider showing success message after install") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'error', 'recommendation': recommendations[0] if recommendations else None } return { 'passed': True, 'message': "Plugin install workflow present", 'severity': 'info', 'recommendation': recommendations[0] if recommendations else None } except Exception as e: return { 'passed': False, 'message': f"Error checking install workflow: {e}", 'severity': 'error' } def test_widget_creation_workflow(self) -> dict: """Test widget creation workflow.""" try: from core.overlay_window import OverlayWindow from core.widget_registry import get_widget_registry issues = [] recommendations = [] # Check widgets tab exists if not hasattr(OverlayWindow, '_create_widgets_tab'): issues.append("_create_widgets_tab missing") if not hasattr(OverlayWindow, '_refresh_widgets_tab'): issues.append("_refresh_widgets_tab missing") if not hasattr(OverlayWindow, '_create_widget_button'): issues.append("_create_widget_button missing") if not hasattr(OverlayWindow, '_add_registered_widget'): issues.append("_add_registered_widget missing") # Check widget registry try: registry = get_widget_registry() if not hasattr(registry, 'get_all_widgets'): issues.append("WidgetRegistry.get_all_widgets missing") except Exception as e: issues.append(f"WidgetRegistry not accessible: {e}") # Check for widget management if not hasattr(OverlayWindow, '_active_widgets'): recommendations.append("Consider tracking _active_widgets to prevent GC") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'error', 'recommendation': recommendations[0] if recommendations else None } return { 'passed': True, 'message': "Widget creation workflow present", 'severity': 'info', 'recommendation': recommendations[0] if recommendations else None } except Exception as e: return { 'passed': False, 'message': f"Error checking widget workflow: {e}", 'severity': 'error' } def test_settings_change_workflow(self) -> dict: """Test settings change workflow.""" try: from core.overlay_window import OverlayWindow issues = [] recommendations = [] # Check settings dialog if not hasattr(OverlayWindow, '_open_settings'): issues.append("_open_settings missing") if not hasattr(OverlayWindow, '_save_settings'): issues.append("_save_settings missing") # Check for apply feedback import inspect if hasattr(OverlayWindow, '_save_settings'): source = inspect.getsource(OverlayWindow._save_settings) if 'reload' not in source.lower(): recommendations.append("Consider reloading plugins after settings save") # Check settings persistence if not hasattr(OverlayWindow, '_refresh_ui'): recommendations.append("Consider adding _refresh_ui for immediate theme changes") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'error', 'recommendation': recommendations[0] if recommendations else None } return { 'passed': True, 'message': "Settings change workflow present", 'severity': 'info', 'recommendation': recommendations[0] if recommendations else None } except Exception as e: return { 'passed': False, 'message': f"Error checking settings workflow: {e}", 'severity': 'error' } def test_hotkey_functionality(self) -> dict: """Test hotkey functionality.""" try: from core.overlay_window import OverlayWindow from core.hotkey_manager import HotkeyManager issues = [] recommendations = [] # Check shortcut setup if not hasattr(OverlayWindow, '_setup_shortcuts'): issues.append("_setup_shortcuts missing") # Check toggle methods if not hasattr(OverlayWindow, 'toggle_overlay'): issues.append("toggle_overlay missing") if not hasattr(OverlayWindow, 'show_overlay'): issues.append("show_overlay missing") if not hasattr(OverlayWindow, 'hide_overlay'): issues.append("hide_overlay missing") # Check hotkey manager try: hm = HotkeyManager() if not hasattr(hm, 'register_hotkey'): recommendations.append("Consider implementing global hotkeys with register_hotkey") except Exception as e: recommendations.append(f"HotkeyManager not fully functional: {e}") # Check for expected shortcuts import inspect if hasattr(OverlayWindow, '_setup_shortcuts'): source = inspect.getsource(OverlayWindow._setup_shortcuts) expected = ['esc', 'ctrl+t', 'ctrl+1'] missing = [e for e in expected if e not in source.lower()] if missing: recommendations.append(f"Consider adding shortcuts: {missing}") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'error', 'recommendation': recommendations[0] if recommendations else None } return { 'passed': True, 'message': "Hotkey functionality present", 'severity': 'info', 'recommendation': recommendations[0] if recommendations else None } except Exception as e: return { 'passed': False, 'message': f"Error checking hotkey functionality: {e}", 'severity': 'error' } def test_window_positioning_persistence(self) -> dict: """Test window positioning and persistence.""" try: from core.overlay_window import OverlayWindow from core.activity_bar import ActivityBar issues = [] recommendations = [] # Check center window if not hasattr(OverlayWindow, '_center_window'): issues.append("_center_window missing") # Check activity bar position persistence if not hasattr(ActivityBar, '_save_position'): recommendations.append("Consider implementing activity bar position persistence") # Check config persistence if not hasattr(ActivityBar, '_save_config'): issues.append("ActivityBar._save_config missing") if not hasattr(ActivityBar, '_load_config'): issues.append("ActivityBar._load_config missing") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'warning', 'recommendation': recommendations[0] if recommendations else None } return { 'passed': True, 'message': "Window positioning features present", 'severity': 'info', 'recommendation': recommendations[0] if recommendations else None } except Exception as e: return { 'passed': False, 'message': f"Error checking window positioning: {e}", 'severity': 'error' } def test_overlay_toggle_flow(self) -> dict: """Test overlay toggle flow.""" try: from core.overlay_window import OverlayWindow issues = [] # Check visibility tracking if not hasattr(OverlayWindow, 'is_visible'): issues.append("is_visible state not tracked") # Check visibility signal if not hasattr(OverlayWindow, 'visibility_changed'): issues.append("visibility_changed signal missing") # Check methods required = ['show_overlay', 'hide_overlay', 'toggle_overlay'] for method in required: if not hasattr(OverlayWindow, method): issues.append(f"{method} missing") # Check tray integration if not hasattr(OverlayWindow, '_tray_activated'): issues.append("_tray_activated missing for tray toggle") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'error' } return { 'passed': True, 'message': "Overlay toggle flow complete", 'severity': 'info' } except Exception as e: return { 'passed': False, 'message': f"Error checking toggle flow: {e}", 'severity': 'error' } def test_tab_navigation_flow(self) -> dict: """Test tab navigation flow.""" try: from core.overlay_window import OverlayWindow issues = [] # Check tab switching if not hasattr(OverlayWindow, '_switch_tab'): issues.append("_switch_tab missing") if not hasattr(OverlayWindow, 'tab_buttons'): issues.append("tab_buttons not tracked") if not hasattr(OverlayWindow, 'tab_stack'): issues.append("tab_stack not defined") # Check expected tabs expected_tabs = ['plugins', 'widgets', 'settings'] import inspect if hasattr(OverlayWindow, '_switch_tab'): source = inspect.getsource(OverlayWindow._switch_tab) for tab in expected_tabs: if f"'{tab}'" not in source and f'"{tab}"' not in source: issues.append(f"Tab '{tab}' may not be handled in _switch_tab") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'error' } return { 'passed': True, 'message': f"Tab navigation flow present ({len(expected_tabs)} tabs)", 'severity': 'info' } except Exception as e: return { 'passed': False, 'message': f"Error checking tab navigation: {e}", 'severity': 'error' } def test_plugin_drawer_flow(self) -> dict: """Test plugin drawer workflow.""" try: from core.activity_bar import ActivityBar issues = [] recommendations = [] # Check drawer toggle if not hasattr(ActivityBar, '_toggle_drawer'): issues.append("_toggle_drawer missing") if not hasattr(ActivityBar, 'drawer_open'): issues.append("drawer_open state not tracked") # Check drawer signals if not hasattr(ActivityBar, 'drawer_toggled'): issues.append("drawer_toggled signal missing") # Check drawer content if not hasattr(ActivityBar, '_refresh_drawer'): issues.append("_refresh_drawer missing") if not hasattr(ActivityBar, '_create_drawer_item'): issues.append("_create_drawer_item missing") # Check click handling if not hasattr(ActivityBar, '_on_drawer_item_clicked'): issues.append("_on_drawer_item_clicked missing") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'error' } return { 'passed': True, 'message': "Plugin drawer flow complete", 'severity': 'info' } except Exception as e: return { 'passed': False, 'message': f"Error checking drawer flow: {e}", 'severity': 'error' } def test_error_recovery(self) -> dict: """Test error recovery flows.""" try: from core.overlay_window import OverlayWindow from core.activity_bar import ActivityBar issues = [] recommendations = [] # Check error handling in plugin loading import inspect if hasattr(OverlayWindow, '_load_plugins'): source = inspect.getsource(OverlayWindow._load_plugins) if 'try' not in source or 'except' not in source: recommendations.append("Consider adding error handling to _load_plugins") # Check error handling in widget creation if hasattr(OverlayWindow, '_add_registered_widget'): source = inspect.getsource(OverlayWindow._add_registered_widget) if 'try' not in source or 'except' not in source: recommendations.append("Consider adding error handling to _add_registered_widget") # Check for fallback UI if not hasattr(OverlayWindow, '_create_placeholder'): recommendations.append("Consider adding placeholder UI for error states") if issues: return { 'passed': False, 'message': "; ".join(issues), 'severity': 'error', 'recommendation': recommendations[0] if recommendations else None } return { 'passed': True, 'message': "Error recovery features checked", 'severity': 'info', 'recommendation': recommendations[0] if recommendations else None } except Exception as e: return { 'passed': False, 'message': f"Error checking error recovery: {e}", 'severity': 'error' }