Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cypress returning Synchronous value within Async command?

So I think this is probably me mixing up sync/async code (Mainly because Cypress has told me so) but I have a function within a page object within Cypress that is searching for customer data. I need to use this data later on in my test case to confirm the values.

Here is my function:

searchCustomer(searchText: string) {
  this.customerInput.type(searchText)
  this.searchButton.click()
  cy.wait('@{AliasedCustomerRequest}').then(intercept => {
    const data = intercept.response.body.data
    console.log('Response Data: \n')
    console.log(data)
    if (data.length > 0) {
      {Click some drop downdowns }
      return data < ----I think here is the problem
    } else {
      {Do other stuff }
    }
  })
}

and in my test case itself:

let customerData = searchAndSelectCustomerIfExist('Joe Schmoe')
//Do some stuff with customerData (Probably fill in some form fields and confirm values)

So You can see what I am trying to do, if we search and find a customer I need to store that data for my test case (so I can then run some cy.validate commands and check if the values exist/etc....)

Cypress basically told me I was wrong via the error message:

cy.then() failed because you are mixing up async and sync code.

In your callback function you invoked 1 or more cy commands but then returned a synchronous value.

Cypress commands are asynchronous and it doesn't make sense to queue cy commands and yet return a synchronous value.

You likely forgot to properly chain the cy commands using another cy.then().

So obviously I am mixing up async/sync code. But since the return was within the .then() I was thinking this would work. But I assume in my test case that doesn't work since the commands run synchronously I assume?

like image 210
msmith1114 Avatar asked Jun 28 '26 08:06

msmith1114


2 Answers

Since you have Cypress commands inside the function, you need to return the chain and use .then() on the returned value.

Also you need to return something from the else branch that's not going to break the code that uses the method, e.g an empty array.

searchCustomer(searchText: string): Chainable<any[]> {

  this.customerInput.type(searchText)
  this.searchButton.click()

  return cy.wait('@{AliasedCustomerRequest}').then(intercept => {

    const data = intercept.response.body.data
    console.log('Response Data: \n')
    console.log(data)
    if (data.length) {
      {Click some drop downdowns }
      return data                              
    } else {
      {Do other stuff }
      return []
    }

  })
}

// using 
searchCustomer('my-customer').then((data: any[]) => {
  if (data.length) {

  }
})

Finally "Click some drop downdowns" is asynchronous code, and you may get headaches calling that inside the search.

It would be better to do those actions after the result is passed back. That also makes your code cleaner (easier to understand) since searchCustomer() does only that, has no side effects.

like image 161
Fody Avatar answered Jun 29 '26 23:06

Fody


you just need to add return before the cy.wait

here's a bare-bones example

it("test", () => {
  function searchCustomer() {
    return cy.wait(100).then(intercept => {
      const data = {text: "my data"}
      return data
    })
  }

  const myCustomer = searchCustomer()
  myCustomer.should("have.key", "text")
  myCustomer.its("text").should("eq", "my data")
});
like image 37
Daniel Avatar answered Jun 29 '26 22:06

Daniel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!