org.jboss.hal.testsuite.finder.FinderNavigation.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.hal.testsuite.finder.FinderNavigation.java

Source

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2010, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * 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.jboss.hal.testsuite.finder;

import static org.jboss.hal.testsuite.util.Console.DEFAULT_PAGE_LOAD_TIMEOUT;

import org.jboss.arquillian.graphene.Graphene;
import org.jboss.hal.testsuite.page.BasePage;
import org.jboss.hal.testsuite.util.ConfigUtils;
import org.jboss.hal.testsuite.util.Console;
import org.jboss.hal.testsuite.util.Find;
import org.jboss.hal.testsuite.util.PropUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * A class to select a column or row in a finder of the specified page.
 *
 * @author Harald Pehl
 */
public class FinderNavigation {

    private static class AddressTuple {

        final String column;
        final String row;

        AddressTuple(final String column, final String row) {
            this.column = column;
            this.row = row;
        }
    }

    public interface Hook {
        void performAfterRowClick();
    }

    private static final Logger log = LoggerFactory.getLogger(FinderNavigation.class);
    private static final String WILDCARD = "*";

    private final WebDriver browser;
    private final Class<? extends BasePage> page;
    private final List<AddressTuple> address;
    private final Hook hook;
    private boolean refresh;
    private int naviRetriesNo = 0;
    private long pageLoadTimeout = DEFAULT_PAGE_LOAD_TIMEOUT;

    /**
     * This constructor should not be used regularly! <br />
     * It's for special cases when you need to perform some action after every row click.<br />
     * E.g. sleeps to workaround HAL-803
     * @param hook action after every row click
     */
    public FinderNavigation(final WebDriver browser, final Class<? extends BasePage> page, Hook hook) {
        this.browser = browser;
        this.page = page;
        this.hook = hook;
        this.address = new ArrayList<>();
        this.refresh = true;
    }

