# Unit tests for structural_navigator.py methods. # # Copyright 2025 Igalia, S.L. # Author: Joanmarie Diggs # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., Franklin Street, Fifth Floor, # Boston MA 02110-1301 USA. # pylint: disable=wrong-import-position # pylint: disable=import-outside-toplevel # pylint: disable=too-many-public-methods # pylint: disable=too-many-statements # pylint: disable=protected-access # pylint: disable=too-many-arguments # pylint: disable=too-many-positional-arguments # pylint: disable=too-many-locals # pylint: disable=too-many-lines """Unit tests for structural_navigator.py methods.""" from __future__ import annotations from typing import TYPE_CHECKING import gi import pytest gi.require_version("Atspi", "2.0") from gi.repository import Atspi if TYPE_CHECKING: from cthulhu_test_context import CthulhuTestContext @pytest.mark.unit class TestStructuralNavigator: """Test StructuralNavigator class methods.""" def _setup_dependencies(self, test_context): """Set up dependencies for structural_navigator module testing.""" additional_modules = [ "cthulhu.cmdnames", "cthulhu.command_manager", "cthulhu.dbus_service", "cthulhu.debug", "cthulhu.focus_manager", "cthulhu.guilabels", "cthulhu.input_event_manager", "cthulhu.keybindings", "cthulhu.messages", "cthulhu.object_properties", "cthulhu.cthulhu_gui_navlist", "cthulhu.cthulhu_i18n", "cthulhu.script_manager", "cthulhu.AXHypertext", "cthulhu.AXObject", "cthulhu.AXTable", "cthulhu.AXText", "cthulhu.AXUtilities", "cthulhu.input_event", "cthulhu.braille_presenter", "cthulhu.presentation_manager", ] essential_modules = test_context.setup_shared_dependencies(additional_modules) self._setup_mocks(test_context, essential_modules) return essential_modules def _setup_cycle_navigation_mode_mocks( self, test_context, nav, current_mode, supports_collection=True, ): """Set up common navigator mocks for cycle_mode testing scenarios.""" mock_get_mode = test_context.Mock(return_value=current_mode) test_context.patch_object(nav, "get_mode", new=mock_get_mode) mock_set_mode = test_context.Mock() test_context.patch_object(nav, "set_mode", new=mock_set_mode) test_context.patch_object(nav, "_is_active_script", return_value=True) mock_determine_root = None if supports_collection: mock_root = test_context.Mock() mock_determine_root = test_context.Mock(return_value=mock_root) test_context.patch_object(nav, "_determine_root_container", new=mock_determine_root) return mock_get_mode, mock_set_mode, mock_determine_root def _setup_mocks(self, test_context, essential_modules) -> None: """Set up common mocks for StructuralNavigator tests.""" input_event_handler_mock = test_context.Mock() essential_modules["cthulhu.input_event"].InputEventHandler = test_context.Mock( return_value=input_event_handler_mock, ) key_bindings_instance = test_context.Mock() essential_modules["cthulhu.keybindings"].KeyBindings = test_context.Mock( return_value=key_bindings_instance, ) controller_mock = test_context.Mock() controller_mock.register_decorated_module.return_value = None essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = controller_mock essential_modules["cthulhu.debug"].print_message = test_context.Mock() essential_modules["cthulhu.debug"].LEVEL_INFO = 800 focus_manager_instance = test_context.Mock() focus_manager_instance.get_locus_of_focus.return_value = None essential_modules["cthulhu.focus_manager"].get_manager.return_value = focus_manager_instance essential_modules["cthulhu.AXObject"].get_parent.return_value = None essential_modules["cthulhu.AXObject"].supports_collection.return_value = True essential_modules["cthulhu.AXObject"].find_ancestor.return_value = None essential_modules["cthulhu.AXObject"].get_attributes_dict.return_value = {} essential_modules["cthulhu.AXUtilities"].is_web_application.return_value = False essential_modules["cthulhu.AXUtilities"].is_heading.return_value = False essential_modules["cthulhu.AXUtilities"].is_live_region.return_value = False essential_modules["cthulhu.AXUtilities"].find_descendant.return_value = None script_manager_instance = test_context.Mock() script_manager_instance.get_active_script.return_value = None essential_modules["cthulhu.script_manager"].get_manager.return_value = script_manager_instance essential_modules["cthulhu.cthulhu_i18n"]._ = lambda msg: msg essential_modules["cthulhu.messages"].STRUCTURAL_NAVIGATION_KEYS_DOCUMENT = "Document mode" essential_modules["cthulhu.messages"].STRUCTURAL_NAVIGATION_KEYS_GUI = "GUI mode" essential_modules["cthulhu.messages"].STRUCTURAL_NAVIGATION_KEYS_OFF = "Off mode" essential_modules[ "cthulhu.messages" ].STRUCTURAL_NAVIGATION_NOT_SUPPORTED_FULL = "Not supported full" essential_modules[ "cthulhu.messages" ].STRUCTURAL_NAVIGATION_NOT_SUPPORTED_BRIEF = "Not supported brief" essential_modules["cthulhu.messages"].WRAPPING_TO_TOP = "Wrapping to top" essential_modules["cthulhu.messages"].WRAPPING_TO_BOTTOM = "Wrapping to bottom" essential_modules["cthulhu.cmdnames"].STRUCTURAL_NAVIGATION_MODE_CYCLE = "cycle_mode" essential_modules["cthulhu.cmdnames"].BLOCKQUOTE_PREV = "previous_blockquote" essential_modules["cthulhu.cmdnames"].BLOCKQUOTE_NEXT = "next_blockquote" essential_modules["cthulhu.cmdnames"].BLOCKQUOTE_LIST = "list_blockquotes" essential_modules["cthulhu.cmdnames"].BUTTON_PREV = "previous_button" essential_modules["cthulhu.cmdnames"].BUTTON_NEXT = "next_button" essential_modules["cthulhu.cmdnames"].BUTTON_LIST = "list_buttons" essential_modules["cthulhu.cmdnames"].CHECK_BOX_PREV = "previous_checkbox" essential_modules["cthulhu.cmdnames"].CHECK_BOX_NEXT = "next_checkbox" essential_modules["cthulhu.cmdnames"].CHECK_BOX_LIST = "list_checkboxes" essential_modules["cthulhu.cmdnames"].COMBO_BOX_PREV = "previous_combobox" essential_modules["cthulhu.cmdnames"].COMBO_BOX_NEXT = "next_combobox" essential_modules["cthulhu.cmdnames"].COMBO_BOX_LIST = "list_comboboxes" essential_modules["cthulhu.cmdnames"].ENTRY_PREV = "previous_entry" essential_modules["cthulhu.cmdnames"].ENTRY_NEXT = "next_entry" essential_modules["cthulhu.cmdnames"].ENTRY_LIST = "list_entries" essential_modules["cthulhu.cmdnames"].FORM_FIELD_PREV = "previous_form_field" essential_modules["cthulhu.cmdnames"].FORM_FIELD_NEXT = "next_form_field" essential_modules["cthulhu.cmdnames"].FORM_FIELD_LIST = "list_form_fields" essential_modules["cthulhu.cmdnames"].HEADING_PREV = "previous_heading" essential_modules["cthulhu.cmdnames"].HEADING_NEXT = "next_heading" essential_modules["cthulhu.cmdnames"].HEADING_LIST = "list_headings" essential_modules["cthulhu.cmdnames"].HEADING_AT_LEVEL_PREV = "previous_heading_level_%d" essential_modules["cthulhu.cmdnames"].HEADING_AT_LEVEL_NEXT = "next_heading_level_%d" essential_modules["cthulhu.cmdnames"].HEADING_AT_LEVEL_LIST = "list_headings_level_%d" essential_modules["cthulhu.cmdnames"].IFRAME_PREV = "previous_iframe" essential_modules["cthulhu.cmdnames"].IFRAME_NEXT = "next_iframe" essential_modules["cthulhu.cmdnames"].IFRAME_LIST = "list_iframes" essential_modules["cthulhu.cmdnames"].IMAGE_PREV = "previous_image" essential_modules["cthulhu.cmdnames"].IMAGE_NEXT = "next_image" essential_modules["cthulhu.cmdnames"].IMAGE_LIST = "list_images" essential_modules["cthulhu.cmdnames"].LANDMARK_PREV = "previous_landmark" essential_modules["cthulhu.cmdnames"].LANDMARK_NEXT = "next_landmark" essential_modules["cthulhu.cmdnames"].LANDMARK_LIST = "list_landmarks" essential_modules["cthulhu.cmdnames"].LIST_PREV = "previous_list" essential_modules["cthulhu.cmdnames"].LIST_NEXT = "next_list" essential_modules["cthulhu.cmdnames"].LIST_LIST = "list_lists" essential_modules["cthulhu.cmdnames"].LIST_ITEM_PREV = "previous_list_item" essential_modules["cthulhu.cmdnames"].LIST_ITEM_NEXT = "next_list_item" essential_modules["cthulhu.cmdnames"].LIST_ITEM_LIST = "list_list_items" essential_modules["cthulhu.cmdnames"].LIVE_REGION_PREV = "previous_live_region" essential_modules["cthulhu.cmdnames"].LIVE_REGION_NEXT = "next_live_region" essential_modules["cthulhu.cmdnames"].LIVE_REGION_LAST = "last_live_region" essential_modules["cthulhu.cmdnames"].PARAGRAPH_PREV = "previous_paragraph" essential_modules["cthulhu.cmdnames"].PARAGRAPH_NEXT = "next_paragraph" essential_modules["cthulhu.cmdnames"].PARAGRAPH_LIST = "list_paragraphs" essential_modules["cthulhu.cmdnames"].RADIO_BUTTON_PREV = "previous_radio_button" essential_modules["cthulhu.cmdnames"].RADIO_BUTTON_NEXT = "next_radio_button" essential_modules["cthulhu.cmdnames"].RADIO_BUTTON_LIST = "list_radio_buttons" essential_modules["cthulhu.cmdnames"].SEPARATOR_PREV = "previous_separator" essential_modules["cthulhu.cmdnames"].SEPARATOR_NEXT = "next_separator" essential_modules["cthulhu.cmdnames"].TABLE_PREV = "previous_table" essential_modules["cthulhu.cmdnames"].TABLE_NEXT = "next_table" essential_modules["cthulhu.cmdnames"].TABLE_LIST = "list_tables" essential_modules["cthulhu.cmdnames"].LINK_PREV = "previous_link" essential_modules["cthulhu.cmdnames"].LINK_NEXT = "next_link" essential_modules["cthulhu.cmdnames"].LINK_LIST = "list_links" essential_modules["cthulhu.cmdnames"].UNVISITED_LINK_PREV = "previous_unvisited_link" essential_modules["cthulhu.cmdnames"].UNVISITED_LINK_NEXT = "next_unvisited_link" essential_modules["cthulhu.cmdnames"].UNVISITED_LINK_LIST = "list_unvisited_links" essential_modules["cthulhu.cmdnames"].VISITED_LINK_PREV = "previous_visited_link" essential_modules["cthulhu.cmdnames"].VISITED_LINK_NEXT = "next_visited_link" essential_modules["cthulhu.cmdnames"].VISITED_LINK_LIST = "list_visited_links" essential_modules["cthulhu.cmdnames"].LARGE_OBJECT_PREV = "previous_large_object" essential_modules["cthulhu.cmdnames"].LARGE_OBJECT_NEXT = "next_large_object" essential_modules["cthulhu.cmdnames"].LARGE_OBJECT_LIST = "list_large_objects" essential_modules["cthulhu.cmdnames"].CLICKABLE_PREV = "previous_clickable" essential_modules["cthulhu.cmdnames"].CLICKABLE_NEXT = "next_clickable" essential_modules["cthulhu.cmdnames"].CLICKABLE_LIST = "list_clickables" def test_init(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator initialization through get_navigator function.""" self._setup_dependencies(test_context) from cthulhu import command_manager from cthulhu.structural_navigator import get_navigator nav = get_navigator() assert ( nav._last_input_event is None or nav._last_input_event is not None ) # May be set by other tests assert isinstance(nav._suspended, bool) assert isinstance(nav._mode_for_script, dict) # Verify commands are registered in CommandManager cmd_manager = command_manager.get_manager() assert cmd_manager.get_command("structural_navigator_mode_cycle") is not None assert cmd_manager.get_command("previous_button") is not None assert cmd_manager.get_command("next_button") is not None def test_commands_registered(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.setup registers commands with CommandManager.""" self._setup_dependencies(test_context) from cthulhu import command_manager from cthulhu.structural_navigator import get_navigator nav = get_navigator() nav.set_up_commands() # Commands are now registered in CommandManager cmd_manager = command_manager.get_manager() assert cmd_manager.get_command("structural_navigator_mode_cycle") is not None assert cmd_manager.get_command("previous_button") is not None assert cmd_manager.get_command("next_button") is not None assert cmd_manager.get_command("list_buttons") is not None def test_setup_creates_heading_level_commands(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.setup creates heading level commands.""" self._setup_dependencies(test_context) from cthulhu import command_manager from cthulhu.structural_navigator import get_navigator nav = get_navigator() nav.set_up_commands() cmd_manager = command_manager.get_manager() for level in range(1, 7): assert cmd_manager.get_command(f"previous_heading_level_{level}") is not None assert cmd_manager.get_command(f"next_heading_level_{level}") is not None assert cmd_manager.get_command(f"list_headings_level_{level}") is not None @pytest.mark.parametrize( "current_mode, expected_next_mode, supports_collection", [ pytest.param("OFF", "DOCUMENT", True, id="off_to_document"), pytest.param("DOCUMENT", "GUI", True, id="document_to_gui"), pytest.param("GUI", "OFF", False, id="gui_to_off"), ], ) def test_cycle_mode_transitions( self, test_context: CthulhuTestContext, current_mode, expected_next_mode, supports_collection, ) -> None: """Test StructuralNavigator.cycle_mode transitions.""" essential_modules = self._setup_dependencies(test_context) mock_script = test_context.Mock() from cthulhu.structural_navigator import NavigationMode, get_navigator nav = get_navigator() current_nav_mode = getattr(NavigationMode, current_mode) expected_nav_mode = getattr(NavigationMode, expected_next_mode) mock_get_mode, mock_set_mode = self._setup_cycle_navigation_mode_mocks( test_context, nav, current_nav_mode, supports_collection=supports_collection, )[:2] if supports_collection: essential_modules["cthulhu.AXObject"].supports_collection.return_value = True pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager() pres_manager.present_message.reset_mock() result = nav.cycle_mode(mock_script, None, True) assert result is True mock_get_mode.assert_called_once_with(mock_script) mock_set_mode.assert_called_once_with(mock_script, expected_nav_mode) pres_manager.present_message.assert_called() @pytest.mark.parametrize( "scenario, notify_user, is_active_script, expected_result, expects_message_call", [ pytest.param("no_notify", False, True, True, False, id="no_notify_no_message"), pytest.param( "inactive_script", True, False, False, None, id="inactive_script_returns_false", ), ], ) def test_cycle_mode_edge_scenarios( self, test_context: CthulhuTestContext, scenario, notify_user, is_active_script, expected_result, expects_message_call, ) -> None: """Test StructuralNavigator.cycle_mode edge scenarios.""" essential_modules = self._setup_dependencies(test_context) mock_script = test_context.Mock() from cthulhu.structural_navigator import NavigationMode, get_navigator nav = get_navigator() if scenario == "no_notify": self._setup_cycle_navigation_mode_mocks(test_context, nav, NavigationMode.OFF) essential_modules["cthulhu.AXObject"].supports_collection.return_value = True else: mock_is_active_script = test_context.Mock(return_value=is_active_script) test_context.patch_object(nav, "_is_active_script", new=mock_is_active_script) result = nav.cycle_mode(mock_script, None, notify_user) assert result is expected_result pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager() if expects_message_call is False: pres_manager.present_message.assert_not_called() elif scenario == "inactive_script": mock_is_active_script.assert_called_once_with(mock_script) @pytest.mark.parametrize( "suspend_value, is_active, initial_suspended, expected_suspended, expects_debug, " "expects_cmd_mgr", [ pytest.param(True, True, False, True, True, True, id="suspend_navigation"), pytest.param(False, True, True, False, True, True, id="resume_navigation"), pytest.param( True, False, False, False, False, False, id="inactive_script_no_change", ), ], ) def test_suspend_commands_scenarios( self, test_context: CthulhuTestContext, suspend_value, is_active, initial_suspended, expected_suspended, expects_debug, expects_cmd_mgr, ) -> None: """Test StructuralNavigator.suspend_commands various scenarios.""" essential_modules = self._setup_dependencies(test_context) # Mock command_manager mock_cmd_mgr = test_context.Mock() essential_modules["cthulhu.command_manager"].get_manager.return_value = mock_cmd_mgr mock_script = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() nav._suspended = initial_suspended test_context.patch_object(nav, "_is_active_script", return_value=is_active) mock_cmd_mgr = test_context.Mock() essential_modules["cthulhu.command_manager"].get_manager.return_value = mock_cmd_mgr nav.suspend_commands(mock_script, suspend_value, "test reason") assert nav._suspended == expected_suspended if expects_debug: essential_modules["cthulhu.debug"].print_message.assert_any_call( essential_modules["cthulhu.debug"].LEVEL_INFO, f"STRUCTURAL NAVIGATOR: Suspended: {suspend_value}: test reason", True, ) if expects_cmd_mgr: mock_cmd_mgr.set_group_suspended.assert_called_once() else: mock_cmd_mgr.set_group_suspended.assert_not_called() def test_get_object_in_direction_empty_objects(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator._get_object_in_direction with empty objects list.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() result = nav._get_object_in_direction(mock_script, [], True) assert result is None def test_get_object_in_direction_next_object(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator._get_object_in_direction returns next object.""" essential_modules = self._setup_dependencies(test_context) from cthulhu.structural_navigator import get_navigator nav = get_navigator() mock_obj1 = test_context.Mock() mock_obj2 = test_context.Mock() mock_obj3 = test_context.Mock() objects = [mock_obj1, mock_obj2, mock_obj3] essential_modules[ "cthulhu.focus_manager" ].get_manager.return_value.get_locus_of_focus.return_value = mock_obj2 essential_modules["cthulhu.AXObject"].get_parent.return_value = mock_obj2 def mock_get_path(obj): if obj == mock_obj1: return [0, 0] if obj == mock_obj2: return [0, 1] if obj == mock_obj3: return [0, 2] return [] essential_modules["cthulhu.AXObject"].get_path.side_effect = mock_get_path mock_script = test_context.Mock() def mock_path_comparison(path1, _path2): if path1 == [0, 0]: # obj1 return -1 if path1 == [0, 1]: # obj2 return 0 if path1 == [0, 2]: # obj3 return 1 return 0 mock_script.utilities.path_comparison.side_effect = mock_path_comparison result = nav._get_object_in_direction(mock_script, objects, True) assert result == mock_obj3 def test_get_object_in_direction_previous_object(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator._get_object_in_direction returns previous object.""" essential_modules = self._setup_dependencies(test_context) from cthulhu import ax_utilities from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(ax_utilities.AXUtilities, "is_live_region", return_value=False) mock_obj1 = test_context.Mock() mock_obj2 = test_context.Mock() mock_obj3 = test_context.Mock() objects = [mock_obj1, mock_obj2, mock_obj3] essential_modules[ "cthulhu.focus_manager" ].get_manager.return_value.get_locus_of_focus.return_value = mock_obj2 essential_modules["cthulhu.AXObject"].get_parent.return_value = mock_obj2 def mock_get_path(obj): if obj == mock_obj1: return [0, 0] if obj == mock_obj2: return [0, 1] if obj == mock_obj3: return [0, 2] # Later in tree return [] essential_modules["cthulhu.AXObject"].get_path.side_effect = mock_get_path mock_script = test_context.Mock() def mock_path_comparison(path1, _path2): if path1 == [0, 0]: # obj1 return -1 if path1 == [0, 1]: # obj2 return 0 if path1 == [0, 2]: # obj3 return 1 return 0 mock_script.utilities.path_comparison.side_effect = mock_path_comparison result = nav._get_object_in_direction(mock_script, objects, False) assert result == mock_obj1 def test_get_object_in_direction_wrap_to_beginning(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator._get_object_in_direction wraps to beginning when at end.""" self._setup_dependencies(test_context) from cthulhu.structural_navigator import get_navigator nav = get_navigator() mock_result = test_context.Mock() mock_script = test_context.Mock() test_context.patch_object(nav, "_get_object_in_direction", return_value=mock_result) result = nav._get_object_in_direction(mock_script, [], True, True) assert result == mock_result def test_get_object_in_direction_wrap_to_end(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator._get_object_in_direction wraps to end when at beginning.""" self._setup_dependencies(test_context) from cthulhu.structural_navigator import get_navigator nav = get_navigator() mock_result = test_context.Mock() mock_script = test_context.Mock() test_context.patch_object(nav, "_get_object_in_direction", return_value=mock_result) result = nav._get_object_in_direction(mock_script, [], False, True) assert result == mock_result def test_get_object_in_direction_no_wrap_at_end(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator._get_object_in_direction returns None at end when no wrap.""" self._setup_dependencies(test_context) from cthulhu.structural_navigator import get_navigator nav = get_navigator() mock_script = test_context.Mock() test_context.patch_object(nav, "_get_object_in_direction", return_value=None) result = nav._get_object_in_direction(mock_script, [], True, False) assert result is None def test_previous_button_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_button method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() mock_get_buttons = test_context.Mock(return_value=[]) test_context.patch_object(nav, "_get_all_buttons", new=mock_get_buttons) mock_get_in_direction = test_context.Mock(return_value=None) test_context.patch_object(nav, "_get_object_in_direction", new=mock_get_in_direction) mock_present = test_context.Mock() test_context.patch_object(nav, "_present_object", new=mock_present) result = nav.previous_button(mock_script, mock_event, True) assert result is True mock_get_buttons.assert_called_once_with(mock_script) mock_get_in_direction.assert_called_once() mock_present.assert_called_once() def test_next_button_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_button method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() mock_get_buttons = test_context.Mock(return_value=[]) test_context.patch_object(nav, "_get_all_buttons", new=mock_get_buttons) mock_get_in_direction = test_context.Mock(return_value=None) test_context.patch_object(nav, "_get_object_in_direction", new=mock_get_in_direction) mock_present = test_context.Mock() test_context.patch_object(nav, "_present_object", new=mock_present) result = nav.next_button(mock_script, mock_event, True) assert result is True mock_get_buttons.assert_called_once_with(mock_script) mock_get_in_direction.assert_called_once() mock_present.assert_called_once() def test_list_buttons_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.list_buttons method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() mock_get_buttons = test_context.Mock(return_value=[]) test_context.patch_object(nav, "_get_all_buttons", new=mock_get_buttons) mock_present = test_context.Mock() test_context.patch_object(nav, "_present_object_list", new=mock_present) result = nav.list_buttons(mock_script, mock_event) assert result is True mock_get_buttons.assert_called_once_with(mock_script) mock_present.assert_called_once() @pytest.mark.parametrize( "is_same_script,expected", [ (True, True), (False, False), ], ) def test_is_active_script( self, test_context: CthulhuTestContext, is_same_script: bool, expected: bool, ) -> None: """Test StructuralNavigator._is_active_script with active and inactive scripts.""" essential_modules = self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_active_script = mock_script if is_same_script else test_context.Mock() essential_modules[ "cthulhu.script_manager" ].get_manager.return_value.get_active_script.return_value = mock_active_script from cthulhu.structural_navigator import get_navigator nav = get_navigator() result = nav._is_active_script(mock_script) assert result is expected def test_get_mode_default(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.get_mode returns default mode for new script.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() from cthulhu.structural_navigator import NavigationMode, get_navigator nav = get_navigator() result = nav.get_mode(mock_script) assert result == NavigationMode.OFF def test_get_mode_cached(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.get_mode returns cached mode for known script.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() from cthulhu.structural_navigator import NavigationMode, get_navigator nav = get_navigator() nav._mode_for_script[mock_script] = NavigationMode.DOCUMENT result = nav.get_mode(mock_script) assert result == NavigationMode.DOCUMENT def test_set_mode(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.set_mode sets mode for script.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() from cthulhu.structural_navigator import NavigationMode, get_navigator nav = get_navigator() nav.set_mode(mock_script, NavigationMode.GUI) assert nav._mode_for_script[mock_script] == NavigationMode.GUI def test_determine_root_container_document_mode(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator._determine_root_container for document mode.""" essential_modules = self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_focus = test_context.Mock() mock_document = test_context.Mock() essential_modules[ "cthulhu.focus_manager" ].get_manager().get_locus_of_focus.return_value = mock_focus essential_modules["cthulhu.AXObject"].find_ancestor_inclusive.side_effect = ( lambda focus, predicate: None ) mock_script.utilities.get_top_level_document_for_object.return_value = mock_document from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_determine_root_container", return_value=mock_document) result = nav._determine_root_container(mock_script) assert result == mock_document def test_determine_root_container_fallback(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator._determine_root_container fallback to app.""" essential_modules = self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_app = test_context.Mock(spec=Atspi.Accessible) mock_script.app = mock_app essential_modules["cthulhu.AXUtilities"].is_web_application.return_value = False essential_modules["cthulhu.AXObject"].find_ancestor_inclusive.return_value = mock_app from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_determine_root_container", return_value=mock_app) result = nav._determine_root_container(mock_script) assert result == mock_app @pytest.mark.parametrize( "element_type,ax_method", [ pytest.param("buttons", "find_all_buttons", id="get_all_buttons"), pytest.param("headings", "find_all_headings", id="get_all_headings"), pytest.param("links", "find_all_links", id="get_all_links"), pytest.param("tables", "find_all_tables", id="get_all_tables"), pytest.param("lists", "find_all_lists", id="get_all_lists"), pytest.param("checkboxes", "find_all_check_boxes", id="get_all_checkboxes"), pytest.param("entries", "find_all_editable_objects", id="get_all_entries"), pytest.param("form_fields", "find_all_form_fields", id="get_all_form_fields"), pytest.param("images", "find_all_images_and_image_maps", id="get_all_images"), pytest.param("landmarks", "find_all_landmarks", id="get_all_landmarks"), pytest.param("paragraphs", "find_all_paragraphs", id="get_all_paragraphs"), ], ) def test_get_all_elements( self, test_context: CthulhuTestContext, element_type: str, ax_method: str, ) -> None: """Test StructuralNavigator._get_all_* methods with various element types.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() mock_elements = [test_context.Mock(), test_context.Mock()] if element_type == "lists": test_context.patch( f"cthulhu.structural_navigator.AXUtilities.{ax_method}", side_effect=lambda root, include_description_lists=False, include_tab_lists=False, pred=None: mock_elements, ) elif element_type == "paragraphs": test_context.patch( f"cthulhu.structural_navigator.AXUtilities.{ax_method}", side_effect=lambda root, include_items=False, pred=None: mock_elements, ) else: test_context.patch( f"cthulhu.structural_navigator.AXUtilities.{ax_method}", side_effect=lambda root, pred=None: mock_elements, ) if element_type in ["links", "entries", "images"]: mock_root = test_context.Mock() test_context.patch_object( nav, "_determine_root_container", side_effect=lambda script: mock_root, ) navigation_method = getattr(nav, f"_get_all_{element_type}") result = navigation_method(mock_script) assert result == mock_elements def test_get_mode(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.get_mode method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() from cthulhu.structural_navigator import NavigationMode, get_navigator nav = get_navigator() result = nav.get_mode(mock_script) assert result == NavigationMode.OFF def test_last_input_event_was_navigation_command(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.last_input_event_was_navigation_command method.""" essential_modules = self._setup_dependencies(test_context) essential_modules[ "cthulhu.input_event_manager" ].get_manager().last_event_equals_or_is_release_for_event.return_value = True from cthulhu.structural_navigator import get_navigator nav = get_navigator() mock_event = test_context.Mock() mock_event.as_single_line_string = test_context.Mock(return_value="mock event") nav._last_input_event = mock_event result = nav.last_input_event_was_navigation_command() assert isinstance(result, bool) assert result is True def test_get_state_string(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator._get_state_string method.""" essential_modules = self._setup_dependencies(test_context) mock_obj = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() essential_modules["cthulhu.AXObject"].get_state_set.return_value = None essential_modules["cthulhu.AXUtilities"].is_switch.return_value = False essential_modules["cthulhu.AXUtilities"].is_checked.return_value = False essential_modules["cthulhu.AXUtilities"].is_expanded.return_value = False essential_modules["cthulhu.AXUtilities"].is_pressed.return_value = False essential_modules["cthulhu.AXUtilities"].is_selected.return_value = False test_context.patch_object(nav, "_get_state_string", return_value="test state") result = nav._get_state_string(mock_obj) assert isinstance(result, str) assert result == "test state" def test_get_item_string(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator._get_item_string method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_obj = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object( nav, "_get_item_string", return_value="Test Object: Test Description", ) result = nav._get_item_string(mock_script, mock_obj) assert isinstance(result, str) assert "Test Description" in result def test_is_non_document_object(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator._is_non_document_object method.""" essential_modules = self._setup_dependencies(test_context) mock_obj = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() essential_modules["cthulhu.AXUtilities"].is_application.return_value = False essential_modules["cthulhu.AXUtilities"].is_document.return_value = False essential_modules["cthulhu.AXUtilities"].is_frame.return_value = False essential_modules["cthulhu.AXObject"].get_role.return_value = None result = nav._is_non_document_object(mock_obj) assert isinstance(result, bool) def test_previous_heading_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_heading method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_headings", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.previous_heading(mock_script, mock_event, True) assert result is True def test_next_heading_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_heading method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_headings", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.next_heading(mock_script, mock_event, True) assert result is True def test_list_headings_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.list_headings method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_headings", return_value=[]) test_context.patch_object(nav, "_present_object_list", new=test_context.Mock()) result = nav.list_headings(mock_script, mock_event) assert result is True def test_previous_link_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_link method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_links", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.previous_link(mock_script, mock_event, True) assert result is True def test_next_link_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_link method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_links", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.next_link(mock_script, mock_event, True) assert result is True def test_list_links_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.list_links method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_links", return_value=[]) test_context.patch_object(nav, "_present_object_list", new=test_context.Mock()) result = nav.list_links(mock_script, mock_event) assert result is True def test_previous_table_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_table method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_tables", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.previous_table(mock_script, mock_event, True) assert result is True def test_next_table_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_table method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_tables", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.next_table(mock_script, mock_event, True) assert result is True def test_list_tables_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.list_tables method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_tables", return_value=[]) test_context.patch_object(nav, "_present_object_list", new=test_context.Mock()) result = nav.list_tables(mock_script, mock_event) assert result is True def test_previous_list_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_list method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_lists", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.previous_list(mock_script, mock_event, True) assert result is True def test_next_list_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_list method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_lists", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.next_list(mock_script, mock_event, True) assert result is True def test_list_lists_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.list_lists method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_lists", return_value=[]) test_context.patch_object(nav, "_present_object_list", new=test_context.Mock()) result = nav.list_lists(mock_script, mock_event) assert result is True def test_previous_blockquote_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_blockquote method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_blockquotes", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.previous_blockquote(mock_script, mock_event, True) assert result is True def test_next_blockquote_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_blockquote method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_blockquotes", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.next_blockquote(mock_script, mock_event, True) assert result is True def test_previous_checkbox_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_checkbox method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_checkboxes", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.previous_checkbox(mock_script, mock_event, True) assert result is True def test_next_checkbox_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_checkbox method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_checkboxes", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.next_checkbox(mock_script, mock_event, True) assert result is True def test_previous_entry_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_entry method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_entries", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.previous_entry(mock_script, mock_event, True) assert result is True def test_next_entry_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_entry method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_entries", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.next_entry(mock_script, mock_event, True) assert result is True def test_previous_form_field_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_form_field method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_form_fields", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.previous_form_field(mock_script, mock_event, True) assert result is True def test_next_form_field_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_form_field method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_form_fields", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.next_form_field(mock_script, mock_event, True) assert result is True def test_previous_image_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_image method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_images", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.previous_image(mock_script, mock_event, True) assert result is True def test_next_image_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_image method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_images", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.next_image(mock_script, mock_event, True) assert result is True def test_previous_landmark_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_landmark method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_landmarks", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.previous_landmark(mock_script, mock_event, True) assert result is True def test_next_landmark_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_landmark method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_landmarks", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.next_landmark(mock_script, mock_event, True) assert result is True def test_previous_paragraph_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.previous_paragraph method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_paragraphs", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.previous_paragraph(mock_script, mock_event, True) assert result is True def test_next_paragraph_method(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.next_paragraph method.""" self._setup_dependencies(test_context) mock_script = test_context.Mock() mock_event = test_context.Mock() from cthulhu.structural_navigator import get_navigator nav = get_navigator() test_context.patch_object(nav, "_get_all_paragraphs", return_value=[]) test_context.patch_object(nav, "_get_object_in_direction", return_value=None) test_context.patch_object(nav, "_present_object", new=test_context.Mock()) result = nav.next_paragraph(mock_script, mock_event, True) assert result is True @pytest.mark.parametrize( "expected_next_method,expected_prev_method", [ pytest.param("next_heading_level_1", "previous_heading_level_1", id="heading_level_1"), pytest.param("next_heading_level_2", "previous_heading_level_2", id="heading_level_2"), pytest.param("next_heading_level_3", "previous_heading_level_3", id="heading_level_3"), pytest.param("next_heading_level_4", "previous_heading_level_4", id="heading_level_4"), pytest.param("next_heading_level_5", "previous_heading_level_5", id="heading_level_5"), pytest.param("next_heading_level_6", "previous_heading_level_6", id="heading_level_6"), ], ) def test_heading_level_navigation_methods_exist( self, test_context: CthulhuTestContext, expected_next_method: str, expected_prev_method: str, ) -> None: """Test StructuralNavigator has navigation methods for each heading level.""" essential_modules = test_context.setup_shared_dependencies( [ "cthulhu.keybindings", "cthulhu.dbus_service", "cthulhu.debug", "cthulhu.focus_manager", "cthulhu.AXObject", "cthulhu.AXUtilities", ], ) self._setup_mocks(test_context, essential_modules) from cthulhu.structural_navigator import StructuralNavigator navigator = StructuralNavigator() assert hasattr(navigator, expected_next_method) assert hasattr(navigator, expected_prev_method) assert callable(getattr(navigator, expected_next_method)) assert callable(getattr(navigator, expected_prev_method)) @pytest.mark.parametrize( "mode_value", [ pytest.param("OFF", id="off_mode"), pytest.param("DOCUMENT", id="document_mode"), pytest.param("GUI", id="gui_mode"), ], ) def test_navigation_mode_values( self, test_context: CthulhuTestContext, mode_value: str, ) -> None: """Test NavigationMode enum values.""" essential_modules = test_context.setup_shared_dependencies( [ "cthulhu.keybindings", "cthulhu.dbus_service", "cthulhu.debug", "cthulhu.focus_manager", "cthulhu.AXObject", "cthulhu.AXUtilities", ], ) self._setup_mocks(test_context, essential_modules) from cthulhu.structural_navigator import NavigationMode if mode_value == "OFF": assert NavigationMode.OFF.value == "OFF" elif mode_value == "DOCUMENT": assert NavigationMode.DOCUMENT.value == "DOCUMENT" elif mode_value == "GUI": assert NavigationMode.GUI.value == "GUI" def test_basic_structural_navigator_state(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator basic state management.""" essential_modules = test_context.setup_shared_dependencies( [ "cthulhu.keybindings", "cthulhu.dbus_service", "cthulhu.debug", "cthulhu.focus_manager", "cthulhu.AXObject", "cthulhu.AXUtilities", ], ) self._setup_mocks(test_context, essential_modules) from cthulhu.structural_navigator import StructuralNavigator navigator = StructuralNavigator() assert navigator._last_input_event is None assert navigator._suspended is False assert isinstance(navigator._mode_for_script, dict) def test_get_is_enabled(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.get_is_enabled returns setting value.""" self._setup_dependencies(test_context) from cthulhu.structural_navigator import get_navigator nav = get_navigator() result = nav.get_is_enabled() assert result is True def test_set_is_enabled_no_change(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.set_is_enabled returns early if value unchanged.""" self._setup_dependencies(test_context) from cthulhu.structural_navigator import get_navigator nav = get_navigator() result = nav.set_is_enabled(True) assert result is True def test_set_is_enabled_true_with_previous_mode(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.set_is_enabled restores previous mode when enabling.""" essential_modules = self._setup_dependencies(test_context) mock_script = test_context.Mock() essential_modules[ "cthulhu.script_manager" ].get_manager.return_value.get_active_script.return_value = mock_script mock_cmd_mgr = test_context.Mock() essential_modules["cthulhu.command_manager"].get_manager.return_value = mock_cmd_mgr from cthulhu import gsettings_registry from cthulhu.structural_navigator import NavigationMode, get_navigator gsettings_registry.get_registry().set_runtime_value( "structural-navigation", "enabled", False, ) nav = get_navigator() nav._previous_mode_for_script[mock_script] = NavigationMode.DOCUMENT test_context.patch_object(nav, "_is_active_script", return_value=True) result = nav.set_is_enabled(True) assert result is True assert nav._mode_for_script[mock_script] == NavigationMode.DOCUMENT mock_cmd_mgr.set_group_enabled.assert_called_once() def test_set_is_enabled_true_without_previous_mode(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.set_is_enabled without previous mode when enabling.""" essential_modules = self._setup_dependencies(test_context) mock_script = test_context.Mock() essential_modules[ "cthulhu.script_manager" ].get_manager.return_value.get_active_script.return_value = mock_script mock_cmd_mgr = test_context.Mock() essential_modules["cthulhu.command_manager"].get_manager.return_value = mock_cmd_mgr from cthulhu import gsettings_registry from cthulhu.structural_navigator import get_navigator gsettings_registry.get_registry().set_runtime_value( "structural-navigation", "enabled", False, ) nav = get_navigator() test_context.patch_object(nav, "_is_active_script", return_value=True) result = nav.set_is_enabled(True) assert result is True mock_cmd_mgr.set_group_enabled.assert_called_once() def test_set_is_enabled_false_saves_mode(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.set_is_enabled saves current mode when disabling.""" essential_modules = self._setup_dependencies(test_context) mock_script = test_context.Mock() essential_modules[ "cthulhu.script_manager" ].get_manager.return_value.get_active_script.return_value = mock_script mock_cmd_mgr = test_context.Mock() essential_modules["cthulhu.command_manager"].get_manager.return_value = mock_cmd_mgr from cthulhu.structural_navigator import NavigationMode, get_navigator nav = get_navigator() nav._mode_for_script[mock_script] = NavigationMode.DOCUMENT test_context.patch_object(nav, "_is_active_script", return_value=True) result = nav.set_is_enabled(False) assert result is True assert nav._previous_mode_for_script[mock_script] == NavigationMode.DOCUMENT assert nav._mode_for_script[mock_script] == NavigationMode.OFF mock_cmd_mgr.set_group_enabled.assert_called_once() def test_set_is_enabled_false_already_off(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.set_is_enabled returns early if already OFF.""" essential_modules = self._setup_dependencies(test_context) mock_script = test_context.Mock() essential_modules[ "cthulhu.script_manager" ].get_manager.return_value.get_active_script.return_value = mock_script mock_cmd_mgr = test_context.Mock() essential_modules["cthulhu.command_manager"].get_manager.return_value = mock_cmd_mgr from cthulhu.structural_navigator import NavigationMode, get_navigator nav = get_navigator() nav._mode_for_script[mock_script] = NavigationMode.OFF result = nav.set_is_enabled(False) assert result is True mock_cmd_mgr.set_group_enabled.assert_not_called() def test_get_triggers_focus_mode(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.get_triggers_focus_mode returns setting value.""" self._setup_dependencies(test_context) from cthulhu.structural_navigator import get_navigator nav = get_navigator() result = nav.get_triggers_focus_mode() assert result is False def test_set_triggers_focus_mode(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.set_triggers_focus_mode updates setting.""" self._setup_dependencies(test_context) from cthulhu import gsettings_registry from cthulhu.structural_navigator import get_navigator gsettings_registry.get_registry().set_runtime_value( "structural-navigation", "triggers-focus-mode", True, ) nav = get_navigator() result = nav.set_triggers_focus_mode(False) assert result is True assert nav.get_triggers_focus_mode() is False def test_set_triggers_focus_mode_no_change(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.set_triggers_focus_mode returns early if unchanged.""" self._setup_dependencies(test_context) from cthulhu import gsettings_registry from cthulhu.structural_navigator import get_navigator gsettings_registry.get_registry().set_runtime_value( "structural-navigation", "triggers-focus-mode", True, ) nav = get_navigator() result = nav.set_triggers_focus_mode(True) assert result is True def test_last_command_prevents_focus_mode_true(self, test_context: CthulhuTestContext) -> None: """Test StructuralNavigator.last_command_prevents_focus_mode returns True.""" essential_modules = self._setup_dependencies(test_context) essential_modules[ "cthulhu.input_event_manager" ].get_manager.return_value.last_event_equals_or_is_release_for_event.return_value = True from cthulhu.structural_navigator import get_navigator nav = get_navigator() mock_event = test_context.Mock() nav._last_input_event = mock_event result = nav.last_command_prevents_focus_mode() assert result is True def test_last_command_prevents_focus_mode_false_no_event( self, test_context: CthulhuTestContext, ) -> None: """Test StructuralNavigator.last_command_prevents_focus_mode returns False if no event.""" self._setup_dependencies(test_context) from cthulhu.structural_navigator import get_navigator nav = get_navigator() nav._last_input_event = None result = nav.last_command_prevents_focus_mode() assert result is False def test_last_command_prevents_focus_mode_false_setting_true( self, test_context: CthulhuTestContext, ) -> None: """Test last_command_prevents_focus_mode returns False if setting True.""" self._setup_dependencies(test_context) from cthulhu import gsettings_registry from cthulhu.structural_navigator import get_navigator gsettings_registry.get_registry().set_runtime_value( "structural-navigation", "triggers-focus-mode", True, ) nav = get_navigator() mock_event = test_context.Mock() nav._last_input_event = mock_event result = nav.last_command_prevents_focus_mode() assert result is False def test_present_line_emits_region_changed(self, test_context: CthulhuTestContext) -> None: """Test _present_line emits region_changed with STRUCTURAL_NAVIGATOR mode.""" essential_modules = self._setup_dependencies(test_context) from cthulhu import focus_manager from cthulhu.structural_navigator import NavigationMode, get_navigator focus_manager_mock = essential_modules["cthulhu.focus_manager"] manager_instance = test_context.Mock() focus_manager_mock.get_manager.return_value = manager_instance manager_instance.in_say_all.return_value = False focus_manager_mock.STRUCTURAL_NAVIGATOR = focus_manager.STRUCTURAL_NAVIGATOR nav = get_navigator() mock_script = test_context.Mock() mock_obj = test_context.Mock() test_offset = 5 test_context.patch_object(nav, "get_mode", return_value=NavigationMode.DOCUMENT) mock_script.utilities.get_line_contents_at_offset.return_value = [ (mock_obj, test_offset, test_offset + 10, "test text"), ] nav._present_line(mock_script, mock_obj, test_offset, notify_user=True) manager_instance.emit_region_changed.assert_called() call_kwargs = manager_instance.emit_region_changed.call_args assert call_kwargs.kwargs.get("mode") == focus_manager.STRUCTURAL_NAVIGATOR def test_present_object_emits_region_changed_in_document_mode( self, test_context: CthulhuTestContext, ) -> None: """Test _present_object emits region_changed with STRUCTURAL_NAVIGATOR mode.""" essential_modules = self._setup_dependencies(test_context) from cthulhu import focus_manager from cthulhu.structural_navigator import NavigationMode, get_navigator focus_manager_mock = essential_modules["cthulhu.focus_manager"] manager_instance = test_context.Mock() focus_manager_mock.get_manager.return_value = manager_instance manager_instance.in_say_all.return_value = False focus_manager_mock.STRUCTURAL_NAVIGATOR = focus_manager.STRUCTURAL_NAVIGATOR nav = get_navigator() mock_script = test_context.Mock() mock_obj = test_context.Mock() test_offset = 10 test_context.patch_object(nav, "get_mode", return_value=NavigationMode.DOCUMENT) nav._present_object(mock_script, mock_obj, offset=test_offset, notify_user=True) manager_instance.emit_region_changed.assert_called() call_kwargs = manager_instance.emit_region_changed.call_args assert call_kwargs.kwargs.get("mode") == focus_manager.STRUCTURAL_NAVIGATOR