Selenium JavaScript Executor (English version)

Javascript Executor in Selenium have a lot of benefits that normal Selenium API cannot provide.

For instance,

  • You want to fetch data from server but it needs an authentication.
  • You want data inside a response of fetch or AJAX (XHR).
  • You want to get value from Javascript such as data from performance API (Navigation Timing, User Timing, etc.)

A lot of Selenium users believe that Selenium doesn’t support use cases above. But it’s not true. We can do it using Javascript Executor API in Selenium.

Contents

Create a JavaScript Executor

You can get JavaScript Executor by casting the WebDriver into JavaScriptExecutor.

JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;

Execute Javascript And Get Results

JavascriptExecutor expose 2 methods, executeScript() and executeAsyncScript(). There are some differences between these 2 methods.

You should use executeScript() when your JavaScript doesn’t need to wait for Promise and callback. Otherwise, you need executeAsyncScript().

executeScript(String script, … args)

The first parameter is JavaScript code, the latter parameters are free style (but parameter types must be one of Boolean, Long, Double, String, List or WebElement). You can use these parameters in your JavaScript by reffering to ‘arguments[i]’ where ‘i’ is index of the parameter.

If your JavaScript contain a statement which return Promise, executeScript() won’t wait for ‘.then()’. So you must not use this method for Promise or callback.

 

Example 1: Change style of a WebElement and using parameters in your JavaScript.

The example 1 shows that it sends 4 parameters. ‘script’ is your JavaScript that you want wat to execute. The latter parameters are arguments that we will use in JavaScript. You can see that there are ‘arguments[0]’, ‘arguments[1]’, and ‘arguments[2]’ in  JavaScript. That is the syntax  to referring to the parameters of executeScript(). In this case, ‘arguments[0]’ is a WebElement, ‘arguments[1]’ is string “none”, and ‘arguments[2]’ is boolean true.

public void testJsExecutor(){
    driver.get("https://www.blognone.com/");
    JavascriptExecutor jsExe = (JavascriptExecutor)driver; 
    WebElement img = driver.findElement(By.cssSelector("img")); 
    String script = "arguments[0].style.display=arguments[1]; if(arguments[2]){ console.log('its true'); }";
    jsExe.executeScript(script, img, "none", true);
}

 

Example 2: Get a list of performance entries from Performance API.
Sometimes you need to get an array from JavaScript and return it to Java. It’s quite complicated!
The example 2 shows how to return array and how to extract the data in Java.

public void testJsExecutorReturnList(){
    driver.get("https://www.blognone.com/"); 
    JavascriptExecutor jsExe = (JavascriptExecutor)driver; 
    String script = "return window.performance.getEntries();"; 
    List<Map<String, Object>> entries = (List<Map<String, Object>>)jsExe.executeScript(script); 
    for(int i = 0; i < entries.size(); i++) { 
        System.out.println("------- Entry: " + i + "--------"); 
        for(Map.Entry<String, Object> entry : entries.get(i).entrySet()) { 
            System.out.println(entry.getKey() + ":" + entry.getValue()); 
        } 
        System.out.println("------------------------"); 
    }
}

Example 3: Get a list of performance entries and then stringify it.

If the data is array of data in JSON format, we had better stringify it and then returns string to Java.

public void testJsExecutorReturnJsonString(){
    driver.get("https://www.blognone.com/"); 
    JavascriptExecutor jsExe = (JavascriptExecutor)driver; 
    String script = "return JSON.stringify(performance.getEntries());"; 
    String entriesString = (String)jsExe.executeScript(script); 
    System.out.println(entriesString);
}

 

 

executeAsyncScript(String script, … args)

This method is similar to executeScript(). The the difference is executeAsyncScript() will automatically append one parameter called ‘callback’.

This ‘callback’ parameter is a function. We can use it to tell Selenium that the JavaScript has been completely finished. Otherwise, Selenium will wait until timeout.

!!! Caution !!! You must set timeout of JavaScriptExecutor before using this method because default timeout is 0 ms, so it’s going to be timeout immediately after your script have been executed.

Example 4: Get content size of an image (from cache)

public void getImageSize(){
    driver.get("https://www.blognone.com/"); 
    WebElement img = driver.findElement(By.cssSelector("img")); 
    String imgUrl = img.getAttribute("src").trim();
    String script = "var callback = arguments[arguments.length - 1];" +  
 "fetch(arguments[0],{cache:'force-cache'}).then((response)=> {" +
     "return response.blob(); }).then((blob)=>{" +
     " callback(blob.size); });";
    driver.manage().timeouts().setScriptTimeout(10, TimeUnit.SECONDS); 
    Object response = ((JavascriptExecutor) driver).executeAsyncScript(script, imgUrl);
    System.out.println(response); 
}

 

Example 5: Digest image content with SHA-256 algorithm.

public void getImageSHA256(){
    driver.get("https://www.blognone.com/");
    WebElement img = driver.findElement(By.cssSelector("img"));
    String imgUrl = img.getAttribute("src").trim();   
    String script = "function hex(buffer) { var hexCodes = [];  var view = new DataView(buffer);  for (var i = 0; i < view.byteLength; i += 4) { var value = view.getUint32(i); var stringValue = value.toString(16); var padding = '00000000'; var paddedValue = (padding + stringValue).slice(-padding.length); hexCodes.push(paddedValue);  }  return hexCodes.join(\"\");}" + 
    "var callback = arguments[arguments.length - 1];" +  
    "fetch(arguments[0],{cache:'force-cache'}).then((response)=> {" +
        "return response.arrayBuffer(); }).then((buffer)=>{" + 
            " return crypto.subtle.digest('SHA-256', buffer); }).then((hashArray)=>{" +  
                " callback(hex(hashArray));"+ "});";   
    driver.manage().timeouts().setScriptTimeout(15, TimeUnit.SECONDS);
    Object response = ((JavascriptExecutor) driver).executeAsyncScript(script, imgUrl);   
    System.out.println(response); 
}

Leave a Reply

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