← Back to Learning Modules

UI Test Automation

Master web and desktop application testing with modern automation frameworks

Overview

UI Test Automation involves testing the graphical user interface of applications to ensure they work as expected from a user perspective. This comprehensive guide covers web automation, desktop testing, mobile web testing, and cross-browser compatibility.

Key Concepts: Element identification, page object model, wait strategies, cross-browser testing, visual validation, and test data management.

Element Identification & Interaction

The foundation of UI automation is reliably finding and interacting with page elements:

Basic Element Operations

// Using Playwright for UI automation const { test, expect } = require('@playwright/test'); test.describe('Element Interaction Tests', () => { test('should interact with various element types', async ({ page }) => { await page.goto('https://example.com/form'); // Text input await page.fill('#username', 'testuser'); await expect(page.locator('#username')) .toHaveValue('testuser'); // Password input await page.fill('#password', 'password123'); // Dropdown selection await page.selectOption('#country', { label: 'United States' }); // Checkbox await page.check('#terms-checkbox'); await expect(page.locator('#terms-checkbox')) .toBeChecked(); // Radio button await page.check('#gender-male'); // Button click await page.click('#submit-button'); // Verify navigation await expect(page).toHaveURL(/\/dashboard/); }); test('should handle dynamic content', async ({ page }) => { await page.goto('https://example.com/dynamic'); // Wait for element to appear await page.click('#load-data-button'); await page.waitForSelector( '#data-table tbody tr', { timeout: 5000 } ); // Verify data loaded const rows = await page .locator('#data-table tbody tr') .count(); expect(rows).toBeGreaterThan(0); // Interact with dynamic element await page.click( '#data-table tbody tr:first-child .edit-button' ); // Wait for modal to appear await page.waitForSelector( '#edit-modal', { state: 'visible' } ); // Fill modal form await page.fill('#edit-modal #name', 'Updated Name'); await page.click('#edit-modal #save-button'); // Wait for modal to disappear await page.waitForSelector( '#edit-modal', { state: 'hidden' } ); }); test('should handle file uploads', async ({ page }) => { await page.goto('https://example.com/upload'); // Upload single file await page.setInputFiles( '#file-input', 'test-files/sample.pdf' ); // Upload multiple files await page.setInputFiles('#multiple-files', [ 'test-files/image1.jpg', 'test-files/image2.png' ]); await page.click('#upload-button'); // Verify upload success await expect(page.locator('.upload-success')) .toBeVisible(); }); test('should handle alerts and confirmations', async ({ page }) => { await page.goto('https://example.com/alerts'); // Handle alert dialog page.on('dialog', async dialog => { expect(dialog.type()).toBe('alert'); expect(dialog.message()) .toContain('Are you sure?'); await dialog.accept(); }); await page.click('#delete-button'); // Verify action completed await expect(page.locator('.success-message')) .toBeVisible(); }); });
// Using Selenium WebDriver with TestNG import org.openqa.selenium.*; import org.openqa.selenium.support.ui.WebDriverWait; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.Select; import org.testng.annotations.*; import static org.testng.Assert.*; public class ElementInteractionTest { private WebDriver driver; private WebDriverWait wait; @BeforeMethod public void setUp() { // Assume WebDriver is initialized wait = new WebDriverWait( driver, Duration.ofSeconds(10) ); } @Test public void testBasicElementInteractions() { driver.get("https://example.com/form"); // Text input WebElement usernameField = driver .findElement(By.id("username")); usernameField.sendKeys("testuser"); assertEquals( usernameField.getAttribute("value"), "testuser" ); // Password input driver .findElement(By.id("password")) .sendKeys("password123"); // Dropdown selection Select countryDropdown = new Select( driver.findElement(By.id("country")) ); countryDropdown.selectByVisibleText("United States"); // Checkbox WebElement termsCheckbox = driver .findElement(By.id("terms-checkbox")); termsCheckbox.click(); assertTrue(termsCheckbox.isSelected()); // Radio button driver .findElement(By.id("gender-male")) .click(); // Button click driver .findElement(By.id("submit-button")) .click(); // Verify navigation wait.until( ExpectedConditions.urlContains("/dashboard") ); assertTrue( driver.getCurrentUrl().contains("/dashboard") ); } @Test public void testDynamicContent() { driver.get("https://example.com/dynamic"); // Click to load data driver .findElement(By.id("load-data-button")) .click(); // Wait for table rows to appear wait.until( ExpectedConditions.presenceOfElementLocated( By.cssSelector("#data-table tbody tr") ) ); // Verify data loaded List<WebElement> rows = driver .findElements(By.cssSelector("#data-table tbody tr")); assertTrue(rows.size() > 0); // Click edit button on first row WebElement firstEditButton = driver.findElement( By.cssSelector( "#data-table tbody tr:first-child .edit-button" ) ); firstEditButton.click(); // Wait for modal WebElement modal = wait.until( ExpectedConditions.visibilityOfElementLocated( By.id("edit-modal") ) ); // Fill form in modal modal.findElement(By.id("name")).clear(); modal .findElement(By.id("name")) .sendKeys("Updated Name"); modal .findElement(By.id("save-button")) .click(); // Wait for modal to disappear wait.until(ExpectedConditions.invisibilityOf(modal)); } @Test public void testFileUpload() { driver.get("https://example.com/upload"); // Upload single file WebElement fileInput = driver .findElement(By.id("file-input")); fileInput.sendKeys( System.getProperty("user.dir") + "/test-files/sample.pdf" ); // Upload multiple files WebElement multipleFileInput = driver .findElement(By.id("multiple-files")); String filePaths = System.getProperty("user.dir") + "/test-files/image1.jpg\n" + System.getProperty("user.dir") + "/test-files/image2.png"; multipleFileInput.sendKeys(filePaths); driver .findElement(By.id("upload-button")) .click(); // Verify upload success WebElement successMessage = wait.until( ExpectedConditions.visibilityOfElementLocated( By.className("upload-success") ) ); assertTrue(successMessage.isDisplayed()); } @Test public void testAlertHandling() { driver.get("https://example.com/alerts"); driver .findElement(By.id("delete-button")) .click(); // Handle alert Alert alert = wait.until( ExpectedConditions.alertIsPresent() ); assertTrue(alert.getText().contains("Are you sure?")); alert.accept(); // Verify action completed WebElement successMessage = wait.until( ExpectedConditions.visibilityOfElementLocated( By.className("success-message") ) ); assertTrue(successMessage.isDisplayed()); } }
# Using Selenium WebDriver with pytest from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait, Select from selenium.webdriver.support import expected_conditions as EC import pytest class TestElementInteraction: @pytest.fixture(autouse=True) def setup(self): # Assume driver is initialized self.wait = WebDriverWait(self.driver, 10) yield def test_basic_element_interactions(self): self.driver.get("https://example.com/form") # Text input username_field = self.driver.find_element(By.ID, "username") username_field.send_keys("testuser") assert username_field.get_attribute("value") == "testuser" # Password input self.driver.find_element(By.ID, "password").send_keys("password123") # Dropdown selection country_dropdown = Select(self.driver.find_element(By.ID, "country")) country_dropdown.select_by_visible_text("United States") # Checkbox terms_checkbox = self.driver.find_element(By.ID, "terms-checkbox") terms_checkbox.click() assert terms_checkbox.is_selected() # Button click self.driver.find_element(By.ID, "submit-button").click() # Verify navigation self.wait.until(EC.url_contains("/dashboard")) assert "/dashboard" in self.driver.current_url

