Advanced User Interactions
Master complex interactions using Actions class and handling dynamic content.
Actions Class
The Actions class provides advanced user gestures:
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.WebElement;
public class AdvancedInteractions {
private WebDriver driver;
private Actions actions;
public void setUp() {
driver = new ChromeDriver();
actions = new Actions(driver);
}
// Mouse hover
public void hoverOverElement(WebElement element) {
actions.moveToElement(element).perform();
}
// Click and hold
public void clickAndHold(WebElement element) {
actions.clickAndHold(element).perform();
}
// Double click
public void doubleClick(WebElement element) {
actions.doubleClick(element).perform();
}
// Right click (context menu)
public void rightClick(WebElement element) {
actions.contextClick(element).perform();
}
// Drag and drop
public void dragAndDrop(WebElement source, WebElement target) {
actions.dragAndDrop(source, target).perform();
}
// Drag and drop by offset
public void dragByOffset(WebElement element, int xOffset, int yOffset) {
actions.dragAndDropBy(element, xOffset, yOffset).perform();
}
}
Complex Action Chains
public class ActionChains {
@Test
public void testComplexActions() {
WebDriver driver = new ChromeDriver();
Actions actions = new Actions(driver);
driver.get("https://example.com");
WebElement menu = driver.findElement(By.id("menu"));
WebElement submenu = driver.findElement(By.id("submenu"));
// Chain multiple actions
actions
.moveToElement(menu)
.pause(Duration.ofSeconds(1))
.moveToElement(submenu)
.click()
.build()
.perform();
}
@Test
public void testSliderInteraction() {
WebElement slider = driver.findElement(By.id("slider"));
// Move slider by offset
actions
.clickAndHold(slider)
.moveByOffset(50, 0)
.release()
.perform();
}
@Test
public void testFileUpload() {
WebElement uploadButton = driver.findElement(By.id("upload"));
// Send file path
uploadButton.sendKeys("/path/to/file.pdf");
// Alternative with Robot class
uploadButton.click();
// Use Robot for native OS dialogs
Robot robot = new Robot();
StringSelection selection = new StringSelection("/path/to/file.pdf");
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null);
robot.keyPress(KeyEvent.VK_CONTROL);
robot.keyPress(KeyEvent.VK_V);
robot.keyRelease(KeyEvent.VK_V);
robot.keyRelease(KeyEvent.VK_CONTROL);
robot.keyPress(KeyEvent.VK_ENTER);
robot.keyRelease(KeyEvent.VK_ENTER);
}
}
Handling Dynamic Content
Explicit Waits with Custom Conditions
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
public class DynamicContentHandler {
@Test
public void waitForElementToAppear() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// Wait for visibility
WebElement element = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.id("dynamic"))
);
// Wait for clickability
WebElement button = wait.until(
ExpectedConditions.elementToBeClickable(By.id("submit"))
);
// Wait for text to be present
wait.until(
ExpectedConditions.textToBePresentInElementLocated(
By.id("message"), "Success"
)
);
// Wait for element to be invisible
wait.until(
ExpectedConditions.invisibilityOfElementLocated(By.id("loading"))
);
// Wait for attribute value
wait.until(
ExpectedConditions.attributeContains(
By.id("status"), "class", "active"
)
);
}
@Test
public void customWaitCondition() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// Custom condition - wait for element count
wait.until((WebDriver d) -> {
List<WebElement> elements = d.findElements(By.className("item"));
return elements.size() >= 5;
});
// Custom condition - wait for title
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return driver.getTitle().toLowerCase().contains("expected");
}
});
}
}
Fluent Wait for Complex Scenarios
import org.openqa.selenium.support.ui.FluentWait;
public class FluentWaitExample {
@Test
public void fluentWaitWithPolling() {
FluentWait<WebDriver> wait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofMillis(500))
.ignoring(NoSuchElementException.class)
.ignoring(StaleElementReferenceException.class);
WebElement element = wait.until(driver -> {
WebElement el = driver.findElement(By.id("dynamic"));
if (el.isDisplayed() && el.isEnabled()) {
return el;
}
return null;
});
element.click();
}
@Test
public void waitForAjaxComplete() {
FluentWait<WebDriver> wait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(20))
.pollingEvery(Duration.ofMillis(250));
// Wait for jQuery Ajax to complete
wait.until(driver -> {
JavascriptExecutor js = (JavascriptExecutor) driver;
return (Boolean) js.executeScript("return jQuery.active == 0");
});
}
@Test
public void waitForAngularLoad() {
FluentWait<WebDriver> wait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(15))
.pollingEvery(Duration.ofMillis(200));
// Wait for Angular to finish loading
wait.until(driver -> {
JavascriptExecutor js = (JavascriptExecutor) driver;
return js.executeScript(
"return window.getAllAngularTestabilities()" +
".findIndex(x => !x.isStable()) === -1"
).equals(true);
});
}
}
Handling Stale Elements
public class StaleElementHandler {
@Test
public void handleStaleElement() {
WebElement element = driver.findElement(By.id("dynamic"));
// Page refresh or DOM manipulation
driver.navigate().refresh();
// Element is now stale - will throw StaleElementReferenceException
// element.click(); // ❌ This will fail
// Re-find element
element = driver.findElement(By.id("dynamic"));
element.click(); // ✅ This works
}
public WebElement getStaleProofElement(By locator) {
int attempts = 0;
while (attempts < 3) {
try {
WebElement element = driver.findElement(locator);
element.isDisplayed(); // Trigger stale check
return element;
} catch (StaleElementReferenceException e) {
attempts++;
}
}
throw new RuntimeException("Element is stale after 3 attempts");
}
public void clickWithRetry(By locator) {
int attempts = 0;
while (attempts < 3) {
try {
driver.findElement(locator).click();
return;
} catch (StaleElementReferenceException e) {
attempts++;
}
}
throw new RuntimeException("Could not click element after 3 attempts");
}
}
Shadow DOM Handling
public class ShadowDOMHandler {
@Test
public void accessShadowDOM() {
driver.get("https://example.com/shadow-dom");
// Find shadow host
WebElement shadowHost = driver.findElement(By.id("shadow-host"));
// Access shadow root
JavascriptExecutor js = (JavascriptExecutor) driver;
WebElement shadowRoot = (WebElement) js.executeScript(
"return arguments[0].shadowRoot", shadowHost
);
// Find element inside shadow DOM
WebElement shadowElement = shadowRoot.findElement(By.cssSelector(".shadow-content"));
shadowElement.click();
}
@Test
public void nestedShadowDOM() {
// First shadow root
WebElement host1 = driver.findElement(By.id("host1"));
WebElement shadow1 = (WebElement) ((JavascriptExecutor) driver)
.executeScript("return arguments[0].shadowRoot", host1);
// Second shadow root inside first
WebElement host2 = shadow1.findElement(By.id("host2"));
WebElement shadow2 = (WebElement) ((JavascriptExecutor) driver)
.executeScript("return arguments[0].shadowRoot", host2);
// Element inside nested shadow
WebElement element = shadow2.findElement(By.cssSelector(".nested"));
element.click();
}
}
Infinite Scroll Handling
public class InfiniteScrollHandler {
@Test
public void scrollToBottom() {
JavascriptExecutor js = (JavascriptExecutor) driver;
long lastHeight = (Long) js.executeScript("return document.body.scrollHeight");
while (true) {
// Scroll to bottom
js.executeScript("window.scrollTo(0, document.body.scrollHeight);");
// Wait for new content to load
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Calculate new height
long newHeight = (Long) js.executeScript("return document.body.scrollHeight");
// Break if no more content
if (newHeight == lastHeight) {
break;
}
lastHeight = newHeight;
}
}
@Test
public void lazyLoadImages() {
JavascriptExecutor js = (JavascriptExecutor) driver;
List<WebElement> images = driver.findElements(By.tagName("img"));
for (WebElement img : images) {
// Scroll to image to trigger lazy load
js.executeScript("arguments[0].scrollIntoView(true);", img);
// Wait for image to load
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
wait.until(driver -> {
String src = img.getAttribute("src");
return src != null && !src.isEmpty();
});
}
}
}
Web Table Handling
public class WebTableHandler {
@Test
public void readTableData() {
WebElement table = driver.findElement(By.id("dataTable"));
// Get all rows
List<WebElement> rows = table.findElements(By.tagName("tr"));
// Iterate through rows
for (WebElement row : rows) {
List<WebElement> cells = row.findElements(By.tagName("td"));
for (WebElement cell : cells) {
System.out.print(cell.getText() + "\t");
}
System.out.println();
}
}
@Test
public void findCellByText() {
String searchText = "John Doe";
List<WebElement> rows = driver.findElements(By.xpath("//table[@id='users']//tr"));
for (WebElement row : rows) {
if (row.getText().contains(searchText)) {
List<WebElement> cells = row.findElements(By.tagName("td"));
String email = cells.get(2).getText(); // 3rd column
System.out.println("Email: " + email);
break;
}
}
}
@Test
public void sortTable() {
// Click sort header
WebElement header = driver.findElement(By.xpath("//th[text()='Name']"));
header.click();
// Wait for sorting to complete
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
wait.until(ExpectedConditions.attributeContains(header, "class", "sorted"));
// Verify sorting
List<WebElement> names = driver.findElements(By.xpath("//table//td[1]"));
List<String> actualOrder = names.stream()
.map(WebElement::getText)
.collect(Collectors.toList());
List<String> expectedOrder = new ArrayList<>(actualOrder);
Collections.sort(expectedOrder);
Assert.assertEquals(actualOrder, expectedOrder, "Table should be sorted");
}
}
Cookie Management
import org.openqa.selenium.Cookie;
public class CookieHandler {
@Test
public void manageCookies() {
driver.get("https://example.com");
// Add cookie
Cookie cookie = new Cookie("test", "value");
driver.manage().addCookie(cookie);
// Get cookie
Cookie retrieved = driver.manage().getCookieNamed("test");
System.out.println("Cookie value: " + retrieved.getValue());
// Get all cookies
Set<Cookie> allCookies = driver.manage().getCookies();
for (Cookie c : allCookies) {
System.out.println(c.getName() + " = " + c.getValue());
}
// Delete cookie
driver.manage().deleteCookieNamed("test");
// Delete all cookies
driver.manage().deleteAllCookies();
}
@Test
public void bypassLoginWithCookies() {
// Login normally first time
driver.get("https://example.com/login");
driver.findElement(By.id("username")).sendKeys("user");
driver.findElement(By.id("password")).sendKeys("pass");
driver.findElement(By.id("submit")).click();
// Save session cookies
Set<Cookie> cookies = driver.manage().getCookies();
// Close and reopen browser
driver.quit();
driver = new ChromeDriver();
// Restore cookies
driver.get("https://example.com");
for (Cookie cookie : cookies) {
driver.manage().addCookie(cookie);
}
// Refresh to apply cookies
driver.navigate().refresh();
// Should be logged in
}
}
Key Takeaways
✅ Actions class handles complex user gestures
✅ Explicit waits handle dynamic content reliably
✅ Stale elements require re-finding after DOM changes
✅ JavaScript execution enables advanced scenarios