Files
voculinux/tests/test_sound_effects_sensitivity.py
T
d29c884782 fix(settings): gray out Dictation Tone while sound effects are off (#877)
* 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>
2026-09-29 22:43:56 -07:00

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)