t.me/atinfo_chat Telegram группа по автоматизации тестирования

Поиск элемента через WebElement

Когда мы ищем дочерний элемент в основном элементе, можно ли сочетать xpath и css?

driver.findElement(By.cssSelector("")).findElement(By.xpath()).click()

Можно

главное чтобы xpath в этом случае начинался с точки

driver.findElement(By.cssSelector("bla bla")).findElement(By.xpath(".//*")).click()
3 Симпатий

Не понимаю почему нельзя найти элемент с использованием ancestor? пишу следующее

WebElement element = driver.findElement(By.cssSelector("[id='row.name']"));

String text = element.findElement(By.xpath(".//ancestor::section[@class='form-group nav-item']/h3/a")).getText()

text начинает искать с самого начала DOM, а не относительно element Искать элемент можно только ниже относительно элемента?

P.S. локатор проверил через браузер.

ancestor:: — Возвращает множество предков.

Множество с локатором section[@class='form-group nav-item']/h3/a - это N элементов, судя по вашей верстке. Ввиду того, что вы используете findElement, а не findElements, на выходе получаете первый элемент из множества. Если выберете второй способ, увидите целую пачку нодов. Все логично.

Но я считал что будет выполняться поиск от конкретного элемента. Конечно когда я пишу:

//*[@id='row.name']/ancestor::section[@class='form-group nav-item']/h3/a

поиск выполняется относительно //*[@id='row.name']

В element.findElement я также пытаюсь найти относительно этого же элемента. Вот в этом и вопрос, почему он не находит относительно заданного локатора?

Ну вообще как бы есть разница между // и /.

Если я напишу один слеш, вообще ничего не находит. На скриншоте пример выполнения этой регулярки в браузере:

Начнем с того, что такое @id='row.name' и где оно на скрине?

Перепутал c row.descriptional, но не суть:

HTML:


<div id='main'>
    <div id='first'>
        <section>
            <h3>
                <a>first</a>
            </h3>
        </section>
    </div>
    <div id='second'>
        <section>
            <h3>
                <a>second</a>
            </h3>
        </section>
    </div>    
    <div id='third'>
        <section>
            <h3>
                <a>third</a>
            </h3>
            <div id='target'></div>
        </section>
    </div>    
</div>

Code:


WebDriver driver = new FirefoxDriver();
driver.get("http://jsfiddle.net/fcf0zvv1");
driver.switchTo().frame("result");
WebElement el1 = driver.findElement(By.cssSelector("[id='target']"));
WebElement el2 = el1.findElement(By.xpath(".//ancestor::section/h3/a"));
System.out.println(el2.getText());
driver.quit();

Output:

third

Такой способ я выше описал, он у меня не срабатывает. Игнорирует el1.

Ну я продемонстрировал, что ничего не игнорируется и данная конструкция вполне себе дееспособная.
Если у вас что-то “не срабатывает” - проблема на вашей стороне. Единственный действенный способ для вас - изолировать проблемное место и предоставить код, который бы мог запустить любой посетитель форума. Так как в таких случаях (“у всех работает, а у меня нет”) дебажить по скриншотам довольно затруднительно.

Понял. Спасибо

Хотел бы спросить вас по поводу точки. Почему локатор должен начинаться именно с точки? Дело в том что вы решили мне сейчас вопрос по вот этой теме. Обычно когда я ищу элемент в элементе, то не использовал точку и команды (click, isDisplayed…) выполнялись верно. Но когда я начал работу с Popup именно один из всех элементов не срабатывал из-за этой точки. Почему?

По спецификации.
http://www.w3.org/TR/xpath/

[quote]
// is short for /descendant-or-self::node()/. For example, //para is short for /descendant-or-self::node()/child::para and so will select any para element in the document (even a para element that is a document element will be selected by //para since the document element node is a child of the root node); div//para is short for div/descendant-or-self::node()/child::para and so will select all para descendants of div children.[/quote]

[quote]
A location step of . is short for self::node(). This is particularly useful in conjunction with //. For example, the location path .//para is short for self::node()/descendant-or-self::node()/child::para and so will select all para descendant elements of the context node.[/quote]

Я думал что в случае такого поиска элементов (//foo) он будет продолжать поиск от существующего элемента. Походу точка или self::node() говорит о том что поиск будет начинаться относительно предыдущего найденного элемента. Для примера продемонстрирую следующий скриншот:

На нем можно увидеть, что действительно в DOM находится 2 элемента. Поэтому когда я пытался искать элемент в элементе не используя точку, то локатор использовался относительно body а не последнего элемента:

public class Example{
...
public void testLocator(){

WebElement element = driver.findElement(By.xpath("//*[@additional-view][last()]//section[@name='row.__states.AvalabiltyOnVideoServers']"));

element.findElement(By.xpath("//td[normalize-space(.)='Московский']/input[@ng-model='row.__selected']")).click() // так как на странице 2 элемента с последним локатором, то найдет 2 элемента и выберет 1 который как видно на скриншоте находится за Popup. element учитываться не будет

element.findElement(By.xpath(".//td[normalize-space(.)='Московский']/input[@ng-model='row.__selected']")).click() // точка как я выше указал говорит нам что поиск локатора нужно начать относительно выше найденного элемента

}
...
}