125 lines
4.0 KiB
Python
125 lines
4.0 KiB
Python
"""Ce qui est testable dans le lanceur Qt SANS boucle d'événements.
|
|
|
|
`ZonzaTray.__init__` construit le menu et l'icône de façon synchrone (aucun
|
|
`.show()`, aucune boucle) : c'est déjà éprouvable. Le marshalage par signal
|
|
Qt.QueuedConnection, lui, N'EST PAS testé ici — sa livraison dépend d'une
|
|
boucle d'événements qui tourne, ce qu'aucun test de ce fichier ne fait
|
|
tourner. On teste donc directement les méthodes qu'un slot appellerait
|
|
(`_apply_title`, `toggle_sounds`), pas le trajet signal → file → slot.
|
|
"""
|
|
import pytest
|
|
|
|
PySide6 = pytest.importorskip("PySide6")
|
|
|
|
from PySide6.QtWidgets import QApplication # noqa: E402
|
|
|
|
from core import CONFIG # noqa: E402
|
|
from lanceur_qt import _STATE_TITLES, ZonzaTray # noqa: E402
|
|
|
|
|
|
@pytest.fixture(scope="module", autouse=True)
|
|
def _app_qt():
|
|
# QIcon/QPixmap/QSystemTrayIcon exigent une QApplication déjà construite —
|
|
# sans .exec() : on ne fait jamais tourner la boucle d'événements.
|
|
app = QApplication.instance() or QApplication([])
|
|
yield app
|
|
|
|
|
|
class _ControleurFactice:
|
|
"""Ni toggle() ni transcripteur ne sont appelés tant que .show() ne l'est
|
|
pas — ce que ces tests évitent justement."""
|
|
def toggle(self):
|
|
raise AssertionError("ne doit pas être appelé sans .show()")
|
|
|
|
|
|
class _SystemeFactice:
|
|
def raccourci_defaut(self):
|
|
return "<ctrl>+<alt>+d"
|
|
|
|
|
|
@pytest.fixture
|
|
def tray():
|
|
t = ZonzaTray(_ControleurFactice(), _SystemeFactice())
|
|
yield t
|
|
# évite qu'un menu/tray fantôme traîne d'un test à l'autre (pas de .show()
|
|
# appelé ici, donc rien n'a jamais été rendu visible).
|
|
t.menu.deleteLater()
|
|
|
|
|
|
# --- construction du menu -----------------------------------------------
|
|
|
|
def test_le_menu_contient_exactement_sons_et_quitter():
|
|
t = ZonzaTray(_ControleurFactice(), _SystemeFactice())
|
|
titres = [a.text() for a in t.menu.actions() if not a.isSeparator()]
|
|
assert titres == ["Sons", "Quitter"]
|
|
|
|
|
|
def test_sons_est_une_case_a_cocher():
|
|
t = ZonzaTray(_ControleurFactice(), _SystemeFactice())
|
|
assert t.sounds_action.isCheckable()
|
|
|
|
|
|
def test_l_etat_initial_de_la_case_suit_la_config():
|
|
CONFIG["sounds"] = True
|
|
t = ZonzaTray(_ControleurFactice(), _SystemeFactice())
|
|
assert t.sounds_action.isChecked() is True
|
|
|
|
|
|
def test_le_menu_est_bien_celui_du_tray():
|
|
t = ZonzaTray(_ControleurFactice(), _SystemeFactice())
|
|
assert t.tray.contextMenu() is t.menu
|
|
|
|
|
|
# --- bascule des sons -----------------------------------------------------
|
|
|
|
def test_toggle_sounds_coupe_le_son(tray):
|
|
CONFIG["sounds"] = True
|
|
tray.toggle_sounds(False)
|
|
assert CONFIG["sounds"] is False
|
|
assert tray.sounds_action.isChecked() is False
|
|
|
|
|
|
def test_toggle_sounds_reactive_le_son(tray):
|
|
CONFIG["sounds"] = False
|
|
tray.toggle_sounds(True)
|
|
assert CONFIG["sounds"] is True
|
|
assert tray.sounds_action.isChecked() is True
|
|
|
|
|
|
def test_toggle_sounds_ne_touche_a_rien_d_autre_dans_la_config(tray):
|
|
avant = dict(CONFIG)
|
|
tray.toggle_sounds(True)
|
|
apres = dict(CONFIG)
|
|
del avant["sounds"]
|
|
del apres["sounds"]
|
|
assert avant == apres
|
|
|
|
|
|
# --- correspondance état → icône ------------------------------------------
|
|
|
|
def test_trois_etats_sont_couverts():
|
|
assert set(_STATE_TITLES) == {"idle", "recording", "transcribing"}
|
|
|
|
|
|
def test_etat_idle_par_defaut(tray):
|
|
assert tray._current_state == "idle"
|
|
|
|
|
|
@pytest.mark.parametrize("etat", ["idle", "recording", "transcribing"])
|
|
def test_apply_title_memorise_l_etat_recu(tray, etat):
|
|
tray._apply_title(etat)
|
|
assert tray._current_state == etat
|
|
|
|
|
|
def test_apply_title_pose_une_icone_non_nulle_pour_chaque_etat(tray):
|
|
for etat in _STATE_TITLES:
|
|
tray._apply_title(etat)
|
|
assert not tray.tray.icon().isNull()
|
|
|
|
|
|
def test_un_etat_inconnu_retombe_sur_l_icone_de_repos(tray):
|
|
# _apply_title ne doit jamais planter sur un état imprévu — repli sur
|
|
# "idle" (même garde que app.py : `_STATE_TITLES.get(state, "🎙️")`).
|
|
tray._apply_title("etat-invente")
|
|
assert not tray.tray.icon().isNull()
|