- 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>
233 lines
8.1 KiB
Python
233 lines
8.1 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Tests de stabilité simplifiés - Visual Workflow Builder Frontend V2
|
|
Auteur : Dom, Alice, Kiro - 09 janvier 2026
|
|
|
|
Script de validation sans dépendance pytest.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
# Chemin vers le frontend VWB
|
|
SCRIPT_DIR = Path(__file__).parent
|
|
PROJECT_ROOT = SCRIPT_DIR.parent.parent
|
|
FRONTEND_PATH = PROJECT_ROOT / "visual_workflow_builder" / "frontend"
|
|
SRC_PATH = FRONTEND_PATH / "src"
|
|
|
|
|
|
def test_api_client_initial_state_is_offline():
|
|
"""Vérifie que l'état initial du client API est 'offline'."""
|
|
api_client_path = SRC_PATH / "services" / "apiClient.ts"
|
|
if not api_client_path.exists():
|
|
return False, f"Fichier non trouvé: {api_client_path}"
|
|
|
|
content = api_client_path.read_text(encoding='utf-8')
|
|
|
|
# Vérifier que l'état initial est 'offline' et non 'checking'
|
|
if "connectionState: ConnectionState = 'offline'" not in content:
|
|
return False, "L'état initial du client API doit être 'offline'"
|
|
|
|
if "connectionState: ConnectionState = 'checking'" in content:
|
|
return False, "L'état initial ne doit PAS être 'checking'"
|
|
|
|
return True, "OK"
|
|
|
|
|
|
def test_api_client_lazy_initialization():
|
|
"""Vérifie que l'initialisation est paresseuse (lazy)."""
|
|
api_client_path = SRC_PATH / "services" / "apiClient.ts"
|
|
content = api_client_path.read_text(encoding='utf-8')
|
|
|
|
# Vérifier la présence du commentaire sur l'initialisation paresseuse
|
|
if "paresseuse" not in content.lower() and "lazy" not in content.lower():
|
|
return False, "Le code doit mentionner l'initialisation paresseuse (lazy)"
|
|
|
|
return True, "OK"
|
|
|
|
|
|
def test_api_client_async_notifications():
|
|
"""Vérifie que les notifications sont asynchrones (setTimeout)."""
|
|
api_client_path = SRC_PATH / "services" / "apiClient.ts"
|
|
content = api_client_path.read_text(encoding='utf-8')
|
|
|
|
# Vérifier la présence de setTimeout pour les notifications
|
|
if 'setTimeout' not in content:
|
|
return False, "Les notifications doivent être asynchrones (setTimeout)"
|
|
|
|
return True, "OK"
|
|
|
|
|
|
def test_connection_status_hook_initial_state():
|
|
"""Vérifie que useConnectionStatus a un état initial 'offline'."""
|
|
hook_path = SRC_PATH / "hooks" / "useConnectionStatus.ts"
|
|
if not hook_path.exists():
|
|
return False, f"Fichier non trouvé: {hook_path}"
|
|
|
|
content = hook_path.read_text(encoding='utf-8')
|
|
|
|
# Vérifier que l'état initial est 'offline'
|
|
if "status: 'offline'" not in content:
|
|
return False, "L'état initial du hook doit être 'offline'"
|
|
|
|
return True, "OK"
|
|
|
|
|
|
def test_connection_status_hook_uses_refs():
|
|
"""Vérifie que le hook utilise des refs pour les callbacks."""
|
|
hook_path = SRC_PATH / "hooks" / "useConnectionStatus.ts"
|
|
content = hook_path.read_text(encoding='utf-8')
|
|
|
|
if 'useRef' not in content:
|
|
return False, "Le hook doit utiliser useRef pour éviter les re-renders"
|
|
|
|
return True, "OK"
|
|
|
|
|
|
def test_connection_status_hook_initial_state_constant():
|
|
"""Vérifie que l'état initial est une constante stable."""
|
|
hook_path = SRC_PATH / "hooks" / "useConnectionStatus.ts"
|
|
content = hook_path.read_text(encoding='utf-8')
|
|
|
|
if 'INITIAL_STATE' not in content:
|
|
return False, "L'état initial doit être une constante INITIAL_STATE"
|
|
|
|
return True, "OK"
|
|
|
|
|
|
def test_use_api_client_hook_initial_offline():
|
|
"""Vérifie que useConnectionState a un état initial 'offline'."""
|
|
hook_path = SRC_PATH / "hooks" / "useApiClient.ts"
|
|
if not hook_path.exists():
|
|
return False, f"Fichier non trouvé: {hook_path}"
|
|
|
|
content = hook_path.read_text(encoding='utf-8')
|
|
|
|
# Vérifier que l'état initial est 'offline'
|
|
if "'offline'" not in content:
|
|
return False, "useConnectionState doit avoir un état initial 'offline'"
|
|
|
|
return True, "OK"
|
|
|
|
|
|
def test_workflow_manager_uses_connection_state():
|
|
"""Vérifie que WorkflowManager utilise useConnectionState."""
|
|
component_path = SRC_PATH / "components" / "WorkflowManager" / "index.tsx"
|
|
if not component_path.exists():
|
|
return False, f"Fichier non trouvé: {component_path}"
|
|
|
|
content = component_path.read_text(encoding='utf-8')
|
|
|
|
if 'useConnectionState' not in content:
|
|
return False, "WorkflowManager doit utiliser useConnectionState"
|
|
|
|
return True, "OK"
|
|
|
|
|
|
def test_executor_uses_connection_status():
|
|
"""Vérifie que Executor utilise useConnectionStatus."""
|
|
component_path = SRC_PATH / "components" / "Executor" / "index.tsx"
|
|
if not component_path.exists():
|
|
return False, f"Fichier non trouvé: {component_path}"
|
|
|
|
content = component_path.read_text(encoding='utf-8')
|
|
|
|
if 'useConnectionStatus' not in content:
|
|
return False, "Executor doit utiliser useConnectionStatus"
|
|
|
|
return True, "OK"
|
|
|
|
|
|
def test_french_comments_in_api_client():
|
|
"""Vérifie que apiClient.ts a des commentaires en français."""
|
|
api_client_path = SRC_PATH / "services" / "apiClient.ts"
|
|
content = api_client_path.read_text(encoding='utf-8')
|
|
|
|
french_words = ['Auteur', 'janvier', 'gestion', 'connexion', 'hors ligne']
|
|
found_french = any(word in content for word in french_words)
|
|
|
|
if not found_french:
|
|
return False, "apiClient.ts doit avoir des commentaires en français"
|
|
|
|
return True, "OK"
|
|
|
|
|
|
def test_typescript_syntax_balanced():
|
|
"""Vérifie la syntaxe TypeScript basique."""
|
|
files_to_check = [
|
|
SRC_PATH / "services" / "apiClient.ts",
|
|
SRC_PATH / "hooks" / "useApiClient.ts",
|
|
SRC_PATH / "hooks" / "useConnectionStatus.ts",
|
|
]
|
|
|
|
for file_path in files_to_check:
|
|
if not file_path.exists():
|
|
continue
|
|
|
|
content = file_path.read_text(encoding='utf-8')
|
|
|
|
if content.count('{') != content.count('}'):
|
|
return False, f"Accolades non équilibrées dans {file_path.name}"
|
|
|
|
if content.count('(') != content.count(')'):
|
|
return False, f"Parenthèses non équilibrées dans {file_path.name}"
|
|
|
|
return True, "OK"
|
|
|
|
|
|
def run_all_tests():
|
|
"""Exécute tous les tests et affiche les résultats."""
|
|
tests = [
|
|
("État initial apiClient = 'offline'", test_api_client_initial_state_is_offline),
|
|
("Initialisation paresseuse (lazy)", test_api_client_lazy_initialization),
|
|
("Notifications asynchrones (setTimeout)", test_api_client_async_notifications),
|
|
("useConnectionStatus état initial 'offline'", test_connection_status_hook_initial_state),
|
|
("useConnectionStatus utilise useRef", test_connection_status_hook_uses_refs),
|
|
("useConnectionStatus INITIAL_STATE constant", test_connection_status_hook_initial_state_constant),
|
|
("useApiClient état initial 'offline'", test_use_api_client_hook_initial_offline),
|
|
("WorkflowManager utilise useConnectionState", test_workflow_manager_uses_connection_state),
|
|
("Executor utilise useConnectionStatus", test_executor_uses_connection_status),
|
|
("Commentaires français dans apiClient", test_french_comments_in_api_client),
|
|
("Syntaxe TypeScript équilibrée", test_typescript_syntax_balanced),
|
|
]
|
|
|
|
print("=" * 70)
|
|
print("Tests de Stabilité - Visual Workflow Builder Frontend V2")
|
|
print("Auteur : Dom, Alice, Kiro - 09 janvier 2026")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
passed = 0
|
|
failed = 0
|
|
|
|
for name, test_func in tests:
|
|
try:
|
|
success, message = test_func()
|
|
if success:
|
|
print(f"✅ PASS: {name}")
|
|
passed += 1
|
|
else:
|
|
print(f"❌ FAIL: {name}")
|
|
print(f" Raison: {message}")
|
|
failed += 1
|
|
except Exception as e:
|
|
print(f"❌ ERROR: {name}")
|
|
print(f" Exception: {e}")
|
|
failed += 1
|
|
|
|
print()
|
|
print("=" * 70)
|
|
print(f"Résultats: {passed} passés, {failed} échoués sur {len(tests)} tests")
|
|
print("=" * 70)
|
|
|
|
return failed == 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
success = run_all_tests()
|
|
sys.exit(0 if success else 1)
|