如何验证我是否从赛普拉斯的 table 中选择了正确的用户 (html-table / angular-material)

How do I verify I've selected the correct user from a table in Cypress (html-table / angular-material)

我正在尝试删除特定用户,如果我想要的用户不存在,我宁愿测试失败然后删除另一个用户。

这个问题,我似乎无法找到一种方法来验证复选框是否在我想要的用户旁边。我一直在使用 .each() 嵌套来首先找到用户 Mandy Smith,然后 select 具有相同索引的复选框。

HTML:

注意,我正在尝试删除用户“Mandy Smith”,虽然我可以使用以下代码 select 她,但取决于测试到达这一点的速度,它可能会实际删除第一个用户。

删除 Mandy Smith 的当前代码:

         // looks for the checkbox related to Mandy Smith User
          cy.get('input[type="checkbox"]')
            .each(($elem, index) => {
                if(index === 2) {
                  cy.wrap($elem).click({force:true});
                }
            });

虽然这通常会抓住她,但取决于清除过滤器的速度,它实际上可以选择要删除的自动用户。我当然不想要这个,我正在尝试找到一种好方法来验证 Mandy Smith 所在的索引并将其应用于她的复选框。

复选框是动态的,所以它会根据点击的内容迭代,所以我不想选择带有标签“#mat-checkbox-11”的确切复选框,因为它恰好是这样的当时我正在查看代码,但它早于 5。

我认为这会让我找到索引并将其应用于复选框:

// looks for the checkbox related to Mandy Smith User
    it(`should select Mandy, through verification that it's her index`, () => {
        cy.get('.mat-column-name.ng-star-inserted')
          .each(($name, i) => {
            if($name === 'Mandy Smith') {
                let dex = i;
                cy.get('input[type="checkbox"]')
                  .each(($elem, index) => {
                    if(index === dex) {
                        cy.wrap($elem).click({force:true});
                    }
                });
            }
        });
    });

这实际上最终只是删除了自动用户。

我做了 运行 一些测试,发现它返回的字符串是“Automated User Mandy Smith”。 所以,即使我正在遍历table,它似乎拉两个文本字段。当所有 class 名称都相同时,我是否缺少有关如何获取一组文本的内容?

这意味着它会删除第一个用户,因为 if 语句将显示 Mandy Smith,然后选择第一个用户(自动用户)而不是第二个用户 Mandy Smith。也许我需要知道如何从每个 class 中获取具有相同名称的文本。

我不知道,我现在有点不知道如何做到这一点。

编辑:我发现我可以在 if 语句中调用文本,但它永远不会进入 if 语句。我检查了它产生的结果并将其粘贴到我的 if 中,但它仍然不起作用。下面是获取元素 innerText 的代码,它用于与单词进行比较。它不会评估为真有什么原因吗?

    it(`should select Mandy, through verification that it's her`, () => {
        cy.get('.mat-cell.cdk-cell.text-capitalize.cdk-column-name.mat-column-name')
          .each(($name, i) => {
            //let columnText = ($name).invoke('text');
            if(cy.get($name).invoke('text') === ' Mandy Smith ') {
                cy.log('it made it inside the if');
            }
        });
    });

编辑 7/15/2020:这是整个 table 的图像。黄色箭头是她的行,正上方是用户 1 的行。绿色箭头是她的复选框,红色箭头是她在名字栏中的名字

感谢大家的帖子。我今天要搞砸了,测试一下各种评论,看看能不能找到解决办法。请随时提供反馈,因为我会全天查看。

已解决谢谢大家,这里是答案,我和一位高级开发人员一起 phone 看到我在做什么后他 运行 我通过一些事情。感谢@oldschooled 的出色回答,以及后续的 @eric99 也为我提供了更多的分解。你们两个都是摇滚明星。

回答

    it(`should select Mandy Smith with contains only`, () => {
        cy.contains('td', 'Mandy Smith').siblings().eq(0).children().eq(0).click();
    });

也许我可以建议另一种方法,比如使用 cypress xpath。首先安装这个: https://www.npmjs.com/package/cypress-xpath

然后您可以 select 使用 xpath 的复选框,如果它包含文本 Mandy Smith 等。

[编辑:查看下面@eric99 的附加答案以获得很好的解释。]


cy.get('.mat-column-name.ng-star-inserted').contains('mandy smith')

如果名称是仅匹配一个元素的那些 .mat-column-name 元素中的唯一标识符,return 应该是您要查找的基本元素。

从那里开始,您应该能够自信地做任何您想做的事,因为您拥有 Mandy Smith,例如导航到 child/sibling/parent 元素并找到可点击的元素(例如复选框),进行其他断言等。 这是假设输入字段与名称列有某种关系,即两个元素存在于同一行或 parent 元素中。

