[Sharing is learning] [Get rewards] Join Us Now to Become Katalon Champions. Learn more
Patrik
Katalon Apprentice
07/11/2018
edited July 11

Implement WebUI.waitForElementText for non static Web Services

We have a Vaadin based web service and HTML pages are non static.
E.g. changing some selection will cause a replacement of DOM instead of a "page reload".

There are HTML tags like this where the element attributes stay the same, only the text part will be changed:
Before clicking on web page:
<div class="v-label v-widget register-contentbox-heading 
v-label-register-contentbox-heading v-has-width" 
id="lbl_contentbox_heading" style="width: 361px;">This is the FIRST text</div>

After changing some selection on page the text will be relaced by server:
<div class="v-label v-widget register-contentbox-heading 
v-label-register-contentbox-heading v-has-width" 
id="lbl_contentbox_heading" style="width: 361px;">This is the SECOND text I 
want to wait for</div>

Those available Keywords do not work:
  1. WebUI.waitForPageLoad(5) -- is not usable, as the browser does not initially load the page
  2. WebUI.waitForElementVisible(findTestObject('lbl_element_om_page'), 5) -- this one waits until the element is visible. But the element was already visible before, too
  3. WebUI.verifyElementText(findTestObject('lbl_element_om_page'), 'This is the SECOND text I want to wait for') -- The element is already present, but still with old text value 'This is the FIRST text'
  4. WebUI.waitForElementAttributeValue() -- This is not usable, because the Element does not have a 'text' attribute

That's why I would like to request a new build-in "WebUI.waitForElementText()" with timeout and selection of failure handling.

Currently I have created my own CustomKeyword to search in complete HTML body for a given text fragment with a timeout condition:
CustomKeywords.'guiResources.guiSupportFunctions.waitForTextDisplayed'('This is the SECOND text I want to wait for' , 5, FailureHandling.CONTINUE_ON_FAILURE)
The corresponding CustomKeyword  code looks like this in my file guiSupportFunctions.groovy

package guiResources

import org.openqa.selenium.WebDriver
import com.kms.katalon.core.webui.driver.DriverFactory
import org.openqa.selenium.By

class guiSupportFunctions {

    private static final KeywordLogger LOG = new KeywordLogger();

    /**
     * Wait until the given text is visible on page
     *
     * @param input
     *            Der String, auf dessen Anzeige gewartet wird
     * @param timeout
     *            timeout value, wie lange gewartet wird
     * @param failureHandling
     *            failureHandling to control the test step result
     * @return true, if "input" is found within the given timeout value,
     *            else return false
     */

    @Keyword
    public static boolean waitForTextDisplayed(String input) {
        return waitForTextDisplayed(input, 5, FailureHandling.STOP_ON_FAILURE)
    }

    @Keyword
    public static boolean waitForTextDisplayed(String input, Integer timeout) {
        return waitForTextDisplayed(input, timeout, FailureHandling.STOP_ON_FAILURE)
    }

    @Keyword
    public static boolean waitForTextDisplayed(String input, Integer timeout, FailureHandling failureHandling) {
        WebDriver driver = DriverFactory.getWebDriver()
        long startWaitTime = java.util.Calendar.getInstance().getTimeInMillis();
        long elapsedTime = 0;
        long timeoutms = timeout * 1000;

        LOG.logInfo("waitForTextDisplayed('" + input + "') - wait for max " + (timeoutms / 1000) + " secs");

        while (!isTextPresentOnPage(driver, input) && (elapsedTime < timeoutms)) {
            try {
                Thread.sleep(100);
                elapsedTime = java.util.Calendar.getInstance().getTimeInMillis() - startWaitTime;
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            (!isTextPresentOnPage(driver, input) && (elapsedTime < timeoutms))
        }
        elapsedTime = java.util.Calendar.getInstance().getTimeInMillis() - startWaitTime;
        LOG.logInfo("elapsed time: " + (elapsedTime / 1000) + "s");
        boolean found = isTextPresentOnPage(driver, input);

        // For negativ tests it possible *not* to find the given text
        if (!found) {
            LOG.logInfo("Did wait for " + (timeoutms / 1000) + " seconds for text '" + input
                    + "' to be displayed - without success. Giving up.");
            if ( failureHandling == FailureHandling.STOP_ON_FAILURE) {
                throw new StepFailedException("Did wait for " + (timeoutms / 1000) + " seconds for text '" + input
                + "' to be displayed - without success. Giving up.");
            } else {
                KeywordUtil.markFailed("Did wait for " + (timeoutms / 1000) + " seconds for text '" + input
                        + "' to be displayed - without success. Giving up.")
            }
            return false;
        }
        LOG.logInfo("Text is displayed. Continue with test.");
        return true;
    }

    /**
     * Check if given text is present directly in html-body
     *
     * @param text
     *            The text to be find
     * @return true, if "text" is visible/found,
     *            else false
     */
    private static boolean isTextPresentOnPage(WebDriver driver, String text) {
        String bodyText = "";
        try {
            bodyText = driver.findElement(By.tagName("body")).getText();
        } catch (Exception e) {
            LOG.logInfo("isTextPresentOnPage(): Giving up. Could not read body text because of Exception: " + e.getLocalizedMessage());
            KeywordUtil.markFailed("isTextPresentOnPage(): Giving up. Could not read body text because of Exception: " + e.getLocalizedMessage());
            return false;
        }
        return bodyText.contains(text.trim());
    }
}


Upvote
Quote

Comments

  • Russ Thomas
    Katalon Evangelist
    07/11/2018
    Hi Patrik

    FWIW, here is my jQuery/JavaScript version:

      /**
    * Makes multiple attempts to verify the element identified by <code>selector</code> has
    * innerText <code>expected</code>.
    * @param selector (String) CSS selector to be checked.
    * @param expected (String) The expected text.
    * @param isRegex (boolean) Optional. Whether <code>expected</code> is to be treated as a regex.
    * @param timeoutSeconds (int) Optional. How long to wait in seconds (default 60).
    */
    static void jQ_waitText(String selector, String expected, boolean isRegex = false, int timeoutSeconds = 60) {
    comment('jQ_waitText checking innerText on ' + selector)
    boolean match = false
    def result
    String js = '''
    var re, selector = arguments[0],
    expected = arguments[1],
    text = $(selector).text(),
    isRegex = arguments[2];
    if(isRegex) {
    re = new RegExp(expected);
    return {
    match: re.test(text),
    actual: text
    }
    }
    return {
    match: text.toString() === expected.toString(),
    actual: text.toString()
    }
    '''
    int count = 0;
    while (!match) {
    count++
    WebUI.comment('jQ_waitText checking innerText on ' + selector + ' ' + count)
    if(count > timeoutSeconds) {
    markFailed('jQ_waitText "' + selector + '" is "' + result["actual"] + '" which does not match "' + expected + '" (timeout).')
    return
    }

    try {
    result = jsexec(js, Arrays.asList(selector, expected, isRegex))
    match = result["match"]
    } catch (Exception e) {
    match = false
    }

    if(match) {
    break
    }
    WebUI.delay(1)
    }
    markPassed('Success: ' + selector + ' text is ' + expected + '!')
    }

    Where:
    * jsexec is a wrapper over WebUI.executeJavaScript
    * markPassed and markFailed are wrappers over KeywordUtil.markFailed and KeywordUtil.markFailed.




    Upvote
    Quote
Sign In or Register to comment.