Selenium WebDriver XPath 指向第二行,但 findElements returns 第一行

Selenium WebDriver XPath points to second row, but findElements returns first row

我有 HTML 看起来像这样:

<table id="data-table" class="table table-striped table-bordered table-hover dataTable no-footer" role="grid" style="width: 100%;">
    <thead>
        <tr role="row" style="height: 0px;"><th scope="col" class="sorting" aria-controls="data-table" rowspan="1" colspan="1" style="width: 277px; padding-top: 0px; padding-bottom: 0px; border-top-width: 0px; border-bottom-width: 0px; height: 0px;"><div class="dataTables_sizing" style="height:0;overflow:hidden;">Store Number</div></th><th scope="col" class="sorting" aria-controls="data-table" rowspan="1" colspan="1" style="width: 241px; padding-top: 0px; padding-bottom: 0px; border-top-width: 0px; border-bottom-width: 0px; height: 0px;"><div class="dataTables_sizing" style="height:0;overflow:hidden;">Store Name</div></th><th scope="col" class="sorting" aria-controls="data-table" rowspan="1" colspan="1" style="width: 156px; padding-top: 0px; padding-bottom: 0px; border-top-width: 0px; border-bottom-width: 0px; height: 0px;"><div class="dataTables_sizing" style="height:0;overflow:hidden;">Active</div></th><th scope="col" class="sorting" aria-controls="data-table" rowspan="1" colspan="1" style="width: 237px; padding-top: 0px; padding-bottom: 0px; border-top-width: 0px; border-bottom-width: 0px; height: 0px;"><div class="dataTables_sizing" style="height:0;overflow:hidden;">Action</div></th></tr>
    </thead>

    <tbody>







    <tr id="store-3" data-model="{&quot;Id&quot;:3,&quot;Name&quot;:&quot;a&quot;,&quot;IsActive&quot;:true,&quot;DBVersion&quot;:&quot;&quot;,&quot;Address&quot;:{&quot;Address&quot;:&quot;&quot;,&quot;Address2&quot;:&quot;&quot;,&quot;State&quot;:&quot;&quot;,&quot;City&quot;:&quot;&quot;,&quot;Zip&quot;:0},&quot;Phone&quot;:&quot;&quot;}" role="row" class="odd">
            <th scope="row" class="sorting_1">3</th>
            <td>a</td>
            <td>
                <label class="tgl">
                    <input onclick="ToggleStoreActive(this,event)" class="form-isactive" type="checkbox" checked="">
                    <span class="tgl_body">
                        <span class="tgl_switch"></span>
                        <span class="tgl_track">
                            <span class="tgl_bgd"></span>
                            <span class="tgl_bgd tgl_bgd-negative"></span>
                        </span>
                    </span>
                </label>
            </td>
            <td>
                <button class="btn btn-danger" onclick="DeleteStore(this)">
                    <i class="fa fa-trash-o" aria-hidden="true"></i>
                </button>
                <button class="btn btn-primary" onclick="PopModel(this)">
                    <i class="fa fa-pencil" aria-hidden="true"></i>
                </button>
            </td>
        </tr><tr id="store-4" data-model="{&quot;Id&quot;:4,&quot;Name&quot;:&quot;b&quot;,&quot;IsActive&quot;:true,&quot;DBVersion&quot;:&quot;&quot;,&quot;Address&quot;:{&quot;Address&quot;:&quot;&quot;,&quot;Address2&quot;:&quot;&quot;,&quot;State&quot;:&quot;&quot;,&quot;City&quot;:&quot;&quot;,&quot;Zip&quot;:0},&quot;Phone&quot;:&quot;&quot;}" role="row" class="even">
            <th scope="row" class="sorting_1">4</th>
            <td>b</td>
            <td>
                <label class="tgl">
                    <input onclick="ToggleStoreActive(this,event)" class="form-isactive" type="checkbox" checked="">
                    <span class="tgl_body">
                        <span class="tgl_switch"></span>
                        <span class="tgl_track">
                            <span class="tgl_bgd"></span>
                            <span class="tgl_bgd tgl_bgd-negative"></span>
                        </span>
                    </span>
                </label>
            </td>
            <td>
                <button class="btn btn-danger" onclick="DeleteStore(this)">
                    <i class="fa fa-trash-o" aria-hidden="true"></i>
                </button>
                <button class="btn btn-primary" onclick="PopModel(this)">
                    <i class="fa fa-pencil" aria-hidden="true"></i>
                </button>
            </td>
        </tr><tr id="store-5" data-model="{&quot;Id&quot;:5,&quot;Name&quot;:&quot;c&quot;,&quot;IsActive&quot;:true,&quot;DBVersion&quot;:&quot;&quot;,&quot;Address&quot;:{&quot;Address&quot;:&quot;&quot;,&quot;Address2&quot;:&quot;&quot;,&quot;State&quot;:&quot;&quot;,&quot;City&quot;:&quot;&quot;,&quot;Zip&quot;:0},&quot;Phone&quot;:&quot;&quot;}" role="row" class="odd">
            <th scope="row" class="sorting_1">5</th>
            <td>c</td>
            <td>
                <label class="tgl">
                    <input onclick="ToggleStoreActive(this,event)" class="form-isactive" type="checkbox" checked="">
                    <span class="tgl_body">
                        <span class="tgl_switch"></span>
                        <span class="tgl_track">
                            <span class="tgl_bgd"></span>
                            <span class="tgl_bgd tgl_bgd-negative"></span>
                        </span>
                    </span>
                </label>
            </td>
            <td>
                <button class="btn btn-danger" onclick="DeleteStore(this)">
                    <i class="fa fa-trash-o" aria-hidden="true"></i>
                </button>
                <button class="btn btn-primary" onclick="PopModel(this)">
                    <i class="fa fa-pencil" aria-hidden="true"></i>
                </button>
            </td>
        </tr></tbody>
</table>

并试图获取第二行("Store Number" 4 和 "Store Name" 'b' 的行),使用以下 Groovy 代码:

'get the initial number of data rows'
String dataRowsSelector = '//table[contains(@id, "data-table")]/tbody/tr'

List<WebElement> dataRows = driver.findElements(By.xpath(dataRowsSelector))

int initialRowCount = dataRows.size()

assertNotEquals(index, 0)

'find the row pointed to by the index'
String rowSelector = String.format('//table[contains(@id, "data-table")]/tbody/tr[%d]', (index + 1))

assertEquals(rowSelector, '//table[contains(@id, "data-table")]/tbody/tr[2]')

WebElement rowToDelete = driver.findElement(By.xpath(rowSelector))

'get the delete button'
String deleteButtonSelector = '//button[./i[contains(concat(" ", @class, " "), " fa-trash-o ")]]'

WebElement deleteButton = rowToDelete.findElement(By.xpath(deleteButtonSelector))

println(rowToDelete.findElement(By.xpath('//td[1]')).getAttribute('innerText'))

assertTrue(rowToDelete.findElement(By.xpath('//td[1]')).getAttribute('innerText').contains('b'))

我运行那个代码,rowToDelete变成了第一行数据而不是第二行(我用3作为索引再次尝试,它仍然是第一行),即使针对目标 xpath 的断言有效。此外,WebElement 的第一个 <td> 包含 a 而不是 b。 (当我尝试使用 3 作为索引时,我再次得到 a,即使我期待 c

如何解决这种奇怪的行为?

问题显然出在我的 XPath 本身。当尝试 select WebElements 相对于其他 WebElements 时,使用 XPath,我应该在 // 前面放一个点来给它范围。