Skip to content

Commit

Permalink
Feature/38 enhance element property getters logging (#54)
Browse files Browse the repository at this point in the history
* Added logPageSource parameter to settings

* Added logging of values for getAttribute() and getText() of Element

* Enhanced LocalizationManager to get value from core if the key is missed in passed assembly

* Added logging to ElementStateProviders' waiting functions
  • Loading branch information
mialeska authored Jun 23, 2020
1 parent fc42830 commit db30415
Show file tree
Hide file tree
Showing 26 changed files with 352 additions and 52 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.aquality-automation</groupId>
<artifactId>aquality-selenium-core</artifactId>
<version>1.1.0</version>
<version>1.2.0</version>

<packaging>jar</packaging>
<name>Aquality Selenium Core</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ public interface ILoggerConfiguration {
* @return language used for logging.
*/
String getLanguage();

/**
* Perform page source logging in case of catastrophic failures or not.
* @return true if enabled, false otherwise.
*/
boolean logPageSource();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@
public class LoggerConfiguration implements ILoggerConfiguration {

private static final String DEFAULT_LANGUAGE = "en";
private final ISettingsFile settingsFile;
private final String language;
private final boolean doLogPageSource;

@Inject
public LoggerConfiguration(ISettingsFile settingsFile){
this.settingsFile = settingsFile;
language = settingsFile.getValueOrDefault("/logger/language", DEFAULT_LANGUAGE).toString();
doLogPageSource = Boolean.parseBoolean(
settingsFile.getValueOrDefault("/logger/logPageSource", true).toString());
}

@Override
public String getLanguage() {
return settingsFile.getValueOrDefault("/logger/language", DEFAULT_LANGUAGE).toString();
return language;
}

@Override
public boolean logPageSource() {
return doLogPageSource;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package aquality.selenium.core.elements;

import aquality.selenium.core.elements.interfaces.IElementCacheHandler;
import aquality.selenium.core.localization.ILocalizedLogger;
import aquality.selenium.core.elements.interfaces.ILogElementState;
import aquality.selenium.core.waitings.IConditionalWait;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
Expand All @@ -24,13 +24,13 @@ public class CachedElementStateProvider extends ElementStateProvider {
private final By locator;
private final IConditionalWait conditionalWait;
private final IElementCacheHandler elementCacheHandler;
private final ILocalizedLogger localizedLogger;

public CachedElementStateProvider(By locator, IConditionalWait conditionalWait, IElementCacheHandler elementCacheHandler, ILocalizedLogger localizedLogger) {
public CachedElementStateProvider(By locator, IConditionalWait conditionalWait,
IElementCacheHandler elementCacheHandler, ILogElementState logger) {
super(logger);
this.locator = locator;
this.conditionalWait = conditionalWait;
this.elementCacheHandler = elementCacheHandler;
this.localizedLogger = localizedLogger;
}

protected List<Class<? extends Exception>> getHandledExceptions() {
Expand All @@ -52,11 +52,11 @@ protected boolean tryInvokeFunction(Predicate<WebElement> predicate, List<Class<
}
}

protected boolean waitForCondition(BooleanSupplier condition, String conditionName, Duration timeout) {
protected boolean waitForCondition(BooleanSupplier condition, String conditionKeyPart, Duration timeout) {
logElementState("loc.wait.for.state", conditionKeyPart);
boolean result = conditionalWait.waitFor(condition, timeout);
if (!result) {
String timeoutString = timeout == null ? "" : String.format("%1$s s.", timeout.getSeconds());
localizedLogger.warn("loc.element.not.in.state", locator, conditionName.toUpperCase(), timeoutString);
logElementState("loc.wait.for.state.failed", conditionKeyPart);
}
return result;
}
Expand All @@ -69,10 +69,12 @@ public boolean isClickable() {
@Override
public void waitForClickable(Duration timeout) {
String errorMessage = String.format("Element %1$s has not become clickable after timeout.", locator);
String conditionKeyPart = elementClickable().getStateName();
try {
logElementState("loc.wait.for.state", conditionKeyPart);
conditionalWait.waitForTrue(this::isClickable, timeout, null, errorMessage);
} catch (TimeoutException e) {
localizedLogger.error("loc.element.not.in.state", elementClickable().getStateName(), ". ".concat(e.getMessage()));
logElementState("loc.wait.for.state.failed", conditionKeyPart);
throw new org.openqa.selenium.TimeoutException(e.getMessage(), e);
}
}
Expand All @@ -84,12 +86,12 @@ public boolean isDisplayed() {

@Override
public boolean waitForDisplayed(Duration timeout) {
return waitForCondition(() -> tryInvokeFunction(WebElement::isDisplayed), ElementState.DISPLAYED.toString(), timeout);
return waitForCondition(() -> tryInvokeFunction(WebElement::isDisplayed), "displayed", timeout);
}

@Override
public boolean waitForNotDisplayed(Duration timeout) {
return waitForCondition(() -> !isDisplayed(), "invisible or absent", timeout);
return waitForCondition(() -> !isDisplayed(), "not.displayed", timeout);
}

@Override
Expand All @@ -99,12 +101,12 @@ public boolean isExist() {

@Override
public boolean waitForExist(Duration timeout) {
return waitForCondition(() -> tryInvokeFunction(element -> true), ElementState.EXISTS_IN_ANY_STATE.toString(), timeout);
return waitForCondition(() -> tryInvokeFunction(element -> true), "exist", timeout);
}

@Override
public boolean waitForNotExist(Duration timeout) {
return waitForCondition(() -> !isExist(), "absent", timeout);
return waitForCondition(() -> !isExist(), "not.exist", timeout);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package aquality.selenium.core.elements;

import aquality.selenium.core.elements.interfaces.IElementFinder;
import aquality.selenium.core.elements.interfaces.ILogElementState;
import aquality.selenium.core.waitings.IConditionalWait;
import org.openqa.selenium.By;

import java.time.Duration;
import java.util.function.BooleanSupplier;

public class DefaultElementStateProvider extends ElementStateProvider {

private final By locator;
private final IConditionalWait conditionalWait;
private final IElementFinder elementFinder;

public DefaultElementStateProvider(By locator, IConditionalWait conditionalWait, IElementFinder elementFinder) {
public DefaultElementStateProvider(By locator, IConditionalWait conditionalWait, IElementFinder elementFinder,
ILogElementState logger) {
super(logger);
this.locator = locator;
this.conditionalWait = conditionalWait;
this.elementFinder = elementFinder;
Expand All @@ -25,7 +29,12 @@ public boolean isClickable() {

@Override
public void waitForClickable(Duration timeout) {
waitForIsClickable(timeout, false);
try {
waitForIsClickable(timeout, false);
} catch (Exception e) {
logElementState("loc.wait.for.state.failed", elementClickable().getStateName());
throw e;
}
}

private boolean waitForIsClickable(Duration timeout, boolean catchTimeoutException) {
Expand All @@ -35,7 +44,10 @@ private boolean waitForIsClickable(Duration timeout, boolean catchTimeoutExcepti
}

private boolean isElementInDesiredCondition(DesiredState elementStateCondition, Duration timeout) {
return !elementFinder.findElements(locator, elementStateCondition, timeout).isEmpty();
return doAndLogWaitForState(
() -> !elementFinder.findElements(locator, elementStateCondition, timeout).isEmpty(),
elementStateCondition.getStateName(),
timeout);
}

@Override
Expand All @@ -45,7 +57,10 @@ public boolean isDisplayed() {

@Override
public boolean waitForDisplayed(Duration timeout) {
return isAnyElementFound(timeout, ElementState.DISPLAYED);
return doAndLogWaitForState(
() -> isAnyElementFound(timeout, ElementState.DISPLAYED),
"displayed",
timeout);
}

private boolean isAnyElementFound(Duration timeout, ElementState state) {
Expand All @@ -54,7 +69,10 @@ private boolean isAnyElementFound(Duration timeout, ElementState state) {

@Override
public boolean waitForNotDisplayed(Duration timeout) {
return conditionalWait.waitFor(() -> !isDisplayed(), timeout);
return doAndLogWaitForState(
() -> conditionalWait.waitFor(() -> !isDisplayed(), timeout),
"not.displayed",
timeout);
}

@Override
Expand All @@ -64,12 +82,18 @@ public boolean isExist() {

@Override
public boolean waitForExist(Duration timeout) {
return isAnyElementFound(timeout, ElementState.EXISTS_IN_ANY_STATE);
return doAndLogWaitForState(
() -> isAnyElementFound(timeout, ElementState.EXISTS_IN_ANY_STATE),
"exist",
timeout);
}

@Override
public boolean waitForNotExist(Duration timeout) {
return conditionalWait.waitFor(() -> !isExist(), timeout);
return doAndLogWaitForState(
() -> conditionalWait.waitFor(() -> !isExist(), timeout),
"not.exist",
timeout);
}

@Override
Expand All @@ -86,4 +110,21 @@ public boolean waitForEnabled(Duration timeout) {
public boolean waitForNotEnabled(Duration timeout) {
return isElementInDesiredCondition(elementNotEnabled(), timeout);
}

private boolean doAndLogWaitForState(BooleanSupplier waitingAction, String conditionKeyPart, Duration timeout)
{
if (Duration.ZERO.equals(timeout))
{
return waitingAction.getAsBoolean();
}

logElementState("loc.wait.for.state", conditionKeyPart);
boolean result = waitingAction.getAsBoolean();
if (!result)
{
logElementState("loc.wait.for.state.failed", conditionKeyPart);
}

return result;
}
}
29 changes: 24 additions & 5 deletions src/main/java/aquality/selenium/core/elements/Element.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import aquality.selenium.core.applications.IApplication;
import aquality.selenium.core.configurations.IElementCacheConfiguration;
import aquality.selenium.core.configurations.ILoggerConfiguration;
import aquality.selenium.core.elements.interfaces.*;
import aquality.selenium.core.localization.ILocalizationManager;
import aquality.selenium.core.localization.ILocalizedLogger;
import aquality.selenium.core.logging.Logger;
import aquality.selenium.core.utilities.IElementActionRetrier;
Expand Down Expand Up @@ -41,6 +43,8 @@ protected Element(final By loc, final String name, final ElementState state) {

protected abstract ILocalizedLogger getLocalizedLogger();

protected abstract ILocalizationManager getLocalizationManager();

protected abstract IConditionalWait getConditionalWait();

protected abstract String getElementType();
Expand All @@ -53,10 +57,19 @@ protected IElementCacheHandler getCache() {
return elementCacheHandler;
}

protected ILoggerConfiguration getLoggerConfiguration() {
return getLocalizedLogger().getConfiguration();
}

protected Logger getLogger() {
return Logger.getInstance();
}

protected ILogElementState logElementState() {
return ((messageKey, stateKey) -> getLocalizedLogger().infoElementAction(getElementType(), getName(), messageKey,
getLocalizationManager().getLocalizedMessage(stateKey)));
}

@Override
public By getLocator() {
return locator;
Expand All @@ -70,8 +83,8 @@ public String getName() {
@Override
public IElementStateProvider state() {
return getElementCacheConfiguration().isEnabled()
? new CachedElementStateProvider(locator, getConditionalWait(), getCache(), getLocalizedLogger())
: new DefaultElementStateProvider(locator, getConditionalWait(), getElementFinder());
? new CachedElementStateProvider(locator, getConditionalWait(), getCache(), logElementState())
: new DefaultElementStateProvider(locator, getConditionalWait(), getElementFinder(), logElementState());
}

@Override
Expand All @@ -81,7 +94,9 @@ public RemoteWebElement getElement(Duration timeout) {
? getCache().getElement(timeout)
: (RemoteWebElement) getElementFinder().findElement(locator, elementState, timeout);
} catch (NoSuchElementException e) {
logPageSource(e);
if (getLoggerConfiguration().logPageSource()) {
logPageSource(e);
}
throw e;
}
}
Expand All @@ -98,13 +113,17 @@ protected void logPageSource(WebDriverException exception) {
@Override
public String getText() {
logElementAction("loc.get.text");
return doWithRetry(() -> getElement().getText());
String value = doWithRetry(() -> getElement().getText());
logElementAction("loc.text.value", value);
return value;
}

@Override
public String getAttribute(String attr) {
logElementAction("loc.el.getattr", attr);
return doWithRetry(() -> getElement().getAttribute(attr));
String value = doWithRetry(() -> getElement().getAttribute(attr));
logElementAction("loc.el.attr.value", attr, value);
return value;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
package aquality.selenium.core.elements;

import aquality.selenium.core.elements.interfaces.IElementStateProvider;
import aquality.selenium.core.elements.interfaces.ILogElementState;
import org.openqa.selenium.WebElement;

public abstract class ElementStateProvider implements IElementStateProvider {

private final ILogElementState logger;

protected ElementStateProvider(ILogElementState logger) {
this.logger = logger;
}

protected void logElementState(String messageKey, String conditionKeyPart) {
String conditionKey = "loc.el.state.".concat(conditionKeyPart);
logger.logElementState(messageKey, conditionKey);
}

protected boolean isElementEnabled(WebElement element) {
return element.isEnabled();
}

protected DesiredState elementEnabled() {
return new DesiredState(this::isElementEnabled, "ENABLED")
return new DesiredState(this::isElementEnabled, "enabled")
.withCatchingTimeoutException()
.withThrowingNoSuchElementException();
}

protected DesiredState elementNotEnabled() {
return new DesiredState(element -> !isElementEnabled(element), "NOT ENABLED")
return new DesiredState(element -> !isElementEnabled(element), "not.enabled")
.withCatchingTimeoutException()
.withThrowingNoSuchElementException();
}

protected DesiredState elementClickable() {
return new DesiredState(webElement -> webElement.isDisplayed() && webElement.isEnabled(), "CLICKABLE");
return new DesiredState(webElement -> webElement.isDisplayed() && webElement.isEnabled(), "clickable");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package aquality.selenium.core.elements.interfaces;

/**
* Describes interface that can log element state.
*/
public interface ILogElementState {
/**
* Logs element state.
* @param messageKey key of localized message to log.
* @param stateKey key of localized state to log.
*/
void logElementState(String messageKey, String stateKey);
}
Loading

0 comments on commit db30415

Please sign in to comment.