Lesson 2 of 14 ~35 min
Course progress
0%

Advanced Interactions & Dynamic Content

Handle complex user interactions and dynamic web elements

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");
    }
}
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

What causes a StaleElementReferenceException?

Element doesn't exist
Element is not visible
Element reference is outdated after DOM change
Wrong locator strategy

Which Actions method performs a right-click?

rightClick()
contextClick()
secondaryClick()
menuClick()