Search code examples
javascripttypescriptautomationcucumbercypress

"CypressError: `cy.should()` failed..." was invoked from then() block


I have a block of code where I check a response after an async request.

graphql.ts snippet:

19    return cy.request({...}).then((response) => {
20         expect(response.status, 'Invalid response status').to.equal(200)
21          return cy.wrap(response.body.data)
22      })

My test fails with the error:

CypressError: `cy.should()` failed because you invoked a command inside the callback. `cy.should()` retries the inner function, which would result in commands being added to the queue multiple times. Use `cy.then()` instead of `cy.should()`, or move any commands outside the callback function. 

Actually, should() was not used. But stack trace shows me cy.get() invoke from expect(response.status, 'Invalid response status').to.equal(200) row inside then() block.

> `cy.get()` 
  at $Cypress.commandEnqueued (http://172.18.0.20/__cypress/runner/cypress_runner.js:135214:76) 
  at $Cypress.listener (http://172.18.0.20/__cypress/runner/cypress_runner.js:86503:17) 
  ...
From Your Spec Code:
  at Context.eval (infra/api/graphql.ts:20:10) 

Could someone explain this behavior?

UPD: My call stack looks like:

Then('updated pipeline is available via API', function (table) {
    cy
        .getUserInfo()
        .then(usr => {
            retryWithTimeout(retry => {
                requestGraphQL(pipelineQuery, usr.api_key, vars)
                    .should(res => {
                        cy.get(`@${Alias}`).then(alias => {
                            if (somecondition) retry('Msg')
                            ....
                        })
                    })
            }, { timeout: 120, delay: 20 })
        })
})

const requestGraphQL = (body: string, authKey: string, vars: any = {}, url= graphqlUrlPath): Chainable => cyRequest(body, authKey, vars, url)

const cyRequest = (queryStr: string, authKey: string, vars: any, url: string): Chainable<any> => {
  return cy.request({...}).then((response) => {
         expect(response.status, 'Invalid response status').to.equal(200)
          return cy.wrap(response.body.data)
      })
}

Solution

  • The .should(callback) command has built-in retry. If the callback function throws, it will repeat up to the timeout set.

    That means the cy.get('@${Alias}')... could be added to the queue hundreds of times, which is obviously not what you intend.

    Cypress has some built-in checking which looks for command blowouts like this, but it does not know what exactly you are doing inside the .should(), only that the command queue has been modified.

    It's hard to tell what your test is aiming to do, but the short answer to fix this is to access the alias before calling .should().

    Something like this:

    cy.get(`@${Alias}`).then(alias => {
      cy.getUserInfo().then(usr => {
        retryWithTimeout(retry => { 
          requestGraphQL(pipelineQuery, usr.api_key, vars)
            .should(res => {  
              if (somecondition) retry('Msg')
    

    The details of your special functions would be required to give you a full working sample.