Получить локатор By уже найденного WebElement

Есть ли элегантный способ получить локатор Selenium WebElement, который я уже нашел/идентифицировал?

Чтобы быть понятным в вопросе: я хочу, чтобы "By locator" использовался для поиска элемента. Я в этом случае не заинтересован в конкретном атрибуте или определенном локаторе, таком как css-locator.

Я знаю, что могу проанализировать результат метода WebElement toString():

WebElement element = driver.findElement(By.id("myPreciousElement"));
System.out.println(element.toString());

Выход был бы, например:

[[FirefoxDriver: firefox на WINDOWS (....)] → id: myPreciousElement]

если вы нашли свой элемент по xpath:

WebElement element = driver.findElement(By.xpath("//div[@someId = 'someValue']"));
System.out.println(element.toString());

Тогда ваш вывод будет:

[[FirefoxDriver: firefox на WINDOWS (....)] → xpath://div [@someId = 'someValue']]

Итак, в настоящее время я написал свой собственный метод, который анализирует этот результат и дает мне "воссозданный" по локатору.


НО есть ли более элегантный способ, уже реализованный в Selenium, чтобы получить локатор By, используемый для поиска элемента?

Я пока не смог найти его.

Если вы уверены, что это не так, вы можете подумать о какой-либо причине, почему разработчики API могут не предоставлять эту функцию?



* Несмотря на то, что это не имеет никакого отношения к вопросу, если кто-то задается вопросом, почему вам понадобится эта функциональность, всего 2 примера:

  • если вы используете PageFactory, скорее всего, не будете иметь локаторы как переменные-члены в вашем классе страницы, но вам может понадобиться их позже при работе с элементами страницы.
  • вы работаете с API-интерфейсами людей, которые просто используют шаблон объекта страницы без PageFactory и, следовательно, ожидают, что вы передадите локаторы вместо самого элемента. *

Ответ 1

Ответ Нет. Вы не можете извлечь By из ранее найденного WebElement по умолчанию.

При этом можно реализовать индивидуальное решение, но Selenium не предлагает этого "из коробки".

Подумайте о том, почему "почему"..

By by = By.id("someId");
WebElement e = driver.findElement(by);

у вас уже есть объект By, поэтому вам не нужно вызывать что-то вроде e.getBy()

Ответ 2

Нет, нет. Я применил возможное решение как прокси:

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
}

Ответ 3

Для меня работал с commons-lang3

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.7</version>
</dependency>

Для удаленного веб-элемента используйте метод вроде:

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]";
    }

Ответ 4

Я написал эту служебную функцию, которая возвращает строковую комбинацию стратегии локатора + значение локатора.

private String getLocatorFromWebElement(WebElement element) {

    return element.toString().split("->")[1].replaceFirst("(?s)(.*)\\]", "$1" + "");
}

Ответ 5

Нет никакого элегантного способа, предоставленного Selenium. И это ужасно

1) PageObject и PageFactory подразумевают, что у нас есть WebElements в классах Page, но у нас нет локаторов этих элементов.

2) Если я нахожу элемент в качестве потомка текущего элемента, используя webElement.findElement(By), то у меня нет локатора этого потомка, даже если я сохранил родительский локатор в переменной.

3) Если я использую функцию findElements которая возвращает List элементов, то у меня нет локатора для каждого конкретного элемента.

4) Наличие локатора для элемента полезно, по крайней мере, потому что ExpectedConditions с локатором в качестве параметра реализованы намного лучше, чем ExpectedConditions с WebElement качестве параметра.

Для меня Selenium - непродуманная и плохо реализованная библиотека

Ответ 6

В настоящее время не существует какого-либо конкретного метода с конца селена, чтобы сделать это. Что вы можете сделать, это написать свой собственный метод. Вы получите представление о том, какой тип селектора и путь используется, просто распечатав имеющийся у вас веб-элемент.

Это выглядит примерно так

[[ChromeDriver: chrome в XP (d85e7e220b2ec51b7faf42210816285e)] → xpath://input [@title= 'Search']]

Теперь, что вам нужно сделать, это извлечь локатор и его значение. Вы можете попробовать что-то вроде этого

'private By getByFromElement (элемент WebElement) {

    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;
}

'