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