- 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>
708 lines
26 KiB
Python
708 lines
26 KiB
Python
"""
|
|
Tests Unitaires - Actions VWB
|
|
|
|
Auteur : Dom, Alice, Kiro - 09 janvier 2026
|
|
|
|
Tests complets pour les actions VisionOnly du Visual Workflow Builder :
|
|
- BaseVWBAction
|
|
- VWBClickAnchorAction
|
|
- VWBTypeTextAction
|
|
- VWBWaitForAnchorAction
|
|
|
|
Ces tests valident l'exécution des actions, la gestion d'erreurs,
|
|
et l'intégration avec le ScreenCapturer.
|
|
"""
|
|
|
|
import unittest
|
|
import time
|
|
from datetime import datetime
|
|
from unittest.mock import Mock, patch, MagicMock
|
|
import numpy as np
|
|
|
|
# Import des actions VWB
|
|
import sys
|
|
from pathlib import Path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
|
|
from visual_workflow_builder.backend.actions.base_action import (
|
|
BaseVWBAction, VWBActionResult, VWBActionStatus
|
|
)
|
|
from visual_workflow_builder.backend.actions.vision_ui.click_anchor import VWBClickAnchorAction
|
|
from visual_workflow_builder.backend.actions.vision_ui.type_text import VWBTypeTextAction
|
|
from visual_workflow_builder.backend.actions.vision_ui.wait_for_anchor import VWBWaitForAnchorAction
|
|
|
|
from visual_workflow_builder.backend.contracts.error import VWBErrorType, VWBErrorSeverity
|
|
from visual_workflow_builder.backend.contracts.evidence import VWBEvidenceType
|
|
from visual_workflow_builder.backend.contracts.visual_anchor import (
|
|
VWBVisualAnchor, VWBVisualAnchorType, create_image_anchor
|
|
)
|
|
|
|
|
|
class MockScreenCapturer:
|
|
"""Mock du ScreenCapturer pour les tests."""
|
|
|
|
def __init__(self, should_fail=False):
|
|
self.should_fail = should_fail
|
|
self.capture_count = 0
|
|
|
|
def capture(self):
|
|
"""Simule une capture d'écran."""
|
|
self.capture_count += 1
|
|
|
|
if self.should_fail:
|
|
return None
|
|
|
|
# Retourner une image factice (1920x1080 RGB)
|
|
return np.random.randint(0, 255, (1080, 1920, 3), dtype=np.uint8)
|
|
|
|
|
|
class ConcreteTestAction(BaseVWBAction):
|
|
"""Action concrète pour tester BaseVWBAction."""
|
|
|
|
def __init__(self, action_id: str, parameters: dict, should_fail=False):
|
|
super().__init__(
|
|
action_id=action_id,
|
|
name="Test Action",
|
|
description="Action de test",
|
|
parameters=parameters
|
|
)
|
|
self.should_fail = should_fail
|
|
self.execute_core_called = False
|
|
|
|
def validate_parameters(self):
|
|
"""Validation simple pour les tests."""
|
|
errors = []
|
|
if not self.parameters.get('required_param'):
|
|
errors.append("required_param manquant")
|
|
return errors
|
|
|
|
def execute_core(self, step_id: str):
|
|
"""Exécution de test."""
|
|
self.execute_core_called = True
|
|
|
|
if self.should_fail:
|
|
raise ValueError("Test failure")
|
|
|
|
return VWBActionResult(
|
|
action_id=self.action_id,
|
|
step_id=step_id,
|
|
status=VWBActionStatus.SUCCESS,
|
|
start_time=datetime.now(),
|
|
end_time=datetime.now(),
|
|
execution_time_ms=100.0,
|
|
output_data={'test': 'success'},
|
|
evidence_list=[]
|
|
)
|
|
|
|
|
|
class TestBaseVWBAction(unittest.TestCase):
|
|
"""Tests pour BaseVWBAction."""
|
|
|
|
def setUp(self):
|
|
"""Configuration des tests."""
|
|
self.mock_capturer = MockScreenCapturer()
|
|
self.valid_parameters = {'required_param': 'test_value'}
|
|
self.invalid_parameters = {}
|
|
|
|
def test_action_creation(self):
|
|
"""Test de création d'une action."""
|
|
action = ConcreteTestAction(
|
|
action_id='test_001',
|
|
parameters=self.valid_parameters
|
|
)
|
|
|
|
self.assertEqual(action.action_id, 'test_001')
|
|
self.assertEqual(action.name, 'Test Action')
|
|
self.assertEqual(action.current_status, VWBActionStatus.PENDING)
|
|
self.assertIsNone(action.current_result)
|
|
|
|
def test_parameter_validation_success(self):
|
|
"""Test de validation réussie des paramètres."""
|
|
action = ConcreteTestAction(
|
|
action_id='test_001',
|
|
parameters=self.valid_parameters
|
|
)
|
|
|
|
errors = action.validate_parameters()
|
|
self.assertEqual(len(errors), 0)
|
|
|
|
def test_parameter_validation_failure(self):
|
|
"""Test de validation échouée des paramètres."""
|
|
action = ConcreteTestAction(
|
|
action_id='test_001',
|
|
parameters=self.invalid_parameters
|
|
)
|
|
|
|
errors = action.validate_parameters()
|
|
self.assertGreater(len(errors), 0)
|
|
self.assertIn("required_param manquant", errors)
|
|
|
|
def test_successful_execution(self):
|
|
"""Test d'exécution réussie."""
|
|
action = ConcreteTestAction(
|
|
action_id='test_001',
|
|
parameters=self.valid_parameters
|
|
)
|
|
action.screen_capturer = self.mock_capturer
|
|
|
|
result = action.execute('step_001')
|
|
|
|
self.assertTrue(action.execute_core_called)
|
|
self.assertEqual(result.status, VWBActionStatus.SUCCESS)
|
|
self.assertEqual(result.action_id, 'test_001')
|
|
self.assertEqual(result.step_id, 'step_001')
|
|
self.assertGreater(result.execution_time_ms, 0)
|
|
|
|
def test_execution_with_parameter_error(self):
|
|
"""Test d'exécution avec erreur de paramètres."""
|
|
action = ConcreteTestAction(
|
|
action_id='test_001',
|
|
parameters=self.invalid_parameters
|
|
)
|
|
|
|
result = action.execute('step_001')
|
|
|
|
self.assertFalse(action.execute_core_called)
|
|
self.assertEqual(result.status, VWBActionStatus.FAILED)
|
|
self.assertIsNotNone(result.error)
|
|
self.assertEqual(result.error.error_type, VWBErrorType.PARAMETER_INVALID)
|
|
|
|
def test_execution_with_exception(self):
|
|
"""Test d'exécution avec exception."""
|
|
action = ConcreteTestAction(
|
|
action_id='test_001',
|
|
parameters=self.valid_parameters,
|
|
should_fail=True
|
|
)
|
|
action.screen_capturer = self.mock_capturer
|
|
|
|
result = action.execute('step_001')
|
|
|
|
self.assertTrue(action.execute_core_called)
|
|
self.assertEqual(result.status, VWBActionStatus.FAILED)
|
|
self.assertIsNotNone(result.error)
|
|
self.assertEqual(result.error.error_type, VWBErrorType.SYSTEM_ERROR)
|
|
|
|
def test_retry_mechanism(self):
|
|
"""Test du mécanisme de retry."""
|
|
action = ConcreteTestAction(
|
|
action_id='test_001',
|
|
parameters={'required_param': 'test', 'retry_count': 2},
|
|
should_fail=True
|
|
)
|
|
action.screen_capturer = self.mock_capturer
|
|
|
|
result = action.execute('step_001')
|
|
|
|
# Vérifier que execute_core a été appelé plusieurs fois
|
|
self.assertTrue(action.execute_core_called)
|
|
self.assertEqual(result.retry_count, 2) # Dernier essai
|
|
self.assertEqual(result.status, VWBActionStatus.FAILED)
|
|
|
|
|
|
class TestVWBClickAnchorAction(unittest.TestCase):
|
|
"""Tests pour VWBClickAnchorAction."""
|
|
|
|
def setUp(self):
|
|
"""Configuration des tests."""
|
|
self.mock_capturer = MockScreenCapturer()
|
|
|
|
# Créer une ancre visuelle de test
|
|
self.test_anchor = create_image_anchor(
|
|
name="Bouton Test",
|
|
reference_image_base64="fake_image_data",
|
|
created_by="test_user",
|
|
bounding_box={'x': 100, 'y': 200, 'width': 120, 'height': 40}
|
|
)
|
|
|
|
self.valid_parameters = {
|
|
'visual_anchor': self.test_anchor,
|
|
'click_type': 'left',
|
|
'confidence_threshold': 0.8
|
|
}
|
|
|
|
def test_click_action_creation(self):
|
|
"""Test de création d'une action de clic."""
|
|
action = VWBClickAnchorAction(
|
|
action_id='click_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
self.assertEqual(action.action_id, 'click_001')
|
|
self.assertEqual(action.name, 'Clic sur Ancre Visuelle')
|
|
self.assertEqual(action.visual_anchor, self.test_anchor)
|
|
self.assertEqual(action.click_type, 'left')
|
|
|
|
def test_click_parameter_validation_success(self):
|
|
"""Test de validation réussie des paramètres de clic."""
|
|
action = VWBClickAnchorAction(
|
|
action_id='click_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
errors = action.validate_parameters()
|
|
self.assertEqual(len(errors), 0)
|
|
|
|
def test_click_parameter_validation_failure(self):
|
|
"""Test de validation échouée des paramètres de clic."""
|
|
invalid_params = {
|
|
'visual_anchor': None,
|
|
'click_type': 'invalid',
|
|
'confidence_threshold': 1.5
|
|
}
|
|
|
|
action = VWBClickAnchorAction(
|
|
action_id='click_001',
|
|
parameters=invalid_params
|
|
)
|
|
|
|
errors = action.validate_parameters()
|
|
self.assertGreater(len(errors), 0)
|
|
self.assertTrue(any("Ancre visuelle requise" in error for error in errors))
|
|
self.assertTrue(any("Type de clic invalide" in error for error in errors))
|
|
|
|
def test_successful_click_execution(self):
|
|
"""Test d'exécution réussie du clic."""
|
|
action = VWBClickAnchorAction(
|
|
action_id='click_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
result = action.execute('step_001')
|
|
|
|
self.assertEqual(result.status, VWBActionStatus.SUCCESS)
|
|
self.assertTrue(result.output_data['anchor_found'])
|
|
self.assertGreater(result.output_data['anchor_confidence'], 0)
|
|
# Vérifier qu'il y a au moins 1 evidence (peut y avoir screenshot avant + interaction)
|
|
self.assertGreaterEqual(len(result.evidence_list), 1)
|
|
# Vérifier qu'il y a une evidence de clic
|
|
click_evidence = None
|
|
for evidence in result.evidence_list:
|
|
if evidence.evidence_type == VWBEvidenceType.CLICK_EVIDENCE:
|
|
click_evidence = evidence
|
|
break
|
|
self.assertIsNotNone(click_evidence)
|
|
|
|
def test_click_with_screen_capture_failure(self):
|
|
"""Test de clic avec échec de capture d'écran."""
|
|
failing_capturer = MockScreenCapturer(should_fail=True)
|
|
|
|
action = VWBClickAnchorAction(
|
|
action_id='click_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=failing_capturer
|
|
)
|
|
|
|
result = action.execute('step_001')
|
|
|
|
self.assertEqual(result.status, VWBActionStatus.FAILED)
|
|
self.assertIsNotNone(result.error)
|
|
self.assertEqual(result.error.error_type, VWBErrorType.SCREEN_CAPTURE_FAILED)
|
|
|
|
def test_click_action_info(self):
|
|
"""Test des informations de l'action de clic."""
|
|
action = VWBClickAnchorAction(
|
|
action_id='click_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
info = action.get_action_info()
|
|
|
|
self.assertEqual(info['action_id'], 'click_001')
|
|
self.assertEqual(info['type'], 'click_anchor')
|
|
self.assertEqual(info['parameters']['anchor_name'], 'Bouton Test')
|
|
self.assertEqual(info['parameters']['click_type'], 'left')
|
|
|
|
|
|
class TestVWBTypeTextAction(unittest.TestCase):
|
|
"""Tests pour VWBTypeTextAction."""
|
|
|
|
def setUp(self):
|
|
"""Configuration des tests."""
|
|
self.mock_capturer = MockScreenCapturer()
|
|
|
|
# Créer une ancre pour champ de saisie
|
|
self.input_anchor = create_image_anchor(
|
|
name="Champ Email",
|
|
reference_image_base64="fake_input_image",
|
|
created_by="test_user",
|
|
bounding_box={'x': 200, 'y': 300, 'width': 200, 'height': 30}
|
|
)
|
|
|
|
self.valid_parameters = {
|
|
'visual_anchor': self.input_anchor,
|
|
'text_to_type': 'test@example.com',
|
|
'clear_field_first': True,
|
|
'click_before_typing': True
|
|
}
|
|
|
|
def test_type_action_creation(self):
|
|
"""Test de création d'une action de saisie."""
|
|
action = VWBTypeTextAction(
|
|
action_id='type_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
self.assertEqual(action.action_id, 'type_001')
|
|
self.assertEqual(action.name, 'Saisie de Texte')
|
|
self.assertEqual(action.text_to_type, 'test@example.com')
|
|
self.assertTrue(action.clear_field_first)
|
|
self.assertTrue(action.click_before_typing)
|
|
|
|
def test_type_parameter_validation_success(self):
|
|
"""Test de validation réussie des paramètres de saisie."""
|
|
action = VWBTypeTextAction(
|
|
action_id='type_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
errors = action.validate_parameters()
|
|
self.assertEqual(len(errors), 0)
|
|
|
|
def test_type_parameter_validation_failure(self):
|
|
"""Test de validation échouée des paramètres de saisie."""
|
|
invalid_params = {
|
|
'visual_anchor': None,
|
|
'text_to_type': 123, # Doit être une string
|
|
'typing_speed_ms': -1 # Doit être positif
|
|
}
|
|
|
|
action = VWBTypeTextAction(
|
|
action_id='type_001',
|
|
parameters=invalid_params
|
|
)
|
|
|
|
errors = action.validate_parameters()
|
|
self.assertGreater(len(errors), 0)
|
|
self.assertTrue(any("Ancre visuelle requise" in error for error in errors))
|
|
self.assertTrue(any("doit être une chaîne" in error for error in errors))
|
|
|
|
def test_successful_type_execution(self):
|
|
"""Test d'exécution réussie de la saisie."""
|
|
action = VWBTypeTextAction(
|
|
action_id='type_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
result = action.execute('step_001')
|
|
|
|
self.assertEqual(result.status, VWBActionStatus.SUCCESS)
|
|
self.assertEqual(result.output_data['text_typed'], 'test@example.com')
|
|
self.assertTrue(result.output_data['field_cleared'])
|
|
# Vérifier qu'il y a au moins 1 evidence (peut y avoir screenshot avant + interaction)
|
|
self.assertGreaterEqual(len(result.evidence_list), 1)
|
|
# Vérifier qu'il y a une evidence de saisie
|
|
type_evidence = None
|
|
for evidence in result.evidence_list:
|
|
if evidence.evidence_type == VWBEvidenceType.TYPE_EVIDENCE:
|
|
type_evidence = evidence
|
|
break
|
|
self.assertIsNotNone(type_evidence)
|
|
|
|
def test_type_action_info(self):
|
|
"""Test des informations de l'action de saisie."""
|
|
action = VWBTypeTextAction(
|
|
action_id='type_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
info = action.get_action_info()
|
|
|
|
self.assertEqual(info['action_id'], 'type_001')
|
|
self.assertEqual(info['type'], 'type_text')
|
|
self.assertEqual(info['parameters']['text_to_type'], 'test@example.com')
|
|
self.assertEqual(info['parameters']['anchor_name'], 'Champ Email')
|
|
|
|
|
|
class TestVWBWaitForAnchorAction(unittest.TestCase):
|
|
"""Tests pour VWBWaitForAnchorAction."""
|
|
|
|
def setUp(self):
|
|
"""Configuration des tests."""
|
|
self.mock_capturer = MockScreenCapturer()
|
|
|
|
# Créer une ancre pour l'attente
|
|
self.wait_anchor = create_image_anchor(
|
|
name="Loading Spinner",
|
|
reference_image_base64="fake_spinner_image",
|
|
created_by="test_user"
|
|
)
|
|
|
|
self.valid_parameters = {
|
|
'visual_anchor': self.wait_anchor,
|
|
'wait_mode': 'appear',
|
|
'max_wait_time_ms': 5000,
|
|
'check_interval_ms': 100
|
|
}
|
|
|
|
def test_wait_action_creation(self):
|
|
"""Test de création d'une action d'attente."""
|
|
action = VWBWaitForAnchorAction(
|
|
action_id='wait_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
self.assertEqual(action.action_id, 'wait_001')
|
|
self.assertEqual(action.name, 'Attente d\'Ancre Visuelle')
|
|
self.assertEqual(action.wait_mode, 'appear')
|
|
self.assertEqual(action.max_wait_time_ms, 5000)
|
|
|
|
def test_wait_parameter_validation_success(self):
|
|
"""Test de validation réussie des paramètres d'attente."""
|
|
action = VWBWaitForAnchorAction(
|
|
action_id='wait_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
errors = action.validate_parameters()
|
|
self.assertEqual(len(errors), 0)
|
|
|
|
def test_wait_parameter_validation_failure(self):
|
|
"""Test de validation échouée des paramètres d'attente."""
|
|
invalid_params = {
|
|
'visual_anchor': None,
|
|
'wait_mode': 'invalid_mode',
|
|
'max_wait_time_ms': -1000,
|
|
'check_interval_ms': 10000 # Plus grand que max_wait_time_ms
|
|
}
|
|
|
|
action = VWBWaitForAnchorAction(
|
|
action_id='wait_001',
|
|
parameters=invalid_params
|
|
)
|
|
|
|
errors = action.validate_parameters()
|
|
self.assertGreater(len(errors), 0)
|
|
self.assertTrue(any("Ancre visuelle requise" in error for error in errors))
|
|
self.assertTrue(any("Mode d'attente invalide" in error for error in errors))
|
|
|
|
def test_successful_wait_execution(self):
|
|
"""Test d'exécution réussie de l'attente."""
|
|
# Paramètres pour une attente courte
|
|
short_wait_params = self.valid_parameters.copy()
|
|
short_wait_params['max_wait_time_ms'] = 1000
|
|
short_wait_params['check_interval_ms'] = 100
|
|
|
|
action = VWBWaitForAnchorAction(
|
|
action_id='wait_001',
|
|
parameters=short_wait_params,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
result = action.execute('step_001')
|
|
|
|
# Le résultat peut être SUCCESS ou TIMEOUT selon la simulation
|
|
self.assertIn(result.status, [VWBActionStatus.SUCCESS, VWBActionStatus.TIMEOUT])
|
|
|
|
if result.status == VWBActionStatus.SUCCESS:
|
|
self.assertTrue(result.output_data['condition_met'])
|
|
self.assertGreaterEqual(len(result.evidence_list), 1) # Au moins 1 evidence
|
|
# Vérifier que la première evidence est du bon type
|
|
wait_evidence = None
|
|
for evidence in result.evidence_list:
|
|
if evidence.evidence_type == VWBEvidenceType.WAIT_EVIDENCE:
|
|
wait_evidence = evidence
|
|
break
|
|
self.assertIsNotNone(wait_evidence)
|
|
|
|
def test_wait_timeout(self):
|
|
"""Test de timeout de l'attente."""
|
|
# Paramètres pour un timeout rapide
|
|
timeout_params = self.valid_parameters.copy()
|
|
timeout_params['max_wait_time_ms'] = 200 # Très court
|
|
timeout_params['check_interval_ms'] = 50
|
|
timeout_params['wait_mode'] = 'disappear' # Mode difficile à satisfaire
|
|
|
|
action = VWBWaitForAnchorAction(
|
|
action_id='wait_001',
|
|
parameters=timeout_params,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
result = action.execute('step_001')
|
|
|
|
# Devrait timeout
|
|
self.assertEqual(result.status, VWBActionStatus.TIMEOUT)
|
|
self.assertIsNotNone(result.error)
|
|
self.assertEqual(result.error.error_type, VWBErrorType.WAIT_TIMEOUT)
|
|
self.assertTrue(result.output_data['timeout_reached'])
|
|
|
|
def test_wait_action_info(self):
|
|
"""Test des informations de l'action d'attente."""
|
|
action = VWBWaitForAnchorAction(
|
|
action_id='wait_001',
|
|
parameters=self.valid_parameters,
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
info = action.get_action_info()
|
|
|
|
self.assertEqual(info['action_id'], 'wait_001')
|
|
self.assertEqual(info['type'], 'wait_for_anchor')
|
|
self.assertEqual(info['parameters']['wait_mode'], 'appear')
|
|
self.assertEqual(info['parameters']['anchor_name'], 'Loading Spinner')
|
|
|
|
|
|
class TestActionsIntegration(unittest.TestCase):
|
|
"""Tests d'intégration entre les actions VWB."""
|
|
|
|
def setUp(self):
|
|
"""Configuration des tests d'intégration."""
|
|
self.mock_capturer = MockScreenCapturer()
|
|
|
|
# Créer des ancres pour différents types d'actions
|
|
self.button_anchor = create_image_anchor(
|
|
name="Submit Button",
|
|
reference_image_base64="fake_button_image",
|
|
created_by="test_user"
|
|
)
|
|
|
|
self.input_anchor = create_image_anchor(
|
|
name="Username Field",
|
|
reference_image_base64="fake_input_image",
|
|
created_by="test_user"
|
|
)
|
|
|
|
def test_sequential_actions_workflow(self):
|
|
"""Test d'un workflow séquentiel d'actions."""
|
|
|
|
# Action 1: Saisir du texte
|
|
type_action = VWBTypeTextAction(
|
|
action_id='type_001',
|
|
parameters={
|
|
'visual_anchor': self.input_anchor,
|
|
'text_to_type': 'testuser',
|
|
'clear_field_first': True
|
|
},
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
# Action 2: Cliquer sur le bouton
|
|
click_action = VWBClickAnchorAction(
|
|
action_id='click_001',
|
|
parameters={
|
|
'visual_anchor': self.button_anchor,
|
|
'click_type': 'left'
|
|
},
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
# Exécuter les actions en séquence
|
|
type_result = type_action.execute('step_001')
|
|
click_result = click_action.execute('step_002')
|
|
|
|
# Vérifier les résultats
|
|
self.assertEqual(type_result.status, VWBActionStatus.SUCCESS)
|
|
self.assertEqual(click_result.status, VWBActionStatus.SUCCESS)
|
|
|
|
# Vérifier que les ancres ont été utilisées
|
|
self.assertEqual(self.input_anchor.usage_count, 1)
|
|
self.assertEqual(self.button_anchor.usage_count, 1)
|
|
|
|
def test_action_evidence_chain(self):
|
|
"""Test de la chaîne d'evidence entre actions."""
|
|
click_action = VWBClickAnchorAction(
|
|
action_id='click_001',
|
|
parameters={
|
|
'visual_anchor': self.button_anchor,
|
|
'click_type': 'left'
|
|
},
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
result = click_action.execute('step_001')
|
|
|
|
# Vérifier la présence d'evidence
|
|
self.assertGreater(len(result.evidence_list), 0)
|
|
|
|
# Vérifier les métadonnées de l'evidence
|
|
evidence = result.evidence_list[0]
|
|
self.assertEqual(evidence.action_id, 'click_001')
|
|
self.assertEqual(evidence.step_id, 'step_001')
|
|
self.assertIsNotNone(evidence.data.get('anchor_id'))
|
|
|
|
def test_anchor_statistics_update(self):
|
|
"""Test de mise à jour des statistiques d'ancre."""
|
|
initial_usage = self.button_anchor.usage_count
|
|
initial_success_rate = self.button_anchor.success_rate
|
|
|
|
click_action = VWBClickAnchorAction(
|
|
action_id='click_001',
|
|
parameters={
|
|
'visual_anchor': self.button_anchor,
|
|
'click_type': 'left'
|
|
},
|
|
screen_capturer=self.mock_capturer
|
|
)
|
|
|
|
result = click_action.execute('step_001')
|
|
|
|
# Vérifier la mise à jour des statistiques
|
|
self.assertEqual(self.button_anchor.usage_count, initial_usage + 1)
|
|
|
|
if result.status == VWBActionStatus.SUCCESS:
|
|
self.assertGreaterEqual(self.button_anchor.success_rate, initial_success_rate)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# Configuration des tests
|
|
unittest.TestCase.maxDiff = None
|
|
|
|
# Exécution des tests
|
|
print("=" * 70)
|
|
print(" TESTS UNITAIRES - ACTIONS VWB")
|
|
print("=" * 70)
|
|
print("Auteur : Dom, Alice, Kiro - 09 janvier 2026")
|
|
print("")
|
|
|
|
# Créer la suite de tests
|
|
loader = unittest.TestLoader()
|
|
suite = unittest.TestSuite()
|
|
|
|
# Ajouter les classes de tests
|
|
suite.addTests(loader.loadTestsFromTestCase(TestBaseVWBAction))
|
|
suite.addTests(loader.loadTestsFromTestCase(TestVWBClickAnchorAction))
|
|
suite.addTests(loader.loadTestsFromTestCase(TestVWBTypeTextAction))
|
|
suite.addTests(loader.loadTestsFromTestCase(TestVWBWaitForAnchorAction))
|
|
suite.addTests(loader.loadTestsFromTestCase(TestActionsIntegration))
|
|
|
|
# Exécuter les tests
|
|
runner = unittest.TextTestRunner(verbosity=2)
|
|
result = runner.run(suite)
|
|
|
|
# Résumé
|
|
print("\n" + "=" * 70)
|
|
print(f"RÉSUMÉ DES TESTS ACTIONS VWB")
|
|
print("=" * 70)
|
|
print(f"Tests exécutés : {result.testsRun}")
|
|
print(f"Succès : {result.testsRun - len(result.failures) - len(result.errors)}")
|
|
print(f"Échecs : {len(result.failures)}")
|
|
print(f"Erreurs : {len(result.errors)}")
|
|
|
|
if result.failures:
|
|
print("\nÉCHECS :")
|
|
for test, traceback in result.failures:
|
|
print(f"- {test}: {traceback}")
|
|
|
|
if result.errors:
|
|
print("\nERREURS :")
|
|
for test, traceback in result.errors:
|
|
print(f"- {test}: {traceback}")
|
|
|
|
success_rate = ((result.testsRun - len(result.failures) - len(result.errors)) / result.testsRun) * 100
|
|
print(f"\nTaux de succès : {success_rate:.1f}%")
|
|
|
|
if success_rate == 100.0:
|
|
print("🎉 TOUS LES TESTS ACTIONS VWB SONT RÉUSSIS !")
|
|
else:
|
|
print("⚠️ Certains tests ont échoué - vérification nécessaire") |