Xpath для поиска родительского элемента со смещением в элементе

#selenium #xpath

#selenium #xpath

Вопрос:

У меня есть автоматический тест Selenium, который использует следующий XPath

 "//tr[td/strong='Riparian 2m Ungrazed - RBS' and td/button[@id='btnDeleteOption_WG']]"
  

В настоящее время, всякий раз, когда я запрашиваю этот XPath, он возвращает нужный мне элемент, но также возвращает другой скрытый элемент, которого нет на экране.

Я сравнил два элемента, и единственное различие заключается в следующем

 offsetHeight: 39           offsetHeight: 0
offsetLeft: 8              offsetLeft: 0
offsetParent: td           offsetParent: null
offsetTop: 10              offsetTop: 0
offsetWidth: 179           offsetWidth: 0
  

Мне нужно найти элемент слева с параметром offsetParent <td> .

Кто-нибудь может помочь с XPath, чтобы найти это?

Спасибо!

Ниже приведен HTML-код элемента:

 <td class="text-center" style="white-space: nowrap;">
  <button id="btnShowItems_WG" class="btn btn-sm btn-primary" ng-disabled="!WG_field.optionHasItems" ng-click="WG_field.optionHasItems ? items.visible = !items.visible : false;" title="View Items" disabled="disabled">
        <span class="glyphicon glyphicon-list"></span>
    </button>
  <button ng-hide="!efsEditRole_RoleAssignedToUser" ng-disabled="false" id="btnAddOptionItem_16_WG" class="btn btn-primary btn-sm" ng-click="appSummaryVm.addItem($index, 'WG'); appSummaryVm.addItemsByOption(WG_field.fieldId, WG_field.tranche, WG_field.optionCode, WG_field.optionTypeId, $index, 'WG');"
    title="Add Item" aria-hidden="false">
        <span class="glyphicon glyphicon-plus"></span>
    </button>
</td>
<td><strong>Riparian 2m Ungrazed - RBS</strong></td>
<td style="width: 10%;">
  <input name="optionQuantityWG_16" type="number" class="form-control ng-pristine ng-untouched ng-valid ng-not-empty ng-valid-min ng-valid-max ng-valid-required ng-valid-pattern" ng-class="{
                              'form-control input-error' : (existingOptionsWG.optionQuantityWG_16.$dirty amp;amp;amp;amp; (
                                                                    existingOptionsWG.optionQuantityWG_16.$error.max
                                                                    || existingOptionsWG.optionQuantityWG_16.$error.min
                                                                    || existingOptionsWG.optionQuantityWG_16.$error.pattern
                                                                    || existingOptionsWG.optionQuantityWG_16.$invalid
                                                                    || existingOptionsWG.optionQuantityWG_16.$error.required
                                                            ))
                            }" ng-change="appSummaryVm.updateOptionTotals(WG_field, 'WG'); appSummaryVm.edit();" ng-model="WG_field.optionQuantity" ng-pattern="/^d (.d{1,2})?$/" min="" max="" step="0.01" style="text-align: right;" ng-true-value="10.00"
    required="" id="optionWGW0316" ng-disabled="!efsEditRole_RoleAssignedToUser" aria-invalid="false">

  <div class="form-control inline-errorUp ng-hide" ng-show="existingOptionsWG.optionQuantityWG_16.$dirty amp;amp;amp;amp; (
                    existingOptionsWG.optionQuantityWG_16.$error.max
                    || existingOptionsWG.optionQuantityWG_16.$error.min
                    || existingOptionsWG.optionQuantityWG_16.$error.pattern
                    || existingOptionsWG.optionQuantityWG_16.$error.required
                    || existingOptionsWG.optionQuantityWG_16.$invalid
                 )" aria-hidden="true">
    <span ng-show="existingOptionsWG.optionQuantityWG_16.$dirty amp;amp;amp;amp; existingOptionsWG.optionQuantityWG_16.$error.max" aria-hidden="true" class="ng-hide">
            maximum of 999999999.99
        </span>
    <span ng-show="existingOptionsWG.optionQuantityWG_16.$dirty amp;amp;amp;amp; existingOptionsWG.optionQuantityWG_16.$error.min" aria-hidden="true" class="ng-hide">
            cannot be zero or less
        </span>
    <span ng-show="existingOptionsWG.optionQuantityWG_16.$dirty amp;amp;amp;amp; (existingOptionsWG.optionQuantityWG_16.$error.pattern || existingOptionsWG.optionQuantityWG_16.$invalid)" ng-hide="existingOptionsWG.optionQuantityWG_16.$error.max || existingOptionsWG.optionQuantityWG_16.$error.min || existingOptionsWG.optionQuantityWG_16.$error.required"
      aria-hidden="false" class="">
            2 decimal digits only
        </span>
    <span ng-show="existingOptionsWG.optionQuantityWG_16.$dirty amp;amp;amp;amp; existingOptionsWG.optionQuantityWG_16.$error.required" aria-hidden="true" class="ng-hide">
            required
        </span>
  </div>
</td>
<td class="text-right">6.00
  <!----><span ng-if="WG_field.optionUnitType">/</span>
  <!---->m</td>
<td class="text-right">60.00</td>
<td class="text-right">0.70</td>
<td class="text-right">96.00</td>
<td class="text-center">
  <button ng-hide="!efsEditRole_RoleAssignedToUser" ng-disabled="false" id="btnDeleteOption_WG" class="btn btn-warning btn-sm" style="width: 110px;" ng-click="appSummaryVm.showRemovePrompt(WG_field, 'option', 'WG', $event); appSummaryVm.edit();" title="Delete 'Riparian 2m Ungrazed' Option from field 3/003/003/3"
    aria-hidden="false">
        <span class="glyphicon glyphicon-trash"></span> remove option
    </button>
</td>  

Комментарии:

1. можете ли вы предоставить общий доступ к html?

2. Без соответствующего HTML я не уверен, как вы ожидаете, что мы создадим новый локатор и решим эту проблему.

3. прошу прощения, я добавил соответствующий HTML

4. Ну, перво-наперво, идентификаторы должны быть уникальными в HTML-документе. Основываясь на вышесказанном, вы могли бы просто использовать "//button[@id='btnDeleteOption_WG']" , потому что это единственный идентификатор в этом фрагменте кода. Если идентификаторы не уникальны, это ошибка, которую вы должны заставить своих разработчиков исправить.

Ответ №1:

Вы можете получить все элементы и отфильтровать их по видимости. Ниже приведен пример кода Java, как получить первый видимый элемент из списка веб-элементов.

С Java 8:

 List<WebElement> elements = driver.findElements(By.xpath("//tr[td/strong='Riparian 2m Ungrazed - RBS' and td/button[@id='btnDeleteOption_WG']]"));
WebElement element = elements
        .stream()
        .filter(WebElement::isDisplayed)
        .collect(Collectors.toList())
        .get(0);
  

Python:

 elements = driver.find_elements_by_xpath("//tr[td/strong='Riparian 2m Ungrazed - RBS' and td/button[@id='btnDeleteOption_WG']]")
visible_one = list(filter(lambda x: x.is_displayed(), elements))[0]
visible_one.click()