Skip to content

Commit fc42830

Browse files
authored
Feature/45 add find child elements to element (#51) +semver:feature
* Implemented findChildElements in ElementFactory * Add overloads to ElementFactory's findElements method * Add findChildElements methods to IParent and Element * Implement xpath extraction logic in ElementFactory to be able to find multiple elements in non-web WebDriver-based libraries
1 parent 000f9e2 commit fc42830

File tree

9 files changed

+926
-99
lines changed

9 files changed

+926
-99
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.github.aquality-automation</groupId>
88
<artifactId>aquality-selenium-core</artifactId>
9-
<version>1.0.2</version>
9+
<version>1.1.0</version>
1010

1111
<packaging>jar</packaging>
1212
<name>Aquality Selenium Core</name>

src/main/java/aquality/selenium/core/elements/Element.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.openqa.selenium.remote.RemoteWebElement;
1414

1515
import java.time.Duration;
16+
import java.util.List;
1617
import java.util.function.Supplier;
1718

1819
public abstract class Element implements IElement {
@@ -128,6 +129,16 @@ public <T extends IElement> T findChildElement(By childLoc, String name, IElemen
128129
return getElementFactory().findChildElement(this, childLoc, name, supplier, state);
129130
}
130131

132+
@Override
133+
public <T extends IElement> List<T> findChildElements(By childLoc, String name, Class<T> clazz, ElementState state, ElementsCount count) {
134+
return getElementFactory().findChildElements(this, childLoc, name, clazz, count, state);
135+
}
136+
137+
@Override
138+
public <T extends IElement> List<T> findChildElements(By childLoc, String name, IElementSupplier<T> supplier, ElementState state, ElementsCount count) {
139+
return getElementFactory().findChildElements(this, childLoc, name, supplier, count, state);
140+
}
141+
131142
protected <T> T doWithRetry(Supplier<T> action) {
132143
return getElementActionRetrier().doWithRetry(action);
133144
}

src/main/java/aquality/selenium/core/elements/ElementFactory.java

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import aquality.selenium.core.waitings.IConditionalWait;
1010
import com.google.inject.Inject;
1111
import org.openqa.selenium.By;
12+
import org.openqa.selenium.By.ByTagName;
1213
import org.openqa.selenium.By.ByXPath;
1314
import org.openqa.selenium.InvalidArgumentException;
1415
import org.openqa.selenium.WebElement;
@@ -26,6 +27,8 @@
2627
public class ElementFactory implements IElementFactory {
2728

2829
private static final int XPATH_SUBSTRING_BEGIN_INDEX = 10;
30+
private static final int TAGNAME_SUBSTRING_BEGIN_INDEX = 12;
31+
private static final String TAGNAME_XPATH_PREFIX = "//";
2932
private static final Duration ZERO_TIMEOUT = Duration.ZERO;
3033

3134
private final IConditionalWait conditionalWait;
@@ -59,10 +62,23 @@ public <T extends IElement> T findChildElement(IElement parentElement, By childL
5962
@Override
6063
public <T extends IElement> T findChildElement(IElement parentElement, By childLoc, String name, IElementSupplier<T> supplier, ElementState state) {
6164
String childName = name == null ? "Child element of ".concat(parentElement.getName()) : name;
62-
By fullLocator = new ByChained(parentElement.getLocator(), childLoc);
65+
By fullLocator = generateAbsoluteChildLocator(parentElement.getLocator(), childLoc);
6366
return supplier.get(fullLocator, childName, state);
6467
}
6568

69+
@Override
70+
public <T extends IElement> List<T> findChildElements(IElement parentElement, By childLoc, String name, Class<T> clazz, ElementsCount count, ElementState state) {
71+
IElementSupplier<T> elementSupplier = getDefaultElementSupplier(clazz);
72+
return findChildElements(parentElement, childLoc, name, elementSupplier, count, state);
73+
}
74+
75+
@Override
76+
public <T extends IElement> List<T> findChildElements(IElement parentElement, By childLoc, String name, IElementSupplier<T> supplier, ElementsCount count, ElementState state) {
77+
String childName = name == null ? "Child element of ".concat(parentElement.getName()) : name;
78+
By fullLocator = generateAbsoluteChildLocator(parentElement.getLocator(), childLoc);
79+
return findElements(fullLocator, childName, supplier, count, state);
80+
}
81+
6682
@Override
6783
public <T extends IElement> List<T> findElements(By locator, String name, IElementSupplier<T> supplier,
6884
ElementsCount count, ElementState state) {
@@ -119,14 +135,63 @@ public <T extends IElement> List<T> findElements(By locator, String name, Class<
119135
* @return target element's locator
120136
*/
121137
protected By generateXpathLocator(By multipleElementsLocator, WebElement webElement, int elementIndex) {
122-
Class supportedLocatorType = ByXPath.class;
123-
if (multipleElementsLocator.getClass().equals(supportedLocatorType)) {
138+
if (isLocatorSupportedForXPathExtraction(multipleElementsLocator)) {
124139
return By.xpath(
125-
String.format("(%1$s)[%2$s]", multipleElementsLocator.toString().substring(XPATH_SUBSTRING_BEGIN_INDEX), elementIndex));
140+
String.format("(%1$s)[%2$s]", extractXPathFromLocator(multipleElementsLocator), elementIndex));
126141
}
127142
throw new InvalidArgumentException(String.format(
128-
"Cannot define unique baseLocator for element %1$s. Multiple elements' baseLocator %2$s is not %3$s, and is not supported yet",
129-
webElement.toString(), multipleElementsLocator, supportedLocatorType));
143+
"Cannot define unique baseLocator for element %1$s. Multiple elements' baseLocator type %2$s is not supported yet",
144+
webElement.toString(), multipleElementsLocator.getClass()));
145+
}
146+
147+
/**
148+
* Extracts XPath from passed locator.
149+
* Current implementation works only with ByXPath.class and ByTagName locator types,
150+
* but you can implement your own for the specific WebDriver type.
151+
*
152+
* @param locator locator to get xpath from.
153+
* @return extracted XPath.
154+
*/
155+
protected String extractXPathFromLocator(By locator) {
156+
Class supportedLocatorType = ByXPath.class;
157+
if (locator.getClass().equals(supportedLocatorType)) {
158+
return locator.toString().substring(XPATH_SUBSTRING_BEGIN_INDEX);
159+
}
160+
if (locator.getClass().equals(ByTagName.class)){
161+
return TAGNAME_XPATH_PREFIX + locator.toString().substring(TAGNAME_SUBSTRING_BEGIN_INDEX);
162+
}
163+
throw new InvalidArgumentException(String.format(
164+
"Cannot define xpath from locator %1$s. Locator type %2$s is not %3$s, and is not supported yet",
165+
locator.toString(), locator.getClass(), supportedLocatorType));
166+
}
167+
168+
/**
169+
* Generates absolute child locator for target element.
170+
*
171+
* @param parentLoc parent locator
172+
* @param childLoc child locator relative to parent
173+
* @return absolute locator of the child
174+
*/
175+
protected By generateAbsoluteChildLocator(By parentLoc, By childLoc) {
176+
if (isLocatorSupportedForXPathExtraction(parentLoc) && isLocatorSupportedForXPathExtraction(childLoc)) {
177+
String childLocString = extractXPathFromLocator(childLoc);
178+
String parentLocString = extractXPathFromLocator(parentLoc);
179+
return By.xpath(parentLocString.concat(
180+
childLocString.startsWith(".") ? childLocString.substring(1) : childLocString));
181+
}
182+
return new ByChained(parentLoc, childLoc);
183+
}
184+
185+
/**
186+
* Defines is the locator can be transformed to xpath or not.
187+
* Current implementation works only with ByXPath.class and ByTagName locator types,
188+
* but you can implement your own for the specific WebDriver type.
189+
*
190+
* @param locator locator to transform
191+
* @return true if the locator can be transformed to xpath, false otherwise.
192+
*/
193+
protected boolean isLocatorSupportedForXPathExtraction(By locator) {
194+
return locator.getClass().equals(ByXPath.class) || locator.getClass().equals(ByTagName.class);
130195
}
131196

132197
/**

0 commit comments

Comments
 (0)