package org.fluentlenium.core;
import lombok.experimental.Delegate;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.fluentlenium.configuration.Configuration;
import org.fluentlenium.core.action.KeyboardActions;
import org.fluentlenium.core.action.MouseActions;
import org.fluentlenium.core.action.WindowAction;
import org.fluentlenium.core.alert.Alert;
import org.fluentlenium.core.alert.AlertImpl;
import org.fluentlenium.core.components.ComponentInstantiator;
import org.fluentlenium.core.components.ComponentsManager;
import org.fluentlenium.core.css.CssControl;
import org.fluentlenium.core.css.CssControlImpl;
import org.fluentlenium.core.domain.FluentList;
import org.fluentlenium.core.domain.FluentWebElement;
import org.fluentlenium.core.events.ComponentsEventsRegistry;
import org.fluentlenium.core.events.EventsRegistry;
import org.fluentlenium.core.inject.DefaultContainerInstanciator;
import org.fluentlenium.core.inject.FluentInjector;
import org.fluentlenium.core.script.FluentJavascript;
import org.fluentlenium.core.search.Search;
import org.fluentlenium.core.search.SearchFilter;
import org.fluentlenium.core.wait.FluentWait;
import org.fluentlenium.utils.UrlUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.HasCapabilities;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.internal.WrapsDriver;
import org.openqa.selenium.internal.WrapsElement;
import org.openqa.selenium.support.events.EventFiringWebDriver;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Util Class which offers some shortcut to webdriver methods
*/
@SuppressWarnings("PMD.GodClass")
public class FluentDriver implements FluentControl { // NOPMD GodClass
@Delegate
private final Configuration configuration;
@Delegate(types = ComponentInstantiator.class)
private final ComponentsManager componentsManager;
private final EventsRegistry events;
private final ComponentsEventsRegistry componentsEventsRegistry;
@Delegate
private final FluentInjector fluentInjector;
@Delegate
private final CssControl cssControl; // NOPMD UnusedPrivateField
private final Search search;
private final WebDriver driver;
private final MouseActions mouseActions;
private final KeyboardActions keyboardActions;
private final WindowAction windowAction;
/**
* Wrap the driver into a Fluent driver.
*
* @param driver underlying selenium driver
* @param configuration configuration
* @param adapter adapter fluent control interface
*/
public FluentDriver(WebDriver driver, Configuration configuration, FluentControl adapter) {
this.configuration = configuration;
componentsManager = new ComponentsManager(adapter);
this.driver = driver;
search = new Search(driver, this, componentsManager, adapter);
if (driver instanceof EventFiringWebDriver) {
events = new EventsRegistry(this);
componentsEventsRegistry = new ComponentsEventsRegistry(events, componentsManager);
} else {
events = null;
componentsEventsRegistry = null;
}
mouseActions = new MouseActions(driver);
keyboardActions = new KeyboardActions(driver);
fluentInjector = new FluentInjector(adapter, events, componentsManager, new DefaultContainerInstanciator(this));
cssControl = new CssControlImpl(adapter, adapter);
windowAction = new WindowAction(adapter, componentsManager.getInstantiator(), driver);
configureDriver(); // NOPMD ConstructorCallsOverridableMethod
}
private void configureDriver() {
if (getDriver() != null && getDriver().manage() != null && getDriver().manage().timeouts() != null) {
if (configuration.getPageLoadTimeout() != null) {
getDriver().manage().timeouts().pageLoadTimeout(configuration.getPageLoadTimeout(), TimeUnit.MILLISECONDS);
}
if (configuration.getImplicitlyWait() != null) {
getDriver().manage().timeouts().implicitlyWait(configuration.getImplicitlyWait(), TimeUnit.MILLISECONDS);
}
if (configuration.getScriptTimeout() != null) {
getDriver().manage().timeouts().setScriptTimeout(configuration.getScriptTimeout(), TimeUnit.MILLISECONDS);
}
}
}
@Override
public void takeHtmlDump() {
takeHtmlDump(new Date().getTime() + ".html");
}
@Override
public void takeHtmlDump(String fileName) {
File destFile = null;
try {
if (configuration.getHtmlDumpPath() == null) {
destFile = new File(fileName);
} else {
destFile = Paths.get(configuration.getHtmlDumpPath(), fileName).toFile();
}
String html;
synchronized (FluentDriver.class) {
html = $("html").first().html();
}
FileUtils.write(destFile, html, "UTF-8");
} catch (Exception e) {
if (destFile == null) {
destFile = new File(fileName);
}
try {
PrintWriter printWriter = new PrintWriter(destFile, "UTF-8");
printWriter.write("Can't dump HTML");
printWriter.println();
e.printStackTrace(printWriter);
IOUtils.closeQuietly(printWriter);
} catch (IOException e1) {
throw new RuntimeException("error when dumping HTML", e); //NOPMD PreserveStackTrace
}
}
}
@Override
public boolean canTakeScreenShot() {
return getDriver() instanceof TakesScreenshot;
}
@Override
public void takeScreenShot() {
takeScreenShot(new Date().getTime() + ".png");
}
@Override
public void takeScreenShot(String fileName) {
if (!canTakeScreenShot()) {
throw new WebDriverException("Current browser doesn't allow taking screenshot.");
}
File scrFile = ((TakesScreenshot) getDriver()).getScreenshotAs(OutputType.FILE);
try {
File destFile;
if (configuration.getScreenshotPath() == null) {
destFile = new File(fileName);
} else {
destFile = Paths.get(configuration.getScreenshotPath(), fileName).toFile();
}
FileUtils.copyFile(scrFile, destFile);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("error when taking the snapshot", e);
}
FileUtils.deleteQuietly(scrFile);
}
@Override
public WebDriver getDriver() {
return driver;
}
private Search getSearch() {
return search;
}
@Override
public EventsRegistry events() {
if (events == null) {
throw new IllegalStateException("An EventFiringWebDriver instance is required to use events. "
+ "You should set 'eventsEnabled' configuration property to 'true' "
+ "or override newWebDriver() to build an EventFiringWebDriver.");
}
return events;
}
@Override
public MouseActions mouse() {
return mouseActions;
}
@Override
public KeyboardActions keyboard() {
return keyboardActions;
}
@Override
public WindowAction window() {
return windowAction;
}
@Override
public FluentWait await() {
FluentWait fluentWait = new FluentWait(this);
Long atMost = configuration.getAwaitAtMost();
if (atMost != null) {
fluentWait.atMost(atMost);
}
Long pollingEvery = configuration.getAwaitPollingEvery();
if (pollingEvery != null) {
fluentWait.pollingEvery(pollingEvery);
}
return fluentWait;
}
@Override
public Set<Cookie> getCookies() {
return getDriver().manage().getCookies();
}
@Override
public Cookie getCookie(String name) {
return getDriver().manage().getCookieNamed(name);
}
private String buildUrl(String url) {
String currentUrl = getDriver().getCurrentUrl();
String baseUrl = UrlUtils.sanitizeBaseUrl(getBaseUrl(), currentUrl);
return UrlUtils.concat(baseUrl, url);
}
@Override
public String url() {
String baseUrl = buildUrl(null);
String currentUrl = getDriver().getCurrentUrl();
if (currentUrl != null && baseUrl != null && currentUrl.startsWith(baseUrl)) {
currentUrl = currentUrl.substring(baseUrl.length());
}
return currentUrl;
}
@Override
public String pageSource() {
return getDriver().getPageSource();
}
@Override
public <P extends FluentPage> P goTo(P page) {
if (page == null) {
throw new IllegalArgumentException("Page is mandatory");
}
page.go();
return page;
}
@Override
public void goTo(String url) {
if (url == null) {
throw new IllegalArgumentException("Url is mandatory");
}
getDriver().get(buildUrl(url));
}
@Override
public void goToInNewTab(String url) {
if (url == null) {
throw new IllegalArgumentException("Url is mandatory");
}
String newTab;
synchronized (getClass()) {
Set<String> initialTabs = getDriver().getWindowHandles();
executeScript("window.open('" + buildUrl(url) + "', '_blank');");
Set<String> tabs = getDriver().getWindowHandles();
tabs.removeAll(initialTabs);
newTab = tabs.iterator().next();
}
getDriver().switchTo().window(newTab);
}
@Override
public Capabilities capabilities() {
WebDriver currentDriver = getDriver();
Capabilities capabilities = currentDriver instanceof HasCapabilities
? ((HasCapabilities) currentDriver).getCapabilities()
: null;
while (currentDriver instanceof WrapsDriver && capabilities == null) {
currentDriver = ((WrapsDriver) currentDriver).getWrappedDriver();
capabilities = currentDriver instanceof HasCapabilities ? ((HasCapabilities) currentDriver).getCapabilities() : null;
}
return capabilities;
}
@Override
public FluentJavascript executeScript(String script, Object... args) {
return new FluentJavascript((JavascriptExecutor) getDriver(), false, script, args);
}
@Override
public FluentJavascript executeAsyncScript(String script, Object... args) {
return new FluentJavascript((JavascriptExecutor) getDriver(), true, script, args);
}
@Override
public FluentList<FluentWebElement> $(String selector, SearchFilter... filters) {
return find(selector, filters);
}
@Override
public FluentWebElement el(String selector, SearchFilter... filters) {
return find(selector, filters).first();
}
@Override
public FluentList<FluentWebElement> $(SearchFilter... filters) {
return find(filters);
}
@Override
public FluentWebElement el(SearchFilter... filters) {
return find(filters).first();
}
@Override
public FluentList<FluentWebElement> $(By locator, SearchFilter... filters) {
return find(locator, filters);
}
@Override
public FluentWebElement el(By locator, SearchFilter... filters) {
return find(locator, filters).first();
}
@Override
public FluentList<FluentWebElement> find(String selector, SearchFilter... filters) {
return getSearch().find(selector, filters);
}
@Override
public FluentList<FluentWebElement> find(By locator, SearchFilter... filters) {
return getSearch().find(locator, filters);
}
@Override
public FluentList<FluentWebElement> find(SearchFilter... filters) {
return getSearch().find(filters);
}
@Override
public FluentList<FluentWebElement> find(List<WebElement> rawElements) {
return getSearch().find(rawElements);
}
@Override
public FluentList<FluentWebElement> $(List<WebElement> rawElements) {
return getSearch().$(rawElements);
}
@Override
public FluentWebElement el(WebElement rawElement) {
return getSearch().el(rawElement);
}
@Override
public void switchTo(FluentList<? extends FluentWebElement> elements) {
switchTo(elements.first());
}
@Override
public void switchTo(FluentWebElement element) {
if (null == element || !"iframe".equals(element.tagName())) {
getDriver().switchTo().defaultContent();
} else {
WebElement target = element.getElement();
while (target instanceof WrapsElement && target != ((WrapsElement) target).getWrappedElement()) {
target = ((WrapsElement) target).getWrappedElement();
}
getDriver().switchTo().frame(target);
}
}
@Override
public void switchTo() {
switchTo((FluentWebElement) null);
}
@Override
public void switchToDefault() {
switchTo((FluentWebElement) null);
}
@Override
public Alert alert() {
return new AlertImpl(getDriver());
}
/**
* Quit the underlying web driver and release fluent driver resources.
*/
public void quit() {
if (getDriver() != null) {
getDriver().quit();
}
releaseFluent();
}
/**
* Release fluent driver resources.
*/
public void releaseFluent() {
fluentInjector.release();
if (componentsEventsRegistry != null) {
componentsEventsRegistry.close();
}
}
}