Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
70c5cdf
extracted ISettingsFile and added tests
knysh Jan 27, 2020
a3d6cb1
extracted ILoggerConfiguration
knysh Jan 28, 2020
d47c94a
extracted ITimeoutConfiguration
knysh Jan 28, 2020
b95e87e
extracted IRetryConfiguration
knysh Jan 28, 2020
3ca9faf
renamed one test
knysh Jan 28, 2020
6221008
added empty end line and
knysh Jan 28, 2020
ecb9c0a
fixed naming and added empty line at the end of files
knysh Jan 28, 2020
22f0ff4
fixed naming and added empty line at the end of files
knysh Jan 28, 2020
2e0080a
fixed naming and added empty line at the end of files
knysh Jan 28, 2020
b503d61
merged with master and added test for CustomSettingsFile
knysh Jan 29, 2020
7c4c470
merge and small refactoring of tests
knysh Jan 29, 2020
cba8d49
merge from master
knysh Jan 29, 2020
9350294
merge from master
knysh Jan 29, 2020
7c948cf
extracted IElementCacheConfiguration
knysh Jan 30, 2020
81383dd
application to applications
knysh Jan 30, 2020
2cde734
support multiple threads in ConditionalWaitTests
knysh Jan 30, 2020
d8d96cd
removed SupportedLanguaged and added default language if configuratio…
knysh Jan 30, 2020
b1ef703
moved SettingsFileUtil to ISettingsFile
knysh Jan 30, 2020
4d9b80e
rename applications to application in docs
knysh Jan 30, 2020
033abb7
#6 removed using of DI in tests
knysh Jan 31, 2020
ffa1c1b
Implement IElementFinder, add DesiredState and ElementState helpful c…
mialeska Feb 3, 2020
0078bf7
Completed ElementFinder, add RelativeElementFinder, add tests
mialeska Feb 4, 2020
913cc7a
Resolve conflicts with the master
mialeska Feb 4, 2020
4aaeb26
add links to IElementFinder documentation
mialeska Feb 5, 2020
e667c18
fix the sonar code smells
mialeska Feb 5, 2020
64b9ae4
making chrome headless in tests
mialeska Feb 5, 2020
b360791
hot fix for conditional wait tests
knysh Feb 5, 2020
d872075
Merge remote-tracking branch 'origin/Feature/13-ElementFinder' into F…
knysh Feb 5, 2020
29e444a
refactor ElementFinder, moved common part to base class
mialeska Feb 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package aquality.selenium.core.applications;

