All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.thucydides.core.pages.Pages Maven / Gradle / Ivy

There is a newer version: 4.2.1
Show newest version
package net.thucydides.core.pages;

import net.serenitybdd.core.environment.ConfiguredEnvironment;
import net.serenitybdd.core.pages.PageObject;
import net.serenitybdd.core.pages.PageObjects;
import net.thucydides.core.annotations.Fields;
import net.thucydides.core.steps.EnclosingClass;
import net.thucydides.core.webdriver.Configuration;
import net.thucydides.core.webdriver.WebDriverFacade;
import net.thucydides.core.webdriver.WebdriverProxyFactory;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

import static org.apache.commons.lang3.StringUtils.isNotEmpty;

/**
 * The Pages object keeps track of what web pages a test visits, and helps with mapping pages to Page Objects.
 * A Pages object is associated with a WebDriver driver instance, so you need a Pages object for any
 * given WebDriver driver.
 *
 * @author johnsmart
 */
public class Pages implements Serializable {

    private static final long serialVersionUID = 1L;

    private transient WebDriver driver;

    private static final Logger LOGGER = LoggerFactory.getLogger(Pages.class);

    private String defaultBaseUrl;

    private final Configuration configuration;

    private WebdriverProxyFactory proxyFactory;

    private transient boolean usePreviousPage = false;

    public Pages(Configuration configuration) {
        this.configuration = configuration;
        proxyFactory = WebdriverProxyFactory.getFactory();
    }

    public Pages() {
        this(ConfiguredEnvironment.getConfiguration());
    }

    public Pages(final WebDriver driver) {
        this(ConfiguredEnvironment.getConfiguration());
        this.driver = driver;
    }

    public Pages(final WebDriver driver, Configuration configuration) {
        this(configuration);
        this.driver = driver;
    }

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

    public WebDriver getDriver() {
        return driver;
    }

    protected WebdriverProxyFactory getProxyFactory() {
        return proxyFactory;
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    PageObject currentPage = null;

    public  T getAt(final Class pageObjectClass) {
        return getPage(pageObjectClass);
    }

    public  T getPage(final Class pageObjectClass) {
        T pageCandidate = getCurrentPageOfType(pageObjectClass);
        pageCandidate.setDefaultBaseUrl(getDefaultBaseUrl());
        return pageCandidate;
    }

    @SuppressWarnings("unchecked")
	public  T get(final Class pageObjectClass) {
        T nextPage;
        if (shouldUsePreviousPage(pageObjectClass)) {
            nextPage = (T) currentPage;
        } else {
            T pageCandidate = getCurrentPageOfType(pageObjectClass);
            pageCandidate.setDefaultBaseUrl(getDefaultBaseUrl());
            cacheCurrentPage(pageCandidate);
            nextPage = pageCandidate;
        }
        usePreviousPage = false;
        return nextPage;
    }

    @SuppressWarnings("unchecked")
    public  T currentPageAt(final Class pageObjectClass) {
        T nextPage;
        if (shouldUsePreviousPage(pageObjectClass)) {
            nextPage = (T) currentPage;
        } else {
            T pageCandidate = getCurrentPageOfType(pageObjectClass);
            pageCandidate.setDefaultBaseUrl(getDefaultBaseUrl());
            openBrowserIfRequiredFor(pageCandidate);
            checkUrlPatterns(pageObjectClass, pageCandidate);
            cacheCurrentPage(pageCandidate);
            nextPage = pageCandidate;
            nextPage.addJQuerySupport();
        }
        usePreviousPage = false;
        return nextPage;
    }

    private  void  openBrowserIfRequiredFor(T pageCandidate) {
        if (browserNotOpen()) {
            openHeadlessDriverIfNotOpen();
            pageCandidate.open();
        }
    }


    private void openHeadlessDriverIfNotOpen() {
        if (browserIsHeadless()) {
            driver.get("about:blank");
        }
    }

    private boolean browserNotOpen() {
        if (getDriver() instanceof WebDriverFacade) {
            return !((WebDriverFacade) getDriver()).isInstantiated();
        } else {
            return StringUtils.isEmpty(getDriver().getCurrentUrl());
        }
    }

    private boolean browserIsHeadless() {
        if (getDriver() instanceof WebDriverFacade) {
            return (((WebDriverFacade) getDriver()).getProxiedDriver() instanceof HtmlUnitDriver);
        } else {
            return (getDriver() instanceof HtmlUnitDriver);
        }
    }
    private  void checkUrlPatterns(Class pageObjectClass, T pageCandidate) {
        if (!pageCandidate.matchesAnyUrl()) {
            String currentUrl = getDriver().getCurrentUrl();
            if (!pageCandidate.compatibleWithUrl(currentUrl)) {
                thisIsNotThePageYourLookingFor(pageObjectClass);
            }
        }
    }

    private  boolean shouldUsePreviousPage(final Class pageObjectClass) {
        if (!usePreviousPage) {
            return false;
        } else {
            return currentPageIsSameTypeAs(pageObjectClass);
        }
    }

    private void cacheCurrentPage(PageObject newPage) {
        this.currentPage = newPage;
    }

    private  boolean currentPageIsSameTypeAs(Class pageObjectClass) {
        return (currentPage != null) && (currentPage.getClass().equals(pageObjectClass));
    }

    public boolean isCurrentPageAt(final Class pageObjectClass) {
        try {
            PageObject pageCandidate = getCurrentPageOfType(pageObjectClass);
            String currentUrl = getDriver().getCurrentUrl();
            return pageCandidate.compatibleWithUrl(currentUrl);
        } catch (WrongPageError e) {
            return false;
        }
    }


    /**
     * Create a new Page Object of the given type.
     * The Page Object must have a constructor
     *
     * @param pageObjectClass
     * @throws IllegalArgumentException
     */
    @SuppressWarnings("unchecked")
    private  T getCurrentPageOfType(final Class pageObjectClass) {
        T currentPage = null;
        try {
            currentPage = createFromSimpleConstructor(pageObjectClass);
            if (currentPage == null) {
                currentPage = createFromConstructorWithWebdriver(pageObjectClass);
            }
            if (hasPageFactoryProperty(currentPage)) {
                setPageFactory(currentPage);
            }

        } catch (NoSuchMethodException e) {
            LOGGER.warn("This page object does not appear have a constructor that takes a WebDriver parameter: {} ({})",
                    pageObjectClass, e.getMessage());
            thisPageObjectLooksDodgy(pageObjectClass, "This page object does not appear have a constructor that takes a WebDriver parameter");
        } catch (InvocationTargetException e) {
        	// Unwrap the underlying exception
            LOGGER.warn("Failed to instantiate page of type {} ({})", pageObjectClass, e.getTargetException());
            thisPageObjectLooksDodgy(pageObjectClass,"Failed to instantiate page (" + e.getTargetException() +")");
        }catch (Exception e) {
        	//shouldn't even get here
            LOGGER.warn("Failed to instantiate page of type {} ({})", pageObjectClass, e);
            thisPageObjectLooksDodgy(pageObjectClass,"Failed to instantiate page (" + e +")");
        }
        return currentPage;
    }

    private  T createFromSimpleConstructor(Class pageObjectClass)
            throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        T newPage = null;
        try {
            if (hasDefaultConstructor(pageObjectClass)) {
                Class[] constructorArgs = new Class[0];
                Constructor constructor = pageObjectClass.getConstructor(constructorArgs);
                newPage = (T) constructor.newInstance();
                newPage.setDriver(driver);
            } else if (hasOuterClassConstructor(pageObjectClass)) {
                Constructor constructor = pageObjectClass.getConstructor(new Class[] {pageObjectClass.getEnclosingClass()});
                newPage = (T) constructor.newInstance(EnclosingClass.of(pageObjectClass).newInstance());
                newPage.setDriver(driver);
            }

        } catch (NoSuchMethodException e) {
            // Try a different constructor
        }
        return newPage;
    }

