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

Playwright C#如何精确匹配表格行并点击对应删除按钮

问题解决:Playwright C# 表格行精确匹配删除按钮

场景说明

  • 使用C#编写Playwright自动化脚本
  • 操作一个多列多行的表格
  • 每行有唯一名称,同一行包含多个操作按钮
  • 需求:定位到指定名称行的删除按钮并点击

当前代码

public static async Task ClickScenarioDeleteButton(IPage Page, string scenarioName)
{
    //var deleteButton = Page.Locator($"div:has-text('{scenarioName}') >> data-testid=generic-remove-button")
    //    .First;
    //await deleteButton.ClickAsync();

    await Page
        .GetByRole(AriaRole.Row)
        .Filter(new() { HasText = scenarioName })
        .GetByTestId("generic-remove-button")
        .ClickAsync();
}

问题现象

当表格中只有一行名称为PWATest_20240430_100831时,代码可以正常运行;但新增一行名称为No2SchPWATest_20240430_100831(原名称前缀加了"No2Sch")后,出现严格模式冲突错误:

Message: 
  Microsoft.Playwright.PlaywrightException : Error: strict mode violation: GetByRole(AriaRole.Row).Filter(new() { HasText = "PWATest_20240430_100831" }).GetByTestId("generic-remove-button") resolved to 2 elements:
      1) <button type="button" data-testid="generic-remove-button…>…</button> aka GetByRole(AriaRole.Row, new() { Name = "CustomerName PWATest_20240430_100831 24 Hours None" }).GetByTestId("generic-remove-button")
      2) <button type="button" data-testid="generic-remove-button…>…</button> aka GetByRole(AriaRole.Row, new() { Name = "CustomerName No2SchPWATest_20240430_100831 24 Hours None" }).GetByTestId("generic-remove-button")

注:所有行的"CustomerName"字段值相同

困境

C#版本的Playwright Filter方法不支持Exact = true参数,无法直接通过Filter实现精确文本匹配,试了多种方法都没解决,需要可行方案。

参考HTML片段

<td data-label="Valid" class="mud-table-cell"><div class="scenario-validity" b-ho1i1ctrb5=""><!--!--><div class="icon-component" b-ehxpcnsxe0=""><!--!--><div class="mud-tooltip-root mud-tooltip-inline"><!--!--><svg class="mud-icon-root mud-svg-icon mud-success-text mud-icon-size-small" focusable="false" viewBox="0 0 24 24" aria-hidden="true"><!--!--><path d="M0 0h24v24H0z" fill="none"></path><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"></path></svg><!--!--><div id="popover-1f0d4cb7-5b9a-4b7a-80c5-54be38c1bcf0" class="mud-popover-cascading-value"></div></div></div></div></td>

解决方案

方法1:使用Locator结合精确文本选择器

直接用Locator配合:text-is()伪类实现精确匹配,绕过Filter的参数限制:

public static async Task ClickScenarioDeleteButton(IPage Page, string scenarioName)
{
    await Page.Locator($"tr:has(:text-is('{scenarioName}')) >> [data-testid='generic-remove-button']")
              .ClickAsync();
}

:text-is()会严格匹配完整文本内容,不会命中包含目标文本的其他元素,完美解决前缀包含的问题。

方法2:定位特定单元格再找父行

如果名称所在单元格有专属属性(比如data-label),可以先精准定位该单元格,再向上找父行,最后定位删除按钮:

public static async Task ClickScenarioDeleteButton(IPage Page, string scenarioName)
{
    await Page.Locator($"td[data-label='ScenarioName']:text-is('{scenarioName}')")
              .Locator("..") // 定位到父级tr元素
              .GetByTestId("generic-remove-button")
              .ClickAsync();
}

注意替换data-label='ScenarioName'为实际名称所在单元格的属性,确保只匹配目标单元格的精确文本。

方法3:Filter结合自定义匹配逻辑

利用Filter的Lambda表达式参数,手动编写精确匹配逻辑:

public static async Task ClickScenarioDeleteButton(IPage Page, string scenarioName)
{
    await Page.GetByRole(AriaRole.Row)
              .Filter(async row => 
              {
                  var text = await row.TextContentAsync();
                  // 用前后空格避免部分匹配,也可根据实际文本结构调整
                  return text?.Contains($" {scenarioName} ") ?? false;
                  // 若需要更严谨的匹配,可用正则表达式
                  // return System.Text.RegularExpressions.Regex.IsMatch(text ?? "", $@"\b{System.Text.RegularExpressions.Regex.Escape(scenarioName)}\b");
              })
              .GetByTestId("generic-remove-button")
              .ClickAsync();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 00:27:11