Conditional Action Chains
Required Importsโ
All examples in this guide assume the following imports:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.jspinak.brobot.action.Action;
import io.github.jspinak.brobot.action.ActionResult;
import io.github.jspinak.brobot.action.ObjectCollection;
import io.github.jspinak.brobot.action.basic.click.ClickOptions;
import io.github.jspinak.brobot.action.basic.type.TypeOptions;
import io.github.jspinak.brobot.action.basic.find.PatternFindOptions;
import io.github.jspinak.brobot.action.conditionals.ConditionalActionChain;
import io.github.jspinak.brobot.datatypes.state.stateObject.stateImage.StateImage;
Setup and Prerequisitesโ
Component Setupโ
All ConditionalActionChain examples require a Spring component with injected Action service:
@Component
public class MyAutomation {
private static final Logger log = LoggerFactory.getLogger(MyAutomation.class);
@Autowired
private Action action;
// Your automation methods here
}
StateImage Initializationโ
StateImages must be initialized before use. Example:
StateImage buttonImage = new StateImage.Builder()
.addPattern("images/buttons/my-button.png")
.setSimilarity(0.85)
.build();
For more on StateImage setup, see States in Brobot.
Assumed Variablesโ
Unless otherwise specified, the following examples assume you have:
@Autowired Action action- Injected Brobot Action service- StateImage variables (e.g.,
buttonImage,menuButton) - Pre-initialized StateImage instances - Proper Spring component context with
@Componentannotation
See the Production Examples section for complete, compilable code.
Overviewโ
ConditionalActionChain provides a powerful fluent API for building complex action sequences with conditional execution. This implementation includes the crucial then() method for sequential composition and numerous convenience methods.
Key Featuresโ
- Sequential Composition: The
then()method enables multi-step workflows - Convenience Methods: Direct methods like
click(),type(),scrollDown() - Keyboard Shortcuts: Built-in support for common key combinations
- Control Flow: Methods for stopping chains, retrying, and error handling
- No Explicit Waits: Follows model-based principles - timing via action configurations
- Proper Conditional Logic: True if/then/else execution flow
Basic Examplesโ
Simple Find and Clickโ
// Basic find and click pattern
ConditionalActionChain.find(buttonImage)
.ifFoundClick()
.ifNotFoundLog("Button not found")
.perform(action, new ObjectCollection.Builder().build());
Sequential Actions with then()โ
// The then() method enables sequential workflows
ConditionalActionChain.find(menuButton)
.ifFoundClick()
.then(searchField) // Move to next element
.ifFoundClick()
.ifFoundType("search query")
.then(submitButton) // Continue the flow
.ifFoundClick()
.perform(action, new ObjectCollection.Builder().build());
Real-World Scenariosโ
Login Flowโ
public ActionResult performLogin(String username, String password) {
return ConditionalActionChain.find(loginButton)
.ifFoundClick()
.ifNotFoundLog("Login button not visible")
.then(usernameField) // Sequential action using then()
.ifFoundClick()
.ifFoundType(username)
.then(passwordField) // Continue to next field
.ifFoundClick()
.ifFoundType(password)
.then(submitButton) // Move to submit
.ifFoundClick()
.then(successMessage) // Check for success
.ifFoundLog("Login successful!")
.ifNotFoundLog("Login might have failed")
.perform(action, new ObjectCollection.Builder().build());
}
Save with Confirmation Dialogโ
public ActionResult saveWithConfirmation() {
StateImage saveButton = new StateImage.Builder()
.addPattern("images/buttons/save.png")
.build();
return ConditionalActionChain.find(saveButton)
.ifFoundClick()
.ifNotFoundLog("Save button not found")
.then(confirmDialog) // Look for confirmation
.then(yesButton) // Find yes button within dialog
.ifFoundClick()
.ifNotFoundLog("No confirmation needed")
.then(successMessage) // Verify success
.ifFoundLog("Save successful")
.ifNotFoundLog("Save may have failed")
.perform(action, new ObjectCollection.Builder().build());
}
Retry Patternโ
public ActionResult clickWithRetry(StateImage target, int maxRetries) {
return ConditionalActionChain
.retry(new PatternFindOptions.Builder().build(), maxRetries)
.ifFoundClick()
.ifFoundLog("Successfully clicked after retries")
.ifNotFoundLog("Failed after all attempts")
.perform(action, new ObjectCollection.Builder()
.withImages(target)
.build());
}
Advanced Patternsโ
Multi-Step Form Fillingโ
public ActionResult fillComplexForm(FormData data) {
return ConditionalActionChain
.find(new PatternFindOptions.Builder().build())
.ifNotFoundLog("Form not visible")
.ifNotFoundDo(res -> { throw new RuntimeException("Cannot proceed without form"); })
// Name field - using clearAndType
.then(nameField)
.ifFoundClick()
.clearAndType(data.getName())
// Email field - using tab navigation
.pressTab()
.type(data.getEmail())
// Phone field - using direct navigation
.then(phoneField)
.ifFoundClick()
.ifFoundType(data.getPhone())
// Submit form
.then(submitButton)
.ifFoundClick()
.takeScreenshot("form-submission")
.perform(action, new ObjectCollection.Builder().build());
}
Dynamic UI Navigation with Scrollingโ
public ActionResult scrollToFind(StateImage target) {
return ConditionalActionChain.find(target)
.ifNotFound(chain -> chain.scrollDown())
.ifNotFound(new PatternFindOptions.Builder().build())
.ifNotFound(chain -> chain.scrollDown())
.ifNotFound(new PatternFindOptions.Builder().build())
.ifNotFound(chain -> chain.scrollDown())
.ifFoundClick()
.ifFoundLog("Found and clicked target after scrolling")
.ifNotFoundLog("Could not find target even after scrolling")
.perform(action, new ObjectCollection.Builder()
.withImages(target)
.build());
}
Keyboard Shortcuts Workflowโ
public ActionResult useKeyboardShortcuts() {
return ConditionalActionChain
.find(editorField)
.ifFoundClick()
.pressCtrlA() // Select all
.pressDelete() // Delete content
.type("New content here")
.pressCtrlS() // Save
.then(savedIndicator)
.ifFoundLog("Document saved successfully")
.perform(action, new ObjectCollection.Builder().build());
}
Conditional Patternsโ
Error Handling with Control Flowโ
public ActionResult handleErrors() {
return ConditionalActionChain
.find(submitButton)
.ifFoundClick()
.then(errorDialog)
.ifFoundLog("Error dialog appeared")
.ifFound(chain -> chain.takeScreenshot("error-state"))
.ifFoundDo(res -> {
log.error("Operation failed with error: {}", res.getText());
})
.stopIf(res -> res.getText() != null &&
!res.getText().isEmpty() &&
res.getText().get(0).contains("CRITICAL"))
.then(retryButton)
.ifFoundClick()
.ifFoundLog("Retrying operation")
.perform(action, new ObjectCollection.Builder().build());
}
Wait for Element to Disappearโ
public ActionResult waitForLoadingToComplete() {
StateImage loadingSpinner = new StateImage.Builder()
.addPattern("images/indicators/loading.png")
.build();
return ConditionalActionChain
.find(submitButton)
.ifFoundClick()
.waitVanish(loadingSpinner) // Wait for spinner to disappear
.then(successMessage)
.ifFoundLog("Operation completed successfully")
.then(errorDialog)
.ifFoundLog("Operation failed")
.perform(action, new ObjectCollection.Builder().build());
}
Highlighting and Debuggingโ
public ActionResult debugWorkflow() {
return ConditionalActionChain
.find(targetElement)
.ifFound(chain -> chain.highlight()) // Highlight found element
.ifFoundLog("Found target element") // Log for debugging
.takeScreenshot("debug-1") // Take screenshot
.ifFoundClick()
.takeScreenshot("debug-2") // Another screenshot
.perform(action, new ObjectCollection.Builder().build());
}
Model-Based Automation Principlesโ
No Explicit Waitsโ
Following model-based automation principles, ConditionalActionChain does not include a wait() method. Instead, timing is configured through action options:
// WRONG - Process-based approach with explicit waits
chain.click().wait(2.0).type("text") // Don't do this!
// CORRECT - Model-based approach with action configurations
PatternFindOptions findWithDelay = new PatternFindOptions.Builder()
.setPauseBeforeBegin(2.0) // Timing in action configuration
.build();
ConditionalActionChain.find(findWithDelay)
.ifFoundClick()
.then(new TypeOptions.Builder()
.setTypeDelay(0.1) // Type-specific timing
.build())
.perform(action, objectCollection);
State-Based Navigationโ
public ActionResult navigateToState(State targetState) {
// Focus on states, not processes
return ConditionalActionChain
.find(targetState.getIdentifyingImage())
.ifFoundLog("Already in target state")
.ifNotFound(navigationButton)
.ifFoundClick()
.then(targetState.getIdentifyingImage())
.ifFoundLog("Successfully navigated to state")
.ifNotFoundDo(res -> {
throw new StateTransitionException("Failed to reach target state");
})
.perform(action, new ObjectCollection.Builder().build());
}
Testing Patternsโ
Mock-Friendly Chainsโ
@Test
public void testEnhancedChainFeatures() {
// Setup mock
Action mockAction = mock(Action.class);
ActionResult successResult = new ActionResult();
successResult.setSuccess(true);
when(mockAction.perform(any(ActionConfig.class), any(ObjectCollection[].class)))
.thenReturn(successResult);
// Test the then() method
ActionResult result = ConditionalActionChain
.find(loginButton)
.ifFoundClick()
.then(usernameField) // Sequential composition
.ifFoundType("testuser")
.then(passwordField) // Continue flow
.ifFoundType("password")
.perform(mockAction, new ObjectCollection.Builder().build());
assertTrue(result.isSuccess());
}
Complete API Referenceโ
Core Methodsโ
find(PatternFindOptions)/find(StateImage)- Start chain with findthen(ActionConfig)/then(StateImage)- Sequential action compositionifFound(ActionConfig)- Execute if previous succeededifNotFound(ActionConfig)- Execute if previous failedalways(ActionConfig)- Execute regardless
Convenience Methodsโ
click()/ifFoundClick()- Click actionstype(String)/ifFoundType(String)- Type textclearAndType(String)- Clear field and typescrollDown()/scrollUp()- Scroll actionshighlight()- Highlight last found elementwaitVanish(StateImage)- Wait for element to disappear
Keyboard Shortcutsโ
pressEnter(),pressTab(),pressEscape()pressCtrlS(),pressCtrlA(),pressDelete()pressKey(int keyCode)- Press specific keypressKeyCombo(int modifier, int key)- Key combinations
Control Flowโ
stopChain()- Stop executionstopIf(Predicate<ActionResult>)- Conditional stopretry(ActionConfig, int)- Retry patternthrowError(String)- Throw exception
Logging & Debuggingโ
log(String)- Log messageifFoundLog(String)/ifNotFoundLog(String)- Conditional loggingtakeScreenshot(String)- Capture screenshot
Custom Handlersโ
ifFoundDo(Consumer<ActionResult>)- Custom success handlerifNotFoundDo(Consumer<ActionResult>)- Custom failure handlerifFound(Consumer<ConditionalActionChain>)- Chain operationsifNotFound(Consumer<ConditionalActionChain>)- Chain operations
Production Examplesโ
Here's a complete, compilable example showing ConditionalActionChain in a real component:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.jspinak.brobot.action.Action;
import io.github.jspinak.brobot.action.ActionResult;
import io.github.jspinak.brobot.action.ObjectCollection;
import io.github.jspinak.brobot.action.conditionals.ConditionalActionChain;
import io.github.jspinak.brobot.datatypes.state.stateObject.stateImage.StateImage;
@Component
public class LoginAutomationExample {
private static final Logger log = LoggerFactory.getLogger(LoginAutomationExample.class);
@Autowired
private Action action;
// Initialize StateImages (in real code, these would come from State definitions)
private final StateImage loginButton = new StateImage.Builder()
.addPattern("images/login/login-button.png")
.build();
private final StateImage usernameField = new StateImage.Builder()
.addPattern("images/login/username-field.png")
.build();
private final StateImage passwordField = new StateImage.Builder()
.addPattern("images/login/password-field.png")
.build();
private final StateImage submitButton = new StateImage.Builder()
.addPattern("images/login/submit-button.png")
.build();
private final StateImage successMessage = new StateImage.Builder()
.addPattern("images/login/success-message.png")
.build();
public ActionResult performLogin(String username, String password) {
return ConditionalActionChain.find(loginButton)
.ifFoundClick()
.ifNotFoundLog("Login button not visible")
.then(usernameField)
.ifFoundClick()
.ifFoundType(username)
.then(passwordField)
.ifFoundClick()
.ifFoundType(password)
.then(submitButton)
.ifFoundClick()
.then(successMessage)
.ifFoundLog("Login successful!")
.ifNotFoundLog("Login might have failed")
.perform(action, new ObjectCollection.Builder().build());
}
}
This example is fully compilable and demonstrates:
- Complete Spring component setup with @Component and @Autowired
- StateImage initialization with proper builders
- ConditionalActionChain usage with real variables
- Error handling and logging
- Production-ready code structure
Best Practicesโ
- Use then() for Sequential Actions: The then() method is essential for multi-step workflows
- No Explicit Waits: Use action configurations for timing, not wait() calls
- Leverage Convenience Methods: Use built-in methods like click() and type()
- Add Logging: Use ifFoundLog/ifNotFoundLog for debugging
- Handle Failures: Always provide ifNotFound alternatives
- Keep Chains Focused: Break complex workflows into smaller methods
- Think States, Not Processes: Focus on state transitions, not step-by-step procedures
Migration from Basic ConditionalActionChainโ
The original ConditionalActionChain was limited. Here's how to migrate:
// OLD - Limited ConditionalActionChain (no then() method!)
ConditionalActionChain.find(button)
.ifFound(click())
// Can't continue to next element without then()!
// NEW - ConditionalActionChain
ConditionalActionChain.find(button)
.ifFoundClick()
.then(nextElement) // Now you can continue!
.ifFoundClick()
.then(anotherElement) // And continue further!
.ifFoundType("text")
Related Documentationโ
Getting Startedโ
- Pure Actions Quick Start - Simpler alternatives for basic operations
- States in Brobot - Understanding StateImage and state management
Core ActionConfig Guidesโ
- ActionConfig Overview - ActionConfig architecture and concepts
- Action Chaining - ActionChainOptions for complex workflows
- Complex Workflows - Advanced workflow patterns
- Form Automation - Complete form examples
- Conditional Actions - Alternative conditional approaches
- Convenience Methods - Simpler one-line API