feat(gui): localiser les documents livrés + bouton ouvrir le dossier (P1-5)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
118
tests/unit/test_gui_v6_result_hint.py
Normal file
118
tests/unit/test_gui_v6_result_hint.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""Message d'aide localisant les documents non livrés (P1-5) + ouverture dossier.
|
||||
|
||||
Pur : pas de display. ``failure_hint`` formate un texte ; ``open_in_file_manager``
|
||||
dispatch vers la bonne commande OS (monkeypatchée).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import gui_v6.fsutil as fsutil
|
||||
from gui_v6.processing_runner import RunSummary
|
||||
from gui_v6.tabs.tab_usage import failure_hint
|
||||
|
||||
|
||||
def test_no_hint_when_all_ok():
|
||||
s = RunSummary(total=3, succeeded=3, failed=0)
|
||||
assert failure_hint(s, Path("/out")) is None
|
||||
|
||||
|
||||
def test_hint_when_failures_mentions_output_dir():
|
||||
s = RunSummary(total=3, succeeded=2, failed=1)
|
||||
hint = failure_hint(s, Path("/out/anonymise"))
|
||||
assert hint is not None
|
||||
assert "/out/anonymise" in hint
|
||||
# Honnêteté : préciser que les échecs ne sont PAS anonymisés.
|
||||
assert "pas" in hint.lower()
|
||||
|
||||
|
||||
def test_hint_when_stopped():
|
||||
s = RunSummary(total=3, succeeded=1, failed=0, stopped=True)
|
||||
assert failure_hint(s, Path("/out")) is not None
|
||||
|
||||
|
||||
def test_no_hint_without_output_dir():
|
||||
s = RunSummary(total=1, succeeded=0, failed=1)
|
||||
assert failure_hint(s, None) is None
|
||||
|
||||
|
||||
def test_open_in_file_manager_dispatches(monkeypatch):
|
||||
calls = {}
|
||||
monkeypatch.setattr(fsutil.sys, "platform", "linux")
|
||||
monkeypatch.setattr(fsutil.subprocess, "Popen", lambda args, **k: calls.setdefault("args", args))
|
||||
fsutil.open_in_file_manager(Path("/out"))
|
||||
assert calls["args"][0] == "xdg-open"
|
||||
assert calls["args"][1] == "/out"
|
||||
|
||||
|
||||
def test_no_claim_written_when_zero_succeeded():
|
||||
"""0 succès : ne pas prétendre qu'un dossier contient des documents écrits."""
|
||||
s = RunSummary(total=2, succeeded=0, failed=2)
|
||||
hint = failure_hint(s, Path("/out"))
|
||||
assert hint is not None
|
||||
assert "écrits dans" not in hint
|
||||
assert "Aucun document" in hint
|
||||
|
||||
|
||||
def test_hint_with_path_when_some_succeeded():
|
||||
"""≥1 succès : localiser le dossier de sortie effectif."""
|
||||
s = RunSummary(total=3, succeeded=2, failed=1)
|
||||
hint = failure_hint(s, Path("/out"))
|
||||
assert hint is not None
|
||||
assert "/out" in hint
|
||||
assert "écrits dans" in hint
|
||||
|
||||
|
||||
# -- garde anti-régression du bug Critical (empilement de widgets) -----------
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def usage_tab():
|
||||
"""``UsageTab`` headless (Xvfb) — skip propre si pas de display."""
|
||||
pytest.importorskip("customtkinter")
|
||||
try:
|
||||
import customtkinter as ctk
|
||||
|
||||
from gui_v6.tabs.tab_usage import UsageTab
|
||||
|
||||
root = ctk.CTk()
|
||||
root.withdraw()
|
||||
tab = UsageTab(root)
|
||||
except Exception as exc: # pas de display Tk
|
||||
pytest.skip(f"display Tk indisponible: {exc}")
|
||||
try:
|
||||
yield tab
|
||||
finally:
|
||||
try:
|
||||
root.destroy()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def test_show_failure_hint_does_not_accumulate(usage_tab):
|
||||
"""Bug Critical : deux runs en échec ne doivent pas empiler de hints sous
|
||||
``_rsec`` (handle ``_hint_row`` détruit inconditionnellement)."""
|
||||
usage_tab._last_output_dir = Path("/out")
|
||||
summary = RunSummary(total=3, succeeded=2, failed=1)
|
||||
|
||||
usage_tab._show_failure_hint(summary)
|
||||
count_after_first = len(usage_tab._rsec.winfo_children())
|
||||
|
||||
usage_tab._show_failure_hint(summary)
|
||||
count_after_second = len(usage_tab._rsec.winfo_children())
|
||||
|
||||
assert count_after_second == count_after_first
|
||||
|
||||
|
||||
def test_show_failure_hint_clears_stale_hint(usage_tab):
|
||||
"""Un run en échec suivi d'un run nominal ne doit pas laisser de hint périmé."""
|
||||
usage_tab._last_output_dir = Path("/out")
|
||||
usage_tab._show_failure_hint(RunSummary(total=3, succeeded=2, failed=1))
|
||||
with_hint = len(usage_tab._rsec.winfo_children())
|
||||
|
||||
usage_tab._show_failure_hint(RunSummary(total=3, succeeded=3, failed=0))
|
||||
without_hint = len(usage_tab._rsec.winfo_children())
|
||||
|
||||
assert without_hint == with_hint - 1
|
||||
Reference in New Issue
Block a user