import aquality.selenium.core.configurations.*;
import aquality.selenium.core.elements.IElementsModule;
import aquality.selenium.core.elements.interfaces.IElementFinder;
import aquality.selenium.core.localization.ILocalizationManager;
import aquality.selenium.core.localization.ILocalizationModule;
import aquality.selenium.core.localization.ILocalizedLogger;
Expand All @@ -18,7 +20,7 @@
* Describes all dependencies which is registered for the project.
*/
public class AqualityModule<T extends IApplication> extends AbstractModule
implements ILocalizationModule, IUtilitiesModule, IWaitingsModule {
implements ILocalizationModule, IUtilitiesModule, IWaitingsModule, IElementsModule {

private final Provider<T> applicationProvider;

Expand All @@ -42,5 +44,6 @@ protected void configure() {
bind(ILocalizationManager.class).to(getLocalizationManagerImplementation()).in(Singleton.class);
bind(ILocalizedLogger.class).to(getLocalizedLoggerImplementation()).in(Singleton.class);
bind(IConditionalWait.class).to(getConditionalWaitImplementation());
bind(IElementFinder.class).to(getElementFinderImplementation());
}
}
47 changes: 47 additions & 0 deletions src/main/java/aquality/selenium/core/elements/DesiredState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package aquality.selenium.core.elements;

import org.openqa.selenium.WebElement;

import java.util.function.Predicate;

/**
* Defines desired state for element with ability to handle exceptions.
*/
public class DesiredState {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make this class package-private? and do not we need to extract an interface of it?

Copy link
Contributor Author

@mialeska mialeska Feb 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, we cannot make it private, we will need it in child libraries.
no, I don't think that we need to extract an interface of it. This class is a simple model, and we expect it to remain the same. We don't register it in DI, so the interface is needless


private final Predicate<WebElement> desiredStatePredicate;
private final String stateName;
private boolean isCatchingTimeoutException;
private boolean isThrowingNoSuchElementException;

public DesiredState(Predicate<WebElement> desiredStatePredicate, String stateName){
this.desiredStatePredicate = desiredStatePredicate;
this.stateName = stateName;
}

public Predicate<WebElement> getElementStateCondition() {
return desiredStatePredicate;
}

public String getStateName() {
return stateName;
}

public DesiredState withCatchingTimeoutException(){
this.isCatchingTimeoutException = true;
return this;
}

public DesiredState withThrowingNoSuchElementException(){
this.isThrowingNoSuchElementException = true;
return this;
}

public boolean isCatchingInTimeoutException() {
return isCatchingTimeoutException;
}

public boolean isThrowingNoSuchElementException() {
return isThrowingNoSuchElementException;
}
}
80 changes: 80 additions & 0 deletions src/main/java/aquality/selenium/core/elements/ElementFinder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package aquality.selenium.core.elements;

import aquality.selenium.core.elements.interfaces.IElementFinder;
import aquality.selenium.core.localization.ILocalizedLogger;
import aquality.selenium.core.waitings.IConditionalWait;
import com.google.inject.Inject;
import org.openqa.selenium.*;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* Implementation of IElementFinder.
*/
public class ElementFinder implements IElementFinder {
private final ILocalizedLogger localizedLogger;
private final IConditionalWait conditionalWait;

@Inject
public ElementFinder(ILocalizedLogger localizedLogger, IConditionalWait conditionalWait) {
this.localizedLogger = localizedLogger;
this.conditionalWait = conditionalWait;
}

@Override
public List<WebElement> findElements(By locator, DesiredState desiredState, Long timeoutInSeconds) {
AtomicBoolean wasAnyElementFound = new AtomicBoolean(false);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this variable? why can't we operate only with 'resultElements.isEmpty()'?
or it is just because lambda usage?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result elements is a different array, in fact it has elements after filtering, while this variable defines whether any element was there before filtering. please recheck your suggestion

List<WebElement> resultElements = new ArrayList<>();
try {
conditionalWait.waitFor(driver ->
tryToFindElements(locator, desiredState, wasAnyElementFound, resultElements, driver),
timeoutInSeconds,
null);
} catch (TimeoutException e) {
handleTimeoutException(e, locator, desiredState, wasAnyElementFound.get());
}

return resultElements;
}

protected boolean tryToFindElements(By locator, DesiredState desiredState, AtomicBoolean wasAnyElementFound,
List<WebElement> resultElements, SearchContext context) {
List<WebElement> currentFoundElements = context.findElements(locator);
wasAnyElementFound.set(!currentFoundElements.isEmpty());
currentFoundElements
.stream()
.filter(desiredState.getElementStateCondition())
.forEachOrdered(resultElements::add);
return !resultElements.isEmpty();
}

/**
* depends on configuration of DesiredState object it can be required to throw or not NoSuchElementException
*
* @param exception TimeoutException to handle
* @param locator locator that is using to find elements
* @param desiredState DesiredState object
* @param wasAnyElementFound was any element found by locator or not.
*/
protected void handleTimeoutException(TimeoutException exception, By locator, DesiredState desiredState, boolean wasAnyElementFound) {
String message = String.format("No elements with locator '%1$s' were found in %2$s state", locator, desiredState.getStateName());
if (desiredState.isCatchingInTimeoutException()) {
if (!wasAnyElementFound) {
if (desiredState.isThrowingNoSuchElementException()) {
throw new NoSuchElementException(message);
}
localizedLogger.debug("loc.no.elements.found.in.state", locator, desiredState.getStateName());
} else {
localizedLogger.debug("loc.elements.were.found.but.not.in.state", locator, desiredState.getStateName());
}
} else {
String combinedMessage = String.format("%1$s: %2$s", exception.getMessage(), message);
if (desiredState.isThrowingNoSuchElementException() && !wasAnyElementFound) {
throw new NoSuchElementException(combinedMessage);
}
throw new TimeoutException(combinedMessage);
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/aquality/selenium/core/elements/ElementState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package aquality.selenium.core.elements;

public enum ElementState {
DISPLAYED("displayed"),
EXISTS_IN_ANY_STATE("exists");

private final String state;

ElementState(String state) {
this.state = state;
}

@Override
public String toString() {
return state;
}
}
15 changes: 15 additions & 0 deletions src/main/java/aquality/selenium/core/elements/IElementsModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package aquality.selenium.core.elements;

import aquality.selenium.core.elements.interfaces.IElementFinder;

/**
* Describes implementations of elements services to be registered in DI container.
*/
public interface IElementsModule {
/**
* @return class which implements IElementFinder
*/
default Class<? extends IElementFinder> getElementFinderImplementation() {
return ElementFinder.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package aquality.selenium.core.elements;

import aquality.selenium.core.localization.ILocalizedLogger;
import aquality.selenium.core.waitings.IConditionalWait;
import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;

/**
* Implementation of IElementFinder for a relative SearchContext.
*/
public class RelativeElementFinder extends ElementFinder {
private final IConditionalWait conditionalWait;
private final Supplier<SearchContext> searchContextSupplier;

public RelativeElementFinder(ILocalizedLogger localizedLogger, IConditionalWait conditionalWait,
Supplier<SearchContext> searchContextSupplier) {
super(localizedLogger, conditionalWait);
this.conditionalWait = conditionalWait;
this.searchContextSupplier = searchContextSupplier;
}

@Override
public List<WebElement> findElements(By locator, DesiredState desiredState, Long timeoutInSeconds) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if I am right the difference between this method and another one from the ElementFinder class that here we use 'searchContextSupplier.get()' instead 'driver' - can we create one parametrized and re-use it for both cases by passing necessary supplier?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually no, you are wrong.
Nevertheless, I guess that I can extract the method from the lambda expression

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactored, please check again

AtomicBoolean wasAnyElementFound = new AtomicBoolean(false);
List<WebElement> resultElements = new ArrayList<>();
try {
conditionalWait.waitForTrue(() ->
tryToFindElements(locator, desiredState, wasAnyElementFound, resultElements,
searchContextSupplier.get()),
timeoutInSeconds,
null);
} catch (TimeoutException e) {
handleTimeoutException(new org.openqa.selenium.TimeoutException(e.getMessage(), e), locator, desiredState,
wasAnyElementFound.get());
} catch (org.openqa.selenium.TimeoutException e) {
handleTimeoutException(e, locator, desiredState, wasAnyElementFound.get());
}

return resultElements;
}
}
Loading