My test is "too fast" and clicks buttons before the page is ready!

The problem is, sometimes the page takes a few seconds to fetch data, and even though I can see the button, it’s like it’s not “active” yet. Katalon either throws a Step Failed error saying it can’t find the element, or it clicks it but nothing happens because the background scripts haven’t finished loading.

I tried to fix it by putting WebUI.delay(5) everywhere, which works sometimes, but it makes my tests so slow! And if the internet is slightly slower one day, even 5 seconds isn’t enough. Here is the part of the page that is causing the headache:

HTML

<form id="data-entry">
  <div id="loading-spinner" class="visible">Loading form data...</div>
  
  <button id="submit-btn" type="submit">Submit</button>
</form>

How do I make Katalon “wait” intelligently instead of just guessing with delays?

You need to wait for the button to be clickable for appropriate n-seconds before your script calls WebUI.click.
There are several ways to do it.
One approach would be using WebUI.enhancedClick

Avoid using WebUI.delay()

Using hard waits like WebUI.delay() is not recommended because they make tests slower and flaky. Instead, always prefer condition‑based waits that react to the actual state of the UI.

Other approaches

1. Wait until the element is clickable

Use this when the element exists in the DOM but is not yet ready for interaction:

WebUI.waitForElementClickable(findTestObject('Your Test Object'), 30)

2. Use enhancedClick

This can help in cases where a normal click fails due to overlays or timing issues. Use it sparingly, as it may hide real UI problems.

WebUI.enhancedClick(findTestObject('Your Test Object'))

3. Wait until the element is enabled

This works only if the application correctly uses the disabled attribute.

WebUI.waitForElementNotHasAttribute(findTestObject('Your Test Object'), 'disabled',60)

4. Wait for the loading spinner to disappear

If your application shows a spinner or loader during background processing, waiting for it to disappear is often the most reliable approach. (I have done this in my current project and reduced flaky tests drastically)

@Keyword

def waitForLoaderToDisappear() {

    // If spinner is hidden but still in DOM

    WebUI.waitForElementNotVisible(findTestObject('Object Repository/.../loading_spinner'),60)



    // OR if spinner is removed from DOM entirely

    WebUI.waitForElementNotPresent(findTestObject('Object Repository/.../loading_spinner'), 60)

}

Then use in your tests

CustomKeywords.'your.package.waitForLoaderToDisappear'()

WebUI.click(findTestObject('Your Test Object'))

If you want to understand the basic reason of your failure then read below para, if you are not interested to read then simply use some implicit wait commands

Katalon executes UI actions as soon as Selenium detects an element in the DOM. In modern web applications, elements often appear in the DOM before the UI has fully stabilized—for example, while JavaScript rendering, animations, or background network calls are still in progress. Although the element technically exists, it may not yet be ready for user interaction.

Because of this, Katalon may attempt to click elements too early, which leads to intermittent failures such as element not interactable, stale element references, or click interception. These failures are not functional bugs—they are synchronization issues caused by UI timing.

Some modern automation frameworks or tool like Playwright handle this internally by waiting until the page and the target element reach an interactive and stable state before performing actions. Katalon, on the other hand, gives more control to the test engineer, which means synchronization must be handled explicitly.

To mitigate this in Katalon, we intentionally add buffering through explicit wait keywords such as waiting for visibility or clickability rather than just element presence. This ensures that actions are executed only when the UI is fully ready, improving test stability and reducing false failures.

So the difference is not capability, but default behavior and how much synchronization is automated versus controlled by the test design.

Use some of the below keywords in katalon to deal your situations

  1. verifyElementPresent is not ideal for synchronization
  2. waitForElementPresent, waitForElementVisible, waitForElementClickable are better

Wait for spinner invisible + button clickable—flakiness killer for async loads.

Best Fix

WebUI.waitForElementNotVisible(findTestObject('Page/loading_spinner'), 30)  // Spinner gone
WebUI.waitForElementClickable(findTestObject('Page/submit-btn'), 10)       // Button ready
WebUI.click(findTestObject('Page/submit-btn'))

Enhanced: WebUI.enhancedClick(to) (7.2.5+) auto-waits/retries.

Custom Loader Wait:

@Keyword
void waitForDataReady() {
    WebUI.waitForElementNotPresent(findTestObject('loading_spinner'), 60)
    WebUI.waitForPageLoad(10)
}

Use before every interaction—tests speed up 2x, no guesswork!

you need to go slow with your tests

See the following post where I quoted the source of WebUI.enhancedClick keyword.

If you read the source, you would be able to understand what the keyword internally does.

What you are describing is a Race Condition, a common pitfall in test automation. Using WebUI.delay() (Hard Sleeps) is considered an “anti-pattern” because it leads to “brittle” and inefficient tests.

An architect’s approach is to use Conditional Synchronization. We don’t wait for a fixed amount of time; we wait for a specific State. In your case, you need to wait for the “Loading Spinner” to vanish or for the button to become “Clickable.”

The Solution: Smart Wait Patterns

Katalon has built-in “Wait” keywords that are much more efficient than delays. You should replace your delays with a two-step verification process.

  1. Wait for Element to be Present: Ensures the HTML exists.

  2. Wait for Element to be Clickable: Ensures the JavaScript has finished binding to the button.

Custom Keyword: The “Ready State” Waiter

For professional-grade scripts, we often create a keyword that waits for the entire browser’s “Document Ready State” or for a specific “Invisibility” of a loader. Mine was quite complex i made subset it will work for simple workflow if you need one with more complexity you can overload these functiond depending on need

import com.kms.katalon.core.annotation.Keyword

import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

import com.kms.katalon.core.testobject.TestObject

public class MySyncHelper {

    @Keyword

    def waitAndClick(TestObject buttonObject, TestObject loaderObject, int timeout = 10) {

        // 1. Wait for the loading spinner to disappear first

        WebUI.waitForElementNotPresent(loaderObject, timeout)

       

        // 2. Wait for the button to be ready for interaction

        WebUI.waitForElementClickable(buttonObject, timeout)

        

        // 3. Perform the click

        WebUI.click(buttonObject)

        

        WebUI.comment("Successfully synchronized and clicked the button.")

    }

}


Strategic Advice:

  • The Golden Rule: Never use WebUI.delay(). Always use WebUI.waitForElementVisible() or WebUI.waitForElementClickable().

  • Project Settings: Go to Project > Settings > Execution and increase the Default wait for element timeout. This acts as a global safety net.

By moving to this event-driven waiting logic, your tests will run as fast as the application allows, but never faster.

Are you seeing a specific “Loading” spinner on your page, or does the button just sit there in a disabled state until the data arrives?

Wait for Element to be present or clickable, Static wait is not appeciated!

hi @fmcclure

Wait for the spinner to disappear, then wait for the button to be clickable.

WebUI.waitForElementNotVisible(findTestObject('Page/loading-spinner'), 30)
WebUI.waitForElementClickable(findTestObject('Page/submit-btn'), 10)
WebUI.click(findTestObject('Page/submit-btn'))

Use waitForElementNotPresent instead if the spinner gets removed from the DOM entirely rather than just hidden.