Code Cookbook
This comprehensive cookbook provides reusable code snippets, utility methods, and design patterns for common automation challenges. Each recipe is production-ready and follows Ranorex best practices.
📖 Quick Index
| Category | Recipes |
|---|---|
| Error Handling | Screenshots, Logging, Recovery patterns |
| Timing & Waits | Smart waits, Synchronization, Timeouts |
| Form Interactions | Data entry, Validation, Complex forms |
| Test Data | Generation, File processing, Data providers |
| Mobile Automation | Device control, App switching, Gestures |
Error Handling
Smart Screenshot Capture
Enhanced screenshot functionality with context and automatic failure handling.
using Ranorex;
using Ranorex.Core;
using System;
using System.Drawing;
using System.IO;
public static class ScreenshotHelper
{
private static string _screenshotDirectory = Path.Combine(Environment.CurrentDirectory, "Screenshots");
static ScreenshotHelper()
{
Directory.CreateDirectory(_screenshotDirectory);
}
/// <summary>
/// Takes a screenshot with context information and saves it to the report
/// </summary>
public static void CaptureOnFailure(string testStep, Exception exception = null)
{
try
{
// Capture desktop screenshot
var screenshot = Imaging.CaptureDesktop();
var timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
var filename = $"Failure_{testStep}_{timestamp}.png";
var fullPath = Path.Combine(_screenshotDirectory, filename);
// Save to file system
screenshot.Save(fullPath);
// Add to Ranorex report
var message = exception != null ?
$"Test step '{testStep}' failed: {exception.Message}" :
$"Test step '{testStep}' failed";
Report.Log(ReportLevel.Failure, message, screenshot);
// Also capture active window if possible
CaptureActiveWindow(testStep, timestamp);
}
catch (Exception ex)
{
Report.Error("Screenshot", $"Failed to capture screenshot: {ex.Message}");
}
}
/// <summary>
/// Captures only the active window for focused debugging
/// </summary>
public static void CaptureActiveWindow(string context, string timestamp = null)
{
try
{
timestamp = timestamp ?? DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
// Find the currently focused window
var activeWindow = Host.Local.Find<Form>("./form[@focused='True']").FirstOrDefault();
if (activeWindow != null)
{
var windowScreenshot = activeWindow.CaptureCompressedImage();
var filename = $"Window_{context}_{timestamp}.png";
Report.Info($"Active Window - {context}",
$"Captured active window: {activeWindow.Text}", windowScreenshot);
}
}
catch (Exception ex)
{
Report.Warn("Window Screenshot", $"Could not capture active window: {ex.Message}");
}
}
/// <summary>
/// Captures a specific element with highlighting
/// </summary>
public static void CaptureElement(Element element, string description)
{
try
{
// Highlight the element first
element.Highlight();
Delay.Milliseconds(500);
// Capture the element
var elementImage = element.CaptureCompressedImage();
Report.Info("Element Capture", description, elementImage);
}
catch (Exception ex)
{
Report.Warn("Element Screenshot", $"Could not capture element: {ex.Message}");
}
}
}
// Usage example with comprehensive error handling
public class RobustTestExample
{
public void Run()
{
Host.Initialize();
try
{
PerformLogin();
NavigateToFeature();
ValidateResults();
}
catch (RanorexException ex)
{
ScreenshotHelper.CaptureOnFailure("Ranorex Operation", ex);
throw;
}
catch (Exception ex)
{
ScreenshotHelper.CaptureOnFailure("General Error", ex);
throw;
}
finally
{
Host.Shutdown();
}
}
private void PerformLogin()
{
try
{
var repo = MyAppRepository.Instance;
repo.LoginDialog.UsernameField.PressKeys("testuser");
repo.LoginDialog.PasswordField.PressKeys("password");
repo.LoginDialog.LoginButton.Click();
}
catch (Exception ex)
{
ScreenshotHelper.CaptureOnFailure("Login Process", ex);
throw new ApplicationException("Login failed", ex);
}
}
}
Retry Mechanism with Exponential Backoff
using System;
using System.Threading;
public static class RetryHelper
{
public static T ExecuteWithRetry<T>(Func<T> operation, int maxAttempts = 3, int baseDelayMs = 1000)
{
Exception lastException = null;
for (int attempt = 1; attempt <= maxAttempts; attempt++)
{
try
{
return operation();
}
catch (Exception ex)
{
lastException = ex;
Report.Warn("Retry", $"Attempt {attempt} failed: {ex.Message}");
if (attempt < maxAttempts)
{
int delay = baseDelayMs * (int)Math.Pow(2, attempt - 1);
Thread.Sleep(delay);
}
}
}
throw new ApplicationException($"Operation failed after {maxAttempts} attempts", lastException);
}
public static void ExecuteWithRetry(Action operation, int maxAttempts = 3, int baseDelayMs = 1000)
{
ExecuteWithRetry(() => { operation(); return true; }, maxAttempts, baseDelayMs);
}
}
// Usage
var result = RetryHelper.ExecuteWithRetry(() =>
{
var element = Host.Local.FindSingle<Button>("/form/button[@text='Submit']");
element.Click();
return element.Text;
}, maxAttempts: 3, baseDelayMs: 500);
Timing and Waits
Smart Wait Utilities
Advanced waiting strategies that adapt to application behavior.
using Ranorex;
using System;
using System.Threading;
public static class WaitHelper
{
/// <summary>
/// Waits for an element to appear with custom condition
/// </summary>
public static bool WaitForElement(string xpath, Func<Element, bool> condition = null,
TimeSpan? timeout = null, string description = "element")
{
timeout = timeout ?? TimeSpan.FromSeconds(10);
var endTime = DateTime.Now.Add(timeout.Value);
while (DateTime.Now < endTime)
{
if (Host.Local.TryFindSingle(xpath, out Element element))
{
if (condition == null || condition(element))
{
Report.Success("Wait", $"Found {description} successfully");
return true;
}
}
Thread.Sleep(250);
}
Report.Failure("Wait", $"Timeout waiting for {description} after {timeout}");
return false;
}
/// <summary>
/// Waits for an element to disappear (useful for loading indicators)
/// </summary>
public static bool WaitForElementToDisappear(string xpath, TimeSpan? timeout = null,
string description = "element")
{
timeout = timeout ?? TimeSpan.FromSeconds(30);
var endTime = DateTime.Now.Add(timeout.Value);
// First, wait a bit for the element to potentially appear
Thread.Sleep(500);
while (DateTime.Now < endTime)
{
if (!Host.Local.TryFindSingle(xpath, out Element element) || !element.Exists)
{
Report.Success("Wait", $"{description} disappeared as expected");
return true;
}
Thread.Sleep(250);
}
Report.Failure("Wait", $"Timeout: {description} did not disappear after {timeout}");
return false;
}
/// <summary>
/// Waits for a complex condition with polling
/// </summary>
public static bool WaitForCondition(Func<bool> condition, string description,
TimeSpan? timeout = null, int pollIntervalMs = 500)
{
timeout = timeout ?? TimeSpan.FromSeconds(15);
var endTime = DateTime.Now.Add(timeout.Value);
while (DateTime.Now < endTime)
{
try
{
if (condition())
{
Report.Success("Wait", $"Condition met: {description}");
return true;
}
}
catch (Exception ex)
{
Report.Debug("Wait", $"Condition check failed: {ex.Message}");
}
Thread.Sleep(pollIntervalMs);
}
Report.Failure("Wait", $"Timeout waiting for condition: {description}");
return false;
}
/// <summary>
/// Waits for page to load (web applications)
/// </summary>
public static bool WaitForPageLoad(string domain, TimeSpan? timeout = null)
{
return WaitForCondition(() =>
{
var webDoc = Host.Local.FindSingle<WebDocument>($"/dom[@domain='{domain}']");
return webDoc.PageUrl != "about:blank" &&
!webDoc.PageUrl.Contains("loading") &&
webDoc.ReadyState == "complete";
}, $"page load for domain {domain}", timeout);
}
}
// Usage examples
public class WaitExamples
{
public void DemonstrateWaits()
{
// Wait for element to be enabled
WaitHelper.WaitForElement("/form/button[@text='Submit']",
element => element.Enabled,
TimeSpan.FromSeconds(5),
"Submit button to be enabled");
// Wait for loading spinner to disappear
WaitHelper.WaitForElementToDisappear("/form//*[@class='loading-spinner']",
TimeSpan.FromSeconds(30),
"loading spinner");
// Wait for complex business condition
WaitHelper.WaitForCondition(() =>
{
var statusLabel = Host.Local.FindSingle<Text>("/form/text[@name='status']");
return statusLabel.Text == "Processing Complete";
}, "processing to complete");
// Wait for web page to load
WaitHelper.WaitForPageLoad("example.com");
}
}
Form Interactions
Advanced Form Automation
Comprehensive form handling with validation and error recovery.
using Ranorex;
using System;
using System.Collections.Generic;
using System.Linq;
public class FormAutomationHelper
{
/// <summary>
/// Fills a form using a dictionary of field names and values
/// </summary>
public static void FillForm(Dictionary<string, string> formData, string formXPath = null)
{
foreach (var field in formData)
{
FillField(field.Key, field.Value, formXPath);
}
}
/// <summary>
/// Fills a single form field with intelligent field detection
/// </summary>
public static void FillField(string fieldName, string value, string formXPath = null)
{
try
{
string baseXPath = formXPath ?? "//";
Element field = null;
// Try different field identification strategies
string[] patterns = {
$"{baseXPath}*[@name='{fieldName}']",
$"{baseXPath}*[@id='{fieldName}']",
$"{baseXPath}*[@automationid='{fieldName}']",
$"{baseXPath}text[@name='{fieldName}']",
$"{baseXPath}input[@name='{fieldName}']",
$"{baseXPath}*[contains(@placeholder, '{fieldName}')]",
$"{baseXPath}*[preceding-sibling::*[contains(text(), '{fieldName}')]]"
};
foreach (var pattern in patterns)
{
if (Host.Local.TryFindSingle(pattern, out field))
{
break;
}
}
if (field == null)
{
throw new ElementNotFoundException($"Could not find field: {fieldName}");
}
// Handle different field types
HandleFieldInput(field, value, fieldName);
}
catch (Exception ex)
{
Report.Failure("Form Fill", $"Failed to fill field '{fieldName}': {ex.Message}");
ScreenshotHelper.CaptureOnFailure($"FillField_{fieldName}", ex);
throw;
}
}
private static void HandleFieldInput(Element field, string value, string fieldName)
{
// Ensure field is visible and enabled
if (!field.Visible)
{
throw new InvalidOperationException($"Field '{fieldName}' is not visible");
}
if (field is Text textField)
{
// Regular text input
textField.Click();
textField.PressKeys("{Ctrl}a"); // Select all existing text
textField.PressKeys(value);
Report.Info("Form", $"Filled text field '{fieldName}' with: {value}");
}
else if (field is ComboBox comboBox)
{
// Dropdown selection
comboBox.Click();
Delay.Milliseconds(300);
comboBox.PressKeys(value);
comboBox.PressKeys("{Return}");
Report.Info("Form", $"Selected '{value}' in dropdown '{fieldName}'");
}
else if (field is CheckBox checkBox)
{
// Checkbox handling
bool shouldCheck = value.ToLower() == "true" || value == "1" || value.ToLower() == "yes";
if (checkBox.Checked != shouldCheck)
{
checkBox.Click();
}
Report.Info("Form", $"Set checkbox '{fieldName}' to: {shouldCheck}");
}
else if (field is RadioButton radioButton)
{
// Radio button
if (value.ToLower() == "true" || value == "1")
{
radioButton.Click();
Report.Info("Form", $"Selected radio button '{fieldName}'");
}
}
else
{
// Generic element - try clicking and typing
field.Click();
field.PressKeys("{Ctrl}a");
field.PressKeys(value);
Report.Info("Form", $"Filled generic field '{fieldName}' with: {value}");
}
// Brief pause to allow field processing
Delay.Milliseconds(200);
}
/// <summary>
/// Validates form field values
/// </summary>
public static Dictionary<string, string> ValidateForm(Dictionary<string, string> expectedValues,
string formXPath = null)
{
var validationResults = new Dictionary<string, string>();
foreach (var expected in expectedValues)
{
try
{
string actualValue = GetFieldValue(expected.Key, formXPath);
if (actualValue == expected.Value)
{
validationResults[expected.Key] = "PASS";
Report.Success("Validation", $"Field '{expected.Key}' has correct value: {actualValue}");
}
else
{
validationResults[expected.Key] = $"FAIL: Expected '{expected.Value}', got '{actualValue}'";
Report.Failure("Validation", validationResults[expected.Key]);
}
}
catch (Exception ex)
{
validationResults[expected.Key] = $"ERROR: {ex.Message}";
Report.Error("Validation", $"Failed to validate field '{expected.Key}': {ex.Message}");
}
}
return validationResults;
}
private static string GetFieldValue(string fieldName, string formXPath)
{
string baseXPath = formXPath ?? "//";
string[] patterns = {
$"{baseXPath}*[@name='{fieldName}']",
$"{baseXPath}*[@id='{fieldName}']",
$"{baseXPath}text[@name='{fieldName}']",
$"{baseXPath}input[@name='{fieldName}']"
};
foreach (var pattern in patterns)
{
if (Host.Local.TryFindSingle(pattern, out Element field))
{
if (field is Text textField)
return textField.Text;
else if (field is ComboBox comboBox)
return comboBox.Text;
else if (field is CheckBox checkBox)
return checkBox.Checked.ToString();
else
return field.GetAttributeValueOrDefault("Value", field.GetAttributeValueOrDefault("Text", ""));
}
}
throw new ElementNotFoundException($"Could not find field: {fieldName}");
}
}
// Usage example
public class FormExampleUsage
{
public void FillRegistrationForm()
{
var formData = new Dictionary<string, string>
{
["firstName"] = "John",
["lastName"] = "Doe",
["email"] = "john.doe@example.com",
["country"] = "United States",
["agreeToTerms"] = "true",
["newsletter"] = "false"
};
// Fill the form
FormAutomationHelper.FillForm(formData, "/form[@title='Registration']");
// Submit the form
var submitButton = Host.Local.FindSingle<Button>("/form[@title='Registration']/button[@text='Submit']");
submitButton.Click();
// Wait for confirmation
WaitHelper.WaitForElement("//*[contains(text(), 'Registration successful')]",
timeout: TimeSpan.FromSeconds(10), description: "registration confirmation");
// Validate the form was submitted correctly (if form stays visible)
var validationResults = FormAutomationHelper.ValidateForm(formData, "/form[@title='Registration']");
foreach (var result in validationResults.Where(r => r.Value != "PASS"))
{
Report.Warn("Form Validation", $"{result.Key}: {result.Value}");
}
}
}
Test Data
Advanced Data Generation
Comprehensive test data generation for realistic testing scenarios.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public static class TestDataGenerator
{
private static readonly Random _random = new Random();
private static readonly string[] FirstNames = {
"James", "Mary", "John", "Patricia", "Robert", "Jennifer", "Michael", "Linda",
"William", "Elizabeth", "David", "Barbara", "Richard", "Susan", "Joseph", "Jessica"
};
private static readonly string[] LastNames = {
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
"Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez", "Wilson", "Anderson", "Thomas"
};
private static readonly string[] EmailDomains = {
"gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "example.com", "test.com"
};
private static readonly string[] Countries = {
"United States", "Canada", "United Kingdom", "Germany", "France", "Australia", "Japan", "Brazil"
};
public static string RandomString(int length, bool includeNumbers = true, bool includeSpecialChars = false)
{
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
if (includeNumbers)
chars += "0123456789";
if (includeSpecialChars)
chars += "!@#$%^&*()_+-=[]{}|;:,.<>?";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[_random.Next(s.Length)]).ToArray());
}
public static string RandomFirstName() => FirstNames[_random.Next(FirstNames.Length)];
public static string RandomLastName() => LastNames[_random.Next(LastNames.Length)];
public static string RandomCountry() => Countries[_random.Next(Countries.Length)];
public static string RandomEmail(string firstName = null, string lastName = null)
{
firstName = firstName ?? RandomFirstName();
lastName = lastName ?? RandomLastName();
var domain = EmailDomains[_random.Next(EmailDomains.Length)];
var separator = _random.Next(2) == 0 ? "." : "_";
var number = _random.Next(2) == 0 ? _random.Next(1, 999).ToString() : "";
return $"{firstName.ToLower()}{separator}{lastName.ToLower()}{number}@{domain}";
}
public static string RandomPhoneNumber(string format = "###-###-####")
{
var result = format;
foreach (char c in result.ToCharArray())
{
if (c == '#')
{
result = result.ReplaceFirst("#", _random.Next(0, 10).ToString());
}
}
return result;
}
public static DateTime RandomDateBetween(DateTime start, DateTime end)
{
var range = end - start;
var randomDays = _random.Next((int)range.TotalDays);
return start.AddDays(randomDays);
}
public static DateTime RandomBirthdate(int minAge = 18, int maxAge = 80)
{
var today = DateTime.Today;
var start = today.AddYears(-maxAge);
var end = today.AddYears(-minAge);
return RandomDateBetween(start, end);
}
// Extension method helper
private static string ReplaceFirst(this string text, string search, string replace)
{
int pos = text.IndexOf(search);
if (pos < 0) return text;
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
}
}
// Complete user data generation
public class TestUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Country { get; set; }
public DateTime BirthDate { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public static TestUser GenerateRandomUser()
{
var firstName = TestDataGenerator.RandomFirstName();
var lastName = TestDataGenerator.RandomLastName();
return new TestUser
{
FirstName = firstName,
LastName = lastName,
Email = TestDataGenerator.RandomEmail(firstName, lastName),
Phone = TestDataGenerator.RandomPhoneNumber(),
Country = TestDataGenerator.RandomCountry(),
BirthDate = TestDataGenerator.RandomBirthdate(),
Username = $"{firstName.ToLower()}{lastName.ToLower()}{TestDataGenerator.RandomString(3, true, false)}",
Password = TestDataGenerator.RandomString(12, true, true)
};
}
}
File Processing Utilities
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
public static class FileDataProvider
{
/// <summary>
/// Reads CSV data with header support
/// </summary>
public static List<Dictionary<string, string>> ReadCsv(string filePath, bool hasHeaders = true)
{
var result = new List<Dictionary<string, string>>();
var lines = File.ReadAllLines(filePath);
if (lines.Length == 0) return result;
string[] headers = null;
int startIndex = 0;
if (hasHeaders)
{
headers = ParseCsvLine(lines[0]);
startIndex = 1;
}
else
{
// Generate column names: Column1, Column2, etc.
var firstLine = ParseCsvLine(lines[0]);
headers = firstLine.Select((v, i) => $"Column{i + 1}").ToArray();
}
for (int i = startIndex; i < lines.Length; i++)
{
var values = ParseCsvLine(lines[i]);
var row = new Dictionary<string, string>();
for (int j = 0; j < headers.Length && j < values.Length; j++)
{
row[headers[j]] = values[j];
}
result.Add(row);
}
return result;
}
private static string[] ParseCsvLine(string line)
{
var result = new List<string>();
var current = new StringBuilder();
bool inQuotes = false;
for (int i = 0; i < line.Length; i++)
{
char c = line[i];
if (c == '"')
{
inQuotes = !inQuotes;
}
else if (c == ',' && !inQuotes)
{
result.Add(current.ToString().Trim());
current.Clear();
}
else
{
current.Append(c);
}
}
result.Add(current.ToString().Trim());
return result.ToArray();
}
/// <summary>
/// Reads JSON test data
/// </summary>
public static T ReadJson<T>(string filePath)
{
var jsonContent = File.ReadAllText(filePath);
return JsonSerializer.Deserialize<T>(jsonContent);
}
/// <summary>
/// Writes test results to JSON
/// </summary>
public static void WriteJson<T>(string filePath, T data)
{
var options = new JsonSerializerOptions { WriteIndented = true };
var jsonContent = JsonSerializer.Serialize(data, options);
File.WriteAllText(filePath, jsonContent);
}
}
Browser & Web
JavaScript Execution and Web Utilities
using Ranorex;
using Ranorex.Core;
using System;
using System.Collections.Generic;
public static class WebAutomationHelper
{
/// <summary>
/// Executes JavaScript and returns the result
/// </summary>
public static T ExecuteScript<T>(string script, string domain = null)
{
try
{
var webDoc = domain != null ?
Host.Local.FindSingle<WebDocument>($"/dom[@domain='{domain}']") :
Host.Local.FindSingle<WebDocument>("/dom");
var result = webDoc.ExecuteScript(script);
if (result == null || result is DBNull)
{
return default(T);
}
if (result is T)
return (T)result;
else
return (T)Convert.ChangeType(result, typeof(T));
}
catch (Exception ex)
{
Report.Error("JavaScript", $"Failed to execute script: {ex.Message}");
throw;
}
}
/// <summary>
/// Waits for jQuery to complete (if jQuery is available)
/// </summary>
public static bool WaitForJQuery(TimeSpan? timeout = null)
{
timeout = timeout ?? TimeSpan.FromSeconds(10);
return WaitHelper.WaitForCondition(() =>
{
try
{
var jqueryActive = ExecuteScript<long>("return jQuery.active || 0");
return jqueryActive == 0;
}
catch
{
return true; // jQuery not available, assume ready
}
}, "jQuery to complete", timeout);
}
/// <summary>
/// Scrolls an element into view
/// </summary>
public static void ScrollIntoView(Element element)
{
try
{
var webElement = element as WebElement;
if (webElement != null)
{
var script = "arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});";
webElement.WebDocument.ExecuteScript(script, webElement);
}
else
{
// For desktop elements, use MoveTo to bring into view
element.MoveTo();
}
}
catch (Exception ex)
{
Report.Warn("Scroll", $"Could not scroll element into view: {ex.Message}");
}
}
/// <summary>
/// Manages browser cookies
/// </summary>
public static void SetCookie(string name, string value, string domain = null)
{
var script = domain != null ?
$"document.cookie = '{name}={value}; domain={domain}; path=/'" :
$"document.cookie = '{name}={value}; path=/'";
ExecuteScript<object>(script);
Report.Info("Cookie", $"Set cookie: {name}={value}");
}
public static string GetCookie(string name)
{
var script = $@"
var nameEQ = '{name}=';
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {{
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}}
return null;";
return ExecuteScript<string>(script);
}
/// <summary>
/// Handles file uploads
/// </summary>
public static void UploadFile(Element fileInput, string filePath)
{
try
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"Upload file not found: {filePath}");
}
fileInput.Click();
fileInput.PressKeys(filePath);
Report.Info("File Upload", $"Uploaded file: {filePath}");
}
catch (Exception ex)
{
Report.Failure("File Upload", $"Failed to upload file: {ex.Message}");
throw;
}
}
}
Mobile Automation
Mobile Device Control and Gestures
using Ranorex;
using Ranorex.Core.Remoting;
using System;
public static class MobileHelper
{
/// <summary>
/// Switches between mobile applications
/// </summary>
public static void SwitchToApp(string packageName, bool clearAppData = false)
{
try
{
if (clearAppData)
{
Mobile.ClearAppData(packageName);
}
Mobile.RunApplication(packageName, true);
Report.Info("Mobile", $"Switched to app: {packageName}");
// Wait for the app's main window to appear
WaitHelper.WaitForCondition(() =>
{
var app = Host.Local.FindSingle<MobileApp>($"/mobileapp[@packagename='{packageName}']");
return app.Visible;
}, $"app '{packageName}' to load", TimeSpan.FromSeconds(20));
}
catch (Exception ex)
{
Report.Failure("Mobile", $"Failed to switch to app {packageName}: {ex.Message}");
throw;
}
}
/// <summary>
/// Performs swipe gestures
/// </summary>
public static void SwipeElement(Element element, SwipeDirection direction, int distance = 200)
{
try
{
var bounds = element.Bounds;
var centerX = bounds.X + bounds.Width / 2;
var centerY = bounds.Y + bounds.Height / 2;
Point startPoint, endPoint;
switch (direction)
{
case SwipeDirection.Up:
startPoint = new Point(centerX, centerY + distance / 2);
endPoint = new Point(centerX, centerY - distance / 2);
break;
case SwipeDirection.Down:
startPoint = new Point(centerX, centerY - distance / 2);
endPoint = new Point(centerX, centerY + distance / 2);
break;
case SwipeDirection.Left:
startPoint = new Point(centerX + distance / 2, centerY);
endPoint = new Point(centerX - distance / 2, centerY);
break;
case SwipeDirection.Right:
startPoint = new Point(centerX - distance / 2, centerY);
endPoint = new Point(centerX + distance / 2, centerY);
break;
default:
throw new ArgumentException($"Invalid swipe direction: {direction}");
}
Touch.StartTouch(startPoint);
Touch.MoveTouch(endPoint);
Touch.EndTouch();
Report.Info("Mobile", $"Swiped {direction} on element");
}
catch (Exception ex)
{
Report.Failure("Mobile", $"Failed to swipe: {ex.Message}");
throw;
}
}
/// <summary>
/// Handles device orientation
/// </summary>
public static void SetOrientation(DeviceOrientation orientation)
{
try
{
Mobile.Device.Orientation = orientation;
// Wait for the orientation to change
WaitHelper.WaitForCondition(() => Mobile.Device.Orientation == orientation,
$"orientation to be {orientation}", TimeSpan.FromSeconds(10));
Report.Info("Mobile", $"Set device orientation to: {orientation}");
}
catch (Exception ex)
{
Report.Failure("Mobile", $"Failed to set orientation: {ex.Message}");
throw;
}
}
/// <summary>
/// Simulates device back button
/// </summary>
public static void PressBackButton()
{
try
{
Mobile.PressHardwareButton(HardwareButton.Back);
Report.Info("Mobile", "Pressed back button");
}
catch (Exception ex)
{
Report.Failure("Mobile", $"Failed to press back button: {ex.Message}");
throw;
}
}
}
public enum SwipeDirection
{
Up,
Down,
Left,
Right
}
Utilities & Helpers
Common Automation Patterns
using Ranorex;
using System;
using System.Collections.Generic;
using System.Linq;
public static class AutomationUtilities
{
/// <summary>
/// Finds and clicks an element with retry logic
/// </summary>
public static bool ClickElementSafely(string xpath, TimeSpan? timeout = null, string description = null)
{
description = description ?? $"element at {xpath}";
timeout = timeout ?? TimeSpan.FromSeconds(10);
return RetryHelper.ExecuteWithRetry(() =>
{
var element = Host.Local.FindSingle<Element>(xpath, (int)timeout.Value.TotalMilliseconds);
if (!element.Visible)
{
throw new InvalidOperationException($"{description} is not visible");
}
if (!element.Enabled)
{
throw new InvalidOperationException($"{description} is not enabled");
}
element.Click();
Report.Info("Click", $"Successfully clicked {description}");
return true;
}, maxAttempts: 3);
}
/// <summary>
/// Gets all text content from a container element
/// </summary>
public static List<string> ExtractAllText(Element container)
{
var textElements = container.Find<Text>(".//*");
return textElements.Select(t => t.Text?.Trim())
.Where(text => !string.IsNullOrWhiteSpace(text))
.ToList();
}
/// <summary>
/// Validates element properties
/// </summary>
public static bool ValidateElementProperties(Element element, Dictionary<string, object> expectedProperties)
{
bool allValid = true;
foreach (var property in expectedProperties)
{
try
{
var actualValue = element.GetAttributeValue(property.Key);
if (!actualValue.Equals(property.Value))
{
Report.Failure("Validation",
$"Property '{property.Key}': Expected '{property.Value}', got '{actualValue}'");
allValid = false;
}
else
{
Report.Success("Validation",
$"Property '{property.Key}' has correct value: {actualValue}");
}
}
catch (Exception ex)
{
Report.Error("Validation", $"Failed to get property '{property.Key}': {ex.Message}");
allValid = false;
}
}
return allValid;
}
/// <summary>
/// Performs bulk operations on element collections
/// </summary>
public static void ProcessElementCollection<T>(IList<T> elements, Action<T, int> action) where T : Element
{
for (int i = 0; i < elements.Count; i++)
{
try
{
action(elements[i], i);
}
catch (Exception ex)
{
Report.Warn("Collection Processing",
$"Failed to process element {i}: {ex.Message}");
}
}
}
}
/// <summary>
/// Application state management
/// </summary>
public static class ApplicationState
{
private static readonly Dictionary<string, object> _state = new Dictionary<string, object>();
public static void Set(string key, object value)
{
_state[key] = value;
Report.Debug("State", $"Set {key} = {value}");
}
public static T Get<T>(string key, T defaultValue = default(T))
{
if (_state.TryGetValue(key, out object value))
{
return (T)value;
}
return defaultValue;
}
public static void Clear()
{
_state.Clear();
Report.Debug("State", "Cleared application state");
}
}
Next Steps
This cookbook provides the foundation for building robust automation solutions. For more advanced scenarios:
- Advanced Integration - Database integration, API testing, CI/CD patterns
- Desktop Patterns - Windows-specific automation techniques
- Web Patterns - Modern web application automation
- Mobile Patterns - iOS and Android automation patterns
Contributing
Have a useful pattern or utility to share? Follow these guidelines:
- Document thoroughly - Include purpose, parameters, and usage examples
- Handle errors gracefully - Use try-catch and provide meaningful error messages
- Follow naming conventions - Use descriptive method and variable names
- Test comprehensively - Ensure your code works in different scenarios
These patterns form the building blocks for scalable, maintainable automation frameworks. Use them as starting points and adapt them to your specific needs!