Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.serenitybdd.core.pages.PageObject Maven / Gradle / Ivy
package net.serenitybdd.core.pages;
import com.google.common.base.Predicate;
import net.serenitybdd.core.Serenity;
import net.serenitybdd.core.SystemTimeouts;
import net.serenitybdd.model.collect.NewList;
import net.serenitybdd.core.di.SerenityInfrastructure;
import net.serenitybdd.model.environment.EnvironmentSpecificConfiguration;
import net.serenitybdd.model.time.SystemClock;
import net.thucydides.model.ThucydidesSystemProperty;
import net.serenitybdd.annotations.WhenPageOpens;
import net.thucydides.model.environment.SystemEnvironmentVariables;
import net.thucydides.core.fluent.ThucydidesFluentAdapter;
import net.thucydides.core.pages.Pages;
import net.thucydides.core.pages.WrongPageError;
import net.thucydides.core.pages.components.Dropdown;
import net.thucydides.core.pages.components.FileToUpload;
import net.thucydides.core.pages.jquery.JQueryEnabledPage;
import net.thucydides.model.reflection.MethodFinder;
import net.thucydides.core.scheduling.FluentWaitWithRefresh;
import net.thucydides.core.scheduling.SerenityFluentWait;
import net.thucydides.core.scheduling.ThucydidesFluentWait;
import net.thucydides.core.steps.PageObjectStepDelayer;
import net.thucydides.core.steps.StepEventBus;
import net.thucydides.core.steps.WaitForBuilder;
import net.thucydides.model.util.EnvironmentVariables;
import net.thucydides.model.util.Inflector;
import net.thucydides.core.webdriver.*;
import net.thucydides.core.webdriver.javascript.JavascriptExecutorFacade;
import net.thucydides.core.webelements.Checkbox;
import net.thucydides.core.webelements.RadioButtonGroup;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.*;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Sleeper;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Clock;
import java.time.Duration;
import java.time.temporal.TemporalUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Stream;
import static net.serenitybdd.core.pages.ParameterisedLocator.withArguments;
import static net.serenitybdd.core.selectors.Selectors.xpathOrCssSelector;
import static net.thucydides.model.ThucydidesSystemProperty.*;
import static net.thucydides.core.webdriver.javascript.JavascriptSupport.javascriptIsSupportedIn;
/**
* A base class representing a WebDriver page object.
*
* @author johnsmart
*/
public abstract class PageObject {
private static final int WAIT_FOR_ELEMENT_PAUSE_LENGTH = 250;
private static final Logger LOGGER = LoggerFactory.getLogger(PageObject.class);
private WebDriver driver;
private Pages pages;
private MatchingPageExpressions matchingPageExpressions;
private RenderedPageObjectView renderedView;
private PageUrls pageUrls;
private final SystemClock clock;
private Duration waitForTimeout;
private Duration waitForElementTimeout;
private final Sleeper sleeper;
private final Clock webdriverClock;
private JavascriptExecutorFacade javascriptExecutorFacade;
private EnvironmentVariables environmentVariables;
public static PageObject fromSearchContext(SearchContext searchContext) {
return null;
}
public void setImplicitTimeout(int duration, TemporalUnit unit) {
waitForElementTimeout = Duration.of(duration, unit);
setDriverImplicitTimeout(waitForElementTimeout);
}
private void setDriverImplicitTimeout(Duration implicitTimeout) {
if (driver instanceof ConfigurableTimeouts) {
((ConfigurableTimeouts) driver).setImplicitTimeout(implicitTimeout);
} else {
driver.manage().timeouts().implicitlyWait(Duration.ofMillis(waitForElementTimeout.toMillis()));
}
}
public void resetImplicitTimeout() {
if (driver instanceof ConfigurableTimeouts) {
waitForElementTimeout = ((ConfigurableTimeouts) driver).resetTimeouts();
} else {
waitForElementTimeout = getDefaultImplicitTimeout();
driver.manage().timeouts().implicitlyWait(Duration.ofMillis(waitForElementTimeout.toMillis()));
}
}
private Duration getDefaultImplicitTimeout() {
Long configuredTimeout = new SystemTimeouts(environmentVariables).getImplicitTimeout();
return Duration.ofMillis(configuredTimeout);
}
private enum OpenMode {
CHECK_URL_PATTERNS,
IGNORE_URL_PATTERNS
}
protected PageObject() {
this.webdriverClock = Clock.systemDefaultZone();
this.clock = SerenityInfrastructure.getClock();
this.environmentVariables = SystemEnvironmentVariables.currentEnvironmentVariables();
this.sleeper = Sleeper.SYSTEM_SLEEPER;
}
protected PageObject(final WebDriver driver, Predicate super PageObject> callback) {
this();
this.driver = driver;
callback.apply(this);
}
public PageObject(final WebDriver webdriver, final int ajaxTimeout) {
this();
setDriver(webdriver, ajaxTimeout);
}
public PageObject(final WebDriver driver) {
this();
ThucydidesWebDriverSupport.useDriver(driver);
setDriver(driver);
}
public PageObject(final WebDriver driver, final EnvironmentVariables environmentVariables) {
this();
this.environmentVariables = environmentVariables;
setDriver(driver);
}
protected void setDriver(WebDriver webdriver, long timeout) {
this.driver = webdriver;
if (SerenityInfrastructure.getConfiguration().getBaseUrl() != null) {
setDefaultBaseUrl(SerenityInfrastructure.getConfiguration().getBaseUrl());
}
new DefaultPageObjectInitialiser(getDriver(), timeout).apply(this);
}
public T setDriver(WebDriver webdriver) {
setDriver(webdriver, getImplicitWaitTimeout().toMillis());
return (T) this;
}
public T withDriver(WebDriver driver) {
return setDriver(driver);
}
public Duration getWaitForTimeout() {
if (waitForTimeout == null) {
int configuredWaitForTimeoutInMilliseconds = fluentWaitTimeout();
waitForTimeout = Duration.ofMillis(configuredWaitForTimeoutInMilliseconds);
}
return waitForTimeout;
}
private int fluentWaitTimeout() {
return (WEBDRIVER_WAIT_FOR_TIMEOUT.integerFrom(environmentVariables,
WEBDRIVER_TIMEOUTS_FLUENTWAIT.integerFrom(environmentVariables,
(int) DefaultTimeouts.DEFAULT_WAIT_FOR_TIMEOUT.toMillis())));
}
@Deprecated
public Duration getWaitForElementTimeout() {
return getImplicitWaitTimeout();
}
public Duration getImplicitWaitTimeout() {
if (waitForElementTimeout == null) {
long configuredWaitForTimeoutInMilliseconds = new SystemTimeouts(environmentVariables).getImplicitTimeout();
waitForElementTimeout = Duration.ofMillis(configuredWaitForTimeoutInMilliseconds);
}
return waitForElementTimeout;
}
public void setPages(Pages pages) {
this.pages = pages;
}
@Deprecated
public T switchToPage(final Class pageObjectClass) {
if (pages.getDriver() == null) {
pages.setDriver(driver);
}
return pages.getPage(pageObjectClass);
}
/**
* Upload a file via an HTML form.
* By default, this will look for a file on the file system, at the location provided.
*/
public FileToUpload upload(final String filename) {
return new FileToUpload(getDriver(), filename).useRemoteDriver(isDefinedRemoteUrl());
}
public FileToUpload uploadData(String data) throws IOException {
Path datafile = Files.createTempFile("upload", "data");
Files.write(datafile, data.getBytes(StandardCharsets.UTF_8));
return new FileToUpload(getDriver(), datafile.toAbsolutePath().toString()).useRemoteDriver(isDefinedRemoteUrl());
}
public FileToUpload uploadData(byte[] data) throws IOException {
Path datafile = Files.createTempFile("upload", "data");
Files.write(datafile, data);
return new FileToUpload(getDriver(), datafile.toAbsolutePath().toString()).useRemoteDriver(isDefinedRemoteUrl());
}
private boolean isDefinedRemoteUrl() {
boolean isRemoteUrl = ThucydidesSystemProperty.WEBDRIVER_REMOTE_URL.isDefinedIn(environmentVariables);
boolean isBrowserStack = ThucydidesSystemProperty.BROWSERSTACK_URL.isDefinedIn(environmentVariables);
return isRemoteUrl || isBrowserStack;
}
private PageUrls getPageUrls() {
if (pageUrls == null) {
pageUrls = new PageUrls(this, environmentVariables);
}
return pageUrls;
}
/**
* Only for testing purposes.
*/
public void setPageUrls(PageUrls pageUrls) {
this.pageUrls = pageUrls;
}
public void setWaitForTimeout(final long waitForTimeoutInMilliseconds) {
this.waitForTimeout = Duration.ofMillis(waitForTimeoutInMilliseconds);
getRenderedView().setWaitForTimeout(this.waitForTimeout);
}
public void setWaitForElementTimeout(final long waitForTimeoutInMilliseconds) {
this.waitForElementTimeout = Duration.ofMillis(waitForTimeoutInMilliseconds);
}
protected RenderedPageObjectView getRenderedView() {
if (renderedView == null) {
renderedView = new RenderedPageObjectView(getDriver(), this, getWaitForTimeout(), true);
}
return renderedView;
}
protected SystemClock getClock() {
return clock;
}
private MatchingPageExpressions getMatchingPageExpressions() {
if (matchingPageExpressions == null) {
matchingPageExpressions = new MatchingPageExpressions(this);
}
return matchingPageExpressions;
}
public WebDriver getDriver() {
if (driver == null) {
driver = Serenity.getDriver();
}
return driver;
}
/**
* Determines if the current driver is equipped with Chrome Dev Tools
*/
public boolean hasDevTools() {
if (getDriver() instanceof WebDriverFacade) {
return (((WebDriverFacade) getDriver()).getProxiedDriver() instanceof HasDevTools);
} else {
return (getDriver() instanceof HasDevTools);
}
}
public Optional maybeGetDevTools() {
return Optional.ofNullable(getDevTools());
}
public DevTools getDevTools() {
if (!hasDevTools()) {
return null;
}
if (getDriver() instanceof WebDriverFacade) {
return ((HasDevTools) ((WebDriverFacade) getDriver()).getProxiedDriver()).getDevTools();
} else {
return ((HasDevTools) getDriver()).getDevTools();
}
}
public String getTitle() {
return driver.getTitle();
}
public boolean matchesAnyUrl() {
return thereAreNoPatternsDefined();
}
/**
* Does this page object work for this URL? When matching a URL, we check
* with and without trailing slashes
*/
public final boolean compatibleWithUrl(final String currentUrl) {
return thereAreNoPatternsDefined() || matchUrlAgainstEachPattern(currentUrl);
}
private boolean matchUrlAgainstEachPattern(final String currentUrl) {
return getMatchingPageExpressions().matchUrlAgainstEachPattern(currentUrl);
}
private boolean thereAreNoPatternsDefined() {
return getMatchingPageExpressions().isEmpty();
}
public PageObject waitForRenderedElements(final By byElementCriteria) {
getRenderedView().waitFor(byElementCriteria);
return this;
}
/**
* @deprecated TimeUnit has been replaced by TemporalUnit in Selenium. For more consistancy use a TemporalUnit parameter.
*/
@Deprecated
public RenderedPageObjectView withTimeoutOf(int timeout, TimeUnit units) {
return withTimeoutOf(Duration.of(timeout, TemporalUnitConverter.fromTimeUnit(units)));
}
public RenderedPageObjectView withTimeoutOf(int timeout, TemporalUnit units) {
return withTimeoutOf(Duration.of(timeout, units));
}
public RenderedPageObjectView withTimeoutOf(Duration timeout) {
return new RenderedPageObjectView(getDriver(), this, timeout, false);
}
/**
* Alternative to withTimeoutOf()
*/
public RenderedPageObjectView waitingForNoLongerThan(int timeout, TimeUnit units) {
return withTimeoutOf(Duration.of(timeout, TemporalUnitConverter.fromTimeUnit(units)));
}
/**
* Alternative to withTimeoutOf() using a DSL
*/
public WaitingBuilder waitingForNoLongerThan(int timeout) {
return new WaitingBuilder(timeout, this);
}
public static class WaitingBuilder {
private final int timeout;
private final PageObject page;
public WaitingBuilder(int timeout, PageObject page) {
this.timeout = timeout;
this.page = page;
}
public RenderedPageObjectView milliseconds() {
return page.withTimeoutOf(Duration.ofMillis(timeout));
}
public RenderedPageObjectView seconds() {
return page.withTimeoutOf(Duration.ofSeconds(timeout));
}
public RenderedPageObjectView minutes() {
return page.withTimeoutOf(Duration.ofMinutes(timeout));
}
}
public PageObject waitFor(String xpathOrCssSelector, Object firstArgument, Object... arguments) {
List args = new ArrayList<>();
args.add(firstArgument);
args.addAll(Arrays.asList(arguments));
return waitForRenderedElements(xpathOrCssSelector(withArguments(xpathOrCssSelector, args.toArray())));
}
public PageObject waitFor(String xpathOrCssSelector) {
return waitForRenderedElements(xpathOrCssSelector(xpathOrCssSelector));
}
public T waitFor(ExpectedCondition expectedCondition) {
return getRenderedView().waitFor(expectedCondition);
}
public T waitFor(String message, ExpectedCondition expectedCondition) {
return getRenderedView().waitFor(message, expectedCondition);
}
public PageObject waitForRenderedElementsToBePresent(final By byElementCriteria) {
getRenderedView().waitForPresenceOf(byElementCriteria);
return this;
}
public PageObject waitForPresenceOf(String xpathOrCssSelector, Object... arguments) {
return waitForRenderedElementsToBePresent(xpathOrCssSelector(withArguments(xpathOrCssSelector, arguments)));
}
public PageObject waitForRenderedElementsToDisappear(final By byElementCriteria) {
getRenderedView().waitForElementsToDisappear(byElementCriteria);
return this;
}
public PageObject waitForAbsenceOf(String xpathOrCssSelector, Object... arguments) {
return waitForRenderedElementsToDisappear(xpathOrCssSelector(withArguments(xpathOrCssSelector, arguments)));
}
public PageObject waitForAbsenceOf(By byLocator) {
return waitForRenderedElementsToDisappear(byLocator);
}
/**
* Waits for a given text to appear anywhere on the page.
*/
public PageObject waitForTextToAppear(final String expectedText) {
getRenderedView().waitForText(expectedText);
return this;
}
public PageObject waitForTitleToAppear(final String expectedTitle) {
waitOnPage().until(ExpectedConditions.titleIs(expectedTitle));
return this;
}
public WebDriverWait waitOnPage() {
return new WebDriverWait(getDriver(), getWaitForTimeout());
}
public PageObject waitForTitleToDisappear(final String expectedTitle) {
getRenderedView().waitForTitleToDisappear(expectedTitle);
return this;
}
/**
* Waits for a given text to appear inside the element.
*/
public PageObject waitForTextToAppear(final WebElement element,
final String expectedText) {
getRenderedView().waitForText(element, expectedText);
return this;
}
private boolean driverIsDisabled() {
return StepEventBus.getParallelEventBus().webdriverCallsAreSuspended();
}
/**
* Waits for a given text to disappear from the element.
*/
public PageObject waitForTextToDisappear(final WebElement element,
final String expectedText) {
if (!driverIsDisabled()) {
waitForCondition().until(elementDoesNotContain(element, expectedText));
}
return this;
}
private ExpectedCondition elementDoesNotContain(final WebElement element, final String expectedText) {
return new ExpectedCondition() {
public Boolean apply(WebDriver driver) {
return !element.getText().contains(expectedText);
}
};
}
public PageObject waitForTextToDisappear(final String expectedText) {
return waitForTextToDisappear(expectedText, getWaitForTimeout().toMillis());
}
/**
* Waits for a given text to not be anywhere on the page.
*/
public PageObject waitForTextToDisappear(final String expectedText,
final long timeoutInMilliseconds) {
getRenderedView().waitForTextToDisappear(expectedText, timeoutInMilliseconds);
return this;
}
/**
* Waits for a given text to appear anywhere on the page.
*/
public PageObject waitForTextToAppear(final String expectedText,
final long timeout) {
getRenderedView().waitForTextToAppear(expectedText, timeout);
return this;
}
/**
* Waits for any of a number of text blocks to appear anywhere on the
* screen.
*/
public PageObject waitForAnyTextToAppear(final String... expectedText) {
getRenderedView().waitForAnyTextToAppear(expectedText);
return this;
}
public PageObject waitForAnyTextToAppear(final WebElement element,
final String... expectedText) {
getRenderedView().waitForAnyTextToAppear(element, expectedText);
return this;
}
/**
* Waits for all of a number of text blocks to appear on the screen.
*/
public PageObject waitForAllTextToAppear(final String... expectedTexts) {
getRenderedView().waitForAllTextToAppear(expectedTexts);
return this;
}
public PageObject waitForAnyRenderedElementOf(final By... expectedElements) {
getRenderedView().waitForAnyRenderedElementOf(expectedElements);
return this;
}
protected void waitABit(final long timeInMilliseconds) {
getClock().pauseFor(timeInMilliseconds);
}
public WaitForBuilder extends PageObject> waitFor(int duration) {
return new PageObjectStepDelayer(clock, this).waitFor(duration);
}
public List thenReturnElementList(final By byListCriteria) {
return driver.findElements(byListCriteria);
}
/**
* Check that the specified text appears somewhere in the page.
*/
public void shouldContainText(final String textValue) {
if (!containsText(textValue)) {
String errorMessage = String.format(
"The text '%s' was not found in the page", textValue);
throw new NoSuchElementException(errorMessage);
}
}
/**
* Check that all of the specified texts appears somewhere in the page.
*/
public void shouldContainAllText(final String... textValues) {
if (!containsAllText(textValues)) {
String errorMessage = String.format(
"One of the text elements in '%s' was not found in the page", (Object[]) textValues);
throw new NoSuchElementException(errorMessage);
}
}
/**
* Does the specified web element contain a given text value. Useful for dropdowns and so on.
*
* @deprecated use element(webElement).containsText(textValue)
*/
@Deprecated
public boolean containsTextInElement(final WebElement webElement, final String textValue) {
return element(webElement).containsText(textValue);
}
/*
* Check that the element contains a given text.
* @deprecated use element(webElement).shouldContainText(textValue)
*/
@Deprecated
public void shouldContainTextInElement(final WebElement webElement, final String textValue) {
element(webElement).shouldContainText(textValue);
}
/*
* Check that the element does not contain a given text.
* @deprecated use element(webElement).shouldNotContainText(textValue)
*/
@Deprecated
public void shouldNotContainTextInElement(final WebElement webElement, final String textValue) {
element(webElement).shouldNotContainText(textValue);
}
/**
* Clear a field and enter a value into it.
*/
public void typeInto(final WebElement field, final String value) {
element(field).type(value);
}
/**
* Clear a field and enter a value into it.
* This is a more fluent alternative to using the typeInto method.
*/
public FieldEntry enter(CharSequence... keysToSend) {
return new FieldEntry(keysToSend);
}
public void selectFromDropdown(final WebElement dropdown,
final String visibleLabel) {
Dropdown.forWebElement(dropdown).select(visibleLabel);
notifyScreenChange();
}
public void selectMultipleItemsFromDropdown(final WebElement dropdown,
final String... selectedLabels) {
Dropdown.forWebElement(dropdown).selectMultipleItems(selectedLabels);
notifyScreenChange();
}
public Set getSelectedOptionLabelsFrom(final WebElement dropdown) {
return Dropdown.forWebElement(dropdown).getSelectedOptionLabels();
}
public Set getSelectedOptionValuesFrom(final WebElement dropdown) {
return Dropdown.forWebElement(dropdown).getSelectedOptionValues();
}
public String getSelectedValueFrom(final WebElement dropdown) {
return Dropdown.forWebElement(dropdown).getSelectedValue();
}
public String getSelectedLabelFrom(final WebElement dropdown) {
return Dropdown.forWebElement(dropdown).getSelectedLabel();
}
public void setCheckbox(final WebElement field, final boolean value) {
Checkbox checkbox = new Checkbox(field);
checkbox.setChecked(value);
notifyScreenChange();
}
public boolean containsText(final String textValue) {
return getRenderedView().containsText(textValue);
}
/**
* Check that the specified text appears somewhere in the page.
*/
public boolean containsAllText(final String... textValues) {
for (String textValue : textValues) {
if (!getRenderedView().containsText(textValue)) {
return false;
}
}
return true;
}
/**
* Fail the test if this element is not displayed (rendered) on the screen.
*/
public void shouldBeVisible(final WebElement field) {
element(field).shouldBeVisible();
}
public void shouldBeVisible(final By byCriteria) {
waitOnPage().until(ExpectedConditions.visibilityOfElementLocated(byCriteria));
}
public void shouldNotBeVisible(final WebElement field) {
try {
element(field).shouldNotBeVisible();
} catch (NoSuchElementException e) {
// A non-existant element is not visible
}
}
public void shouldNotBeVisible(final By byCriteria) {
List matchingElements = getDriver().findElements(byCriteria);
if (!matchingElements.isEmpty()) {
waitOnPage().until(ExpectedConditions.invisibilityOfElementLocated(byCriteria));
}
}
public long waitForTimeoutInMilliseconds() {
return getWaitForTimeout().toMillis();
}
public long implicitTimoutMilliseconds() {
return getImplicitWaitTimeout().toMillis();
}
public String updateUrlWithBaseUrlIfDefined(String startingUrl) {
if (getPageUrls().getDeclaredDefaultUrl().isPresent()) {
startingUrl = getPageUrls().addDefaultUrlTo(startingUrl);
}
String baseUrl = getPageUrls().getSystemBaseUrl();
if (isDefined(baseUrl)) {
if (isFullUrl(startingUrl)) {
return replaceHost(startingUrl, baseUrl);
} else if (isRelative(startingUrl)) {
return getPageUrls().addBaseUrlTo(startingUrl);
}
}
return startingUrl;
}
protected boolean isRelative(String startingUrl) {
return !(isFullUrl(startingUrl));
}
private boolean isDefined(String url) {
return (url != null) && (!StringUtils.isEmpty(url));
}
private boolean isFullUrl(String startingUrl) {
try {
new URL(startingUrl);
return true;
} catch (MalformedURLException e) {
return false;
}
}
private String replaceHost(final String starting, final String base) {
String updatedUrl = starting;
try {
URL startingUrl = new URL(starting);
URL baseUrl = new URL(base);
String startingHostComponent = hostComponentFrom(startingUrl.getProtocol(),
startingUrl.getHost(),
startingUrl.getPort());
String baseHostComponent = hostComponentFrom(baseUrl.getProtocol(),
baseUrl.getHost(),
baseUrl.getPort());
updatedUrl = starting.replaceFirst(startingHostComponent, baseHostComponent);
} catch (MalformedURLException e) {
LOGGER.error("Failed to analyse default page URL: Starting URL: {}, Base URL: {}", starting, base);
LOGGER.error("URL analysis failed with exception:", e);
}
return updatedUrl;
}
private String hostComponentFrom(final String protocol, final String host, final int port) {
StringBuilder hostComponent = new StringBuilder(protocol);
hostComponent.append("://");
hostComponent.append(host);
if (port > 0) {
hostComponent.append(":");
hostComponent.append(port);
}
return hostComponent.toString();
}
/**
* Open the webdriver browser using a paramaterized URL. Parameters are
* represented in the URL using {0}, {1}, etc.
*/
public final void open(final String[] parameterValues) {
open(OpenMode.CHECK_URL_PATTERNS, parameterValues);
}
/**
* Opens page without checking URL patterns. Same as open(String...)) otherwise.
*/
public final void openUnchecked(final String... parameterValues) {
open(OpenMode.IGNORE_URL_PATTERNS, parameterValues);
}
private void open(final OpenMode openMode, final String... parameterValues) {
String startingUrl = getPageUrls().getStartingUrl(parameterValues);
LOGGER.debug("Opening page at url {}", startingUrl);
openPageAtUrl(startingUrl);
checkUrlPatterns(openMode);
initializePage();
LOGGER.debug("Page opened");
}
public final OpenWithParams open(final String urlTemplateName) {
return new OpenWithParams(this, urlTemplateName);
}
public static class OpenWithParams {
private PageObject pageObject;
private final String urlTemplateName;
public OpenWithParams(PageObject pageObject, String urlTemplateName) {
this.pageObject = pageObject;
this.urlTemplateName = urlTemplateName;
}
public void withParameters(String... parameters) {
pageObject.open(urlTemplateName, parameters);
}
}
public final void open(final String urlTemplateName,
final String[] parameterValues) {
open(OpenMode.CHECK_URL_PATTERNS, urlTemplateName, parameterValues);
}
/**
* Opens page without checking URL patterns. Same as {@link #open(String, String[])} otherwise.
*/
public final void openUnchecked(final String urlTemplateName,
final String[] parameterValues) {
open(OpenMode.IGNORE_URL_PATTERNS, urlTemplateName, parameterValues);
}
private void open(final OpenMode openMode, final String urlTemplateName,
final String[] parameterValues) {
String startingUrl = getPageUrls().getNamedUrl(urlTemplateName, parameterValues);
LOGGER.debug("Opening page at url {}", startingUrl);
openPageAtUrl(startingUrl);
checkUrlPatterns(openMode);
initializePage();
LOGGER.debug("Page opened");
}
/**
* Open the webdriver browser to the base URL, determined by the DefaultUrl
* annotation if present. If the DefaultUrl annotation is not present, the
* default base URL will be used. If the DefaultUrl annotation is present, a
* URL based on the current base url from the system-wide default url
* and the relative path provided in the DefaultUrl annotation will be used to
* determine the URL to open. For example, consider the following class:
*
*
* @DefaultUrl("http://localhost:8080/client/list")
* public class ClientList extends PageObject {
* ...
*
* @WhenPageOpens
* public void waitUntilTitleAppears() {...}
* }
*
*
* Suppose you are using a base URL of http://stage.acme.com. When you call open() for this class,
* it will open http://stage.acme.com/client/list. It will then invoke the waitUntilTitleAppears() method.
*/
final public void open() {
open(OpenMode.CHECK_URL_PATTERNS);
}
/**
* Opens page without checking URL patterns. Same as {@link #open()} otherwise.
*/
final public void openUnchecked() {
open(OpenMode.IGNORE_URL_PATTERNS);
}
private void open(final OpenMode openMode) {
String startingUrl = updateUrlWithBaseUrlIfDefined(getPageUrls().getStartingUrl());
openPageAtUrl(startingUrl);
checkUrlPatterns(openMode);
initializePage();
}
private void initializePage() {
addJQuerySupportIfRequired();
callWhenPageOpensMethods();
}
private void checkUrlPatterns(final OpenMode openMode) {
if (openMode == OpenMode.CHECK_URL_PATTERNS) {
ensurePageIsOnAMatchingUrl();
}
}
private void ensurePageIsOnAMatchingUrl() {
if (!matchesAnyUrl()) {
String currentUrl = getDriver().getCurrentUrl();
if (!compatibleWithUrl(currentUrl)) {
thisIsNotThePageYourLookingFor();
}
}
}
/**
* Use the @At annotation (if present) to check that a page object is displaying the correct page.
* Will throw an exception if the current URL does not match the expected one.
*/
public void shouldBeDisplayed() {
ensurePageIsOnAMatchingUrl();
}
private void thisIsNotThePageYourLookingFor() {
String errorDetails = "This is not the page you're looking for: "
+ "I was looking for a page compatible with " + this.getClass() + " but "
+ "I was at the URL " + getDriver().getCurrentUrl();
throw new WrongPageError(errorDetails);
}
final public void openAt(String relativeUrl) {
openPageAtUrl(updateUrlWithBaseUrlIfDefined(relativeUrl));
callWhenPageOpensMethods();
}
final public void openUrl(String absoluteUrl) {
openPageAtUrl(absoluteUrl);
callWhenPageOpensMethods();
}
/**
* Override this method
*/
public void callWhenPageOpensMethods() {
if (StepEventBus.getParallelEventBus().currentTestIsSuspended()) {
return;
}
for (Method annotatedMethod : methodsAnnotatedWithWhenPageOpens()) {
try {
annotatedMethod.setAccessible(true);
annotatedMethod.invoke(this);
} catch (Throwable e) {
LOGGER.error("Could not execute @WhenPageOpens annotated method: " + e.getMessage());
if (e instanceof InvocationTargetException) {
e = ((InvocationTargetException) e).getTargetException();
}
if (AssertionError.class.isAssignableFrom(e.getClass())) {
throw (AssertionError) e;
} else {
throw new UnableToInvokeWhenPageOpensMethods("Could not execute @WhenPageOpens annotated method: "
+ e.getMessage(), e);
}
}
}
}
private List methodsAnnotatedWithWhenPageOpens() {
List methods = MethodFinder.inClass(this.getClass()).getAllMethods();
List annotatedMethods = new ArrayList<>();
for (Method method : methods) {
if (method.getAnnotation(WhenPageOpens.class) != null) {
if (method.getParameterTypes().length == 0) {
annotatedMethods.add(method);
} else {
throw new UnableToInvokeWhenPageOpensMethods(
"Could not execute @WhenPageOpens annotated method: WhenPageOpens method cannot have parameters: " +
method);
}
}
}
return annotatedMethods;
}
public static String[] withParameters(final String... parameterValues) {
return parameterValues;
}
private void openPageAtUrl(final String startingUrl) {
String url = NormalizeUrlForm.ofUrl(startingUrl);
getDriver().get(url);
addJQuerySupportIfRequired();
}
/**
* Open an environment-specific page defined in the `serenity.conf` file under the `pages` section.
*
* @param pageName
*/
public void openPageNamed(String pageName) {
getDriver().get(environmentSpecificPageUrl(pageName));
}
public void navigateToPageNamed(String pageName) {
getDriver().navigate().to(environmentSpecificPageUrl(pageName));
}
private String environmentSpecificPageUrl(String pageName) {
return EnvironmentSpecificConfiguration.from(environmentVariables)
.getOptionalProperty("pages." + pageName)
.orElseThrow(() -> new NoSuchPageException(
"No page called " + pageName + " was specified in the serenity.conf file"));
}
public void clickOn(final WebElement webElement) {
element(webElement).click();
}
/**
* Returns true if at least one matching element is found on the page and is visible.
*/
public Boolean isElementVisible(final By byCriteria) {
return getRenderedView().elementIsDisplayed(byCriteria);
}
public void setDefaultBaseUrl(final String defaultBaseUrl) {
getPageUrls().overrideDefaultBaseUrl(defaultBaseUrl);
}
/**
* Returns true if the specified element has the focus.
*
* @deprecated Use element(webElement).hasFocus() instead
*/
public boolean hasFocus(final WebElement webElement) {
return element(webElement).hasFocus();
}
public void blurActiveElement() {
getJavascriptExecutorFacade().executeScript("document.activeElement.blur();");
}
protected JavascriptExecutorFacade getJavascriptExecutorFacade() {
if (javascriptExecutorFacade == null) {
javascriptExecutorFacade = new JavascriptExecutorFacade(driver);
}
return javascriptExecutorFacade;
}
/**
* Provides a fluent API for querying web elements.
*/
public T element(WebElement webElement) {
return net.serenitybdd.core.pages.WebElementFacadeImpl.wrapWebElement(getDriver(), webElement,
getImplicitWaitTimeout().toMillis(),
getWaitForTimeout().toMillis(),
nameOf(webElement));
}
private String nameOf(WebElement webElement) {
try {
return webElement.toString();
} catch (Exception e) {
return "Unknown web element";
}
}
public T $(WithLocator locator) {
return element(locator.getLocator());
}
public T $(WithByLocator locator) {
return element(locator.getLocator());
}
public T $(WebElement webElement) {
return element(webElement);
}
public T $(String xpathOrCssSelector, Object... arguments) {
return element(xpathOrCssSelector, arguments);
}
public T $(By bySelector) {
return element(bySelector);
}
public net.serenitybdd.core.pages.WebElementFacade $(ResolvableElement selector) {
return find(selector);
}
public ListOfWebElementFacades $$(ResolvableElement selector) {
return findAll(selector);
}
/**
* Return the text value of a given element
*/
public String textOf(WithLocator locator) {
return $(locator).getText();
}
public String textOf(WithByLocator locator) {
return $(locator).getText();
}
public String textOf(String xpathOrCssSelector, Object... arguments) {
return $(xpathOrCssSelector, arguments).getText();
}
public String textOf(By bySelector) {
return $(bySelector).getText();
}
/**
* Return the text value of a given element
*/
public String textContentOf(WithLocator locator) {
return $(locator).getTextContent();
}
public String textContentOf(WithByLocator locator) {
return $(locator).getTextContent();
}
public String textContentOf(String xpathOrCssSelector, Object... arguments) {
return $(xpathOrCssSelector, arguments).getTextContent();
}
public String textContentOf(By bySelector) {
return $(bySelector).getTextContent();
}
public ListOfWebElementFacades $$(String xpathOrCssSelector, Object... arguments) {
return findAll(xpathOrCssSelector, arguments);
}
public ListOfWebElementFacades $$(By bySelector) {
return findAll(bySelector);
}
/**
* Provides a fluent API for querying web elements.
*/
public T element(By bySelector) {
return net.serenitybdd.core.pages.WebElementFacadeImpl.wrapWebElement(getDriver(),
bySelector,
getImplicitWaitTimeout().toMillis(),
getWaitForTimeout().toMillis(),
bySelector.toString());
}
public T find(By selector) {
return element(selector);
}
public net.serenitybdd.core.pages.WebElementFacade find(ResolvableElement selector) {
return selector.resolveFor(this);
}
public ListOfWebElementFacades findAll(ResolvableElement selector) {
return findAllWithRetry((page) -> selector.resolveAllFor(this));
}
public T find(WithByLocator selector) {
return element(selector.getLocator());
}
public T find(WithLocator selector) {
return element(selector.getLocator());
}
public T find(List selectors) {
T element = null;
for (By selector : selectors) {
if (element == null) {
element = element(selector);
} else {
element = element.find(selector);
}
}
return element;
}
public T findBy(List selectors) {
T element = null;
for (String selector : selectors) {
if (element == null) {
element = element(selector);
} else {
element = element.findBy(selector);
}
}
return element;
}
public T findNested(By... selectors) {
T element = null;
for (By selector : selectors) {
if (element == null) {
element = element(selector);
} else {
element = element.findBy(selector);
}
}
return element;
}
public T find(String selector) {
return findBy(NewList.of(selector));
}
public T findNested(String... selectors) {
return findBy(NewList.of(selectors));
}
public Optional findFirst(String xpathOrCSSSelector) {
return findEach(xpathOrCSSSelector).findFirst();
}
public Optional findFirst(By bySelector) {
return findEach(bySelector).findFirst();
}
public Stream findEach(By bySelector) {
return findAll(bySelector).stream();
}
public Stream findEach(WithByLocator bySelector) {
return findAll(bySelector.getLocator()).stream();
}
public Stream findEach(WithLocator bySelector) {
return findAll(bySelector.getLocator()).stream();
}
/**
* FindEach will return a stream of WebElementFacades matching the described nested structure.
* Only the last selector will return a list; the initial selectors will be used to locate the list of elements.
*
* @param bySelectors
* @return
*/
public Stream findEach(By... bySelectors) {
if (bySelectors.length == 1) {
return findEach(bySelectors[0]);
}
return find(allButLastIn(bySelectors))
.thenFindAll(lastIn(bySelectors)).stream();
}
public Stream findEach(String... xpathOrCssSelectors) {
if (xpathOrCssSelectors.length == 1) {
return findEach(xpathOrCssSelectors[0]);
}
return findBy(allButLastIn(xpathOrCssSelectors))
.thenFindAll(lastIn(xpathOrCssSelectors)).stream();
}
public ListOfWebElementFacades findNestedElements(String... xpathOrCssSelectors) {
if (xpathOrCssSelectors.length == 1) {
return findAll(xpathOrCssSelectors[0]);
}
return findBy(allButLastIn(xpathOrCssSelectors))
.thenFindAll(lastIn(xpathOrCssSelectors));
}
private List allButLastIn(T[] selectors) {
List subList = new ArrayList<>();
subList.addAll(Arrays.asList(selectors).subList(0, selectors.length - 1));
return subList;
}
private T lastIn(T[] selectors) {
return selectors[selectors.length - 1];
}
public Stream findEach(String xpathOrCSSSelector) {
return findAll(xpathOrCSSSelector).stream();
}
public ListOfWebElementFacades findAll(By bySelector) {
return findAllWithRetry(page -> {
List matchingWebElements = new ArrayList<>();
for (WebElement webElement : driver.findElements(bySelector)) {
WebElementFacade element = element(webElement);
matchingWebElements.add(element);
}
return new ListOfWebElementFacades(matchingWebElements);
});
}
public ListOfWebElementFacades findAll(WithLocator bySelector) {
return findAllWithRetry((page) -> page.findAll(bySelector.getLocator()));
}
public ListOfWebElementFacades findAll(WithByLocator bySelector) {
return findAllWithRetry((page) -> page.findAll(bySelector.getLocator()));
}
/**
* Provides a fluent API for querying web elements.
*/
public T element(String xpathOrCssSelector, Object... arguments) {
return element(xpathOrCssSelector(withArguments(xpathOrCssSelector, arguments)));
}
public T findBy(String xpathOrCssSelector, Object... arguments) {
return element(withArguments(xpathOrCssSelector, arguments));
}
public Optional findFirst(String xpathOrCssSelector, Object... arguments) {
return findAll(xpathOrCssSelector, arguments).stream().findFirst();
}
public ListOfWebElementFacades findAll(String xpathOrCssSelector, Object... arguments) {
return findAllWithRetry((page) -> page.findAll(xpathOrCssSelector(withArguments(xpathOrCssSelector, arguments))));
}
public boolean containsElements(By bySelector) {
return !findAll(bySelector).isEmpty();
}
public boolean containsElements(String xpathOrCssSelector, Object... arguments) {
return !findAll(xpathOrCssSelector, arguments).isEmpty();
}
public Object evaluateJavascript(final String script) {
if (StepEventBus.getParallelEventBus().isDryRun()) {
return "";
}
addJQuerySupportIfRequired();
JavascriptExecutorFacade js = new JavascriptExecutorFacade(driver);
return js.executeScript(script);
}
public Object evaluateAsyncJavascript(final String script) {
if (StepEventBus.getParallelEventBus().isDryRun()) {
return "";
}
addJQuerySupportIfRequired();
JavascriptExecutorFacade js = new JavascriptExecutorFacade(driver);
return js.executeAsyncScript(script);
}
public Object evaluateJavascript(final String script, final Object... params) {
if (StepEventBus.getParallelEventBus().isDryRun()) {
return "";
}
addJQuerySupportIfRequired();
JavascriptExecutorFacade js = new JavascriptExecutorFacade(driver);
return js.executeScript(script, params);
}
public Object evaluateAsyncJavascript(final String script, final Object... params) {
if (StepEventBus.getParallelEventBus().isDryRun()) {
return "";
}
addJQuerySupportIfRequired();
JavascriptExecutorFacade js = new JavascriptExecutorFacade(driver);
return js.executeAsyncScript(script, params);
}
public void addJQuerySupportIfRequired() {
if (javascriptIsSupportedIn(getDriver()) && jqueryIntegrationIsActivated() && driverIsJQueryCompatible() && pageIsLoaded()) {
JQueryEnabledPage jQueryEnabledPage = JQueryEnabledPage.withDriver(getDriver());
jQueryEnabledPage.activateJQuery();
}
}
protected boolean driverIsJQueryCompatible() {
try {
if (getDriver() instanceof WebDriverFacade) {
return SupportedWebDriver.forClass(((WebDriverFacade) getDriver()).getDriverClass())
.supportsJavascriptInjection();
}
return SupportedWebDriver.forClass(getDriver().getClass()).supportsJavascriptInjection();
} catch (IllegalArgumentException probablyAMockedDriver) {
return false;
}
}
private boolean enableJQuery = false;
public void enableJQuery() { this.enableJQuery = true; }
private Boolean jqueryIntegrationIsActivated() {
return enableJQuery || SERENITY_JQUERY_INTEGRATION.booleanFrom(environmentVariables, false);
}
public RadioButtonGroup inRadioButtonGroup(String name) {
return new RadioButtonGroup(getDriver().findElements(By.name(name)));
}
private boolean pageIsLoaded() {
try {
return (driverIsInstantiated() && getDriver().getCurrentUrl() != null);
} catch (WebDriverException e) {
return false;
}
}
protected boolean driverIsInstantiated() {
if (getDriver() instanceof WebDriverFacade) {
return ((WebDriverFacade) getDriver()).isEnabled() && ((WebDriverFacade) getDriver()).isInstantiated();
}
return true;
}
public ThucydidesFluentWait waitForWithRefresh() {
return new FluentWaitWithRefresh<>(getDriver(), webdriverClock, sleeper)
.withTimeout(getWaitForTimeout().toMillis(), TimeUnit.MILLISECONDS)
.pollingEvery(WAIT_FOR_ELEMENT_PAUSE_LENGTH, TimeUnit.MILLISECONDS)
.ignoring(NoSuchElementException.class, NoSuchFrameException.class);
}
public SerenityFluentWait waitForCondition() {
return (SerenityFluentWait) new SerenityFluentWait(getDriver(), webdriverClock, sleeper)
.withTimeout(getWaitForTimeout())
.pollingEvery(Duration.ofMillis(WAIT_FOR_ELEMENT_PAUSE_LENGTH))
.ignoring(NoSuchElementException.class, NoSuchFrameException.class);
}
public WebElementFacade waitFor(WebElement webElement) {
return getRenderedView().waitFor(webElement);
}
public WebElementFacade waitFor(WebElementFacade webElement) {
return getRenderedView().waitFor(webElement);
}
public WebElementFacadeWait waitForElement() {
return getRenderedView().waitForElement();
}
public Alert getAlert() {
return getDriver().switchTo().alert();
}
public Actions withAction() {
WebDriver proxiedDriver = (getDriver() instanceof WebDriverFacade) ?
((WebDriverFacade) getDriver()).getProxiedDriver() : getDriver();
return new SerenityActions(proxiedDriver);
}
public class FieldEntry {
private final CharSequence[] keysToSend;
public FieldEntry(final CharSequence... keysToSend) {
this.keysToSend = keysToSend;
}
public void into(final WebElement field) {
element(field).type(keysToSend);
}
public void into(final net.serenitybdd.core.pages.WebElementFacade field) {
field.type(keysToSend);
}
public void into(final By bySelector) {
WebElement field = getDriver().findElement(bySelector);
into(field);
}
public void into(String selector) {
$(selector).type(keysToSend);
}
}
private void notifyScreenChange() {
StepEventBus.getParallelEventBus().notifyScreenChange();
}
protected ThucydidesFluentAdapter fluent() {
return new ThucydidesFluentAdapter(getDriver());
}
public T moveTo(String xpathOrCssSelector, Object... arguments) {
if (!driverIsDisabled()) {
withAction().moveToElement(findBy(xpathOrCssSelector, arguments)).perform();
}
return findBy(xpathOrCssSelector, arguments);
}
public T moveTo(By locator) {
if (!driverIsDisabled()) {
withAction().moveToElement(find(locator)).perform();
}
return find(locator);
}
public void waitForAngularRequestsToFinish() {
JavascriptCompatibleVersion.of(getDriver()).ifPresent(
driver -> WaitForAngular.withDriver(driver).untilAngularRequestsHaveFinished()
);
}
Inflector inflection = Inflector.getInstance();
@Override
public String toString() {
return inflection.of(getClass().getSimpleName())
.inHumanReadableForm().toString();
}
protected ListOfWebElementFacades findAllWithRetry(Function finder) {
return new FindAllWithRetry(environmentVariables).find(finder, this);
}
}