Skip to content

Commit

Permalink
Feature/#770 refactor fluentdriver (#818)
Browse files Browse the repository at this point in the history
* #770: Remove unnecessary private methods

* #770: Change private method order in FluentDriver

* #770: Add javadoc and null check to ImageUtils

* #770: Remove getSearch() private method

* #770: Extract argument and state validation, and elaborate error messages

* #770: Extract await() logic from FluentDriver to FluentDriverWait

* #770: Extract retrieveing capabilities logic from FluentDriver to FluentDriverCapabiltiesProvider

* #770: Extract screenshot persisting logic from FluentDriver to FluentDriverScreenshotPersister

* #770: Extract taking html dump from FluentDriver to FluentDriverHtmlDumper

* #770: Deduplicate getting wrapped element

* #770: Remove NOPMD comment

* #770: Move wrapped element retrieval logic to ElementUtils

* #770: Extract timeout configuration logic to FluentDriverTimeoutConfigurer

* #770: Rename capabilities provider and update javadoc

* #770: Fix test file names in unit test

* #770: Add some unit tests to FluentDriver

* #770: Remove null check and fix javadoc in ImageUtils

* #770: Extract some methods to decrease cognitive complexity

* #770: Remove unused import

* #770: Update temp file creation logic in unit tests

* #770: Fix Java 8 compatibility issue with charset
  • Loading branch information
Tamás Balog authored and slawekradzyminski committed Apr 24, 2019
1 parent f09dfca commit cbf9d71
Show file tree
Hide file tree
Showing 17 changed files with 784 additions and 185 deletions.
249 changes: 68 additions & 181 deletions fluentlenium-core/src/main/java/org/fluentlenium/core/FluentDriver.java

Large diffs are not rendered by default.

@@ -0,0 +1,65 @@
package org.fluentlenium.core;

import static java.util.Objects.requireNonNull;

import org.fluentlenium.configuration.Configuration;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Paths;
import java.util.function.Supplier;

/**
* Takes HTML dump..
*/
public class FluentDriverHtmlDumper {

private final Configuration configuration;

public FluentDriverHtmlDumper(Configuration configuration) {
this.configuration = requireNonNull(configuration);
}

/**
* Dumps the HTML provided by the html supplier to a file.
* <p>
* If the configuration is set with an html dump path, the argument file name will be concatenated to that, creating
* the destination file path, otherwise the destination file will be the argument file name.
* <p>
* If an error occurs during taking the HTML dump, the dump file is still created, but it will contain a message
* that HTML dump could not be taken.
*
* @param fileName the file name to dump the HTML to
* @throws RuntimeException when an error occurs during dumping HTML
*/
public void takeHtmlDump(String fileName, Supplier<String> htmlSupplier) {
File destFile = null;
try {
destFile = getDestinationFile(fileName);
FileUtils.write(destFile, htmlSupplier.get(), "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);
} catch (IOException ioe) {
throw new RuntimeException("Error when dumping HTML", e); //NOPMD PreserveStackTrace
}
}
}

private File getDestinationFile(String fileName) {
File destFile;
if (configuration.getHtmlDumpPath() == null) {
destFile = new File(fileName);
} else {
destFile = Paths.get(configuration.getHtmlDumpPath(), fileName).toFile();
}
return destFile;
}
}
@@ -0,0 +1,62 @@
package org.fluentlenium.core;

import static java.util.Objects.requireNonNull;

import org.fluentlenium.configuration.Configuration;
import org.fluentlenium.utils.ImageUtils;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.UnhandledAlertException;
import org.openqa.selenium.WebDriver;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;

/**
* Persists a screenshot to a target file.
*/
public class FluentDriverScreenshotPersister {

private final Configuration configuration;
private final WebDriver driver;

public FluentDriverScreenshotPersister(Configuration configuration, WebDriver driver) {
this.configuration = requireNonNull(configuration);
this.driver = driver;
}

/**
* Persists a screenshot to the argument target file using the screenshot path from {@link Configuration}.
* <p>
* If there is no screenshot path set in the configuration, the file will be the argument file name,
* otherwise the argument file name will be concatenated to the screenshot path to create the destination file.
*
* @param fileName the target file to save the screenshot to
* @throws RuntimeException when an error occurs during taking the screenshot
*/
public void persistScreenshot(String fileName) {
try {
File destFile;
if (configuration.getScreenshotPath() == null) {
destFile = new File(fileName);
} else {
destFile = Paths.get(configuration.getScreenshotPath(), fileName).toFile();
}
FileUtils.writeByteArrayToFile(destFile, prepareScreenshot());
} catch (IOException e) {
throw new RuntimeException("Error when taking the screenshot", e);
}
}

private byte[] prepareScreenshot() {
byte[] screenshot;
try {
screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
} catch (UnhandledAlertException uae) {
screenshot = new ImageUtils(driver).handleAlertAndTakeScreenshot();
}
return screenshot;
}
}
@@ -0,0 +1,57 @@
package org.fluentlenium.core;

