Explicit Waits

You can set timeout values in UTAM for implicit and explicit waits for Java and JavaScript.

Implicit and Explicit Timeouts

An implicit timeout is a time duration used to find an element inside its parent element or inside the driver for a root page object.

Note: Because we feel strongly that implicit timeout should be zero, as of Autumn 2021 (exact version to be specified), implicit timeouts in UTAM are set to 0. Although we can't prevent usage of implicit timeouts and we still support the ability to override them, we strongly discourage their usage.

An explicit wait timeout is used for any waitFor method inside UTAM framework, for example waitFor is used inside compose and beforeLoad in JSON, or can be invoked on a page object in a test. Explicit timeout duration depends on many factors such as application performance and network access to the environment where tests are being run. It should be set to the maximum reasonable duration for which a test should wait for the server to respond and for the browser to finish rendering the page. It can be as short as 5000 ms and as long as 90 seconds.

If timeouts aren’t set, the default for implicit timeouts is 0 and explicit timeouts is 20 seconds.

For more information on how implicit and explicit timeouts affect UI tests, see:

Many page objects and UI tests rely on large implicit timeout values to find an element that appears as the result of a user action. This approach can be problematic and can often lead to flappy tests due to changes in the testing environment or network delays. Instead, write more durable tests by waiting for certain actions to complete and yield until the conditions are met. Instead of waiting for an implicit time, wait for a condition to be ready (explicit wait) for the test to proceed.

Here are some examples of using explicit waits.

Wait for DOM Complete Status

Typically for root page objects that are loaded directly inside the driver (such as home pages, landing pages, or modals), it makes sense to override the "load" method to wait for DOM elements to finish loading (action waitForDocumentReady):

{
    "root": true,
    "selector": { "css": "one-record-home" },
    "beforeLoad": [
        {
            "element": "document",
            "apply": "waitForDocumentReady"
        },
        {
            "element": "root",
            "apply": "isPresent"
        }
    ]
}

Use Explicit Wait instead of a Retry

Consider this Java unit test code snippet that retries an attempt to click a button:

    try {
        button.click();
        Thread.sleep(1000);
    } catch (Exception e) {
        button.click();
    }

This code pattern usually means that the button isn't clickable initially and there’s a waiting period for an element to be in the state to accept such actions.

Here's a better pattern: wrap the click action into an explicit wait.

{
    "elements" : [
        {
            "name": "button",
            "type": "clickable",
            "selector": { "css": ".not-yet-clickable" }
        }
    ],
    "methods" : [
        {
            "name" : "waitAndClick",
            "compose" : [
                {
                    "apply" : "waitFor",
                    "args" : [
                        {
                        "type": "function",
                        "predicate": [
                                {
                                    "element": "button",
                                    "apply" : "click"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}

Wait Before Assertion

Consider this Java unit test code snippet that repeatedly attempts to assert displayed text while the text is being loaded.

    while(iterations < 10) {
        try {
            Thread.sleep(1000);
            if(fieldWithText.getText().equals("my expected text")) { return; }
        } catch (Exception e) {
            iterations ++;
        }
    }

Instead of hardcoding retry and sleep steps in a loop, add a getLoadingText method that accepts expected text as a parameter and uses an explicit wait to wait for the text to match.

{
  "elements" : [
    {
      "name": "fieldWithText",
      "selector": { "css": ".loading" }
    }
  ],
  "methods" : [
    {
      "name" : "getLoadingText",
      "compose" : [
        {
          "apply" : "waitFor",
          "args" : [
            {
              "type": "function",
              "predicate": [
                {
                  "element": "fieldWithText",
                  "apply" : "getText",
                  "matcher" : {
                    "type" : "stringEquals",
                    "args" : [
                      {
                        "name" : "expectedText",
                        "type" : "string"
                      }
                    ]
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Now the Java test code can look like this:

assert pageObject.getLoadingText("my expected text");

Wait for an Element's Presence

See examples in the guidelines about presence and nullable.

Wait for a Container Element

What if we need to wait for a container element? A container element is a placeholder for any internal component known only at runtime. Here's a container declaration:

{
    "elements": [
        {
          "name": "panelContainer",
          "type": "container",
          "public": true
        }
    ]
}

To wait for a container element to be loaded, we can use one of the following approaches:

  1. Add a waitForAnyPanelContent compose method that waits for any element inside a container using containsElement. This method is universal because it waits for any child element, so it's the preferred option. If waiting for the universal selector doesn't work for some reason, the page object needs to wait for a particular selector, as described in the example for the next approach.
{
    "methods" : [
        {
            "name" : "waitForAnyPanelContent",
            "compose" : [
                {
                    "apply" : "waitFor",
                    "args" : [
                        {
                        "type": "function",
                        "predicate": [
                                {
                                    "element": "root",
                                    "apply" : "containsElement",
                                    "args" : [
                                        // this selector is same as container's default
                                        // it waits for any child
                                        {
                                            "type": "locator",
                                            "value" : { "css": ":scope > *:first-child" }
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}
  1. Add a waitForMyComponentPanelContent compose method that waits for a particular selector using the same containsElement basic action.
{
    "methods" : [
        {
            "name" : "waitForMyComponentPanelContent",
            "compose" : [
                {
                    "apply" : "waitFor",
                    "args" : [
                        {
                        "type": "function",
                        "predicate": [
                                {
                                    "element": "root",
                                    "apply" : "containsElement",
                                    "args" : [
                                        // this selector is specific for MyComponent
                                        {
                                            "type": "locator",
                                            "value" : { "css": "my-component" }
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}
  1. If you need your method to return an instance of a particular page object inside a container, add a waitForMyComponentPanelContent compose method that invokes a container method wrapped in a wait with a hardcoded page object type. Then, the method will return an object of the given type:
{
    "elements": [
        {
          "name": "panelContainer",
          "type": "container"
        }
    ],
    "methods" : [
        {
            "name" : "waitForMyComponentPanelContent",
            "compose" : [
                {
                    "apply" : "waitFor",
                    "args" : [
                        {
                            "type": "function",
                            "predicate": [
                                {
                                    "returnType": "utam/pageObjects/MyComponent",
                                    "element": "panelContainer",
                                    "args": [
                                        {
                                            "type": "pageObject",
                                            "value": "utam/pageObjects/MyComponent"
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}