diff --git a/app/blitztext_linux.py b/app/blitztext_linux.py index 36553cc..ade0aeb 100644 --- a/app/blitztext_linux.py +++ b/app/blitztext_linux.py @@ -449,10 +449,10 @@ def init_ui(self) -> None: form_general.addRow(t("settings.notes_folder.label"), self.edit_notes_folder) form_general.addRow(create_help_label(t("settings.notes_folder.help"))) - self.spin_history_size = QComboBox() - self.spin_history_size.addItems(["10", "25", "50", "75", "100"]) - self.spin_history_size.setCurrentText(str(self.config.history_size)) - form_general.addRow(t("settings.history_size.label"), self.spin_history_size) + self.combo_history_size = QComboBox() + self.combo_history_size.addItems(["10", "25", "50", "75", "100"]) + self.combo_history_size.setCurrentText(str(self.config.history_size)) + form_general.addRow(t("settings.history_size.label"), self.combo_history_size) form_general.addRow(create_help_label(t("settings.history_size.help"))) self.combo_ui_language = QComboBox() @@ -674,7 +674,7 @@ def save_settings(self) -> None: self.config.autopaste = self.check_autopaste.isChecked() self.config.notes_folder = self.edit_notes_folder.text().strip() - self.config.history_size = int(self.spin_history_size.currentText()) + self.config.history_size = int(self.combo_history_size.currentText()) self.config.ui_language = self.combo_ui_language.currentData() self.config.compose_signature_text = self.edit_compose_signature.toPlainText() self.config.compose_signature_auto_append = self.check_compose_signature_auto_append.isChecked() diff --git a/app/tts_window.py b/app/tts_window.py index 52dd723..9f09b00 100644 --- a/app/tts_window.py +++ b/app/tts_window.py @@ -869,12 +869,12 @@ def _detach_cloud_thread(self) -> None: worker = self._cloud_worker thread = self._cloud_thread if worker is not None: - for signal, callback in ( + for sig, callback in ( (worker.finished, self._on_cloud_finished), (worker.error, self._on_cloud_error), ): try: - signal.disconnect(callback) + sig.disconnect(callback) except TypeError: pass worker.request_cancel() diff --git a/tests/test_history_panel.py b/tests/test_history_panel.py new file mode 100644 index 0000000..d29b28f --- /dev/null +++ b/tests/test_history_panel.py @@ -0,0 +1,82 @@ +import os + +import pytest +from unittest.mock import patch + +from app.history_panel import HistoryPanel + +pytestmark = pytest.mark.skipif( + os.environ.get("WHISPER_GUI_TESTS") != "1", + reason="requires WHISPER_GUI_TESTS=1", +) + +# Reuse the application when running alongside other GUI test modules. +@pytest.fixture +def isolated_qapp(): + from PyQt6.QtWidgets import QApplication + return QApplication.instance() or QApplication([]) + +@pytest.fixture +def history_panel(isolated_qapp): + panel = HistoryPanel(max_entries=10) + yield panel + panel.close() + panel.deleteLater() + +def test_add_entry_increases_count_and_displays_entry(history_panel): + history_panel.show() + assert history_panel._empty_label.isVisible() + assert history_panel.entry_count == 0 + history_panel.add_entry("Hello World") + assert history_panel.entry_count == 1 + assert history_panel._entries[0].text == "Hello World" + assert not history_panel._empty_label.isVisible() + +def test_add_entry_respects_max_entries(history_panel): + history_panel.set_max_entries(10) + + for i in range(1, 15): + history_panel.add_entry(str(i)) + + assert history_panel.entry_count == 10 + assert history_panel._entries[0].text == "14" + assert history_panel._entries[-1].text == "5" + assert len(history_panel._entry_widgets) == 10 + +def test_clear_all_removes_all_entries(history_panel, isolated_qapp): + history_panel.show() + history_panel.add_entry("1") + history_panel.add_entry("2") + history_panel.clear_all() + + # Process events so the widget hides properly + isolated_qapp.processEvents() + + assert history_panel.entry_count == 0 + assert not history_panel._entry_widgets + assert history_panel._empty_label.isVisible() + +@patch('app.history_panel._clipboard_write') +@patch('app.history_panel.save_merged_dictation') +def test_merge_dictation(mock_save, mock_clip, history_panel): + history_panel.add_entry("Dict 1", is_dictation=True) + history_panel.add_entry("Dict 2", is_dictation=True) + history_panel.add_entry("Clip", is_dictation=False) + + mock_save.return_value = "/tmp/fake.md" + + # Needs at least 2 dictations to merge + history_panel._merge_dictation() + + # They are inserted backwards, so chronologically they are "Dict 1" \n\n "Dict 2" + mock_clip.assert_called_once_with("Dict 1\n\nDict 2") + mock_save.assert_called_once_with(history_panel.notes_folder, "Dict 1\n\nDict 2") + +def test_clear_button_arming(history_panel): + assert not history_panel._clear_armed + history_panel._on_clear_clicked() + assert history_panel._clear_armed + assert history_panel._clear_disarm_timer.isActive() + + history_panel._disarm_clear() + assert not history_panel._clear_armed diff --git a/tests/test_settings_dialog.py b/tests/test_settings_dialog.py index ce18428..0a8f115 100644 --- a/tests/test_settings_dialog.py +++ b/tests/test_settings_dialog.py @@ -155,7 +155,7 @@ def _fake_save_self(config_dir, preset_key, ui_language="de"): _collect_custom_terms=lambda: [], check_autopaste=_Check(True), edit_notes_folder=_Edit(""), - spin_history_size=_Combo("50"), + combo_history_size=_Combo("50"), combo_ui_language=_Combo( text="English" if ui_language == "en" else "Deutsch", data=ui_language, diff --git a/tests/test_tray_preset_menu.py b/tests/test_tray_preset_menu.py index 5927859..17285f5 100644 --- a/tests/test_tray_preset_menu.py +++ b/tests/test_tray_preset_menu.py @@ -28,7 +28,7 @@ @pytest.fixture -def tray_app(tmp_path): +def tray_app(tmp_path, monkeypatch): """Echte App mit isolierter Config (tmp), ohne evdev-Thread. Die Config wird nach dem Boot auf ein tmp-Verzeichnis umgehÀngt, damit der @@ -39,6 +39,7 @@ def tray_app(tmp_path): from app.blitztext_linux import BlitztextApp qapp = QApplication.instance() or QApplication([]) + monkeypatch.setattr(BlitztextApp, "start_hotkey_worker", lambda self: None) app = BlitztextApp(qapp) app.stop_hotkey_worker() # kein echter evdev-Thread im Test