You should use WebUI.enhancedClick instead of WebUI.click.
WebUI.enchancedClick internally always waits for the target element.
See the following source code of the WebUI.enhancedClick keyword:
package com.kms.katalon.core.webui.keyword.builtin
import java.text.MessageFormat
import java.time.Duration
import org.openqa.selenium.JavascriptExecutor
import org.openqa.selenium.WebDriver
import org.openqa.selenium.WebElement
import org.openqa.selenium.interactions.Actions
import org.openqa.selenium.support.ui.ExpectedConditions
import org.openqa.selenium.support.ui.WebDriverWait
import com.kms.katalon.core.annotation.internal.Action
import com.kms.katalon.core.configuration.RunConfiguration
import com.kms.katalon.core.exception.StepFailedException
import com.kms.katalon.core.helper.KeywordHelper
import com.kms.katalon.core.keyword.internal.SupportLevel
import com.kms.katalon.core.model.FailureHandling
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.trymonad.Try
import com.kms.katalon.core.webui.common.WebUiCommonHelper
import com.kms.katalon.core.webui.constants.StringConstants
import com.kms.katalon.core.webui.driver.DriverFactory
import com.kms.katalon.core.webui.keyword.internal.WebUIAbstractKeyword
import com.kms.katalon.core.webui.keyword.internal.WebUIKeywordMain
import groovy.transform.CompileStatic
@Action(value = "enhancedClick")
class EnhancedClickKeyword extends WebUIAbstractKeyword {
@CompileStatic
@Override
public SupportLevel getSupportLevel(Object ...params) {
return super.getSupportLevel(params)
}
@CompileStatic
@Override
public Object execute(Object ...params) {
TestObject to = getTestObject(params[0])
FailureHandling flowControl = (FailureHandling)(params.length > 1 && params[1] instanceof FailureHandling ? params[1] : RunConfiguration.getDefaultFailureHandling())
click(to,flowControl)
}
private void scrollToElement(WebDriver webDriver, WebElement webElement) {
try {
Actions builder = new Actions(webDriver);
builder.moveToElement(webElement);
builder.build().perform();
} catch(Exception e) {
logger.logError(e.getMessage());
}
try {
((JavascriptExecutor) webDriver).executeScript("arguments[0].scrollIntoView(true);", webElement);
} catch(Exception e) {
logger.logError(e.getMessage());
}
}
@CompileStatic
public void click(TestObject to, FailureHandling flowControl) throws StepFailedException {
WebDriver driver = DriverFactory.getWebDriver();
int timeoutInSeconds = RunConfiguration.getElementTimeoutForWeb()
long timeoutInMillis = RunConfiguration.getElementTimeoutForWebInMillis()
WebUIKeywordMain.runKeywordUntilTimeout({
boolean isSwitchIntoFrame = false
try {
WebUiCommonHelper.checkTestObjectParameter(to)
isSwitchIntoFrame = WebUiCommonHelper.switchToParentFrame(driver, to)
WebElement webElement = WebUIAbstractKeyword.findWebElement(driver, to, timeoutInMillis)
logger.logDebug(MessageFormat.format(StringConstants.KW_LOG_INFO_CLICKING_ON_OBJ, to.getObjectId()))
Try.ofFailable({
logger.logDebug("Trying Selenium click !");
webElement.click();
return Boolean.TRUE;
}).orElseTry({
logger.logDebug("Trying to scroll to the element, wait for it to be clickable and use Selenium click !");
scrollToElement(driver, webElement);
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(timeoutInSeconds));
webElement = wait.until(ExpectedConditions.elementToBeClickable(webElement));
webElement.click();
return Boolean.TRUE;
}).orElseTry({
logger.logDebug("Trying Javascript click !");
JavascriptExecutor executor = (JavascriptExecutor) driver;
executor.executeScript("arguments[0].click();", webElement);
return Boolean.TRUE;
}).onSuccess({
logger.logPassed(MessageFormat.format(StringConstants.KW_LOG_PASSED_OBJ_CLICKED, to.getObjectId()))
}).get();
} finally {
if (isSwitchIntoFrame) {
WebUiCommonHelper.switchToDefaultContent(driver)
}
}
}, flowControl, RunConfiguration.getTakeScreenshotOption(), (to != null) ? MessageFormat.format(StringConstants.KW_MSG_CANNOT_CLICK_ON_OBJ_X, to.getObjectId())
: StringConstants.KW_MSG_CANNOT_CLICK_ON_OBJ)
}
}
Especially, please find the statement:
...
@CompileStatic
public void click(TestObject to, FailureHandling flowControl) throws StepFailedException {
...
// built-in wait here!
WebElement webElement = WebUIAbstractKeyword.findWebElement(driver, to, timeoutInMillis)
As you see in the source, the keyword always waits for the target element to be present in the DOM of the page. Also the keyword does check the clickability of the target element. In most cases, this wait strategy works.
I think that the problem of WebUI.enhancedClick keyword is its poor documentation. The doc writes:
Click on the given element using various trial-and-error methods.
Well, how poor the doc is! It explains nothing. Nobody would trust it unless appropriately documented.