Skip to content

Commit 4e21d59

Browse files
mialeskaknysh
andauthored
Feature/13 element finder (#28)
* extracted ISettingsFile and added tests * extracted ILoggerConfiguration * extracted ITimeoutConfiguration * extracted IRetryConfiguration * renamed one test * added empty end line and updated com.fasterxml.jackson.core to 2.10.2 * fixed naming and added empty line at the end of files * fixed naming and added empty line at the end of files * fixed naming and added empty line at the end of files * merged with master and added test for CustomSettingsFile * merge and small refactoring of tests * merge from master * merge from master * extracted IElementCacheConfiguration * application to applications added tests for conditionalWait * support multiple threads in ConditionalWaitTests added friendly messages to the tests * removed SupportedLanguaged and added default language if configuration is absent * moved SettingsFileUtil to ISettingsFile * rename applications to application in docs * #6 removed using of DI in tests * Implement IElementFinder, add DesiredState and ElementState helpful classes * Completed ElementFinder, add RelativeElementFinder, add tests * Resolve conflicts with the master * add links to IElementFinder documentation * fix the sonar code smells * making chrome headless in tests * hot fix for conditional wait tests * refactor ElementFinder, moved common part to base class Co-authored-by: knysh <[email protected]>
1 parent ed4476d commit 4e21d59

16 files changed

+639
-16
lines changed

src/main/java/aquality/selenium/core/applications/AqualityModule.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package aquality.selenium.core.applications;
22

33
import aquality.selenium.core.configurations.*;
4+
import aquality.selenium.core.elements.IElementsModule;
5+
import aquality.selenium.core.elements.interfaces.IElementFinder;
46
import aquality.selenium.core.localization.ILocalizationManager;
57
import aquality.selenium.core.localization.ILocalizationModule;
68
import aquality.selenium.core.localization.ILocalizedLogger;
@@ -18,7 +20,7 @@
1820
* Describes all dependencies which is registered for the project.
1921
*/
2022
public class AqualityModule<T extends IApplication> extends AbstractModule
21-
implements ILocalizationModule, IUtilitiesModule, IWaitingsModule {
23+
implements ILocalizationModule, IUtilitiesModule, IWaitingsModule, IElementsModule {
2224

2325
private final Provider<T> applicationProvider;
2426

@@ -42,5 +44,6 @@ protected void configure() {
4244
bind(ILocalizationManager.class).to(getLocalizationManagerImplementation()).in(Singleton.class);
4345
bind(ILocalizedLogger.class).to(getLocalizedLoggerImplementation()).in(Singleton.class);
4446
bind(IConditionalWait.class).to(getConditionalWaitImplementation());
47+
bind(IElementFinder.class).to(getElementFinderImplementation());
4548
}
4649
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package aquality.selenium.core.elements;
2+
3+
import org.openqa.selenium.WebElement;
4+
5+
import java.util.function.Predicate;
6+
7+
/**
8+
* Defines desired state for element with ability to handle exceptions.
9+
*/
10+
public class DesiredState {
11+
12+
private final Predicate<WebElement> desiredStatePredicate;
13+
private final String stateName;
14+
private boolean isCatchingTimeoutException;
15+
private boolean isThrowingNoSuchElementException;
16+
17+
public DesiredState(Predicate<WebElement> desiredStatePredicate, String stateName){
18+
this.desiredStatePredicate = desiredStatePredicate;
19+
this.stateName = stateName;
20+
}
21+
22+
public Predicate<WebElement> getElementStateCondition() {
23+
return desiredStatePredicate;
24+
}
25+
26+
public String getStateName() {
27+
return stateName;
28+
}
29+
30+
public DesiredState withCatchingTimeoutException(){
31+
this.isCatchingTimeoutException = true;
32+
return this;
33+
}
34+
35+
public DesiredState withThrowingNoSuchElementException(){
36+
this.isThrowingNoSuchElementException = true;
37+
return this;
38+
}
39+
40+
public boolean isCatchingInTimeoutException() {
41+
return isCatchingTimeoutException;
42+
}
43+
44+
public boolean isThrowingNoSuchElementException() {
45+
return isThrowingNoSuchElementException;
46+
}
47+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package aquality.selenium.core.elements;
2+
3+
import aquality.selenium.core.elements.interfaces.IElementFinder;
4+
import aquality.selenium.core.localization.ILocalizedLogger;
5+
import aquality.selenium.core.waitings.IConditionalWait;
6+
import com.google.inject.Inject;
7+
import org.openqa.selenium.*;
8+
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.concurrent.atomic.AtomicBoolean;
12+
13+
/**
14+
* Implementation of IElementFinder.
15+
*/
16+
public class ElementFinder implements IElementFinder {
17+
private final ILocalizedLogger localizedLogger;
18+
private final IConditionalWait conditionalWait;
19+
20+
@Inject
21+
public ElementFinder(ILocalizedLogger localizedLogger, IConditionalWait conditionalWait) {
22+
this.localizedLogger = localizedLogger;
23+
this.conditionalWait = conditionalWait;
24+
}
25+
26+
@Override
27+
public List<WebElement> findElements(By locator, DesiredState desiredState, Long timeoutInSeconds) {
28+
AtomicBoolean wasAnyElementFound = new AtomicBoolean(false);
29+
List<WebElement> resultElements = new ArrayList<>();
30+
try {
31+
conditionalWait.waitFor(driver ->
32+
tryToFindElements(locator, desiredState, wasAnyElementFound, resultElements, driver),
33+
timeoutInSeconds,
34+
null);
35+
} catch (TimeoutException e) {
36+
handleTimeoutException(e, locator, desiredState, wasAnyElementFound.get());
37+
}
38+
39+
return resultElements;
40+
}
41+
42+
protected boolean tryToFindElements(By locator, DesiredState desiredState, AtomicBoolean wasAnyElementFound,
43+
List<WebElement> resultElements, SearchContext context) {
44+
List<WebElement> currentFoundElements = context.findElements(locator);
45+
wasAnyElementFound.set(!currentFoundElements.isEmpty());
46+
currentFoundElements
47+
.stream()
48+
.filter(desiredState.getElementStateCondition())
49+
.forEachOrdered(resultElements::add);
50+
return !resultElements.isEmpty();
51+
}
52+
53+
/**
54+
* depends on configuration of DesiredState object it can be required to throw or not NoSuchElementException
55+
*
56+
* @param exception TimeoutException to handle
57+
* @param locator locator that is using to find elements
58+
* @param desiredState DesiredState object
59+
* @param wasAnyElementFound was any element found by locator or not.
60+
*/
61+
protected void handleTimeoutException(TimeoutException exception, By locator, DesiredState desiredState, boolean wasAnyElementFound) {
62+
String message = String.format("No elements with locator '%1$s' were found in %2$s state", locator, desiredState.getStateName());
63+
if (desiredState.isCatchingInTimeoutException()) {
64+
if (!wasAnyElementFound) {
65+
if (desiredState.isThrowingNoSuchElementException()) {
66+
throw new NoSuchElementException(message);
67+
}
68+
localizedLogger.debug("loc.no.elements.found.in.state", locator, desiredState.getStateName());
69+
} else {
70+
localizedLogger.debug("loc.elements.were.found.but.not.in.state", locator, desiredState.getStateName());
71+
}
72+
} else {
73+
String combinedMessage = String.format("%1$s: %2$s", exception.getMessage(), message);
74+
if (desiredState.isThrowingNoSuchElementException() && !wasAnyElementFound) {
75+
throw new NoSuchElementException(combinedMessage);
76+
}
77+
throw new TimeoutException(combinedMessage);
78+
}
79+
}
80+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package aquality.selenium.core.elements;
2+
3+
public enum ElementState {
4+
DISPLAYED("displayed"),
5+
EXISTS_IN_ANY_STATE("exists");
6+
7+
private final String state;
8+
9+
ElementState(String state) {
10+
this.state = state;
11+
}
12+
13+
@Override
14+
public String toString() {
15+
return state;
16+
}
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package aquality.selenium.core.elements;
2+
3+
import aquality.selenium.core.elements.interfaces.IElementFinder;
4+
5+
/**
6+
* Describes implementations of elements services to be registered in DI container.
7+
*/
8+
public interface IElementsModule {
9+
/**
10+
* @return class which implements IElementFinder
11+
*/
12+
default Class<? extends IElementFinder> getElementFinderImplementation() {
13+
return ElementFinder.class;
14+
}
15+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package aquality.selenium.core.elements;
2+
3+
import aquality.selenium.core.localization.ILocalizedLogger;
4+
import aquality.selenium.core.waitings.IConditionalWait;
5+
import org.openqa.selenium.By;
6+
import org.openqa.selenium.SearchContext;
7+
import org.openqa.selenium.WebElement;
8+
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.concurrent.TimeoutException;
12+
import java.util.concurrent.atomic.AtomicBoolean;
13+
import java.util.function.Supplier;
14+
15+
/**
16+
* Implementation of IElementFinder for a relative SearchContext.
17+
*/
18+
public class RelativeElementFinder extends ElementFinder {
19+
private final IConditionalWait conditionalWait;
20+
private final Supplier<SearchContext> searchContextSupplier;
21+
22+
public RelativeElementFinder(ILocalizedLogger localizedLogger, IConditionalWait conditionalWait,
23+
Supplier<SearchContext> searchContextSupplier) {
24+
super(localizedLogger, conditionalWait);
25+
this.conditionalWait = conditionalWait;
26+
this.searchContextSupplier = searchContextSupplier;
27+
}
28+
29+
@Override
30+
public List<WebElement> findElements(By locator, DesiredState desiredState, Long timeoutInSeconds) {
31+
AtomicBoolean wasAnyElementFound = new AtomicBoolean(false);
32+
List<WebElement> resultElements = new ArrayList<>();
33+
try {
34+
conditionalWait.waitForTrue(() ->
35+
tryToFindElements(locator, desiredState, wasAnyElementFound, resultElements,
36+
searchContextSupplier.get()),
37+
timeoutInSeconds,
38+
null);
39+
} catch (TimeoutException e) {
40+
handleTimeoutException(new org.openqa.selenium.TimeoutException(e.getMessage(), e), locator, desiredState,
41+
wasAnyElementFound.get());
42+
} catch (org.openqa.selenium.TimeoutException e) {
43+
handleTimeoutException(e, locator, desiredState, wasAnyElementFound.get());
44+
}
45+
46+
return resultElements;
47+
}
48+
}

0 commit comments

Comments
 (0)