Answer
Handling AJAX and Dynamic Content in Cucumber
The Problem
Gherkin
When the user clicks "Search"
Then the results should show "15 products"
-- AJAX loads results asynchronously — immediate assertion fails
Solution: WebDriverWait in BasePage (not in step defs)
Java
// BasePage.java — shared wait utilities
public class BasePage {
protected WebDriver driver;
protected WebDriverWait wait;
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(15));
}
protected WebElement waitForVisible(By locator) {
return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
}
protected WebElement waitForClickable(By locator) {
return wait.until(ExpectedConditions.elementToBeClickable(locator));
}
protected void waitForTextToBe(By locator, String expectedText) {
wait.until(ExpectedConditions.textToBe(locator, expectedText));
}
protected void waitForInvisible(By locator) {
wait.until(ExpectedConditions.invisibilityOfElementLocated(locator));
}
protected void waitForAjax() {
wait.until(driver -> ((JavascriptExecutor) driver)
.executeScript("return jQuery.active").equals(0L));
}
protected void waitForPageLoad() {
wait.until(driver -> ((JavascriptExecutor) driver)
.executeScript("return document.readyState").equals("complete"));
}
protected WebElement waitForPresent(By locator) {
return wait.until(ExpectedConditions.presenceOfElementLocated(locator));
}
}
Page Object Uses Wait (Clean Step Defs)
Java
// SearchPage.java
public class SearchPage extends BasePage {
public SearchPage(WebDriver driver) {
super(driver);
}
public void search(String keyword) {
waitForClickable(By.id("searchBox")).sendKeys(keyword);
waitForClickable(By.id("searchBtn")).click();
waitForInvisible(By.css(".loading-spinner")); // wait for spinner to go away
}
public int getResultCount() {
String countText = waitForVisible(By.css(".result-count")).getText();
// "Showing 15 products" → extract 15
return Integer.parseInt(countText.replaceAll("[^0-9]", ""));
}
}
// SearchSteps.java — clean, no waits here
public class SearchSteps {
@When("the user searches for {string}")
public void search(String keyword) {
ctx.searchPage.search(keyword); // wait is inside searchPage
}
@Then("the results should show {int} products")
public void verifyResultCount(int expected) {
assertEquals(expected, ctx.searchPage.getResultCount());
}
}
Feature File stays clean
Gherkin
Scenario: Search for products
Given the user is on the product catalog page
When the user searches for "laptop"
Then the results should show 15 products
And the first result should be "Laptop Pro 15"
Common Wait Patterns in Cucumber
| Scenario | Wait to Use |
|---|---|
| After AJAX call | waitForInvisible(spinner) then waitForVisible(results) |
| After button click | waitForPageLoad() or waitForUrl() |
| After form submit | waitForVisible(successMessage) |
| Dynamic dropdown | waitForPresent(dropdownOption) |
| Toast notification | waitForVisible(toast) + waitForInvisible(toast) |
| File download | Files.waitFor(path, Duration.ofSeconds(30)) |
Never Use Thread.sleep
Java
// BAD — brittle, wastes time, causes CI flakiness
Thread.sleep(3000);
// GOOD — waits exactly as long as needed
waitForVisible(By.css(".results-container"));
