UI Automation
Cypress
Commands & Assertions

Cypress Commands and Assertions

Parul Dhingra - Senior Quality Analyst
Parul Dhingra13+ Years ExperienceHire Me

Senior Quality Analyst

Updated: 1/23/2026

Understanding Cypress commands and assertions is fundamental to writing effective tests. Cypress commands are asynchronous and chainable, with built-in retry logic that handles most timing issues automatically. This guide covers every command you'll need, from basic interactions to advanced patterns.

Master these commands and you'll write tests that are both reliable and readable.

Command Fundamentals

How Cypress Commands Work

Cypress commands are asynchronous but don't use Promises directly. Instead, they're queued and executed in order.

// Commands are queued, not executed immediately
cy.visit('/login')          // Queued first
cy.get('#email')            // Queued second
cy.type('user@test.com')    // Queued third
// All execute in order when the test runs

Subject and Chaining

Commands either start a new chain or continue an existing chain by accepting a subject:

// Parent command - starts new chain
cy.visit('/page')
cy.get('[data-cy="button"]')
 
// Child command - requires previous subject
cy.get('[data-cy="input"]').type('hello')  // .type() needs an element
cy.get('[data-cy="input"]').clear()        // .clear() needs an element
 
// Dual command - can start or continue
cy.contains('Submit')                        // Starts new chain
cy.get('form').contains('Submit')            // Continues chain from form

Automatic Retry

Cypress automatically retries commands and assertions:

// Cypress retries finding button until it exists or 4s timeout
cy.get('[data-cy="button"]')
 
// Retries both get() and should() until passing or timeout
cy.get('[data-cy="message"]').should('be.visible')

Querying Commands

cy.get() - Query by Selector

// CSS selectors
cy.get('#username')                      // By ID
cy.get('.error-message')                 // By class
cy.get('input[type="email"]')            // By attribute
cy.get('[data-cy="submit-btn"]')         // By data attribute (recommended)
 
// Pseudo-selectors
cy.get('li:first')                       // First list item
cy.get('li:last')                        // Last list item
cy.get('li:nth-child(3)')                // Third item
cy.get('input:visible')                  // Only visible inputs
cy.get('button:contains("Submit")')      // Button containing text
 
// Options
cy.get('[data-cy="slow-element"]', { timeout: 10000 })  // Custom timeout

cy.contains() - Query by Text

// Find element containing text
cy.contains('Welcome')                    // Any element with "Welcome"
cy.contains('button', 'Submit')           // Button containing "Submit"
cy.contains('[data-cy="nav"]', 'Home')    // Element in nav containing "Home"
 
// Regex matching
cy.contains(/hello/i)                     // Case-insensitive match
cy.contains(/^Exact Match$/)              // Exact text match
 
// Within specific element
cy.get('[data-cy="sidebar"]').contains('Settings')

cy.find() - Query Within Element

// Find within a subject
cy.get('[data-cy="user-card"]')
  .find('.user-name')                     // Find .user-name inside user-card
  .should('contain', 'John')
 
// Chained finds
cy.get('table')
  .find('tbody')
  .find('tr')
  .first()
  .find('td')

cy.filter() / cy.not() - Filter Results

// Filter to matching elements
cy.get('li').filter('.active')            // Only active items
cy.get('button').filter(':visible')       // Only visible buttons
cy.get('input').filter('[required]')      // Only required inputs
 
// Exclude elements
cy.get('li').not('.disabled')             // Exclude disabled items
cy.get('button').not('[data-cy="cancel"]')

cy.first() / cy.last() / cy.eq()

// Get first/last element
cy.get('li').first()                      // First item
cy.get('li').last()                       // Last item
 
// Get by index (0-based)
cy.get('li').eq(0)                        // First item
cy.get('li').eq(2)                        // Third item
cy.get('li').eq(-1)                       // Last item (negative index)

cy.within() - Scope Commands

// All commands scoped to login form
cy.get('[data-cy="login-form"]').within(() => {
  cy.get('input[name="email"]').type('user@test.com')
  cy.get('input[name="password"]').type('password123')
  cy.get('button[type="submit"]').click()
})

cy.parent() / cy.parents() / cy.children()

// Navigate DOM tree
cy.get('input#email')
  .parent()                               // Immediate parent
  .should('have.class', 'form-group')
 
cy.get('input#email')
  .parents('form')                        // Ancestor matching selector
  .should('exist')
 
cy.get('ul')
  .children()                             // Direct children only
  .should('have.length', 5)
 
cy.get('ul')
  .children('.active')                    // Filtered children

cy.siblings() / cy.prev() / cy.next()

