Speech history added, bound to fenrir+control+h.
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
|
||||
class command:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return _("opens speech history")
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["SpeechHistoryManager"].open_history()
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
|
||||
class command:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return _("closes speech history")
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["SpeechHistoryManager"].close_history()
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
|
||||
class command:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return _("copies current speech history item to the clipboard")
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["SpeechHistoryManager"].copy_current_to_clipboard()
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
|
||||
class command:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return _("speaks current speech history item")
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["SpeechHistoryManager"].present_current()
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
|
||||
class command:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return _("selects the next speech history item")
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["SpeechHistoryManager"].next_entry()
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
|
||||
class command:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def get_description(self):
|
||||
return _("selects the previous speech history item")
|
||||
|
||||
def run(self):
|
||||
self.env["runtime"]["SpeechHistoryManager"].prev_entry()
|
||||
|
||||
def set_callback(self, callback):
|
||||
pass
|
||||
@@ -103,6 +103,10 @@ class FenrirManager:
|
||||
self.environment["runtime"][
|
||||
"InputManager"
|
||||
].clear_event_buffer()
|
||||
if self.environment["runtime"]["SpeechHistoryManager"].is_active():
|
||||
self.environment["runtime"][
|
||||
"InputManager"
|
||||
].clear_event_buffer()
|
||||
|
||||
self.detect_shortcut_command()
|
||||
|
||||
@@ -159,6 +163,14 @@ class FenrirManager:
|
||||
current_command, "vmenu-navigation"
|
||||
)
|
||||
return
|
||||
elif self.environment["runtime"]["SpeechHistoryManager"].is_active():
|
||||
if self.environment["runtime"]["CommandManager"].command_exists(
|
||||
current_command, "speech-history"
|
||||
):
|
||||
self.environment["runtime"]["CommandManager"].execute_command(
|
||||
current_command, "speech-history"
|
||||
)
|
||||
return
|
||||
|
||||
# default
|
||||
self.environment["runtime"]["CommandManager"].execute_command(
|
||||
@@ -298,12 +310,18 @@ class FenrirManager:
|
||||
if self.command != "":
|
||||
self.singleKeyCommand = True
|
||||
elif (
|
||||
self.environment["runtime"]["DiffReviewManager"].is_active()
|
||||
(
|
||||
self.environment["runtime"]["DiffReviewManager"].is_active()
|
||||
or self.environment["runtime"][
|
||||
"SpeechHistoryManager"
|
||||
].is_active()
|
||||
)
|
||||
and self.command != ""
|
||||
):
|
||||
# Diff mode uses non-Fenrir modified bindings (Shift/Ctrl).
|
||||
# Modal modes use non-Fenrir modified bindings.
|
||||
# Promote resolved shortcuts to executable commands so
|
||||
# combinations like Shift+H and Ctrl+Right are dispatched.
|
||||
# combinations like Shift+H, Ctrl+Right, and plain arrows
|
||||
# are dispatched.
|
||||
self.singleKeyCommand = True
|
||||
|
||||
if not (self.singleKeyCommand or self.modifierInput):
|
||||
|
||||
@@ -20,6 +20,7 @@ general_data = {
|
||||
"ScreenManager",
|
||||
"InputManager",
|
||||
"OutputManager",
|
||||
"SpeechHistoryManager",
|
||||
"HelpManager",
|
||||
"MemoryManager",
|
||||
"EventManager",
|
||||
@@ -48,5 +49,6 @@ general_data = {
|
||||
"onSwitchApplicationProfile",
|
||||
"help",
|
||||
"vmenu-navigation",
|
||||
"speech-history",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -75,6 +75,14 @@ class OutputManager:
|
||||
return
|
||||
if (len(text) > 1) and (text.strip(string.whitespace) == ""):
|
||||
return
|
||||
if self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
||||
"speech", "enabled"
|
||||
):
|
||||
speech_history_manager = self.env["runtime"].get(
|
||||
"SpeechHistoryManager"
|
||||
)
|
||||
if speech_history_manager:
|
||||
speech_history_manager.add_text(text)
|
||||
is_capital = self._should_announce_capital(text, announce_capital)
|
||||
use_pitch_for_capital = False
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ runtime_data = {
|
||||
"CommandManager": None,
|
||||
"ScreenManager": None,
|
||||
"OutputManager": None,
|
||||
"SpeechHistoryManager": None,
|
||||
"DebugManager": None,
|
||||
"SettingsManager": None,
|
||||
"FenrirManager": None,
|
||||
|
||||
@@ -32,6 +32,7 @@ settings_data = {
|
||||
"hardware_baud_rate": 9600,
|
||||
"auto_read_incoming": True,
|
||||
"read_numbers_as_digits": False,
|
||||
"history_size": 50,
|
||||
"rapid_update_threshold": 5,
|
||||
"rapid_update_window": 0.3,
|
||||
"batch_flush_interval": 0.5,
|
||||
|
||||
@@ -29,6 +29,7 @@ from fenrirscreenreader.core import readAllManager
|
||||
from fenrirscreenreader.core import remoteManager
|
||||
from fenrirscreenreader.core import sayAllManager
|
||||
from fenrirscreenreader.core import screenManager
|
||||
from fenrirscreenreader.core import speechHistoryManager
|
||||
from fenrirscreenreader.core import tableManager
|
||||
from fenrirscreenreader.core import textManager
|
||||
from fenrirscreenreader.core import vmenuManager
|
||||
@@ -738,6 +739,11 @@ class SettingsManager:
|
||||
environment["runtime"]["OutputManager"] = outputManager.OutputManager()
|
||||
environment["runtime"]["OutputManager"].initialize(environment)
|
||||
|
||||
environment["runtime"][
|
||||
"SpeechHistoryManager"
|
||||
] = speechHistoryManager.SpeechHistoryManager()
|
||||
environment["runtime"]["SpeechHistoryManager"].initialize(environment)
|
||||
|
||||
environment["runtime"]["InputManager"] = inputManager.InputManager()
|
||||
environment["runtime"]["InputManager"].initialize(environment)
|
||||
self.load_keyboard_layout(environment)
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Fenrir TTY screen reader
|
||||
# By Chrys, Storm Dragon, and contributors.
|
||||
|
||||
from fenrirscreenreader.core.i18n import _
|
||||
|
||||
|
||||
class SpeechHistoryManager:
|
||||
def __init__(self):
|
||||
self.env = None
|
||||
self.history = []
|
||||
self.curr_index = -1
|
||||
self.active = False
|
||||
self.bindings_backup = None
|
||||
self.raw_bindings_backup = None
|
||||
|
||||
def initialize(self, environment):
|
||||
self.env = environment
|
||||
|
||||
def shutdown(self):
|
||||
self.set_active(False)
|
||||
|
||||
def is_active(self):
|
||||
return self.active
|
||||
|
||||
def add_text(self, text):
|
||||
if self.active:
|
||||
return False
|
||||
if not isinstance(text, str):
|
||||
return False
|
||||
if text == "":
|
||||
return False
|
||||
text_key = self._get_history_key(text)
|
||||
if text_key == "":
|
||||
return False
|
||||
if text_key in [self._get_history_key(item) for item in self.history]:
|
||||
return False
|
||||
history_size = self._get_history_size()
|
||||
if history_size <= 0:
|
||||
return False
|
||||
self.history.insert(0, text)
|
||||
del self.history[history_size:]
|
||||
if self.curr_index >= len(self.history):
|
||||
self.curr_index = len(self.history) - 1
|
||||
return True
|
||||
|
||||
def open_history(self):
|
||||
if not self.history:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("speech history empty"), interrupt=True
|
||||
)
|
||||
return False
|
||||
self.curr_index = -1
|
||||
self.set_active(True)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Speech history"), interrupt=True
|
||||
)
|
||||
return True
|
||||
|
||||
def close_history(self, announce=True):
|
||||
if announce:
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("speech history closed"), interrupt=True
|
||||
)
|
||||
self.set_active(False)
|
||||
|
||||
def next_entry(self):
|
||||
if not self._has_history():
|
||||
return
|
||||
if self.curr_index == -1:
|
||||
self.curr_index = 0
|
||||
self.present_current()
|
||||
return
|
||||
if self.curr_index <= 0:
|
||||
self.curr_index = 0
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("First speech history item"), interrupt=True
|
||||
)
|
||||
self.present_current(interrupt=False)
|
||||
return
|
||||
self.curr_index -= 1
|
||||
self.present_current()
|
||||
|
||||
def prev_entry(self):
|
||||
if not self._has_history():
|
||||
return
|
||||
if self.curr_index == -1:
|
||||
self.curr_index = 0
|
||||
self.present_current()
|
||||
return
|
||||
if self.curr_index >= len(self.history) - 1:
|
||||
self.curr_index = len(self.history) - 1
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("Last speech history item"), interrupt=True
|
||||
)
|
||||
self.present_current(interrupt=False)
|
||||
return
|
||||
self.curr_index += 1
|
||||
self.present_current()
|
||||
|
||||
def present_current(self, interrupt=True):
|
||||
if not self._has_history():
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("speech history empty"), interrupt=True
|
||||
)
|
||||
return
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
self.history[self.curr_index], interrupt=interrupt
|
||||
)
|
||||
|
||||
def copy_current_to_clipboard(self):
|
||||
if not self._has_history():
|
||||
self.close_history()
|
||||
return
|
||||
text = self.history[self.curr_index]
|
||||
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
||||
"clipboardHistory", text
|
||||
)
|
||||
self.env["runtime"]["OutputManager"].present_text(
|
||||
_("copied to clipboard"),
|
||||
sound_icon="CopyToClipboard",
|
||||
interrupt=True,
|
||||
)
|
||||
self.set_active(False)
|
||||
|
||||
def set_active(self, active):
|
||||
if active == self.active:
|
||||
return
|
||||
self.active = active
|
||||
if self.active:
|
||||
self._install_bindings()
|
||||
else:
|
||||
self._restore_bindings()
|
||||
|
||||
def _has_history(self):
|
||||
if not self.history:
|
||||
self.curr_index = -1
|
||||
return False
|
||||
if self.curr_index >= len(self.history):
|
||||
self.curr_index = len(self.history) - 1
|
||||
return True
|
||||
|
||||
def _get_history_size(self):
|
||||
try:
|
||||
return self.env["runtime"]["SettingsManager"].get_setting_as_int(
|
||||
"speech", "history_size"
|
||||
)
|
||||
except Exception:
|
||||
return 50
|
||||
|
||||
def _get_history_key(self, text):
|
||||
return " ".join(text.split())
|
||||
|
||||
def _install_bindings(self):
|
||||
self.bindings_backup = self.env["bindings"].copy()
|
||||
self.raw_bindings_backup = self.env["rawBindings"].copy()
|
||||
self.env["bindings"] = {
|
||||
str([1, ["KEY_UP"]]): "SPEECH_HISTORY_PREV",
|
||||
str([1, ["KEY_DOWN"]]): "SPEECH_HISTORY_NEXT",
|
||||
str([1, ["KEY_SPACE"]]): "SPEECH_HISTORY_CURRENT",
|
||||
str([1, ["KEY_ENTER"]]): "SPEECH_HISTORY_COPY",
|
||||
str([1, ["KEY_KPENTER"]]): "SPEECH_HISTORY_COPY",
|
||||
str([1, ["KEY_ESC"]]): "SPEECH_HISTORY_CLOSE",
|
||||
}
|
||||
self.env["rawBindings"] = {
|
||||
str([1, ["KEY_UP"]]): [1, ["KEY_UP"]],
|
||||
str([1, ["KEY_DOWN"]]): [1, ["KEY_DOWN"]],
|
||||
str([1, ["KEY_SPACE"]]): [1, ["KEY_SPACE"]],
|
||||
str([1, ["KEY_ENTER"]]): [1, ["KEY_ENTER"]],
|
||||
str([1, ["KEY_KPENTER"]]): [1, ["KEY_KPENTER"]],
|
||||
str([1, ["KEY_ESC"]]): [1, ["KEY_ESC"]],
|
||||
}
|
||||
|
||||
def _restore_bindings(self):
|
||||
if self.bindings_backup is not None:
|
||||
self.env["bindings"] = self.bindings_backup
|
||||
if self.raw_bindings_backup is not None:
|
||||
self.env["rawBindings"] = self.raw_bindings_backup
|
||||
self.bindings_backup = None
|
||||
self.raw_bindings_backup = None
|
||||
@@ -0,0 +1,44 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from fenrirscreenreader.core.eventData import FenrirEventType
|
||||
from fenrirscreenreader.core.fenrirManager import FenrirManager
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_speech_history_plain_key_modal_command_is_dispatched():
|
||||
manager = FenrirManager.__new__(FenrirManager)
|
||||
manager.modifierInput = False
|
||||
manager.singleKeyCommand = False
|
||||
manager.command = ""
|
||||
|
||||
event_manager = Mock(put_to_event_queue=Mock())
|
||||
input_manager = Mock(
|
||||
is_key_press=Mock(return_value=False),
|
||||
no_key_pressed=Mock(return_value=False),
|
||||
get_curr_shortcut=Mock(return_value=str([1, ["KEY_UP"]])),
|
||||
get_command_for_shortcut=Mock(return_value="SPEECH_HISTORY_PREV"),
|
||||
)
|
||||
speech_history_manager = Mock(is_active=Mock(return_value=True))
|
||||
diff_review_manager = Mock(is_active=Mock(return_value=False))
|
||||
|
||||
manager.environment = {
|
||||
"input": {
|
||||
"key_forward": 0,
|
||||
"prev_input": ["KEY_UP"],
|
||||
"curr_input": ["KEY_UP"],
|
||||
},
|
||||
"runtime": {
|
||||
"InputManager": input_manager,
|
||||
"EventManager": event_manager,
|
||||
"DiffReviewManager": diff_review_manager,
|
||||
"SpeechHistoryManager": speech_history_manager,
|
||||
},
|
||||
}
|
||||
|
||||
manager.detect_shortcut_command()
|
||||
|
||||
event_manager.put_to_event_queue.assert_called_once_with(
|
||||
FenrirEventType.execute_command, "SPEECH_HISTORY_PREV"
|
||||
)
|
||||
@@ -164,6 +164,43 @@ def test_speak_text_drops_speech_when_cancel_holds_driver_lock():
|
||||
speech_driver.speak.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_present_text_records_speech_history_when_enabled():
|
||||
output_manager, _sound_driver, speech_driver = build_output_manager()
|
||||
speech_history_manager = Mock(add_text=Mock())
|
||||
output_manager.env["runtime"]["SpeechHistoryManager"] = (
|
||||
speech_history_manager
|
||||
)
|
||||
|
||||
output_manager.present_text("hello history", interrupt=False)
|
||||
|
||||
speech_history_manager.add_text.assert_called_once_with("hello history")
|
||||
speech_driver.speak.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_present_text_does_not_record_when_speech_disabled():
|
||||
output_manager, _sound_driver, speech_driver = build_output_manager()
|
||||
speech_history_manager = Mock(add_text=Mock())
|
||||
output_manager.env["runtime"]["SpeechHistoryManager"] = (
|
||||
speech_history_manager
|
||||
)
|
||||
|
||||
def _get_setting_as_bool(section, setting):
|
||||
if (section, setting) == ("speech", "enabled"):
|
||||
return False
|
||||
return True
|
||||
|
||||
output_manager.env["runtime"][
|
||||
"SettingsManager"
|
||||
].get_setting_as_bool.side_effect = _get_setting_as_bool
|
||||
|
||||
output_manager.present_text("hello history", interrupt=False)
|
||||
|
||||
speech_history_manager.add_text.assert_not_called()
|
||||
speech_driver.speak.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_key_interrupt_command_uses_nonblocking_interrupt():
|
||||
module = load_key_interrupt_module()
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from fenrirscreenreader.core.speechHistoryManager import SpeechHistoryManager
|
||||
|
||||
|
||||
def build_speech_history_manager(history_size=3):
|
||||
spoken_messages = []
|
||||
output_manager = Mock()
|
||||
|
||||
def _capture_message(message, **_kwargs):
|
||||
spoken_messages.append(message)
|
||||
|
||||
output_manager.present_text.side_effect = _capture_message
|
||||
settings_manager = Mock()
|
||||
settings_manager.get_setting_as_int.return_value = history_size
|
||||
memory_manager = Mock(add_value_to_first_index=Mock())
|
||||
env = {
|
||||
"runtime": {
|
||||
"OutputManager": output_manager,
|
||||
"SettingsManager": settings_manager,
|
||||
"MemoryManager": memory_manager,
|
||||
},
|
||||
"bindings": {"original": "COMMAND"},
|
||||
"rawBindings": {"original": [1, ["KEY_FENRIR"]]},
|
||||
}
|
||||
manager = SpeechHistoryManager()
|
||||
manager.initialize(env)
|
||||
return manager, env, spoken_messages, memory_manager
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_speech_history_keeps_configured_number_of_items():
|
||||
manager, _env, _spoken_messages, _memory_manager = (
|
||||
build_speech_history_manager(history_size=2)
|
||||
)
|
||||
|
||||
assert manager.add_text("one")
|
||||
assert manager.add_text("two")
|
||||
assert manager.add_text("three")
|
||||
|
||||
assert manager.history == ["three", "two"]
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_speech_history_suppresses_exact_duplicates_until_item_drops():
|
||||
manager, _env, _spoken_messages, _memory_manager = (
|
||||
build_speech_history_manager(history_size=2)
|
||||
)
|
||||
|
||||
assert manager.add_text("hello world")
|
||||
assert not manager.add_text("hello world")
|
||||
assert manager.add_text("other")
|
||||
assert manager.add_text("third")
|
||||
assert manager.add_text("hello world")
|
||||
|
||||
assert manager.history == ["hello world", "third"]
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_speech_history_dedupe_keeps_case_and_suppresses_whitespace_variants():
|
||||
manager, _env, _spoken_messages, _memory_manager = (
|
||||
build_speech_history_manager()
|
||||
)
|
||||
|
||||
assert manager.add_text("hello")
|
||||
assert manager.add_text("Hello")
|
||||
assert not manager.add_text("hello ")
|
||||
assert not manager.add_text("hello ")
|
||||
|
||||
assert manager.history == ["Hello", "hello"]
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_open_empty_history_announces_empty_without_modal_bindings():
|
||||
manager, env, spoken_messages, _memory_manager = (
|
||||
build_speech_history_manager()
|
||||
)
|
||||
|
||||
assert not manager.open_history()
|
||||
|
||||
assert not manager.is_active()
|
||||
assert spoken_messages == ["speech history empty"]
|
||||
assert env["bindings"] == {"original": "COMMAND"}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_open_history_installs_modal_bindings_and_replay_is_not_recorded():
|
||||
manager, env, spoken_messages, _memory_manager = (
|
||||
build_speech_history_manager()
|
||||
)
|
||||
manager.add_text("first")
|
||||
manager.add_text("second")
|
||||
|
||||
assert manager.open_history()
|
||||
manager.add_text("replayed")
|
||||
|
||||
assert manager.is_active()
|
||||
assert spoken_messages == ["Speech history"]
|
||||
assert manager.curr_index == -1
|
||||
assert manager.history == ["second", "first"]
|
||||
assert "original" not in env["bindings"]
|
||||
assert "original" not in env["rawBindings"]
|
||||
assert env["bindings"][str([1, ["KEY_UP"]])] == "SPEECH_HISTORY_PREV"
|
||||
assert env["bindings"][str([1, ["KEY_ENTER"]])] == "SPEECH_HISTORY_COPY"
|
||||
assert env["bindings"][str([1, ["KEY_ESC"]])] == "SPEECH_HISTORY_CLOSE"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_navigation_moves_between_newer_and_older_items():
|
||||
manager, _env, spoken_messages, _memory_manager = (
|
||||
build_speech_history_manager()
|
||||
)
|
||||
manager.add_text("oldest")
|
||||
manager.add_text("middle")
|
||||
manager.add_text("newest")
|
||||
manager.open_history()
|
||||
|
||||
manager.prev_entry()
|
||||
manager.prev_entry()
|
||||
manager.prev_entry()
|
||||
manager.next_entry()
|
||||
|
||||
assert spoken_messages[-4:] == ["newest", "middle", "oldest", "middle"]
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_copy_current_adds_clipboard_and_restores_bindings():
|
||||
manager, env, spoken_messages, memory_manager = (
|
||||
build_speech_history_manager()
|
||||
)
|
||||
manager.add_text("first")
|
||||
manager.add_text("second")
|
||||
manager.open_history()
|
||||
manager.prev_entry()
|
||||
manager.prev_entry()
|
||||
|
||||
manager.copy_current_to_clipboard()
|
||||
|
||||
memory_manager.add_value_to_first_index.assert_called_once_with(
|
||||
"clipboardHistory", "first"
|
||||
)
|
||||
assert spoken_messages[-1] == "copied to clipboard"
|
||||
assert not manager.is_active()
|
||||
assert env["bindings"] == {"original": "COMMAND"}
|
||||
assert env["rawBindings"] == {"original": [1, ["KEY_FENRIR"]]}
|
||||
Reference in New Issue
Block a user