Files
cthulhu/tests/test_table_navigator.py

2242 lines
103 KiB
Python

# Unit tests for table_navigator.py methods.
#
# Copyright 2025 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# 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 table_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 unittest.mock import MagicMock
from cthulhu_test_context import CthulhuTestContext
@pytest.mark.unit
class TestTableNavigator:
"""Test TableNavigator class methods."""
def _setup_dependencies(self, test_context: CthulhuTestContext) -> dict[str, MagicMock]:
"""Set up dependencies for table_navigator module testing."""
additional_modules = [
"cthulhu.command_manager",
"cthulhu.guilabels",
"cthulhu.input_event_manager",
"cthulhu.cmdnames",
"cthulhu.messages",
"cthulhu.object_properties",
"cthulhu.cthulhu_gui_navlist",
"cthulhu.cthulhu_i18n",
"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)
# Set up cmdnames with all required values for structural_navigator
cmdnames = essential_modules["cthulhu.cmdnames"]
cmdnames.STRUCTURAL_NAVIGATION_MODE_CYCLE = "cycle_mode"
cmdnames.BLOCKQUOTE_PREV = "previous_blockquote"
cmdnames.BLOCKQUOTE_NEXT = "next_blockquote"
cmdnames.BLOCKQUOTE_LIST = "list_blockquotes"
cmdnames.BUTTON_PREV = "previous_button"
cmdnames.BUTTON_NEXT = "next_button"
cmdnames.BUTTON_LIST = "list_buttons"
cmdnames.CHECK_BOX_PREV = "previous_checkbox"
cmdnames.CHECK_BOX_NEXT = "next_checkbox"
cmdnames.CHECK_BOX_LIST = "list_checkboxes"
cmdnames.COMBO_BOX_PREV = "previous_combobox"
cmdnames.COMBO_BOX_NEXT = "next_combobox"
cmdnames.COMBO_BOX_LIST = "list_comboboxes"
cmdnames.ENTRY_PREV = "previous_entry"
cmdnames.ENTRY_NEXT = "next_entry"
cmdnames.ENTRY_LIST = "list_entries"
cmdnames.FORM_FIELD_PREV = "previous_form_field"
cmdnames.FORM_FIELD_NEXT = "next_form_field"
cmdnames.FORM_FIELD_LIST = "list_form_fields"
cmdnames.HEADING_PREV = "previous_heading"
cmdnames.HEADING_NEXT = "next_heading"
cmdnames.HEADING_LIST = "list_headings"
cmdnames.HEADING_AT_LEVEL_PREV = "previous_heading_level_%d"
cmdnames.HEADING_AT_LEVEL_NEXT = "next_heading_level_%d"
cmdnames.HEADING_AT_LEVEL_LIST = "list_headings_level_%d"
cmdnames.IFRAME_PREV = "previous_iframe"
cmdnames.IFRAME_NEXT = "next_iframe"
cmdnames.IFRAME_LIST = "list_iframes"
cmdnames.IMAGE_PREV = "previous_image"
cmdnames.IMAGE_NEXT = "next_image"
cmdnames.IMAGE_LIST = "list_images"
cmdnames.LANDMARK_PREV = "previous_landmark"
cmdnames.LANDMARK_NEXT = "next_landmark"
cmdnames.LANDMARK_LIST = "list_landmarks"
cmdnames.LIST_PREV = "previous_list"
cmdnames.LIST_NEXT = "next_list"
cmdnames.LIST_LIST = "list_lists"
cmdnames.LIST_ITEM_PREV = "previous_list_item"
cmdnames.LIST_ITEM_NEXT = "next_list_item"
cmdnames.LIST_ITEM_LIST = "list_list_items"
cmdnames.LIVE_REGION_PREV = "previous_live_region"
cmdnames.LIVE_REGION_NEXT = "next_live_region"
cmdnames.LIVE_REGION_LAST = "last_live_region"
cmdnames.PARAGRAPH_PREV = "previous_paragraph"
cmdnames.PARAGRAPH_NEXT = "next_paragraph"
cmdnames.PARAGRAPH_LIST = "list_paragraphs"
cmdnames.RADIO_BUTTON_PREV = "previous_radio_button"
cmdnames.RADIO_BUTTON_NEXT = "next_radio_button"
cmdnames.RADIO_BUTTON_LIST = "list_radio_buttons"
cmdnames.SEPARATOR_PREV = "previous_separator"
cmdnames.SEPARATOR_NEXT = "next_separator"
cmdnames.TABLE_PREV = "previous_table"
cmdnames.TABLE_NEXT = "next_table"
cmdnames.TABLE_LIST = "list_tables"
cmdnames.UNVISITED_LINK_PREV = "previous_unvisited_link"
cmdnames.UNVISITED_LINK_NEXT = "next_unvisited_link"
cmdnames.UNVISITED_LINK_LIST = "list_unvisited_links"
cmdnames.VISITED_LINK_PREV = "previous_visited_link"
cmdnames.VISITED_LINK_NEXT = "next_visited_link"
cmdnames.VISITED_LINK_LIST = "list_visited_links"
cmdnames.LINK_PREV = "previous_link"
cmdnames.LINK_NEXT = "next_link"
cmdnames.LINK_LIST = "list_links"
cmdnames.CLICKABLE_PREV = "previous_clickable"
cmdnames.CLICKABLE_NEXT = "next_clickable"
cmdnames.CLICKABLE_LIST = "list_clickables"
cmdnames.LARGE_OBJECT_PREV = "previous_large_object"
cmdnames.LARGE_OBJECT_NEXT = "next_large_object"
cmdnames.LARGE_OBJECT_LIST = "list_large_objects"
cmdnames.CONTAINER_START = "container_start"
cmdnames.CONTAINER_END = "container_end"
essential_modules["cthulhu.cthulhu_i18n"]._ = lambda x: x
essential_modules["cthulhu.debug"].print_message = test_context.Mock()
essential_modules["cthulhu.debug"].LEVEL_INFO = 800
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
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"].supports_collection.return_value = True
essential_modules["cthulhu.AXUtilities"].is_heading.return_value = False
return essential_modules
def test_init(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.__init__ creates instance with correct default values."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu import command_manager
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
assert navigator._previous_reported_row is None
assert navigator._previous_reported_col is None
assert navigator._last_input_event is None
assert navigator.get_is_enabled() is True
# D-Bus registration and commands are registered during setup()
navigator.set_up_commands()
# Verify commands are registered in CommandManager
cmd_manager = command_manager.get_manager()
assert cmd_manager.get_command("table_navigator_toggle_enabled") is not None
assert cmd_manager.get_command("table_cell_down") is not None
mock_controller.register_decorated_module.assert_called_with("TableNavigator", navigator)
def test_is_enabled_default_true(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.is_enabled returns True by default."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
assert navigator.get_is_enabled() is True
def test_is_enabled_after_disable(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.get_is_enabled returns False when disabled."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu import gsettings_registry
from cthulhu.table_navigator import TableNavigator
gsettings_registry.get_registry().set_runtime_value("table-navigation", "enabled", False)
navigator = TableNavigator()
assert navigator.get_is_enabled() is False
def test_last_input_event_was_navigation_command_none(
self,
test_context: CthulhuTestContext,
) -> None:
"""Test last_input_event_was_navigation_command with None event returns False."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_manager = test_context.Mock()
essential_modules["cthulhu.input_event_manager"].get_manager.return_value = mock_manager
mock_manager.last_event_equals_or_is_release_for_event.return_value = False
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
result = navigator.last_input_event_was_navigation_command()
assert result is False
mock_manager.last_event_equals_or_is_release_for_event.assert_not_called()
def test_last_input_event_was_navigation_command_with_event(
self,
test_context: CthulhuTestContext,
) -> None:
"""Test last_input_event_was_navigation_command with event returns True when matching."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_manager = test_context.Mock()
essential_modules["cthulhu.input_event_manager"].get_manager.return_value = mock_manager
mock_manager.last_event_equals_or_is_release_for_event.return_value = True
mock_event = test_context.Mock()
mock_event.as_single_line_string.return_value = "test_event"
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._last_input_event = mock_event
result = navigator.last_input_event_was_navigation_command()
assert result is True
mock_manager.last_event_equals_or_is_release_for_event.assert_called_once_with(mock_event)
@pytest.mark.parametrize(
"initial_enabled,expected_enabled,expected_message_attr",
[
(False, True, "TABLE_NAVIGATION_ENABLED"),
(True, False, "TABLE_NAVIGATION_DISABLED"),
],
)
def test_toggle_enabled(
self,
test_context: CthulhuTestContext,
initial_enabled: bool,
expected_enabled: bool,
expected_message_attr: str,
) -> None:
"""Test TableNavigator.toggle_enabled toggles state and presents appropriate message."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_cmd_mgr = test_context.Mock()
essential_modules["cthulhu.command_manager"].get_manager.return_value = mock_cmd_mgr
from cthulhu.table_navigator import TableNavigator
guilabels_mock = essential_modules["cthulhu.guilabels"]
guilabels_mock.KB_GROUP_TABLE_NAVIGATION = "Table navigation"
navigator = TableNavigator()
mock_cmd_mgr.is_group_enabled.return_value = initial_enabled
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.toggle_enabled(mock_script, mock_event, notify_user=True)
assert result is True
expected_message = getattr(essential_modules["cthulhu.messages"], expected_message_attr)
pres_manager.present_message.assert_called_once_with(expected_message)
def test_toggle_enabled_no_notify(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.toggle_enabled does not present message when notify_user=False."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_cmd_mgr = test_context.Mock()
essential_modules["cthulhu.command_manager"].get_manager.return_value = mock_cmd_mgr
from cthulhu.table_navigator import TableNavigator
guilabels_mock = essential_modules["cthulhu.guilabels"]
guilabels_mock.KB_GROUP_TABLE_NAVIGATION = "Table navigation"
navigator = TableNavigator()
mock_cmd_mgr.is_group_enabled.return_value = False
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.toggle_enabled(mock_script, mock_event, notify_user=False)
assert result is True
pres_manager.present_message.assert_not_called()
@pytest.mark.parametrize(
"is_focusable, has_name, has_children, is_whitespace, expected_result",
[
pytest.param(True, False, False, True, False, id="focusable_object"),
pytest.param(False, True, False, True, False, id="object_with_name"),
pytest.param(False, False, True, True, False, id="object_with_non_blank_children"),
pytest.param(False, False, False, False, False, id="object_with_text"),
pytest.param(False, False, False, True, True, id="blank_object"),
pytest.param(False, False, True, True, True, id="object_with_only_blank_children"),
],
)
def test_is_blank(
self,
test_context,
is_focusable,
has_name,
has_children,
is_whitespace,
expected_result,
) -> None:
"""Test _is_blank correctly identifies blank objects based on various criteria."""
essential_modules = self._setup_dependencies(test_context)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_focusable",
return_value=is_focusable,
)
test_context.patch(
"cthulhu.table_navigator.AXObject.get_name",
return_value="name" if has_name else "",
)
test_context.patch(
"cthulhu.table_navigator.AXObject.get_child_count",
return_value=1 if has_children else 0,
)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_whitespace_or_empty",
return_value=is_whitespace,
)
if has_children:
mock_child = test_context.Mock()
test_context.patch(
"cthulhu.table_navigator.AXObject.iter_children",
return_value=[mock_child],
)
# For the recursive call on child, configure mock to handle both expected cases
if expected_result and not is_focusable and not has_name:
# Child should also be blank - override child-specific mocking
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_focusable",
side_effect=lambda obj: False if obj == mock_child else is_focusable,
)
test_context.patch(
"cthulhu.table_navigator.AXObject.get_name",
side_effect=lambda obj: (
"" if obj == mock_child else ("name" if has_name else "")
),
)
test_context.patch(
"cthulhu.table_navigator.AXObject.get_child_count",
side_effect=lambda obj: 0 if obj == mock_child else (1 if has_children else 0),
)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_whitespace_or_empty",
side_effect=lambda obj: True if obj == mock_child else is_whitespace,
)
else:
# Child should not be blank - make it focusable to stop recursion
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_focusable",
side_effect=lambda obj: True if obj == mock_child else is_focusable,
)
else:
test_context.patch("cthulhu.table_navigator.AXObject.iter_children", return_value=[])
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
mock_obj = test_context.Mock(spec=Atspi.Accessible)
result = navigator._is_blank(mock_obj)
assert result == expected_result
def test_get_current_cell_basic(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator._get_current_cell returns focus manager's locus when it is a cell."""
essential_modules = self._setup_dependencies(test_context)
mock_cell = test_context.Mock(spec=Atspi.Accessible)
mock_focus_manager = test_context.Mock()
mock_focus_manager.get_locus_of_focus.return_value = mock_cell
test_context.patch(
"cthulhu.table_navigator.focus_manager.get_manager",
return_value=mock_focus_manager,
)
test_context.patch("cthulhu.table_navigator.AXObject.get_parent", return_value=None)
def mock_is_cell_or_header(obj):
return obj == mock_cell
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_table_cell_or_header",
new=mock_is_cell_or_header,
)
test_context.patch("cthulhu.table_navigator.AXUtilities.find_ancestor", return_value=None)
test_context.patch("cthulhu.table_navigator.debug.print_tokens", return_value=None)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
result = navigator._get_current_cell()
assert result == mock_cell
def test_get_current_cell_nested(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator._get_current_cell returns parent when it is also a table cell."""
essential_modules = self._setup_dependencies(test_context)
mock_focus_manager = test_context.Mock()
essential_modules["cthulhu.focus_manager"].get_manager.return_value = mock_focus_manager
mock_inner_cell = test_context.Mock(spec=Atspi.Accessible)
mock_parent_cell = test_context.Mock(spec=Atspi.Accessible)
mock_focus_manager.get_locus_of_focus.return_value = mock_inner_cell
test_context.patch(
"cthulhu.table_navigator.AXObject.get_parent",
side_effect=lambda obj: mock_parent_cell if obj == mock_inner_cell else None,
)
def mock_is_table_cell(obj):
return obj in [mock_inner_cell, mock_parent_cell]
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_table_cell_or_header",
new=mock_is_table_cell,
)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
result = navigator._get_current_cell()
assert result == mock_parent_cell
def test_get_cell_coordinates_basic(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator._get_cell_coordinates returns coordinates from AXTable."""
essential_modules = self._setup_dependencies(test_context)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_coordinates",
return_value=(2, 3),
)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
mock_cell = test_context.Mock(spec=Atspi.Accessible)
result = navigator._get_cell_coordinates(mock_cell)
assert result == (2, 3)
def test_get_cell_coordinates_with_previous(self, test_context: CthulhuTestContext) -> None:
"""Test _get_cell_coordinates returns previous coordinates when cell matches."""
essential_modules = self._setup_dependencies(test_context)
mock_cell = test_context.Mock(spec=Atspi.Accessible)
mock_table = test_context.Mock(spec=Atspi.Accessible)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_coordinates",
return_value=(2, 3),
)
test_context.patch("cthulhu.table_navigator.AXUtilities.get_table", return_value=mock_table)
test_context.patch("cthulhu.table_navigator.AXTable.get_cell_at", return_value=mock_cell)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._previous_reported_row = 1
navigator._previous_reported_col = 2
result = navigator._get_cell_coordinates(mock_cell)
assert result == (1, 2)
@pytest.mark.parametrize(
"direction, boundary_check, boundary_message, get_next_cell_method",
[
pytest.param(
"left",
"is_start_of_row",
"TABLE_ROW_BEGINNING",
"get_cell_on_left",
id="move_left",
),
pytest.param(
"right",
"is_end_of_row",
"TABLE_ROW_END",
"get_cell_on_right",
id="move_right",
),
pytest.param(
"up",
"is_top_of_column",
"TABLE_COLUMN_TOP",
"get_cell_above",
id="move_up",
),
pytest.param(
"down",
"is_bottom_of_column",
"TABLE_COLUMN_BOTTOM",
"get_cell_below",
id="move_down",
),
],
)
def test_move_direction_scenarios(
self,
test_context: CthulhuTestContext,
direction: str,
boundary_check: str,
boundary_message: str,
get_next_cell_method: str,
) -> None:
"""Test TableNavigator move methods for various scenarios."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
mock_script = test_context.Mock()
mock_event = test_context.Mock()
move_method = getattr(navigator, f"move_{direction}")
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
navigator._get_current_cell = test_context.Mock(return_value=None)
pres_manager.present_message.reset_mock()
result = move_method(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
assert navigator._last_input_event == mock_event
mock_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch(f"cthulhu.table_navigator.AXTable.{boundary_check}", return_value=True)
navigator._get_current_cell = test_context.Mock(return_value=mock_cell)
pres_manager.present_message.reset_mock()
result = move_method(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_with(
getattr(essential_modules["cthulhu.messages"], boundary_message),
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_next_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch(f"cthulhu.table_navigator.AXTable.{boundary_check}", return_value=False)
test_context.patch(
f"cthulhu.table_navigator.AXTable.{get_next_cell_method}",
return_value=mock_next_cell,
)
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
navigator._get_cell_coordinates = test_context.Mock(return_value=(1, 2))
mock_present_cell = test_context.Mock()
navigator._present_cell = mock_present_cell
mock_script.reset_mock()
result = move_method(mock_script, mock_event)
assert result is True
mock_present_cell.assert_called_once()
call_args = mock_present_cell.call_args[0]
assert call_args[0] == mock_script
assert call_args[1] == mock_next_cell
assert call_args[4] == mock_current_cell
assert call_args[5] is True
def test_move_left_successful(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.move_left successfully moves to left cell and presents it."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_left_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXTable.is_start_of_row", return_value=False)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_on_left",
return_value=mock_left_cell,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
navigator._get_cell_coordinates = test_context.Mock(return_value=(1, 2))
mock_present_cell = test_context.Mock()
navigator._present_cell = mock_present_cell
mock_script = test_context.Mock()
mock_event = test_context.Mock()
result = navigator.move_left(mock_script, mock_event)
assert result is True
mock_present_cell.assert_called_once_with(
mock_script,
mock_left_cell,
1,
1,
mock_current_cell,
True,
)
def test_move_right_successful(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.move_right successfully moves to right cell and presents it."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_right_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXTable.is_end_of_row", return_value=False)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_on_right",
return_value=mock_right_cell,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
navigator._get_cell_coordinates = test_context.Mock(return_value=(1, 2))
mock_present_cell = test_context.Mock()
navigator._present_cell = mock_present_cell
mock_script = test_context.Mock()
mock_event = test_context.Mock()
result = navigator.move_right(mock_script, mock_event)
assert result is True
mock_present_cell.assert_called_once_with(
mock_script,
mock_right_cell,
1,
3,
mock_current_cell,
True,
)
def test_move_up_not_in_table(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.move_up presents not in table message when current cell is None."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=None)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_up(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
assert navigator._last_input_event == mock_event
def test_move_up_at_top_of_column(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.move_up presents column top message when at top of column."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXTable.is_top_of_column", return_value=True)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_cell)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_up(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_COLUMN_TOP,
)
def test_move_up_successful(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.move_up successfully moves to cell above and presents it."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_up_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXTable.is_top_of_column", return_value=False)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_above",
return_value=mock_up_cell,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
navigator._get_cell_coordinates = test_context.Mock(return_value=(2, 1))
mock_present_cell = test_context.Mock()
navigator._present_cell = mock_present_cell
mock_script = test_context.Mock()
mock_event = test_context.Mock()
result = navigator.move_up(mock_script, mock_event)
assert result is True
mock_present_cell.assert_called_once_with(
mock_script,
mock_up_cell,
1,
1,
mock_current_cell,
True,
)
def test_move_down_successful(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.move_down successfully moves to cell below and presents it."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_down_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch(
"cthulhu.table_navigator.AXTable.is_bottom_of_column",
return_value=False,
)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_below",
return_value=mock_down_cell,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
navigator._get_cell_coordinates = test_context.Mock(return_value=(1, 1))
mock_present_cell = test_context.Mock()
navigator._present_cell = mock_present_cell
mock_script = test_context.Mock()
mock_event = test_context.Mock()
result = navigator.move_down(mock_script, mock_event)
assert result is True
mock_present_cell.assert_called_once_with(
mock_script,
mock_down_cell,
2,
1,
mock_current_cell,
True,
)
def test_move_to_first_cell_not_in_table(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_first_cell presents not in table message when current cell is None."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=None)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_to_first_cell(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
def test_move_to_first_cell_successful(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_first_cell successfully moves to first cell and presents it."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_table = test_context.Mock(spec=Atspi.Accessible)
mock_first_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXUtilities.get_table", return_value=mock_table)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_first_cell",
return_value=mock_first_cell,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
mock_present_cell = test_context.Mock()
navigator._present_cell = mock_present_cell
mock_script = test_context.Mock()
mock_event = test_context.Mock()
result = navigator.move_to_first_cell(mock_script, mock_event)
assert result is True
mock_present_cell.assert_called_once_with(
mock_script,
mock_first_cell,
0,
0,
mock_current_cell,
True,
)
def test_present_cell_not_cell_or_header(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator._present_cell returns early when object is not a cell or header."""
essential_modules = self._setup_dependencies(test_context)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_table_cell_or_header",
return_value=False,
)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
mock_script = test_context.Mock()
mock_cell = test_context.Mock(spec=Atspi.Accessible)
mock_previous_cell = test_context.Mock(spec=Atspi.Accessible)
navigator._present_cell(mock_script, mock_cell, 1, 2, mock_previous_cell)
assert navigator._previous_reported_row != 1
assert navigator._previous_reported_col != 2
def test_present_cell_successful(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator._present_cell sets focus, coordinates, and presents cell."""
essential_modules = self._setup_dependencies(test_context)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_table_cell_or_header",
return_value=True,
)
test_context.patch("cthulhu.table_navigator.AXObject.grab_focus", return_value=None)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.get_descendant_supporting_text", return_value=None
)
test_context.patch("cthulhu.table_navigator.AXObject.supports_text", return_value=False)
test_context.patch("cthulhu.table_navigator.AXTable.get_cell_spans", return_value=(1, 1))
mock_focus_manager = test_context.Mock()
essential_modules["cthulhu.focus_manager"].get_manager.return_value = mock_focus_manager
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu import gsettings_registry
from cthulhu.table_navigator import TableNavigator
registry = gsettings_registry.get_registry()
registry.set_runtime_value("speech", "announce-cell-coordinates", False)
registry.set_runtime_value("speech", "announce-cell-span", False)
navigator = TableNavigator()
mock_script = test_context.Mock()
mock_script.utilities.grab_focus_when_setting_caret.return_value = False
test_context.patch("cthulhu.table_navigator.AXUtilities.is_gui_cell", return_value=False)
mock_script.present_object = test_context.Mock()
mock_cell = test_context.Mock(spec=Atspi.Accessible)
mock_previous_cell = test_context.Mock(spec=Atspi.Accessible)
navigator._present_cell(mock_script, mock_cell, 1, 2, mock_previous_cell)
assert navigator._previous_reported_row == 1
assert navigator._previous_reported_col == 2
mock_focus_manager.set_locus_of_focus.assert_called()
mock_script.present_object.assert_called_once_with(
mock_cell,
offset=0,
priorObj=mock_previous_cell,
interrupt=True,
)
def test_get_navigator(self, test_context: CthulhuTestContext) -> None:
"""Test table_navigator.get_navigator returns singleton TableNavigator instance."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu import table_navigator
navigator1 = table_navigator.get_navigator()
navigator2 = table_navigator.get_navigator()
assert navigator1 is navigator2
assert isinstance(navigator1, table_navigator.TableNavigator)
def test_get_current_cell_needs_ancestor_search(self, test_context: CthulhuTestContext) -> None:
"""Test _get_current_cell when cell is not table cell and needs ancestor search."""
essential_modules = self._setup_dependencies(test_context)
mock_initial_cell = test_context.Mock(spec=Atspi.Accessible)
mock_ancestor_cell = test_context.Mock(spec=Atspi.Accessible)
mock_focus_manager = test_context.Mock()
mock_focus_manager.get_locus_of_focus.return_value = mock_initial_cell
test_context.patch(
"cthulhu.table_navigator.focus_manager.get_manager",
return_value=mock_focus_manager,
)
test_context.patch("cthulhu.table_navigator.AXObject.get_parent", return_value=None)
def mock_is_cell_or_header(obj):
return obj == mock_ancestor_cell
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_table_cell_or_header",
new=mock_is_cell_or_header,
)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.find_ancestor",
return_value=mock_ancestor_cell,
)
test_context.patch("cthulhu.table_navigator.debug.print_tokens", return_value=None)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
result = navigator._get_current_cell()
assert result == mock_ancestor_cell
def test_move_left_skip_blank_cells(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.move_left skipping blank cells when setting is enabled."""
essential_modules = self._setup_dependencies(test_context)
from cthulhu import gsettings_registry
gsettings_registry.get_registry().set_runtime_value(
"table-navigation",
"skip-blank-cells",
True,
)
mock_script = test_context.Mock()
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_blank_cell = test_context.Mock(spec=Atspi.Accessible)
mock_final_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_coordinates",
return_value=(1, 1),
)
test_context.patch(
"cthulhu.table_navigator.AXTable.is_start_of_row",
side_effect=lambda cell: cell == mock_final_cell,
)
cell_sequence = [mock_blank_cell, mock_final_cell]
call_count = [0]
def mock_get_cell_on_left(_cell):
if call_count[0] < len(cell_sequence):
result = cell_sequence[call_count[0]]
call_count[0] += 1
return result
return None
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_on_left",
new=mock_get_cell_on_left,
)
def mock_is_blank(cell):
return cell == mock_blank_cell
test_context.patch("cthulhu.table_navigator.debug.print_tokens", return_value=None)
test_context.patch("cthulhu.table_navigator.TableNavigator._present_cell", return_value=None)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._is_blank = mock_is_blank
result = navigator.move_left(mock_script, mock_current_cell)
assert result is True
def test_move_right_at_end_of_row(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.move_right when at end of row returns True and notifies user."""
essential_modules = self._setup_dependencies(test_context)
mock_script = test_context.Mock()
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_coordinates",
return_value=(1, 5),
)
test_context.patch("cthulhu.table_navigator.AXTable.get_cell_on_right", return_value=None)
test_context.patch("cthulhu.table_navigator.AXTable.is_end_of_row", return_value=True)
test_context.patch("cthulhu.table_navigator.debug.print_tokens", return_value=None)
test_context.patch("cthulhu.table_navigator.TableNavigator._present_cell", return_value=None)
mock_focus_manager = test_context.Mock()
mock_focus_manager.get_locus_of_focus.return_value = mock_current_cell
test_context.patch(
"cthulhu.table_navigator.focus_manager.get_manager",
return_value=mock_focus_manager,
)
test_context.patch("cthulhu.table_navigator.AXObject.get_parent", return_value=None)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_table_cell_or_header",
return_value=True,
)
test_context.patch("cthulhu.table_navigator.AXUtilities.find_ancestor", return_value=None)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_right(mock_script)
# move_right returns True even at end of row, but presents a message
assert result is True
pres_manager.present_message.assert_called_once()
def test_move_to_last_cell_not_in_table(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_last_cell presents not in table message when current cell is None."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=None)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_to_last_cell(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
assert navigator._last_input_event == mock_event
def test_move_to_last_cell_successful(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.move_to_last_cell successfully moves to last cell and presents it."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_table = test_context.Mock(spec=Atspi.Accessible)
mock_last_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXUtilities.get_table", return_value=mock_table)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_last_cell",
return_value=mock_last_cell,
)
test_context.patch("cthulhu.table_navigator.AXTable.get_row_count", return_value=5)
test_context.patch("cthulhu.table_navigator.AXTable.get_column_count", return_value=3)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
mock_present_cell = test_context.Mock()
navigator._present_cell = mock_present_cell
mock_script = test_context.Mock()
mock_event = test_context.Mock()
result = navigator.move_to_last_cell(mock_script, mock_event)
assert result is True
mock_present_cell.assert_called_once_with(
mock_script,
mock_last_cell,
5,
3,
mock_current_cell,
True,
)
def test_move_to_top_of_column_not_in_table(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_top_of_column presents not in table message when current cell is None."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=None)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_to_top_of_column(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
def test_move_to_top_of_column_already_at_top(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_top_of_column presents column top message when already at top."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXTable.is_top_of_column", return_value=True)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_cell)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_to_top_of_column(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_COLUMN_TOP,
)
def test_move_to_top_of_column_successful(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_top_of_column successfully moves to top of column and presents it."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_top_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXTable.is_top_of_column", return_value=False)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_top_of_column",
return_value=mock_top_cell,
)
def mock_get_cell_coordinates(cell, prefer_attribute=False): # pylint: disable=unused-argument
if cell == mock_current_cell:
return (3, 1)
if cell == mock_top_cell:
return (0, 1)
return (0, 0)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_coordinates",
new=mock_get_cell_coordinates,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
mock_present_cell = test_context.Mock()
navigator._present_cell = mock_present_cell
mock_script = test_context.Mock()
mock_event = test_context.Mock()
result = navigator.move_to_top_of_column(mock_script, mock_event)
assert result is True
mock_present_cell.assert_called_once_with(
mock_script,
mock_top_cell,
3,
1,
mock_current_cell,
True,
)
def test_move_to_bottom_of_column_not_in_table(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_bottom_of_column presents not in table message when current cell is None."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=None)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_to_bottom_of_column(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
def test_move_to_bottom_of_column_already_at_bottom(
self,
test_context: CthulhuTestContext,
) -> None:
"""Test move_to_bottom_of_column presents column bottom message when already at bottom."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch(
"cthulhu.table_navigator.AXTable.is_bottom_of_column",
return_value=True,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_cell)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_to_bottom_of_column(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_COLUMN_BOTTOM,
)
def test_move_to_bottom_of_column_successful(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_bottom_of_column successfully moves to bottom of column and presents it."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_bottom_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch(
"cthulhu.table_navigator.AXTable.is_bottom_of_column",
return_value=False,
)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_bottom_of_column",
return_value=mock_bottom_cell,
)
def mock_get_cell_coordinates(cell, prefer_attribute=False): # pylint: disable=unused-argument
if cell == mock_current_cell:
return (1, 2)
if cell == mock_bottom_cell:
return (4, 2)
return (0, 0)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_coordinates",
new=mock_get_cell_coordinates,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
mock_present_cell = test_context.Mock()
navigator._present_cell = mock_present_cell
mock_script = test_context.Mock()
mock_event = test_context.Mock()
result = navigator.move_to_bottom_of_column(mock_script, mock_event)
assert result is True
mock_present_cell.assert_called_once_with(
mock_script,
mock_bottom_cell,
1,
2,
mock_current_cell,
True,
)
def test_move_to_beginning_of_row_not_in_table(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_beginning_of_row presents not in table message when current cell is None."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=None)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_to_beginning_of_row(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
def test_move_to_beginning_of_row_already_at_start(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_beginning_of_row presents row beginning message when already at start."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXTable.is_start_of_row", return_value=True)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_cell)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_to_beginning_of_row(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_ROW_BEGINNING,
)
def test_move_to_beginning_of_row_successful(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_beginning_of_row successfully moves to beginning of row and presents it."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_start_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXTable.is_start_of_row", return_value=False)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_start_of_row",
return_value=mock_start_cell,
)
def mock_get_cell_coordinates(cell, prefer_attribute=False): # pylint: disable=unused-argument
if cell == mock_start_cell:
return (2, 0)
return (2, 3)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_coordinates",
new=mock_get_cell_coordinates,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
mock_present_cell = test_context.Mock()
navigator._present_cell = mock_present_cell
mock_script = test_context.Mock()
mock_event = test_context.Mock()
result = navigator.move_to_beginning_of_row(mock_script, mock_event)
assert result is True
mock_present_cell.assert_called_once_with(
mock_script,
mock_start_cell,
2,
0,
mock_current_cell,
True,
)
def test_move_to_end_of_row_not_in_table(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_end_of_row presents not in table message when current cell is None."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=None)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_to_end_of_row(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
def test_move_to_end_of_row_already_at_end(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_end_of_row presents row end message when already at end."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXTable.is_end_of_row", return_value=True)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_cell)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.move_to_end_of_row(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_ROW_END,
)
def test_move_to_end_of_row_successful(self, test_context: CthulhuTestContext) -> None:
"""Test move_to_end_of_row successfully moves to end of row and presents it."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_end_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXTable.is_end_of_row", return_value=False)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_end_of_row",
return_value=mock_end_cell,
)
def mock_get_cell_coordinates(cell, prefer_attribute=False): # pylint: disable=unused-argument
if cell == mock_end_cell:
return (2, 4)
return (2, 1)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_coordinates",
new=mock_get_cell_coordinates,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
mock_present_cell = test_context.Mock()
navigator._present_cell = mock_present_cell
mock_script = test_context.Mock()
mock_event = test_context.Mock()
result = navigator.move_to_end_of_row(mock_script, mock_event)
assert result is True
mock_present_cell.assert_called_once_with(
mock_script,
mock_end_cell,
2,
4,
mock_current_cell,
True,
)
def test_set_dynamic_column_headers_row_not_in_table(
self,
test_context: CthulhuTestContext,
) -> None:
"""Test set_dynamic_column_headers_row presents not in table message.
Test case when current cell is None.
"""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=None)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.set_dynamic_column_headers_row(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
def test_set_dynamic_column_headers_row_successful(self, test_context: CthulhuTestContext) -> None:
"""Test set_dynamic_column_headers_row successfully sets column headers row."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_table = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXUtilities.get_table", return_value=mock_table)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_coordinates",
return_value=(1, 2),
)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.set_dynamic_column_headers_row",
return_value=None,
)
essential_modules["cthulhu.messages"].DYNAMIC_COLUMN_HEADER_SET = "Column header row set to %d"
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.set_dynamic_column_headers_row(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with("Column header row set to 2")
def test_clear_dynamic_column_headers_row_not_in_table(
self,
test_context: CthulhuTestContext,
) -> None:
"""Test clear_dynamic_column_headers_row presents not in table message.
Test case when current cell is None.
"""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=None)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.clear_dynamic_column_headers_row(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
def test_clear_dynamic_column_headers_row_successful(
self,
test_context: CthulhuTestContext,
) -> None:
"""Test clear_dynamic_column_headers_row successfully clears column headers row."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_table = test_context.Mock(spec=Atspi.Accessible)
mock_focus_manager = test_context.Mock()
essential_modules["cthulhu.focus_manager"].get_manager.return_value = mock_focus_manager
mock_focus_manager.get_locus_of_focus.return_value = mock_current_cell
test_context.patch("cthulhu.table_navigator.AXUtilities.get_table", return_value=mock_table)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.clear_dynamic_column_headers_row",
return_value=None,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
pres_manager.interrupt_presentation.reset_mock()
result = navigator.clear_dynamic_column_headers_row(mock_script, mock_event)
assert result is True
pres_manager.interrupt_presentation.assert_called_once()
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].DYNAMIC_COLUMN_HEADER_CLEARED,
)
def test_set_dynamic_row_headers_column_not_in_table(
self,
test_context: CthulhuTestContext,
) -> None:
"""Test set_dynamic_row_headers_column presents not in table message.
Test case when current cell is None.
"""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=None)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.set_dynamic_row_headers_column(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
def test_set_dynamic_row_headers_column_successful(self, test_context: CthulhuTestContext) -> None:
"""Test set_dynamic_row_headers_column successfully sets row headers column."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_table = test_context.Mock(spec=Atspi.Accessible)
test_context.patch("cthulhu.table_navigator.AXUtilities.get_table", return_value=mock_table)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_coordinates",
return_value=(2, 1),
)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.set_dynamic_row_headers_column",
return_value=None,
)
essential_modules["cthulhu.messages"].DYNAMIC_ROW_HEADER_SET = "Row header column set to %s"
test_context.patch(
"cthulhu.table_navigator.AXUtilities.get_column_label",
create=True,
return_value="B",
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.set_dynamic_row_headers_column(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with("Row header column set to B")
def test_clear_dynamic_row_headers_column_not_in_table(
self,
test_context: CthulhuTestContext,
) -> None:
"""Test clear_dynamic_row_headers_column presents not in table message.
Test case when current cell is None.
"""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=None)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
result = navigator.clear_dynamic_row_headers_column(mock_script, mock_event)
assert result is True
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].TABLE_NOT_IN_A,
)
def test_clear_dynamic_row_headers_column_successful(
self,
test_context: CthulhuTestContext,
) -> None:
"""Test clear_dynamic_row_headers_column successfully clears row headers column."""
essential_modules = self._setup_dependencies(test_context)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
mock_current_cell = test_context.Mock(spec=Atspi.Accessible)
mock_table = test_context.Mock(spec=Atspi.Accessible)
mock_focus_manager = test_context.Mock()
essential_modules["cthulhu.focus_manager"].get_manager.return_value = mock_focus_manager
mock_focus_manager.get_locus_of_focus.return_value = mock_current_cell
test_context.patch("cthulhu.table_navigator.AXUtilities.get_table", return_value=mock_table)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.clear_dynamic_row_headers_column",
return_value=None,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._get_current_cell = test_context.Mock(return_value=mock_current_cell)
mock_script = test_context.Mock()
mock_event = test_context.Mock()
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
pres_manager.interrupt_presentation.reset_mock()
result = navigator.clear_dynamic_row_headers_column(mock_script, mock_event)
assert result is True
pres_manager.interrupt_presentation.assert_called_once()
pres_manager.present_message.assert_called_once_with(
essential_modules["cthulhu.messages"].DYNAMIC_ROW_HEADER_CLEARED,
)
def test_get_cell_coordinates_with_different_previous_cell(
self,
test_context: CthulhuTestContext,
) -> None:
"""Test _get_cell_coordinates returns actual coordinates when previous cell differs."""
essential_modules = self._setup_dependencies(test_context)
mock_cell = test_context.Mock(spec=Atspi.Accessible)
mock_table = test_context.Mock(spec=Atspi.Accessible)
mock_different_cell = test_context.Mock(spec=Atspi.Accessible)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_coordinates",
return_value=(2, 3),
)
test_context.patch("cthulhu.table_navigator.AXUtilities.get_table", return_value=mock_table)
test_context.patch(
"cthulhu.table_navigator.AXTable.get_cell_at",
return_value=mock_different_cell,
)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
navigator._previous_reported_row = 1
navigator._previous_reported_col = 2
result = navigator._get_cell_coordinates(mock_cell)
assert result == (2, 3)
def test_present_cell_with_settings_enabled(self, test_context: CthulhuTestContext) -> None:
"""Test _present_cell with speakCellCoordinates and speakCellSpan enabled."""
essential_modules = self._setup_dependencies(test_context)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_table_cell_or_header",
return_value=True,
)
test_context.patch("cthulhu.table_navigator.AXObject.grab_focus", return_value=None)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.get_descendant_supporting_text", return_value=None
)
test_context.patch("cthulhu.table_navigator.AXObject.supports_text", return_value=False)
test_context.patch("cthulhu.table_navigator.AXTable.get_cell_spans", return_value=(2, 3))
mock_focus_manager = test_context.Mock()
essential_modules["cthulhu.focus_manager"].get_manager.return_value = mock_focus_manager
essential_modules["cthulhu.messages"].TABLE_CELL_COORDINATES = "Row %(row)s, column %(column)s"
essential_modules["cthulhu.messages"].cell_span.return_value = "spans 2 rows and 3 columns"
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
mock_script = test_context.Mock()
mock_script.utilities.grab_focus_when_setting_caret.return_value = False
test_context.patch("cthulhu.table_navigator.AXUtilities.is_gui_cell", return_value=False)
mock_script.present_object = test_context.Mock()
mock_cell = test_context.Mock(spec=Atspi.Accessible)
mock_previous_cell = test_context.Mock(spec=Atspi.Accessible)
pres_manager = essential_modules["cthulhu.presentation_manager"].get_manager()
pres_manager.present_message.reset_mock()
navigator._present_cell(mock_script, mock_cell, 1, 2, mock_previous_cell)
assert navigator._previous_reported_row == 1
assert navigator._previous_reported_col == 2
mock_focus_manager.set_locus_of_focus.assert_called()
mock_script.present_object.assert_called_once_with(
mock_cell,
offset=0,
priorObj=mock_previous_cell,
interrupt=True,
)
assert pres_manager.present_message.call_count == 2
def test_present_cell_with_text_descendant_and_gui_cell(
self,
test_context: CthulhuTestContext,
) -> None:
"""Test _present_cell when cell has text descendant and is gui cell."""
essential_modules = self._setup_dependencies(test_context)
mock_text_obj = test_context.Mock(spec=Atspi.Accessible)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_table_cell_or_header",
return_value=True,
)
test_context.patch("cthulhu.table_navigator.AXObject.grab_focus", return_value=None)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.get_descendant_supporting_text",
return_value=mock_text_obj,
)
test_context.patch(
"cthulhu.table_navigator.AXObject.supports_text",
side_effect=lambda obj: obj == mock_text_obj,
)
test_context.patch("cthulhu.table_navigator.AXTable.get_cell_spans", return_value=(1, 1))
mock_focus_manager = test_context.Mock()
essential_modules["cthulhu.focus_manager"].get_manager.return_value = mock_focus_manager
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu import gsettings_registry
from cthulhu.table_navigator import TableNavigator
registry = gsettings_registry.get_registry()
registry.set_runtime_value("speech", "announce-cell-coordinates", False)
registry.set_runtime_value("speech", "announce-cell-span", False)
navigator = TableNavigator()
mock_script = test_context.Mock()
mock_script.utilities.grab_focus_when_setting_caret.return_value = True
test_context.patch("cthulhu.table_navigator.AXUtilities.is_gui_cell", return_value=True)
mock_script.utilities.set_caret_position = test_context.Mock()
mock_script.present_object = test_context.Mock()
mock_cell = test_context.Mock(spec=Atspi.Accessible)
mock_previous_cell = test_context.Mock(spec=Atspi.Accessible)
navigator._present_cell(mock_script, mock_cell, 1, 2, mock_previous_cell)
mock_focus_manager.set_locus_of_focus.assert_called_with(None, mock_text_obj, False)
assert navigator._previous_reported_row == 1
assert navigator._previous_reported_col == 2
def test_get_skip_blank_cells(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.get_skip_blank_cells returns setting value."""
self._setup_dependencies(test_context)
from cthulhu import gsettings_registry
from cthulhu.table_navigator import get_navigator
gsettings_registry.get_registry().set_runtime_value(
"table-navigation",
"skip-blank-cells",
True,
)
nav = get_navigator()
result = nav.get_skip_blank_cells()
assert result is True
def test_set_skip_blank_cells(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.set_skip_blank_cells updates setting."""
self._setup_dependencies(test_context)
from cthulhu.table_navigator import get_navigator
nav = get_navigator()
result = nav.set_skip_blank_cells(True)
assert result is True
assert nav.get_skip_blank_cells() is True
def test_set_skip_blank_cells_no_change(self, test_context: CthulhuTestContext) -> None:
"""Test TableNavigator.set_skip_blank_cells returns early if value unchanged."""
self._setup_dependencies(test_context)
from cthulhu import gsettings_registry
from cthulhu.table_navigator import get_navigator
gsettings_registry.get_registry().set_runtime_value(
"table-navigation",
"skip-blank-cells",
True,
)
nav = get_navigator()
result = nav.set_skip_blank_cells(True)
assert result is True
def test_present_cell_emits_region_changed(self, test_context: CthulhuTestContext) -> None:
"""Test _present_cell emits region_changed with TABLE_NAVIGATOR mode."""
essential_modules = self._setup_dependencies(test_context)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.is_table_cell_or_header",
return_value=True,
)
test_context.patch("cthulhu.table_navigator.AXObject.grab_focus", return_value=None)
test_context.patch(
"cthulhu.table_navigator.AXUtilities.get_descendant_supporting_text", return_value=None
)
test_context.patch("cthulhu.table_navigator.AXObject.supports_text", return_value=False)
test_context.patch("cthulhu.table_navigator.AXTable.get_cell_spans", return_value=(1, 1))
mock_focus_manager = test_context.Mock()
essential_modules["cthulhu.focus_manager"].get_manager.return_value = mock_focus_manager
from cthulhu import focus_manager, gsettings_registry
essential_modules["cthulhu.focus_manager"].TABLE_NAVIGATOR = focus_manager.TABLE_NAVIGATOR
registry = gsettings_registry.get_registry()
registry.set_runtime_value("speech", "announce-cell-coordinates", False)
registry.set_runtime_value("speech", "announce-cell-span", False)
mock_controller = test_context.Mock()
essential_modules["cthulhu.dbus_service"].get_remote_controller.return_value = mock_controller
mock_keybindings_class = test_context.Mock()
mock_keybindings_instance = test_context.Mock()
mock_keybindings_class.return_value = mock_keybindings_instance
test_context.patch(
"cthulhu.table_navigator.keybindings.KeyBindings",
new=mock_keybindings_class,
)
from cthulhu.table_navigator import TableNavigator
navigator = TableNavigator()
mock_script = test_context.Mock()
mock_script.utilities.grab_focus_when_setting_caret.return_value = False
test_context.patch("cthulhu.table_navigator.AXUtilities.is_gui_cell", return_value=False)
mock_script.present_object = test_context.Mock()
mock_cell = test_context.Mock(spec=Atspi.Accessible)
mock_previous_cell = test_context.Mock(spec=Atspi.Accessible)
navigator._present_cell(mock_script, mock_cell, 1, 2, mock_previous_cell)
mock_focus_manager.emit_region_changed.assert_called()
call_kwargs = mock_focus_manager.emit_region_changed.call_args
assert call_kwargs.kwargs.get("mode") == focus_manager.TABLE_NAVIGATOR