How to Configure Different Nodes in Business Workflow
Overview
Business Workflow Test Automation provides various node types, each serving a specific purpose in your workflow. This comprehensive guide explains how to configure each node type, including all parameters, scripts, and best practices.
Every node has three configuration tabs:
- Parameters: Node-specific settings and inputs
- Pre-Script: JavaScript executed before the node runs
- Post-Script: JavaScript executed after the node completes
1. Simple Conditional
Purpose
Controls workflow branching based on a boolean condition. Routes execution to different paths based on whether a condition evaluates to true or false.
When to Use
- Decision points in business logic
- Validation checks before proceeding
- Route workflows based on data values
- Handle different scenarios (success vs. failure)
- Check prerequisites before expensive operations
Parameters Tab
Condition Field:
A JavaScript expression that evaluates to true or false.
Syntax:
// Basic comparison
inputs.userStatus === "active"
// Numeric comparison
inputs.orderTotal > 100
// Multiple conditions with AND
inputs.isAdmin === true && inputs.accountAge > 30
// Multiple conditions with OR
inputs.paymentStatus === "success" || inputs.paymentStatus === "pending"
// Null/undefined checks
inputs.userId !== null && typeof inputs.userId !== 'undefined'
// String contains
inputs.errorMessage.includes("timeout")
// Array length
inputs.cartItems.length > 0
// Negation
!inputs.isGuest
// Complex nested conditions
(inputs.orderTotal > 100 && inputs.isPremium) || inputs.hasDiscountCommon Condition Examples:
Status Checks:
inputs.paymentStatus === "success"
inputs.accountStatus === "verified"
inputs.orderStatus === "completed"Numeric Comparisons:
inputs.cartTotal > 50
inputs.retryCount < 3
inputs.stockLevel >= inputs.orderQuantityBoolean Checks:
inputs.emailVerified === true
inputs.hasActiveSubscription
!inputs.isBlockedExistence Checks:
typeof inputs.userId !== 'undefined'
inputs.orderId !== null
inputs.email && inputs.email.length > 0String Matching:
inputs.userRole === "admin" || inputs.userRole === "moderator"
inputs.country === "US" || inputs.country === "CA"Pre-Script Tab
Optional. Use to prepare data before evaluating the condition.
Example:
// Normalize data before condition check
if (typeof inputs.userStatus === 'string') {
inputs.userStatus = inputs.userStatus.toLowerCase();
}
// Log for debugging
console.log("Checking condition with status: " + inputs.userStatus);
// Set fallback values
if (!inputs.retryCount) {
inputs.retryCount = 0;
}Post-Script Tab
Optional. Use to log which path was taken or set additional variables based on the branch.
Example:
// Log which branch was taken
if (inputs.conditionResult === true) {
console.log("Condition passed - proceeding to success path");
} else {
console.log("Condition failed - proceeding to failure path");
}
// Set tracking variables
inputs.lastConditionCheck = new Date().toISOString();Connection Points
Output Ports:
- True: Connect to nodes that execute when condition is true
- False: Connect to nodes that execute when condition is false
Use Case Examples
Example 1: Payment Validation
// Condition
inputs.paymentStatus === "success" && inputs.transactionId !== null
// True → Create Order
// False → Send Payment Failure EmailExample 2: Stock Check
// Condition
inputs.availableStock >= inputs.requestedQuantity
// True → Reserve Inventory
// False → Send Out of Stock NotificationExample 3: User Role Check
// Condition
inputs.userRole === "admin" || inputs.userRole === "supervisor"
// True → Allow Action
// False → Show Access DeniedExample 4: Retry Logic
// Condition
inputs.retryCount < inputs.maxRetries && inputs.lastAttemptFailed
// True → Retry Operation
// False → Send Failure NotificationBest Practices
Do:
- Keep conditions simple and readable
- Use meaningful variable names
- Test both true and false paths
- Add comments for complex conditions
- Log condition evaluation for debugging
Don’t:
- Create overly complex nested conditions
- Use undefined variables (check existence first)
- Forget to handle both branches
- Mix business logic with condition evaluation
- Use side effects in conditions
2. Event Conditional
Purpose
Similar to Simple Conditional but adds timeout functionality. Waits for a condition to become true, with a maximum wait time. Useful for asynchronous operations and approval workflows.
When to Use
- Approval workflows with timeout deadlines
- Waiting for external events (webhooks, callbacks)
- Polling for status changes with maximum wait
- Asynchronous operation completion
- Time-sensitive decisions
Parameters Tab
Timeout:
Maximum time to wait for the condition to become true.
Timeout Value:
- Numeric value (integer)
- Example:
5,30,120
Timeout Unit:
- Milliseconds: Very short waits (1000 = 1 second)
- Seconds: Short waits (60 = 1 minute)
- Minutes: Medium waits (30 = 30 minutes)
- Hours: Long waits (24 = 1 day)
- Days: Very long waits (7 = 1 week)
- Months: Extremely long waits (3 = 3 months)
Examples:
5 seconds
2 minutes
1 hour
2 days
1 monthCondition Field:
Same as Simple Conditional - JavaScript boolean expression.
inputs.approvalStatus === "approved"
inputs.paymentProcessed === true
inputs.webhookReceived === trueHow It Works
- Check Condition: Evaluates the condition
- If True: Immediately proceeds on True path
- If False: Waits and rechecks periodically
- If Becomes True: Proceeds on True path
- If Timeout Expires: Proceeds on False path
Pre-Script Tab
Example:
// Initialize tracking
inputs.conditionCheckStartTime = new Date().toISOString();
inputs.checkCount = 0;
console.log("Starting event conditional with timeout: " + inputs.timeoutValue + " " + inputs.timeoutUnit);Post-Script Tab
Example:
// Log outcome
if (inputs.conditionMetBeforeTimeout) {
console.log("Condition met within timeout period");
const elapsed = (new Date() - new Date(inputs.conditionCheckStartTime)) / 1000;
console.log("Time elapsed: " + elapsed + " seconds");
} else {
console.log("Timeout expired - condition not met");
}Connection Points
Output Ports:
- True: Condition became true within timeout
- False: Timeout expired without condition becoming true
Use Case Examples
Example 1: Approval Workflow
// Timeout: 2 days
// Condition: inputs.approvalStatus === "approved"
// True → Approval received → Process request
// False → Timeout expired → Send reminder or escalateExample 2: Payment Processing
// Timeout: 5 minutes
// Condition: inputs.paymentStatus === "completed"
// True → Payment completed → Create order
// False → Payment timeout → Cancel transactionExample 3: Email Verification
// Timeout: 24 hours
// Condition: inputs.emailVerified === true
// True → Email verified → Activate account
// False → Verification timeout → Send reminderExample 4: Webhook Callback
// Timeout: 30 seconds
// Condition: inputs.webhookCallbackReceived === true
// True → Callback received → Process data
// False → Callback timeout → Use fallback methodBest Practices
Do:
- Set realistic timeouts for the operation
- Consider using shorter timeouts for testing
- Handle timeout scenarios gracefully
- Log timeout events for monitoring
- Provide user feedback on delays
Don’t:
- Set extremely long timeouts without reason
- Ignore timeout scenarios
- Use event conditionals for instant checks (use Simple Conditional)
- Forget to test timeout path
- Block critical workflows with long waits
3. Signal
Purpose
Sends a signal that can be used to coordinate between workflows or trigger events. Useful for orchestrating complex, multi-workflow processes.
When to Use
- Coordinating parallel workflows
- Triggering dependent processes
- Synchronization points in distributed workflows
- Event notifications between systems
- Workflow orchestration
Parameters Tab
Signal Name:
A unique identifier for the signal.
Naming Convention:
- Use descriptive names
- lowercase_with_underscores or camelCase
- Indicate the event being signaled
Examples:
order_created
payment_completed
inventory_reserved
email_sent
user_verified
approval_granted
process_started
task_completedSignal Value (Optional):
Can include data with the signal using variables:
{
"orderId": "${inputs.orderId}",
"status": "completed",
"timestamp": "${inputs.completionTime}"
}Pre-Script Tab
Example:
// Prepare signal data
inputs.signalData = {
orderId: inputs.orderId,
userId: inputs.userId,
status: "completed",
timestamp: new Date().toISOString()
};
console.log("Sending signal: order_completed for order " + inputs.orderId);Post-Script Tab
Example:
// Confirm signal sent
console.log("Signal sent successfully: " + inputs.signalName);
inputs.signalSentAt = new Date().toISOString();How Signals Work
Sending Signals:
- Signal node executes when reached in workflow
- Signal is broadcast with the specified name
- Any workflows waiting for this signal are notified
Receiving Signals (in other workflows):
- Use “Wait for Signal” functionality
- Specify signal name to wait for
- Workflow pauses until signal received
Use Case Examples
Example 1: Order Processing Coordination
Workflow A - Order Creation:
HTTP Request: Create Order
↓
Signal: Send "order_created"Workflow B - Fulfillment (separate workflow):
Wait for Signal: "order_created"
↓
HTTP Request: Start Fulfillment ProcessExample 2: Multi-Stage Approval
Workflow A - First Approval:
Compliance Approval: Manager Approval
↓
Simple Conditional: Approved?
True ↓
Signal: Send "manager_approved"Workflow B - Second Approval:
Wait for Signal: "manager_approved"
↓
Compliance Approval: Director ApprovalExample 3: Parallel Processing
Workflow A - Payment Processing:
HTTP Request: Process Payment
↓
Signal: Send "payment_completed"Workflows B and C (both waiting):
Wait for Signal: "payment_completed"
↓
Workflow B: Send Receipt Email
Workflow C: Update Accounting SystemBest Practices
Do:
- Use descriptive, unique signal names
- Document what each signal means
- Include relevant data with signals
- Test signal coordination thoroughly
- Log signal sending/receiving
Don’t:
- Use generic signal names (“signal1”, “event”)
- Forget to handle cases where signal might not arrive
- Create circular signal dependencies
- Overuse signals when simpler approaches work
- Forget to clean up or reset signals
4. Wait Activity
Purpose
Pauses workflow execution for a specified duration. Useful for rate limiting, simulating delays, or allowing time for external processes to complete.
When to Use
- Rate limiting between API calls
- Simulating realistic delays (think time, processing time)
- Allowing asynchronous operations to complete
- Throttling requests to avoid overwhelming services
- Testing timeout scenarios
- Waiting for email delivery before checking
Parameters Tab
Duration:
Time to wait, specified in milliseconds.
Conversion Guide:
1 second = 1,000 milliseconds
5 seconds = 5,000 milliseconds
1 minute = 60,000 milliseconds
5 minutes = 300,000 milliseconds
1 hour = 3,600,000 millisecondsCan use variables:
${inputs.waitTime}
${inputs.delayInMs}Examples:
1000 // 1 second
5000 // 5 seconds
30000 // 30 seconds
60000 // 1 minute
300000 // 5 minutesPre-Script Tab
Example:
// Calculate dynamic wait time
if (inputs.retryCount > 0) {
// Exponential backoff: 1s, 2s, 4s, 8s...
inputs.waitTime = Math.pow(2, inputs.retryCount) * 1000;
} else {
inputs.waitTime = 1000; // Initial wait
}
console.log("Waiting for " + (inputs.waitTime / 1000) + " seconds");Post-Script Tab
Example:
// Log wait completion
console.log("Wait completed");
inputs.lastWaitCompletedAt = new Date().toISOString();Use Case Examples
Example 1: Rate Limiting API Calls
Loop Over Items: Process 100 records
Inside loop:
HTTP Request: Update record
Wait Activity: 100ms (rate limit to 10 requests/sec)Example 2: Retry with Backoff
HTTP Request: Call External Service
Simple Conditional: Request failed?
True ↓
Wait Activity: 5000ms (5 seconds)
Loop back to retryExample 3: Email Delivery Wait
SES Email: Send verification email
Wait Activity: 3000ms (allow email to send)
HTTP Request: Check email delivery statusExample 4: Simulating User Think Time
UI Test: User fills form
Wait Activity: 2000ms (user reviews before submitting)
UI Test: User submits formExample 5: Asynchronous Processing
HTTP Request: Trigger async job
Wait Activity: 10000ms (10 seconds)
HTTP Request: Check job status
Loop if not completeDynamic Wait Calculation
Pre-Script Examples:
Linear Backoff:
// Wait increases linearly: 1s, 2s, 3s, 4s...
inputs.waitTime = (inputs.retryCount + 1) * 1000;Exponential Backoff:
// Wait doubles each time: 1s, 2s, 4s, 8s...
inputs.waitTime = Math.pow(2, inputs.retryCount) * 1000;Random Jitter:
// Add randomness to avoid thundering herd
const baseWait = 5000;
const jitter = Math.random() * 2000; // 0-2 seconds
inputs.waitTime = baseWait + jitter;Based on Response Time:
// Wait proportional to last response time
if (inputs.lastResponseTime) {
inputs.waitTime = inputs.lastResponseTime * 2; // Wait 2x response time
} else {
inputs.waitTime = 1000; // Default
}Best Practices
Do:
- Use appropriate wait times for the operation
- Consider shorter waits for testing
- Add logging to track wait times
- Use dynamic waits when appropriate
- Document why waiting is necessary
Don’t:
- Use waits as a substitute for proper status checking
- Set unnecessarily long waits
- Wait in tight loops without exit conditions
- Forget to test the scenario without waits
- Use waits to hide timing bugs
5. HTTP Request
Purpose
Makes HTTP API calls to external services or your own backend. The most commonly used node for testing APIs and integrating with services.
When to Use
- Calling REST APIs
- Creating resources (POST)
- Retrieving data (GET)
- Updating resources (PUT/PATCH)
- Deleting resources (DELETE)
- Triggering webhooks
- Integrating with third-party services
Parameters Tab
Host:
The base URL of the API.
Format:
https://api.example.com
http://localhost:3000
https://staging-api.company.comCan use variable:
${inputs.apiHost}
${inputs.environment === "prod" ? "https://api.example.com" : "https://staging-api.example.com"}Endpoint Path:
The specific API endpoint path (without the host).
Format:
/users
/users/123
/orders
/products/search
/auth/loginCan include variables:
/users/${inputs.userId}
/orders/${inputs.orderId}/items
/products/category/${inputs.categoryId}Method:
HTTP method to use.
Options:
- GET: Retrieve data
- POST: Create resources
- PUT: Replace resources
- PATCH: Update resources partially
- DELETE: Delete resources
- HEAD: Get headers only
- OPTIONS: Get allowed methods
Authentication Type:
How to authenticate the request.
Options:
1. Predefined Auth:
- Use saved authentication configuration
- Select from dropdown of configured auth profiles
- Keeps credentials centralized and secure
2. Header Auth:
- Add authentication in request headers
- Common for API keys, custom tokens
- Example:
Key: Authorization Value: Bearer ${inputs.authToken} Key: X-API-Key Value: ${inputs.apiKey}
3. Query Auth:
- Add authentication in query parameters
- Less secure, visible in URLs
- Example:
Key: api_key Value: ${inputs.apiKey} Key: token Value: ${inputs.accessToken}
4. Bearer Token:
- Special case of header auth
- Automatically adds:
Authorization: Bearer <token> - Simply provide the token value
Query Params (Optional):
URL query parameters (the part after ? in URLs).
Format:
Key: page Value: 1
Key: limit Value: 20
Key: sort Value: name
Key: order Value: ascCan use variables:
Key: userId Value: ${inputs.userId}
Key: status Value: ${inputs.filterStatus}
Key: from Value: ${inputs.startDate}Results in URL:
/products?page=1&limit=20&sort=name&order=ascHeaders (Optional):
HTTP headers to send with the request.
Common Headers:
Content-Type: application/json
Accept: application/json
Authorization: Bearer ${inputs.token}
X-Request-ID: ${inputs.requestId}
User-Agent: E2E-Test-Automation/1.0Body (Optional):
Request payload for POST, PUT, PATCH methods.
Format: JSON
Example:
{
"userId": "${inputs.userId}",
"action": "updateProfile",
"data": {
"email": "${inputs.newEmail}",
"firstName": "${inputs.firstName}",
"lastName": "${inputs.lastName}"
}
}Simple Example:
{
"email": "user@example.com",
"password": "securepass123"
}Complex Example:
{
"order": {
"userId": "${inputs.userId}",
"items": [
{
"productId": "${inputs.productId}",
"quantity": ${inputs.quantity},
"price": ${inputs.price}
}
],
"shipping": {
"address": "${inputs.shippingAddress}",
"method": "${inputs.shippingMethod}"
},
"payment": {
"method": "credit_card",
"token": "${inputs.paymentToken}"
}
}
}Pre-Script Tab
Prepare request data before the API call.
Example:
// Build complex request body
inputs.requestPayload = {
userId: inputs.userId,
timestamp: new Date().toISOString(),
action: "purchase",
items: inputs.cartItems.map(item => ({
id: item.productId,
quantity: item.quantity,
price: item.price
}))
};
// Set dynamic headers
inputs.requestId = "REQ-" + Date.now();
// Log request details
console.log("Making API call to: " + inputs.apiHost + inputs.endpoint);
console.log("Method: " + inputs.method);
console.log("Request body: " + JSON.stringify(inputs.requestPayload));Post-Script Tab
Process the API response.
Example:
// Parse response
const response = JSON.parse(outputs.responseBody);
// Check status
if (outputs.statusCode === 200 || outputs.statusCode === 201) {
// Extract data from successful response
inputs.userId = response.data.id;
inputs.userName = response.data.name;
inputs.userEmail = response.data.email;
console.log("API call successful");
console.log("User ID: " + inputs.userId);
} else if (outputs.statusCode === 404) {
console.error("Resource not found");
inputs.resourceNotFound = true;
} else if (outputs.statusCode >= 500) {
console.error("Server error: " + outputs.statusCode);
throw new Error("API server error");
} else {
console.error("API call failed with status: " + outputs.statusCode);
console.error("Response: " + outputs.responseBody);
throw new Error("API call failed");
}
// Extract specific fields
if (response.pagination) {
inputs.totalPages = response.pagination.totalPages;
inputs.currentPage = response.pagination.currentPage;
}
// Track response time
inputs.lastApiResponseTime = outputs.responseTime;Available Outputs
After the HTTP Request completes, these outputs are available:
outputs.statusCode:
- HTTP status code (200, 404, 500, etc.)
outputs.responseBody:
- Response body as string (usually JSON)
- Parse with
JSON.parse(outputs.responseBody)
outputs.responseHeaders:
- Response headers as object
outputs.responseTime:
- Time taken for the request in milliseconds
outputs.requestUrl:
- Full URL that was called
Use Case Examples
Example 1: User Login
// Parameters
Host: https://api.example.com
Path: /auth/login
Method: POST
Body: {
"email": "${inputs.userEmail}",
"password": "${inputs.userPassword}"
}
// Post-Script
const response = JSON.parse(outputs.responseBody);
if (outputs.statusCode === 200) {
inputs.authToken = response.token;
inputs.userId = response.userId;
inputs.loginSuccessful = true;
}Example 2: Get User Profile
// Parameters
Host: ${inputs.apiHost}
Path: /users/${inputs.userId}
Method: GET
Headers:
Authorization: Bearer ${inputs.authToken}
// Post-Script
const user = JSON.parse(outputs.responseBody);
inputs.userName = user.name;
inputs.userEmail = user.email;
inputs.accountStatus = user.status;Example 3: Create Order
// Parameters
Host: https://api.example.com
Path: /orders
Method: POST
Headers:
Content-Type: application/json
Authorization: Bearer ${inputs.authToken}
Body: {
"userId": "${inputs.userId}",
"items": ${JSON.stringify(inputs.cartItems)},
"total": ${inputs.orderTotal}
}
// Post-Script
const order = JSON.parse(outputs.responseBody);
if (outputs.statusCode === 201) {
inputs.orderId = order.orderId;
inputs.orderNumber = order.orderNumber;
inputs.orderCreatedAt = order.createdAt;
}Example 4: Retry Logic
// Pre-Script
if (!inputs.retryCount) {
inputs.retryCount = 0;
}
// Post-Script
if (outputs.statusCode >= 500 || outputs.statusCode === 408) {
// Server error or timeout
inputs.retryCount++;
if (inputs.retryCount < 3) {
console.log("Request failed, retry " + inputs.retryCount);
inputs.shouldRetry = true;
} else {
throw new Error("Max retries exceeded");
}
} else {
inputs.shouldRetry = false;
}Best Practices
Do:
- Use meaningful variable names
- Validate response status codes
- Parse JSON safely with try-catch
- Log important request/response details
- Handle different status codes appropriately
- Use variables for dynamic values
- Set appropriate headers
- Document expected responses
Don’t:
- Hardcode sensitive credentials
- Ignore error status codes
- Assume response format without checking
- Make requests without authentication when required
- Forget to handle timeout scenarios
- Use GET requests with body (not standard)
- Expose API keys in logs
6. Compliance Approval
Purpose
Sends an approval request email and waits for approval response. Useful for workflows requiring human approval with timeout handling.
When to Use
- Multi-stage approval workflows
- Compliance review processes
- Budget approval workflows
- Change request approvals
- Document review and sign-off
Parameters Tab
Timeout:
Maximum time to wait for approval.
Format: Similar to Event Conditional
- Value: Number
- Unit: Seconds, Minutes, Hours, Days
Examples:
2 days
24 hours
3 days
1 week (7 days)To Email:
Recipient’s email address who will approve/reject.
Format:
approver@example.com
${inputs.managerEmail}
${inputs.approverEmail}Can be multiple (comma-separated):
manager@example.com,director@example.comEmail Subject:
Subject line of the approval email.
Example:
Approval Required: Order #${inputs.orderNumber}
Budget Approval Request - $${inputs.amount}
Document Review: ${inputs.documentName}Email Body:
Content of the approval email. Can be plain text or HTML.
Plain Text Example:
Hello,
Please review and approve the following request:
Order Number: ${inputs.orderNumber}
Customer: ${inputs.customerName}
Amount: $${inputs.orderTotal}
Click below to approve or reject:
[Approve] [Reject]
This request will expire in 2 days.HTML Example:
<html>
<body>
<h2>Approval Required</h2>
<p>Hello ${inputs.approverName},</p>
<p>Please review the following purchase order:</p>
<table border="1" cellpadding="10">
<tr><td><strong>Order Number:</strong></td><td>${inputs.orderNumber}</td></tr>
<tr><td><strong>Customer:</strong></td><td>${inputs.customerName}</td></tr>
<tr><td><strong>Amount:</strong></td><td>$${inputs.orderTotal}</td></tr>
<tr><td><strong>Date:</strong></td><td>${inputs.orderDate}</td></tr>
</table>
<p>
<a href="${inputs.approveUrl}" style="background-color: green; color: white; padding: 10px 20px; text-decoration: none;">Approve</a>
<a href="${inputs.rejectUrl}" style="background-color: red; color: white; padding: 10px 20px; text-decoration: none; margin-left: 10px;">Reject</a>
</p>
<p><small>This request expires in ${inputs.timeoutDays} days.</small></p>
</body>
</html>Pre-Script Tab
Example:
// Prepare approval email data
inputs.approverName = inputs.managerName;
inputs.timeoutDays = 2;
// Generate approval URLs
const approvalToken = "APPROVE-" + inputs.orderId + "-" + Date.now();
inputs.approveUrl = "https://example.com/approve?token=" + approvalToken;
inputs.rejectUrl = "https://example.com/reject?token=" + approvalToken;
// Store for tracking
inputs.approvalRequestSentAt = new Date().toISOString();
inputs.approvalToken = approvalToken;
console.log("Sending approval request to: " + inputs.approverEmail);Post-Script Tab
Example:
// Check approval result
if (outputs.approved === true) {
console.log("Request approved by: " + inputs.approverEmail);
inputs.approvalStatus = "approved";
inputs.approvedAt = new Date().toISOString();
inputs.approvedBy = inputs.approverEmail;
} else if (outputs.timeout === true) {
console.log("Approval timeout - no response within " + inputs.timeoutDays + " days");
inputs.approvalStatus = "timeout";
} else {
console.log("Request rejected by: " + inputs.approverEmail);
inputs.approvalStatus = "rejected";
inputs.rejectedAt = new Date().toISOString();
inputs.rejectionReason = outputs.rejectionReason || "Not provided";
}Connection Points
Output Ports:
- Approved: Approval was granted
- Rejected/Timeout: Approval was denied or timeout expired
Use Case Examples
Example 1: Purchase Order Approval
// First level approval
Compliance Approval: Manager approval (timeout: 2 days)
Approved ↓
Compliance Approval: Director approval (timeout: 3 days)
Approved ↓
HTTP Request: Process orderExample 2: Budget Request
Subject: Budget Approval - $${inputs.requestedAmount}
Body:
Department: ${inputs.department}
Purpose: ${inputs.purpose}
Amount: $${inputs.requestedAmount}
Please review and approve.Example 3: Document Review
Subject: Document Review Required: ${inputs.documentName}
Body:
<p>Please review the attached document and provide approval.</p>
<p>Document: ${inputs.documentName}</p>
<p>Version: ${inputs.version}</p>
<p>Submitted by: ${inputs.submitter}</p>Best Practices
Do:
- Set realistic timeout periods
- Provide clear approval instructions
- Include all necessary information in email
- Handle timeout scenarios gracefully
- Send reminders before timeout (in separate workflow)
- Log approval decisions
- Make approve/reject links clear
Don’t:
- Set extremely short timeouts
- Forget to handle timeout path
- Include sensitive data in emails
- Make approval process confusing
- Forget to track who approved/rejected
- Use Compliance Approval for non-approval workflows
7. External TC Execution
Purpose
Executes external test cases via webhook. Integrates with external testing systems or triggers remote processes.
When to Use
- Integrating with external test systems
- Triggering tests in other tools
- Calling third-party validation services
- Executing tests in other environments
- Coordinating with CI/CD pipelines
Parameters Tab
Webhook CURL:
The complete cURL command for the webhook (optional if providing individual fields).
Example:
curl -X POST https://external-system.com/api/execute-test \
-H "Content-Type: application/json" \
-H "Authorization: Bearer token123" \
-d '{"testId": "TC-001", "environment": "staging"}'Host:
Base URL of the external system.
Example:
https://external-test-system.com
https://ci.company.com
${inputs.externalSystemUrl}Method:
HTTP method for the webhook.
Options: GET, POST, PUT, PATCH, DELETE
Endpoint:
The webhook endpoint path.
Example:
/api/execute-test
/webhooks/trigger-test
/tests/${inputs.testId}/runAuthentication Type:
Same options as HTTP Request:
- Predefined Auth
- Header Auth
- Query Auth
- Bearer Token
Query Params:
URL parameters for the webhook.
Example:
testId: ${inputs.externalTestId}
environment: staging
async: trueHeaders:
HTTP headers for the webhook.
Example:
Content-Type: application/json
Authorization: Bearer ${inputs.externalApiToken}
X-Callback-URL: ${inputs.callbackUrl}Body:
Request payload for POST/PUT methods.
Example:
{
"testCaseId": "${inputs.testCaseId}",
"environment": "${inputs.environment}",
"parameters": {
"userId": "${inputs.userId}",
"scenario": "${inputs.scenario}"
},
"callback": {
"url": "${inputs.callbackUrl}",
"method": "POST"
}
}Pre-Script Tab
Example:
// Prepare webhook request
inputs.callbackUrl = "https://e2e-automation.com/webhooks/test-complete?id=" + inputs.workflowId;
inputs.externalTestId = "EXT-" + Date.now();
// Set timeout for external test
inputs.externalTestTimeout = 300000; // 5 minutes
console.log("Triggering external test: " + inputs.testCaseId);
console.log("Callback URL: " + inputs.callbackUrl);Post-Script Tab
Example:
// Process webhook response
const response = JSON.parse(outputs.responseBody);
if (outputs.statusCode === 200 || outputs.statusCode === 202) {
inputs.externalTestTriggered = true;
inputs.externalTestJobId = response.jobId;
inputs.externalTestStatus = response.status || "pending";
console.log("External test triggered successfully");
console.log("Job ID: " + inputs.externalTestJobId);
} else {
console.error("Failed to trigger external test");
console.error("Status: " + outputs.statusCode);
console.error("Response: " + outputs.responseBody);
throw new Error("External test execution failed");
}
// Extract callback data if needed
if (response.estimatedDuration) {
inputs.estimatedWaitTime = response.estimatedDuration;
}Use Case Examples
Example 1: Trigger Selenium Test
{
"testSuite": "regression",
"browser": "chrome",
"environment": "staging",
"testCases": ["login", "checkout", "payment"],
"callback": {
"url": "${inputs.callbackUrl}",
"on": ["complete", "failure"]
}
}Example 2: Execute Performance Test
{
"testType": "load",
"duration": 300,
"virtualUsers": 100,
"rampUp": 60,
"targetUrl": "${inputs.targetUrl}",
"scenarios": ["browse", "search", "purchase"]
}Example 3: Mobile App Test
{
"platform": "iOS",
"device": "iPhone 14",
"appVersion": "${inputs.appVersion}",
"testSuite": "smoke",
"notifyOnComplete": true
}Best Practices
Do:
- Provide callback URLs for async operations
- Set appropriate timeouts
- Handle webhook failures gracefully
- Log webhook triggers
- Validate webhook responses
- Document webhook contracts
- Test webhook integration
Don’t:
- Assume synchronous responses
- Ignore webhook failures
- Forget to handle timeouts
- Hardcode credentials
- Skip error handling
- Use webhooks for simple operations
8. SES Email
Purpose
Sends emails using AWS Simple Email Service (SES). Useful for notifications, confirmations, and alerts in workflows.
When to Use
- Order confirmations
- Registration emails
- Notification emails
- Status updates
- Error alerts
- Reports and summaries
Parameters Tab
SES Email Node Notification:
AWS SES configuration (usually pre-configured in E2E Test Automation).
AWS Email Service:
AWS account credentials for SES.
Source Email:
From email address (must be verified in AWS SES).
Format:
noreply@example.com
notifications@company.com
support@example.com
${inputs.senderEmail}To Email:
Recipient email address(es).
Single recipient:
user@example.com
${inputs.userEmail}Multiple recipients (comma-separated):
user1@example.com,user2@example.com,user3@example.com
${inputs.userEmail},${inputs.managerEmail}Subject:
Email subject line.
Example:
Order Confirmation - #${inputs.orderNumber}
Welcome to ${inputs.companyName}!
Password Reset Request
Your Report is ReadyBody:
Email content (plain text or HTML).
Plain Text Example:
Hello ${inputs.userName},
Thank you for your order!
Order Number: ${inputs.orderNumber}
Order Date: ${inputs.orderDate}
Total Amount: $${inputs.orderTotal}
Your order will be shipped to:
${inputs.shippingAddress}
Track your order: ${inputs.trackingUrl}
Thank you for shopping with us!
Best regards,
The TeamHTML Example:
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.header { background-color: #4CAF50; color: white; padding: 20px; text-align: center; }
.content { padding: 20px; }
.footer { background-color: #f1f1f1; padding: 10px; text-align: center; font-size: 12px; }
.button { background-color: #4CAF50; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
</style>
</head>
<body>
<div class="header">
<h1>Order Confirmation</h1>
</div>
<div class="content">
<p>Hello ${inputs.userName},</p>
<p>Thank you for your order! Your purchase has been confirmed.</p>
<h3>Order Details</h3>
<table>
<tr>
<th>Order Number</th>
<td>${inputs.orderNumber}</td>
</tr>
<tr>
<th>Order Date</th>
<td>${inputs.orderDate}</td>
</tr>
<tr>
<th>Total Amount</th>
<td>$${inputs.orderTotal}</td>
</tr>
</table>
<h3>Items Ordered</h3>
<ul>
${inputs.orderItems}
</ul>
<h3>Shipping Information</h3>
<p>${inputs.shippingAddress}</p>
<p>Estimated Delivery: ${inputs.estimatedDelivery}</p>
<p style="text-align: center; margin-top: 30px;">
<a href="${inputs.trackingUrl}" class="button">Track Your Order</a>
</p>
</div>
<div class="footer">
<p>Thank you for shopping with us!</p>
<p>If you have any questions, contact us at support@example.com</p>
</div>
</body>
</html>Pre-Script Tab
Example:
// Build dynamic email content
inputs.orderItems = inputs.cartItems.map(item =>
`<li>${item.name} - Quantity: ${item.quantity} - $${item.price}</li>`
).join('');
inputs.orderTotal = inputs.cartItems.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
).toFixed(2);
inputs.orderDate = new Date().toLocaleDateString();
// Generate tracking URL
inputs.trackingUrl = "https://example.com/track?order=" + inputs.orderNumber;
console.log("Sending order confirmation email to: " + inputs.userEmail);Post-Script Tab
Example:
// Log email sent
if (outputs.emailSent === true) {
console.log("Email sent successfully to: " + inputs.userEmail);
inputs.emailSentAt = new Date().toISOString();
inputs.emailStatus = "sent";
} else {
console.error("Failed to send email");
console.error("Error: " + outputs.error);
inputs.emailStatus = "failed";
}
// Track for future reference
inputs.lastEmailType = "order_confirmation";
inputs.lastEmailRecipient = inputs.userEmail;Email Templates
Welcome Email:
<h1>Welcome to ${inputs.companyName}!</h1>
<p>Hi ${inputs.firstName},</p>
<p>Thank you for joining us. We're excited to have you!</p>
<p><a href="${inputs.verificationUrl}">Verify your email address</a></p>Password Reset:
<h2>Password Reset Request</h2>
<p>Hello ${inputs.userName},</p>
<p>We received a request to reset your password.</p>
<p><a href="${inputs.resetUrl}">Reset your password</a></p>
<p>This link expires in 24 hours.</p>
<p>If you didn't request this, please ignore this email.</p>Order Shipped:
<h2>Your Order Has Shipped!</h2>
<p>Hi ${inputs.userName},</p>
<p>Good news! Your order #${inputs.orderNumber} has been shipped.</p>
<p>Tracking Number: ${inputs.trackingNumber}</p>
<p><a href="${inputs.trackingUrl}">Track your shipment</a></p>
<p>Estimated Delivery: ${inputs.estimatedDelivery}</p>Payment Confirmation:
<h2>Payment Received</h2>
<p>Dear ${inputs.customerName},</p>
<p>We have received your payment of $${inputs.paymentAmount}.</p>
<p>Transaction ID: ${inputs.transactionId}</p>
<p>Payment Method: ${inputs.paymentMethod}</p>
<p>Date: ${inputs.paymentDate}</p>Use Case Examples
Example 1: Order Workflow
HTTP Request: Create Order
↓
SES Email: Send order confirmation
↓
Wait Activity: 1 day
↓
SES Email: Send shipping updateExample 2: Registration Workflow
HTTP Request: Create user account
↓
SES Email: Send welcome email with verification link
↓
Event Conditional: Wait for email verification (24 hours)
Verified ↓
SES Email: Send getting started guideExample 3: Error Notification
HTTP Request: Critical operation
↓
Simple Conditional: Operation failed?
True ↓
SES Email: Send error alert to adminBest Practices
Do:
- Use verified sender addresses
- Test email templates before production
- Include unsubscribe links if applicable
- Use responsive HTML for mobile
- Personalize with variables
- Keep subject lines clear and concise
- Provide plain text alternative
- Log email sending for tracking
Don’t:
- Send emails without user consent
- Include sensitive data in plain text
- Forget to test email rendering
- Use all caps in subject lines
- Send too many emails
- Ignore email bounce notifications
- Use generic “noreply” without alternative contact
9. UI Test
Purpose
Executes a UI automation test scenario within the workflow. Integrates UI testing with API and workflow testing.
When to Use
- Testing complete user journeys
- Validating UI after API changes
- End-to-end business process testing
- User acceptance testing
- Integration testing with UI components
Parameters Tab
Test Suite:
Select the UI test suite containing your UI scenarios.
Dropdown Selection:
- Lists all available UI test suites
- Choose the suite containing the test you want to run
Test Scenario:
Select the specific UI test scenario to execute.
Dropdown Selection:
- Lists scenarios in the selected test suite
- Choose the specific test to run
Test Dataset:
Select dataset variables for the UI test (if needed).
Optional:
- Provides variable values for the UI test
- Allows parameterized testing
- Can use different data sets for same scenario
Framework:
Testing framework used for UI tests.
Usually auto-selected:
- Playwright
- Selenium
- Cypress
- (Framework is typically configured at suite level)
Pre-Script Tab
Example:
// Prepare data for UI test
inputs.testUsername = inputs.userEmail;
inputs.testPassword = inputs.userPassword;
inputs.testEnvironment = "staging";
// Set UI test specific variables
inputs.browserType = "chrome";
inputs.headless = false; // Set to true for faster execution
console.log("Starting UI test: " + inputs.scenarioName);
console.log("Test user: " + inputs.testUsername);Post-Script Tab
Example:
// Process UI test results
if (outputs.testStatus === "passed") {
console.log("UI test passed successfully");
inputs.uiTestPassed = true;
// Extract data from UI test
if (outputs.extractedData) {
inputs.userId = outputs.extractedData.userId;
inputs.sessionToken = outputs.extractedData.sessionToken;
}
} else {
console.error("UI test failed");
console.error("Failure reason: " + outputs.failureReason);
console.error("Screenshot: " + outputs.screenshotUrl);
inputs.uiTestPassed = false;
// Optionally stop workflow on UI test failure
throw new Error("UI test failed: " + outputs.failureReason);
}
// Log test duration
console.log("UI test duration: " + outputs.testDuration + "ms");Available Outputs
outputs.testStatus:
- Status of the test: “passed”, “failed”, “skipped”
outputs.testDuration:
- Time taken to execute the test in milliseconds
outputs.failureReason:
- Reason for failure if test failed
outputs.screenshotUrl:
- URL to screenshot if test failed
outputs.extractedData:
- Any data extracted during UI test execution
Use Case Examples
Example 1: Login and API Validation
UI Test: User login
↓
Post-Script: Extract session token
↓
HTTP Request: Validate session via API
↓
HTTP Request: Get user profile using extracted tokenExample 2: Complete Purchase Flow
UI Test: Add product to cart
↓
HTTP Request: Verify cart via API
↓
UI Test: Proceed to checkout
↓
HTTP Request: Create order via API
↓
SES Email: Send order confirmation
↓
UI Test: Verify order confirmation pageExample 3: Registration with Backend Verification
UI Test: User registration form
↓
Post-Script: Extract registered email
↓
Database: Verify user created in DB
↓
HTTP Request: Check user status via API
↓
SES Email: Send welcome email
↓
UI Test: Verify welcome pageExample 4: Conditional UI Testing
HTTP Request: Get user role
↓
Simple Conditional: Is admin?
├─ True → UI Test: Admin dashboard test
└─ False → UI Test: Regular user dashboard testIntegration Patterns
Pattern 1: UI → API
UI action creates data
↓
API verifies data was created correctlyPattern 2: API → UI
API creates/updates data
↓
UI test verifies data appears correctlyPattern 3: UI → API → UI
UI action
↓
API validates backend state
↓
UI verifies confirmationBest Practices
Do:
- Keep UI tests focused and specific
- Extract data from UI tests for API calls
- Verify UI changes with API calls
- Handle UI test failures gracefully
- Use appropriate wait times
- Test realistic user scenarios
- Combine with API testing for full coverage
Don’t:
- Run long UI tests in tight loops
- Ignore UI test failures
- Forget to extract necessary data
- Skip API validation after UI actions
- Use UI tests for simple API validation
- Create overly complex UI workflows
- Forget to handle async operations
10. Database
Purpose
Executes MongoDB database queries directly in the workflow. Useful for data validation, setup, and teardown.
When to Use
- Validating data was stored correctly
- Setting up test data
- Cleaning up after tests
- Checking database state
- Direct data manipulation
- Complex queries not available via API
Parameters Tab
Query:
MongoDB query in standard MongoDB syntax.
Query Types:
Find (Read):
// Find single document
db.users.findOne({ email: "${inputs.userEmail}" })
// Find multiple documents
db.users.find({ status: "active" })
// Find with projection (specific fields)
db.users.find(
{ role: "admin" },
{ name: 1, email: 1, _id: 0 }
)
// Find with sorting
db.orders.find({ userId: "${inputs.userId}" }).sort({ createdAt: -1 })
// Find with limit
db.products.find({ category: "electronics" }).limit(10)Insert (Create):
// Insert single document
db.users.insertOne({
email: "${inputs.userEmail}",
name: "${inputs.userName}",
status: "active",
createdAt: new Date(),
role: "user"
})
// Insert multiple documents
db.orders.insertMany([
{
userId: "${inputs.userId}",
total: ${inputs.orderTotal},
status: "pending"
},
{
userId: "${inputs.userId}",
total: 50.00,
status: "completed"
}
])Update (Modify):
// Update single document
db.users.updateOne(
{ email: "${inputs.userEmail}" },
{ $set: { status: "verified", verifiedAt: new Date() } }
)
// Update multiple documents
db.orders.updateMany(
{ status: "pending", createdAt: { $lt: new Date(Date.now() - 7*24*60*60*1000) } },
{ $set: { status: "expired" } }
)
// Increment value
db.users.updateOne(
{ userId: "${inputs.userId}" },
{ $inc: { loginCount: 1 } }
)
// Push to array
db.users.updateOne(
{ userId: "${inputs.userId}" },
{ $push: { orders: "${inputs.orderId}" } }
)Delete (Remove):
// Delete single document
db.users.deleteOne({ email: "${inputs.userEmail}" })
// Delete multiple documents
db.orders.deleteMany({ status: "test", createdAt: { $lt: new Date(Date.now() - 30*24*60*60*1000) } })Aggregate (Complex Queries):
// Group and count
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$userId", totalOrders: { $sum: 1 }, totalSpent: { $sum: "$total" } } }
])
// Lookup (join)
db.orders.aggregate([
{ $match: { orderId: "${inputs.orderId}" } },
{ $lookup: {
from: "users",
localField: "userId",
foreignField: "userId",
as: "userDetails"
}
}
])Pre-Script Tab
Example:
// Prepare query parameters
inputs.queryEmail = inputs.userEmail.toLowerCase();
inputs.currentDate = new Date().toISOString();
// Dynamic query building
if (inputs.includeInactive) {
inputs.statusFilter = { $in: ["active", "inactive"] };
} else {
inputs.statusFilter = "active";
}
console.log("Executing database query");
console.log("Collection: users");
console.log("Filter email: " + inputs.queryEmail);Post-Script Tab
Example:
// Process query results
try {
const result = JSON.parse(outputs.queryResult);
if (Array.isArray(result)) {
// Multiple documents
console.log("Found " + result.length + " documents");
if (result.length > 0) {
inputs.userExists = true;
inputs.userId = result[0]._id || result[0].userId;
inputs.userStatus = result[0].status;
inputs.userRole = result[0].role;
} else {
inputs.userExists = false;
}
} else if (result && typeof result === 'object') {
// Single document
console.log("Found document");
inputs.userExists = true;
inputs.userId = result._id || result.userId;
inputs.userStatus = result.status;
inputs.userRole = result.role;
} else {
// No results
console.log("No documents found");
inputs.userExists = false;
}
} catch (error) {
console.error("Error parsing database results: " + error);
throw error;
}
// Validate data
if (inputs.userExists && inputs.userStatus !== "active") {
console.warn("User found but status is: " + inputs.userStatus);
}Use Case Examples
Example 1: Verify User Created
// After API call to create user
Database: Find user
Query: db.users.findOne({ email: "${inputs.userEmail}" })
Post-Script:
if (result exists) {
inputs.userCreated = true;
inputs.dbUserId = result._id;
} else {
throw new Error("User not created in database");
}Example 2: Clean Up Test Data
// Before starting test
Database: Delete test users
Query: db.users.deleteMany({ email: { $regex: "^test.*@example.com$" } })
Post-Script:
console.log("Deleted " + outputs.deletedCount + " test users");Example 3: Validate Order Data
// After order creation
Database: Get order with items
Query: db.orders.aggregate([
{ $match: { orderId: "${inputs.orderId}" } },
{ $lookup: { from: "orderItems", localField: "orderId", foreignField: "orderId", as: "items" } }
])
Post-Script:
const order = result[0];
inputs.orderItemCount = order.items.length;
inputs.orderTotalFromDB = order.total;
if (inputs.orderTotalFromDB !== inputs.expectedTotal) {
throw new Error("Order total mismatch");
}Example 4: Setup Test Data
// Before test workflow
Database: Create test user
Query: db.users.insertOne({
email: "test.user@example.com",
name: "Test User",
status: "active",
role: "user",
createdAt: new Date()
})
Post-Script:
inputs.testUserId = outputs.insertedId;
console.log("Test user created with ID: " + inputs.testUserId);Best Practices
Do:
- Use variables for dynamic values
- Validate query results in post-script
- Handle empty results gracefully
- Use appropriate indexes for performance
- Clean up test data after tests
- Log query execution
- Use specific queries (avoid find all)
- Parse results safely with try-catch
Don’t:
- Use database queries for simple API validations
- Modify production data in tests
- Create queries that return huge datasets
- Forget to handle null/undefined results
- Use database queries in tight loops
- Skip connection error handling
- Perform complex operations better suited for application code
11. Loop Over Items
Purpose
Repeats a set of actions multiple times. Useful for iteration, retries, and batch processing.
When to Use
- Retry logic (attempt operation multiple times)
- Batch processing (process multiple items)
- Pagination (iterate through pages)
- Rate limiting (controlled repetition)
- Performance testing (repeat load)
Parameters Tab
Iteration Count:
Number of times to repeat the loop.
Format:
- Integer value
- Example:
3,10,100
Can use variable:
${inputs.maxRetries}
${inputs.pageCount}
${inputs.numberOfRecords}How It Works
- Loop starts with index 0
- Executes all connected nodes inside loop
- Increments index
- Repeats until iteration count reached
- Proceeds to next node after loop
Loop Index:
- Available as
inputs.loopIndex - Starts at 0
- Increments each iteration
Pre-Script Tab
Example:
// Setup before loop
inputs.successCount = 0;
inputs.failureCount = 0;
inputs.results = [];
console.log("Starting loop with " + inputs.iterationCount + " iterations");Post-Script Tab
Example:
// After each iteration
inputs.results.push({
iteration: inputs.loopIndex,
success: inputs.lastOperationSuccess,
timestamp: new Date().toISOString()
});
if (inputs.lastOperationSuccess) {
inputs.successCount++;
} else {
inputs.failureCount++;
}
console.log("Completed iteration " + (inputs.loopIndex + 1) + " of " + inputs.iterationCount);Loop Control
Break Early (in node inside loop):
// In post-script of node inside loop
if (inputs.desiredConditionMet) {
inputs.breakLoop = true; // Signal to exit loop
}Skip Iteration:
// In post-script of node inside loop
if (inputs.shouldSkip) {
console.log("Skipping iteration " + inputs.loopIndex);
return; // Continue to next iteration
}Use Case Examples
Example 1: Retry with Backoff
Loop Over Items: 3 iterations
Inside loop:
HTTP Request: Call flaky API
Simple Conditional: Request successful?
True → Break loop (success)
False → Wait Activity: (iteration × 1000)ms → ContinueExample 2: Batch Processing
HTTP Request: Get total page count
Post-Script: inputs.pageCount = response.totalPages
Loop Over Items: ${inputs.pageCount} iterations
Inside loop:
HTTP Request: Get page ${inputs.loopIndex + 1}
Post-Script: Process page data
Database: Store processed data
Wait Activity: 100ms (rate limiting)Example 3: Load Testing
Loop Over Items: 100 iterations
Inside loop:
HTTP Request: Create order
Wait Activity: Random delay (100-500ms)
Post-Script: Track response timeExample 4: Multi-Item Processing
HTTP Request: Get items to process
Post-Script: inputs.itemCount = response.items.length
Loop Over Items: ${inputs.itemCount} iterations
Inside loop:
Pre-Script: inputs.currentItem = inputs.items[inputs.loopIndex]
HTTP Request: Process item ${inputs.currentItem.id}
Database: Update item statusPre-Script Inside Loop
Example:
// Executed before each iteration
console.log("=== Iteration " + (inputs.loopIndex + 1) + " ===");
// Access current item from array
if (inputs.itemsToProcess && inputs.itemsToProcess.length > inputs.loopIndex) {
inputs.currentItem = inputs.itemsToProcess[inputs.loopIndex];
console.log("Processing item: " + inputs.currentItem.id);
}
// Dynamic configuration based on iteration
if (inputs.loopIndex === 0) {
console.log("First iteration - using default settings");
} else {
console.log("Retry attempt " + inputs.loopIndex);
inputs.timeout = inputs.timeout * 2; // Exponential backoff
}Post-Script Inside Loop
Example:
// Executed after each iteration
console.log("Iteration " + (inputs.loopIndex + 1) + " completed");
// Track iteration results
if (!inputs.iterationResults) {
inputs.iterationResults = [];
}
inputs.iterationResults.push({
index: inputs.loopIndex,
success: inputs.operationSuccess,
duration: inputs.operationDuration,
data: inputs.operationData
});
// Check if we should exit loop early
if (inputs.operationSuccess && inputs.exitOnFirstSuccess) {
console.log("Success achieved - exiting loop early");
inputs.breakLoop = true;
}
// Handle failures
if (!inputs.operationSuccess) {
inputs.consecutiveFailures = (inputs.consecutiveFailures || 0) + 1;
if (inputs.consecutiveFailures >= 3) {
console.log("Too many consecutive failures - exiting loop");
throw new Error("Loop failed after " + inputs.consecutiveFailures + " consecutive failures");
}
} else {
inputs.consecutiveFailures = 0; // Reset on success
}Advanced Loop Patterns
Pattern 1: Retry Until Success
Loop: 5 iterations
HTTP Request: Operation
Conditional: Success?
True → Set breakLoop = true → Exit
False → Wait → ContinuePattern 2: Process with Batch Size
Pre-Script:
inputs.batchSize = 10;
inputs.totalItems = 100;
inputs.batches = Math.ceil(inputs.totalItems / inputs.batchSize);
Loop: ${inputs.batches} iterations
Pre-Script:
const start = inputs.loopIndex * inputs.batchSize;
const end = start + inputs.batchSize;
inputs.currentBatch = inputs.allItems.slice(start, end);
Process current batchPattern 3: Rate-Limited API Calls
Loop: ${inputs.requestCount} iterations
HTTP Request: Call API
Wait Activity: 100ms (10 requests per second limit)
Post-Script:
if (outputs.statusCode === 429) { // Rate limited
console.log("Rate limited - increasing delay");
inputs.delayBetweenRequests = inputs.delayBetweenRequests * 2;
}Best Practices
Do:
- Set reasonable iteration counts
- Add exit conditions for early termination
- Log each iteration for debugging
- Track results for later analysis
- Handle failures gracefully
- Use appropriate delays in loops
- Consider memory for large loops
Don’t:
- Create infinite or extremely long loops
- Forget to add delays in API call loops
- Ignore loop failures
- Process huge datasets in memory
- Use loops when better alternatives exist
- Forget to clean up after loop
- Skip error handling in loops
Summary
Business Workflow Test Automation provides 11 powerful node types:
- Simple Conditional - Basic branching logic
- Event Conditional - Time-based conditional logic
- Signal - Workflow coordination
- Wait Activity - Execution delays
- HTTP Request - API calls
- Compliance Approval - Approval emails with timeout
- External TC Execution - External test webhooks
- SES Email - Email notifications
- UI Test - UI automation integration
- Database - MongoDB queries
- Loop Over Items - Iteration and retries
Every node supports:
- Parameters: Node-specific configuration
- Pre-Script: Setup before execution
- Post-Script: Processing after execution
- Variables: Data flow between nodes
Master these nodes to create sophisticated, powerful business workflow tests that validate your complete end-to-end processes.