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>
This commit is contained in:
708
tests/unit/test_vwb_actions_09jan2026.py
Normal file
708
tests/unit/test_vwb_actions_09jan2026.py
Normal file
@@ -0,0 +1,708 @@
|
||||
"""
|
||||
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")
|
||||
Reference in New Issue
Block a user