- 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>
480 lines
17 KiB
Python
480 lines
17 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Test Palette Cross-Machine dans Navigateur - 10 janvier 2026
|
|
Auteur : Dom, Alice, Kiro
|
|
|
|
Ce script teste le fonctionnement réel de la palette d'outils VWB
|
|
dans le navigateur avec détection automatique d'URL cross-machine.
|
|
"""
|
|
|
|
import subprocess
|
|
import time
|
|
import requests
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
import threading
|
|
import signal
|
|
import os
|
|
|
|
class VWBTestServer:
|
|
"""Serveur de test pour le backend VWB"""
|
|
|
|
def __init__(self):
|
|
self.process = None
|
|
self.port = 5004
|
|
self.is_running = False
|
|
|
|
def start(self):
|
|
"""Démarrer le serveur backend"""
|
|
print(f"🚀 Démarrage du serveur backend sur le port {self.port}...")
|
|
|
|
try:
|
|
# Démarrer le serveur backend
|
|
backend_path = Path("visual_workflow_builder/backend")
|
|
if not backend_path.exists():
|
|
print("❌ Répertoire backend non trouvé")
|
|
return False
|
|
|
|
# Utiliser le script de démarrage existant
|
|
script_path = Path("scripts/start_vwb_backend_catalogue_complet_10jan2026.py")
|
|
if script_path.exists():
|
|
self.process = subprocess.Popen([
|
|
sys.executable, str(script_path)
|
|
], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
else:
|
|
# Fallback vers démarrage direct
|
|
self.process = subprocess.Popen([
|
|
sys.executable, "-m", "flask", "run",
|
|
"--host", "0.0.0.0", "--port", str(self.port)
|
|
], cwd=backend_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
env={**os.environ, "FLASK_APP": "app_catalogue_simple.py"})
|
|
|
|
# Attendre que le serveur démarre
|
|
for i in range(30): # 30 secondes max
|
|
try:
|
|
response = requests.get(f"http://localhost:{self.port}/health", timeout=1)
|
|
if response.status_code == 200:
|
|
print(f"✅ Serveur backend démarré sur le port {self.port}")
|
|
self.is_running = True
|
|
return True
|
|
except:
|
|
time.sleep(1)
|
|
|
|
print("❌ Timeout lors du démarrage du serveur")
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"❌ Erreur lors du démarrage du serveur: {e}")
|
|
return False
|
|
|
|
def stop(self):
|
|
"""Arrêter le serveur backend"""
|
|
if self.process:
|
|
print("🛑 Arrêt du serveur backend...")
|
|
self.process.terminate()
|
|
try:
|
|
self.process.wait(timeout=5)
|
|
except subprocess.TimeoutExpired:
|
|
self.process.kill()
|
|
self.is_running = False
|
|
|
|
def test_backend_health():
|
|
"""Tester la santé du backend"""
|
|
print("🔍 Test de santé du backend...")
|
|
|
|
try:
|
|
response = requests.get("http://localhost:5004/health", timeout=5)
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
print(f"✅ Backend en ligne: {data.get('status', 'unknown')}")
|
|
return True
|
|
else:
|
|
print(f"❌ Backend répond avec le code: {response.status_code}")
|
|
return False
|
|
except Exception as e:
|
|
print(f"❌ Erreur de connexion au backend: {e}")
|
|
return False
|
|
|
|
def test_catalog_api():
|
|
"""Tester l'API du catalogue"""
|
|
print("🔍 Test de l'API catalogue...")
|
|
|
|
try:
|
|
response = requests.get("http://localhost:5004/api/vwb/catalog/actions", timeout=5)
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
actions = data.get('actions', [])
|
|
print(f"✅ API catalogue fonctionnelle: {len(actions)} actions disponibles")
|
|
|
|
# Afficher quelques actions pour vérification
|
|
for action in actions[:3]:
|
|
print(f" - {action.get('name', 'Sans nom')} ({action.get('id', 'sans-id')})")
|
|
|
|
return True
|
|
else:
|
|
print(f"❌ API catalogue répond avec le code: {response.status_code}")
|
|
return False
|
|
except Exception as e:
|
|
print(f"❌ Erreur lors du test de l'API catalogue: {e}")
|
|
return False
|
|
|
|
def test_frontend_build():
|
|
"""Tester la compilation du frontend"""
|
|
print("🔍 Test de compilation du frontend...")
|
|
|
|
frontend_path = Path("visual_workflow_builder/frontend")
|
|
if not frontend_path.exists():
|
|
print("❌ Répertoire frontend non trouvé")
|
|
return False
|
|
|
|
try:
|
|
# Vérifier que les fichiers TypeScript compilent
|
|
result = subprocess.run([
|
|
"npx", "tsc", "--noEmit"
|
|
], cwd=frontend_path, capture_output=True, text=True, timeout=60)
|
|
|
|
if result.returncode == 0:
|
|
print("✅ Frontend compile sans erreurs TypeScript")
|
|
return True
|
|
else:
|
|
print("❌ Erreurs de compilation frontend:")
|
|
print(result.stderr)
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"❌ Erreur lors de la compilation frontend: {e}")
|
|
return False
|
|
|
|
def test_catalog_service_detection():
|
|
"""Tester la détection automatique d'URL du service catalogue"""
|
|
print("🔍 Test de détection automatique d'URL...")
|
|
|
|
# Simuler différents scénarios de détection
|
|
test_urls = [
|
|
"http://localhost:5004",
|
|
"http://127.0.0.1:5004",
|
|
]
|
|
|
|
working_urls = []
|
|
for url in test_urls:
|
|
try:
|
|
response = requests.get(f"{url}/health", timeout=2)
|
|
if response.status_code == 200:
|
|
working_urls.append(url)
|
|
print(f"✅ URL fonctionnelle détectée: {url}")
|
|
except:
|
|
print(f"❌ URL non accessible: {url}")
|
|
|
|
if working_urls:
|
|
print(f"✅ Détection automatique réussie: {len(working_urls)} URL(s) fonctionnelle(s)")
|
|
return True
|
|
else:
|
|
print("❌ Aucune URL fonctionnelle détectée")
|
|
return False
|
|
|
|
def test_static_fallback():
|
|
"""Tester le fallback vers le catalogue statique"""
|
|
print("🔍 Test du fallback catalogue statique...")
|
|
|
|
# Lire le fichier du catalogue statique
|
|
static_catalog_path = Path("visual_workflow_builder/frontend/src/data/staticCatalog.ts")
|
|
if not static_catalog_path.exists():
|
|
print("❌ Catalogue statique non trouvé")
|
|
return False
|
|
|
|
content = static_catalog_path.read_text()
|
|
|
|
# Vérifier que les actions de base sont présentes
|
|
required_actions = [
|
|
"click_anchor",
|
|
"type_text",
|
|
"wait_for_anchor",
|
|
"extract_text",
|
|
"hotkey"
|
|
]
|
|
|
|
missing_actions = []
|
|
for action in required_actions:
|
|
if action not in content:
|
|
missing_actions.append(action)
|
|
|
|
if missing_actions:
|
|
print(f"❌ Actions manquantes dans le catalogue statique: {missing_actions}")
|
|
return False
|
|
else:
|
|
print(f"✅ Catalogue statique complet: {len(required_actions)} actions de base")
|
|
return True
|
|
|
|
def create_test_html():
|
|
"""Créer une page HTML de test pour le navigateur"""
|
|
print("🔍 Création de la page de test navigateur...")
|
|
|
|
html_content = """<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Test Palette Cross-Machine VWB</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 20px;
|
|
background: #0f172a;
|
|
color: #e2e8f0;
|
|
}
|
|
.test-container {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
background: #1e293b;
|
|
border-radius: 12px;
|
|
border: 1px solid #334155;
|
|
}
|
|
.status {
|
|
padding: 10px;
|
|
margin: 10px 0;
|
|
border-radius: 8px;
|
|
font-weight: bold;
|
|
}
|
|
.success { background: #22c55e; color: white; }
|
|
.error { background: #ef4444; color: white; }
|
|
.warning { background: #f59e0b; color: white; }
|
|
.info { background: #3b82f6; color: white; }
|
|
button {
|
|
background: #1976d2;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
margin: 5px;
|
|
}
|
|
button:hover {
|
|
background: #1565c0;
|
|
}
|
|
#results {
|
|
margin-top: 20px;
|
|
padding: 15px;
|
|
background: #334155;
|
|
border-radius: 8px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="test-container">
|
|
<h1>🧪 Test Palette Cross-Machine VWB</h1>
|
|
<p>Cette page teste la détection automatique d'URL et le fallback statique de la palette d'outils.</p>
|
|
|
|
<div class="status info">
|
|
📍 URL actuelle: <span id="currentUrl"></span>
|
|
</div>
|
|
|
|
<div>
|
|
<button onclick="testCatalogService()">🔍 Tester Service Catalogue</button>
|
|
<button onclick="testStaticFallback()">📦 Tester Fallback Statique</button>
|
|
<button onclick="testUrlDetection()">🌐 Tester Détection URL</button>
|
|
<button onclick="clearResults()">🧹 Effacer Résultats</button>
|
|
</div>
|
|
|
|
<div id="results"></div>
|
|
</div>
|
|
|
|
<script>
|
|
// Afficher l'URL actuelle
|
|
document.getElementById('currentUrl').textContent = window.location.href;
|
|
|
|
// Simuler le service catalogue (version simplifiée)
|
|
class TestCatalogService {
|
|
constructor() {
|
|
this.urls = [
|
|
window.location.origin,
|
|
'http://localhost:5004',
|
|
'http://127.0.0.1:5004'
|
|
];
|
|
this.staticActions = [
|
|
{ id: 'click_anchor', name: 'Cliquer sur Ancre', category: 'vision_ui' },
|
|
{ id: 'type_text', name: 'Saisir Texte', category: 'vision_ui' },
|
|
{ id: 'wait_for_anchor', name: 'Attendre Ancre', category: 'control' },
|
|
{ id: 'extract_text', name: 'Extraire Texte', category: 'data' },
|
|
{ id: 'hotkey', name: 'Raccourci Clavier', category: 'control' }
|
|
];
|
|
}
|
|
|
|
async testUrl(url) {
|
|
try {
|
|
const response = await fetch(`${url}/health`, {
|
|
method: 'GET',
|
|
timeout: 2000
|
|
});
|
|
return response.ok;
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async detectWorkingUrl() {
|
|
for (const url of this.urls) {
|
|
if (await this.testUrl(url)) {
|
|
return url;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
getStaticActions() {
|
|
return this.staticActions;
|
|
}
|
|
}
|
|
|
|
const catalogService = new TestCatalogService();
|
|
|
|
function addResult(message, type = 'info') {
|
|
const results = document.getElementById('results');
|
|
const div = document.createElement('div');
|
|
div.className = `status ${type}`;
|
|
div.innerHTML = message;
|
|
results.appendChild(div);
|
|
}
|
|
|
|
async function testCatalogService() {
|
|
addResult('🔍 Test du service catalogue en cours...', 'info');
|
|
|
|
const workingUrl = await catalogService.detectWorkingUrl();
|
|
if (workingUrl) {
|
|
addResult(`✅ Service catalogue détecté sur: ${workingUrl}`, 'success');
|
|
|
|
// Tester l'API catalogue
|
|
try {
|
|
const response = await fetch(`${workingUrl}/api/vwb/catalog/actions`);
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
addResult(`✅ API catalogue fonctionnelle: ${data.actions?.length || 0} actions`, 'success');
|
|
} else {
|
|
addResult(`❌ API catalogue non accessible (${response.status})`, 'error');
|
|
}
|
|
} catch (error) {
|
|
addResult(`❌ Erreur API catalogue: ${error.message}`, 'error');
|
|
}
|
|
} else {
|
|
addResult('❌ Aucun service catalogue détecté', 'error');
|
|
}
|
|
}
|
|
|
|
function testStaticFallback() {
|
|
addResult('📦 Test du fallback statique...', 'info');
|
|
|
|
const staticActions = catalogService.getStaticActions();
|
|
if (staticActions.length > 0) {
|
|
addResult(`✅ Catalogue statique disponible: ${staticActions.length} actions`, 'success');
|
|
staticActions.forEach(action => {
|
|
addResult(` - ${action.name} (${action.id})`, 'info');
|
|
});
|
|
} else {
|
|
addResult('❌ Catalogue statique vide', 'error');
|
|
}
|
|
}
|
|
|
|
async function testUrlDetection() {
|
|
addResult('🌐 Test de détection automatique d\'URL...', 'info');
|
|
|
|
for (const url of catalogService.urls) {
|
|
addResult(`⏳ Test de ${url}...`, 'info');
|
|
const isWorking = await catalogService.testUrl(url);
|
|
if (isWorking) {
|
|
addResult(`✅ ${url} accessible`, 'success');
|
|
} else {
|
|
addResult(`❌ ${url} non accessible`, 'error');
|
|
}
|
|
}
|
|
}
|
|
|
|
function clearResults() {
|
|
document.getElementById('results').innerHTML = '';
|
|
}
|
|
|
|
// Test automatique au chargement
|
|
window.addEventListener('load', () => {
|
|
addResult('🚀 Page de test chargée - Prêt pour les tests', 'success');
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>"""
|
|
|
|
test_file = Path("test_palette_cross_machine_navigateur.html")
|
|
test_file.write_text(html_content)
|
|
print(f"✅ Page de test créée: {test_file.absolute()}")
|
|
return test_file
|
|
|
|
def main():
|
|
"""Fonction principale de test"""
|
|
print("🚀 Test Palette Cross-Machine dans Navigateur - 10 janvier 2026")
|
|
print("=" * 70)
|
|
|
|
server = VWBTestServer()
|
|
|
|
try:
|
|
# Tests préliminaires
|
|
tests = [
|
|
("Compilation Frontend", test_frontend_build),
|
|
("Catalogue Statique", test_static_fallback),
|
|
]
|
|
|
|
passed = 0
|
|
for test_name, test_func in tests:
|
|
print(f"\n🔍 {test_name}...")
|
|
if test_func():
|
|
passed += 1
|
|
else:
|
|
print(f"❌ Échec du test: {test_name}")
|
|
|
|
# Démarrer le serveur pour les tests réseau
|
|
print(f"\n🚀 Démarrage du serveur de test...")
|
|
if server.start():
|
|
# Tests avec serveur
|
|
network_tests = [
|
|
("Santé Backend", test_backend_health),
|
|
("API Catalogue", test_catalog_api),
|
|
("Détection URL", test_catalog_service_detection),
|
|
]
|
|
|
|
for test_name, test_func in network_tests:
|
|
print(f"\n🔍 {test_name}...")
|
|
if test_func():
|
|
passed += 1
|
|
else:
|
|
print(f"❌ Échec du test: {test_name}")
|
|
|
|
total_tests = len(tests) + len(network_tests)
|
|
else:
|
|
print("❌ Impossible de démarrer le serveur - tests réseau ignorés")
|
|
total_tests = len(tests)
|
|
|
|
# Créer la page de test navigateur
|
|
print(f"\n🌐 Création de la page de test navigateur...")
|
|
test_file = create_test_html()
|
|
|
|
print("\n" + "=" * 70)
|
|
print(f"📊 Résultats: {passed}/{total_tests} tests passés")
|
|
|
|
if passed == total_tests:
|
|
print("✅ TOUS LES TESTS PASSENT")
|
|
print(f"\n🌐 Ouvrez le fichier suivant dans votre navigateur pour tester:")
|
|
print(f" file://{test_file.absolute()}")
|
|
print(f"\n💡 Instructions:")
|
|
print(f" 1. Ouvrez le fichier HTML dans votre navigateur")
|
|
print(f" 2. Cliquez sur les boutons de test")
|
|
print(f" 3. Vérifiez que la détection d'URL fonctionne")
|
|
print(f" 4. Vérifiez que le fallback statique fonctionne")
|
|
return True
|
|
else:
|
|
print("❌ CERTAINS TESTS ONT ÉCHOUÉ")
|
|
return False
|
|
|
|
finally:
|
|
# Arrêter le serveur
|
|
server.stop()
|
|
|
|
if __name__ == "__main__":
|
|
success = main()
|
|
sys.exit(0 if success else 1) |