Latest Codex app exposed a bug where redrawing the screen could cause speech to slip through even when it's turned off. Hopefully fixed now.
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
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()
|
||||
@@ -142,6 +142,37 @@ def test_progress_detector_beeps_for_interruptible_status_without_ellipsis():
|
||||
command.play_activity_beep.assert_called_once_with()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_progress_detector_beeps_for_plain_working_elapsed_status():
|
||||
progress_module = _load_progress_module()
|
||||
command = progress_module.command()
|
||||
sample = "Working (21s • esc to interrupt)"
|
||||
command.env = {
|
||||
"commandBuffer": {
|
||||
"progress_monitoring": True,
|
||||
"lastProgressValue": -1,
|
||||
"lastProgressTime": 0,
|
||||
},
|
||||
"runtime": {
|
||||
"DebugManager": Mock(write_debug_out=Mock()),
|
||||
"ScreenManager": Mock(is_screen_change=Mock(return_value=False)),
|
||||
"CursorManager": Mock(is_cursor_vertical_move=Mock(return_value=False)),
|
||||
},
|
||||
"screen": {
|
||||
"new_delta": sample,
|
||||
"new_delta_is_typing": False,
|
||||
"new_content_text": sample,
|
||||
"old_cursor": {"x": 0, "y": 0},
|
||||
"new_cursor": {"x": 0, "y": 0},
|
||||
},
|
||||
}
|
||||
command.play_activity_beep = Mock()
|
||||
|
||||
command.run()
|
||||
|
||||
command.play_activity_beep.assert_called_once_with()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_progress_detector_allows_long_key_value_status_delta():
|
||||
progress_module = _load_progress_module()
|
||||
|
||||
@@ -160,7 +160,22 @@ def test_pty_raw_tab_records_recent_tab_keypress():
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_pty_plain_stdin_does_not_record_tab_keypress():
|
||||
@pytest.mark.parametrize(
|
||||
("sequence", "key_name"),
|
||||
[
|
||||
(b"a", "KEY_A"),
|
||||
(b"\x7f", "KEY_BACKSPACE"),
|
||||
(b"\x1b[D", "KEY_LEFT"),
|
||||
(b"\x1bOD", "KEY_LEFT"),
|
||||
(b"\x1b[C", "KEY_RIGHT"),
|
||||
(b"\x1bOC", "KEY_RIGHT"),
|
||||
(b"\x1b[A", "KEY_UP"),
|
||||
(b"\x1bOA", "KEY_UP"),
|
||||
(b"\x1b[B", "KEY_DOWN"),
|
||||
(b"\x1bOB", "KEY_DOWN"),
|
||||
],
|
||||
)
|
||||
def test_pty_forwarded_stdin_records_recent_terminal_input(sequence, key_name):
|
||||
pty_driver = PtyDriver()
|
||||
settings_manager = Mock()
|
||||
settings_manager.get_setting_as_bool.return_value = False
|
||||
@@ -175,10 +190,10 @@ def test_pty_plain_stdin_does_not_record_tab_keypress():
|
||||
}
|
||||
pty_driver.inject_text_to_screen = Mock()
|
||||
|
||||
pty_driver.handle_stdin_input(b"a", Mock())
|
||||
pty_driver.handle_stdin_input(sequence, Mock())
|
||||
|
||||
input_manager.record_unmanaged_keypress.assert_not_called()
|
||||
pty_driver.inject_text_to_screen.assert_called_once_with(b"a")
|
||||
input_manager.record_unmanaged_keypress.assert_called_once_with(key_name)
|
||||
pty_driver.inject_text_to_screen.assert_called_once_with(sequence)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
|
||||
@@ -531,12 +531,27 @@ def test_x11_normal_focus_out_resets_input_state():
|
||||
x11 = X11Driver()
|
||||
x11.active = True
|
||||
x11.reset_input_state = Mock()
|
||||
output_manager = Mock()
|
||||
x11.env = {"runtime": {"OutputManager": output_manager}}
|
||||
event = Mock(type=X.FocusOut, mode=X.NotifyNormal)
|
||||
|
||||
x11.handle_x_event(event, Mock())
|
||||
|
||||
assert x11.active is False
|
||||
x11.reset_input_state.assert_called_once_with()
|
||||
output_manager.interrupt_output_async.assert_called_once_with()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_x11_reports_target_focus_state():
|
||||
x11 = X11Driver()
|
||||
x11.active = False
|
||||
|
||||
assert x11.is_active() is False
|
||||
|
||||
x11.active = True
|
||||
|
||||
assert x11.is_active() is True
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
|
||||
Reference in New Issue
Block a user