Wait for data

Sometimes you want to wait for the data to populate the list. For example, a network cy.intercept could listen for all requests and grab a list of posted items, and the test needs to check this list.

Using cy.should callback function

<button id="post-list">Upload list</button>
<script>
  document
    .getElementById('post-list')
    .addEventListener('click', function () {
      // post the list of users after a delay
      setTimeout(function () {
        const users = [
          {
            id: 101,
            name: 'Joe',
          },
          {
            id: 102,
            name: 'Mary',
          },
        ]
        fetch('https://jsonplaceholder.cypress.io/users', {
          method: 'POST',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(users),
        })
      }, 1300)
    })
</script>

We can use the cy.shouldopen in new window callback function that re-runs until the assertions inside pass. Then we can use cy.thenopen in new window command to return the list value - it should be an array by then.

let list
// spy on the POST requests
cy.intercept('POST', '/users', (req) => {
  if (Array.isArray(req.body)) {
    list = req.body
  }
})

cy.get('#post-list').click()
// need to somehow wait for the list to be defined
// we cannot simply do expect(list).to.be.defined
// since it will be just evaluated once
// instead we need to use cy.should() assertions
cy.should(() => {
  expect(list).to.be.an('array')
})
  .then(() => {
    // now that we have the list let's yield it
    return list
  })
  // now the assertions will run against the list
  .should('have.length', 2)

Alternative: use cy.wrapopen in new window

Another alternative to the cy.should with callback function is to use an object with cy.wrapopen in new window command to check a property of an object.

<button id="post-list">Upload list</button>
<script>
  document
    .getElementById('post-list')
    .addEventListener('click', function () {
      // post the list of users after a delay
      setTimeout(function () {
        const users = [
          {
            id: 101,
            name: 'Joe',
          },
          {
            id: 102,
            name: 'Mary',
          },
        ]
        fetch('https://jsonplaceholder.cypress.io/users', {
          method: 'POST',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(users),
        })
      }, 1300)
    })
</script>
// instead of a plain variable, store the list
// as a property of an object
const data = {}
// spy on the POST requests
cy.intercept('POST', '/users', (req) => {
  if (Array.isArray(req.body)) {
    data.list = req.body
  }
})

cy.get('#post-list').click()
// the "data" variable is going to be the same
// only its contents is going to change. Thus we
// can wrap the "data" variable right away
cy.wrap(data)
  // note that "have.property" assertion is special,
  // it yields the property!
  .should('have.property', 'list')
  // thus the next assertion runs against the "data.list" value
  .should('be.an', 'array')
  // and now the assertions will run against the list
  .and('have.length', 2)

For more information about have.property assertion, see the assertions page.