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

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

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

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

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']]

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


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

Я не мог найти один до сих пор.

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



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

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

8 ответов

Решение

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

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

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

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

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

Я мог бы реализовать что-то подобное в будущем, но без обещаний. Опять же - это просто сахар.

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

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
}

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

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

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

`

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

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

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

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

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

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

Для меня работал с 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]";
    }

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

private String getLocatorFromWebElement(WebElement element) {

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

Мое решение, когда я столкнулся с необходимостью использовать локатор By для ExpectedConditions, и у меня были мои локаторы в Фабрике объектов страницы, состояло в том, чтобы использовать строку, в которой был локатор, а затем построить мой объект By и локатор элементов из этого.

      public class PageObject {
    private static final String XPATH_NAME = "...";

    public @iOSXCUITFindBy(xpath = XPATH_NAME)
    List<MobileElement> mobileElementName;

    public By getByXPath(){
        return new By.ByXPath(XPATH_NAME);
    }

    public PageObject() {
        PageFactory.initElements(driver, this);
    }
}

Можно получить локатор XPath для данного WebElement:

      public static String getFullXPath(WebDriver driver, WebElement element) {
    // Use JavaScript to obtain the full XPath
    JavascriptExecutor executor = (JavascriptExecutor) driver;
    String fullXPath = (String) executor.executeScript(
        "function absoluteXPath(element) {" +
        "var comp, comps = [];" +
        "var parent = null;" +
        "var xpath = '';" +
        "var getPos = function(element) {" +
        "var position = 1, curNode;" +
        "if (element.nodeType == Node.ATTRIBUTE_NODE) {" +
        "return null;" +
        "}" +
        "for (curNode = element.previousSibling; curNode; curNode = curNode.previousSibling) {" +
        "if (curNode.nodeName == element.nodeName) {" +
        "++position;" +
        "}" +
        "}" +
        "return position;" +
        "};" +
        "if (element instanceof Document) {" +
        "return '/';" +
        "}" +
        "for (; element && !(element instanceof Document); element = element.nodeType == Node.ATTRIBUTE_NODE ? element.ownerElement : element.parentNode) {" +
        "comp = comps[comps.length] = {};" +
        "switch (element.nodeType) {" +
        "case Node.TEXT_NODE:" +
        "comp.name = 'text()';" +
        "break;" +
        "case Node.ATTRIBUTE_NODE:" +
        "comp.name = '@' + element.nodeName;" +
        "break;" +
        "case Node.PROCESSING_INSTRUCTION_NODE:" +
        "comp.name = 'processing-instruction()';" +
        "break;" +
        "case Node.COMMENT_NODE:" +
        "comp.name = 'comment()';" +
        "break;" +
        "case Node.ELEMENT_NODE:" +
        "comp.name = element.nodeName;" +
        "break;" +
        "}" +
        "comp.position = getPos(element);" +
        "}" +
        "for (var i = comps.length - 1; i >= 0; i--) {" +
        "comp = comps[i];" +
        "xpath += '/' + comp.name.toLowerCase();" +
        "if (comp.position !== null) {" +
        "xpath += '[' + comp.position + ']';" +
        "}" +
        "}" +
        "return xpath;" +
        "} return absoluteXPath(arguments[0]);", element);

    return fullXPath;
}
Другие вопросы по тегам