-
Notifications
You must be signed in to change notification settings - Fork 4
Implemented ElementFactory #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
165 changes: 165 additions & 0 deletions
165
src/main/java/aquality/selenium/core/elements/ElementFactory.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package aquality.selenium.core.elements; | ||
|
||
import aquality.selenium.core.elements.interfaces.IElement; | ||
import aquality.selenium.core.elements.interfaces.IElementFactory; | ||
import aquality.selenium.core.elements.interfaces.IElementFinder; | ||
import aquality.selenium.core.elements.interfaces.IElementSupplier; | ||
import aquality.selenium.core.localization.ILocalizationManager; | ||
import aquality.selenium.core.logging.Logger; | ||
import aquality.selenium.core.waitings.IConditionalWait; | ||
import com.google.inject.Inject; | ||
import org.openqa.selenium.By; | ||
import org.openqa.selenium.By.ByXPath; | ||
import org.openqa.selenium.InvalidArgumentException; | ||
import org.openqa.selenium.WebElement; | ||
import org.openqa.selenium.support.pagefactory.ByChained; | ||
|
||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.InvocationTargetException; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.TimeoutException; | ||
|
||
public class ElementFactory implements IElementFactory { | ||
|
||
private static final int XPATH_SUBSTRING_BEGIN_INDEX = 10; | ||
private static final long ZERO_TIMEOUT = 0L; | ||
|
||
private final IConditionalWait conditionalWait; | ||
private final IElementFinder elementFinder; | ||
private final ILocalizationManager localizationManager; | ||
|
||
@Inject | ||
public ElementFactory(IConditionalWait conditionalWait, IElementFinder elementFinder, ILocalizationManager localizationManager) { | ||
this.conditionalWait = conditionalWait; | ||
this.elementFinder = elementFinder; | ||
this.localizationManager = localizationManager; | ||
} | ||
|
||
@Override | ||
public <T extends IElement> T getCustomElement(IElementSupplier<T> elementSupplier, By locator, String name, ElementState state) { | ||
return elementSupplier.get(locator, name, state); | ||
} | ||
|
||
@Override | ||
public <T extends IElement> T getCustomElement(Class<T> clazz, By locator, String name, ElementState state) { | ||
IElementSupplier<T> elementSupplier = getDefaultElementSupplier(clazz); | ||
return getCustomElement(elementSupplier, locator, name, state); | ||
} | ||
|
||
@Override | ||
public <T extends IElement> T findChildElement(IElement parentElement, By childLoc, String name, Class<T> clazz, ElementState state) { | ||
IElementSupplier<T> elementSupplier = getDefaultElementSupplier(clazz); | ||
return findChildElement(parentElement, childLoc, name, elementSupplier, state); | ||
} | ||
|
||
@Override | ||
public <T extends IElement> T findChildElement(IElement parentElement, By childLoc, String name, IElementSupplier<T> supplier, ElementState state) { | ||
String childName = name == null ? "Child element of ".concat(parentElement.getName()) : name; | ||
By fullLocator = new ByChained(parentElement.getLocator(), childLoc); | ||
return supplier.get(fullLocator, childName, state); | ||
} | ||
|
||
@Override | ||
public <T extends IElement> List<T> findElements(By locator, String name, IElementSupplier<T> supplier, | ||
ElementsCount count, ElementState state) { | ||
try { | ||
waitForElementsCount(locator, count, state); | ||
} catch (TimeoutException e) { | ||
throw new org.openqa.selenium.TimeoutException(e.getMessage()); | ||
} | ||
List<WebElement> webElements = elementFinder.findElements(locator, state, ZERO_TIMEOUT); | ||
String namePrefix = name == null ? "element" : name; | ||
List<T> list = new ArrayList<>(); | ||
for (int index = 1; index <= webElements.size(); index++) { | ||
WebElement webElement = webElements.get(index - 1); | ||
String currentName = String.format("%1$s %2$s", namePrefix, index); | ||
T element = supplier.get(generateXpathLocator(locator, webElement, index), currentName, state); | ||
list.add(element); | ||
} | ||
return list; | ||
} | ||
|
||
protected void waitForElementsCount(By locator, ElementsCount count, ElementState state) throws TimeoutException { | ||
switch (count) { | ||
case ZERO: | ||
conditionalWait.waitForTrue(() -> elementFinder.findElements(locator, state, ZERO_TIMEOUT).isEmpty(), | ||
localizationManager.getLocalizedMessage("loc.elements.found.but.should.not", | ||
locator.toString(), state.toString())); | ||
break; | ||
case MORE_THEN_ZERO: | ||
conditionalWait.waitForTrue(() -> !elementFinder.findElements(locator, state, ZERO_TIMEOUT).isEmpty(), | ||
localizationManager.getLocalizedMessage("loc.no.elements.found.by.locator", | ||
locator.toString(), state.toString())); | ||
break; | ||
case ANY: | ||
conditionalWait.waitFor(() -> elementFinder.findElements(locator, state, ZERO_TIMEOUT) != null); | ||
break; | ||
default: | ||
throw new IllegalArgumentException("No such expected value: ".concat(count.toString())); | ||
} | ||
} | ||
|
||
@Override | ||
public <T extends IElement> List<T> findElements(By locator, String name, Class<T> clazz, | ||
ElementsCount count, ElementState state) { | ||
IElementSupplier<T> elementSupplier = getDefaultElementSupplier(clazz); | ||
return findElements(locator, name, elementSupplier, count, state); | ||
} | ||
|
||
/** | ||
* Generates xpath locator for target element. | ||
* | ||
* @param multipleElementsLocator locator used to find elements. Currently only {@link ByXPath} locators are supported. | ||
* @param webElement target element. | ||
* @param elementIndex index of target element. | ||
* @return target element's locator | ||
*/ | ||
protected By generateXpathLocator(By multipleElementsLocator, WebElement webElement, int elementIndex) { | ||
Class supportedLocatorType = ByXPath.class; | ||
if (multipleElementsLocator.getClass().equals(supportedLocatorType)) { | ||
return By.xpath( | ||
String.format("(%1$s)[%2$s]", multipleElementsLocator.toString().substring(XPATH_SUBSTRING_BEGIN_INDEX), elementIndex)); | ||
} | ||
throw new InvalidArgumentException(String.format( | ||
"Cannot define unique baseLocator for element %1$s. Multiple elements' baseLocator %2$s is not %3$s, and is not supported yet", | ||
webElement.toString(), multipleElementsLocator, supportedLocatorType)); | ||
} | ||
|
||
/** | ||
* Gets map between elements interfaces and their implementations. | ||
* Can be extended for custom elements with custom interfaces. | ||
* | ||
* @return Map where key is interface and value is its implementation. | ||
*/ | ||
protected Map<Class<? extends IElement>, Class<? extends IElement>> getElementTypesMap() { | ||
return new HashMap<>(); | ||
} | ||
|
||
protected <T extends IElement> Class<T> resolveElementClass(Class<T> clazz) { | ||
if (clazz.isInterface() && !getElementTypesMap().containsKey(clazz)) { | ||
throw new IllegalArgumentException( | ||
String.format("Interface %1$s is not found in getElementTypesMap()", clazz)); | ||
} | ||
|
||
return clazz.isInterface() ? (Class<T>) getElementTypesMap().get(clazz) : clazz; | ||
} | ||
|
||
protected <T extends IElement> IElementSupplier<T> getDefaultElementSupplier(Class<T> clazz) { | ||
return (locator, name, state) -> { | ||
try { | ||
Constructor<T> ctor = resolveElementClass(clazz) | ||
.getDeclaredConstructor(By.class, String.class, ElementState.class); | ||
ctor.setAccessible(true); | ||
T instance = ctor.newInstance(locator, name, state); | ||
ctor.setAccessible(false); | ||
return instance; | ||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { | ||
Logger.getInstance().debug(e.getMessage()); | ||
throw new IllegalArgumentException("Something went wrong during element casting"); | ||
} | ||
}; | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
src/main/java/aquality/selenium/core/elements/ElementsCount.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package aquality.selenium.core.elements; | ||
|
||
public enum ElementsCount { | ||
ZERO, | ||
MORE_THEN_ZERO, | ||
ANY | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
src/main/java/aquality/selenium/core/elements/interfaces/IElement.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package aquality.selenium.core.elements.interfaces; | ||
|
||
import org.openqa.selenium.By; | ||
import org.openqa.selenium.remote.RemoteWebElement; | ||
|
||
public interface IElement extends IParent { | ||
|
||
/** | ||
* Gets clear WebElement. | ||
* | ||
* @return WebElement | ||
*/ | ||
default RemoteWebElement getElement() { | ||
return getElement(null); | ||
} | ||
|
||
/** | ||
* Gets clear WebElement. | ||
* | ||
* @param timeout Timeout for waiting | ||
* @return WebElement | ||
*/ | ||
RemoteWebElement getElement(Long timeout); | ||
|
||
/** | ||
* Gets element name. | ||
* | ||
* @return name | ||
*/ | ||
String getName(); | ||
|
||
/** | ||
* Gets element locator. | ||
* | ||
* @return locator | ||
*/ | ||
By getLocator(); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does initializing with null mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it means that the timeout wasn't specified and we will use the default one