    /**
     * This constructor should not be used regularly! <br />
     * It's for special cases when you need to delay after every row click.<br />
     * This is workaround for HAL-803 snd should be used mainly when you are interested about preview content.
     * @param clickDelay milis to wait after every row click
     */
    public FinderNavigation(final WebDriver browser, final Class<? extends BasePage> page, final long clickDelay) {
        this(browser, page, () -> {
            try {
                Thread.sleep(clickDelay);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
        });
    }

    public FinderNavigation(final WebDriver browser, final Class<? extends BasePage> page) {
        this(browser, page, () -> {
        });
    }

    /**
     * Sets the timeout in seconds for the application to be loaded in the beginning of the navigation after refresh.
     * Use carefully in exceptional situations where default timeout of 30 seconds is not sufficient,
     * e.g. for large domain performance tests.
     * @param pageLoadTimeout - timeout in seconds
     */
    public FinderNavigation withPageLoadTimeout(long pageLoadTimeout) {
        this.pageLoadTimeout = pageLoadTimeout;
        return this;
    }

    /**
     * Adds navigation step of column selection.
     */
    public FinderNavigation step(String column) {
        address.add(new AddressTuple(column, WILDCARD));
        return this;
    }

    /**
     * Adds navigation step of selection of row inside a column.
     */
    public FinderNavigation step(String column, String row) {
        address.add(new AddressTuple(column, row));
        return this;
    }

    public Column selectColumn() {
        WebElement column = navigate()[0];
        if (column == null) {
            throw new IllegalStateException("No address for selecting a column given");
        }
        return Graphene.createPageFragment(Column.class, column);
    }

    /**
     * @param exactRowText decides whether the row will be selected by its exact label (equals) or by part of its label (contains)
     */
    public Column selectColumn(Boolean exactRowText) {
        WebElement column = navigate(exactRowText)[0];
        if (column == null) {
            throw new IllegalStateException("No address for selecting a column given");
        }
        return Graphene.createPageFragment(Column.class, column);
    }

    public Row selectRow() {
        WebElement row = navigate()[1];
        if (row == null) {
            throw new IllegalStateException("No address for selecting a row given.");
        }
        return Graphene.createPageFragment(Row.class, row);
    }

    /**
     * @param exactRowText decides whether the row will be selected by its exact label (equals) or by part of its label (contains)
     */
    public Row selectRow(Boolean exactRowText) {
        WebElement row = navigate(exactRowText)[1];
        if (row == null) {
            throw new IllegalStateException("No address for selecting a row given.");
        }
        return Graphene.createPageFragment(Row.class, row);
    }

    public PreviewFragment getPreview() {
        By previewSelector = By.className("preview-content");
        Graphene.waitGui().until().element(previewSelector).is().visible();
        WebElement preview = browser.findElement(previewSelector);
        return Graphene.createPageFragment(PreviewFragment.class, preview);
    }

    private WebElement[] navigate() {
        return navigate(false);
    }

    private WebElement[] navigate(Boolean exactRowText) {
        WebElement[] columnRow = new WebElement[2];
        if (refresh) {
            // Console.withBrowser(browser).refreshAndNavigate(page);
            Console.withBrowser(browser).withPageLoadTimeout(pageLoadTimeout).waitForFirstNavigationPanel(page);
        }
        for (int i = 0; i < address.size(); i++) {
            AddressTuple tuple = address.get(i);

            columnRow[0] = browser.findElement(columnSelector(tuple.column));
            if (!WILDCARD.equals(tuple.row)) {
                By rowSelector = exactRowText ? rowSelectorEquals(tuple.row) : rowSelector(tuple.row);
                columnRow[1] = new Find().elementWithGuiTimeout(columnRow[0], rowSelector);
                if (!columnRow[1].isDisplayed()) {
                    ((JavascriptExecutor) browser).executeScript("arguments[0].scrollIntoView(true);",
                            columnRow[1]);
                }
                columnRow[1].click();
                hook.performAfterRowClick();
                Graphene.waitModel().until().element(columnRow[0], rowSelector).attribute("class")
                        .contains("cellTableSelectedRowCell");

                // wait for next column to be visible
                if (i < address.size() - 1) {
                    AddressTuple nextTuple = address.get(i + 1);
                    try {
                        Graphene.waitModel().until().element(columnSelector(nextTuple.column)).is().visible();

                        // TODO remove catch as soon as JBEAP-2168 is fixed!
                    } catch (TimeoutException e) {
                        if (ConfigUtils.get("jbeap2168workaround") != null && naviRetriesNo++ < 3) {
                            log.warn("Navigation frozen! JBEAP-2168 needs to be fixed!");
                            return navigate(exactRowText);
                        } else {
                            naviRetriesNo = 0;
                            throw new TimeoutException(
                                    "Navigation frozen! Probably due to JBEAP-2168 or changed navigation tree.", e);
                        }
                    }
                }
            }
        }
        naviRetriesNo = 0;
        return columnRow;
    }

    /**
     * This method should be called after normal finder navigation was called and made with selectRow or selectColumn
     * so user is navigated to some page.
     * It is good when you want to re-navigate at some page you are currently on.
     * You can only move in submenus on current page.
     * @return empty navigation without refresh and navigation to HomePage
     */
    public FinderNavigation resetNavigation() {
        address.clear();
        this.refresh = false;
        return this;
    }

    /**
     * Method delete all inserted addresses.
     * After method navigation has only page where to navigate, but no addresses in submenus.
     * Navigation has still active refresh.
     */
    public void clearNavigation() {
        if (address.size() > 0)
            address.clear();
    }

    /**
     * Select current row, click View and wait for configured model timeout for application detail to open.
     */
    public void openApplication() {
        this.selectRow().invoke(FinderNames.VIEW);
        Application.waitUntilVisible();
    }

    /**
     * Select current row, click View and wait for <i>timeout</i> seconds for application detail to open.
     */
    public void openApplication(int timeout) {
        this.selectRow().invoke(FinderNames.VIEW);
        Application.waitUntilVisible(timeout);
    }

    private By columnSelector(String name) {
        return By.cssSelector("[data-column=\"" + name + "\"]");
    }

    By rowSelectorEquals(String label) {
        return getRowSelector(" and text()='" + label + "']]");
    }

    By rowSelector(String label) {
        return getRowSelector(" and contains(.,'" + label + "')]]");
    }

    private By getRowSelector(String xpathSuffix) {
        String cellClass = PropUtils.get("table.cell.class");
        return By.ByXPath.xpath(".//td[contains(@class,'" + cellClass
                + "') and descendant::div[@class='navigation-column-item'" + xpathSuffix);
    }
}