import sys from unittest.mock import patch, MagicMock import pytest from pathlib import Path # Mock tkinter and other GUI/macOS specific imports sys.modules['tkinter'] = MagicMock() sys.modules['tkinter.ttk'] = MagicMock() sys.modules['tkinter.scrolledtext'] = MagicMock() sys.modules['tkinter.messagebox'] = MagicMock() sys.modules['Foundation'] = MagicMock() sys.modules['AppKit'] = MagicMock() sys.modules['PIL'] = MagicMock() sys.modules['PIL.Image'] = MagicMock() sys.modules['PIL.ImageTk'] = MagicMock() import install from install import ProleInstaller @pytest.fixture def mock_installer(tmp_path): test_cfg_src = Path(__file__).parent / "fixtures" / "test-prole.cfg" cfg_path = tmp_path / "prole.cfg" cfg_path.write_text(test_cfg_src.read_text()) with patch('install.tk.Tk'), \ patch('install.tk.Frame'), \ patch('install.tk.Canvas'), \ patch('install.tk.Label'), \ patch('install.tk.Button'), \ patch('install.tk.StringVar'), \ patch('install.tk.BooleanVar'), \ patch('install.tk.IntVar'), \ patch('install.ttk.Style'), \ patch('install.Path.exists', return_value=True): root = MagicMock() root.winfo_screenwidth.return_value = 1920 root.winfo_screenheight.return_value = 1080 app = ProleInstaller(root, config_path=str(cfg_path)) app.bg_canvas.winfo_width.return_value = 1000 return app def test_service_layer_navigation_pause(mock_installer): # Go to init_cluster mock_installer.show_page('init_cluster') # Mock cluster ready with patch.object(mock_installer, '_cluster_ready_for_navigation', return_value=True): # Click Next - should NOT go to init_password yet, but show overlay with patch.object(mock_installer, '_run_service_layer_overlay') as mock_overlay: mock_installer.on_next() mock_overlay.assert_called_once() assert mock_installer.pages[mock_installer.page_index][0] == 'init_cluster' def test_service_layer_overlay_behavior(mock_installer): mock_installer.show_page('init_cluster') # Trigger overlay with patch.object(mock_installer, '_cluster_ready_for_navigation', return_value=True), \ patch.object(mock_installer, '_create_console_output'): mock_installer.on_next() assert mock_installer._showing_service_overlay is True assert mock_installer._common_services_success is False # Next button should be disabled via update_footer mock_installer.next_button = MagicMock() mock_installer.prev_button = MagicMock() mock_installer.update_footer() mock_installer.next_button.configure.assert_any_call(state='disabled') # Simulate success mock_installer._common_services_success = True mock_installer.next_button.reset_mock() mock_installer.update_footer() mock_installer.next_button.configure.assert_any_call(state='normal') # Now Next should proceed with patch.object(mock_installer, '_cluster_ready_for_navigation', return_value=True): mock_installer.on_next() assert mock_installer.pages[mock_installer.page_index][0] == 'init_password' def test_service_layer_prev_behavior(mock_installer): mock_installer.show_page('init_cluster') with patch.object(mock_installer, '_cluster_ready_for_navigation', return_value=True), \ patch.object(mock_installer, '_create_console_output'): mock_installer.on_next() assert mock_installer._showing_service_overlay is True # Prev from overlay should go back to init_cluster and reset overlay flag mock_installer.on_prev() assert mock_installer._showing_service_overlay is False assert mock_installer.pages[mock_installer.page_index][0] == 'init_cluster'