Files
voculinux/tests/test_sound_effects_sensitivity.py
T
xavierk 80a3792576
CI / security (pull_request) Failing after 1m25s
CI / lint (pull_request) Failing after 34s
CI / ai-review (pull_request) Successful in 25s
chore: import vocalinux project
Import the vocalinux source tree (tray app, Next.js site in web/,
packaging, installers, docs and tests) on top of the Gitea template
commit. The project README replaces the template README, and the
template CI files (.gitea/workflows/ci.yml, .jscpd.json, CI.md) are kept.
2026-11-06 11:51:44 +05:30

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)