org.xwiki.test.ui.po.LiveTableElement.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.test.ui.po.LiveTableElement.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.test.ui.po;

import java.util.List;

import org.apache.commons.lang3.StringEscapeUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.Select;

/**
 * Represents the actions possible on a livetable.
 * 
 * @version $Id: 2fce4d15013ce18d4d646a5c41b1434e4a0cde55 $
 * @since 3.2M3
 */
public class LiveTableElement extends BaseElement {
    private String livetableId;

    public LiveTableElement(String livetableId) {
        this.livetableId = livetableId;
    }

    /**
     * @return if the livetable has finished displaying and is ready for service
     */
    public boolean isReady() {
        Object result = getDriver().executeJavascript("return Element.hasClassName('"
                + StringEscapeUtils.escapeEcmaScript(livetableId) + "-ajax-loader','hidden')");
        return result instanceof Boolean ? (Boolean) result : false;
    }

    /**
     * Wait till the livetable has finished displaying all its rows (so that we can then assert the livetable content
     * without running the risk that the rows have not been updated yet).
     */
    public void waitUntilReady() {
        long t1 = System.currentTimeMillis();
        while ((System.currentTimeMillis() - t1 < getDriver().getTimeout() * 1000L)) {
            if (isReady()) {
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // Do nothing just break out
                break;
            }
        }
        throw new TimeoutException("Livetable isn't ready after the timeout has expired.");
    }

    public boolean hasColumn(String columnTitle) {
        List<WebElement> elements = getDriver().findElementsWithoutWaiting(
                By.xpath("//th[contains(@class, 'xwiki-livetable-display-header-text') and normalize-space(.) = '"
                        + columnTitle + "']"));
        return elements.size() > 0;
    }

    public void filterColumn(String inputId, String filterValue) {
        // Make extra sure Selenium can't go quicker than the live table status by forcing it before filtering.
        getDriver().executeJavascript("return $('" + StringEscapeUtils.escapeEcmaScript(livetableId)
                + "-ajax-loader').removeClassName('hidden')");

        WebElement element = getDriver().findElement(By.id(inputId));
        if ("select".equals(element.getTagName())) {
            new Select(element).selectByVisibleText(filterValue);
        } else {
            element.clear();
            element.sendKeys(filterValue);
        }
        waitUntilReady();
    }

    /**
     * @param inputId the filter input identifier
     * @return the value of the filter input for the specified column
     * @see #filterColumn(String, String)
     */
    public String getFilterValue(String inputId) {
        return getDriver().findElement(By.id(inputId)).getAttribute("value");
    }

    /**
     * @param columnTitle the title of live table column
     * @return the 0-based index of the specified column
     */
    public int getColumnIndex(String columnTitle) {
        WebElement liveTable = getDriver().findElement(By.id(livetableId));

        String escapedColumnTitle = columnTitle.replace("\\", "\\\\").replace("'", "\\'");
        String columnXPath = "//thead[@class = 'xwiki-livetable-display-header']//th[normalize-space(.) = '%s']";
        WebElement column = liveTable.findElement(By.xpath(String.format(columnXPath, escapedColumnTitle)));

        return ((Long) ((JavascriptExecutor) getDriver()).executeScript("return arguments[0].cellIndex;", column))
                .intValue();
    }

    /**
     * Checks if there is a row that has the given value for the specified column.
     *
     * @param columnTitle the title of live table column
     * @param columnValue the value to match rows against
     * @return {@code true} if there is a row that matches the given value for the specified column, {@code false}
     *         otherwise
     */
    public boolean hasRow(String columnTitle, String columnValue) {
        List<WebElement> elements = getRows(columnTitle);

        boolean result = elements.size() > 0;
        boolean match = false;
        if (result) {
            for (WebElement element : elements) {
                match = element.getText().equals(columnValue);
                if (match) {
                    break;
                }
            }
        }

        return result && match;
    }

    /**
     * Checks if there are as many rows as there are passed values and check that the values match.
     *
     * @since 7.2M2
     */
    public boolean hasExactRows(String columnTitle, List<String> columnValues) {
        List<WebElement> elements = getRows(columnTitle);

        boolean result = elements.size() == columnValues.size();
        if (result) {
            for (int i = 0; i < elements.size(); i++) {
                result = result && elements.get(i).getText().equals(columnValues.get(i));
                if (!result) {
                    break;
                }
            }
        }

        return result;
    }

    private List<WebElement> getRows(String columnTitle) {
        String cellXPath = String.format(".//tr/td[position() = %s]", getColumnIndex(columnTitle) + 1);
        WebElement liveTableBody = getDriver().findElement(By.id(this.livetableId + "-display"));
        return liveTableBody.findElements(By.xpath(cellXPath));
    }

    /**
     * @return the number of rows in the live table
     */
    public int getRowCount() {
        WebElement liveTableBody = getDriver().findElementWithoutWaiting(By.id(this.livetableId + "-display"));
        // We use XPath because we're interested only in the direct children.
        return getDriver().findElementsWithoutWaiting(liveTableBody, By.xpath("tr")).size();
    }

    /**
     * @since 5.3RC1
     */
    public WebElement getRow(int rowNumber) {
        WebElement liveTableBody = getDriver().findElementWithoutWaiting(By.id(this.livetableId + "-display"));
        return getDriver().findElementWithoutWaiting(liveTableBody, By.xpath("tr[" + rowNumber + "]"));
    }

    /**
     * @since 5.3RC1
     */
    public WebElement getCell(WebElement rowElement, int columnNumber) {
        return getDriver().findElementWithoutWaiting(rowElement, By.xpath("td[" + columnNumber + "]"));
    }

    /**
     * @since 5.3RC1
     */
    public ViewPage clickCell(int rowNumber, int columnNumber) {
        WebElement tdElement = getCell(getRow(rowNumber), columnNumber);
        // First scroll the element into view, if needed, by moving the mouse to the top left corner of the element.
        new Actions(getDriver()).moveToElement(tdElement, 0, 0).perform();
        // Find the first A element and click it!
        tdElement.findElement(By.tagName("a")).click();
        return new ViewPage();
    }

    /**
     * @since 3.2M3
     */
    public void waitUntilRowCountGreaterThan(int minimalExpectedRowCount) {
        final By by = By.xpath("//tbody[@id = '" + this.livetableId + "-display']//tr");
        getDriver().waitUntilCondition(new ExpectedCondition<Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                // Refresh the current page since we need the livetable to fetch the JSON again
                driver.navigate().refresh();
                return driver.findElements(by).size() >= minimalExpectedRowCount;
            }
        });
    }

    /**
     * Same as {@link #waitUntilRowCountGreaterThan(int, int)} but with a specific timeout (ie not using the default
     * timeout)
     *
     * @since 9.1RC1
     */
    // We need to decide if it's bettter to introduce this method or to globally increase the default timeout.
    public void waitUntilRowCountGreaterThan(int minimalExpectedRowCount, int timeout) {
        int originalTimeout = getDriver().getTimeout();
        getDriver().setTimeout(timeout);
        try {
            waitUntilRowCountGreaterThan(minimalExpectedRowCount);
        } finally {
            getDriver().setTimeout(originalTimeout);
        }
    }
}