如何使用 Cypress 检查可能不存在的元素

How to check for an element that may not exist using Cypress

提问人:Charles Anderson 提问时间:12/12/2017 最后编辑:Mads BruggerCharles Anderson 更新时间:7/16/2023 访问量:77863

问:

我正在编写一个赛普拉斯测试来登录一个网站。有 和 字段和一个按钮。大多数情况下,登录很简单,但有时首先会出现一个警告对话框,必须将其关闭。usernamepasswordSubmit

我试过这个:

cy.get('#login-username').type('username');
cy.get('#login-password').type(`password{enter}`);

// Check for a possible warning dialog and dismiss it
if (cy.get('.warning')) {
  cy.get('#warn-dialog-submit').click();
}

这很好用,但如果警告未出现,测试将失败:

CypressError:重试超时:应找到元素:“.warning”,但从未找到它。

然后我尝试了这个,但失败了,因为警告出现得不够快,所以没有找到任何东西:Cypress.$

cy.get('#login-username').type('username');
cy.get('#login-password').type(`password{enter}`);

// Check for a possible warning dialog and dismiss it
if (Cypress.$('.warning').length > 0) {
  cy.get('#warn-dialog-submit').click();
}

检查元素是否存在的正确方法是什么?我需要这样的东西,如果找不到元素,就不会抱怨。cy.get()

自动化测试 Cypress Cypress -条件测试

评论

0赞 Jennifer Shehane 12/13/2017
尝试对 DOM 元素进行条件测试有各种缺点,在 Cypress 中也有各种解决方法。所有这些在条件测试的 Cypress 文档中都有深入的解释。由于有多种方法可以完成您正在尝试执行的操作,因此我建议您通读整个文档,并确定最适合您的应用程序和测试需求的方法。
0赞 TylerH 6/1/2023
对于另一个人来说,这是一个更好的规范欺骗目标,反之亦然。cc @user16695029 你到底为什么要把它作为那个的副本而不是相反?
4赞 user16695029 6/1/2023
我认为 Fody 关于轮询元素的答案非常好,比这里的任何答案都要好得多。(有条件+重试+提前超时)

答:

35赞 Ran Adler 1/6/2020 #1
    export function clickIfExist(element) {
        cy.get('body').then((body) => {
            if (body.find(element).length > 0) {
                cy.get(element).click();
            }
        });
    }

评论

6赞 dewey 2/25/2022
有没有办法设置查找元素的超时时间?
2赞 M. Justin 5/21/2022
@dewey 该方法是 JQuery 方法,而不是 Cypress 方法,因此它没有任何内置超时。这是一个关于使用 JQuery 或纯 Javascript 等待元素存在的问题:stackoverflow.com/questions/5525071/...。我想你可以做这样的事情:findif (body.find(element).length == 0) {cy.wait(timeout);} if (body.find(element).length > 0) {cy.get(element).click();}
0赞 Patryk Janik 3/20/2020 #2

我已经用纯js完成了。

cy.get('body').then((jqBodyWrapper) => {

               if (jqBodyWrapper[0].querySelector('.pager-last a')) {
                   cy.get('.pager-last a').then(jqWrapper => {
                       // hardcoded due to similarities found on page

                       const splitLink = jqWrapper[0].href.split("2C");
                       AMOUNT_OF_PAGES_TO_BE_RETRIEVED = Number(splitLink[splitLink.length - 1]) + 1;
                   })
               } else {
                   AMOUNT_OF_PAGES_TO_BE_RETRIEVED = 1;
               }
           });

我正在尝试检查身体上是否存在元素

