import time from unittest.mock import Mock, call import pytest from fenrirscreenreader.core.fenrirManager import FenrirManager def _build_manager(input_active=True, deepest_input=None): manager = FenrirManager.__new__(FenrirManager) input_manager = Mock( get_last_input_time=Mock(return_value=time.time()), get_last_deepest_input=Mock(return_value=deepest_input or []), ) command_manager = Mock(execute_default_trigger=Mock()) screen_manager = Mock(handle_screen_update=Mock()) cursor_manager = Mock( is_cursor_vertical_move=Mock(return_value=True), is_cursor_horizontal_move=Mock(return_value=False), ) manager.environment = { "runtime": { "InputDriver": Mock(is_active=Mock(return_value=input_active)), "InputManager": input_manager, "ScreenManager": screen_manager, "CursorManager": cursor_manager, "CommandManager": command_manager, } } return manager, screen_manager, input_manager, command_manager @pytest.mark.unit def test_screen_update_while_x11_target_is_unfocused_updates_state_silently(): manager, screen_manager, input_manager, command_manager = _build_manager( input_active=False, deepest_input=["KEY_DOWN"], ) event = {"data": {"text": "background repaint"}} manager.handle_screen_update(event) screen_manager.handle_screen_update.assert_called_once_with(event["data"]) command_manager.execute_default_trigger.assert_not_called() input_manager.clear_last_deep_input.assert_called_once_with() @pytest.mark.unit def test_programmatic_cursor_move_does_not_run_cursor_change_triggers(): manager, _screen_manager, _input_manager, command_manager = _build_manager( deepest_input=[] ) manager.handle_screen_update({"data": {"text": "spinner repaint"}}) command_manager.execute_default_trigger.assert_called_once_with( "onScreenUpdate" ) @pytest.mark.unit def test_navigation_cursor_move_runs_cursor_change_triggers(): manager, _screen_manager, _input_manager, command_manager = _build_manager( deepest_input=["KEY_DOWN"] ) manager.handle_screen_update({"data": {"text": "cursor moved"}}) assert command_manager.execute_default_trigger.call_args_list == [ call("onCursorChange"), call("onScreenUpdate"), ] @pytest.mark.unit @pytest.mark.parametrize("key_name", ["KEY_RIGHT", "KEY_BACKSPACE"]) def test_recent_cursor_input_survives_update_before_cursor_moves(key_name): manager, _screen_manager, input_manager, command_manager = _build_manager( deepest_input=[key_name] ) manager.environment["runtime"][ "CursorManager" ].is_cursor_vertical_move.return_value = False manager.environment["runtime"][ "CursorManager" ].is_cursor_horizontal_move.return_value = False manager.handle_screen_update({"data": {"text": "intermediate repaint"}}) command_manager.execute_default_trigger.assert_called_once_with( "onScreenUpdate" ) input_manager.clear_last_deep_input.assert_not_called() @pytest.mark.unit def test_fenrir_review_command_does_not_enable_screen_cursor_speech(): manager, _screen_manager, _input_manager, command_manager = _build_manager( deepest_input=["KEY_FENRIR", "KEY_KP8"] ) manager.handle_screen_update({"data": {"text": "background repaint"}}) command_manager.execute_default_trigger.assert_called_once_with( "onScreenUpdate" ) @pytest.mark.unit def test_heartbeat_triggers_are_silent_while_x11_target_is_unfocused(): manager, _screen_manager, _input_manager, command_manager = _build_manager( input_active=False ) manager.handle_heart_beat({"data": None}) command_manager.execute_default_trigger.assert_not_called()