Page Object Model

The Page Object Model (POM) is a design pattern that creates object repositories for UI elements, making tests more maintainable and readable:

// Page Object Model in Playwright class LoginPage { constructor(page) { this.page = page; this.usernameInput = page.locator('#username'); this.passwordInput = page.locator('#password'); this.loginButton = page.locator('#login-button'); this.errorMessage = page.locator('.error-message'); this.forgotPasswordLink = page.locator('#forgot-password'); } async navigate() { await this.page.goto('/login'); } async login(username, password) { await this.usernameInput.fill(username); await this.passwordInput.fill(password); await this.loginButton.click(); } async getErrorMessage() { return await this.errorMessage.textContent(); } async isErrorVisible() { return await this.errorMessage.isVisible(); } async clickForgotPassword() { await this.forgotPasswordLink.click(); } } class DashboardPage { constructor(page) { this.page = page; this.welcomeMessage = page.locator('.welcome-message'); this.userMenu = page.locator('#user-menu'); this.logoutButton = page.locator('#logout'); this.navigationMenu = page.locator('.nav-menu'); } async isLoaded() { await this.welcomeMessage.waitFor({ state: 'visible' }); return true; } async getWelcomeText() { return await this.welcomeMessage.textContent(); } async navigateToSection(sectionName) { await this.navigationMenu .locator(`text=${sectionName}`) .click(); } async logout() { await this.userMenu.click(); await this.logoutButton.click(); } } // Using Page Objects in Tests const { test, expect } = require('@playwright/test'); test.describe('Login Tests with Page Objects', () => { let loginPage; let dashboardPage; test.beforeEach(async ({ page }) => { loginPage = new LoginPage(page); dashboardPage = new DashboardPage(page); await loginPage.navigate(); }); test('should login with valid credentials', async ({ page }) => { await loginPage.login('testuser', 'password123'); // Verify successful login await dashboardPage.isLoaded(); const welcomeText = await dashboardPage.getWelcomeText(); expect(welcomeText).toContain('Welcome, testuser'); }); test('should show error for invalid credentials', async ({ page }) => { await loginPage.login('invaliduser', 'wrongpassword'); // Verify error is shown expect(await loginPage.isErrorVisible()).toBe(true); const errorText = await loginPage.getErrorMessage(); expect(errorText).toContain('Invalid credentials'); }); test('should navigate to forgot password', async ({ page }) => { await loginPage.clickForgotPassword(); // Verify navigation await expect(page).toHaveURL(/\/forgot-password/); }); });
// Page Object Model in Java with Selenium import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; import org.openqa.selenium.support.ui.WebDriverWait; import org.openqa.selenium.support.ui.ExpectedConditions; import java.time.Duration; public class LoginPage { private WebDriver driver; private WebDriverWait wait; @FindBy(id = "username") private WebElement usernameInput; @FindBy(id = "password") private WebElement passwordInput; @FindBy(id = "login-button") private WebElement loginButton; @FindBy(className = "error-message") private WebElement errorMessage; @FindBy(id = "forgot-password") private WebElement forgotPasswordLink; public LoginPage(WebDriver driver) { this.driver = driver; this.wait = new WebDriverWait( driver, Duration.ofSeconds(10) ); PageFactory.initElements(driver, this); } public void navigate() { driver.get("/login"); } public void login(String username, String password) { usernameInput.sendKeys(username); passwordInput.sendKeys(password); loginButton.click(); } public String getErrorMessage() { wait.until( ExpectedConditions.visibilityOf(errorMessage) ); return errorMessage.getText(); } public boolean isErrorVisible() { try { wait.until( ExpectedConditions.visibilityOf(errorMessage) ); return errorMessage.isDisplayed(); } catch (Exception e) { return false; } } public void clickForgotPassword() { forgotPasswordLink.click(); } } public class DashboardPage { private WebDriver driver; private WebDriverWait wait; @FindBy(className = "welcome-message") private WebElement welcomeMessage; @FindBy(id = "user-menu") private WebElement userMenu; @FindBy(id = "logout") private WebElement logoutButton; @FindBy(className = "nav-menu") private WebElement navigationMenu; public DashboardPage(WebDriver driver) { this.driver = driver; this.wait = new WebDriverWait( driver, Duration.ofSeconds(10) ); PageFactory.initElements(driver, this); } public boolean isLoaded() { try { wait.until( ExpectedConditions.visibilityOf(welcomeMessage) ); return true; } catch (Exception e) { return false; } } public String getWelcomeText() { wait.until( ExpectedConditions.visibilityOf(welcomeMessage) ); return welcomeMessage.getText(); } public void navigateToSection(String sectionName) { WebElement section = driver.findElement( By.xpath( "//div[@class='nav-menu']//a[text()='" + sectionName + "']" ) ); section.click(); } public void logout() { userMenu.click(); wait.until( ExpectedConditions.elementToBeClickable(logoutButton) ); logoutButton.click(); } } // Using Page Objects in Tests import org.testng.annotations.*; import static org.testng.Assert.*; public class LoginTest { private WebDriver driver; private LoginPage loginPage; private DashboardPage dashboardPage; @BeforeMethod public void setUp() { // Initialize WebDriver loginPage = new LoginPage(driver); dashboardPage = new DashboardPage(driver); loginPage.navigate(); } @Test public void testValidLogin() { loginPage.login("testuser", "password123"); // Verify successful login assertTrue(dashboardPage.isLoaded()); String welcomeText = dashboardPage.getWelcomeText(); assertTrue(welcomeText.contains("Welcome, testuser")); } @Test public void testInvalidLogin() { loginPage.login("invaliduser", "wrongpassword"); // Verify error is shown assertTrue(loginPage.isErrorVisible()); String errorText = loginPage.getErrorMessage(); assertTrue(errorText.contains("Invalid credentials")); } }
# Page Object Model in Python with Selenium from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import pytest class LoginPage: def __init__(self, driver): self.driver = driver self.wait = WebDriverWait(driver, 10) # Locators self.username_input = (By.ID, "username") self.password_input = (By.ID, "password") self.login_button = (By.ID, "login-button") self.error_message = (By.CLASS_NAME, "error-message") self.forgot_password_link = (By.ID, "forgot-password") def navigate(self): self.driver.get("/login") def login(self, username, password): self.driver.find_element( *self.username_input ).send_keys(username) self.driver.find_element( *self.password_input ).send_keys(password) self.driver.find_element(*self.login_button).click() def get_error_message(self): error_element = self.wait.until( EC.visibility_of_element_located(self.error_message) ) return error_element.text def is_error_visible(self): try: self.wait.until( EC.visibility_of_element_located(self.error_message) ) return True except: return False def click_forgot_password(self): self.driver.find_element( *self.forgot_password_link ).click() class DashboardPage: def __init__(self, driver): self.driver = driver self.wait = WebDriverWait(driver, 10) # Locators self.welcome_message = (By.CLASS_NAME, "welcome-message") self.user_menu = (By.ID, "user-menu") self.logout_button = (By.ID, "logout") self.navigation_menu = (By.CLASS_NAME, "nav-menu") def is_loaded(self): try: self.wait.until( EC.visibility_of_element_located(self.welcome_message) ) return True except: return False def get_welcome_text(self): welcome_element = self.wait.until( EC.visibility_of_element_located(self.welcome_message) ) return welcome_element.text def navigate_to_section(self, section_name): section_locator = ( By.XPATH, f"//div[@class='nav-menu']//a[text()='{section_name}']" ) self.driver.find_element(*section_locator).click() def logout(self): self.driver.find_element(*self.user_menu).click() logout_element = self.wait.until( EC.element_to_be_clickable(self.logout_button) ) logout_element.click() # Usage in tests import pytest class TestLogin: @pytest.fixture(autouse=True) def setup(self): # Assume driver is initialized self.login_page = LoginPage(self.driver) self.dashboard_page = DashboardPage(self.driver) self.login_page.navigate() yield def test_valid_login(self): self.login_page.login("testuser", "password123") # Verify successful login assert self.dashboard_page.is_loaded() welcome_text = self.dashboard_page.get_welcome_text() assert "Welcome, testuser" in welcome_text def test_invalid_login(self): self.login_page.login("invaliduser", "wrongpassword") # Verify error is shown assert self.login_page.is_error_visible() error_text = self.login_page.get_error_message() assert "Invalid credentials" in error_text def test_forgot_password_navigation(self): self.login_page.click_forgot_password() # Verify navigation assert "/forgot-password" in self.driver.current_url def test_dashboard_navigation(self): self.login_page.login("testuser", "password123") # Navigate to a section self.dashboard_page.navigate_to_section("Reports") # Verify navigation worked assert "/reports" in self.driver.current_url