""" Tests pour Fiche #8 - Anti-bugs terrain : Normalisation texte Auteur: Dom, Alice Kiro - 15 décembre 2024 Objectif: Valider la normalisation de texte pour OCR capricieux et variations Tests: 1. Normalisation accents/casse/espaces 2. Fuzzy matching OCR 3. Gestion NBSP et caractères spéciaux """ import pytest # Marquer tous les tests de ce fichier comme fiche8 pytestmark = pytest.mark.fiche8 from datetime import datetime from core.execution.target_resolver import TargetResolver, ResolutionContext, _norm_text, _fuzzy_ratio from core.models.workflow_graph import TargetSpec from core.models.screen_state import ScreenState, RawLevel, PerceptionLevel, ContextLevel, WindowContext, EmbeddingRef from core.models.ui_element import UIElement, UIElementEmbeddings, VisualFeatures def E(eid, role, bbox, label="", etype="ui", conf=0.9): """Helper pour créer un UIElement rapidement""" return UIElement( element_id=eid, type=etype, role=role, bbox=bbox, center=(bbox[0] + bbox[2]//2, bbox[1] + bbox[3]//2), label=label, label_confidence=1.0, embeddings=UIElementEmbeddings(image=None, text=None), visual_features=VisualFeatures(dominant_color="n/a", has_icon=False, shape="rectangle", size_category="medium"), confidence=conf, tags=[], metadata={} ) def S(elements): """Helper pour créer un ScreenState rapidement""" return ScreenState( screen_state_id="s", timestamp=datetime.now(), session_id="sess", window=WindowContext(app_name="app", window_title="win", screen_resolution=[1920,1080]), raw=RawLevel(screenshot_path="x", capture_method="test", file_size_bytes=1), perception=PerceptionLevel( embedding=EmbeddingRef(provider="p", vector_id="v", dimensions=1), detected_text=[], text_detection_method="none", confidence_avg=0.0 ), context=ContextLevel(), ui_elements=elements ) def test_text_normalization_accents_case_spaces(): """Test normalisation accents/casse/espaces avec NBSP""" # Label avec NBSP + majuscules + accents btn = E("btn", "button", (100, 100, 120, 30), "Se\u00A0Connecter", etype="button") screen = S([btn]) spec = TargetSpec(by_text="se connecter") # minuscules, espace normal r = TargetResolver() res = r.resolve_target(spec, screen, ResolutionContext(screen_state=screen, previous_target=None)) assert res is not None assert res.element.element_id == "btn" def test_fuzzy_matching_ocr_errors(): """Test fuzzy matching pour erreurs OCR typiques""" # OCR a lu "S1gn-in" au lieu de "Sign in" btn1 = E("btn1", "label", (100, 100, 120, 30), "S1gn-in", etype="label") # Changé en label # Autre bouton avec texte différent btn2 = E("btn2", "label", (250, 100, 120, 30), "Cancel", etype="label") screen = S([btn1, btn2]) spec = TargetSpec(by_text="Sign in") # Seulement by_text r = TargetResolver() res = r.resolve_target(spec, screen, ResolutionContext(screen_state=screen, previous_target=None)) assert res is not None assert res.element.element_id == "btn1" # Doit matcher malgré l'erreur OCR def test_normalization_functions_directly(): """Test direct des fonctions de normalisation""" # Test _norm_text assert _norm_text("Se\u00A0Connecter") == "se connecter" assert _norm_text("Café—Bar") == "cafe-bar" assert _norm_text(" Multiple Spaces ") == "multiple spaces" # Test _fuzzy_ratio assert _fuzzy_ratio("Sign in", "S1gn-in") > 0.70 # Doit dépasser un seuil raisonnable assert _fuzzy_ratio("Sign in", "Sign-in") > 0.85 # Très proche assert _fuzzy_ratio("Sign in", "Cancel") < 0.50 # Très différent if __name__ == "__main__": pytest.main([__file__, "-v"])