import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

import org.fluentlenium.configuration.Configuration;
import org.openqa.selenium.WebDriver;

/**
* Configures a {@link WebDriver} instance with timeouts from a {@link Configuration}.
*/
public class FluentDriverTimeoutConfigurer {

private final Configuration configuration;
private final WebDriver driver;

public FluentDriverTimeoutConfigurer(Configuration configuration, WebDriver driver) {
this.configuration = requireNonNull(configuration);
this.driver = driver;
}

/**
* Configures a {@link WebDriver} instance with timeouts from a {@link Configuration}.
* <p>
* Configures the following options:
* <ul>
* <li>page load timeout</li>
* <li>implicitly wait</li>
* <li>script timeout</li>
* </ul>
*/
public void configureDriver() {
if (driver != null && driver.manage() != null && driver.manage().timeouts() != null) {
configurePageLoadTimeout();
configureImplicitlyWait();
configureScriptTimeout();
}
}

private void configurePageLoadTimeout() {
if (configuration.getPageLoadTimeout() != null) {
driver.manage().timeouts().pageLoadTimeout(configuration.getPageLoadTimeout(), MILLISECONDS);
}
}

private void configureImplicitlyWait() {
if (configuration.getImplicitlyWait() != null) {
driver.manage().timeouts().implicitlyWait(configuration.getImplicitlyWait(), MILLISECONDS);
}
}

private void configureScriptTimeout() {
if (configuration.getScriptTimeout() != null) {
driver.manage().timeouts().setScriptTimeout(configuration.getScriptTimeout(), MILLISECONDS);
}
}
}
@@ -0,0 +1,46 @@
package org.fluentlenium.core;

import static java.util.Objects.requireNonNull;

import org.fluentlenium.configuration.Configuration;
import org.fluentlenium.core.wait.FluentWait;

/**
* Creates and configures a {@link FluentWait} from a {@link Configuration} to be used via {@link FluentDriver}.
*/
public class FluentDriverWait {

private final Configuration configuration;

public FluentDriverWait(Configuration configuration) {
this.configuration = requireNonNull(configuration);
}

/**
* Creates a {@link FluentWait} instance with the argument {@link FluentControl},
* and configures the FluentWait with the {@code awaitAtMost} and {@code pollingEvery} values from
* a {@link Configuration} if they are set in that configuration.
*
* @return the configured FluentWait
*/
public FluentWait await(FluentControl control) {
FluentWait fluentWait = new FluentWait(control);
configureWithAwaitAtMost(fluentWait);
configureWithPollingEvery(fluentWait);
return fluentWait;
}

private void configureWithAwaitAtMost(FluentWait fluentWait) {
Long atMost = configuration.getAwaitAtMost();
if (atMost != null) {
fluentWait.atMost(atMost);
}
}

private void configureWithPollingEvery(FluentWait fluentWait) {
Long pollingEvery = configuration.getAwaitPollingEvery();
if (pollingEvery != null) {
fluentWait.pollingEvery(pollingEvery);
}
}
}
@@ -0,0 +1,34 @@
package org.fluentlenium.core;

import org.openqa.selenium.Capabilities;
import org.openqa.selenium.HasCapabilities;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WrapsDriver;

