Селен: невозможно извлечь дочерний элемент из родительского - PullRequest
0 голосов
/ 27 февраля 2019

Я пытаюсь нажать на элемент, используя структуру:

parentElement.findElement(XPath of Child Element);

Однако, это выдает org.openqa.selenium.NoSuchElementException.

В настоящее время я очищаю список (родительских) элементов, используя XPath: .//*[@id='savedCartViewForm']/div/div.Это идентифицирует правильный список элементов (работает как ожидалось).Когда у меня есть список, я применяю функцию:

parentElement.findElement(By.xpath(XPath));

Использование XPath:

.//input[contains(@aria-label,'Delete')]

и

.//input[contains(@value,'Delete')]

Однако это вызывает org.openqa.selenium.NoSuchElementException.

Я исследовал это на SO в течение нескольких часов и, основываясь на нескольких постах, добавил '.'перед двумя косыми чертами, что означает относительно родителя (а не относительно всего DOM).

Если я удаляю точку, я всегда получаю первый элемент на странице, а не дочерний элемент каждого родительского элемента в списке.

HTML имеет следующую структуру:

<form id="savedCartViewForm" action="/gp/cart/view.html/ref=ord_cart_shr?app-nav-type=none&dc=df" method="post">
  <input type="hidden" value="1" name="fromAUI" />
  <input type="hidden" value="4CPM1MKXXXXXZ" name="requestID" />
  <input type="hidden" value="15XXXXXX0" name="timeStamp" />
  <input type="hidden" value="gkAVhUvXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAFx2yhxAAA" name="token" />
  <div class="a-divider a-divider-section">
    <div class="a-row sc-list-head sc-java-remote-feature">
      <div class="a-row sc-list-body sc-java-remote-feature">
        <div class="a-row sc-list-item sc-list-item-border sc-java-remote-feature" data-quantity="4" data-price="8.99" data-previous-offer-id="ekOCOsC%2Bl7B8l5MNqXXXXXXXXXXXXXXXXXXXXXXXXXXbBFS5rQm%2BDi9cGGpFufHEITXWAr6tAjIiPTFbZiXjZyIce7Y" data-outofstock="0"
          data-minquantity="1" data-itemtype="saved" data-itemislastpantryitem="0" data-itemid="S2e1cb5b5-ebb5-4e70-8474-c596d80bd99a" data-itemcategory="normal" data-item-count="1" data-isprimeasin="0" data-giftwrapped="0" data-giftable="0" data-encoded-offering="erF6bSsUaPN0XP13xfXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYvWwmsZJ2HZwKuDJbLvjiR%2BI2CQAPyug7sPmmGV7DdJ"
          data-best-offer-id="erF6bSsUaPN0XP13xfsXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXwmsZJ2HZwKuDJbLvjiR%2OezFaQg7sPmmGV7DdJ" data-asin="B0742J1KYD">
          <div class="sc-list-item-spinner" style="display:none;">
            <div class="sc-list-item-overwrap" style="display:none;" />
            <div class="sc-list-item-removed-msg a-padding-medium" style="display:none;">
              <div class="sc-list-item-content">
                <div class="a-row a-spacing-base a-spacing-top-base">
                  <div class="a-column a-span10">
                    <div class="a-fixed-left-grid">
                      <div class="a-fixed-left-grid-inner" style="padding-left:115px">
                        <div class="a-fixed-left-grid-col a-float-left a-col-left" style="width:115px;margin-left:-115px;float:left;">
                          <div class="a-fixed-left-grid-col a-col-right" style="padding-left:0%;float:left;">
                            <ul class="a-unordered-list a-nostyle a-vertical a-spacing-mini">
                              <div class="a-row sc-action-links">
                                <span class="a-size-small sc-action-delete">
