如何验证我是否从赛普拉斯的 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()
中使用 tid
s 而不是 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')
到达正确的位置。
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 代码。
运行 只是那个规格并调整命令直到它们工作。
我正在尝试删除特定用户,如果我想要的用户不存在,我宁愿测试失败然后删除另一个用户。
这个问题,我似乎无法找到一种方法来验证复选框是否在我想要的用户旁边。我一直在使用 .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()
中使用 tid
s 而不是 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')
到达正确的位置。
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 代码。运行 只是那个规格并调整命令直到它们工作。