如果两个元素(name-column和复选框输入)在同一行,则不需要循环遍历get()编辑的每个元素return,直接跳转即可到包含您想要的内容的那个,然后导航到位于同一行元素中的同级 input 元素。

例如如果您的 html 结构类似于:

<tr tid='the-rowest-of-rows'>
  <td tid='the-checkist-of-checkboxes'><input tid='bam' ...></td>
  <td tid='name-column'>The mandiest Mandy Smith</td>
</tr>   

那么您的选择器将类似于。

cy.get('[tid=name-column]').contains('Mandy Smith')
  .siblings('[tid=the-checkist-of-checkboxes]')
  .find('[tid=bam]')
  .click()

(注意:我在 get() 中使用 tids 而不是 class 选择器)

因为我不确定你的组件是如何设置的(我们需要这个例子周围的原始 html,包括 input,你的例子中没有) ,我猜它看起来像那样。

根据您的 html 布局,关系可能会有所不同,您可能需要兄弟姐妹,parent 或其中一些 variant/mixture。然而,这一切的重点是:

tl;dr: 使用 get().contains() 检索 Mandy Smith 元素。根据需要从那里导航到 child/sibling/parent 个元素。

进一步阅读:

作为旁注,为了进一步阅读,我建议您查看:

特别注意使用 tid 来选择元素。通常,按 class 选择不是一个好主意。但更重要的是,tid 可以帮助您解决这个问题。根据您的前端框架,您可以直接将 tid 动态添加到您要单击的元素,该元素将与您正在搜索的名称相关联(即 <input tid='input-${name.last}'>),这可能会直接将您带到点击金钱

此外,如果您有兴趣了解有关 if 语句为何未按预期工作的更多信息,以下内容非常棒:

我希望这对你有意义。抱歉写了一本书。如果它没有意义或您有任何疑问,我很乐意尝试回答他们。

@OldSchool 的解释是正确的,但是你没听懂。

在您显示的评论中

cy.get('.mat-cell.cdk-cell.text-capitalize.cdk-column-name.mat-column-name')
  .invoke('text')                // REMOVE THIS COMMAND
  .contains('Mandy Smith');

问题是 invoke('text') 在选择多个元素的 get() 之后 连接所有这些元素的所有文本(即名称列中的所有单元格)。

相比之下,.get().contains('Mandy')选择包含'Mandy'的单个单元格,然后使用sibling()将下一部分(找到复选框)限制为仅table行.

如果您在页面上只有一个 table,我会使用以下内容,因为它是最短的,并且使用 Cypress 选择器功能以达到最大效果。

灵魂

cy.contains('td', 'Mandy Smith')
  .siblings('td')   // get all sibling cells in the 'Mandy Smith' row
  .find('input')    // find the input within these cells
  .click();

注意,如果table数据是异步获取的,cy.contains(selector, content)优于cy.get(selector).contains(content),因为第一个会重试直到内容到达.


我在 Angular Material Grid example page 上测试后修改了上面的 Soultion 代码,使用示例 'Table with selection'(搜索 'Helium'在第二行)。

原来 .siblings(selector) 中使用的选择器无法处理与 .get(selector) 相同的复杂性,例如

.get('td input')      // succeeds and finds all input cells

.siblings('td input') // fails to find the input, although it does exist

因此,使用 .siblings('td').find('input') 到达正确的位置。

来自.find() docs

Get the descendent DOM elements of a specific selector

.find() 将搜索限制在先前的主题内,它们是同级单元格。


这适用于 Angular Material 网格示例页面。如果还是不行,请post你前两行的实际html为文字而不是图片,我会调查。


如果页面上有多个网格,您将需要增强选择器,以便最初将其缩小到所需的网格。


探索性测试

Cypress 有一个巧妙的功能,允许您在 DOM.

的片段上试验不同的命令
  • 将您正在处理的 HTML 的片段复制到一个新的 .html 文件中,将该文件放在 /cypress 文件夹下,例如 /cypress/myFragment.html.

  • 片段html文件的内容不需要是完整的HTML文档,可以省略header和body标签。在这种情况下,您需要一个 <table> 标签,一个 <tbody> 标签,并粘贴在前两行之间,这对于这个实验来说已经足够了。 (不要忘记关闭 </table></tbody>)。

  • 编写访问片段 HTML 的实验规范,例如 cy.visit('cypress/myFragment.html')。赛普拉斯将以与完整 HTML 页面相同的方式加载它。

  • cy.visit() 后面输入您想试验的命令,例如上面的 Solution 代码。

  • 运行 只是那个规格并调整命令直到它们工作。