- 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>
397 lines
15 KiB
Python
397 lines
15 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Script de validation - Checkpoint Interface Propriétés d'Étapes
|
|
Auteur : Dom, Alice, Kiro - 12 janvier 2026
|
|
|
|
Ce script valide l'implémentation des composants de base de l'interface
|
|
des propriétés d'étapes selon les spécifications définies.
|
|
|
|
Composants validés :
|
|
- ParameterFieldRenderer (Tâche 1)
|
|
- StandardParametersEditor (Tâche 2)
|
|
- VWBActionProperties amélioré (Tâche 3)
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Dict, List, Any, Optional
|
|
|
|
# Configuration
|
|
PROJECT_ROOT = Path(__file__).parent.parent
|
|
FRONTEND_PATH = PROJECT_ROOT / "visual_workflow_builder" / "frontend"
|
|
COMPONENTS_PATH = FRONTEND_PATH / "src" / "components" / "PropertiesPanel"
|
|
HOOKS_PATH = FRONTEND_PATH / "src" / "hooks"
|
|
TESTS_PATH = PROJECT_ROOT / "tests" / "property"
|
|
|
|
class ValidationResult:
|
|
"""Résultat de validation"""
|
|
|
|
def __init__(self, name: str):
|
|
self.name = name
|
|
self.success = False
|
|
self.errors: List[str] = []
|
|
self.warnings: List[str] = []
|
|
self.details: Dict[str, Any] = {}
|
|
|
|
def add_error(self, message: str):
|
|
self.errors.append(message)
|
|
|
|
def add_warning(self, message: str):
|
|
self.warnings.append(message)
|
|
|
|
def set_success(self, success: bool):
|
|
self.success = success
|
|
|
|
def add_detail(self, key: str, value: Any):
|
|
self.details[key] = value
|
|
|
|
class InterfacePropertiesValidator:
|
|
"""Validateur pour l'interface des propriétés d'étapes"""
|
|
|
|
def __init__(self):
|
|
self.results: List[ValidationResult] = []
|
|
|
|
def validate_file_structure(self) -> ValidationResult:
|
|
"""Valide la structure des fichiers créés"""
|
|
result = ValidationResult("Structure des fichiers")
|
|
|
|
# Fichiers requis
|
|
required_files = [
|
|
COMPONENTS_PATH / "ParameterFieldRenderer.tsx",
|
|
COMPONENTS_PATH / "StandardParametersEditor.tsx",
|
|
COMPONENTS_PATH / "VWBActionProperties.tsx",
|
|
HOOKS_PATH / "useAutoSave.ts",
|
|
]
|
|
|
|
# Tests de propriétés requis
|
|
required_tests = [
|
|
TESTS_PATH / "test_parameter_field_renderer_properties_12jan2026.py",
|
|
TESTS_PATH / "test_standard_parameters_editor_properties_12jan2026.py",
|
|
TESTS_PATH / "test_auto_save_properties_12jan2026.py",
|
|
TESTS_PATH / "test_vwb_action_properties_12jan2026.py",
|
|
]
|
|
|
|
all_files = required_files + required_tests
|
|
|
|
for file_path in all_files:
|
|
if not file_path.exists():
|
|
result.add_error(f"Fichier manquant: {file_path.relative_to(PROJECT_ROOT)}")
|
|
else:
|
|
# Vérifier la taille du fichier (doit être > 1KB)
|
|
file_size = file_path.stat().st_size
|
|
if file_size < 1024:
|
|
result.add_warning(f"Fichier très petit: {file_path.name} ({file_size} bytes)")
|
|
result.add_detail(f"size_{file_path.name}", file_size)
|
|
|
|
result.set_success(len(result.errors) == 0)
|
|
return result
|
|
|
|
def validate_typescript_compilation(self) -> ValidationResult:
|
|
"""Valide la compilation TypeScript des composants"""
|
|
result = ValidationResult("Compilation TypeScript")
|
|
|
|
try:
|
|
# Vérifier si TypeScript est disponible
|
|
ts_check = subprocess.run(
|
|
["npx", "tsc", "--version"],
|
|
cwd=FRONTEND_PATH,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=10
|
|
)
|
|
|
|
if ts_check.returncode != 0:
|
|
result.add_error("TypeScript non disponible")
|
|
result.set_success(False)
|
|
return result
|
|
|
|
result.add_detail("typescript_version", ts_check.stdout.strip())
|
|
|
|
# Compilation des fichiers TypeScript
|
|
ts_files = [
|
|
"src/components/PropertiesPanel/ParameterFieldRenderer.tsx",
|
|
"src/components/PropertiesPanel/StandardParametersEditor.tsx",
|
|
"src/hooks/useAutoSave.ts"
|
|
]
|
|
|
|
for ts_file in ts_files:
|
|
compile_result = subprocess.run(
|
|
["npx", "tsc", "--noEmit", "--project", "."],
|
|
cwd=FRONTEND_PATH,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=30
|
|
)
|
|
|
|
if compile_result.returncode != 0:
|
|
result.add_error(f"Erreur compilation globale: {compile_result.stderr}")
|
|
break
|
|
else:
|
|
result.add_detail(f"compiled_{Path(ts_file).name}", True)
|
|
|
|
result.set_success(len(result.errors) == 0)
|
|
|
|
except subprocess.TimeoutExpired:
|
|
result.add_error("Timeout lors de la compilation TypeScript")
|
|
result.set_success(False)
|
|
except Exception as e:
|
|
result.add_error(f"Erreur lors de la validation TypeScript: {str(e)}")
|
|
result.set_success(False)
|
|
|
|
return result
|
|
|
|
def validate_component_interfaces(self) -> ValidationResult:
|
|
"""Valide les interfaces des composants"""
|
|
result = ValidationResult("Interfaces des composants")
|
|
|
|
# Vérifier les exports et interfaces dans les fichiers
|
|
component_checks = [
|
|
{
|
|
"file": COMPONENTS_PATH / "ParameterFieldRenderer.tsx",
|
|
"exports": ["ParameterFieldRenderer", "fieldRendererRegistry", "FieldRendererType"],
|
|
"interfaces": ["ParameterFieldRendererProps", "CustomFieldRenderer"]
|
|
},
|
|
{
|
|
"file": COMPONENTS_PATH / "StandardParametersEditor.tsx",
|
|
"exports": ["StandardParametersEditor"],
|
|
"interfaces": ["StandardParametersEditorProps"]
|
|
},
|
|
{
|
|
"file": HOOKS_PATH / "useAutoSave.ts",
|
|
"exports": ["useAutoSave", "useStepParametersAutoSave", "useSyncedAutoSave"],
|
|
"interfaces": ["AutoSaveOptions", "SaveState", "UseAutoSaveResult"]
|
|
}
|
|
]
|
|
|
|
for check in component_checks:
|
|
file_path = check["file"]
|
|
if not file_path.exists():
|
|
result.add_error(f"Fichier manquant: {file_path.name}")
|
|
continue
|
|
|
|
try:
|
|
content = file_path.read_text(encoding='utf-8')
|
|
|
|
# Vérifier les exports
|
|
for export_name in check["exports"]:
|
|
if f"export" in content and export_name in content:
|
|
result.add_detail(f"export_{export_name}", True)
|
|
else:
|
|
result.add_warning(f"Export manquant dans {file_path.name}: {export_name}")
|
|
|
|
# Vérifier les interfaces
|
|
for interface_name in check["interfaces"]:
|
|
if f"interface {interface_name}" in content:
|
|
result.add_detail(f"interface_{interface_name}", True)
|
|
else:
|
|
result.add_warning(f"Interface manquante dans {file_path.name}: {interface_name}")
|
|
|
|
# Vérifier les commentaires français
|
|
if "Auteur : Dom, Alice, Kiro" not in content:
|
|
result.add_warning(f"Attribution d'auteur manquante dans {file_path.name}")
|
|
|
|
if "12 janvier 2026" not in content:
|
|
result.add_warning(f"Date manquante dans {file_path.name}")
|
|
|
|
except Exception as e:
|
|
result.add_error(f"Erreur lecture {file_path.name}: {str(e)}")
|
|
|
|
result.set_success(len(result.errors) == 0)
|
|
return result
|
|
|
|
def validate_property_tests(self) -> ValidationResult:
|
|
"""Valide les tests de propriétés"""
|
|
result = ValidationResult("Tests de propriétés")
|
|
|
|
test_files = [
|
|
TESTS_PATH / "test_parameter_field_renderer_properties_12jan2026.py",
|
|
TESTS_PATH / "test_standard_parameters_editor_properties_12jan2026.py",
|
|
TESTS_PATH / "test_auto_save_properties_12jan2026.py",
|
|
TESTS_PATH / "test_vwb_action_properties_12jan2026.py",
|
|
]
|
|
|
|
for test_file in test_files:
|
|
if not test_file.exists():
|
|
result.add_error(f"Test manquant: {test_file.name}")
|
|
continue
|
|
|
|
try:
|
|
content = test_file.read_text(encoding='utf-8')
|
|
|
|
# Vérifier la structure des tests de propriétés
|
|
required_elements = [
|
|
"from hypothesis import given",
|
|
"PROPERTY_TEST_SETTINGS",
|
|
"Feature: interface-proprietes-etapes-complete",
|
|
"Property",
|
|
"Validates: Requirements"
|
|
]
|
|
|
|
for element in required_elements:
|
|
if element in content:
|
|
result.add_detail(f"has_{element.replace(' ', '_').replace(':', '')}", True)
|
|
else:
|
|
result.add_warning(f"Élément manquant dans {test_file.name}: {element}")
|
|
|
|
# Compter les tests de propriétés
|
|
property_count = content.count("def test_property_")
|
|
result.add_detail(f"property_tests_{test_file.name}", property_count)
|
|
|
|
if property_count == 0:
|
|
result.add_error(f"Aucun test de propriété dans {test_file.name}")
|
|
|
|
except Exception as e:
|
|
result.add_error(f"Erreur lecture test {test_file.name}: {str(e)}")
|
|
|
|
result.set_success(len(result.errors) == 0)
|
|
return result
|
|
|
|
def validate_integration_readiness(self) -> ValidationResult:
|
|
"""Valide la préparation à l'intégration"""
|
|
result = ValidationResult("Préparation à l'intégration")
|
|
|
|
# Vérifier que PropertiesPanel existe et peut être modifié
|
|
properties_panel_path = COMPONENTS_PATH / "index.tsx"
|
|
|
|
if not properties_panel_path.exists():
|
|
result.add_error("PropertiesPanel principal manquant")
|
|
result.set_success(False)
|
|
return result
|
|
|
|
try:
|
|
content = properties_panel_path.read_text(encoding='utf-8')
|
|
|
|
# Vérifier les imports nécessaires
|
|
required_imports = [
|
|
"ParameterFieldRenderer",
|
|
"useStepTypeResolver",
|
|
"VWBActionProperties"
|
|
]
|
|
|
|
for import_name in required_imports:
|
|
if import_name in content:
|
|
result.add_detail(f"import_{import_name}", True)
|
|
else:
|
|
result.add_warning(f"Import manquant: {import_name}")
|
|
|
|
# Vérifier la structure du composant
|
|
if "const PropertiesPanel" in content:
|
|
result.add_detail("component_structure", True)
|
|
else:
|
|
result.add_error("Structure de composant invalide")
|
|
|
|
# Vérifier la mémorisation
|
|
if "memo(PropertiesPanel" in content:
|
|
result.add_detail("memoization", True)
|
|
else:
|
|
result.add_warning("Mémorisation manquante")
|
|
|
|
result.set_success(len(result.errors) == 0)
|
|
|
|
except Exception as e:
|
|
result.add_error(f"Erreur validation PropertiesPanel: {str(e)}")
|
|
result.set_success(False)
|
|
|
|
return result
|
|
|
|
def run_validation(self) -> Dict[str, Any]:
|
|
"""Exécute toutes les validations"""
|
|
print("🔍 Validation de l'interface des propriétés d'étapes - Checkpoint")
|
|
print("=" * 70)
|
|
|
|
# Exécuter toutes les validations
|
|
validations = [
|
|
self.validate_file_structure,
|
|
self.validate_typescript_compilation,
|
|
self.validate_component_interfaces,
|
|
self.validate_property_tests,
|
|
self.validate_integration_readiness,
|
|
]
|
|
|
|
for validation_func in validations:
|
|
print(f"\n📋 {validation_func.__name__.replace('validate_', '').replace('_', ' ').title()}...")
|
|
result = validation_func()
|
|
self.results.append(result)
|
|
|
|
if result.success:
|
|
print(f"✅ {result.name} - Succès")
|
|
else:
|
|
print(f"❌ {result.name} - Échec")
|
|
|
|
# Afficher les erreurs
|
|
for error in result.errors:
|
|
print(f" 🔴 {error}")
|
|
|
|
# Afficher les avertissements
|
|
for warning in result.warnings:
|
|
print(f" 🟡 {warning}")
|
|
|
|
# Résumé global
|
|
print("\n" + "=" * 70)
|
|
print("📊 RÉSUMÉ DE VALIDATION")
|
|
print("=" * 70)
|
|
|
|
total_validations = len(self.results)
|
|
successful_validations = sum(1 for r in self.results if r.success)
|
|
total_errors = sum(len(r.errors) for r in self.results)
|
|
total_warnings = sum(len(r.warnings) for r in self.results)
|
|
|
|
print(f"Validations réussies: {successful_validations}/{total_validations}")
|
|
print(f"Erreurs totales: {total_errors}")
|
|
print(f"Avertissements totaux: {total_warnings}")
|
|
|
|
# Statut global
|
|
global_success = successful_validations == total_validations and total_errors == 0
|
|
|
|
if global_success:
|
|
print("\n🎉 VALIDATION GLOBALE RÉUSSIE")
|
|
print("✅ Tous les composants sont prêts pour l'intégration")
|
|
else:
|
|
print("\n⚠️ VALIDATION GLOBALE PARTIELLE")
|
|
print("🔧 Corrections nécessaires avant l'intégration")
|
|
|
|
# Prochaines étapes
|
|
print("\n📋 PROCHAINES ÉTAPES:")
|
|
print("1. Créer les composants d'état (EmptyStateMessage, LoadingState)")
|
|
print("2. Intégrer tous les composants dans PropertiesPanel")
|
|
print("3. Implémenter les fonctionnalités avancées de validation")
|
|
print("4. Optimiser les performances et l'accessibilité")
|
|
|
|
return {
|
|
"global_success": global_success,
|
|
"successful_validations": successful_validations,
|
|
"total_validations": total_validations,
|
|
"total_errors": total_errors,
|
|
"total_warnings": total_warnings,
|
|
"results": [
|
|
{
|
|
"name": r.name,
|
|
"success": r.success,
|
|
"errors": r.errors,
|
|
"warnings": r.warnings,
|
|
"details": r.details
|
|
}
|
|
for r in self.results
|
|
]
|
|
}
|
|
|
|
def main():
|
|
"""Fonction principale"""
|
|
validator = InterfacePropertiesValidator()
|
|
results = validator.run_validation()
|
|
|
|
# Sauvegarder les résultats
|
|
results_file = PROJECT_ROOT / "validation_checkpoint_interface_proprietes_12jan2026.json"
|
|
with open(results_file, 'w', encoding='utf-8') as f:
|
|
json.dump(results, f, indent=2, ensure_ascii=False)
|
|
|
|
print(f"\n💾 Résultats sauvegardés dans: {results_file}")
|
|
|
|
# Code de sortie
|
|
sys.exit(0 if results["global_success"] else 1)
|
|
|
|
if __name__ == "__main__":
|
|
main() |