Transcripted Summary

While the WebDriver library has lots of built-in methods, sometimes there are actions you'll need that are not directly supported.

For these cases, WebDriver provides a way to allow you to execute JavaScript in the browser. All major web browsers have dedicated JavaScript engines. So anything that we cannot directly do with the WebDriver API, we can write our own JavaScript code to do.


A common example of the use of JavaScript in test automation projects is to scroll a page.

Surprisingly, there's not a built-in functionality to do this with WebDriver, so we'll need to write our own JavaScript in order to do so.

Let's look at an example.

Let's go ahead and click on this Large & Deep DOM link.



Here we see that this is very long, and also wide.



Let's write a program that will scroll down until the table element is in view.

I've created this method inside our HomePage page:


public LargeAndDeepDomPage clickLargeAndDeepDom(){
    clickLink("Large & Deep DOM");
    return new LargeAndDeepDomPage(driver);
}

Inside of the LargeAndDeepDomPage, I have already created the WebDriver, as well as the By element for that table.

We also have a constructor.


package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class LargeAndDeepDomPage {

    private WebDriver driver;
    private By table = By.id("large-table");

    public LargeAndDeepDomPage(WebDriver driver){
        this.driver = driver;
    }
}

Now we want to create a method that will use JavaScript in order to scroll to this table.

The first thing we want to do is create the WebElement to actually find that table.


public void scrollToTable(){
    WebElement tableElement = driver.findElement(table);
}

Now that we have the table, we want to scroll to it. This is where we'll need JavaScript, as there's no method on this table element that will allow you to scroll to it.

So, what we'll do is use this JavascriptExecutor class, and we see that this is in the Selenium project, as well.



This is a class that Selenium provides to allow us to execute JavaScript.


In order to utilize this, we have to cast our driver to JavascriptExecutor.

We'll go ahead and put this in parentheses, and then put our driver after it. That will cast this object. We'll put the whole thing in parentheses so that we can use it.


((JavascriptExecutor)driver)

We've cast this, now it's a JavascriptExecutor object.

If we do ., we see there's two methods available in this class — executeAsyncScript and executeScript.



Both of these will allow us to execute JavaScript code. One just asynchronously, and the other not.

In order to scroll, we'll do the executeScript and then we'll give it the JavaScript code within a String.

Let's table that for a moment.


Let's go ahead and write our String on another line, just so it's clear what we're doing.

JavaScript has a method on its elements called scrollIntoView.

We want to scroll the table into view, however we can't use this WebElement inside of the String itself. So we'll use a placeholder. For that, we can say arguments and then give it an index.

We only have 1 argument, so that would be 0.

This is a function call so we'll use parentheses.


String script = "arguments[0].scrollIntoView();";

Now, we can place this script variable inside of the executeScript call.

In addition to taking a script, executeScript method also optionally takes any objects.

We can give it our tableElement object, and it will replace arguments[0] with this element.


# LargeAndDeepDomPage.java


package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

public class LargeAndDeepDomPage {

    private WebDriver driver;
    private By table = By.id("large-table");

    public LargeAndDeepDomPage(WebDriver driver){
        this.driver = driver;
    }

    public void scrollToTable(){
        WebElement tableElement = driver.findElement(table);
        String script = "arguments[0].scrollIntoView();";
        ((JavascriptExecutor)driver).executeScript(script, tableElement);
    }
}

Now, let's create a test method that will scroll until this table is in view.

In our test section, I've created a new package called “javascript”, and a class called JavaScriptTests.

Let's go ahead and make the test method.


# JavaScriptTests.java


package javascript;

import base.BaseTests;
import org.testng.annotations.Test;

public class JavaScriptTests extends BaseTests {

    @Test
    public void testScrollToTable(){
        homePage.clickLargeAndDeepDom().scrollToTable();
    }
}

We'll just put a breakpoint here, so that we can see it run.

We'll do a debug.

The first thing it needs to do is to click in the DOM, so let's go inside of there. It is clicked.

Now, it's going to return back to here. Now it should scroll to the table.

We see that we're at the top of this page, now. Let's go ahead and allow it to scroll.

We'll go inside of this method, and we'll say find the element, and then scroll to it. And execute the script.

Now, we see that it has indeed scrolled down to this table element.



This approach works when the element is actually in the DOM, then we can easily go ahead and scroll it into view.


What about for pages where the element doesn't exist yet, and it doesn't enter into the DOM until we scroll?

Let's look at an example of this.

We're going to click on this Infinite Scroll link on the home page of our test application.

Let's inspect this.



We see here, there are two divs that have this class “jscroll-added”.

We see here that there are two paragraphs that are in view.

Let's inspect it again.



Now, as we scroll this page and more paragraphs are added, we see that additional items are added to the DOM.

If we wanted to scroll to the fourth or fifth paragraph, we couldn't make a WebElement and say to scroll it into view because the WebElement does not exist.

We would get an exception letting us know that there's no such element. Let's see how we would handle something like this.


Let's write code that will scroll down until a certain paragraph has been loaded.

