* fix(settings): gray out Dictation Tone while sound effects are off The Enable Sound Effects switch mutes every dictation cue, so the tone picker changed nothing while it was off. The row now goes insensitive with the switch and follows the state loaded from config, matching the keep-alive timeout and auto-pause sensitivity pattern. Also bump the pre-commit isort pin 5.13.2 -> 8.0.1 to match uv.lock: the two versions disagree on single-name import wrapping, so the old hook collapsed imports the locked isort (and CI) then expanded back. Fixes #849 * test(settings): cover tone-row sensitivity on settings load --------- Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com>
98 lines
3.6 KiB
Python
98 lines
3.6 KiB
Python
"""The Dictation Tone row is grayed out while sound effects are off (#849)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import sys
|
|
from collections.abc import Iterator
|
|
from typing import Any
|
|
from unittest.mock import MagicMock, Mock, patch
|
|
|
|
import pytest
|
|
|
|
import vocalinux.ui
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def settings_dialog() -> Iterator[Any]:
|
|
"""Import settings_dialog with real base classes for its GTK subclasses.
|
|
|
|
Same approach as tests/test_follow_keyboard_layout.py; both sys.modules and
|
|
the package attribute are restored so later tests patching the module by
|
|
name do not reach a second module object (#686).
|
|
"""
|
|
repository = sys.modules["gi.repository"]
|
|
bases = {name: type(name, (), {}) for name in ("Box", "ListBoxRow", "Dialog")}
|
|
saved_module = sys.modules.pop("vocalinux.ui.settings_dialog", None)
|
|
saved_attribute = getattr(vocalinux.ui, "settings_dialog", None)
|
|
try:
|
|
with patch.object(repository, "Gtk", MagicMock(**bases)):
|
|
module = importlib.import_module("vocalinux.ui.settings_dialog")
|
|
yield module
|
|
finally:
|
|
sys.modules.pop("vocalinux.ui.settings_dialog", None)
|
|
if saved_module is not None:
|
|
sys.modules["vocalinux.ui.settings_dialog"] = saved_module
|
|
if saved_attribute is not None:
|
|
vocalinux.ui.settings_dialog = saved_attribute
|
|
elif hasattr(vocalinux.ui, "settings_dialog"):
|
|
del vocalinux.ui.settings_dialog
|
|
|
|
|
|
@pytest.fixture
|
|
def dialog_class(settings_dialog: Any) -> type[Any]:
|
|
return settings_dialog.SettingsDialog
|
|
|
|
|
|
def _dialog_stub(dialog_class: type[Any], initializing: bool = False) -> Mock:
|
|
"""A Mock dialog whose self-calls reach the real sensitivity helper."""
|
|
dialog = Mock()
|
|
dialog._initializing = initializing
|
|
dialog._applying_settings = False
|
|
dialog._update_sound_effects_sensitivity = (
|
|
lambda enabled: dialog_class._update_sound_effects_sensitivity(dialog, enabled)
|
|
)
|
|
return dialog
|
|
|
|
|
|
@pytest.mark.parametrize("enabled", [True, False])
|
|
def test_toggling_sound_effects_sets_tone_row_sensitivity(
|
|
dialog_class: type[Any], enabled: bool
|
|
) -> None:
|
|
dialog = _dialog_stub(dialog_class)
|
|
|
|
dialog_class._on_sound_effects_toggled(dialog, Mock(), enabled)
|
|
|
|
dialog.tone_row.set_sensitive.assert_called_once_with(enabled)
|
|
dialog.config_manager.set_sound_effects_enabled.assert_called_once_with(enabled)
|
|
|
|
|
|
def test_sensitivity_still_updates_while_settings_load(dialog_class: type[Any]) -> None:
|
|
"""set_active() during load must sync the row even though saving is skipped."""
|
|
dialog = _dialog_stub(dialog_class, initializing=True)
|
|
|
|
dialog_class._on_sound_effects_toggled(dialog, Mock(), False)
|
|
|
|
dialog.tone_row.set_sensitive.assert_called_once_with(False)
|
|
dialog.config_manager.set_sound_effects_enabled.assert_not_called()
|
|
|
|
|
|
def test_loading_settings_syncs_tone_row_sensitivity(
|
|
settings_dialog: Any, dialog_class: type[Any]
|
|
) -> None:
|
|
"""A saved sound-effects=off state must gray out the tone row on load."""
|
|
dialog = _dialog_stub(dialog_class)
|
|
dialog._get_current_settings.return_value = {
|
|
"engine": "whisper_cpp",
|
|
"language": "en",
|
|
"model_size": "base",
|
|
}
|
|
dialog.config_manager.get_settings.return_value = {}
|
|
dialog.config_manager.is_sound_effects_enabled.return_value = False
|
|
dialog.engine_combo.get_model.return_value = [["whisper.cpp"]]
|
|
|
|
with patch.object(settings_dialog, "get_available_engines", return_value={"whisper_cpp": True}):
|
|
dialog_class._load_and_apply_settings(dialog)
|
|
|
|
dialog.tone_row.set_sensitive.assert_called_once_with(False)
|