// Sibling navigation
cy.get('.active')
  .siblings()                             // All siblings
  .should('have.length', 4)
 
cy.get('.current')
  .prev()                                 // Previous sibling
  .should('have.class', 'completed')
 
cy.get('.current')
  .next()                                 // Next sibling
  .should('have.class', 'pending')

Action Commands

cy.type() - Keyboard Input

// Basic typing
cy.get('[data-cy="email"]').type('user@example.com')
 
// Type with delay (simulate slow typing)
cy.get('[data-cy="search"]').type('query', { delay: 100 })
 
// Special keys
cy.get('[data-cy="input"]').type('{enter}')
cy.get('[data-cy="input"]').type('{esc}')
cy.get('[data-cy="input"]').type('{backspace}')
cy.get('[data-cy="input"]').type('{del}')
cy.get('[data-cy="input"]').type('{selectall}{del}')  // Clear field
 
// Modifier keys
cy.get('[data-cy="input"]').type('{ctrl+a}')          // Select all
cy.get('[data-cy="input"]').type('{shift+tab}')       // Shift+Tab
cy.get('[data-cy="input"]').type('{alt+f4}')          // Alt+F4
 
// Arrow keys
cy.get('[data-cy="input"]').type('{leftarrow}')
cy.get('[data-cy="input"]').type('{rightarrow}')
cy.get('[data-cy="input"]').type('{uparrow}')
cy.get('[data-cy="input"]').type('{downarrow}')
 
// Combination
cy.get('[data-cy="search"]').type('search term{enter}')
 
// Force type (bypass actionability checks)
cy.get('[data-cy="hidden-input"]').type('value', { force: true })

cy.clear() - Clear Input

cy.get('[data-cy="email"]').clear()
cy.get('[data-cy="email"]').clear().type('new@email.com')

cy.click() - Click Element

// Basic click
cy.get('[data-cy="submit"]').click()
 
// Click positions
cy.get('[data-cy="area"]').click('topLeft')
cy.get('[data-cy="area"]').click('center')
cy.get('[data-cy="area"]').click('bottomRight')
cy.get('[data-cy="area"]').click(15, 40)      // x, y coordinates
 
// Multiple clicks
cy.get('[data-cy="button"]').click({ multiple: true })  // Click all matching
 
// Force click (bypass checks)
cy.get('[data-cy="covered-button"]').click({ force: true })
 
// Double-click
cy.get('[data-cy="item"]').dblclick()
 
// Right-click
cy.get('[data-cy="item"]').rightclick()

cy.check() / cy.uncheck() - Checkboxes/Radios

// Check
cy.get('[data-cy="agree"]').check()
cy.get('[type="checkbox"]').check()
cy.get('[type="radio"]').check('option1')      // Check specific value
 
// Check multiple
cy.get('[type="checkbox"]').check(['option1', 'option2'])
 
// Uncheck
cy.get('[data-cy="newsletter"]').uncheck()
cy.get('[type="checkbox"]').uncheck(['option1', 'option2'])
 
// Force check
cy.get('[data-cy="hidden-checkbox"]').check({ force: true })

cy.select() - Dropdown Selection

// Select by text
cy.get('[data-cy="country"]').select('United States')
 
// Select by value
cy.get('[data-cy="country"]').select('US')
 
// Select by index
cy.get('[data-cy="country"]').select(2)
 
// Select multiple (for multi-select)
cy.get('[data-cy="tags"]').select(['option1', 'option2'])
 
// Force select
cy.get('[data-cy="dropdown"]').select('value', { force: true })

cy.trigger() - Custom Events

// Mouse events
cy.get('[data-cy="menu"]').trigger('mouseover')
cy.get('[data-cy="menu"]').trigger('mouseout')
cy.get('[data-cy="item"]').trigger('mousedown')
cy.get('[data-cy="item"]').trigger('mouseup')
 
// Custom event with data
cy.get('[data-cy="slider"]').trigger('change', { value: 50 })
 
// Touch events
cy.get('[data-cy="swipeable"]').trigger('touchstart')
cy.get('[data-cy="swipeable"]').trigger('touchmove')
cy.get('[data-cy="swipeable"]').trigger('touchend')

cy.scrollTo() / cy.scrollIntoView()

// Scroll window
cy.scrollTo('bottom')
cy.scrollTo('top')
cy.scrollTo(0, 500)                       // x, y coordinates
cy.scrollTo('50%', '50%')                 // Percentage
 
// Scroll element into view
cy.get('[data-cy="footer"]').scrollIntoView()
 
// Scroll within element
cy.get('[data-cy="scrollable-list"]').scrollTo('bottom')

