buzz/tests/gui_test.py
2026-03-06 19:26:19 +02:00

229 lines
8.5 KiB
Python

import multiprocessing
import os
import platform
from unittest.mock import Mock, patch
import pytest
import sounddevice
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QKeyEvent
from PyQt6.QtWidgets import (
QApplication,
QMessageBox,
)
from pytestqt.qtbot import QtBot
from buzz.locale import _
from buzz.__version__ import VERSION
from buzz.widgets.audio_devices_combo_box import AudioDevicesComboBox
from buzz.widgets.transcriber.advanced_settings_dialog import AdvancedSettingsDialog
from buzz.widgets.transcriber.hugging_face_search_line_edit import (
HuggingFaceSearchLineEdit,
)
from buzz.widgets.transcriber.languages_combo_box import LanguagesComboBox
from buzz.widgets.about_dialog import AboutDialog
from buzz.settings.settings import Settings
from buzz.transcriber.transcriber import (
TranscriptionOptions,
)
from buzz.widgets.transcriber.transcription_options_group_box import (
TranscriptionOptionsGroupBox,
)
from tests.mock_sounddevice import MockInputStream, mock_query_devices
from .mock_qt import MockNetworkAccessManager, MockNetworkReply
if platform.system() == "Linux":
try:
multiprocessing.set_start_method("spawn", force=True)
except RuntimeError:
pass
@pytest.fixture(scope="module", autouse=True)
def audio_setup():
with patch("sounddevice.query_devices") as query_devices_mock, patch(
"sounddevice.InputStream", side_effect=MockInputStream
), patch("sounddevice.check_input_settings"):
query_devices_mock.return_value = mock_query_devices
sounddevice.default.device = 3, 4
yield
class TestLanguagesComboBox:
def test_should_show_sorted_whisper_languages(self, qtbot):
languages_combox_box = LanguagesComboBox("en")
qtbot.add_widget(languages_combox_box)
assert languages_combox_box.itemText(0) == _("Detect Language")
assert languages_combox_box.itemText(1) == _("Afrikaans")
def test_should_select_en_as_default_language(self, qtbot):
languages_combox_box = LanguagesComboBox("en")
qtbot.add_widget(languages_combox_box)
assert languages_combox_box.currentText() == _("English")
def test_should_select_detect_language_as_default(self, qtbot):
languages_combo_box = LanguagesComboBox(None)
qtbot.add_widget(languages_combo_box)
assert languages_combo_box.currentText() == _("Detect Language")
class TestAudioDevicesComboBox:
def test_get_devices(self):
audio_devices_combo_box = AudioDevicesComboBox()
assert audio_devices_combo_box.itemText(0) == "Background Music"
assert audio_devices_combo_box.itemText(1) == "Background Music (UI Sounds)"
assert audio_devices_combo_box.itemText(2) == "BlackHole 2ch"
assert audio_devices_combo_box.itemText(3) == "MacBook Pro Microphone"
assert audio_devices_combo_box.itemText(4) == "Null Audio Device"
assert audio_devices_combo_box.currentText() == "MacBook Pro Microphone"
def test_select_default_mic_when_no_default(self):
sounddevice.default.device = -1, 1
audio_devices_combo_box = AudioDevicesComboBox()
assert audio_devices_combo_box.currentText() == "Background Music"
@pytest.fixture(scope="module", autouse=True)
def clear_settings():
settings = Settings()
settings.clear()
class TestAboutDialog:
def test_should_check_for_updates(self, qtbot: QtBot):
reply = MockNetworkReply(data={"name": "v" + VERSION})
manager = MockNetworkAccessManager(reply=reply)
dialog = AboutDialog(network_access_manager=manager)
qtbot.add_widget(dialog)
mock_message_box_information = Mock()
QMessageBox.information = mock_message_box_information
with qtbot.wait_signal(dialog.network_access_manager.finished):
dialog.check_updates_button.click()
mock_message_box_information.assert_called_with(
dialog, "", _("You're up to date!")
)
class TestAdvancedSettingsDialog:
def test_should_update_advanced_settings(self, qtbot: QtBot):
dialog = AdvancedSettingsDialog(
transcription_options=TranscriptionOptions(
initial_prompt="prompt",
enable_llm_translation=False,
llm_model="",
llm_prompt=""
)
)
qtbot.add_widget(dialog)
transcription_options_mock = Mock()
dialog.transcription_options_changed.connect(transcription_options_mock)
assert dialog.windowTitle() == _("Advanced Settings")
assert dialog.initial_prompt_text_edit.toPlainText() == "prompt"
assert dialog.enable_llm_translation_checkbox.isChecked() is False
assert dialog.llm_model_line_edit.text() == "gpt-4.1-mini"
assert dialog.llm_prompt_text_edit.toPlainText() == _("Please translate each text sent to you from English to Spanish. Translation will be used in an automated system, please do not add any comments or notes, just the translation.")
dialog.initial_prompt_text_edit.setPlainText("new prompt")
dialog.enable_llm_translation_checkbox.setChecked(True)
dialog.llm_model_line_edit.setText("model")
dialog.llm_prompt_text_edit.setPlainText("Please translate this text")
assert transcription_options_mock.call_args[0][0].initial_prompt == "new prompt"
assert transcription_options_mock.call_args[0][0].enable_llm_translation is True
assert transcription_options_mock.call_args[0][0].llm_model == "model"
assert transcription_options_mock.call_args[0][0].llm_prompt == "Please translate this text"
@pytest.mark.skipif(
platform.system() == "Linux" and os.environ.get("XDG_SESSION_TYPE") == "wayland",
reason="Skipping on Wayland sessions due to Qt popup issues"
)
class TestHuggingFaceSearchLineEdit:
def test_should_update_selected_model_on_type(self, qtbot: QtBot):
widget = HuggingFaceSearchLineEdit(
default_value="",
network_access_manager=self.network_access_manager()
)
qtbot.add_widget(widget)
mock_model_selected = Mock()
widget.model_selected.connect(mock_model_selected)
self._set_text_and_wait_response(qtbot, widget)
mock_model_selected.assert_called_with("openai/whisper-tiny")
def test_should_show_list_of_models(self, qtbot: QtBot):
widget = HuggingFaceSearchLineEdit(
default_value="",
network_access_manager=self.network_access_manager()
)
qtbot.add_widget(widget)
self._set_text_and_wait_response(qtbot, widget)
assert widget.popup.count() > 0
assert "openai/whisper-tiny" in widget.popup.item(0).text()
def test_should_select_model_from_list(self, qtbot: QtBot):
widget = HuggingFaceSearchLineEdit(
default_value="",
network_access_manager=self.network_access_manager()
)
qtbot.add_widget(widget)
mock_model_selected = Mock()
widget.model_selected.connect(mock_model_selected)
self._set_text_and_wait_response(qtbot, widget)
# press down arrow and enter to select next item
QApplication.sendEvent(
widget.popup,
QKeyEvent(
QKeyEvent.Type.KeyPress, Qt.Key.Key_Down, Qt.KeyboardModifier.NoModifier
),
)
QApplication.sendEvent(
widget.popup,
QKeyEvent(
QKeyEvent.Type.KeyPress,
Qt.Key.Key_Enter,
Qt.KeyboardModifier.NoModifier,
),
)
mock_model_selected.assert_called_with("openai/whisper-tiny.en")
@staticmethod
def network_access_manager():
reply = MockNetworkReply(
data=[{"id": "openai/whisper-tiny"}, {"id": "openai/whisper-tiny.en"}]
)
return MockNetworkAccessManager(reply=reply)
@staticmethod
def _set_text_and_wait_response(qtbot: QtBot, widget: HuggingFaceSearchLineEdit):
with qtbot.wait_signal(widget.network_manager.finished):
widget.setText("openai/whisper-tiny")
widget.textEdited.emit("openai/whisper-tiny")
class TestTranscriptionOptionsGroupBox:
def test_should_update_model_type(self, qtbot):
widget = TranscriptionOptionsGroupBox()
qtbot.add_widget(widget)
mock_transcription_options_changed = Mock()
widget.transcription_options_changed.connect(mock_transcription_options_changed)
widget.model_type_combo_box.setCurrentIndex(1)
mock_transcription_options_changed.assert_called()