Is there an elegant way to get the By locator of a Selenium WebElement, that I already found/identified?
To be clear about the question: I want the
Answer is No. You cannot extract a By
from a previously found WebElement by default.
That being said - it's possible to implement a custom solution, but Selenium does not offer this out-of-the-box.
Consider the following, on "why"..
By by = By.id("someId");
WebElement e = driver.findElement(by);
you already have the By
object, so you wouldn't need to call something like e.getBy()
No, there's not. I have implemented a possible solution as a proxy:
public class RefreshableWebElement implements WebElement {
public RefreshableWebElement(Driver driver, By by) {
this.driver = driver;
this.by = by;
}
// ...
public WebElement getElement() {
return driver.findElement(by);
}
public void click() {
getElement().click();
}
// other methods here
}
Currently there is no specific method from selenium's end to do so. What you can do is write your custom method. You will get the clue of what selector type and path is used by just printing the webElement you have.
It looks something like this
[[ChromeDriver: chrome on XP (d85e7e220b2ec51b7faf42210816285e)] -> xpath: //input[@title='Search']]
Now, what you need to do is to extract the locator and its value. You can try something like this
`private By getByFromElement(WebElement element) {
By by = null;
//[[ChromeDriver: chrome on XP (d85e7e220b2ec51b7faf42210816285e)] -> xpath: //input[@title='Search']]
String[] pathVariables = (element.toString().split("->")[1].replaceFirst("(?s)(.*)\\]", "$1" + "")).split(":");
String selector = pathVariables[0].trim();
String value = pathVariables[1].trim();
switch (selector) {
case "id":
by = By.id(value);
break;
case "className":
by = By.className(value);
break;
case "tagName":
by = By.tagName(value);
break;
case "xpath":
by = By.xpath(value);
break;
case "cssSelector":
by = By.cssSelector(value);
break;
case "linkText":
by = By.linkText(value);
break;
case "name":
by = By.name(value);
break;
case "partialLinkText":
by = By.partialLinkText(value);
break;
default:
throw new IllegalStateException("locator : " + selector + " not found!!!");
}
return by;
}
`
There is no elegant way provided by Selenium. And this is horrible
1) PageObject
and PageFactory
implies that we have WebElements
in Page
classes, but we don't have locators of those elements.
2) If I find element as descendant of current element using webElement.findElement(By)
, then I don't have the locator of this descendant even if I stored parent's locator in the variable.
3) If I use findElements
function that returns List
of elemetns, then I don't have locator for each specific element.
4) Having locator for element is useful at least because ExpectedConditions
with locator as parameter are much better implemented than ExpectedConditions
with WebElement
as parameter.
For me Selenium is ill-conceived and poorly implemented library
For me worked with commons-lang3
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
For remote web element use method like:
protected String getLocator(WebElement element) {
try {
Object proxyOrigin = FieldUtils.readField(element, "h", true);
Object locator = FieldUtils.readField(proxyOrigin, "locator", true);
Object findBy = FieldUtils.readField(locator, "by", true);
if (findBy != null) {
return findBy.toString();
}
} catch (IllegalAccessException ignored) {
}
return "[unknown]";
}
I had written this utility function which returns a string combination of locator strategy + locator value.
private String getLocatorFromWebElement(WebElement element) {
return element.toString().split("->")[1].replaceFirst("(?s)(.*)\\]", "$1" + "");
}