Cypress.io How to handle async code

While @isotopeee's solution basically works, I did run into issues, especially when using wait(@alias) and an await command right after that. The problem seems to be, that Cypress functions return an internal Chainable type that looks like a Promise but isn't one.

You can however use this to your advantage and instead of writing

describe('Test Case', () => {
  (async () => {
     cy.visit('/')
     await something();
  })()
})

you can write

describe('Test Case', () => {
  cy.visit('/').then(async () => await something())
})

This should work with every Cypress command


Your problem stems from the fact that cypress commands are not promises, although behaving like promises.

I can think of two options:

  • Try to refactor your test code to not use async/await, as these commands don't behave as expected when running your code on cypress (check this bug). Cypress already has a whole way of dealing with async code as it creates a command queue that always run sequentially and in the expected order. That means you could observe the effects of your async code to validate that it happened before moving forward on your test. For instance, if User.createUserOnServer must wait a successful API call, add code to your test that will wait for the request to complete, using cy.server(), cy.route() and cy.wait(), like below:

    cy.server();
    cy.route('POST', '/users/').as('createUser');
    // do something to trigger your request here, like user.createOnServer()
    cy.wait('@createUser', { timeout: 10000});
    
  • Use another third-party library that changes how cypress works with async/await, like cypress-promise. This lib may help you to treat cypress commands as promises that you can await in your before code (read more about it in this article).


I am using the following code snippet to make sure a async function is executed in cypress before the next cypress command will be executed:

cy.wrap(null).then(() => myAsyncFunction());

Example:

function sleep(milliseconds) {
    return new Promise((resolve) => setTimeout(resolve, milliseconds));
}

async function asyncFunction1() {
    console.log('started asyncFunction1');
    await sleep(3000);
    console.log('finalized asyncFunction1');
}

async function asyncFunction2() {
    console.log('started asyncFunction2');
    await sleep(3000);
    console.log('finalized asyncFunction2');
}

describe('Async functions', () => {
    it('should be executed in sequence', () => {
        cy.wrap(null).then(() => asyncFunction1());
        cy.wrap(null).then(() => asyncFunction2());
    });
});

leads to following output:

started asyncFunction1
finalized asyncFunction1
started asyncFunction2
finalized asyncFunction2

Put the async code in cy.then():

  before(() => {
    cy.then(async () => {
      await user.createOnServer()
      offer = await user.createOffer()
      await offer.publish()
      user.login()
      cy.visit(`/offers/${offer.details.id}`)
    })

    // This line can be outside `cy.then` because it does not
    // use any variables created or set inside `cy.then`.
    cy.get('.offer-description__content button').as('showMoreButton')
  })