cy.focus() / cy.blur()

// Focus element
cy.get('[data-cy="input"]').focus()
cy.focused().should('have.attr', 'data-cy', 'input')
 
// Blur element
cy.get('[data-cy="input"]').blur()

cy.selectFile() - File Upload

// Select single file
cy.get('[data-cy="file-input"]').selectFile('cypress/fixtures/image.png')
 
// Select multiple files
cy.get('[data-cy="file-input"]').selectFile([
  'cypress/fixtures/file1.pdf',
  'cypress/fixtures/file2.pdf'
])
 
// Drag and drop
cy.get('[data-cy="dropzone"]').selectFile('cypress/fixtures/doc.pdf', {
  action: 'drag-drop'
})
 
// From fixture
cy.fixture('image.png').then((fileContent) => {
  cy.get('[data-cy="file-input"]').selectFile({
    contents: Cypress.Buffer.from(fileContent),
    fileName: 'image.png'
  })
})

Assertion Commands

cy.should() - Chainable Assertions

// Visibility
cy.get('[data-cy="modal"]').should('be.visible')
cy.get('[data-cy="loading"]').should('not.be.visible')
cy.get('[data-cy="error"]').should('not.exist')
 
// Text content
cy.get('[data-cy="title"]').should('have.text', 'Welcome')
cy.get('[data-cy="message"]').should('contain', 'success')
cy.get('[data-cy="message"]').should('not.contain', 'error')
cy.get('[data-cy="empty"]').should('be.empty')
 
// Attributes
cy.get('[data-cy="link"]').should('have.attr', 'href', '/dashboard')
cy.get('[data-cy="link"]').should('have.attr', 'target', '_blank')
cy.get('[data-cy="img"]').should('have.attr', 'src').and('include', 'logo')
 
// CSS classes
cy.get('[data-cy="item"]').should('have.class', 'active')
cy.get('[data-cy="item"]').should('not.have.class', 'disabled')
 
// CSS properties
cy.get('[data-cy="alert"]').should('have.css', 'background-color', 'rgb(255, 0, 0)')
cy.get('[data-cy="element"]').should('have.css', 'display', 'none')
 
// Form values
cy.get('[data-cy="email"]').should('have.value', 'user@test.com')
cy.get('[data-cy="checkbox"]').should('be.checked')
cy.get('[data-cy="checkbox"]').should('not.be.checked')
 
// Element state
cy.get('[data-cy="submit"]').should('be.disabled')
cy.get('[data-cy="submit"]').should('be.enabled')
cy.get('[data-cy="input"]').should('be.focused')
 
// Length
cy.get('li').should('have.length', 5)
cy.get('li').should('have.length.greaterThan', 3)
cy.get('li').should('have.length.lessThan', 10)

cy.and() - Chained Assertions

// Multiple assertions on same element
cy.get('[data-cy="user-card"]')
  .should('be.visible')
  .and('have.class', 'active')
  .and('contain', 'John Doe')
 
cy.get('[data-cy="link"]')
  .should('have.attr', 'href', '/profile')
  .and('have.attr', 'target', '_blank')
  .and('be.visible')

cy.then() - Custom Assertions

// Access yielded value
cy.get('[data-cy="count"]').then(($el) => {
  const count = parseInt($el.text())
  expect(count).to.be.greaterThan(0)
})
 
// Multiple elements
cy.get('li').then(($items) => {
  expect($items).to.have.length(5)
  expect($items.first()).to.contain('Item 1')
})

cy.its() - Property Access

// Get object property
cy.get('li').its('length').should('eq', 5)
cy.window().its('localStorage.token').should('exist')
cy.location().its('pathname').should('eq', '/dashboard')
 
// Nested properties
cy.wrap({ user: { name: 'John' } })
  .its('user.name')
  .should('eq', 'John')

cy.invoke() - Call Methods

// Call method on element
cy.get('[data-cy="input"]').invoke('val').should('eq', 'expected value')
cy.get('[data-cy="element"]').invoke('text').should('include', 'hello')
 
// Call with arguments
cy.get('[data-cy="input"]').invoke('attr', 'placeholder').should('eq', 'Enter email')
 
// Invoke jQuery methods
cy.get('[data-cy="element"]').invoke('css', 'color').should('eq', 'rgb(0, 0, 0)')
cy.get('[data-cy="element"]').invoke('width').should('be.greaterThan', 100)

Navigation Commands

cy.visit() - Navigate to URL

// Visit URL
cy.visit('https://example.com')
cy.visit('/login')                        // Relative to baseUrl
cy.visit('/dashboard?tab=settings')       // With query params
 
