
Cypress Commands and Assertions
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.
Table Of Contents-
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 runsSubject 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 formAutomatic 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 timeoutcy.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 childrency.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 pagescy.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 inputUsing .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()?