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:
434
scripts/validation_integration_complete_proprietes_12jan2026.py
Normal file
434
scripts/validation_integration_complete_proprietes_12jan2026.py
Normal file
@@ -0,0 +1,434 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script de validation - Intégration complète de l'interface des propriétés
|
||||
Auteur : Dom, Alice, Kiro - 12 janvier 2026
|
||||
|
||||
Ce script valide l'intégration complète de tous les composants de l'interface
|
||||
des propriétés d'étapes après la Tâche 5 (intégration dans PropertiesPanel).
|
||||
|
||||
Composants validés :
|
||||
- ParameterFieldRenderer (Tâche 1)
|
||||
- StandardParametersEditor (Tâche 2)
|
||||
- VWBActionProperties amélioré (Tâche 3)
|
||||
- EmptyStateMessage et LoadingState (Tâche 4)
|
||||
- Intégration complète dans PropertiesPanel (Tâche 5)
|
||||
"""
|
||||
|
||||
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 IntegrationValidator:
|
||||
"""Validateur pour l'intégration complète de l'interface des propriétés"""
|
||||
|
||||
def __init__(self):
|
||||
self.results: List[Dict[str, Any]] = []
|
||||
|
||||
def validate_complete_file_structure(self) -> Dict[str, Any]:
|
||||
"""Valide la structure complète des fichiers après intégration"""
|
||||
result = {
|
||||
'name': 'Structure complète des fichiers',
|
||||
'success': False,
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
'details': {}
|
||||
}
|
||||
|
||||
# Tous les fichiers requis après Tâche 5
|
||||
required_files = [
|
||||
# Composants principaux
|
||||
COMPONENTS_PATH / "ParameterFieldRenderer.tsx",
|
||||
COMPONENTS_PATH / "StandardParametersEditor.tsx",
|
||||
COMPONENTS_PATH / "VWBActionProperties.tsx",
|
||||
COMPONENTS_PATH / "EmptyStateMessage.tsx",
|
||||
COMPONENTS_PATH / "LoadingState.tsx",
|
||||
COMPONENTS_PATH / "index.tsx", # PropertiesPanel intégré
|
||||
|
||||
# Hooks
|
||||
HOOKS_PATH / "useAutoSave.ts",
|
||||
|
||||
# Tests de propriétés
|
||||
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",
|
||||
TESTS_PATH / "test_empty_state_message_properties_12jan2026.py",
|
||||
TESTS_PATH / "test_loading_state_properties_12jan2026.py",
|
||||
]
|
||||
|
||||
for file_path in required_files:
|
||||
if not file_path.exists():
|
||||
result['errors'].append(f"Fichier manquant: {file_path.relative_to(PROJECT_ROOT)}")
|
||||
else:
|
||||
# Vérifier la taille du fichier
|
||||
file_size = file_path.stat().st_size
|
||||
if file_size < 1024: # Moins de 1KB
|
||||
result['warnings'].append(f"Fichier très petit: {file_path.name} ({file_size} bytes)")
|
||||
result['details'][f"size_{file_path.name}"] = file_size
|
||||
|
||||
result['success'] = len(result['errors']) == 0
|
||||
return result
|
||||
|
||||
def validate_properties_panel_integration(self) -> Dict[str, Any]:
|
||||
"""Valide l'intégration dans PropertiesPanel"""
|
||||
result = {
|
||||
'name': 'Intégration PropertiesPanel',
|
||||
'success': False,
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
'details': {}
|
||||
}
|
||||
|
||||
properties_panel_path = COMPONENTS_PATH / "index.tsx"
|
||||
|
||||
if not properties_panel_path.exists():
|
||||
result['errors'].append("PropertiesPanel principal manquant")
|
||||
return result
|
||||
|
||||
try:
|
||||
content = properties_panel_path.read_text(encoding='utf-8')
|
||||
|
||||
# Vérifier les imports des nouveaux composants
|
||||
required_imports = [
|
||||
'StandardParametersEditor',
|
||||
'EmptyStateMessage',
|
||||
'LoadingState',
|
||||
'StepResolutionLoading',
|
||||
'VWBActionLoading',
|
||||
'useStepParametersAutoSave'
|
||||
]
|
||||
|
||||
for import_name in required_imports:
|
||||
if import_name in content:
|
||||
result['details'][f"import_{import_name}"] = True
|
||||
else:
|
||||
result['errors'].append(f"Import manquant: {import_name}")
|
||||
|
||||
# Vérifier la logique de rendu conditionnel
|
||||
conditional_logic_checks = [
|
||||
'getDisplayState',
|
||||
'displayState.type',
|
||||
'case \'loading\'',
|
||||
'case \'empty\'',
|
||||
'case \'vwb-properties\'',
|
||||
'case \'standard-parameters\''
|
||||
]
|
||||
|
||||
for check in conditional_logic_checks:
|
||||
if check in content:
|
||||
result['details'][f"logic_{check.replace(' ', '_').replace('\'', '')}"] = True
|
||||
else:
|
||||
result['warnings'].append(f"Logique conditionnelle manquante: {check}")
|
||||
|
||||
# Vérifier l'intégration de l'auto-sauvegarde
|
||||
autosave_checks = [
|
||||
'autoSave.triggerSave',
|
||||
'autoSave.saveState',
|
||||
'handleValidationChange'
|
||||
]
|
||||
|
||||
for check in autosave_checks:
|
||||
if check in content:
|
||||
result['details'][f"autosave_{check.replace('.', '_')}"] = True
|
||||
else:
|
||||
result['warnings'].append(f"Intégration auto-sauvegarde manquante: {check}")
|
||||
|
||||
# Vérifier la suppression de l'ancien code
|
||||
deprecated_elements = [
|
||||
'renderParameterField',
|
||||
'TextField',
|
||||
'FormControl',
|
||||
'Switch'
|
||||
]
|
||||
|
||||
for element in deprecated_elements:
|
||||
if element in content:
|
||||
result['warnings'].append(f"Élément obsolète encore présent: {element}")
|
||||
|
||||
result['success'] = len(result['errors']) == 0
|
||||
|
||||
except Exception as e:
|
||||
result['errors'].append(f"Erreur lecture PropertiesPanel: {str(e)}")
|
||||
result['success'] = False
|
||||
|
||||
return result
|
||||
|
||||
def validate_component_exports(self) -> Dict[str, Any]:
|
||||
"""Valide les exports de tous les composants"""
|
||||
result = {
|
||||
'name': 'Exports des composants',
|
||||
'success': False,
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
'details': {}
|
||||
}
|
||||
|
||||
# Vérifications des exports par composant
|
||||
component_exports = [
|
||||
{
|
||||
'file': COMPONENTS_PATH / "ParameterFieldRenderer.tsx",
|
||||
'exports': ['ParameterFieldRenderer', 'fieldRendererRegistry', 'FieldRendererType'],
|
||||
'default_export': 'ParameterFieldRenderer'
|
||||
},
|
||||
{
|
||||
'file': COMPONENTS_PATH / "StandardParametersEditor.tsx",
|
||||
'exports': ['StandardParametersEditor', 'StandardParametersEditorProps'],
|
||||
'default_export': 'StandardParametersEditor'
|
||||
},
|
||||
{
|
||||
'file': COMPONENTS_PATH / "EmptyStateMessage.tsx",
|
||||
'exports': ['EmptyStateMessage', 'EmptyStateReason', 'SmartEmptyStateMessage'],
|
||||
'default_export': 'EmptyStateMessage'
|
||||
},
|
||||
{
|
||||
'file': COMPONENTS_PATH / "LoadingState.tsx",
|
||||
'exports': ['LoadingState', 'LoadingType', 'StepResolutionLoading', 'VWBActionLoading'],
|
||||
'default_export': 'LoadingState'
|
||||
},
|
||||
{
|
||||
'file': HOOKS_PATH / "useAutoSave.ts",
|
||||
'exports': ['useAutoSave', 'useStepParametersAutoSave', 'useSyncedAutoSave'],
|
||||
'default_export': 'useAutoSave'
|
||||
}
|
||||
]
|
||||
|
||||
for component in component_exports:
|
||||
file_path = component['file']
|
||||
|
||||
if not file_path.exists():
|
||||
result['errors'].append(f"Fichier manquant: {file_path.name}")
|
||||
continue
|
||||
|
||||
try:
|
||||
content = file_path.read_text(encoding='utf-8')
|
||||
|
||||
# Vérifier les exports nommés
|
||||
for export_name in component['exports']:
|
||||
if f"export" in content and export_name in content:
|
||||
result['details'][f"export_{file_path.name}_{export_name}"] = True
|
||||
else:
|
||||
result['warnings'].append(f"Export manquant dans {file_path.name}: {export_name}")
|
||||
|
||||
# Vérifier l'export par défaut
|
||||
default_export = component['default_export']
|
||||
if f"export default" in content:
|
||||
result['details'][f"default_export_{file_path.name}"] = True
|
||||
else:
|
||||
result['errors'].append(f"Export par défaut manquant dans {file_path.name}")
|
||||
|
||||
except Exception as e:
|
||||
result['errors'].append(f"Erreur lecture {file_path.name}: {str(e)}")
|
||||
|
||||
result['success'] = len(result['errors']) == 0
|
||||
return result
|
||||
|
||||
def validate_typescript_compilation_complete(self) -> Dict[str, Any]:
|
||||
"""Valide la compilation TypeScript complète"""
|
||||
result = {
|
||||
'name': 'Compilation TypeScript complète',
|
||||
'success': False,
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
'details': {}
|
||||
}
|
||||
|
||||
try:
|
||||
# Compilation globale du projet
|
||||
compile_result = subprocess.run(
|
||||
["npx", "tsc", "--noEmit", "--project", "."],
|
||||
cwd=FRONTEND_PATH,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
result['details']['compilation_exit_code'] = compile_result.returncode
|
||||
result['details']['compilation_stdout'] = compile_result.stdout[:500] # Limiter la sortie
|
||||
result['details']['compilation_stderr'] = compile_result.stderr[:500]
|
||||
|
||||
if compile_result.returncode == 0:
|
||||
result['success'] = True
|
||||
result['details']['compilation_status'] = 'success'
|
||||
else:
|
||||
result['errors'].append(f"Erreurs de compilation TypeScript: {compile_result.stderr}")
|
||||
result['details']['compilation_status'] = 'failed'
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
result['errors'].append("Timeout lors de la compilation TypeScript")
|
||||
result['details']['compilation_status'] = 'timeout'
|
||||
except Exception as e:
|
||||
result['errors'].append(f"Erreur lors de la compilation: {str(e)}")
|
||||
result['details']['compilation_status'] = 'error'
|
||||
|
||||
return result
|
||||
|
||||
def validate_property_tests_execution(self) -> Dict[str, Any]:
|
||||
"""Valide l'exécution des tests de propriétés"""
|
||||
result = {
|
||||
'name': 'Exécution des tests de propriétés',
|
||||
'success': False,
|
||||
'errors': [],
|
||||
'warnings': [],
|
||||
'details': {}
|
||||
}
|
||||
|
||||
# Tests de propriétés à exécuter
|
||||
property_test_files = [
|
||||
"test_parameter_field_renderer_properties_12jan2026.py",
|
||||
"test_standard_parameters_editor_properties_12jan2026.py",
|
||||
"test_auto_save_properties_12jan2026.py",
|
||||
"test_vwb_action_properties_12jan2026.py",
|
||||
"test_empty_state_message_properties_12jan2026.py",
|
||||
"test_loading_state_properties_12jan2026.py"
|
||||
]
|
||||
|
||||
successful_tests = 0
|
||||
|
||||
for test_file in property_test_files:
|
||||
test_path = TESTS_PATH / test_file
|
||||
|
||||
if not test_path.exists():
|
||||
result['errors'].append(f"Test manquant: {test_file}")
|
||||
continue
|
||||
|
||||
try:
|
||||
# Exécuter le test avec pytest
|
||||
test_result = subprocess.run(
|
||||
["python", "-m", "pytest", str(test_path), "-v", "--tb=short"],
|
||||
cwd=PROJECT_ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
result['details'][f"test_{test_file}_exit_code"] = test_result.returncode
|
||||
|
||||
if test_result.returncode == 0:
|
||||
successful_tests += 1
|
||||
result['details'][f"test_{test_file}_status"] = 'passed'
|
||||
else:
|
||||
result['warnings'].append(f"Test échoué: {test_file}")
|
||||
result['details'][f"test_{test_file}_status"] = 'failed'
|
||||
result['details'][f"test_{test_file}_stderr"] = test_result.stderr[:200]
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
result['warnings'].append(f"Timeout test: {test_file}")
|
||||
result['details'][f"test_{test_file}_status"] = 'timeout'
|
||||
except Exception as e:
|
||||
result['warnings'].append(f"Erreur test {test_file}: {str(e)}")
|
||||
result['details'][f"test_{test_file}_status"] = 'error'
|
||||
|
||||
result['details']['successful_tests'] = successful_tests
|
||||
result['details']['total_tests'] = len(property_test_files)
|
||||
|
||||
# Succès si au moins 80% des tests passent
|
||||
success_rate = successful_tests / len(property_test_files)
|
||||
result['success'] = success_rate >= 0.8
|
||||
result['details']['success_rate'] = f"{success_rate * 100:.1f}%"
|
||||
|
||||
return result
|
||||
|
||||
def run_complete_validation(self) -> Dict[str, Any]:
|
||||
"""Exécute la validation complète de l'intégration"""
|
||||
print("🔍 Validation complète de l'intégration - Interface Propriétés d'Étapes")
|
||||
print("=" * 80)
|
||||
|
||||
# Exécuter toutes les validations
|
||||
validations = [
|
||||
self.validate_complete_file_structure,
|
||||
self.validate_properties_panel_integration,
|
||||
self.validate_component_exports,
|
||||
self.validate_typescript_compilation_complete,
|
||||
self.validate_property_tests_execution,
|
||||
]
|
||||
|
||||
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" + "=" * 80)
|
||||
print("📊 RÉSUMÉ DE VALIDATION COMPLÈTE")
|
||||
print("=" * 80)
|
||||
|
||||
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🎉 INTÉGRATION COMPLÈTE RÉUSSIE")
|
||||
print("✅ Tous les composants sont intégrés et fonctionnels")
|
||||
print("🚀 Prêt pour les fonctionnalités avancées (Tâches 7-13)")
|
||||
else:
|
||||
print("\n⚠️ INTÉGRATION PARTIELLEMENT RÉUSSIE")
|
||||
print("🔧 Corrections mineures recommandées")
|
||||
|
||||
if successful_validations >= total_validations * 0.8:
|
||||
print("📈 Intégration majoritairement fonctionnelle")
|
||||
|
||||
# Prochaines étapes
|
||||
print("\n📋 PROCHAINES ÉTAPES RECOMMANDÉES:")
|
||||
print("1. Implémenter les fonctionnalités avancées de validation (Tâche 7)")
|
||||
print("2. Optimiser les performances et l'accessibilité (Tâche 8)")
|
||||
print("3. Assurer la cohérence visuelle et le design system (Tâche 9)")
|
||||
print("4. Vérifier la compatibilité et l'intégration système (Tâche 10)")
|
||||
|
||||
return {
|
||||
"global_success": global_success,
|
||||
"successful_validations": successful_validations,
|
||||
"total_validations": total_validations,
|
||||
"total_errors": total_errors,
|
||||
"total_warnings": total_warnings,
|
||||
"integration_ready": successful_validations >= total_validations * 0.8,
|
||||
"results": self.results
|
||||
}
|
||||
|
||||
def main():
|
||||
"""Fonction principale"""
|
||||
validator = IntegrationValidator()
|
||||
results = validator.run_complete_validation()
|
||||
|
||||
# Sauvegarder les résultats
|
||||
results_file = PROJECT_ROOT / "validation_integration_complete_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["integration_ready"] else 1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user