    private  boolean hasDefaultConstructor(Class pageObjectClass) {
        return Arrays.stream(pageObjectClass.getConstructors())
                .anyMatch( constructor -> constructor.getParameters().length == 0 );
    }


    private  boolean hasOuterClassConstructor(Class pageObjectClass) {
        return Arrays.stream(pageObjectClass.getConstructors())
                .anyMatch( constructor -> constructor.getParameters().length == 1
                        && constructor.getParameters()[0].getType() == pageObjectClass.getEnclosingClass() );
    }

    private  T createFromConstructorWithWebdriver(Class pageObjectClass)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class[] constructorArgs = new Class[1];
        constructorArgs[0] = WebDriver.class;
        Constructor constructor = pageObjectClass.getConstructor(constructorArgs);
        return (T) constructor.newInstance(driver);
    }

    private boolean hasPageFactoryProperty(Object pageObject) {
        java.util.Optional pagesField = Fields.of(pageObject.getClass()).withName("pages");
        return ((pagesField.isPresent()) && (pagesField.get().getType() == Pages.class));
    }

    private void setPageFactory(Object pageObject) throws IllegalAccessException {
        Fields.of(pageObject.getClass()).withName("pages").ifPresent(
                field -> assignTo(field, pageObject)
        );
    }

    private void assignTo(Field pagesField, Object pageObject) {
        try {
            pagesField.setAccessible(true);
            pagesField.set(pageObject, this);
        } catch (IllegalAccessException e) {
            LOGGER.warn("Failed to assign page object field: ", e);
        }
    }


    private void thisPageObjectLooksDodgy(final Class pageObjectClass, String message) {

        String errorDetails = "The page object " + pageObjectClass + " looks dodgy:\n" + message;
        throw new WrongPageError(errorDetails);
    }

    private void thisIsNotThePageYourLookingFor(final Class pageObjectClass) {

        String errorDetails = "This is not the page you're looking for: "
                + "I was looking for a page compatible with " + pageObjectClass + " but "
                + "I was at the URL " + getDriver().getCurrentUrl();

        throw new WrongPageError(errorDetails);
    }

    /**
     * The default URL for this set of tests, or the system default URL if undefined.
     */
    public String getDefaultBaseUrl() {

        String baseUrl = defaultBaseUrl;
        if (isNotEmpty(getConfiguration().getBaseUrl())) {
            baseUrl = getConfiguration().getBaseUrl();
        }
        return baseUrl;
    }

    /**
     * Set a default base URL for a specific set of tests.
     */
    public void setDefaultBaseUrl(final String defaultBaseUrl) {
        this.defaultBaseUrl = defaultBaseUrl;
    }

    public Pages onSamePage() {
        usePreviousPage = true;
        return this;
    }

    public static  T instrumentedPageObjectUsing(Class pageObjectClass, WebDriver driver) {
        return PageObjects.usingDriver(driver).ofType(pageObjectClass);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy