</>

Technology

Cucumber

Difficulty

Advanced

Interview Question

How do you handle AJAX/dynamic content and waits in Cucumber step definitions?

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

ScenarioWait to Use
After AJAX callwaitForInvisible(spinner) then waitForVisible(results)
After button clickwaitForPageLoad() or waitForUrl()
After form submitwaitForVisible(successMessage)
Dynamic dropdownwaitForPresent(dropdownOption)
Toast notificationwaitForVisible(toast) + waitForInvisible(toast)
File downloadFiles.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"));

Follow AutomateQA

Related Topics