/**
* Provides wrapped WebDriver capabilities for {@link FluentDriver}.
*/
public class FluentDriverWrappedCapabilitiesProvider {

/**
* Goes through all the underlying wrapped drivers ({@link WrapsDriver}) in the argument {@link WebDriver},
* and returns the {@link Capabilities} of the innermost wrapped driver.
*
* @return capabilities of the innermost wrapped driver.
*/
public Capabilities getCapabilities(WebDriver driver) {
WebDriver currentDriver = driver;
Capabilities capabilities = capabilitiesOf(currentDriver);
while (currentDriver instanceof WrapsDriver && capabilities == null) {
currentDriver = ((WrapsDriver) currentDriver).getWrappedDriver();
capabilities = capabilitiesOf(currentDriver);
}
return capabilities;
}

private Capabilities capabilitiesOf(WebDriver currentDriver) {
return currentDriver instanceof HasCapabilities
? ((HasCapabilities) currentDriver).getCapabilities()
: null;
}
}
@@ -1,5 +1,7 @@
package org.fluentlenium.core.components;

import static org.fluentlenium.core.domain.ElementUtils.getWrappedElement;

import org.fluentlenium.core.FluentControl;
import org.fluentlenium.core.proxy.LocatorProxies;
import org.fluentlenium.core.proxy.ProxyElementListener;
Expand Down Expand Up @@ -160,7 +162,7 @@ public void proxyElementFound(Object proxy, ElementLocator locator, List<WebElem

private WebElement unwrapElement(WebElement element) {
if (element instanceof WrapsElement) {
WebElement wrappedElement = ((WrapsElement) element).getWrappedElement();
WebElement wrappedElement = getWrappedElement(element);
if (wrappedElement != element && wrappedElement != null) { // NOPMD CompareObjectsWithEquals
return unwrapElement(wrappedElement);
}
Expand Down
Expand Up @@ -3,6 +3,8 @@
import org.fluentlenium.core.conditions.FluentConditions;
import org.fluentlenium.core.conditions.message.MessageProxy;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.WrapsElement;

/**
* Utility class for elements.
Expand All @@ -24,4 +26,14 @@ public static NoSuchElementException noSuchElementException(String messageContex
String message = MessageProxy.message(messageBuilder);
return new NoSuchElementException(message);
}

/**
* Returns the wrapped {@link WebElement} from the argument element.
*
* @param element the element to get the wrapped element of
* @return the wrapped webelement
*/
public static WebElement getWrappedElement(WebElement element) {
return ((WrapsElement) element).getWrappedElement();
}
}
Expand Up @@ -19,9 +19,12 @@
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;

/**
* Provides logic for screenshot and image manipulation and conversion.
*/
public class ImageUtils {

public static final String ERROR_WHILE_CONVERTING_IMAGE = "Error while converting image";
private static final String ERROR_WHILE_CONVERTING_IMAGE = "Error while converting image";
private final WebDriver driver;

public ImageUtils(WebDriver driver) {
Expand All @@ -32,6 +35,14 @@ public WebDriver getDriver() {
return driver;
}

/**
* Accepts the current alert window and takes a screenshot.
* <p>
* The FluentLenium logo is also added on to the screenshot.
*
* @return the screenshot as a byte array
* @throws RuntimeException if a problem occurred during reading the screenshot file
*/
public byte[] handleAlertAndTakeScreenshot() {
String alertText = getDriver().switchTo().alert().getText();
getDriver().switchTo().alert().accept();
Expand All @@ -48,6 +59,14 @@ public byte[] handleAlertAndTakeScreenshot() {
}
}

/**
* Converts the file referenced by the argument file name to a {@link BufferedImage}.
*
* @param fileName the name of the file to convert
* @return the converted BufferedImage
* @throws FileNotFoundException if the argument file cannot be found
* @throws RuntimeException if a problem occurred during image conversion
*/
public static BufferedImage toBufferedImage(String fileName) throws FileNotFoundException {
InputStream is = new FileInputStream(new File(fileName));
try {
Expand Down Expand Up @@ -79,7 +98,8 @@ private BufferedImage stitchImages(BufferedImage image1, BufferedImage image2, b
g.drawImage(image2, image1.getWidth() - image2.getWidth(), image1.getHeight() - image2.getHeight(), null);
return stitchedImage;
} else {
BufferedImage stitchedImage = new BufferedImage(image1.getWidth(), image1.getHeight() + image2.getHeight(), BufferedImage.TYPE_INT_RGB);
BufferedImage stitchedImage = new BufferedImage(image1.getWidth(), image1.getHeight() + image2.getHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics graphics = stitchedImage.getGraphics();
graphics.drawImage(image1, 0, 0, null);
graphics.drawImage(image2, 0, image1.getHeight(), null);
Expand Down

0 comments on commit cbf9d71

Please sign in to comment.