<span class="a-declarative" data-sc-item-action="{"itemID":"S2e1cbXXXXXXXXXXXXXXXXXXXXXXXXXXd80b9a","itemType":"saved","isWishListItem":0,"action":"delete","isFresh":0}" data-action="sc-item-action">
<input type="submit" aria-label="Delete MENSI Outdoor Patio Heater M6*0.75 Head Thread With M8X1 End Connection Nuts Thermocouple 410mm" value="Delete" name="submit.delete.S2XXXXXXXXXXXXXXXXXXXXXXXXXX0bd99a"/>
</span>
                                </span>
                                <i class="a-icon a-icon-text-separator" aria-label="|" role="img" />
                                <span class="a-size-small sc-action-add-best-offer sc-invisible-when-no-js">
<input class="wl-refdata" type="hidden" value="true" name="isSelectedForCheckout"/>
<i class="a-icon a-icon-text-separator" aria-label="|" role="img"/>
<span class="a-size-small sc-action-move-to-wishlist sc-invisible-when-no-js">
</div>
</div>
</div>
</div>
</div>
<div class="a-column a-span2 a-text-left a-span-last">
</div>
</div>
</div>
<div class="a-row sc-list-item sc-list-item-border sc-java-remote-feature" data-quantity="8" data-price="7.37" data-outofstock="0" data-minquantity="1" data-itemtype="saved" data-itemislastpantryitem="0" data-itemid="See02cb-XXXXXXXXXXXXXXXXXXXXXXXXXXc5" data-itemcategory="normal" data-item-count="2" data-isprimeasin="0" data-giftwrapped="0" data-giftable="0" data-encoded-offering="MzgFwAMDc3XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4bqRzg7IzfFp%2B%2BDg%2BAYyl4X" data-asin="B004DYKIH4">
<div class="sc-list-item-spinner" style="display:none;">
<div class="sc-list-item-overwrap" style="display:none;"/>
<div class="sc-list-item-removed-msg a-padding-medium" style="display:none;">
<div class="sc-list-item-content">
<div class="a-row a-spacing-base a-spacing-top-base">
<div class="a-column a-span10">
<div class="a-fixed-left-grid">
<div class="a-fixed-left-grid-inner" style="padding-left:115px">
<div class="a-fixed-left-grid-col a-float-left a-col-left" style="width:115px;margin-left:-115px;float:left;">
<div class="a-fixed-left-grid-col a-col-right" style="padding-left:0%;float:left;">
<ul class="a-unordered-list a-nostyle a-vertical a-spacing-mini">
<div class="a-row sc-action-links">
<span class="a-size-small sc-action-delete">
<span class="a-declarative" data-sc-item-action="{"itemID":"SeccXXXXXXXXXXXXXXXXXXXXXXXXXXd441c5","itemType":"saved","isWishListItem":0,"action":"delete","isFresh":0}" data-action="sc-item-action">
<input type="submit" aria-label="Delete Bit Adapter - 1/4" to 1/4" - Turn Any Ratchet Into a Driver! Now with Quick-Change By Pro Tools" value="Delete" name="submit.delete.SeccXXXXXXXXXXXXXXXXXXXXXXXXXX5"/>
</span>
                                </span>
                                <i class="a-icon a-icon-text-separator" aria-label="|" role="img" />
                                <span class="a-size-small sc-action-move-to-cart">
<input class="wl-refdata" type="hidden" value="B003IXYJYO" name="creativeAsin"/>
<input class="wl-refdata" type="hidden" value="5BD74XXXXXXXXXXXXXXXXXXXXXXXXXX68" name="assocToken"/>
<input class="wl-refdata" type="hidden" value="xsc" name="linkCode"/>
<input class="wl-refdata" type="hidden" value="true" name="isSelectedForCheckout"/>
<i class="a-icon a-icon-text-separator" aria-label="|" role="img"/>
<span class="a-size-small sc-action-move-to-wishlist sc-invisible-when-no-js">
<i class="a-icon a-icon-text-separator" aria-label="|" role="img"/>
<span id="comparison-lite-modal-B004DYKIH4" class="a-declarative" data-a-modal="{"cache":"0","hideHeader":"true","width":"80%","ajaxFailMsg":"We\u2019re sorry, an error has occurred. Please try again.","url":"/compare/product/B004DYKIH4/ref=psdc_sXXXXB004DYKIH4?viewType=sfl","height":"570"}" data-action="a-modal">
</div>
</div>
</div>
</div>
</div>
<div class="a-column a-span2 a-text-left a-span-last">
</div>
</div>
</div>

