Table of Contents

Mobile Automation Patterns

This comprehensive guide covers mobile application automation using Ranorex, including native apps, hybrid applications, mobile web, and cross-platform scenarios for both iOS and Android.

📱 Mobile Platform Coverage

Platform Description Key Features
Android Native Native Android applications Activities, Views, Intents
iOS Native Native iOS applications ViewControllers, UI elements
Hybrid Apps Cordova, PhoneGap, Ionic WebView + native components
Mobile Web Browser-based mobile apps Responsive design, touch events
Cross-Platform React Native, Xamarin, Flutter Shared codebase automation

Android Native

Basic Android App Automation

using Ranorex;
using Ranorex.Plugin.Mobile;
using Ranorex.Core.Remoting;
using System;
using System.Collections.Generic;
using System.Linq;

public class AndroidNativeAutomation
{
    private AndroidApp _currentApp;
    private string _appPackageName;
    private IRemoteEndpoint _endpoint;
    
    public void AutomateAndroidApp(string appPackageName)
    {
        try
        {
            _appPackageName = appPackageName;
            
            // Connect to Android device and application
            ConnectToAndroidApp();
            
            // Perform Android-specific interactions
            PerformAndroidInteractions();
            
            // Handle Android system features
            HandleAndroidFeatures();
            
            Report.Success("Android Automation", "Successfully automated Android application");
        }
        catch (Exception ex)
        {
            Report.Failure("Android Automation", $"Failed: {ex.Message}");
            throw;
        }
        finally
        {
            CleanupAndroidApp();
        }
    }
    
