Skip to main content
Cypress App

each

Iterate through an array like structure (arrays or objects with a length property).

Syntax​

.each(callbackFn)
.each(options, callbackFn)

Usage​

Correct Usage

cy.get('ul>li').each(() => {...}) // Iterate through each 'li'
cy.getCookies().each(() => {...}) // Iterate through each cookie

Incorrect Usage

cy.each(() => {...}) // Errors, cannot be chained off 'cy'
cy.clock().each(() => {...}) // Errors, 'clock' does not yield an array

Arguments​

options (Object)

Pass in an options object to change the default behavior of .each().

OptionDefaultDescription
timeoutdefaultCommandTimeoutTime to wait for each invocation of the callback function to resolve before timing out

callbackFn (Function)

Pass a function that is invoked with the following arguments:

  • value - the current item in the collection. If the item is a DOM element, Cypress wraps it in jQuery before passing it to the callback.
  • index - the 0-based index of the current item in the collection
  • collection - the original collection being iterated over

Yields ​

  • .each() yields the same subject it was given.
  • Because .each() is not a query, it is unsafe to chain further commands that act or assert on DOM elements after .each(), as those elements may have re-rendered and gone stale during iteration. Reading the yielded subject in a .then() callback (as in the example below) is safe.

Examples​

DOM Elements​

Iterate over an array of DOM elements

Each element arrives in the callback wrapped in jQuery, so you can read its state with jQuery methods. Wrap it with cy.wrap() to run Cypress commands on it.

cy.get('[data-cy="product-card"]').each(($card) => {
if ($card.find('[data-cy="sold-out"]').length) {
cy.wrap($card).find('button').should('be.disabled')
} else {
cy.wrap($card).find('button').should('be.enabled')
}
})

Assert the expected length before iterating

cy.get() resolves as soon as it finds at least one matching element, and .each() does not retry. If your application renders a list in stages, .each() iterates only the items that exist at that moment. Assert on the expected length first so the test waits for every item.

cy.get('[data-cy="result"]')
.should('have.length', 2)
.each(($result) => {
cy.wrap($result).should('contain', 'Cypress')
})

Scope commands to each element with .within()

cy.get('table tbody tr').each(($row) => {
cy.wrap($row).within(() => {
cy.get('td').eq(2).should('not.be.empty')
})
})

Re-query by index when an action changes the page

If an action in the callback navigates away or re-renders the list, the elements .each() passes in are detached from the DOM. Use index to query a fresh element on every iteration instead.

cy.get('nav a').each(($link, index) => {
cy.get('nav a').eq(index).click()
cy.go('back')
})

Collect results from Cypress commands, then assert once

When each item needs a Cypress command, such as a request, gather the results in the callback and assert on all of them in a .then() that runs after the loop finishes. This test reports every broken link at once instead of stopping at the first one.

const broken = []

cy.get('footer a')
.each(($link) => {
const href = $link.prop('href')

cy.request({ url: href, failOnStatusCode: false }).then((response) => {
if (response.status >= 400) {
broken.push(`${href} (${response.status})`)
}
})
})
.then(() => {
// list the links in the message, since Chai shortens long arrays
expect(broken, `broken links: ${broken.join(', ')}`).to.be.empty
})

If you only need to read values from the elements, such as their text, use a .should() callback instead.

The original collection is always yielded

No matter what the callback function returns, .each() always yields the original collection.

cy.get('li')
.should('have.length', 3)
.each(($li, index, $lis) => {
return 'something else'
})
.then(($lis) => {
expect($lis).to.have.length(3) // true
})

Here the .then() callback only reads the yielded array, which is safe. If instead you needed to act or assert on DOM elements that your application may have re-rendered during iteration, re-query them with a Cypress query rather than reusing the subject yielded by .each().

Arrays​

Iterate over fixture data

.each() works on any array, not only DOM elements. Cypress commands you call in the callback run in order, one item at a time.

// users.json contains an array of user objects
cy.fixture('users.json').each((user) => {
cy.request('POST', '/api/users', user).its('status').should('eq', 201)
})

Promises​

Promises are awaited

If your callback function returns a Promise, .each() waits for it to resolve before iterating over the next item in the collection.

cy.wrap([1, 2, 3]).each((num, i, array) => {
return new Cypress.Promise((resolve) => {
setTimeout(() => {
resolve()
}, num * 100)
})
})

Options​

Wait longer for each callback to resolve

The timeout option applies to each invocation of the callback, not to the loop as a whole.

cy.getCookies().each({ timeout: 10000 }, (cookie) => {
return new Cypress.Promise((resolve) => {
// work with each cookie, then resolve
resolve()
})
})

Notes​

Prefer .should() for assertions on every element​

Use a .should() callback when you only need to assert

.each() runs its callback once and does not retry. When all you need is an assertion about every element, a .should() callback reruns until the assertions pass or time out, so it tolerates elements that are still rendering or updating.

cy.get('[data-cy="result"]').should(($results) => {
expect($results).to.have.length.greaterThan(0)

$results.each((index, result) => {
expect(result.textContent).to.contain('Cypress')
})
})

Because the whole callback reruns, the same pattern works for checks on the list as a whole, such as sort order after your application applies a sort.

cy.get('[data-cy="price"]').should(($prices) => {
const prices = [...$prices].map((price) =>
Number(price.textContent.replace('$', ''))
)

expect(prices).to.deep.equal([...prices].sort((a, b) => a - b))
})

Chaining .should() after .each() does not give you this retry. .should() reruns only its own callback, not .each(), so it keeps checking values collected from the list as it was when .each() ran.

Keep .each() for work that needs Cypress commands per item, such as clicking, typing, or making requests.

Return early​

Stop each prematurely

You can stop the .each() loop early by returning false in the callback function. The callback must return false synchronously. A Promise that resolves to false does not stop the loop.

cy.get('ul>li').each(($li) => {
if ($li.hasClass('last-visible')) {
// stop iterating after this item
return false
}
})

Empty collections​

The callback is not invoked for an empty collection

When the subject has a length of 0, .each() skips the callback entirely and yields the empty subject. It does not fail or wait for items to appear.

A DOM query such as cy.get() fails when no elements match, so an empty subject usually comes from non-DOM data, such as cy.wrap([]), cy.fixture(), or cy.getCookies(). Assert on the length first when the data should never be empty.

Callback return values​

Don't mix Cypress commands with a synchronous return value

The callback follows the same rules as a .then() callback. If it calls Cypress commands and returns a truthy synchronous value (anything other than a falsy value or a Promise-like object with a .then() method), Cypress throws an error because you are mixing up async and sync code. Whatever the callback returns, .each() still yields the original collection.

Rules​

Requirements ​

  • .each() requires being chained off a previous command.
  • .each() requires its subject to have a length property, such as an array or a jQuery collection. Any other subject fails with an error.

Assertions ​

  • .each() will only run assertions you have chained once, and will not retry.

Timeouts ​

  • .each() can time out waiting for a promise you've returned to resolve. The timeout applies to each invocation of the callback and defaults to defaultCommandTimeout.

Command Log​

  • .each() does not log in the Command Log unless it fails, for example when its callback times out or its subject is not array-like.

See also​