cy.get('body').then((jqBodyWrapper) => {

使用纯 js querySelector

if (jqBodyWrapper[0].querySelector('.pager-last a')) {

然后我解雇了我的cy.get

cy.get('.pager-last a').then(jqWrapper => {
1赞 ogulcan 5/23/2020 #3

or for CSS 选择器是 jQuery 中的一个内置方法,用于检查具有指定类名的元素是否存在。然后,您可以返回一个布尔值来执行断言控制。hasClass()has()

Cypress.Commands.add('isExistElement', selector => {
  cy.get('body').then(($el) => {
    if ($el.has(selector)) {
      return true
    } else {
      return false
    }
  })
});

然后,可以用TypeScript文件(index.d.ts)文件将其制作成特殊的cypress方法,并且可以采用可链接的形式。

declare namespace Cypress {
    interface Chainable {
        isExistElement(cssSelector: string): Cypress.Chainable<boolean>
    }
}

如下例所示:

shouldSeeCreateTicketTab() {  
  cy.isExistElement(homePageSelector.createTicketTab).should("be.true");
}

评论

0赞 Anand 12/9/2022
如果找不到元素,这不会引发错误吗?
4赞 mykola kozub 10/26/2020 #4
export const getEl = name => cy.get(`[data-cy="${name}"]`)

export const checkIfElementPresent = (visibleEl, text) => {
   cy.document().then((doc) => {
     if(doc.querySelectorAll(`[data-cy=${visibleEl}]`).length){
      getEl(visibleEl).should('have.text', text)

      return ;
   }
getEl(visibleEl).should('not.exist')})}

评论

3赞 Sitzfleisch 6/3/2023
Cypress.$()执行与较少行相同的操作。doc.querySelectorAll()
40赞 Fody 6/1/2023 #5

使用元素轮询和检查,而不会使测试失败。

在最长等待时间内,对话框永远不会到达,或者此代码将其关闭。

cy.get('#login-username').type('username');
cy.get('#login-password').type(`password{enter}`);

const ifElementExists = (selector, attempt = 0) => {
  if (attempt === 100) return null           // no appearance, return null
  if (Cypress.$(selector).length === 0) {
    cy.wait(100, {log:false})                // wait in small chunks
    getDialog(selector, ++attempt)           // try again
  }
  return cy.get(selector, {log:false})       // done, exit with the element
}

ifElementExists('.warning').then($el => {
  if ($el?.length) {
    $el.find('#warn-dialog-submit').click()
  }
})
-1赞 Lorraine R. 7/16/2023 #6

请注意,您可能需要等待足够的时间,直到元素添加到 DOM 中,并且应该异步执行此操作:

describe(`App content suite`, () => {
    it('app logs in correctly', () => {         
        console.log('Checking if warning needs to be closed');

        const enoughTimeUntilWarningIsAddedToDom = 5000;
        cy.wait(enoughTimeUntilWarningIsAddedToDom);

        (async () => {
            const elementExists = await clickIfElementExists('.warning');

            if (elementExists) {
                console.log('element exists');
            } else {
                console.log('element does not exist');
            }
        })();
    }
}

function clickIfElementExists(elementSelector: string) {
    return new Promise(resolve => {
        cy.get('body').then((body) => {
            resolve(body.find(elementSelector).length > 0);
        });
    })
}

评论

1赞 Giacomo 7/16/2023
如果它没有出现怎么办 - 在这种情况下如何使用 Promise.reject()?
0赞 Lorraine R. 7/16/2023
问题是如何检测元素是否存在,而不会使测试失败,也没有例外。所以,你不应该拒绝。如果元素不存在,则使用值 false 进行解析,并且 false 将返回给 awaiter,您可以在其中检查clickIfElementExistsresolve(false)if (!await clickIfElementExists('element'))
0赞 Lorraine R. 7/16/2023
我稍微澄清了这个例子@Giacomo
3赞 R.Winkles 7/18/2023
虽然这在技术上可能有效,但您可以摆脱函数、承诺、异步 iify 并仅使用cy.wait(5000).then(() => { const exists = Cypress.$(elementSelector)...
4赞 R.Winkles 7/18/2023
不要这样做 - 赛普拉斯的发明是为了摆脱测试中的艰难等待时间。您将设置 5 秒,然后发现它偶尔会失败,因此您增加到 10 秒,然后您必须在下一次测试中执行相同的操作,不久之后您就会在套件运行时间中增加几分钟。
0赞 Satrio Ponco 11/24/2023 #7

据我们所知,柏树使用 promise 来处理测试。因此,如果没有特定元素,我们可以使用 promiseFail 返回。我在这里对@Fody答案进行了一些修改。

const ifElementExists = (selector, attempt = 0) => {
  const promiseFail = new Promise((resolve) => {
    resolve([])
  })
  if (attempt === 10) return promiseFail;
  if (Cypress.$(selector).length === 0) {
    cy.wait(100, { log: false });
    return ifElementExists(selector, ++attempt);
  }
  return cy.get(selector, { log: false }); // done, exit with the element
};

ifElementExists(".todo-list").then($el => {
  if ($el?.length) {
    cy.get(".todo-list li").should("have.length", 2);
  } else {
    cy.log("NOT FOUND 404")
  }
});

评论

0赞 Billy.Burr 11/25/2023
为什么要在不需要的时候添加承诺?