    private void ConnectToAndroidApp()
    {
        try
        {
            // Discover Android devices
            var discoveredDevices = DiscoverAndroidDevices();
            
            if (discoveredDevices == null || !discoveredDevices.Any())
            {
                throw new Exception("No Android devices found");
            }
            
            // Connect to first available device
            var deviceInfo = discoveredDevices.First();
            _endpoint = ConnectToDevice(deviceInfo);
            
            // Start the application
            _endpoint.StartApplication(_appPackageName, true);
            
            // Connect to the Android app using mobile app path
            string appPath = $"/mobileapp[@title='{_appPackageName}']";
            _currentApp = AndroidApp.FromPath(appPath);
            
            if (_currentApp != null && _currentApp.Element.Valid)
            {
                var deviceInfo = _currentApp.GetDeviceInfo();
                Report.Info($"Connected to: {deviceInfo.Manufacturer} {deviceInfo.Brand} running Android {deviceInfo.AndroidVersionName}");
                Report.Success("App Connection", $"Successfully connected to Android app: {_appPackageName}");
            }
            else
            {
                throw new Exception($"Could not connect to Android application: {_appPackageName}");
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Failed to connect to Android app: {ex.Message}");
        }
    }
    
    private IList<IRemoteEndpointInformation> DiscoverAndroidDevices()
    {
        try
        {
            var endpointService = RemoteServiceLocator.Service;
            var discoveryFactory = endpointService.Resolve<IDeviceDiscoveryServiceFactory>();
            var discoveryService = discoveryFactory.Create(RemotePlatform.Android, RemoteConnectionType.USB);
            
            return discoveryService.Discover(new Duration(15000));
        }
        catch (Exception ex)
        {
            Report.Warn($"Device discovery failed: {ex.Message}");
            return null;
        }
    }
    
    private IRemoteEndpoint ConnectToDevice(IRemoteEndpointInformation deviceInfo)
    {
        var endpointService = RemoteServiceLocator.Service;
        var deviceSerial = deviceInfo.DisplayName;
        
        // Clean device name if needed
        int parenthesisIndex = deviceSerial.IndexOf(" (", StringComparison.Ordinal);
        if (parenthesisIndex > 0) 
        {
            deviceSerial = deviceSerial.Substring(0, parenthesisIndex).Trim();
        }
        
        var endpoint = endpointService.AddDevice($"AndroidDevice-{deviceSerial}", 
            RemotePlatform.Android, RemoteConnectionType.USB, deviceSerial);
            
        // Wait for device connection
        int maxWaitChecks = 10;
        for (int waitCheck = 0; waitCheck < maxWaitChecks; waitCheck++)
        {
            System.Threading.Thread.Sleep(1000);
            
            if (endpoint.Status == ChannelState.DeviceConnected || endpoint.Status == ChannelState.Connected)
            {
                return endpoint;
            }
            else if (endpoint.Status == ChannelState.Error)
            {
                throw new Exception("Failed to connect to Android device");
            }
        }
        
        throw new Exception("Timeout waiting for Android device connection");
    }
    
    private void PerformAndroidInteractions()
    {
        // Handle text input
        HandleAndroidTextInput();
        
        // Handle button interactions
        HandleAndroidButtons();
        
        // Handle list interactions with swipe gestures
        HandleAndroidListInteractions();
        
        // Handle navigation
        HandleAndroidNavigation();
    }
    
    private void HandleAndroidTextInput()
    {
        try
        {
            // Find Android text fields using mobile-specific selectors
            var textFields = _currentApp.Find<AndroidElement>("//androidelement[@class='android.widget.EditText']");
            
            foreach (var textField in textFields.Take(2)) // Take first 2 text fields
            {
                if (textField.Valid && textField.Visible)
                {
                    textField.Click();
                    Delay.Milliseconds(500);
                    
                    // Clear existing text and enter new text
                    textField.PressKeys("{Control down}a{Control up}");
                    textField.PressKeys("test input");
                    
                    Report.Info("Text Input", "Successfully filled Android text field");
                }
            }
            
            // Hide virtual keyboard by pressing back
            _currentApp.PressBackKey();
        }
        catch (Exception ex)
        {
            Report.Debug("Text Input", $"Text input handling: {ex.Message}");
        }
    }
    
    private void HandleAndroidButtons()
    {
        try
        {
            // Find buttons by content description
            var buttons = _currentApp.Find<AndroidElement>("//androidelement[@class='android.widget.Button']");
            
            if (buttons.Any())
            {
                var firstButton = buttons.First();
                if (firstButton.Valid && firstButton.Visible)
                {
                    firstButton.Click();
                    Delay.Milliseconds(1000);
                    Report.Success("Button Interaction", "Successfully clicked Android button");
                }
            }
            
            // Try to find buttons by content description
            var menuButton = _currentApp.FindSingle<AndroidElement>("//androidelement[@contentdescription='Menu']", new Duration(3000));
            if (menuButton.Valid)
            {
                menuButton.Click();
                Report.Success("Menu Button", "Successfully clicked menu button");
                Delay.Milliseconds(1000);
                
                // Press back to close menu
                _currentApp.PressBackKey();
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Button Interaction", $"Button handling: {ex.Message}");
        }
    }
    
    private void HandleAndroidListInteractions()
    {
        try
        {
            // Find scrollable list elements
            var scrollableElements = _currentApp.Find<AndroidElement>(
                "//androidelement[@contentdescription='Scrollview manages views in given screen size' or @class='androidx.recyclerview.widget.RecyclerView' or @class='android.widget.ListView']");
            
            foreach (var listElement in scrollableElements)
            {
                if (listElement.Valid && listElement.Visible)
                {
                    Report.Info("List Interaction", "Found scrollable list element");
                    
                    // Perform swipe gestures using mobile-specific API
                    listElement.Swipe(Ranorex.Core.Recorder.Touch.GestureDirection.Up, new Distance(200));
                    Delay.Milliseconds(500);
                    
                    listElement.Swipe(Ranorex.Core.Recorder.Touch.GestureDirection.Down, new Distance(200));
                    Delay.Milliseconds(500);
                    
                    Report.Success("List Interaction", "Successfully performed swipe gestures on list");
                    break; // Only interact with first found list
                }
            }
        }
        catch (Exception ex)
        {
            Report.Debug("List Interaction", $"List interaction: {ex.Message}");
        }
    }
    
    private void HandleAndroidNavigation()
    {
        try
        {
            // Use Android hardware back key
            _currentApp.PressBackKey();
            Report.Success("Navigation", "Used Android hardware back key");
            
            Delay.Milliseconds(1000);
            
            // Find and use navigation drawer if available
            var drawerToggle = _currentApp.FindSingle<AndroidElement>(
                "//androidelement[@contentdescription='Navigate up' or @contentdescription='Open navigation drawer']", 
                new Duration(3000));
            
            if (drawerToggle.Valid)
            {
                drawerToggle.Click();
                Delay.Milliseconds(1000);
                
                // Check if drawer opened
                var drawerMenu = _currentApp.FindSingle<AndroidElement>("//androidelement[@rid='drawerMenu']", new Duration(3000));
                if (drawerMenu.Valid && drawerMenu.Visible)
                {
                    Report.Success("Navigation Drawer", "Successfully opened navigation drawer");
                    
                    // Close drawer by pressing back
                    _currentApp.PressBackKey();
                }
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Navigation", $"Navigation handling: {ex.Message}");
        }
    }
    
    private void HandleAndroidFeatures()
    {
        // Handle Android-specific permissions
        HandleAndroidPermissions();
        
        // Handle Android notifications
        HandleAndroidNotifications();
        
        // Handle Android intents and system dialogs
        HandleAndroidSystemDialogs();
    }
    
    private void HandleAndroidPermissions()
    {
        try
        {
            // Look for common permission dialogs
            var allowButton = _currentApp.FindSingle<AndroidElement>(
                "//androidelement[@text='Allow' or @text='ALLOW' or @text='Grant']", new Duration(3000));
            
            if (allowButton.Valid && allowButton.Visible)
            {
                allowButton.Click();
                Report.Success("Permissions", "Granted Android app permission");
                Delay.Milliseconds(1000);
            }
            
            var denyButton = _currentApp.FindSingle<AndroidElement>(
                "//androidelement[@text='Deny' or @text='DENY']", new Duration(2000));
            
            if (denyButton.Valid && denyButton.Visible)
            {
                Report.Info("Permissions", "Permission denial option available");
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Permissions", $"No permission dialogs found: {ex.Message}");
        }
    }
    
    private void HandleAndroidNotifications()
    {
        try
        {
            // Try to access notification panel (this would typically require system-level access)
            Report.Info("Notifications", "Android notification handling would require system-level automation");
            
            // Look for in-app notifications
            var notifications = _currentApp.Find<AndroidElement>("//androidelement[contains(@text,'notification') or contains(@contentdescription,'notification')]");
            
            if (notifications.Any())
            {
                var firstNotification = notifications.First();
                if (firstNotification.Valid && firstNotification.Visible)
                {
                    firstNotification.Click();
                    Report.Success("Notifications", "Interacted with in-app notification");
                }
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Notifications", $"Notification handling: {ex.Message}");
        }
    }
    
    private void HandleAndroidSystemDialogs()
    {
        try
        {
            // Handle system dialogs like "App not responding"
            var dialogButtons = _currentApp.Find<AndroidElement>("//androidelement[@class='android.widget.Button' and (@text='OK' or @text='Cancel' or @text='Wait')]");
            
            foreach (var button in dialogButtons)
            {
                if (button.Valid && button.Visible)
                {
                    var buttonText = button.GetAttributeValueText("text");
                    Report.Info("System Dialog", $"Found system dialog button: {buttonText}");
                    
                    // Handle as appropriate for your test scenario
                    if (buttonText == "OK" || buttonText == "Wait")
                    {
                        button.Click();
                        Report.Info("System Dialog", $"Clicked {buttonText} button");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Report.Debug("System Dialogs", $"System dialog handling: {ex.Message}");
        }
    }
    
    private void CleanupAndroidApp()
    {
        try
        {
            if (_currentApp != null)
            {
                _currentApp.CloseApplication();
                Report.Info("Cleanup", "Android application closed");
            }
            
            if (_endpoint != null)
            {
                _endpoint.DisconnectAndDisable();
                Report.Info("Cleanup", "Android device endpoint disconnected");
            }
        }
        catch (Exception ex)
        {
            Report.Warn("Cleanup", $"Cleanup issues: {ex.Message}");
        }
    }
}

📱 App Instrumentation: For mobile automation, you can use pre-instrumented applications to avoid manual instrumentation. Learn more about instrumenting apps for seamless automation setup.


iOS Native

iOS Application Automation

using Ranorex;
using Ranorex.Plugin.Mobile;
using System;
using System.Collections.Generic;

public class iOSNativeAutomation
{
    private MobileApp _currentApp;
    private string _appTitle;
    private static readonly TestCredential[] TestCredentials = new[]
    {
        new TestCredential
        {
            Title = "WordPressDemo",
            Username = "testuser1",
            Password = "password123",
            URL = "https://wordpress.com"
        },
        new TestCredential
        {
            Title = "GitHubTest", 
            Username = "developer",
            Password = "secure456",
            URL = "https://github.com"
        }
    };
    
    public void AutomateiOSApp(string appTitle)
    {
        try
        {
            _appTitle = appTitle;
            
            // Connect to iOS application
            ConnectToiOSApp();
            
            // Perform iOS automation test scenarios
            RuniOSTestScenarios();
            
            Report.Success("iOS Automation", "Successfully automated iOS application");
        }
        catch (Exception ex)
        {
            Report.Failure("iOS Automation", $"Failed: {ex.Message}");
            throw;
        }
        finally
        {
            CleanupApplication();
        }
    }
    
    private void ConnectToiOSApp()
    {
        try
        {
            Report.Info($"Connecting to iOS app: {_appTitle}");
            
            // Connect to the iOS app using mobile app path
            string appPath = $"/mobileapp[@title='{_appTitle}']";
            _currentApp = MobileApp.FromPath(appPath);

            if (_currentApp != null && _currentApp.Element.Valid)
            {
                // Handle initial permissions and setup
                HandleInitialSetup();
                
                Report.Success("iOS Connection", $"Successfully connected to iOS app: {_appTitle}");
            }
            else
            {
                throw new Exception($"Could not connect to iOS application: {_appTitle}");
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Failed to connect to iOS app: {ex.Message}");
        }
    }
    
    private void HandleInitialSetup()
    {
        try
        {
            // Handle iOS permission dialogs
            HandleiOSPermissions();
            
            // Wait for app initialization
            Delay.Milliseconds(1000);
            
            Report.Info("iOS Setup", "Initial setup completed");
        }
        catch (Exception ex)
        {
            Report.Warn("iOS Setup", $"Setup issues: {ex.Message}");
        }
    }
    
    private void HandleiOSPermissions()
    {
        try
        {
            // Handle location permission
            var allowLocationButton = _currentApp.FindSingle<Button>("//*[@name='Allow While Using App']", new Duration(3000));
            if (allowLocationButton.Valid)
            {
                allowLocationButton.Click();
                Report.Info("iOS Permissions", "Granted location permission");
                Delay.Milliseconds(1000);
            }
            
            // Handle notification permission
            var allowNotificationsButton = _currentApp.FindSingle<Button>("//*[@name='Allow']", new Duration(3000));
            if (allowNotificationsButton.Valid)
            {
                allowNotificationsButton.Click();
                Report.Info("iOS Permissions", "Granted notification permission");
                Delay.Milliseconds(1000);
            }
            
            // Handle additional permissions (camera, microphone, etc.)
            var okButton = _currentApp.FindSingle<Button>("//*[@name='OK']", new Duration(2000));
            if (okButton.Valid)
            {
                okButton.Click();
                Report.Info("iOS Permissions", "Handled additional permission");
            }
        }
        catch (Exception ex)
        {
            Report.Debug("iOS Permissions", $"No permission dialogs found: {ex.Message}");
        }
    }
    
    private void RuniOSTestScenarios()
    {
        // Run comprehensive iOS test scenarios similar to the KeePass example
        RunCreateNewDatabaseTest();
        RunAddNewEntryTests();
        RunAddNewGroupTests();
    }
    
    private void RunCreateNewDatabaseTest()
    {
        Report.Info("=== iOS Create Database Test ===");
        
        try
        {
            // Simulate login or database creation process
            SimulateLoginProcess();
            
            Report.Success("Create Database", "iOS database creation test completed");
        }
        catch (Exception ex)
        {
            Report.Error($"Create Database test failed: {ex.Message}");
            throw;
        }
    }
    
    private void RunAddNewEntryTests()
    {
        Report.Info("=== iOS Add Entry Tests ===");
        
        try
        {
            foreach (var credential in TestCredentials)
            {
                // Add entry using iOS-specific interactions
                AddEntryiOS(credential);
                
                // Validate entry was added
                ValidateEntryiOS(credential.Title);
                
                // Clean up entry
                DeleteEntryiOS(credential.Title);
            }
            
            Report.Success("Add Entry", "iOS entry tests completed");
        }
        catch (Exception ex)
        {
            Report.Error($"Add Entry tests failed: {ex.Message}");
            throw;
        }
    }
    
    private void RunAddNewGroupTests()
    {
        Report.Info("=== iOS Add Group Tests ===");
        
        try
        {
            string testGroup = "TestGroup";
            
            // Add group using iOS-specific interactions
            AddGroupiOS(testGroup);
            
            // Validate group was added
            ValidateGroupiOS(testGroup);
            
            // Clean up group
            DeleteGroupiOS(testGroup);
            
            Report.Success("Add Group", "iOS group tests completed");
        }
        catch (Exception ex)
        {
            Report.Error($"Add Group tests failed: {ex.Message}");
            throw;
        }
    }
    
    private void SimulateLoginProcess()
    {
        try
        {
            Report.Info("Performing iOS login process");
            
            // Find and interact with login elements using iOS-specific selectors
            var usernameField = _currentApp.FindSingle<TextField>("//textfield[@name='username' or @placeholder='Username']", new Duration(5000));
            if (usernameField.Valid)
            {
                usernameField.Click();
                Delay.Milliseconds(500);
                usernameField.PressKeys("testuser");
            }
            
            var passwordField = _currentApp.FindSingle<TextField>("//textfield[@name='password' or @placeholder='Password']", new Duration(3000));
            if (passwordField.Valid)
            {
                passwordField.Click();
                Delay.Milliseconds(500);
                passwordField.PressKeys("password123");
            }
            
            // Dismiss keyboard by tapping outside
            DismissiOSKeyboard();
            
            var loginButton = _currentApp.FindSingle<Button>("//button[@name='Login' or @name='Sign In']", new Duration(3000));
            if (loginButton.Valid)
            {
                loginButton.Click();
                Delay.Milliseconds(2000);
            }
            
            Report.Info("iOS Login", "Login process completed");
        }
        catch (Exception ex)
        {
            Report.Debug("iOS Login", $"Login simulation: {ex.Message}");
        }
    }
    
    private void AddEntryiOS(TestCredential credential)
    {
        Report.Info($"Adding iOS entry: {credential.Title}");
        
        try
        {
            // Navigate to add entry screen
            NavigateToAddEntryiOS();
            
            // Fill entry details using iOS UI elements
            FillEntryDetailsiOS(credential);
            
            // Save entry
            SaveEntryiOS();
            
            Report.Success("Add Entry", $"iOS entry '{credential.Title}' added successfully");
        }
        catch (Exception ex)
        {
            Report.Error($"Failed to add iOS entry: {ex.Message}");
            throw;
        }
    }
    
    private void NavigateToAddEntryiOS()
    {
        try
        {
            // Look for Add/Plus button in navigation bar
            var addButton = _currentApp.FindSingle<Button>("//button[@name='Add' or @name='+' or contains(@name,'add')]", new Duration(5000));
            if (addButton.Valid)
            {
                addButton.Click();
                Delay.Milliseconds(500);
            }
            
            // Select Entry option if multiple options available
            var newEntryOption = _currentApp.FindSingle<Button>("//button[@name='New Entry' or @name='Entry']", new Duration(3000));
            if (newEntryOption.Valid)
            {
                newEntryOption.Click();
                Delay.Milliseconds(500);
            }
            
            Report.Info("iOS Navigation", "Navigated to add entry screen");
        }
        catch (Exception ex)
        {
            Report.Debug("iOS Navigation", $"Navigation: {ex.Message}");
        }
    }
    
    private void FillEntryDetailsiOS(TestCredential credential)
    {
        try
        {
            // Fill title field
            var titleField = _currentApp.FindSingle<TextField>("//textfield[@name='Title' or @placeholder='Title']", new Duration(3000));
            if (titleField.Valid)
            {
                titleField.Click();
                titleField.PressKeys(credential.Title);
                Delay.Milliseconds(300);
            }
            
            // Fill username field
            var usernameField = _currentApp.FindSingle<TextField>("//textfield[@name='Username' or @placeholder='Username']", new Duration(3000));
            if (usernameField.Valid)
            {
                usernameField.Click();
                usernameField.PressKeys(credential.Username);
                Delay.Milliseconds(300);
            }
            
            // Fill password field
            var passwordField = _currentApp.FindSingle<TextField>("//textfield[@name='Password' or @placeholder='Password']", new Duration(3000));
            if (passwordField.Valid)
            {
                passwordField.Click();
                passwordField.PressKeys(credential.Password);
                Delay.Milliseconds(300);
            }
            
            // Fill URL field if available
            var urlField = _currentApp.FindSingle<TextField>("//textfield[@name='URL' or @placeholder='URL']", new Duration(2000));
            if (urlField.Valid)
            {
                urlField.Click();
                urlField.PressKeys(credential.URL);
                Delay.Milliseconds(300);
            }
            
            // Dismiss keyboard
            DismissiOSKeyboard();
            
            Report.Info("iOS Form", "Entry details filled");
        }
        catch (Exception ex)
        {
            Report.Debug("iOS Form", $"Form filling: {ex.Message}");
        }
    }
    
    private void SaveEntryiOS()
    {
        try
        {
            // Look for Done/Save button
            var saveButton = _currentApp.FindSingle<Button>("//button[@name='Done' or @name='Save']", new Duration(3000));
            if (saveButton.Valid)
            {
                saveButton.Click();
                Delay.Milliseconds(1000);
                Report.Info("iOS Save", "Entry saved successfully");
            }
            else
            {
                Report.Warn("iOS Save", "Save button not found");
            }
        }
        catch (Exception ex)
        {
            Report.Debug("iOS Save", $"Save operation: {ex.Message}");
        }
    }
    
    private void ValidateEntryiOS(string title)
    {
        Report.Info($"Validating iOS entry: {title}");
        
        try
        {
            // Search for the entry in the list
            if (FindEntryInListiOS(title))
            {
                Report.Success("Validation", $"iOS entry '{title}' found and validated");
            }
            else
            {
                throw new Exception($"iOS entry '{title}' not found during validation");
            }
        }
        catch (Exception ex)
        {
            Report.Error($"iOS entry validation failed: {ex.Message}");
            throw;
        }
    }
    
    private bool FindEntryInListiOS(string title)
    {
        try
        {
            // Look for the entry in table view or collection view
            var entryElement = _currentApp.FindSingle<Cell>($"//cell[contains(@name,'{title}')]", new Duration(5000));
            if (entryElement.Valid)
            {
                Report.Info("iOS Search", $"Found entry '{title}' in list");
                return true;
            }
            
            // Try alternative search methods
            var textElement = _currentApp.FindSingle<Text>($"//text[contains(@name,'{title}')]", new Duration(3000));
            if (textElement.Valid)
            {
                Report.Info("iOS Search", $"Found entry '{title}' as text element");
                return true;
            }
            
            return false;
        }
        catch (Exception ex)
        {
            Report.Debug("iOS Search", $"Search error: {ex.Message}");
            return false;
        }
    }
    
    private void DeleteEntryiOS(string title)
    {
        Report.Info($"Deleting iOS entry: {title}");
        
        try
        {
            if (FindEntryInListiOS(title))
            {
                // Long press or swipe to reveal delete option
                var entryElement = _currentApp.FindSingle<Cell>($"//cell[contains(@name,'{title}')]", new Duration(5000));
                if (entryElement.Valid)
                {
                    // Try swipe to delete (iOS pattern)
                    entryElement.Swipe(Ranorex.Core.Recorder.Touch.GestureDirection.Left, new Distance(100));
                    Delay.Milliseconds(500);
                    
                    // Look for delete button
                    var deleteButton = _currentApp.FindSingle<Button>("//button[@name='Delete' or contains(@name,'delete')]", new Duration(3000));
                    if (deleteButton.Valid)
                    {
                        deleteButton.Click();
                        
                        // Confirm deletion if needed
                        var confirmButton = _currentApp.FindSingle<Button>("//button[@name='Delete' or @name='Confirm']", new Duration(2000));
                        if (confirmButton.Valid)
                        {
                            confirmButton.Click();
                        }
                        
                        Delay.Milliseconds(1000);
                        Report.Success("Delete Entry", $"iOS entry '{title}' deleted successfully");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Report.Error($"Failed to delete iOS entry: {ex.Message}");
            throw;
        }
    }
    
    private void AddGroupiOS(string groupName)
    {
        Report.Info($"Adding iOS group: {groupName}");
        
        try
        {
            // Navigate to add group
            var addButton = _currentApp.FindSingle<Button>("//button[@name='Add' or @name='+']", new Duration(5000));
            if (addButton.Valid)
            {
                addButton.Click();
                Delay.Milliseconds(500);
            }
            
            // Select Group option
            var newGroupOption = _currentApp.FindSingle<Button>("//button[@name='New Group' or @name='Group']", new Duration(3000));
            if (newGroupOption.Valid)
            {
                newGroupOption.Click();
                Delay.Milliseconds(500);
            }
            
            // Enter group name
            var groupNameField = _currentApp.FindSingle<TextField>("//textfield[@placeholder='Group Name' or @name='Name']", new Duration(3000));
            if (groupNameField.Valid)
            {
                groupNameField.Click();
                groupNameField.PressKeys(groupName);
                Delay.Milliseconds(500);
            }
            
            // Save group
            var saveButton = _currentApp.FindSingle<Button>("//button[@name='Done' or @name='Save']", new Duration(3000));
            if (saveButton.Valid)
            {
                saveButton.Click();
                Delay.Milliseconds(1000);
            }
            
            // Dismiss keyboard if needed
            DismissiOSKeyboard();
            
            Report.Success("Add Group", $"iOS group '{groupName}' added successfully");
        }
        catch (Exception ex)
        {
            Report.Error($"Failed to add iOS group: {ex.Message}");
            throw;
        }
    }
    
    private void ValidateGroupiOS(string groupName)
    {
        Report.Info($"Validating iOS group: {groupName}");
        
        try
        {
            if (FindGroupInListiOS(groupName))
            {
                Report.Success("Validation", $"iOS group '{groupName}' found and validated");
            }
            else
            {
                throw new Exception($"iOS group '{groupName}' not found during validation");
            }
        }
        catch (Exception ex)
        {
            Report.Error($"iOS group validation failed: {ex.Message}");
            throw;
        }
    }
    
    private bool FindGroupInListiOS(string groupName)
    {
        try
        {
            // Look for the group in list
            var groupElement = _currentApp.FindSingle<Cell>($"//cell[contains(@name,'{groupName}')]", new Duration(5000));
            if (groupElement.Valid)
            {
                Report.Info("iOS Search", $"Found group '{groupName}' in list");
                return true;
            }
            
            return false;
        }
        catch (Exception ex)
        {
            Report.Debug("iOS Search", $"Group search error: {ex.Message}");
            return false;
        }
    }
    
    private void DeleteGroupiOS(string groupName)
    {
        Report.Info($"Deleting iOS group: {groupName}");
        
        try
        {
            if (FindGroupInListiOS(groupName))
            {
                // Similar to entry deletion process
                var groupElement = _currentApp.FindSingle<Cell>($"//cell[contains(@name,'{groupName}')]", new Duration(5000));
                if (groupElement.Valid)
                {
                    groupElement.Swipe(Ranorex.Core.Recorder.Touch.GestureDirection.Left, new Distance(100));
                    Delay.Milliseconds(500);
                    
                    var deleteButton = _currentApp.FindSingle<Button>("//button[@name='Delete']", new Duration(3000));
                    if (deleteButton.Valid)
                    {
                        deleteButton.Click();
                        Delay.Milliseconds(1000);
                        Report.Success("Delete Group", $"iOS group '{groupName}' deleted successfully");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Report.Error($"Failed to delete iOS group: {ex.Message}");
            throw;
        }
    }
    
    private void DismissiOSKeyboard()
    {
        try
        {
            // Tap outside text fields to dismiss keyboard
            var appElement = _currentApp.Element;
            if (appElement.Valid)
            {
                appElement.Click(new Location(100, 100)); // Tap in empty area
                Delay.Milliseconds(500);
            }
        }
        catch (Exception ex)
        {
            Report.Debug("iOS Keyboard", $"Keyboard dismissal: {ex.Message}");
        }
    }
    
    private void CleanupApplication()
    {
        try
        {
            if (_currentApp != null)
            {
                Report.Info("iOS Cleanup", "Closing iOS application");
                // Clean up would typically involve closing the app gracefully
                Delay.Milliseconds(500);
                Report.Info("Cleanup", "iOS application cleanup completed");
            }
        }
        catch (Exception ex)
        {
            Report.Warn("Cleanup", $"iOS cleanup issues: {ex.Message}");
        }
    }
    
    // Data structure for test credentials
    public class TestCredential
    {
        public string Title { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public string URL { get; set; }
    }
}

📱 App Instrumentation: For mobile automation, you can use pre-instrumented applications to avoid manual instrumentation. Learn more about instrumenting apps for seamless automation setup.


Hybrid Apps

Hybrid Application Automation

using Ranorex;
using System;
using System.Threading;

public class HybridAppAutomation
{
    private string _appTitle;
    private string _processName;
    
    public void AutomateHybridApp(string appTitle, string processName = null)
    {
        Host.Initialize();
        
        try
        {
            _appTitle = appTitle;
            _processName = processName ?? "HybridApp";
            
            // Verify touch support
            VerifyTouchSupport();
            
            // Connect to hybrid application
            ConnectToHybridApp();
            
            // Handle native context
            HandleNativeContext();
            
            // Switch to web context
            SwitchToWebContext();
            
            // Handle web content
            HandleWebContext();
            
            // Switch back to native context
            SwitchToNativeContext();
            
            Report.Success("Hybrid App", "Successfully automated hybrid application");
        }
        catch (Exception ex)
        {
            Report.Failure("Hybrid App", $"Failed: {ex.Message}");
            TakeScreenshot("HybridApp_Error");
            throw;
        }
        finally
        {
            Host.Shutdown();
        }
    }
    
    private void VerifyTouchSupport()
    {
        if (!Touch.IsSupported)
        {
            throw new InvalidOperationException("Touch input is not supported for hybrid app automation");
        }
        Report.Success("Touch Support", "Touch input is available");
    }
    
    private void ConnectToHybridApp()
    {
        try
        {
            // Wait for hybrid application window/form to be available
            var appWindow = Host.Local.FindSingle<Form>($"/form[@processname='{_processName}']", 30000);
            
            if (!appWindow.Exists)
            {
                appWindow = Host.Local.FindSingle<Form>($"//form[contains(@title,'{_appTitle}')]", 10000);
            }

            if (appWindow.Exists)
            {
                appWindow.Focus();
                Thread.Sleep(1000);
                
                // Wait for hybrid app to initialize
                WaitForHybridAppReady(TimeSpan.FromSeconds(15));
                
                Report.Success("Hybrid Connection", $"Connected to hybrid app: {_appTitle}");
            }
            else
            {
                throw new Exception($"Could not connect to hybrid application: {_appTitle}");
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Failed to connect to hybrid app: {ex.Message}");
        }
    }
    
    private void WaitForHybridAppReady(TimeSpan timeout)
    {
        // Wait for WebView to be available in hybrid app
        var webView = Host.Local.FindSingle<WebDocument>("//webdocument | //webview", (int)timeout.TotalMilliseconds);
        
        if (webView.Exists)
        {
            Report.Info("Hybrid Ready", "WebView detected - hybrid app ready");
        }
        else
        {
            Report.Warn("Hybrid Ready", "WebView not detected - app may be purely native");
        }
    }
    
    private void HandleNativeContext()
    {
        try
        {
            // Interact with native elements first
            var nativeButton = Host.Local.FindSingle<Button>("//*[@text='Open WebView' or @name='Open WebView']", 5000);
            if (nativeButton.Exists)
            {
                nativeButton.Click();
                // Wait for the webview to appear
                Host.Local.FindSingle<WebDocument>("//webview", 5000);
            }
            
            // Handle native navigation elements
            var toolbar = Host.Local.FindSingle<Container>("//*[@class='android.widget.Toolbar']", 3000);
            if (toolbar.Exists)
            {
                Report.Info("Native Context", "Found native toolbar");
            }
            
            Report.Success("Native Context", "Successfully handled native elements");
        }
        catch (Exception ex)
        {
            Report.Debug("Native Context", $"Limited native elements: {ex.Message}");
        }
    }
    
    private void SwitchToWebContext()
    {
        try
        {
            // Find the WebView container
            var webView = Host.Local.FindSingle<WebDocument>("//*[@class='android.webkit.WebView']", 10000);
            
            if (webView.Exists)
            {
                // Switch focus to web context
                webView.Focus();
                
                // Wait for web content to load
                WaitForWebContentReady(webView);
                
                Report.Success("Context Switch", "Successfully switched to web context");
            }
            else
            {
                throw new Exception("WebView not found");
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Failed to switch to web context: {ex.Message}");
        }
    }
    
    private void WaitForWebContentReady(WebDocument webView)
    {
        // Wait for JavaScript to be ready
        var jsReady = WaitHelper.WaitForCondition(() =>
        {
            try
            {
                var result = webView.ExecuteScript("return document.readyState");
                return result?.ToString() == "complete";
            }
            catch
            {
                return false;
            }
        }, "web content to load", TimeSpan.FromSeconds(20));
        
        if (!jsReady)
        {
            throw new TimeoutException("Web content did not load in time");
        }
    }
    
    private void HandleWebContext()
    {
        try
        {
            var webView = Host.Local.FindSingle<WebDocument>("//*[@class='android.webkit.WebView']");
            
            // Handle web form elements
            HandleWebForm(webView);
            
            // Handle web navigation
            HandleWebNavigation(webView);
            
            // Interact with hybrid elements
            HandleHybridElements(webView);
            
            Report.Success("Web Context", "Successfully handled web content");
        }
        catch (Exception ex)
        {
            Report.Failure("Web Context", $"Failed: {ex.Message}");
            throw;
        }
    }
    
    private void HandleWebForm(WebDocument webView)
    {
        try
        {
            // Fill web form elements
            var emailField = webView.FindSingle<InputTag>("//input[@type='email']");
            emailField.Click();
            emailField.PressKeys("test@example.com");
            
            var passwordField = webView.FindSingle<InputTag>("//input[@type='password']");
            passwordField.Click();
            passwordField.PressKeys("password123");
            
            // Submit form
            var submitButton = webView.FindSingle<Button>("//button[@type='submit']");
            submitButton.Click();
            
            Report.Success("Web Form", "Successfully filled web form");
        }
        catch (Exception ex)
        {
            Report.Debug("Web Form", $"No web form found: {ex.Message}");
        }
    }
    
    private void HandleWebNavigation(WebDocument webView)
    {
        try
        {
            // Click web navigation links
            var navLink = webView.FindSingle<ATag>("//a[contains(@href, 'dashboard')]");
            navLink.Click();
            
            // Wait for the page to navigate
            Host.Local.FindSingle<WebDocument>("/dom", 5000);
            
            // Handle JavaScript navigation
            webView.ExecuteScript("history.back()");
            
            // Wait for the page to navigate back
            Host.Local.FindSingle<WebDocument>("/dom", 5000);
            
            Report.Success("Web Navigation", "Successfully handled web navigation");
        }
        catch (Exception ex)
        {
            Report.Debug("Web Navigation", $"No web navigation: {ex.Message}");
        }
    }
    
    private void HandleHybridElements(WebDocument webView)
    {
        try
        {
            // Trigger native functionality from web
            webView.ExecuteScript("window.cordova.plugins.camera.getPicture();");
            
            // Wait for the native camera view to appear
            // The path should be adapted to the application's UI structure.
            Host.Local.FindSingle<Adapter>("/form", 5000);
            
            Report.Info("Hybrid Elements", "Triggered native functionality from web");
        }
        catch (Exception ex)
        {
            Report.Debug("Hybrid Elements", $"No hybrid plugins available: {ex.Message}");
        }
    }
    
    private void SwitchToNativeContext()
    {
        try
        {
            // Click outside WebView to return to native context
            var appWindow = Host.Local.FindSingle<Form>("/form");
            if (appWindow.Exists)
            {
                var bounds = appWindow.ScreenRectangle;
                Touch.Tap(appWindow, new Location(bounds.Width / 2, 100));
                Thread.Sleep(1000);
                
                // Verify we're back in native context by looking for native elements
                var nativeElement = Host.Local.FindSingle<Button>("//button", 3000);
                if (nativeElement.Exists)
                {
                    Report.Success("Context Switch", "Successfully returned to native context");
                }
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Context Switch", $"Already in native context: {ex.Message}");
        }
    }

    // Helper methods for hybrid automation
    private void TakeScreenshot(string name)
    {
        try
        {
            var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
            var adapter = Host.Local.FindSingle<Adapter>("/form");
            Report.Screenshot(adapter, $"{name}_{timestamp}");
        }
        catch (Exception ex)
        {
            Report.Warn("Screenshot", $"Could not take screenshot: {ex.Message}");
        }
    }
}

Mobile Web

Mobile Browser Automation

using Ranorex;
using System;
using System.Threading;

public class MobileWebAutomation
{
    private string _url;
    private WebDocument _webDocument;
    private string _browserProcess;
    
    public void AutomateMobileWeb(string url, string browserProcess = "chrome")
    {
        Host.Initialize();
        
        try
        {
            _url = url;
            _browserProcess = browserProcess;
            
            // Verify touch support
            VerifyTouchSupport();
            
            // Launch mobile browser
            LaunchMobileBrowser();
            
            // Handle mobile-specific web features
            HandleMobileWebFeatures();
            
            // Test responsive design
            TestResponsiveDesign();
            
            // Handle touch gestures on web
            HandleMobileTouchGestures();
            
            Report.Success("Mobile Web", "Successfully automated mobile web application");
        }
        catch (Exception ex)
        {
            Report.Failure("Mobile Web", $"Failed: {ex.Message}");
            TakeScreenshot("MobileWeb_Error");
            throw;
        }
        finally
        {
            Host.Shutdown();
        }
    }
    
    private void VerifyTouchSupport()
    {
        if (!Touch.IsSupported)
        {
            throw new InvalidOperationException("Touch input is not supported for mobile web automation");
        }
        Report.Success("Touch Support", "Touch input is available");
    }
    
    private void LaunchMobileBrowser()
    {
        try
        {
            // Connect to browser process or launch manually
            var browserWindow = Host.Local.FindSingle<Form>($"/form[@processname='{_browserProcess}']", 10000);
            
            if (!browserWindow.Exists)
            {
                Report.Info("Browser Launch", $"Browser not found. Please manually launch {_browserProcess} and navigate to {_url}");
                // Wait for browser to be available
                browserWindow = Host.Local.FindSingle<Form>($"/form[@processname='{_browserProcess}']", 30000);
            }

            if (browserWindow.Exists)
            {
                browserWindow.Focus();
                Thread.Sleep(2000);
                
                // Try to navigate to URL if address bar is available
                var addressBar = FindElementWithFallback(new[]
                {
                    "//text[@name='Address and search bar']",
                    "//text[contains(@name,'address')]",
                    "//combobox[@name='Address and search bar']"
                });

                if (addressBar != null)
                {
                    Touch.Tap(addressBar, Location.Center);
                    Thread.Sleep(500);
                    addressBar.PressKeys("{Control down}a{Control up}");
                    addressBar.PressKeys(_url);
                    addressBar.PressKeys("{Return}");
                    Thread.Sleep(3000);
                }
                
                // Wait for page to load and get WebDocument
                _webDocument = Host.Local.FindSingle<WebDocument>("/dom", 15000);
                
                if (_webDocument.Exists)
                {
                    Report.Success("Mobile Browser", "Successfully connected to mobile browser");
                }
                else
                {
                    throw new Exception("Could not find web document");
                }
            }
            else
            {
                throw new Exception($"Could not connect to browser: {_browserProcess}");
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Failed to launch mobile browser: {ex.Message}");
        }
    }
    
    private void HandleMobileWebFeatures()
    {
        try
        {
            // Handle mobile viewport
            HandleMobileViewport();
            
            // Handle touch-optimized elements
            HandleTouchOptimizedElements();
            
            // Handle mobile-specific UI patterns
            HandleMobileUIPatterns();
        }
        catch (Exception ex)
        {
            Report.Failure("Mobile Web Features", $"Failed: {ex.Message}");
            throw;
        }
    }
    
    private void HandleMobileViewport()
    {
        try
        {
            // Check viewport meta tag
            var viewportMeta = _webDocument.ExecuteScript(
                "return document.querySelector('meta[name=\"viewport\"]')?.content || 'none'");
            
            Report.Info("Viewport", $"Viewport meta: {viewportMeta}");
            
            // Check if page is mobile-optimized
            var isMobileOptimized = _webDocument.ExecuteScript(
                "return window.innerWidth <= 768 && document.querySelector('meta[name=\"viewport\"]') !== null");
            
            if (isMobileOptimized != null && (bool)isMobileOptimized)
            {
                Report.Success("Mobile Optimization", "Page is mobile-optimized");
            }
            else
            {
                Report.Info("Mobile Optimization", "Page may not be fully mobile-optimized");
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Viewport", $"Could not check viewport: {ex.Message}");
        }
    }
    
    private void HandleTouchOptimizedElements()
    {
        try
        {
            // Find and interact with touch-friendly buttons
            var buttons = _webDocument.Find<Button>("//button | //input[@type='button'] | //a[contains(@class, 'btn')]");
            
            foreach (var button in buttons)
            {
                // Check button size (should be at least 44px for good touch targets)
                var buttonSize = _webDocument.ExecuteScript($@"
                    var btn = arguments[0];
                    var rect = btn.getBoundingClientRect();
                    return {{ width: rect.width, height: rect.height }};
                ", button);
                
                Report.Info("Touch Target", $"Button size: {buttonSize}");
            }
            
            // Test hamburger menu (common mobile pattern)
            var hamburgerMenu = _webDocument.FindSingle<Button>("//button[contains(@class, 'menu') or contains(@class, 'hamburger')]", 5000);
            if (hamburgerMenu.Exists)
            {
                hamburgerMenu.Click();
                // Wait for the menu to appear, for example by waiting for a menu item
                _webDocument.FindSingle<DivTag>("//div[contains(@class, 'mobile-menu-item')]", 5000);
                
                // Close menu
                hamburgerMenu.Click();
                Report.Success("Mobile Menu", "Successfully toggled mobile menu");
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Touch Elements", $"No touch-optimized elements found: {ex.Message}");
        }
    }
    
    private void HandleMobileUIPatterns()
    {
        try
        {
            // Handle pull-to-refresh
            HandlePullToRefresh();
            
            // Handle infinite scroll
            HandleInfiniteScroll();
            
            // Handle swipe navigation
            HandleSwipeNavigation();
        }
        catch (Exception ex)
        {
            Report.Debug("Mobile UI Patterns", $"Could not test all patterns: {ex.Message}");
        }
    }
    
    private void HandlePullToRefresh()
    {
        try
        {
            var browserWindow = Host.Local.FindSingle<Form>($"/form[@processname='{_browserProcess}']");
            if (browserWindow.Exists)
            {
                var bounds = browserWindow.ScreenRectangle;
                var startPoint = new Location(bounds.Width / 2, 200);
                var endPoint = new Location(bounds.Width / 2, bounds.Height / 2);
                
                // Perform pull gesture using actual Touch API
                Touch.TouchStart(browserWindow, 0, startPoint);
                Touch.TouchMove(browserWindow, 0, endPoint, Duration.FromMilliseconds(800));
                Touch.TouchEnd(browserWindow, 0, endPoint);
                
                Thread.Sleep(2000);
                Report.Info("Pull to Refresh", "Performed pull-to-refresh gesture");
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Pull to Refresh", $"Could not perform pull-to-refresh: {ex.Message}");
        }
    }
    
    private void HandleMobileTouchGestures()
    {
        try
        {
            // Test swipe gestures
            TestSwipeGestures();
            
            // Test pinch zoom
            TestPinchZoom();
            
            // Test long press
            TestLongPress();
            
            Report.Success("Touch Gestures", "Successfully tested mobile touch gestures");
        }
        catch (Exception ex)
        {
            Report.Failure("Touch Gestures", $"Failed: {ex.Message}");
            throw;
        }
    }
    
    private void TestSwipeGestures()
    {
        try
        {
            var browserWindow = Host.Local.FindSingle<Form>($"/form[@processname='{_browserProcess}']");
            if (browserWindow.Exists)
            {
                var bounds = browserWindow.ScreenRectangle;
                var centerY = bounds.Height / 2;
                
                // Swipe left
                Touch.TouchStart(browserWindow, 0, new Location(bounds.Width - 50, centerY));
                Touch.TouchMove(browserWindow, 0, new Location(50, centerY), Duration.FromMilliseconds(400));
                Touch.TouchEnd(browserWindow, 0, new Location(50, centerY));
                Thread.Sleep(1000);
                
                // Swipe right
                Touch.TouchStart(browserWindow, 0, new Location(50, centerY));
                Touch.TouchMove(browserWindow, 0, new Location(bounds.Width - 50, centerY), Duration.FromMilliseconds(400));
                Touch.TouchEnd(browserWindow, 0, new Location(bounds.Width - 50, centerY));
                Thread.Sleep(1000);
                
                Report.Info("Swipe Gestures", "Performed left and right swipe gestures");
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Swipe Gestures", $"Could not perform swipe gestures: {ex.Message}");
        }
    }
    
    private void TestPinchZoom()
    {
        try
        {
            var browserWindow = Host.Local.FindSingle<Form>($"/form[@processname='{_browserProcess}']");
            if (browserWindow.Exists)
            {
                var bounds = browserWindow.ScreenRectangle;
                var centerX = bounds.Width / 2;
                var centerY = bounds.Height / 2;
                
                // Pinch to zoom in using actual Touch API
                var point1Start = new Location(centerX - 50, centerY - 50);
                var point1End = new Location(centerX - 100, centerY - 100);
                var point2Start = new Location(centerX + 50, centerY + 50);
                var point2End = new Location(centerX + 100, centerY + 100);
                
                // Start multi-touch
                Touch.TouchStart(browserWindow, 0, point1Start);
                Touch.TouchStart(browserWindow, 1, point2Start);
                Thread.Sleep(100);
                
                // Move touches apart (zoom in)
                Touch.TouchMove(browserWindow, 0, point1End, Duration.FromMilliseconds(500));
                Touch.TouchMove(browserWindow, 1, point2End, Duration.FromMilliseconds(500));
                Thread.Sleep(200);
                
                // End touches
                Touch.TouchEnd(browserWindow, 0, point1End);
                Touch.TouchEnd(browserWindow, 1, point2End);
                
                Thread.Sleep(2000);
                
                // Check zoom level
                var zoomLevel = _webDocument.ExecuteScript("return window.devicePixelRatio || 1");
                Report.Info("Pinch Zoom", $"Zoom level after pinch: {zoomLevel}");
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Pinch Zoom", $"Could not test pinch zoom: {ex.Message}");
        }
    }
    
    private void TestLongPress()
    {
        try
        {
            // Find an element to long press
            var testElement = _webDocument.FindSingle<DivTag>("//div | //p | //span", 5000);
            if (testElement.Exists)
            {
                // Perform long touch using actual Touch API
                Touch.LongTouch(testElement, Location.Center, Duration.FromMilliseconds(1500));
                Thread.Sleep(2000);
                
                // Try to dismiss context menu by pressing Escape or clicking outside
                var browserWindow = Host.Local.FindSingle<Form>($"/form[@processname='{_browserProcess}']");
                if (browserWindow.Exists)
                {
                    // Try pressing Escape key first
                    browserWindow.PressKeys("{Escape}");
                    Thread.Sleep(500);
                    
                    // If that doesn't work, click outside to dismiss any context menu
                    Touch.Tap(browserWindow, new Location(50, 50));
                }
                
                Report.Info("Long Press", "Performed long press gesture");
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Long Press", $"Could not test long press: {ex.Message}");
        }
    }
    
    private void TestResponsiveDesign()
    {
        try
        {
            // Test different screen orientations and layouts
            TestPortraitLayout();
            TestLandscapeLayout();
            TestDifferentViewportSizes();
            
            Report.Success("Responsive Design", "Successfully tested responsive design features");
        }
        catch (Exception ex)
        {
            Report.Debug("Responsive Design", $"Could not test responsive design: {ex.Message}");
        }
    }
    
    private void TestPortraitLayout()
    {
        try
        {
            // Capture layout information in portrait mode
            var layoutInfo = _webDocument.ExecuteScript(@"
                return {
                    viewport: { width: window.innerWidth, height: window.innerHeight },
                    mainContent: document.querySelector('main, #main, .main')?.getBoundingClientRect(),
                    navigation: document.querySelector('nav, .nav, .navigation')?.getBoundingClientRect()
                };
            ");
            
            Report.Info("Portrait Layout", $"Layout info captured: {layoutInfo}");
        }
        catch (Exception ex)
        {
            Report.Debug("Portrait Layout", $"Could not capture portrait layout info: {ex.Message}");
        }
    }
    
    private void TestLandscapeLayout()
    {
        try
        {
            // Note: Actual rotation would be handled through external device management
            // Here we simulate testing landscape layout patterns
            var layoutInfo = _webDocument.ExecuteScript(@"
                return {
                    viewport: { width: window.innerWidth, height: window.innerHeight },
                    isLandscape: window.innerWidth > window.innerHeight,
                    navigation: document.querySelector('nav, .nav, .navigation')?.getBoundingClientRect()
                };
            ");
            
            Report.Info("Landscape Layout", $"Layout info captured: {layoutInfo}");
        }
        catch (Exception ex)
        {
            Report.Debug("Landscape Layout", $"Could not test landscape layout: {ex.Message}");
        }
    }
    
    private void TestDifferentViewportSizes()
    {
        try
        {
            // Test how the page responds to different viewport sizes
            var viewportInfo = _webDocument.ExecuteScript(@"
                return {
                    currentWidth: window.innerWidth,
                    currentHeight: window.innerHeight,
                    isMobile: window.innerWidth <= 768,
                    isTablet: window.innerWidth > 768 && window.innerWidth <= 1024,
                    isDesktop: window.innerWidth > 1024
                };
            ");
            
            Report.Info("Viewport Sizes", $"Viewport info: {viewportInfo}");
        }
        catch (Exception ex)
        {
            Report.Debug("Viewport Sizes", $"Could not test viewport sizes: {ex.Message}");
        }
    }
    
    private void HandleInfiniteScroll()
    {
        try
        {
            // Scroll to bottom of page to test infinite scroll
            var browserWindow = Host.Local.FindSingle<Form>($"/form[@processname='{_browserProcess}']");
            if (browserWindow.Exists)
            {
                var bounds = browserWindow.ScreenRectangle;
                
                for (int i = 0; i < 3; i++)
                {
                    var startY = bounds.Y + (int)(bounds.Height * 0.8);
                    var endY = bounds.Y + (int)(bounds.Height * 0.2);
                    var centerX = bounds.X + (bounds.Width / 2);
                    
                    // Perform scroll using actual Touch API
                    Touch.TouchStart(browserWindow, 0, new Location(centerX - bounds.X, startY - bounds.Y));
                    Touch.TouchMove(browserWindow, 0, new Location(centerX - bounds.X, endY - bounds.Y), Duration.FromMilliseconds(500));
                    Touch.TouchEnd(browserWindow, 0, new Location(centerX - bounds.X, endY - bounds.Y));
                    
                    Thread.Sleep(2000);
                    
                    // Check if new content loaded
                    var contentHeight = _webDocument.ExecuteScript("return document.body.scrollHeight");
                    Report.Info("Infinite Scroll", $"Page height after scroll {i + 1}: {contentHeight}");
                }
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Infinite Scroll", $"Could not test infinite scroll: {ex.Message}");
        }
    }
    
    private void HandleSwipeNavigation()
    {
        try
        {
            var browserWindow = Host.Local.FindSingle<Form>($"/form[@processname='{_browserProcess}']");
            if (browserWindow.Exists)
            {
                var bounds = browserWindow.ScreenRectangle;
                var centerY = bounds.Height / 2;
                
                // Swipe left for next page
                Touch.TouchStart(browserWindow, 0, new Location(bounds.Width - 50, centerY));
                Touch.TouchMove(browserWindow, 0, new Location(50, centerY), Duration.FromMilliseconds(400));
                Touch.TouchEnd(browserWindow, 0, new Location(50, centerY));
                
                Thread.Sleep(2000);
                
                // Swipe right for previous page
                Touch.TouchStart(browserWindow, 0, new Location(50, centerY));
                Touch.TouchMove(browserWindow, 0, new Location(bounds.Width - 50, centerY), Duration.FromMilliseconds(400));
                Touch.TouchEnd(browserWindow, 0, new Location(bounds.Width - 50, centerY));
                
                Report.Info("Swipe Navigation", "Performed swipe navigation gestures");
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Swipe Navigation", $"Could not perform swipe navigation: {ex.Message}");
        }
    }

    // Helper methods for mobile web automation
    private Element FindElementWithFallback(string[] xpaths)
    {
        foreach (var xpath in xpaths)
        {
            try
            {
                if (Host.Local.TryFindSingle(xpath, 2000, out Element element))
                {
                    if (element.Exists && element.Visible)
                    {
                        return element;
                    }
                }
            }
            catch (Exception)
            {
                continue;
            }
        }
        return null;
    }
    
    private void TakeScreenshot(string name)
    {
        try
        {
            var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
            var adapter = Host.Local.FindSingle<Adapter>("/form");
            Report.Screenshot(adapter, $"{name}_{timestamp}");
        }
        catch (Exception ex)
        {
            Report.Warn("Screenshot", $"Could not take screenshot: {ex.Message}");
        }
    }
}

Cross-Platform

Cross-Platform App Automation

using Ranorex;
using System;
using System.Collections.Generic;

public class CrossPlatformAutomation
{
    private string _appIdentifier;
    private bool _isAndroid;
    
    public void AutomateCrossPlatformApp(string appIdentifier, bool isAndroid = true)
    {
        Host.Initialize();
        
        try
        {
            _appIdentifier = appIdentifier;
            _isAndroid = isAndroid;
            
            // Setup cross-platform automation
            SetupCrossPlatformEnvironment();
            
            // Launch application
            LaunchCrossPlatformApp();
            
            // Handle platform-agnostic automation
            HandlePlatformAgnosticElements();
            
            // Handle platform-specific differences
            HandlePlatformSpecificFeatures();
            
            Report.Success("Cross-Platform", "Successfully automated cross-platform application");
        }
        catch (Exception ex)
        {
            Report.Failure("Cross-Platform", $"Failed: {ex.Message}");
            // Consider using a custom screenshot utility or Ranorex's built-in screenshot capabilities.
            // For example: Report.Screenshot(Host.Local.FindSingle<Adapter>("/form"));
            throw;
        }
        finally
        {
            Host.Shutdown();
        }
    }
    
    private void SetupCrossPlatformEnvironment()
    {
        try
        {
            // Note: Device info would typically be obtained through actual device management APIs
            // or configuration rather than fictional Mobile.Device APIs
            var platformName = _isAndroid ? "Android" : "iOS";
            
            Report.Info("Platform Setup", $"Configuring for {platformName} device");
            
            // Configure platform-specific settings
            if (_isAndroid)
            {
                ConfigureAndroidSettings();
            }
            else
            {
                ConfigureiOSSettings();
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Failed to setup cross-platform environment: {ex.Message}");
        }
    }
    
    private void ConfigureAndroidSettings()
    {
        // Android-specific configuration
        // Note: Device orientation would be handled through external device management tools
        // or through app-specific automation rather than fictional Mobile.Device APIs
        Report.Info("Android Config", "Configured Android-specific settings");
    }
    
    private void ConfigureiOSSettings()
    {
        // iOS-specific configuration  
        // Note: Device orientation would be handled through external device management tools
        // or through app-specific automation rather than fictional Mobile.Device APIs
        Report.Info("iOS Config", "Configured iOS-specific settings");
    }
    
    private void LaunchCrossPlatformApp()
    {
        try
        {
            // Note: Application launch would typically be handled through external device management
            // tools, app deployment systems, or by connecting to already running applications
            // rather than using fictional Mobile.RunApplication API
            
            // Try to connect to running application by process name or app identifier
            var appWindow = Host.Local.FindSingle<Form>($"/form[@processname='{_appIdentifier}']", 15000);
            
            if (!appWindow.Exists)
            {
                // Try to find by partial title match
                appWindow = Host.Local.FindSingle<Form>($"//form[contains(@title,'{_appIdentifier}')]", 10000);
            }

            if (appWindow.Exists)
            {
                appWindow.Focus();
                Thread.Sleep(2000);
                
                // Wait for cross-platform framework to initialize
                WaitForFrameworkInitialization(TimeSpan.FromSeconds(20));
                
                Report.Success("App Launch", "Successfully connected to cross-platform application");
            }
            else
            {
                Report.Info("App Launch", $"Could not auto-connect to app '{_appIdentifier}'. Please ensure the application is running and try manual connection.");
                throw new Exception($"Application '{_appIdentifier}' not found. Please launch manually and retry.");
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Failed to launch cross-platform app: {ex.Message}");
        }
    }
    
    private void WaitForFrameworkInitialization(TimeSpan timeout)
    {
        // Wait for React Native, Xamarin, or Flutter to initialize
        var xpaths = new[]
        {
            "//*[@class='com.facebook.react.ReactRootView']",
            "//*[contains(@class, 'xamarin')]",
            "//*[contains(@class, 'flutter')]"
        };

        if (Host.Local.TryFindSingle(string.Join(" | ", xpaths), (int)timeout.TotalMilliseconds, out Element _))
        {
            Report.Info("Framework", "Cross-platform framework initialized");
        }
        else
        {
            Report.Warn("Framework", "Could not detect cross-platform framework initialization");
        }
    }
    
    private void HandlePlatformAgnosticElements()
    {
        try
        {
            // Use accessibility identifiers that work across platforms
            HandleAccessibilityBasedElements();
            
            // Handle common UI patterns
            HandleCommonUIPatterns();
            
            // Handle shared business logic
            HandleSharedBusinessLogic();
        }
        catch (Exception ex)
        {
            Report.Failure("Platform Agnostic", $"Failed: {ex.Message}");
            throw;
        }
    }
    
    private void HandleSharedBusinessLogic()
    {
        try
        {
            // Handle common business flows that work across platforms
            var searchField = FindCrossPlatformElement("search_input");
            if (searchField != null)
            {
                searchField.Click();
                searchField.PressKeys("test query");
                
                var searchButton = FindCrossPlatformElement("search_button");
                if (searchButton != null)
                {
                    searchButton.Click();
                    Thread.Sleep(3000);
                    
                    // Wait for search results
                    var resultsContainer = FindCrossPlatformElement("search_results");
                    if (resultsContainer != null)
                    {
                        Report.Success("Search", "Successfully performed search operation");
                    }
                }
            }
            
            // Handle common data entry patterns
            var dataEntryForm = FindCrossPlatformElement("data_entry_form");
            if (dataEntryForm != null)
            {
                var nameField = FindCrossPlatformElement("name_field");
                var emailField = FindCrossPlatformElement("email_field");
                var submitButton = FindCrossPlatformElement("submit_button");
                
                if (nameField != null && emailField != null && submitButton != null)
                {
                    nameField.Click();
                    nameField.PressKeys("Test User");
                    
                    emailField.Click();
                    emailField.PressKeys("test@example.com");
                    
                    submitButton.Click();
                    Report.Success("Data Entry", "Successfully completed data entry");
                }
            }
            
            Report.Info("Shared Logic", "Handled shared business logic patterns");
        }
        catch (Exception ex)
        {
            Report.Debug("Shared Logic", $"No shared business logic available: {ex.Message}");
        }
    }
    
    private void HandleAccessibilityBasedElements()
    {
        try
        {
            // Use testID/accessibilityIdentifier for cross-platform compatibility
            var usernameField = FindCrossPlatformElement("username_input");
            if (usernameField != null)
            {
                usernameField.Click();
                usernameField.PressKeys("testuser");
            }
            
            var passwordField = FindCrossPlatformElement("password_input");
            if (passwordField != null)
            {
                passwordField.Click();
                passwordField.PressKeys("password123");
            }
            
            var loginButton = FindCrossPlatformElement("login_button");
            if (loginButton != null)
            {
                loginButton.Click();
            }
            
            Report.Success("Accessibility Elements", "Successfully used accessibility-based elements");
        }
        catch (Exception ex)
        {
            Report.Failure("Accessibility Elements", $"Failed: {ex.Message}");
            throw;
        }
    }
    
    private Element FindCrossPlatformElement(string identifier)
    {
        try
        {
            // Try different platform-specific approaches
            var xpaths = new List<string>();
            
            if (_isAndroid)
            {
                xpaths.Add($"//*[@content-desc='{identifier}']");
                xpaths.Add($"//*[@resource-id='*/{identifier}']");
                xpaths.Add($"//*[@text='{identifier}']");
            }
            else
            {
                xpaths.Add($"//*[@name='{identifier}']");
                xpaths.Add($"//*[@label='{identifier}']");
                xpaths.Add($"//*[@value='{identifier}']");
            }
            
            if (Host.Local.TryFindSingle(string.Join(" | ", xpaths), 2000, out Element element))
            {
                return element;
            }
            
            return null;
        }
        catch (Exception ex)
        {
            Report.Debug("Element Search", $"Could not find element {identifier}: {ex.Message}");
            return null;
        }
    }
    
    private void HandleCommonUIPatterns()
    {
        try
        {
            // Handle navigation drawer/menu
            HandleNavigationDrawer();
            
            // Handle tab navigation
            HandleTabNavigation();
            
            // Handle list views
            HandleListViews();
            
            Report.Success("UI Patterns", "Successfully handled common UI patterns");
        }
        catch (Exception ex)
        {
            Report.Debug("UI Patterns", $"Some UI patterns not available: {ex.Message}");
        }
    }
    
    private void HandleNavigationDrawer()
    {
        try
        {
            var menuButton = FindCrossPlatformElement("menu_button");
            if (menuButton != null)
            {
                menuButton.Click();
                
                var settingsOption = FindCrossPlatformElement("settings_option");
                if (settingsOption != null)
                {
                    settingsOption.Click();
                    Report.Success("Navigation", "Successfully used navigation drawer");
                }
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Navigation Drawer", $"Could not handle navigation drawer: {ex.Message}");
        }
    }
    
    private void HandleTabNavigation()
    {
        try
        {
            var homeTab = FindCrossPlatformElement("home_tab");
            var profileTab = FindCrossPlatformElement("profile_tab");
            
            if (homeTab != null && profileTab != null)
            {
                homeTab.Click();
                profileTab.Click();
                
                Report.Success("Tab Navigation", "Successfully navigated between tabs");
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Tab Navigation", $"Could not handle tab navigation: {ex.Message}");
        }
    }
    
    private void HandleListViews()
    {
        try
        {
            var listContainer = FindCrossPlatformElement("items_list");
            if (listContainer != null)
            {
                // Find items within the list
                var listItems = listContainer.Find<Element>(".//*");
                Report.Info("List Items", $"Found {listItems.Count} items in list");
                
                if (listItems.Count > 0)
                {
                    listItems[0].Click();
                    // Wait for the next screen to appear
                    // The path should be adapted to the application's UI structure.
                    Host.Local.FindSingle<Adapter>("/form", 5000);
                    Report.Success("List Interaction", "Successfully interacted with list item");
                }
            }
        }
        catch (Exception ex)
        {
            Report.Debug("List Views", $"Could not handle list views: {ex.Message}");
        }
    }
    
    private void HandlePlatformSpecificFeatures()
    {
        try
        {
            if (_isAndroid)
            {
                HandleAndroidSpecificFeatures();
            }
            else
            {
                HandleiOSSpecificFeatures();
            }
        }
        catch (Exception ex)
        {
            Report.Debug("Platform Specific", $"Could not handle platform-specific features: {ex.Message}");
        }
    }
    
    private void HandleAndroidSpecificFeatures()
    {
        try
        {
            // Handle Android back button using actual UI navigation
            // Note: Hardware button simulation would typically be handled through 
            // external device automation tools rather than fictional Mobile APIs
            var appWindow = Host.Local.FindSingle<Form>("/form");
            if (appWindow.Exists)
            {
                // Try to find and use in-app back button
                var backButton = FindCrossPlatformElement("back_button");
                if (backButton != null)
                {
                    backButton.Click();
                    Report.Info("Android Back", "Used in-app back button");
                }
                else
                {
                    // Simulate back gesture with swipe from left edge
                    var bounds = appWindow.ScreenRectangle;
                    Touch.TouchStart(appWindow, 0, new Location(10, bounds.Height / 2));
                    Touch.TouchMove(appWindow, 0, new Location(bounds.Width / 3, bounds.Height / 2), Duration.FromMilliseconds(400));
                    Touch.TouchEnd(appWindow, 0, new Location(bounds.Width / 3, bounds.Height / 2));
                    Report.Info("Android Back", "Simulated back gesture");
                }
            }
            
            // Handle Android share intent
            var shareButton = FindCrossPlatformElement("share_button");
            if (shareButton != null)
            {
                shareButton.Click();
                Thread.Sleep(2000);
                
                // Wait for the share dialog to appear and look for share options
                var shareDialog = Host.Local.FindSingle<Container>("//container[contains(@text,'Share') or contains(@name,'Share')]", 5000);
                if (shareDialog.Exists)
                {
                    Report.Info("Android Share", "Share dialog appeared");
                    
                    // Dismiss share dialog by tapping outside or finding close button
                    var closeButton = Host.Local.FindSingle<Button>("//button[@text='Cancel' or @name='Cancel']", 3000);
                    if (closeButton.Exists)
                    {
                        closeButton.Click();
                    }
                    else if (appWindow.Exists)
                    {
                        // Tap outside to dismiss
                        Touch.Tap(appWindow, new Location(50, 50));
                    }
                }
            }
            
            Report.Success("Android Features", "Successfully handled Android-specific features");
        }
        catch (Exception ex)
        {
            Report.Debug("Android Features", $"Could not handle Android features: {ex.Message}");
        }
    }
    
    private void HandleiOSSpecificFeatures()
    {
        try
        {
            // Handle iOS navigation
            var backButton = Host.Local.FindSingle<Button>("//*[@name='Back']", 3000);
            if (backButton.Exists)
            {
                backButton.Click();
            }
            
            // Handle iOS action sheet
            var actionButton = FindCrossPlatformElement("action_button");
            if (actionButton != null)
            {
                actionButton.Click();
                
                // Wait for the action sheet to appear
                Host.Local.FindSingle<Container>("//*[@type='UIActionSheet']", 5000);
                
                // Dismiss action sheet
                var cancelButton = Host.Local.FindSingle<Button>("//*[@name='Cancel']", 3000);
                if (cancelButton.Exists)
                {
                    cancelButton.Click();
                }
            }
            
            Report.Success("iOS Features", "Successfully handled iOS-specific features");
        }
        catch (Exception ex)
        {
            Report.Debug("iOS Features", $"Could not handle iOS features: {ex.Message}");
        }
    }
}

Best Practices for Mobile Automation

1. Device Management

  • Use device farms for testing across multiple devices
  • Test on both physical devices and emulators
  • Consider different screen sizes and resolutions

2. Platform Compatibility

  • Use accessibility identifiers for cross-platform compatibility
  • Handle platform-specific UI patterns appropriately
  • Test orientation changes and device rotation

3. Performance Considerations

  • Optimize wait times for mobile network conditions
  • Handle app backgrounding and foregrounding
  • Monitor memory usage and app crashes

4. Touch and Gesture Handling

  • Use appropriate touch targets (minimum 44px)
  • Test complex gestures like pinch, swipe, and long press
  • Handle keyboard appearance and dismissal

5. Error Recovery

  • Implement retry mechanisms for network-dependent operations
  • Handle permission dialogs gracefully
  • Test offline scenarios and poor connectivity

Next Steps

For advanced mobile automation scenarios:

  1. Advanced Integration - API integration with mobile apps
  2. Web Patterns - Mobile web automation techniques
  3. Code Cookbook - Reusable mobile automation utilities

This mobile automation framework provides comprehensive coverage for iOS, Android, hybrid, and cross-platform mobile application testing scenarios.