Selenium WebDriver – Automating Custom Controls – JQuery Sortable Elements

We already have seen automating a custom control – JQuery Slider – in this article. I would request you to read that article first, if you have not already. We are going to see how to automate JQuery Sortable Elements in this post.

Goal:

To model a wrapper element for these sortable items. So that user can move items by index or item name.

JQuery Sortable Element:

It is basically a list of WebElements for which you can re-arrange the order by using drag and drop. Our aim here is to make an element move to a new position by the index or the text. Since we are going to find an element by text, I use a map and store the elements reference.

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public class SortableElement {

    private static final int OFFSET = 5;

    @FindBy(css = ".ui-sortable-handle")
    private List<WebElement> sortableHandles;

    private Map<String, WebElement> map;
    private Actions action;

    public SortableElement(final WebDriver driver){
        this.action = new Actions(driver);
        PageFactory.initElements(driver, this);
    }

    public List<String> getItems() {
        return  this.sortableHandles.stream()
                    .map(WebElement::getText)
                    .map(String::trim)
                    .collect(Collectors.toList());
    }

    public void reorder(int from, int to) {
        this.reorder(sortableHandles.get(from),
                sortableHandles.get(to));
    }

    public void reorder(String from, String to) {
        if (Objects.isNull(map)) {
            map = sortableHandles.stream()
                    .collect(Collectors.toMap(
                            ele -> ele.getText(), //key
                            ele -> ele //value
                    ));
        }
        this.reorder(map.get(from), map.get(to));
    }

    private void reorder(WebElement source, WebElement target) {
        this.action.clickAndHold(source)
                .dragAndDropBy(target, OFFSET, OFFSET)
                .build()
                .perform();
    }
}

Page Object:

Our aim contains the sortable element and exposes via getter method to the test class.

import org.openqa.selenium.WebDriver;

public class SortablePage {

    private final WebDriver driver;

    private SortableElement sortable;

    public SortablePage(final WebDriver driver){
        this.driver = driver;
    }

    public void goTo() {
        driver.get("https://jqueryui.com/sortable/");
        this.driver.switchTo().frame(0);
        this.sortable = new SortableElement(driver);
    }

    public SortableElement getSortables() {
        return sortable;
    }

}

 

Test:

Lets test our SortableElement model using below test class. As our SortableElement could rearrange the order of the items either by text or index, I have 2 different test methods with corresponding data providers.

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.util.Arrays;
import java.util.List;

public class SortableTest {

    private WebDriver driver;
    private SortablePage sortablePage;

    @BeforeTest
    public void setupDriver() {
        this.driver = new ChromeDriver();
        this.sortablePage = new SortablePage(driver);
    }

    @Test(dataProvider = "number-inputs")
    public void intMove(int fromIndex, int toIndex, List<String> expectedOrder) {
        sortablePage.goTo();
        sortablePage.getSortables().reorder(fromIndex, toIndex);
        Assert.assertEquals(expectedOrder, sortablePage.getSortables().getItems());
    }

    @Test(dataProvider = "string-inputs")
    public void stringMove(String fromItem, String toItem, List<String> expectedOrder) {
        sortablePage.goTo();
        sortablePage.getSortables().reorder(fromItem, toItem);
        Assert.assertEquals(expectedOrder, sortablePage.getSortables().getItems());
    }

    @DataProvider(name = "number-inputs")
    public static Object[][] getNumberInputs() {
        return new Object[][]
        {
            {
                0,
                2,
                Arrays.asList("Item 2", "Item 3", "Item 1", "Item 4", "Item 5", "Item 6", "Item 7")
            },
            {
                1,
                3,
                Arrays.asList("Item 1", "Item 3", "Item 4", "Item 2", "Item 5", "Item 6", "Item 7")
            }
        };
    }

    @DataProvider(name = "string-inputs")
    public static Object[][] getStringInputs() {
        return new Object[][]
        {
            {
                "Item 3",
                "Item 4",
                Arrays.asList("Item 1", "Item 2", "Item 4", "Item 3", "Item 5", "Item 6", "Item 7")
            },
            {
                "Item 1",
                "Item 7",
                Arrays.asList("Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 1")
            }
        };
    }
    
}

Demo:

Summary:

By simply adding an abstraction layer, we control a complex element like a simple WebElement.  Now if the behavior changes / a new feature has to be added, modifying Sortable element class will do the trick for us.

Happy Testing & Subscribe 🙂

 

 

Share This:

1 thought on “Selenium WebDriver – Automating Custom Controls – JQuery Sortable Elements

  1. Thanks for such a great Article. It’s worth to read!! Very clear and neat one. Your article is short and detailed. i will refer and share your links.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.