In our HomePage, I have this clickInfiniteScroll method that returns the new InfiniteScrollPage object.


public InfiniteScrollPage clickInfiniteScroll(){
    clickLink("Infinite Scroll");
    return new InfiniteScrollPage(driver);
}

In InfiniteScrollPage, I have this By locator with the class name of “jscroll-added”.


package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class InfiniteScrollPage {

    private WebDriver driver;
    private By textBlocks = By.className("jscroll-added");

    public InfiniteScrollPage(WebDriver driver){
        this.driver = driver;
    }

Let's add a method that's going to say scroll down to “whatever” paragraph — we'll do this by index.

There's a JavaScript function called scrollTo that's on the window element.

We can say — window.scrollTo — and this takes two integers.

The first one represents the X axis, the second one represents the Y axis, so we could scroll horizontally or vertically.

We don't want to scroll horizontally at all, so we'll say 0 for the first parameter.

And we want to the normal amount of scroll vertically. If I were to scroll as a user, that's what I want to scroll.

We can use document.body.scrollHeight and this will do a normal scroll.


/**
* Scrolls until paragraph with index specified is in view
* @param index 1-based
*/

public void scrollToParagraph(int index){
    String script = "window.scrollTo(0, document.body.scrollHeight)";
} 

If we were to execute this, it would scroll one time, but we don't know if we've gotten to the paragraph that the test wants us to get to.

To do this, we'll need to say, “keep scrolling until there are a certain number of paragraphs in the DOM”. We can use a loop to do this.


Firstly, let's create a method that will tell us how many paragraphs are present.

We're going to say, driver.findElements (the plural one), and we'll give it our By.

This returns a List. Then we simply get the size.


private int getNumberOfParagraphsPresent(){
    return driver.findElements(textBlocks).size();
} 

This will let us know how many of those are there.


Okay, back to our scrolling method, we're going to use a while loop.


while(getNumberOfParagraphsPresent() < index){

}

Then we want to scroll, and we can use the JavascriptExecutor.

And I'm actually going to save that to a variable, just to give you some different options on how you can do this.

Again, we need to cast the JavascriptExecutor to the driver. Let's call this jsExecutor.


var jsExecutor = (JavascriptExecutor)driver;

Now, we can say jsExecutor.executeScript(), and give it our script.


/**
* Scrolls until paragraph with index specified is in view
* @param index 1-based
*/
public void scrollToParagraph(int index){
    String script = "window.scrollTo(0, document.body.scrollHeight)";
    var jsExecutor = (JavascriptExecutor)driver;

    while(getNumberOfParagraphsPresent() < index){
        jsExecutor.executeScript(script);
    }
}

This will call this method that gets how many paragraphs there are.

The first time, we know it's going to be 2. 2 is less than 5, so it's going to say do a normal scroll.

Then, it will check again and see how many there are. At that point, it's probably 3, and then 4, and then finally, 5, and it will end.

Don’t worry if you don’t know JavaScript

I want to make a note about the JavaScript code that we're writing. For most of these things, I usually have to look them up to get the proper syntax of what it is that I'm trying to do, and that's okay.

Also, Alan Richardson has a course on Test Automation University called Automating in the Browser Using JavaScript. If you're interested to learn more about JavaScript programming, then definitely check out that course.


Okay, let's go ahead and write a test that will execute this scrollToParagraph method.

I'm back in the JavaScriptTests class, and we'll create a new test.


@Test
public void testScrollToFifthParagraph(){
    homePage.clickInfiniteScroll().scrollToParagraph(5);
}

I want to put a breakpoint inside of this method so that we can see it in action.

Okay, let's run this.

Before our script is run, we see that we have two paragraphs in view.

Let's execute this. It should've executed once by scrolling down a bit, and yes, we see that there are more paragraphs in view.

Let's execute it again. There are more paragraphs in view. Then, finally, it got to the fifth one.

That's how you use the JavascriptExecutor class to execute custom JavaScript.

You can utilize this for more than just scrolling.

Basically, any JavaScript code that you want to write can be executed using this method.



Optional Independent Exercise

For your optional exercise for this chapter, go to the Dropdown link, and we're going to use JavaScript to change this element.

If we inspect this element, we can get the JSpath, here.



That's just so I can show you what this looks like in the dev tools. If I do this, I can get the element, but we already know how to get the element in Selenium WebDriver.

Don't copy that part.

However, this is the part that I want you to use.



We call setAttribute.

Let's change that dropdown to allow for multiple selections. The way we do that is by adding a multiple attribute to the element.

We can then say “multiple”, and this doesn't take a value, so we'll just leave it blank, and then enter.

Notice this dropdown has now changed so that you can select more than one option.

In your test automation code:

  1. Add JavaScript to change that dropdown, and then select both Option 1 and Option 2

  2. In your test, verify that you have those two selected, and that they're “Option 1” and “Option 2”


Here’s the JavaScript command:
arguments[0].setAttribute('multiple', '')

Good luck!


Solution

Programming can be done many different ways, but here’s my solution: DropdownPage, JavaScriptTests.



Resources