Исходя из всего, что я прочитал на SO, XPath выше должен работать.Что вызывает это исключение?

Спасибо

Обновление: Вот несколько ссылок, на которые я опирался:

Поиск дочерних узловWebElements в селене

WebElement.FindElement (By.XPath) возвращает элемент не относительно родителя, а к документу

Как получить всех потомковэлемента, использующего webdriver?

Ответы [ 3 ]

0 голосов
/ 27 февраля 2019

Я исправил проблему, обновив свой XPath до:

/descendant::input[@value = 'Delete']

Теперь я могу определить правильный элемент.Тем не менее, мне до сих пор неясно, почему оригинальный XPath не сработал, а этот работает - так как согласно нескольким постам, которые я прочитал, «//» является сокращением для потомка или себя и.добавлен так, что поиск начинается относительно родительского элемента.Таким образом, причина все еще не имеет смысла для меня.

Если кто-то может предоставить обновленный ответ с объяснением, я выберу этот ответ в качестве ответа на вопрос вместо моего собственного.

0 голосов
/ 05 марта 2019

Это одно из мест, где Селен усложняется.Функциональность Xpath зависит от нескольких вещей.

  1. Некоторые браузеры имеют встроенную поддержку Xpath, а некоторые нет (Предположим, что это всегда может быть изменено).
  2. Некоторые драйверы используют собственную реализацию браузера, а некоторые нет (Предположим, чтоэто всегда может быть изменено).
  3. Если по какой-либо причине native не поддерживается, Selenium переключится на поддержку Wicked Good Xpath.

Теперь у вас есть потенциально разные функции для разных браузеров и разных двоичных версий драйверов.Давайте предположим, что все нативные реализации на 100% соответствуют спецификации для целей этого ответа.

Если вы сталкиваетесь с бинарной версией драйвера / браузером, вернувшейся к Wicked Good Xpath, она не полностью соответствует спецификации на 100%,Вы, вероятно, видите эту ошибку:

https://github.com/google/wicked-good-xpath/issues/43

Теперь имейте в виду, что Selenium использовал раздвоенную версию Wicked good Xpath, поэтому она может отличаться немного больше, чем выше.Исторически // не означало «потомок или я» в мире Selenium, это означало поиск по всему DOM.Текущая реализация Selenium в Wicked Good Xpath доступна по адресу:

https://github.com/SeleniumHQ/selenium/tree/07a18746ff756e90fd79ef253a328bd7dfa9e6dc/third_party/js/wgxpath

Поскольку вы уже разработали, решение вашей проблемы - «потомок или сам», но с сокращенной версии«//» означает что-то еще в Wicked Good XPath, вы не получили ожидаемый результат.

TL; DR;

Не используйте сокращение Xpath при создании Xpaths для тестов Selenium.

0 голосов
/ 27 февраля 2019

Попробуйте код ниже с помощью селекторов css вместо xpath.

List<WebElement> rows = driver.findElements(By.cssSelector("#savedCartViewForm .a-row"));
rows.forEach(row -> {
    WebElement deleteButton = row.findElement(By.cssSelector("input[value = 'Delete']"));
    System.out.println(deleteButton.getAttribute("aria-label"));
});

С xpath:

List<WebElement> rows = driver.findElements(By.xpath("//form[@id = 'savedCartViewForm']//div[contains(@class, 'a-row')]"));
rows.forEach(row -> {
    WebElement deleteButton = row.findElement(By.xpath(".//input[@value = 'Delete']"));
    System.out.println(deleteButton.getAttribute("aria-label"));
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...