""" Fiche #5 - Smoke Test E2E Minimal (dry-run) Auteur: Dom, Alice Kiro - 15 décembre 2024 Objectif: Test ultra simple qui valide le chemin critique sans cliquer pour de vrai Valide: ScreenState + UIElements → TargetResolver → ActionExecutor (dry-run) => Barrière anti-régression : si ça passe, tu as le droit de respirer ! """ import pytest # Marquer tous les tests de ce fichier comme smoke tests pytestmark = pytest.mark.smoke from datetime import datetime from core.models.screen_state import ScreenState, RawLevel, PerceptionLevel, ContextLevel, WindowContext, EmbeddingRef from core.models.ui_element import UIElement, UIElementEmbeddings, VisualFeatures from core.models.workflow_graph import TargetSpec, Action, ActionType, WorkflowEdge, EdgeConstraints, PostConditions from core.execution.target_resolver import TargetResolver from core.execution.action_executor import ActionExecutor, ExecutionStatus from core.execution.error_handler import ErrorHandler def _mk_elem(eid, etype, role, x, y, w, h, label, conf=0.95): """Helper pour créer un UIElement rapidement""" return UIElement( element_id=eid, type=etype, role=role, bbox=(x, y, w, h), # XYWH center=(x + w // 2, y + h // 2), label=label, label_confidence=1.0, embeddings=UIElementEmbeddings(image=None, text=None), visual_features=VisualFeatures( dominant_color="gray", has_icon=False, shape="rectangle", size_category="medium", ), confidence=conf, tags=[], metadata={}, ) @pytest.fixture def screen_state_login(): """ScreenState de test avec écran de login""" ui = [ _mk_elem("lbl_user", "label", "data_display", 100, 100, 120, 20, "Username"), _mk_elem("inp_user", "text_input", "form_input", 240, 95, 260, 30, ""), _mk_elem("lbl_pass", "label", "data_display", 100, 160, 120, 20, "Password"), _mk_elem("inp_pass", "text_input", "form_input", 240, 155, 260, 30, ""), _mk_elem("btn_signin", "button", "submit", 240, 220, 140, 35, "Sign in"), ] return ScreenState( screen_state_id="ss_login_001", timestamp=datetime.now(), session_id="sess_001", window=WindowContext(app_name="TestApp", window_title="Login", screen_resolution=[1920, 1080]), raw=RawLevel(screenshot_path="N/A", capture_method="test", file_size_bytes=0), perception=PerceptionLevel( embedding=EmbeddingRef(provider="test", vector_id="N/A", dimensions=1), detected_text=["Username", "Password", "Sign in"], text_detection_method="test", confidence_avg=1.0, ), context=ContextLevel(user_id="test_user"), ui_elements=ui, ) def test_smoke_resolver_by_text(screen_state_login): """Test que TargetResolver trouve un élément par texte""" r = TargetResolver() t = TargetSpec(by_text="Sign in") resolved = r.resolve_target(t, screen_state_login) assert resolved is not None assert resolved.element.element_id == "btn_signin" def test_smoke_executor_click_dry_run(monkeypatch, tmp_path, screen_state_login): """Test ActionExecutor clic en mode dry-run (sans cliquer pour de vrai)""" # Force no real click + no sleep import core.execution.action_executor as ae monkeypatch.setattr(ae, "PYAUTOGUI_AVAILABLE", False) monkeypatch.setattr(ae.time, "sleep", lambda *_: None) err = ErrorHandler(error_log_dir=str(tmp_path / "errors")) ex = ActionExecutor(error_handler=err, verify_postconditions=False) edge = WorkflowEdge( edge_id="e1", from_node="n0", to_node="n1", action=Action( type=ActionType.MOUSE_CLICK, target=TargetSpec(by_text="Sign in"), parameters={"wait_after_ms": 0}, ), constraints=EdgeConstraints(), post_conditions=PostConditions(), ) res = ex.execute_edge(edge, screen_state_login) assert res.status == ExecutionStatus.SUCCESS assert res.target_resolved is not None assert res.target_resolved.element.element_id == "btn_signin" def test_smoke_executor_text_input_dry_run(monkeypatch, tmp_path, screen_state_login): """Test ActionExecutor saisie de texte en mode dry-run""" import core.execution.action_executor as ae monkeypatch.setattr(ae, "PYAUTOGUI_AVAILABLE", False) monkeypatch.setattr(ae.time, "sleep", lambda *_: None) err = ErrorHandler(error_log_dir=str(tmp_path / "errors")) ex = ActionExecutor(error_handler=err, verify_postconditions=False) edge = WorkflowEdge( edge_id="e2", from_node="n0", to_node="n0", action=Action( type=ActionType.TEXT_INPUT, target=TargetSpec(by_role="form_input", selection_policy="first"), parameters={"text": "hello", "wait_after_ms": 0}, ), constraints=EdgeConstraints(), post_conditions=PostConditions(), ) res = ex.execute_edge(edge, screen_state_login) assert res.status == ExecutionStatus.SUCCESS assert res.target_resolved is not None