Execute Javascript

Sometimes we run into problems that we cannot solve with selenium webdriver commands alone, such as:

  • interacting with shadow DOM elements,
  • scroll page,
  • inject JQuery library into page for convenient ajax status checking
  • and many more

and we have to turn to javascript for help: Inject javacript into the page and execute it there for DOM manipulation.

Selenium provides us 2 ways to execute script: synchronously, and asynchronously, via executeScript and executeAsyncScript methods in JavascriptExeucotr class respectively. All major webdrivers implement this interface.

I am confused.. How do I know when to execute script synchronously and when asynchronously?
If the javaScript you want to execute is synchronous, execute it synchronously; otherwise, asynchronously.

But how do I know my script is synchronous or asynchronous?
If you are asking this question, probably it is time for you to pick up a javascript book(:=).
But any how, a script is asynchronous if:

  • it contains call or statement that does not pause the execution of the code, such as setTimeout and setInterval, or AJAX requests OR
  • it contains code that just schedules some code to run once the server responds
  • Otherwise, synchronous. use executeScript if the script’s return value is available immediately or the execution of it takes no time.

    JavaScript is executed in the context of the currently selected frame or window and the script fragment provided will be executed as the body of an anonymous function. We can pass arguments to this function, and arguments must be a number, a boolean, a String, WebElement, or a List of any combination of the above.

    The script can return value back as well:
    If the script has a return value (i.e. if the script contains a return statement), then the following steps will be taken:

    For an HTML element, this method returns a WebElement

    • For a decimal, a Double is returned
    • For a non-decimal number, a Long is returned
    • For a boolean, a Boolean is returned
    • For all other cases, a String is returned.
    • For an array, return a List<Object> with each object following the rules above. We support nested lists.
      Unless the value is null or there is no return value, in which null is returned

    Before we execute any javaScript, we must ask ourselves the following 3 questions:
    1. What are arguments to be passed from webdriver to javascript?
    2. Any return value?
    3. If script is asynchronous, what are the maximum time that webdriver shall wait for an signal of ‘done’?

    Execute an Synchronous Piece of JavaScript

    Not all javascript need to take arguments or return any value, such as this simple example to just popup an alert:

    JavascriptExecutor js = (JavascriptExecutor) driver;
    js.executeScript(“alert(‘hello’)”);

    Pass Arguments to script
    We access arguments in the script using the “arguments” magic variable: arguments[0] for the first arg, arguments[1] for the second….
    Here is an example of executing script block to highlight the troubled element in red:

    Figure 1: Pass arguments to script

    Return value back
    If the script has a return value (i.e. if the script contains a return statement), then the following steps will be taken:

    For an HTML element, this method returns a WebElement

    • For a decimal, a Double is returned
    • For a non-decimal number, a Long is returned
    • For a boolean, a Boolean is returned
    • For all other cases, a String is returned.
    • For an array, return a List<Object> with each object following the rules above. We support nested lists.
      Unless the value is null or there is no return value, in which null is returned

    An example of returning a list of webElements:

    Figure 2: Return value

    Execute an Asynchronous Piece of JavaScript

    When we execute asynchronous script using .executeAsyncScript() method, we need to understand:

    • is script execution done yet?
      the script must explicitly signal they are finished by invoking the provided callback. This callback is always injected into the executed function as the last argument.
    • How long shall driver wait for the callback at most?
      We must set the script timeout, and we must set this value sufficiently large enough for the script to run:

      driver.manage().timeouts().setScriptTimeout(20,TimeUnit.SECONDS);

      The default timeout for a script to be executed is 0 ms. if you forget to set script timeout, the execution of executeAsynScript method will fail immediately with timeout error.

    • How return value is passed back to webDriver?
      The first argument passed to the callback function will be used as the script’s result.

    A few examples of executeAsyncScript:
    Make browser wait for 500 ms:

    ((JavascriptExecutor) driver).executeAsyncScript(
    “window.setTimeout(arguments[arguments.length – 1], 500);”);

    If you execute asynchronous script using executeScript method, driver will not wait for the time out and execution will fail immediately.

    Another example of getting data from server using ajax call and return the data back to webDriver:

    Figure 3: Execute asynchronous script

    Trouble Shooting
    Script execution may file due to bug in your script, or violating of browser’s cross domain policies etc. Besides the log info in our code, view the browser’s console after executing the WebDriver request can give us more info.

    Can you go over the major difference between executeScript and executeAsyncScript?
    Sure thing.
    1. executeAsyncScript requires explicit signal of Done while executeScript doesn’t
    executeAsyncScript takes a ‘done callback’ as the last argument, which must be called to signal that the script is done executing. This allows it to be used with code that only ‘finishes’ when a callback is used – eg. setTimeout or asynchronous XHR. If the ‘done callback’ is not called within the timeout limits the returned promise will be rejected.

    2. executeAsyncScript requires Timeout while executeScript doesn’t care
    executeScript don’t wait for execute to complete so no timeout for this method, while executeAsyncScript will time out if the result doesn’t come back within the time bound

    Script time out doesn’t apply to executeScript method.

    3. How value is turned back from script:
    executeScript returns value back via return statement in the script; while executeAsyncScript via first argument in callback function

    Both methods block the WebDriver control flow until they complete – either running off the end of the code for executeScript or when calling the ‘done callback’ with executeAsyncScript: “async” in the name signifies the signal mechanism used and does not mean/imply that the JavaScript code is actually executed asynchronously with respect to the WebDriver.

    Leave a Reply

    Your email address will not be published. Required fields are marked *