Pure Actions Quick Start Guide
What are Pure Actions?
Pure actions are a new approach in Brobot that separates finding elements from performing actions on them. This makes your automation code clearer, more testable, and more efficient.
The Old Way vs The New Way
Old Way (Embedded Find)
// Find and click happen together - you can't control them separately
action.click(buttonImage);
New Way (Pure Actions)
// Find first
ActionResult found = action.find(buttonImage);
// Then click if found
if (found.isSuccess()) {
action.perform(ActionType.CLICK, found.getFirstMatch());
}
Even Better Way (Conditional Chains)
// Elegant chaining with automatic conditional execution
ConditionalActionChain.find(findOptions)
.ifFound(click())
.ifNotFound(log("Button not found"))
.perform(action, objectCollection);
Your First Pure Action
Let's start with a simple example - clicking a button:
// Step 1: Import the required classes
import io.github.jspinak.brobot.action.*;
import io.github.jspinak.brobot.action.basic.click.ClickOptions;
import io.github.jspinak.brobot.action.basic.find.PatternFindOptions;
import io.github.jspinak.brobot.model.state.StateImage;
// Step 2: Create your target image
StateImage submitButton = new StateImage.Builder()
.withImage("submit-button.png")
.build();
// Step 3: Find and click using conditional chain
ActionResult result = ConditionalActionChain
.find(new PatternFindOptions.Builder().build())
.ifFound(new ClickOptions.Builder().build())
.perform(action, new ObjectCollection.Builder()
.withImages(submitButton)
.build());
Common Use Cases
1. Click a Button
// Simplest form - using convenience method
Location buttonLocation = new Location(100, 200);
action.perform(ActionType.CLICK, buttonLocation);
// Or with an image
ConditionalActionChain.find(findOptions)
.ifFound(click())
.perform(action, objectCollection);
2. Type in a Field
// Type at current cursor position
action.perform(ActionType.TYPE, "Hello World");
// Find field and type
ConditionalActionChain.find(textFieldImage)
.ifFound(click())
.then(type("user@example.com"))
.perform(action, objectCollection);
3. Highlight Found Elements
// Find all matching elements and highlight them
ActionResult matches = action.find(targetPattern);
for (Match match : matches.getMatchList()) {
action.perform(ActionType.HIGHLIGHT, match.getRegion());
}
4. Right-Click Menu
ConditionalActionChain.find(fileIcon)
.ifFound(rightClick())
.then(find(deleteOption))
.ifFound(click())
.perform(action, objectCollection);
Convenience Methods
The Action class now provides simple one-line methods for common operations:
// Click at a location
action.perform(ActionType.CLICK, new Location(100, 200));
// Highlight a region
action.perform(ActionType.HIGHLIGHT, new Region(50, 50, 200, 100));
// Type text
action.perform(ActionType.TYPE, "Hello World");
// Double-click
action.perform(ActionType.DOUBLE_CLICK, location);
// Right-click
action.perform(ActionType.RIGHT_CLICK, region);
Working with Results
Pure actions give you more control over results:
// Find returns matches you can work with
ActionResult findResult = action.find(targetImage);
if (findResult.isSuccess()) {
// Get the first match
Match firstMatch = findResult.getFirstMatch();
// Get all matches
List<Match> allMatches = findResult.getMatchList();
// Work with each match
for (Match match : allMatches) {
// Highlight each found instance
action.perform(ActionType.HIGHLIGHT, match.getRegion());
// Click each one
action.perform(ActionType.CLICK, match.getRegion());
}
}
Error Handling
Pure actions make error handling explicit and clear:
ConditionalActionChain.find(criticalButton)
.ifFound(click())
.ifNotFoundLog("ERROR: Critical button not found!")
.ifNotFoundDo(result -> {
// Custom error handling
takeScreenshot("error-state");
notifyUser("Application in unexpected state");
})
.perform(action, objectCollection);
Best Practices
1. Separate Find from Action
// Good: Clear separation
ActionResult found = action.find(target);
if (found.isSuccess()) {
action.perform(ActionType.CLICK, found.getFirstMatch());
}
// Better: Use conditional chains
ConditionalActionChain.find(target)
.ifFound(click())
.perform(action, objectCollection);
2. Handle Both Success and Failure
ConditionalActionChain.find(saveButton)
.ifFound(click())
.ifFoundLog("Document saved")
.ifNotFound(log("Save button not found"))
.ifNotFound(tryAlternativeSave())
.perform(action, objectCollection);
3. Reuse Find Results
// Find once, use multiple times
ActionResult buttons = action.find(allButtons);
for (Match button : buttons.getMatchList()) {
action.perform(ActionType.HIGHLIGHT, button.getRegion());
Thread.sleep(500);
action.perform(ActionType.CLICK, button.getRegion());
}
Migration Tips
If you're migrating from the old API:
- Start Small: Migrate one action at a time
- Both APIs Work Together: You can use old and new actions in the same project
- Look for Patterns: Similar code often uses similar migration patterns
- Use the Convenience Methods: They make migration easier
Example migration:
// Old code
action.click(submitButton);
// New code - Option 1 (explicit)
ActionResult found = action.find(submitButton);
if (found.isSuccess()) {
action.perform(ActionType.CLICK, found.getFirstMatch());
}
// New code - Option 2 (chain)
ConditionalActionChain.find(findOptions)
.ifFound(click())
.perform(action, new ObjectCollection.Builder()
.withImages(submitButton)
.build());
Next Steps
- Try the Examples: Start with simple click and type operations
- Explore Conditional Chains: Learn about ifFound, ifNotFound, and always
- Read the Full Documentation:
Getting Help
- Check the API documentation
- See examples for common patterns
- File issues on GitHub if you encounter problems
Remember: Pure actions make your automation code clearer and more maintainable. The separation of Find from Action might seem like extra work at first, but it pays off in better testing, debugging, and code reuse.