
Clicking on a page element is easy to do, however sometimes this action might fail. This can be either because the element we want to click is not yet present on the page, or is not visible, or does not yet allow interactions. To help make sure the click succeeds, here are my dedicated wait based click methods.
Clicking a page element: click
There are 4 methods for clicking on a page element in the 'waiter2' library: 2 of them take the timeout parameter, 2 don't. Additionally, 2 of them take a WebElement (defined with @FindBy) as parameter, 2 take a By variable:
public void click(WebElement elementToClick)
public void click(WebElement elementToClick, int specificTimeout)
public void click(By selectorForElementToClick)
public void click(By selectorForElementToClick, int specificTimeout)
It is worth mentioning that there will never be a 'waitForElementToBeDisplayed' method in the library, simply because it is not needed. For example, when trying to click on a page element, there is no need to wait for it to be displayed before clicking. The logic inside the 'click' method from 'waiter2' retries clicking the element any time an Exception is encountered. If the element would not be present, the NoSuchElementException would be thrown for each click, until it either is displayed, or the timeout value has elapsed. Similarly, if the page element were present but not visible, or not clickable, the click action is tried until it succeeds or the timeout elapses. Any Exception that can be encountered while clicking is treated the same, because the generic 'Exception' is checked for and treated.
So basically, calling the click method while the element to be clicked is not yet ready, but in the process of being ready, will reliably click on it (given it loads within the specified timeout).
In case the element is not clickable for any reason within the specified timeout, a RunTimeException is thrown, with the message:
"waiter2.FAILURE: Could not successfully click on '" + elementToClick + "' within "
+ specificTimeout + " seconds."
The provided selector will be displayed, so that the tester knows what selector was looked for. This helps debug the failure.
Since version: 1.0
The code
The 'click' method variants that take a WebElement as parameter are:
public void click(WebElement elementToClick) {
click(elementToClick, TIMEOUT);
}
public void click(WebElement elementToClick, int specificTimeout) {
WebDriverWait wait = new WebDriverWait(driver,
Duration.ofSeconds(specificTimeout));
try {
ExpectedCondition<Boolean> condition = arg -> {
try {
elementToClick.click();
return true;
} catch (Exception e) {
return false;
}
};
wait.until(condition);
}
catch (TimeoutException e) {
throw new RuntimeException("waiter2.FAILURE: Could not successfully click on '" + elementToClick + "' within "
+ specificTimeout + " seconds.");
}
}
The 'click' method variants that take a By as parameter are:
public void click(By selectorForElementToClick) {
click(selectorForElementToClick, TIMEOUT);
}
public void click(By selectorForElementToClick, int specificTimeout) {
WebDriverWait wait = new WebDriverWait(driver,
Duration.ofSeconds(specificTimeout));
try {
ExpectedCondition<Boolean> condition = arg -> {
try {
driver.findElement(selectorForElementToClick).click();
return true;
} catch (Exception e) {
return false;
}
};
wait.until(condition);
}
catch (TimeoutException e) {
throw new RuntimeException("waiter2.FAILURE: Could not successfully click on '" + selectorForElementToClick + "' within "
+ specificTimeout + " seconds.");
}
}
Usage examples
The best usage for the 'click' method would be: providing a WebElement defined in a page object class with an @FindBy annotation. For example, for a page on which there is a button to click, whose id is "buttonToClick", the corresponding WebElement is:
@FindBy(id = "buttonToClick") public WebElement buttonToClick;
Don't forget that as per the setup, you need to do a PageFactory.initElements before using the WebElement. In the test, in order to click on this page element, you would just need to pass the WebElement as parameter to the 'click' method (shown here with the default timeout value):
waiter.click(page.buttonToClick);
In case you cannot create the WebElement through the @FindBy annotation, because it gets generated dynamically in the test, you can pass a By variable to the 'click' method. There are 2 ways you can do that: either by creating a By variable somewhere in the test and passing it to the method once it has the correct value. Or by creating a String variable somewhere in the test that represents the selector, then passing it to a By.yourSelectorType once it gets assigned the correct value. Below are some examples for the same button as above.
If you want to create a By variable, it would look like the following one (here obviously it represents an id):
By byForButtonToClick = By.id("buttonToClick");
You can now use it in the 'click' method:
waiter.click(byForButtonToClick);
If instead you want to create a variable only for the String representing a selector:
String selectorForButtonToClick = "buttonToClick";
Then, to click the corresponding element:
waiter.click(By.id(selectorForButtonToClick));
Note1: If the selector value is static (it is always the same), use the @FindBy approach. Only use the By approach if you cannot use the @FindBy one, due to not knowing at the beginning of the test what the selector will be (as it gets generated along the way).
Note 2: If you are using the By or the corresponding String only to click that item once, and not anywhere else in the test, DO NOT create a separate variable to store these. Just pass them inline, directly into the 'click' method, like, for example:
waiter.click(By.id("buttonToClick"));
In case the element specified with @FindBy cannot be clicked due to any reason within the specified timeout, the following RunTimeException is thrown:
java.lang.RuntimeException: waiter2.FAILURE: Could not successfully click on 'Proxy element for: DefaultElementLocator 'By.cssSelector: nonExistentElement'' within 30 seconds.
In case the element specified with By cannot be clicked on due to any reason within the specified timeout, the following RunTimeException is thrown:
java.lang.RuntimeException: waiter2.FAILURE: Could not successfully click on 'By.cssSelector: nonExistentElement' within 30 seconds.
Incorrect usage!
Never do the following:
WebElement buttonToClick = driver.findElement(By.id("buttonToClick"));
waiter.click(buttonToClick);
Why? Because if the button to click is not displayed when the 'findElement' method runs, the test fails at that step, and the 'click' method never executes.
So that's it for clicking on page elements, which of course, include more than just a button. The test code can be found here. Next up i will discuss some further useful methods, so stay tuned!