Cypress Command Chaining

Cypress commands connect together using a pattern called chaining. Each command passes its result to the next command in line. This topic explains how chaining works and why it matters for writing clean tests.

A Basic Chain Example

cy.get('.login-form').find('input').first().type('john_doe')

This single line finds a form, searches for input fields inside it, selects the first one, and types text into it. Each command builds on the result of the previous command.

A Simple Way to Picture It

Think of a chain as a bucket brigade passing water toward a fire. Each person hands the bucket to the next person in line. Cypress commands hand their result to the next command, until the final action happens at the end of the chain.

How Cypress Handles Chains

Cypress commands do not return values immediately like normal JavaScript functions. Instead, they return a special object that queues the next command. Cypress executes the entire chain in order, waiting for each step to become ready before moving forward.

Chain Flow Diagram

cy.get('.form')
     |
     v
.find('input')
     |
     v
.first()
     |
     v
.type('john_doe')
     |
     v
Final Result: text typed into the first input

Chaining with Assertions

Assertions fit naturally at the end of a chain, checking the final result of the previous commands.

cy.get('.cart-items').find('li').should('have.length', 3)

This chain finds all list items inside the cart and checks that exactly three exist.

Breaking Chains for Clarity

Long chains can become hard to read when packed onto a single line. Splitting a chain across multiple lines improves readability without changing its behavior.

cy.get('.cart-items')
  .find('li')
  .should('have.length', 3)

Starting a New Chain

Every chain must start with a Cypress command like cy.get or cy.visit. You cannot begin a chain in the middle, since Cypress needs an initial subject to work from.

cy.get('.header')
cy.get('.footer')

These two lines represent separate chains, each starting fresh from its own cy.get command.

Using the then Command

The then command accesses the actual value produced by a previous command, useful for custom logic.

cy.get('.price').then(($price) => {
  const value = parseFloat($price.text())
  expect(value).to.be.greaterThan(0)
})

This pattern reads the text from an element and performs a calculation before making an assertion.

Why Chaining Improves Readability

Chaining reduces the need for temporary variables to store intermediate results. A single readable line often replaces several separate steps found in older testing tools. This compact style keeps test files shorter and easier to scan.

Avoiding Common Chaining Mistakes

A common mistake involves trying to store a Cypress command result in a regular variable for later use.

// Incorrect approach
const button = cy.get('#submit')
button.click()

This pattern fails because Cypress commands run asynchronously behind the scenes. The correct approach chains commands directly instead of storing them.

cy.get('#submit').click()

Chaining Across Multiple Elements

Commands like each allow performing an action across every matched element in a chain.

cy.get('.product-card').each(($card) => {
  cy.wrap($card).should('be.visible')
})

This pattern checks that every product card on the page appears visibly, one at a time.

Key Points

  • Chaining passes the result of one command into the next command.
  • Every chain must start with a base command like cy.get or cy.visit.
  • Assertions typically appear at the end of a chain.
  • The then command accesses actual values for custom logic.
  • Storing Cypress commands in regular variables leads to incorrect behavior.

Leave a Comment

Your email address will not be published. Required fields are marked *