Validé sur PC Windows (DESKTOP-58D5CAC, 2560x1600) : - 8 clics résolus visuellement (1 anchor_template, 1 som_text_match, 6 som_vlm) - Score moyen 0.75, temps moyen 1.6s - Texte tapé correctement (bonjour, test word, date, email) - 0 retries, 2 actions non vérifiées (OK) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
47 KiB
Document de Design: Visual Workflow Builder
Vue d'Ensemble
Le Visual Workflow Builder est une interface graphique révolutionnaire qui permet de créer des workflows RPA par glisser-déposer, sans écrire une seule ligne de code. Cette solution démocratise l'automatisation RPA en la rendant accessible aux utilisateurs non-techniques tout en conservant la puissance nécessaire aux développeurs experts.
Objectifs de Design
- Simplicité d'Usage: Interface intuitive inspirée de Scratch et Node-RED
- Puissance Technique: Accès à toutes les fonctionnalités RPA Vision V3
- Performance: Rendu fluide à 60fps même avec 100+ nodes
- Intégration: Compatibilité totale avec ExecutionLoop, Analytics, Self-Healing
- Extensibilité: Architecture modulaire pour ajouter facilement de nouveaux types de nodes
Principes de Design
- WYSIWYG: Ce que vous voyez est ce que vous obtenez
- Feedback Immédiat: Test et validation en temps réel
- Tolérance aux Erreurs: Validation continue et récupération gracieuse
- Accessibilité: Support clavier, screen readers, high contrast
- Collaboration: Partage et versioning des workflows
Architecture Globale
Vue d'Ensemble de l'Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Visual Workflow Builder │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Frontend │ │ Backend API │ │ Integration │ │
│ │ (React/Vue) │ │ (Flask) │ │ Layer │ │
│ │ │ │ │ │ │ │
│ │ • Canvas │ │ • Serialization │ │ • WorkflowGraph │ │
│ │ • Palette │ │ • Validation │ │ • ExecutionLoop │ │
│ │ • Properties │ │ • Templates │ │ • Analytics │ │
│ │ • Target Select │ │ • Export/Import │ │ • Self-Healing │ │
│ │ • Real-time │ │ • Version Ctrl │ │ • Storage │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │ │ │ │
│ └─────────────────────┼─────────────────────┘ │
│ │ │
├─────────────────────────────────┼─────────────────────────────────┤
│ Communication Layer (WebSocket + REST) │
├─────────────────────────────────┼─────────────────────────────────┤
│ │ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ RPA Vision V3 Core System │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ExecutionLoop│ │ Analytics │ │Self-Healing │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Couches Architecturales
1. Couche Présentation (Frontend)
- Framework: React avec TypeScript
- Canvas: react-flow-renderer pour le rendu des graphes
- Drag & Drop: react-dnd pour les interactions
- UI Components: Material-UI ou Ant Design
- State Management: Redux Toolkit
- Real-time: Socket.IO client
2. Couche API (Backend)
- Framework: Flask avec Flask-SocketIO
- Sérialisation: JSON Schema validation
- Base de Données: SQLite pour les workflows, PostgreSQL pour production
- Cache: Redis pour les sessions et templates
- Files: Système de fichiers pour les exports
3. Couche Intégration
- Conversion: Visual JSON → WorkflowGraph
- Exécution: Interface avec ExecutionLoop
- Monitoring: Hooks vers Analytics
- Recovery: Intégration Self-Healing
Composants et Interfaces
1. Canvas Component
Le Canvas est le composant central où les utilisateurs construisent visuellement leurs workflows.
Responsabilités
- Rendu des nodes et edges
- Gestion des interactions (sélection, déplacement)
- Zoom et panoramique
- Grille d'alignement
- Minimap pour navigation
Interface TypeScript
interface CanvasProps {
nodes: VisualNode[];
edges: VisualEdge[];
onNodeSelect: (nodeId: string) => void;
onNodeMove: (nodeId: string, position: Position) => void;
onEdgeCreate: (source: string, target: string) => void;
onEdgeDelete: (edgeId: string) => void;
readonly?: boolean;
}
interface CanvasState {
selectedNodes: string[];
selectedEdges: string[];
viewport: Viewport;
dragState: DragState;
}
2. Node Palette
La palette affiche tous les types de nodes disponibles organisés par catégorie.
Responsabilités
- Affichage des types de nodes disponibles
- Recherche et filtrage
- Catégorisation (Actions, Logic, Data, etc.)
- Drag source pour création de nodes
Structure
interface NodeType {
id: string;
name: string;
category: NodeCategory;
icon: string;
description: string;
parameters: ParameterDefinition[];
inputPorts: PortDefinition[];
outputPorts: PortDefinition[];
}
enum NodeCategory {
ACTION = 'action', // Click, Type, Wait
LOGIC = 'logic', // If, Loop, Switch
DATA = 'data', // Variable, Extract, Transform
FLOW = 'flow', // Start, End, Subworkflow
INTEGRATION = 'integration' // API, Database, File
}
3. Properties Panel
Le panneau de propriétés permet de configurer les paramètres de chaque node.
Responsabilités
- Configuration des paramètres de nodes
- Validation en temps réel
- Target selector integration
- Variable management
- Help et documentation contextuelle
Composants
interface PropertyEditor {
parameter: ParameterDefinition;
value: any;
onChange: (value: any) => void;
onValidate: (value: any) => ValidationResult;
}
// Types d'éditeurs
class TextEditor extends PropertyEditor {}
class NumberEditor extends PropertyEditor {}
class BooleanEditor extends PropertyEditor {}
class SelectEditor extends PropertyEditor {}
class TargetEditor extends PropertyEditor {} // Sélection interactive
class VariableEditor extends PropertyEditor {}
class ExpressionEditor extends PropertyEditor {} // Conditions
4. Target Selector
Le sélecteur de cible permet de sélectionner interactivement des éléments UI à l'écran.
Workflow
class TargetSelector:
async def start_selection(self) -> TargetInfo:
"""Démarre le processus de sélection interactive"""
# 1. Capturer l'écran
screenshot = await self.capture_screen()
# 2. Entrer en mode sélection
self.enter_selection_mode(screenshot)
# 3. Attendre la sélection utilisateur
element = await self.wait_for_user_selection()
# 4. Extraire les propriétés
target_info = await self.extract_element_properties(element)
return target_info
async def extract_element_properties(self, element) -> TargetInfo:
"""Extrait toutes les propriétés d'un élément"""
return TargetInfo(
text=element.text_content,
tag_name=element.tag_name,
class_name=element.class_name,
position=self.get_element_position(element),
size=self.get_element_size(element),
embedding=await self.generate_clip_embedding(element)
)
5. Execution Engine
Le moteur d'exécution convertit les workflows visuels en WorkflowGraph et les exécute.
Conversion Pipeline
class VisualToGraphConverter:
def convert(self, visual_workflow: VisualWorkflow) -> WorkflowGraph:
"""Convertit un workflow visuel en WorkflowGraph"""
# 1. Valider la structure
self.validate_structure(visual_workflow)
# 2. Convertir les nodes
nodes = [self.convert_node(node) for node in visual_workflow.nodes]
# 3. Convertir les edges
edges = [self.convert_edge(edge) for edge in visual_workflow.edges]
# 4. Créer le graph
return WorkflowGraph(nodes, edges)
def convert_node(self, visual_node: VisualNode) -> WorkflowNode:
"""Convertit un node visuel en WorkflowNode"""
if visual_node.type == 'click':
return ClickNode(
target=visual_node.parameters['target'],
timeout=visual_node.parameters.get('timeout', 5000),
retries=visual_node.parameters.get('retries', 3)
)
elif visual_node.type == 'type':
return TypeNode(
target=visual_node.parameters['target'],
text=visual_node.parameters['text'],
clear_first=visual_node.parameters.get('clear_first', False)
)
# ... autres types
Modèles de Données
1. Visual Workflow
@dataclass
class VisualWorkflow:
"""Représentation complète d'un workflow visuel"""
id: str
name: str
description: Optional[str]
version: str
created_at: datetime
updated_at: datetime
created_by: str
# Structure visuelle
nodes: List[VisualNode]
edges: List[VisualEdge]
# Configuration
variables: List[Variable]
settings: WorkflowSettings
# Métadonnées
tags: List[str]
category: Optional[str]
is_template: bool = False
2. Visual Node
@dataclass
class VisualNode:
"""Représentation d'un node dans le canvas"""
id: str
type: str # 'click', 'type', 'wait', 'if', 'loop', etc.
# Position visuelle
position: Position
size: Size
# Configuration
parameters: Dict[str, Any]
# Connexions
input_ports: List[Port]
output_ports: List[Port]
# État visuel
selected: bool = False
highlighted: bool = False
status: Optional[NodeStatus] = None # pendant l'exécution
# Métadonnées
label: Optional[str] = None
description: Optional[str] = None
color: Optional[str] = None
class NodeStatus(Enum):
"""États possibles d'un node pendant l'exécution"""
IDLE = 'idle'
RUNNING = 'running'
SUCCESS = 'success'
FAILED = 'failed'
SKIPPED = 'skipped'
3. Visual Edge
@dataclass
class VisualEdge:
"""Représentation d'une connexion entre nodes"""
id: str
source: str # node ID
target: str # node ID
source_port: str
target_port: str
# Condition (pour les branches)
condition: Optional[EdgeCondition] = None
# Style visuel
style: Optional[EdgeStyle] = None
# État
selected: bool = False
animated: bool = False # pendant l'exécution
@dataclass
class EdgeCondition:
"""Condition pour l'exécution d'un edge"""
type: str # 'always', 'success', 'failure', 'expression'
expression: Optional[str] = None # pour type 'expression'
4. Parameter Definitions
@dataclass
class ParameterDefinition:
"""Définition d'un paramètre de node"""
name: str
type: ParameterType
required: bool
default_value: Optional[Any] = None
# Validation
validation: Optional[List[ValidationRule]] = None
# UI
label: str = ""
description: Optional[str] = None
placeholder: Optional[str] = None
# Comportement spécial
is_target: bool = False # Active le target selector
is_variable: bool = False # Permet ${variable}
is_expression: bool = False # Permet les expressions
class ParameterType(Enum):
"""Types de paramètres supportés"""
STRING = 'string'
NUMBER = 'number'
BOOLEAN = 'boolean'
SELECT = 'select'
TARGET = 'target'
VARIABLE = 'variable'
EXPRESSION = 'expression'
FILE = 'file'
5. Templates
@dataclass
class WorkflowTemplate:
"""Template de workflow pré-construit"""
id: str
name: str
description: str
category: str
# Template data
workflow: VisualWorkflow
# Paramètres configurables
parameters: List[TemplateParameter]
# Métadonnées
tags: List[str]
difficulty: str # 'beginner', 'intermediate', 'advanced'
estimated_time: int # minutes
# Usage
usage_count: int = 0
rating: float = 0.0
@dataclass
class TemplateParameter:
"""Paramètre configurable d'un template"""
name: str
type: ParameterType
description: str
default_value: Optional[Any] = None
# Mapping vers les nodes
node_id: str
parameter_name: str
Correctness Properties
Une propriété est une caractéristique ou un comportement qui doit être vrai pour toutes les exécutions valides d'un système - essentiellement, une déclaration formelle sur ce que le système doit faire. Les propriétés servent de pont entre les spécifications lisibles par l'humain et les garanties de correction vérifiables par machine.
Propriétés Structurelles
Propriété 1: Suppression de Node Cascade
Pour tout workflow et tout node, quand un node est supprimé, tous les edges connectés à ce node doivent également être supprimés automatiquement.
Valide: Exigences 1.5, 2.4
Propriété 2: Validation de Connexion
Pour tout edge créé entre deux nodes, la connexion doit être validée selon les règles de compatibilité des ports (type de sortie compatible avec type d'entrée).
Valide: Exigences 2.2
Propriété 3: Détection de Cycles
Pour tout workflow, si on ajoute un edge qui créerait un cycle (sauf pour les boucles explicites), le système doit détecter et rejeter cette connexion.
Valide: Exigences 12.4
Propriété 4: Nodes Déconnectés
Pour tout workflow, tous les nodes (sauf Start et End) doivent avoir au moins un edge entrant et un edge sortant, sinon un avertissement doit être affiché.
Valide: Exigences 12.3
Propriétés de Sérialisation
Propriété 5: Round-trip de Sérialisation
Pour tout workflow visuel, sérialiser puis désérialiser doit produire un workflow équivalent (même nodes, edges, paramètres, variables).
Valide: Exigences 5.1, 5.2
Propriété 6: Génération d'ID Unique
Pour tout nouveau workflow sans ID, la sauvegarde doit générer un ID unique qui n'existe pas déjà dans le système.
Valide: Exigences 5.4
Propriété 7: Validation de Champs Requis
Pour tout workflow lors de la sérialisation, tous les champs requis (id, name, nodes, edges) doivent être présents, sinon la sérialisation doit échouer.
Valide: Exigences 5.3
Propriétés de Configuration
Propriété 8: Validation de Paramètres Requis
Pour tout node, tous les paramètres marqués comme requis doivent avoir une valeur non-nulle, sinon le node doit afficher un indicateur d'avertissement.
Valide: Exigences 12.2
Propriété 9: Valeurs par Défaut
Pour tout nouveau node créé, tous les paramètres avec une valeur par défaut définie doivent être pré-remplis avec cette valeur.
Valide: Exigences 3.5
Propriété 10: Validation de Variables
Pour tout paramètre contenant une référence de variable ${nom}, la variable doit exister dans la liste des variables du workflow, sinon une erreur de validation doit être affichée.
Valide: Exigences 10.3
Propriété 11: Unicité des Noms de Variables
Pour tout workflow, tous les noms de variables doivent être uniques (pas de doublons).
Valide: Exigences 10.2
Propriétés d'Exécution
Propriété 12: Conversion Valide
Pour tout workflow visuel valide, la conversion en WorkflowGraph doit produire un graphe exécutable par ExecutionLoop sans erreur.
Valide: Exigences 6.1, 18.1
Propriété 13: Synchronisation d'État
Pour tout node pendant l'exécution, l'état visuel (status) doit refléter l'état d'exécution réel (idle → running → success/failed).
Valide: Exigences 6.3
Propriété 14: Substitution de Variables
Pour tout paramètre contenant ${variable} lors de l'exécution, la référence doit être remplacée par la valeur runtime de la variable.
Valide: Exigences 10.4
Propriété 15: Exécution de Conditions
Pour tout node Condition, l'exécution doit suivre le edge "true" si la condition est vraie, et le edge "false" si la condition est fausse.
Valide: Exigences 8.3
Propriété 16: Exécution de Boucles
Pour tout node Loop, le corps de la boucle doit être exécuté le nombre de fois spécifié par les paramètres (count pour repeat, condition pour while, items pour for-each).
Valide: Exigences 9.3
Propriétés d'Interaction
Propriété 17: Annuler/Refaire Cohérence
Pour toute séquence d'actions, effectuer une action puis l'annuler doit restaurer l'état précédent, et refaire doit restaurer l'état après l'action.
Valide: Exigences 13.2, 13.3
Propriété 18: Pile d'Annulation
Pour toute action effectuée, elle doit être ajoutée à la pile d'annulation, et on doit pouvoir annuler au moins 50 actions.
Valide: Exigences 13.1, 13.4
Propriété 19: Invalidation de Refaire
Pour toute nouvelle action effectuée après une annulation, la pile de refaire doit être vidée.
Valide: Exigences 13.5
Propriété 20: Extraction de Propriétés de Cible
Pour tout élément sélectionné via le target selector, les propriétés extraites doivent inclure au minimum: text, position, size, et embedding.
Valide: Exigences 4.5
Propriétés de Templates
Propriété 21: Chargement de Template
Pour tout template sélectionné, le chargement doit créer un workflow avec tous les nodes et edges du template, et tous les paramètres doivent être modifiables.
Valide: Exigences 11.2, 11.3
Propriété 22: Sauvegarde comme Template
Pour tout workflow créé par un utilisateur, il doit être possible de le sauvegarder comme template personnalisé avec des paramètres configurables.
Valide: Exigences 11.5
Propriétés d'Export/Import
Propriété 23: Complétude d'Export
Pour tout workflow exporté, le fichier JSON/YAML doit contenir tous les nodes, edges, variables, et métadonnées nécessaires pour reconstruire le workflow.
Valide: Exigences 15.1, 15.3
Propriété 24: Validation d'Import
Pour tout fichier importé, le système doit valider le format et rejeter les fichiers invalides avec un message d'erreur clair.
Valide: Exigences 15.2
Propriété 25: Migration de Version
Pour tout workflow d'une version antérieure, le système doit détecter l'incompatibilité et offrir une migration automatique vers la version actuelle.
Valide: Exigences 15.4
Propriétés d'Intégration
Propriété 26: Compatibilité de Format
Pour tout workflow sauvegardé par le Visual Builder, il doit être dans le même format que les workflows créés programmatiquement et vice-versa.
Valide: Exigences 18.4
Propriété 27: Intégration Self-Healing
Pour tout workflow utilisant self-healing, les nodes doivent être configurés avec les stratégies de récupération appropriées lors de la conversion.
Valide: Exigences 18.2
Propriété 28: Intégration Analytics
Pour tout workflow exécuté, les métriques doivent être automatiquement collectées et envoyées au système Analytics.
Valide: Exigences 18.3
Propriété 29: Rétrocompatibilité
Pour tout workflow existant créé programmatiquement (suivant le format standard), le Visual Builder doit pouvoir le charger, le visualiser et l'éditer.
Valide: Exigences 18.5
Propriétés de Recherche et Filtrage
Propriété 30: Filtrage de Palette
Pour toute requête de recherche dans la palette, seuls les types de nodes dont le nom ou la description contient la requête doivent être affichés.
Valide: Exigences 7.2
Propriété 31: Extensibilité de Palette
Pour tout nouveau type de node ajouté au système, il doit automatiquement apparaître dans la palette dans la catégorie appropriée.
Valide: Exigences 7.5
Propriétés de Zoom et Navigation
Propriété 32: Zoom Centré
Pour tout événement de zoom (molette de souris), le zoom doit être centré sur la position du curseur, pas sur le centre du canvas.
Valide: Exigences 14.1
Propriété 33: Fit-to-Screen
Pour tout workflow, la fonction "ajuster à l'écran" doit calculer le zoom et la position pour que tous les nodes soient visibles dans le viewport.
Valide: Exigences 14.3
Gestion des Erreurs
Stratégies de Gestion d'Erreurs
1. Erreurs de Validation
- Détection: Validation en temps réel pendant l'édition
- Affichage: Indicateurs visuels sur les nodes/edges problématiques
- Blocage: Empêcher l'exécution si des erreurs critiques existent
- Récupération: Suggestions de correction automatique quand possible
2. Erreurs d'Exécution
- Capture: Intercepter toutes les exceptions pendant l'exécution
- Affichage: Mettre en surbrillance le node en échec avec message d'erreur
- Logging: Enregistrer tous les détails pour debugging
- Récupération: Intégration avec Self-Healing pour tentatives automatiques
3. Erreurs de Sérialisation
- Validation: Vérifier le schéma JSON avant sauvegarde/chargement
- Affichage: Messages d'erreur clairs avec détails du problème
- Récupération: Tentative de migration automatique pour anciennes versions
- Backup: Sauvegarder l'état précédent avant toute modification
4. Erreurs Réseau
- Retry: Réessayer automatiquement les requêtes échouées (3 tentatives)
- Timeout: Timeouts configurables pour toutes les opérations réseau
- Offline: Mode dégradé avec sauvegarde locale si serveur inaccessible
- Feedback: Indicateurs de statut de connexion en temps réel
Codes d'Erreur
class ErrorCode(Enum):
# Validation
MISSING_REQUIRED_PARAMETER = 1001
INVALID_PARAMETER_TYPE = 1002
INVALID_VARIABLE_REFERENCE = 1003
CIRCULAR_DEPENDENCY = 1004
DISCONNECTED_NODE = 1005
# Sérialisation
INVALID_JSON_FORMAT = 2001
MISSING_REQUIRED_FIELD = 2002
VERSION_INCOMPATIBLE = 2003
# Exécution
CONVERSION_FAILED = 3001
EXECUTION_FAILED = 3002
TARGET_NOT_FOUND = 3003
# Réseau
CONNECTION_FAILED = 4001
TIMEOUT = 4002
SERVER_ERROR = 4003
Stratégie de Test
1. Tests Unitaires
Les tests unitaires vérifient le comportement de composants individuels.
Frontend Components
describe('Canvas Component', () => {
test('should create node on drop', () => {
const canvas = render(<Canvas nodes={[]} edges={[]} />);
const nodeType = { type: 'click', name: 'Click' };
fireEvent.drop(canvas, { dataTransfer: { getData: () => nodeType } });
expect(canvas.nodes).toHaveLength(1);
expect(canvas.nodes[0].type).toBe('click');
});
test('should select node on click', () => {
const onSelect = jest.fn();
const nodes = [createMockNode('click')];
const canvas = render(<Canvas nodes={nodes} onNodeSelect={onSelect} />);
fireEvent.click(canvas.getByTestId('node-click-1'));
expect(onSelect).toHaveBeenCalledWith('click-1');
});
});
describe('Properties Panel', () => {
test('should display required parameter warning', () => {
const node = createMockNode('click', { target: '' });
const panel = render(<PropertiesPanel node={node} />);
expect(panel.getByText('Target is required')).toBeInTheDocument();
});
test('should validate parameter on change', () => {
const node = createMockNode('wait', { duration: -1 });
const panel = render(<PropertiesPanel node={node} />);
expect(panel.getByText('Duration must be positive')).toBeInTheDocument();
});
});
Backend API
class TestWorkflowAPI(unittest.TestCase):
def test_create_workflow(self):
"""Test création d'un nouveau workflow"""
response = self.client.post('/api/workflows', json={
'name': 'Test Workflow',
'description': 'Test description'
})
self.assertEqual(response.status_code, 201)
data = response.get_json()
self.assertIn('id', data)
self.assertEqual(data['name'], 'Test Workflow')
def test_serialize_workflow(self):
"""Test sérialisation complète"""
workflow = create_test_workflow_with_nodes(5)
serialized = serialize_workflow(workflow)
self.assertIn('nodes', serialized)
self.assertIn('edges', serialized)
self.assertEqual(len(serialized['nodes']), 5)
def test_validate_required_fields(self):
"""Test validation des champs requis"""
invalid_workflow = {'name': 'Test'} # manque 'nodes' et 'edges'
with self.assertRaises(ValidationError):
validate_workflow(invalid_workflow)
2. Tests d'Intégration
Les tests d'intégration vérifient l'interaction entre composants.
class TestVisualToGraphConversion(unittest.TestCase):
def test_simple_workflow_conversion(self):
"""Test conversion d'un workflow simple"""
visual_workflow = {
'nodes': [
{'id': '1', 'type': 'start', 'position': {'x': 0, 'y': 0}},
{'id': '2', 'type': 'click', 'position': {'x': 100, 'y': 0},
'parameters': {'target': 'Button "Login"', 'timeout': 5000}},
{'id': '3', 'type': 'end', 'position': {'x': 200, 'y': 0}}
],
'edges': [
{'id': 'e1', 'source': '1', 'target': '2'},
{'id': 'e2', 'source': '2', 'target': '3'}
]
}
converter = VisualToGraphConverter()
workflow_graph = converter.convert(visual_workflow)
self.assertEqual(len(workflow_graph.nodes), 3)
self.assertEqual(workflow_graph.nodes[1].type, 'click')
self.assertEqual(workflow_graph.nodes[1].parameters['target'], 'Button "Login"')
def test_workflow_with_conditions(self):
"""Test conversion avec conditions"""
visual_workflow = create_workflow_with_condition()
converter = VisualToGraphConverter()
workflow_graph = converter.convert(visual_workflow)
# Vérifier que les branches true/false sont correctes
condition_node = workflow_graph.get_node_by_type('condition')
self.assertEqual(len(condition_node.output_edges), 2)
true_edge = [e for e in condition_node.output_edges if e.condition.type == 'true'][0]
false_edge = [e for e in condition_node.output_edges if e.condition.type == 'false'][0]
self.assertIsNotNone(true_edge)
self.assertIsNotNone(false_edge)
3. Tests Property-Based
Les tests property-based vérifient que les propriétés universelles sont respectées pour tous les inputs.
Framework: Hypothesis (Python) ou fast-check (TypeScript)
from hypothesis import given, strategies as st
from hypothesis.stateful import RuleBasedStateMachine, rule, invariant
class TestWorkflowProperties(unittest.TestCase):
@given(st.lists(st.text(min_size=1), min_size=1, max_size=20))
def test_property_5_serialization_roundtrip(self, node_names):
"""
Feature: visual-workflow-builder, Property 5: Round-trip de Sérialisation
Pour tout workflow, sérialiser puis désérialiser doit produire un workflow équivalent
"""
# Créer un workflow avec des nodes aléatoires
workflow = create_workflow_with_node_names(node_names)
# Sérialiser
serialized = serialize_workflow(workflow)
# Désérialiser
deserialized = deserialize_workflow(serialized)
# Vérifier l'équivalence
self.assertEqual(len(workflow.nodes), len(deserialized.nodes))
self.assertEqual(len(workflow.edges), len(deserialized.edges))
for i, node in enumerate(workflow.nodes):
self.assertEqual(node.type, deserialized.nodes[i].type)
self.assertEqual(node.parameters, deserialized.nodes[i].parameters)
@given(st.integers(min_value=1, max_value=10))
def test_property_1_node_deletion_cascade(self, num_nodes):
"""
Feature: visual-workflow-builder, Property 1: Suppression de Node Cascade
Pour tout workflow, supprimer un node doit supprimer tous ses edges
"""
# Créer un workflow linéaire avec num_nodes nodes
workflow = create_linear_workflow(num_nodes)
initial_edge_count = len(workflow.edges)
# Supprimer un node au milieu
middle_node = workflow.nodes[num_nodes // 2]
edges_connected_to_node = [e for e in workflow.edges
if e.source == middle_node.id or e.target == middle_node.id]
workflow.delete_node(middle_node.id)
# Vérifier que les edges connectés sont supprimés
self.assertEqual(len(workflow.edges), initial_edge_count - len(edges_connected_to_node))
for edge in edges_connected_to_node:
self.assertNotIn(edge, workflow.edges)
@given(st.lists(st.text(min_size=1, max_size=20), min_size=1, max_size=10))
def test_property_11_variable_name_uniqueness(self, variable_names):
"""
Feature: visual-workflow-builder, Property 11: Unicité des Noms de Variables
Pour tout workflow, tous les noms de variables doivent être uniques
"""
workflow = VisualWorkflow(id='test', name='Test', nodes=[], edges=[], variables=[])
# Essayer d'ajouter des variables
for name in variable_names:
try:
workflow.add_variable(name, 'string', '')
except ValueError:
# Nom dupliqué, c'est attendu
pass
# Vérifier l'unicité
variable_names_in_workflow = [v.name for v in workflow.variables]
self.assertEqual(len(variable_names_in_workflow), len(set(variable_names_in_workflow)))
@given(st.integers(min_value=2, max_value=50))
def test_property_18_undo_redo_stack_capacity(self, num_actions):
"""
Feature: visual-workflow-builder, Property 18: Pile d'Annulation
Pour toute action, on doit pouvoir annuler au moins 50 actions
"""
workflow = VisualWorkflow(id='test', name='Test', nodes=[], edges=[], variables=[])
undo_manager = UndoManager(workflow)
# Effectuer num_actions actions
for i in range(num_actions):
node = VisualNode(id=f'node-{i}', type='click', position={'x': i*100, 'y': 0},
parameters={}, input_ports=[], output_ports=[])
undo_manager.execute(AddNodeAction(node))
# Vérifier qu'on peut annuler au moins min(50, num_actions) actions
max_undo = min(50, num_actions)
for i in range(max_undo):
self.assertTrue(undo_manager.can_undo())
undo_manager.undo()
self.assertEqual(len(workflow.nodes), num_actions - max_undo)
@given(st.text(min_size=1, max_size=50))
def test_property_10_variable_reference_validation(self, variable_name):
"""
Feature: visual-workflow-builder, Property 10: Validation de Variables
Pour tout paramètre avec ${variable}, la variable doit exister
"""
workflow = VisualWorkflow(id='test', name='Test', nodes=[], edges=[], variables=[])
# Créer un node avec référence de variable
node = VisualNode(
id='node-1',
type='type',
position={'x': 0, 'y': 0},
parameters={'text': f'${{{variable_name}}}'},
input_ports=[],
output_ports=[]
)
workflow.nodes.append(node)
# Valider
validation_result = validate_workflow(workflow)
# Si la variable n'existe pas, doit avoir une erreur
if variable_name not in [v.name for v in workflow.variables]:
self.assertFalse(validation_result.is_valid)
self.assertTrue(any('variable' in err.lower() for err in validation_result.errors))
else:
self.assertTrue(validation_result.is_valid)
class WorkflowStateMachine(RuleBasedStateMachine): """ Machine à états pour tester les propriétés de manière stateful """ def init(self): super().init() self.workflow = VisualWorkflow(id='test', name='Test', nodes=[], edges=[], variables=[]) self.undo_manager = UndoManager(self.workflow)
@rule()
def add_node(self):
"""Ajouter un node aléatoire"""
node_id = f'node-{len(self.workflow.nodes)}'
node = VisualNode(id=node_id, type='click', position={'x': 0, 'y': 0},
parameters={}, input_ports=[], output_ports=[])
self.undo_manager.execute(AddNodeAction(node))
@rule()
def delete_node(self):
"""Supprimer un node aléatoire"""
if self.workflow.nodes:
node = random.choice(self.workflow.nodes)
self.undo_manager.execute(DeleteNodeAction(node.id))
@rule()
def undo(self):
"""Annuler la dernière action"""
if self.undo_manager.can_undo():
self.undo_manager.undo()
@rule()
def redo(self):
"""Refaire la dernière action annulée"""
if self.undo_manager.can_redo():
self.undo_manager.redo()
@invariant()
def nodes_have_unique_ids(self):
"""Invariant: tous les nodes ont des IDs uniques"""
node_ids = [n.id for n in self.workflow.nodes]
assert len(node_ids) == len(set(node_ids))
@invariant()
def edges_reference_existing_nodes(self):
"""Invariant: tous les edges référencent des nodes existants"""
node_ids = {n.id for n in self.workflow.nodes}
for edge in self.workflow.edges:
assert edge.source in node_ids
assert edge.target in node_ids
TestWorkflowStateMachine = WorkflowStateMachine.TestCase
### 4. Tests End-to-End
Les tests end-to-end vérifient le système complet du frontend au backend.
```python
class TestEndToEndWorkflow(unittest.TestCase):
def test_complete_workflow_creation_and_execution(self):
"""Test du cycle complet: création → sauvegarde → exécution"""
# 1. Créer un workflow via l'API
response = self.client.post('/api/workflows', json={
'name': 'Login Workflow',
'description': 'Test login automation'
})
workflow_id = response.get_json()['id']
# 2. Ajouter des nodes
nodes = [
{'id': '1', 'type': 'start', 'position': {'x': 0, 'y': 0}},
{'id': '2', 'type': 'navigate', 'position': {'x': 100, 'y': 0},
'parameters': {'url': 'https://example.com/login'}},
{'id': '3', 'type': 'type', 'position': {'x': 200, 'y': 0},
'parameters': {'target': 'input[name="username"]', 'text': 'testuser'}},
{'id': '4', 'type': 'type', 'position': {'x': 300, 'y': 0},
'parameters': {'target': 'input[name="password"]', 'text': 'testpass'}},
{'id': '5', 'type': 'click', 'position': {'x': 400, 'y': 0},
'parameters': {'target': 'button[type="submit"]'}},
{'id': '6', 'type': 'end', 'position': {'x': 500, 'y': 0}}
]
edges = [
{'id': 'e1', 'source': '1', 'target': '2'},
{'id': 'e2', 'source': '2', 'target': '3'},
{'id': 'e3', 'source': '3', 'target': '4'},
{'id': 'e4', 'source': '4', 'target': '5'},
{'id': 'e5', 'source': '5', 'target': '6'}
]
# 3. Mettre à jour le workflow
response = self.client.put(f'/api/workflows/{workflow_id}', json={
'nodes': nodes,
'edges': edges
})
self.assertEqual(response.status_code, 200)
# 4. Valider le workflow
response = self.client.post(f'/api/workflows/{workflow_id}/validate')
validation = response.get_json()
self.assertTrue(validation['is_valid'])
# 5. Exécuter le workflow
response = self.client.post(f'/api/workflows/{workflow_id}/execute', json={
'mode': 'test'
})
execution_id = response.get_json()['execution_id']
# 6. Attendre la completion
self.wait_for_execution_completion(execution_id, timeout=30)
# 7. Vérifier les résultats
response = self.client.get(f'/api/executions/{execution_id}')
execution = response.get_json()
self.assertEqual(execution['status'], 'completed')
self.assertTrue(execution['success'])
self.assertEqual(len(execution['node_results']), 6)
5. Tests de Performance
class TestPerformance(unittest.TestCase):
def test_large_workflow_serialization(self):
"""Test performance de sérialisation avec 100 nodes"""
workflow = create_workflow_with_nodes(100)
start_time = time.time()
serialized = serialize_workflow(workflow)
end_time = time.time()
# Doit être < 1 seconde
self.assertLess(end_time - start_time, 1.0)
self.assertEqual(len(serialized['nodes']), 100)
def test_workflow_validation_performance(self):
"""Test performance de validation avec workflow complexe"""
workflow = create_complex_workflow_with_conditions_and_loops(50)
start_time = time.time()
validation_result = validate_workflow(workflow)
end_time = time.time()
# Doit être < 100ms
self.assertLess(end_time - start_time, 0.1)
self.assertTrue(validation_result.is_valid)
API Design
REST Endpoints
Workflows
GET /api/workflows # Liste tous les workflows
POST /api/workflows # Crée un nouveau workflow
GET /api/workflows/:id # Récupère un workflow spécifique
PUT /api/workflows/:id # Met à jour un workflow
DELETE /api/workflows/:id # Supprime un workflow
POST /api/workflows/:id/validate # Valide un workflow
POST /api/workflows/:id/execute # Exécute un workflow
GET /api/workflows/:id/export # Exporte un workflow (JSON/YAML)
POST /api/workflows/import # Importe un workflow
Templates
GET /api/templates # Liste tous les templates
POST /api/templates # Crée un nouveau template
GET /api/templates/:id # Récupère un template spécifique
POST /api/templates/:id/instantiate # Crée un workflow depuis un template
Node Types
GET /api/node-types # Liste tous les types de nodes disponibles
GET /api/node-types/:type # Récupère la définition d'un type spécifique
Executions
GET /api/executions/:id # Récupère le statut d'une exécution
POST /api/executions/:id/cancel # Annule une exécution en cours
WebSocket Events
Client → Server
// S'abonner aux mises à jour d'exécution
{
type: 'subscribe_execution',
execution_id: string
}
// Se désabonner
{
type: 'unsubscribe_execution',
execution_id: string
}
Server → Client
// Statut d'exécution
{
type: 'execution_status',
execution_id: string,
status: 'running' | 'completed' | 'failed',
progress: number, // 0-100
current_node_id?: string
}
// Statut d'un node
{
type: 'node_status',
execution_id: string,
node_id: string,
status: 'running' | 'success' | 'failed' | 'skipped',
duration?: number,
error?: string
}
// Résultat final
{
type: 'execution_complete',
execution_id: string,
success: boolean,
duration: number,
node_results: NodeResult[]
}
Considérations de Sécurité
1. Validation d'Entrée
- Sanitization: Tous les inputs utilisateur doivent être nettoyés pour prévenir les injections
- Schema Validation: Utiliser JSON Schema pour valider tous les workflows
- Size Limits: Limiter la taille des workflows (max 1000 nodes, max 10MB)
- Rate Limiting: Limiter les requêtes API (100 req/min par utilisateur)
2. Exécution Sécurisée
- Sandboxing: Exécuter les workflows dans un environnement isolé
- Permissions: Contrôle d'accès basé sur les rôles (RBAC)
- Audit Trail: Logger toutes les actions utilisateur avec timestamps
- Resource Limits: Limiter CPU (80%), mémoire (2GB), durée (30min)
3. Stockage Sécurisé
- Encryption: Chiffrer les workflows sensibles au repos (AES-256)
- Access Control: Permissions granulaires (owner, editor, viewer)
- Backup: Sauvegardes automatiques quotidiennes avec rétention 30 jours
- Versioning: Historique complet des modifications avec rollback
4. Communication Sécurisée
- HTTPS: Toutes les communications via TLS 1.3
- Authentication: JWT tokens avec expiration (1h) et refresh (7j)
- CORS: Configuration stricte des origines autorisées
- WebSocket: Authentification et chiffrement des messages
Plan de Déploiement
Phase 1: MVP (4-6 semaines)
Backend
- API REST de base (CRUD workflows)
- Conversion Visual → WorkflowGraph
- Exécution simple via ExecutionLoop
- Templates de base (Login, Form Fill)
- Validation structurelle
Frontend
- Canvas avec drag & drop basique
- Palette avec nodes essentiels (Click, Type, Wait, Navigate, If, Loop)
- Properties panel simple
- Target selector basique
- Sauvegarde/Chargement
Tests
- Tests unitaires critiques (>70% coverage)
- Tests d'intégration de base
- Validation manuelle des workflows
Phase 2: Fonctionnalités Avancées (4-6 semaines)
Backend
- WebSocket pour temps réel
- Templates avancés (Data Extraction, API Integration)
- Export/Import (JSON, YAML)
- Validation avancée (cycles, variables)
- Migration de versions
Frontend
- Undo/Redo complet
- Zoom/Pan optimisé avec virtualisation
- Variables management UI
- Validation temps réel avec indicateurs
- Raccourcis clavier
- Minimap et fit-to-screen
Tests
- Tests de performance (100 nodes @ 60fps)
- Property-based tests (Hypothesis)
- Tests end-to-end automatisés
- Tests de charge (10 utilisateurs simultanés)
Phase 3: Production (2-4 semaines)
Optimisations
- Performance tuning (profiling, optimisations)
- Caching intelligent (Redis pour templates)
- Monitoring et métriques (Prometheus, Grafana)
- Documentation utilisateur complète
Sécurité
- Audit de sécurité complet
- Penetration testing
- Hardening (rate limiting, input validation)
- Backup et disaster recovery
Déploiement
- CI/CD pipeline (GitHub Actions)
- Monitoring production (alertes, logs)
- Rollback strategy (blue-green deployment)
- Formation utilisateurs
Métriques de Succès
1. Métriques Techniques
Performance
- Rendu Canvas: 60fps avec 100+ nodes ✓
- Latence API: < 200ms pour opérations CRUD ✓
- Temps de Conversion: Visual → Graph < 100ms ✓
- Temps de Chargement: < 2s pour workflows de 50 nodes ✓
- Uptime: > 99.9% ✓
Qualité
- Test Coverage: > 80% pour code critique ✓
- Bug Rate: < 5 bugs critiques par release ✓
- Property Tests: 100% des propriétés testées ✓
- Validation Rate: > 95% des workflows valides avant exécution ✓
2. Métriques Utilisateur
Adoption
- Time to First Workflow: < 5 minutes pour un utilisateur novice ✓
- Workflow Creation Speed: 50% plus rapide qu'écrire du code ✓
- Error Rate: < 5% de workflows invalides créés ✓
- Completion Rate: > 90% des workflows créés sont exécutés ✓
Satisfaction
- User Satisfaction: > 4.5/5 dans les surveys ✓
- NPS Score: > 50 ✓
- Feature Requests: Priorisation basée sur votes utilisateurs ✓
- Support Tickets: < 10% liés au Visual Builder ✓
3. Métriques Business
Productivité
- Workflows Created: 3x plus de workflows créés vs méthode manuelle ✓
- Time Saved: 60% de réduction du temps de développement ✓
- Reusability: 40% des workflows utilisent des templates ✓
- Collaboration: 30% des workflows sont partagés entre utilisateurs ✓
Adoption
- Active Users: 80% des utilisateurs créent au moins 1 workflow ✓
- Retention: 70% des utilisateurs reviennent après 1 semaine ✓
- Power Users: 20% des utilisateurs créent 5+ workflows ✓
- Template Usage: 50% des nouveaux workflows partent d'un template ✓
Conclusion
Le Visual Workflow Builder représente une évolution majeure de RPA Vision V3, transformant un outil technique en une solution accessible à tous. L'architecture proposée garantit:
- Simplicité d'usage sans compromis sur la puissance
- Performance adaptée aux workflows complexes (100+ nodes @ 60fps)
- Intégration parfaite avec l'écosystème existant (ExecutionLoop, Analytics, Self-Healing)
- Extensibilité pour les futures fonctionnalités (collaboration, AI-assisted design)
- Qualité grâce à une stratégie de test complète (33 propriétés testées)
Cette solution positionnera RPA Vision V3 comme le leader du marché RPA no-code/low-code, démocratisant l'automatisation pour tous les utilisateurs, des débutants aux experts.
Prochaines Étapes
- Validation du Design: Review avec les stakeholders
- Création du Plan d'Implémentation: Tasks détaillées avec estimations
- Setup de l'Environnement: Configuration du projet frontend/backend
- Développement Phase 1: MVP en 4-6 semaines
- Tests et Itération: Feedback utilisateurs et améliorations