Wait for element clickable not working

Hello,

I have a dropdown that is a bit problematic when running on headless mode. The dropdown only appears after clicking in a radiobutton. After clicking in the radiobutton, I do get a loader even manually, so I inserted a Web.delay(12) (dynamic waits do not work). Here’s the code so far:

        WebUI.enhancedClick(radioBtn)
	WebUI.delay(12)
	WebUI.waitForElementClickable(dropdown, 10)
	WebUI.enhancedClick(dropdown)
	if(!WebUI.verifyElementPresent(dropdownOptions, ['line': index]), 5, FailureHandling.OPTIONAL)) {
		WebUI.delay(10)
		WebUI.takeFullPageScreenshot()
		CustomKeywords.'clickUsingJS'(dropdown, 10)
		WebUI.delay(10)
	}
	
	int options = WebUiCommonHelper.findWebElements(dropdownOptions', ['line': index]), 1).size()
	int option = CustomKeywords.'diconium.basics.selectRandomIndexFromList'(1, options)	
	WebUI.click(dropdownOption', ['line': index, 'index':option]))

The CustomKeywords.‘clickUsingJS’ has the following code:

		WebDriver driver = DriverFactory.getWebDriver()
		WebElement element = WebUiCommonHelper.findWebElement(to, timeout)
		JavascriptExecutor executor = ((driver) as JavascriptExecutor)
		executor.executeScript("arguments[0].click()",element

Though that’s irrelevant here, because I already tried another forms of clicks and the result is the same. Problem is the step to wait for the element clickable passes even after 1 second (it doesn’t wait the entire time) but the element is not clickable. I know that because of the screenshot. Here’s how the element looks:

**


**

It’s obviously not interactable. This only happens in headless, only sometimes, and more often in Firefox. What other options do I have? I’ve been around this for days and nothing fixes the issue permanently.

Hi @joana.pedroso, Have you tried one of the following: You could try a larger time to start, for example, 60 seconds.

You can read the source of waitForElementClickable keyword.

At line#84

                                    if (foundElement.isEnabled()) {
                                        return foundElement
                                    } else {
                                        return null
                                    }

As you see, this keyword is NOT really checking if the target element is clickable.

This keyword is checking if the target element is enabled.

In most cases a call to isEnabled() will return true immediately as long as the element is present.

As the Selenium Javadoc explains, isEnabled() will return false only when the target HTML element is coded with the disabled attribute. I think the disabled attribute is rarely used.

I think the keyword waitForElementClickable() should be renamed to waitForElementNotDisabled() so that the name explains what it actually does. I think this keyword is just useless. It just confuses users. I would recommend Katalon team to deprecate this keyword.

@vu.tran

So the name of this keyword has mislead @joana.pedroso . This keyword is not a friend for Joana.

1 Like

The HTML DOM spec does not define a way if an HTML element is “clickable” or not. There is no DOM Element property named “boolean isClickable()”.

Roughly speaking, every visible HTML element is always clickable. You can move the mouse over any visible HTML elements and can click the left button of the mouse over them, right? So every visible HTML elements are clickable. However, it depends case by case if a click event to the element is liased with any meaningful action (JavaScript function as event handler, etc) or not. But the waitForElementClickable keyword won’t check it.

@joana.pedroso

You can not rely on the waitForElementClickable keyword. You need to develop an alternative way to fulfill your requirement. But how?

I guess, you know how to distinguish if the target HTML element is interactable as in the sence you want. How can you distinguish it? Can you state it as a readable sentence? For example, you want to check the class attribute of the HTML element to be something like “interactable”.

If you could formulate the condition, you should be able to interprete it into a Groovy code; you should be able to implement your own custom keyword that works as you want.

1 Like

Hi @joana.pedroso, You can see the full list of builtin keywords here: katalon-studio-testing-framework/Include/scripts/groovy/com/kms/katalon/core/webui/keyword at master · katalon-studio/katalon-studio-testing-framework · GitHub

1 Like

Provided that the interactiability of the element for Joana is controlled by the value of “class” attribute, the following built-in keyword might be useful:

boolean result = WebUI.waitForElementAttributeValue(
                    findTestObject(some test object), 
                    "class", "interactable", 10)

This keyword call will wait until the value of class attribute of the specified HTML element to be exactly equal to a string “interactable”. It timeouts after 10 seconds of wait. It returns boolean value.


However, this keyword is short in many cases. Why? The class attribute of a HTML element could vary dynamically. Testing “exactly equal” is not expressive enough. For example, I want to test if the following button element to have bar class or not, but the value of "class" attribute may be variable like:

<button class="foo" ...
<button class="foo bar" ...
<button class="bar foo" ...

A single call to WebUI.waitForElementAttributeValue() keyword can not match 2 or more possible class values that contains bar.

Ideally I want a pair of keywords:

  • WebUI.waitForElementHasClass(TestObject, String className, int timeout, FailureHandling) and
  • WebUI.waitForElementNotHasClass(...)

As you all know, jQuery has hasClass function which is very helpful to work with dynamic HTMLs. It is a shame that Katalon doesn’t provide any built-in keyword equivalent to jQuery’s hasClass(). I suppose that Katalon keywords were designed ages ago before jQuery-era.

The waitForElementHasClass keyword can be implemented as a slight modification of WebUI.waitForElementAttributeValue() keyword line#90:

It uses a simple equality test by ==. You just want to replace it with a bit sophisticated Groovy code snippet. The following Test Case shows a demo how to implement hasClass test:

import org.openqa.selenium.WebElement

import com.kms.katalon.core.testobject.ConditionType
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

TestObject makeTestObject(String xpath) {
	TestObject tObj = new TestObject(xpath)
	tObj.addProperty("xpath", ConditionType.EQUALS, xpath)
	return tObj
}

boolean hasClass(WebElement we, String className) {
	String classValue = we.getAttribute("class")
	if (classValue != null) {
		return (classValue.split(" ") as List).contains(className)
	} else {
		return false
	}
}

WebUI.openBrowser("")
WebUI.navigateToUrl("https://katalon-demo-cura.herokuapp.com/profile.php#login")
TestObject TO_menuToggle = makeTestObject("//a[@id='menu-toggle']")
WebUI.verifyElementPresent(TO_menuToggle, 10) 

// <a id="menu-toggle" href="#" class="btn btn-dark btn-lg toggle"><i class="fa fa-bars"></i></a>
WebElement we = WebUI.findWebElement(TO_menuToggle)
WebUI.comment("the element has class 'foo': ${hasClass(we, 'foo')}")
WebUI.comment("the element has class 'btn': ${hasClass(we, 'btn')}")
WebUI.comment("the element has class 'btn-dark': ${hasClass(we, 'btn-dark')}")
WebUI.comment("the element has class 'toggle': ${hasClass(we, 'toggle')}")

WebUI.closeBrowser()

I think that waitForElementHasClass keyword should be provided built-in. It would be far more useful than the existing waitForElementClickable.

@vu.tran


Alternatively, @joana.pedroso can repeat calling WebUI.wait for ElementAttributeValue() multiple times to cover all possible values of the class attribute, as follows:

boolean b1 = WebUI.verifyElementAttributeValue(findTestObject("id of the button"),
                                               "class", "foo bar", 10)
boolean b2 = WebUI.verifyElementAttributeValue(findTestObject("id of the button"), 
                                               "class", "bar foo", 10)
if (b1 || b2) {
    println "bar class was found"
} else {
    println "bar class was not found"
}

This will work, though very slowly. How silly this code looks! :stuck_out_tongue:

1 Like

Hi,

Thank you for your suggestion. I have raised the internal ticket for this feature request. Our team will consider and discuss on it and I will back to you with the update soon. Thank you!

1 Like

This is an excellent idea, I would have to check what are the values for the class when the element is interactable and not interactable to see if there’s some difference. I was trying to avoid using WebUI.delay() and instead use a dynamic wait approach, so this is perfect.

Thank you!

1 Like