// Options
cy.visit('/slow-page', { timeout: 30000 })
cy.visit('/page', {
  failOnStatusCode: false,                // Don't fail on 4xx/5xx
  onBeforeLoad(win) {
    // Modify window before page loads
    win.localStorage.setItem('token', 'abc123')
  }
})

cy.go() - Browser History

cy.go('back')                             // Go back
cy.go('forward')                          // Go forward
cy.go(-1)                                 // Go back
cy.go(2)                                  // Go forward 2 pages

cy.reload() - Refresh Page

cy.reload()                               // Soft reload
cy.reload(true)                           // Hard reload (clear cache)

cy.url() / cy.location()

// URL assertions
cy.url().should('include', '/dashboard')
cy.url().should('eq', 'https://example.com/dashboard')
 
// Location object
cy.location('pathname').should('eq', '/users/profile')
cy.location('search').should('include', 'page=2')
cy.location('hash').should('eq', '#section1')
cy.location('hostname').should('eq', 'example.com')

cy.title()

cy.title().should('eq', 'Dashboard | MyApp')
cy.title().should('include', 'Dashboard')

Network Commands

cy.intercept() - Spy/Stub Requests

// Spy on requests
cy.intercept('GET', '/api/users').as('getUsers')
cy.get('[data-cy="load-users"]').click()
cy.wait('@getUsers')
 
// Stub response
cy.intercept('GET', '/api/users', {
  statusCode: 200,
  body: [{ id: 1, name: 'John' }]
}).as('getUsers')
 
// Stub with fixture
cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers')
 
// Modify response
cy.intercept('GET', '/api/users', (req) => {
  req.reply((res) => {
    res.body.push({ id: 999, name: 'Extra User' })
  })
})
 
// Delay response
cy.intercept('GET', '/api/slow', {
  body: { data: 'value' },
  delay: 2000
})
 
// Simulate error
cy.intercept('GET', '/api/users', {
  statusCode: 500,
  body: { error: 'Server Error' }
})
 
// Pattern matching
cy.intercept('GET', '/api/users/*').as('getUser')
cy.intercept('POST', '/api/**').as('apiPost')
cy.intercept({ method: 'GET', url: '**/users/**' }).as('userRequest')

cy.wait() - Wait for Alias

// Wait for intercepted request
cy.intercept('GET', '/api/users').as('getUsers')
cy.visit('/users')
cy.wait('@getUsers')
 
// Assert on response
cy.wait('@getUsers').then((interception) => {
  expect(interception.response.statusCode).to.eq(200)
  expect(interception.response.body).to.have.length(5)
})
 
// Multiple waits
cy.wait(['@getUsers', '@getSettings'])
 
// Wait with timeout
cy.wait('@slowRequest', { timeout: 30000 })

cy.request() - HTTP Requests

// GET request
cy.request('/api/users').then((response) => {
  expect(response.status).to.eq(200)
  expect(response.body).to.have.length(5)
})
 
// POST request
cy.request('POST', '/api/users', {
  name: 'John',
  email: 'john@test.com'
}).then((response) => {
  expect(response.status).to.eq(201)
})
 
// With options
cy.request({
  method: 'POST',
  url: '/api/login',
  body: { email: 'user@test.com', password: 'password' },
  failOnStatusCode: false
}).then((response) => {
  if (response.status === 200) {
    cy.setCookie('auth_token', response.body.token)
  }
})

Utility Commands

cy.wrap() - Wrap Object

// Wrap object for assertions
cy.wrap({ name: 'John' }).its('name').should('eq', 'John')
 
// Wrap element
cy.get('li').first().then(($el) => {
  cy.wrap($el).should('have.class', 'active')
})
 
// Wrap promise
cy.wrap(myPromise).then((result) => {
  expect(result).to.eq('expected')
})

cy.log() - Log Message

cy.log('Starting user creation flow')
cy.log('User email:', userEmail)

cy.debug() / cy.pause()

// Pause and open debugger
cy.get('[data-cy="element"]')
  .debug()                                // Opens DevTools debugger
  .click()
 
// Pause test execution
cy.get('[data-cy="step1"]').click()
cy.pause()                                // Test pauses here
cy.get('[data-cy="step2"]').click()

cy.fixture() - Load Test Data

// Load JSON fixture
cy.fixture('users.json').then((users) => {
  expect(users).to.have.length(3)
})
 
// Use in intercept
cy.intercept('GET', '/api/users', { fixture: 'users.json' })
 
// Alias for reuse
cy.fixture('users.json').as('userData')
cy.get('@userData').then((users) => {
  // use users data
})

cy.task() - Node.js Operations

