Files
rpa_vision_v3/tests/unit/test_terrain_duplicate_labels.py
Dom a27b74cf22 v1.0 - Version stable: multi-PC, détection UI-DETR-1, 3 modes exécution
- Frontend v4 accessible sur réseau local (192.168.1.40)
- Ports ouverts: 3002 (frontend), 5001 (backend), 5004 (dashboard)
- Ollama GPU fonctionnel
- Self-healing interactif
- Dashboard confiance

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:23:51 +01:00

121 lines
4.5 KiB
Python

"""
Tests pour Fiche #8 - Anti-bugs terrain : Labels dupliqués
Auteur: Dom, Alice Kiro - 15 décembre 2024
Objectif: Valider la gestion des labels dupliqués avec choix du bon anchor
Tests:
1. Deux labels "Username" dans différents panels
2. Choix du meilleur anchor selon le contexte
3. Préférence pour containers plus spécifiques
"""
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
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_duplicate_labels_chooses_best_container():
"""Test que le resolver choisit le bon anchor quand il y a des labels dupliqués"""
# Panel A (petit, spécifique)
panelA = E("panelA", "panel", (50, 50, 300, 200), etype="panel", conf=1.0)
lblA = E("lblA", "label", (80, 100, 120, 20), "Username", conf=1.0)
inpA = E("inpA", "input", (210, 95, 120, 30), "", etype="text_input")
# Panel B (grand, moins spécifique)
panelB = E("panelB", "panel", (400, 50, 800, 600), etype="panel", conf=1.0)
lblB = E("lblB", "label", (430, 100, 120, 20), "Username", conf=1.0)
inpB = E("inpB", "input", (560, 95, 120, 30), "", etype="text_input")
screen = S([panelA, panelB, lblA, inpA, lblB, inpB])
spec = TargetSpec(by_role="input", context_hints={"right_of_text": "Username"})
r = TargetResolver()
res = r.resolve_target(spec, screen, ResolutionContext(screen_state=screen, previous_target=None))
assert res is not None
# Doit choisir l'input du panel A (plus petit/spécifique)
assert res.element.element_id == "inpA"
def test_duplicate_labels_with_no_container():
"""Test avec labels dupliqués mais sans containers"""
# Deux labels "Password" sans containers
lbl1 = E("lbl1", "label", (100, 100, 120, 20), "Password", conf=1.0)
inp1 = E("inp1", "input", (100, 140, 120, 30), "", etype="text_input")
lbl2 = E("lbl2", "label", (400, 100, 120, 20), "Password", conf=1.0)
inp2 = E("inp2", "input", (400, 140, 120, 30), "", etype="text_input")
screen = S([lbl1, inp1, lbl2, inp2])
spec = TargetSpec(by_role="input", context_hints={"below_text": "Password"})
r = TargetResolver()
res = r.resolve_target(spec, screen, ResolutionContext(screen_state=screen, previous_target=None))
assert res is not None
# Doit choisir un des inputs (le premier trouvé avec la logique actuelle)
assert res.element.element_id in ["inp1", "inp2"]
def test_single_label_still_works():
"""Test que le cas simple (un seul label) fonctionne toujours"""
lbl = E("lbl", "label", (100, 100, 120, 20), "Password", conf=1.0)
inp = E("inp", "input", (100, 140, 120, 30), "", etype="text_input")
screen = S([lbl, inp])
spec = TargetSpec(by_role="input", context_hints={"below_text": "Password"})
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 == "inp"
if __name__ == "__main__":
pytest.main([__file__, "-v"])