You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Capybara选取表格指定行并校验tooltip与Excel数据

问题背景

我之前发布过几个相关提问,后续会将其删除,在本提问中我会更清晰地说明问题。首先我目前正在努力学习技术,此前靠家政保洁维持生计,所以如果我学习存在一定难度、进度未达理想速度,还请谅解。

核心需求

逐行捕获页面表格内容,和Excel电子表格中的信息做校验。目前初步思路是捕获tooltip文本,和工作表存储的对应文本做比对,已经有朋友给了能列出所有tooltip文本的示例代码,但不知道怎么定位指定行号,没法实现逐行匹配校验。

现有代码

  • 朋友提供的获取所有tooltip的示例代码:
page.all('tr.tvRow .tvCell:nth-child(6) img[tooltip]').map { |el| el['tooltip'] }
  • 目前写的零散校验代码,只能校验页面存在对应文本,没法绑定到指定行:
expect(page).to have_css('tr.tvRow .tvCell:nth-child(6) img[tooltip]', exact_text: "Invisível")
expect(page).to have_css('tr.tvRow .tvCell:nth-child(6) img[tooltip]', exact_text: "Facultativo")
expect(page).to have_css('tr.tvRow .tvCell:nth-child(6) img[tooltip]', exact_text: "Obrigatório")

页面DOM结构参考

<td valign="top" colspan="1" rowspan="1">
   <div id="AFRAME_790" onmousedown="event.preventMoving=true;" style="display: inline-block; box-sizing: border-box; min-width: 100%;" class="tv">
      <a name="TVXWFMTGINSTATOCE"></a>
      <div class="tvCaption">
         <div style="display:inline-block;width:100%">
            <img src="r/std/static/pixel.gif" class="icon Enabled icon_grid" draggable="false" onclick="sendEvent(0,event,this,true,0,'','790','ControlMenu#','','','','','');">
            <img src="r/std/static/pixel.gif" class="icon Enabled icon_norows" draggable="false" onclick="sendEvent(0,event,this,searchImage(this),0,'','790','SelectNone#','','','','','');" alt="Selecionar Nenhum" tooltip="Selecionar Nenhum">
            <img src="r/std/static/pixel.gif" class="icon Enabled icon_allrows" draggable="false" onclick="sendEvent(0,event,this,searchImage(this),0,'','790','SelectAll#','','','','','');" alt="Selecionar todos" tooltip="Selecionar todos">&nbsp;Lista campos
            <div onclick="if(notSelecting()) sendEvent(0,event,this,searchImage(this),0,'','790','TMMenu#','','','','','');" style="white-space: nowrap;" class="tvMsg tvMsgEna"><img src="r/std/icons/menu64.png" class="icon" draggable="false"></div>
            <div onclick="if(notSelecting()) sendEvent(0,event,this,searchImage(this),0,'','790','CambioLabel#','','','','','');" class="tvMsg tvMsgEna" style="white-space: nowrap;" tooltip="Editar Label">
               <img src="r/std/static/label16.gif" class="icon" draggable="false">
               <div class="tvCaptionSep"></div>
            </div>
         </div>
      </div>
      <div>
         <div style="" class="tvContent">
            <div style="display:table;width:100%; background:inherit;">
               <div style="display:table-cell;">
                  <table width="100%" class="tvGrid">
                     <tbody>
                        <tr>
                           <th colspan="1" class="tvHeader">Nome</th>
                           <th colspan="1" class="tvHeader">Etiqueta</th>
                           <th colspan="1" class="tvHeader">Tipo Dado</th>
                           <th colspan="1" class="tvHeader">Valores </th>
                           <th colspan="1" width="1" class="tvHeader">Preenchimento automático</th>
                           <th colspan="1" class="tvHeader">Status </th>
                           <th colspan="1"></th>
                        </tr>
                        <tr oncontextmenu="if(!this.cancelEvent) {sendEvent(0,event,this,true,0,'TVXWFMTGINSTATOCE','790','BtnDropDownRow#',0,'','','');return false;} delete this.cancelEvent;" onclick="if(notSelecting()) sendEvent(0,event,this,true,0,'TVXWFMTGINSTATOCE','790','Select#',0,'','','');" onmousedown="if(event.ctrlKey) this.cancelEvent=true;" class="tvRow tvRowOdd tvRoll" style="cursor:pointer;">
                           <td valign="center" align="left" class="tvCell">FLAG_INSERIMENTO_LETTURA_E</td>
                           <td valign="center" align="left" class="tvCell"> Leitura Existente</td>
                           <td align="left" class="tvCell" valign="center" nowrap=""></td>
                           <td valign="center" align="left" class="tvCell"></td>
                           <td align="left" width="1" class="tvCell" valign="center" tooltip=""></td>
                           <td valign="center" align="center" class="tvCell"><img tooltip="Invisível" src="r/std/static/minus16.gif"></td>
                           <td align="center" width="30" style="line-height:1px;padding:0px;" class="tvCell" valign="center">
                              <div onclick="event.cancelBubble=true;sendEvent(0,event,this,searchImage(this),0,'','790','BtnDropDownRow#',0,'','','','');" class="but">
                                 <div><img src="r/std/icons/menu64.png" class="icon" draggable="false" align="absmiddle"></div>
                              </div>
                           </td>
                        </tr>
                        <tr oncontextmenu="if(!this.cancelEvent) {sendEvent(0,event,this,true,0,'TVXWFMTGINSTATOCE','790','BtnDropDownRow#',1,'','','');return false;} delete this.cancelEvent;" onclick="if(notSelecting()) sendEvent(0,event,this,true,0,'TVXWFMTGINSTATOCE','790','Select#',1,'','','');" onmousedown="if(event.ctrlKey) this.cancelEvent=true;" class="tvRow tvRowEven tvRoll" style="cursor:pointer;">
                           <td valign="center" align="left" class="tvCell">XWFMSLETMATRICOLA_E</td>
                           <td valign="center" align="left" class="tvCell"> Nº Hidrômetro</td>
                           <td align="left" class="tvCell" valign="center" nowrap=""></td>
                           <td valign="center" align="left" class="tvCell"></td>
                           <td align="left" width="1" class="tvCell" valign="center" tooltip="\[AIMPMATRICOLA\]">
                              <button onclick="event.cancelBubble=true;sendEvent(0,event,this,searchImage(this),0,'','790','AUTOFILLBTN#',1,'','','','');" class="but butAct" tabindex="0" tooltip="Preenchimento automático ativado" type="BUTTON">
                                 <div><img src="r/std/icons/checkboxselected64.png" class="icon" draggable="false" align="absmiddle"></div>
                              </button>
                           </td>
                           <td valign="center" align="center" class="tvCell"><img tooltip="Invisível" src="r/std/static/minus16.gif"></td>
                           <td align="center" width="30" style="line-height:1px;padding:0px;" class="tvCell" valign="center">
                              <div onclick="event.cancelBubble=true;sendEvent(0,event,this,searchImage(this),0,'','790','BtnDropDownRow#',1,'','','','');" class="but">
                                 <div><img src="r/std/icons/menu64.png" class="icon" draggable="false" align="absmiddle"></div>
                              </div>
                           </td>
                        </tr>
                     </tbody>
                  </table>
               </div>
            </div>
         </div>
      </div>
   </div>