// In cypress.config.js
setupNodeEvents(on, config) {
  on('task', {
    queryDatabase(query) {
      return db.query(query)
    },
    clearDatabase() {
      return db.clear()
    }
  })
}
 
// In test
cy.task('clearDatabase')
cy.task('queryDatabase', 'SELECT * FROM users').then((rows) => {
  expect(rows).to.have.length(0)
})

cy.exec() - Shell Commands

cy.exec('npm run seed-db')
cy.exec('echo "hello"').its('stdout').should('contain', 'hello')

Custom Commands

Creating Custom Commands

// cypress/support/commands.js
 
// Simple command
Cypress.Commands.add('login', (email, password) => {
  cy.visit('/login')
  cy.get('[data-cy="email"]').type(email)
  cy.get('[data-cy="password"]').type(password)
  cy.get('[data-cy="submit"]').click()
  cy.url().should('include', '/dashboard')
})
 
// API login (faster)
Cypress.Commands.add('loginByApi', (email, password) => {
  cy.request('POST', '/api/auth/login', { email, password })
    .its('body.token')
    .then((token) => {
      cy.setCookie('auth_token', token)
    })
})
 
// Command with options
Cypress.Commands.add('getByDataCy', (selector, options = {}) => {
  return cy.get(`[data-cy="${selector}"]`, options)
})
 
// Child command (requires previous subject)
Cypress.Commands.add('shouldBeVisible', { prevSubject: true }, (subject) => {
  cy.wrap(subject).should('be.visible')
})
 
// Dual command
Cypress.Commands.add('getActive', { prevSubject: 'optional' }, (subject) => {
  if (subject) {
    return cy.wrap(subject).find('.active')
  }
  return cy.get('.active')
})

Using Custom Commands

// In tests
cy.loginByApi('user@test.com', 'password123')
cy.getByDataCy('submit-button').click()
cy.getByDataCy('message').shouldBeVisible()

Chaining Patterns

Basic Chaining

cy.get('[data-cy="form"]')
  .find('input[name="email"]')
  .type('user@test.com')
  .should('have.value', 'user@test.com')

Assertions Don't Change Subject

// Subject remains the input element
cy.get('[data-cy="input"]')
  .type('hello')
  .should('have.value', 'hello')    // Subject still input
  .clear()                           // Operates on input
  .should('have.value', '')          // Subject still input

Using .then() for Custom Logic

cy.get('[data-cy="items"]').then(($items) => {
  if ($items.length > 5) {
    cy.get('[data-cy="show-more"]').click()
  }
})

Aliases for Reuse

// Create alias
cy.get('[data-cy="user-card"]').as('userCard')
 
// Use alias
cy.get('@userCard').find('.name').should('contain', 'John')
cy.get('@userCard').find('.email').should('contain', 'john@test.com')

Common Pitfalls

1. Treating Commands as Synchronous

// Wrong - commands are asynchronous
const text = cy.get('[data-cy="title"]').text()  // Returns Chainable, not string
 
// Correct - use .then()
cy.get('[data-cy="title"]').then(($el) => {
  const text = $el.text()
  // use text
})

2. Conditional Testing Without Care

// Risky - may cause flakiness
cy.get('body').then(($body) => {
  if ($body.find('[data-cy="popup"]').length > 0) {
    cy.get('[data-cy="popup"]').find('.close').click()
  }
})
 
// Better - wait for known state
cy.get('[data-cy="popup"]', { timeout: 0 })
  .should('not.exist')

3. Using Arbitrary Waits

// Bad
cy.wait(5000)
 
// Good
cy.intercept('GET', '/api/data').as('getData')
cy.get('[data-cy="load"]').click()
cy.wait('@getData')

4. Forgetting Assertions Are Retried

// This retries until passing or timeout
cy.get('[data-cy="count"]').should('have.text', '5')
 
// This does NOT retry - assertion inside .then()
cy.get('[data-cy="count"]').then(($el) => {
  expect($el.text()).to.eq('5')  // No retry!
})

Test Your Knowledge

Quiz on Cypress Commands and Assertions

Your Score: 0/10

Question: What is the key difference between cy.get() and cy.find()?


Continue Learning


Frequently Asked Questions

Frequently Asked Questions (FAQs) / People Also Ask (PAA)

What's the difference between .should() and .then() for assertions?

Why do my commands return Chainable objects instead of actual values?

How do I check if an element exists without failing the test?

What's the difference between cy.get() and cy.contains()?

How do I handle elements that take a long time to appear?

Can I use async/await with Cypress commands?

How do I select elements with dynamic attributes or IDs?

What's the difference between cy.invoke() and cy.its()?