</td>

解决方案

现有代码的问题是选择器没有限定行范围,所以只要页面任意位置存在匹配的tooltip就会通过校验,没法和行号绑定。Capybara支持通过CSS的nth-of-type选择器直接定位指定序号的表格行,也支持先拿到所有行的集合再按索引取值,两种方式都可以实现逐行校验。

方法1:直接通过行号定位元素校验

CSS选择器里用tr.tvRow:nth-of-type(行序号)就能选中对应的数据行,注意这里的序号从1开始计数,对应表格里的第一行、第二行数据。
比如校验第1行(序号1)的Status列tooltip:

# 校验第1行的tooltip值为"Invisível"
expect(page).to have_css('tr.tvRow:nth-of-type(1) .tvCell:nth-child(6) img[tooltip="Invisível"]')
# 校验第2行的tooltip值为"Invisível"
expect(page).to have_css('tr.tvRow:nth-of-type(2) .tvCell:nth-child(6) img[tooltip="Invisível"]')

可以把Excel里读出来的行号和对应预期值做循环,自动生成校验逻辑,不用手动写每一行的断言。

方法2:先获取所有行集合,再逐行读取校验

如果需要批量处理所有行,和Excel数据按顺序一一比对,可以先拿到所有数据行的集合,再遍历每一行提取tooltip值,和Excel对应行的预期值直接断言,这种方式更适合批量校验场景:

# 先拿到所有数据行的元素集合
all_rows = page.all('tr.tvRow')
# 假设excel_data是你从Excel读出来的数组,每个元素是对应行预期的Status文本
excel_data.each_with_index do |expected_status, index|
  # 从当前行里找第6列的img元素,取tooltip属性
  current_row_tooltip = all_rows[index].find('.tvCell:nth-child(6) img[tooltip]')['tooltip']
  expect(current_row_tooltip).to eq(expected_status)
end

补充说明

如果表格存在分页、动态加载的情况,执行page.all之前记得加等待逻辑,确保所有行都加载完成再做校验,避免拿到的行数量不全导致断言失败。如果不想用行序号匹配,也可以用第一列的字段名(比如FLAG_INSERIMENTO_LETTURA_E)作为定位锚点找对应行,就算行顺序变了也能正确匹配:

# 找第一列文本为指定字段名的行,再校验它的tooltip
target_row = page.find('tr.tvRow', text: 'FLAG_INSERIMENTO_LETTURA_E')
expect(target_row).to have_css('.tvCell:nth-child(6) img[tooltip="Invisível"]')

内容的提问来源于stack exchange,提问作者Elis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 00:06:14