1 Cypress for beginners

A beginner-friendly guide to end-to-end testing with Cypress โ€” from installation to writing your first test, complete with commands, tips, and real-world examples.


๐Ÿ“– What is Cypress?

Cypress allows you to write tests that run directly in the browser, mimicking how a real user interacts with your site. Unlike Selenium, it operates in the same run-loop as your application, making debugging much easier.


โš™๏ธ Installation

In your JavaScript project, run the following command to add Cypress (works with any framework like Frappe, ReactJS, NextJS, etc.):

npm install cypress --save-dev

Initial Setup

  1. Run npx cypress open to launch Cypress
  2. Once opened, Cypress will create a folder named cypress/ with:
    • e2e/ โ€“ for your test files
    • support/ โ€“ custom commands and support logic
    • fixtures/ โ€“ test data
    • cypress.config.js โ€“ configuration file
  3. To run tests in headless mode, use npx cypress run

๐Ÿ“ Your First Test

Create a test file at:

cypress/e2e/sample_test.cy.js

describe('My First Test', () => {
  it('Visits the Portfolio website', () => {
    cy.visit('https://kunj.pages.dev')
    cy.contains('Kunj').should('exist')
  })
})

Run Your Test

npx cypress open          # Opens Cypress UI
npx cypress run           # Headless mode

๐Ÿ“‹ Common Cypress Commands

Command Description
cy.visit() Opens a webpage
cy.get() Selects an element (like jQuery)
cy.contains() Finds elements by text
cy.click() Clicks an element
cy.type() Types into input fields
cy.should() Asserts something (like .should('be.visible'))

๐Ÿ’ป Cypress In-Depth Commands Guide

1. Visiting Pages

cy.visit(url, options?)

Loads a web page in the browser.

Basic usage:

cy.visit('https://kunj.me')

With options:

cy.visit('/login', {
  timeout: 10000,
  onBeforeLoad: (win) => {
    // stub or modify window object
  }
})

2. Querying Elements

cy.get(selector)

Gets one or more DOM elements.

cy.get('button')                     // All buttons
cy.get('.btn-primary')              // Class selector
cy.get('[data-cy=login-button]')    // Best practice!

cy.contains(text)

Find element with specific text.

cy.contains('Submit')               // Finds "Submit" text
cy.contains('button', 'Save')       // Finds button with text "Save"

cy.find(selector)

Chained version of .get() used with parent element.

cy.get('.form').find('input[type="text"]')

3. Interactions

cy.click()

Clicks on an element.

cy.get('[data-cy=submit-button]').click()

cy.type(text)

Types text into an input.

cy.get('[data-cy=email]').type('kunj@gmail.com')
cy.get('[data-cy=password]').type('Secret123', { log: false }) // hides logs

cy.select(value)

Selects a value from a <select> dropdown.

cy.get('select').select('India')

cy.check() / cy.uncheck()

Checks or unchecks a checkbox or radio input.

cy.get('#terms').check()
cy.get('#subscribe').uncheck()

4. Assertions

should()

Used for assertions.

cy.get('h1').should('contain', 'Welcome')
cy.get('[data-cy=error]').should('not.exist')
cy.url().should('include', '/dashboard')

Common assertions:

.should('be.visible')
.should('be.enabled')
.should('have.value', 'Kunj')
.should('have.length', 3)

5. Waiting & Timing

cy.wait(time)

Pauses test execution for specified ms (not recommended unless needed).

cy.wait(2000) // waits for 2 seconds

cy.wait('@alias')

Waits for a specific network request.

cy.intercept('POST', '/api/login').as('login')
cy.get('form').submit()
cy.wait('@login').its('response.statusCode').should('eq', 200)

6. Network Requests

cy.intercept(method, url)

Intercepts (mocks or spies) a network request.

Example: Spying on login API

cy.intercept('POST', '/api/login').as('login')
cy.get('[data-cy=submit]').click()
cy.wait('@login').its('response.statusCode').should('eq', 200)

Example: Mocking (stubbing) a GET response

cy.intercept('GET', '/api/user', {
  statusCode: 200,
  body: { name: 'Kunj', role: 'Admin' },
}).as('getUser')

7. Custom Commands

You can write reusable commands in cypress/support/commands.js:

Cypress.Commands.add('login', (email, password) => {
  cy.get('[data-cy=email]').type(email)
  cy.get('[data-cy=password]').type(password)
  cy.get('[data-cy=submit]').click()
})

Usage:

cy.login('kunj@gmail.com', 'Password123')

8. Fixtures (Mock Data)

Fixtures help you load external static JSON.

Create: cypress/fixtures/user.json

{
  "email": "kunj@gmail.com",
  "password": "secret123"
}

Use in test:

cy.fixture('user').then((user) => {
  cy.get('[data-cy=email]').type(user.email)
  cy.get('[data-cy=password]').type(user.password)
})

9. Aliases

as() to create aliases for reuse

cy.get('[data-cy=email]').as('emailInput')
cy.get('@emailInput').type('kunj@gmail.com')

10. State Management

Common state management commands:

cy.reload()              // Reloads the page
cy.clearCookies()        // Clears cookies
cy.clearLocalStorage()   // Clears local storage
cy.scrollTo()            // Scrolls element or window

Examples:

cy.scrollTo('bottom')
cy.get('.chat-box').scrollTo('top')

11. Test Structure & Hooks

describe('Login Flow', () => {
  before(() => {
    // Runs once before all tests
  })

  beforeEach(() => {
    // Runs before each test
    cy.visit('/login')
  })

  it('Should login with valid credentials', () => {
    // Test case
  })

  afterEach(() => {
    // Cleanup if needed
  })

  after(() => {
    // Runs once after all tests
  })
})

๐Ÿ’ก Tips & Best Practices

Tip Why
Use data-cy attributes More stable than CSS or IDs
Avoid .wait() unless necessary Use cy.intercept() instead
Write atomic, independent tests Easier to debug and maintain
Use beforeEach() to reset state Ensures clean start
Don't test implementation details Test user behavior

๐Ÿ“ Complete Test Example

describe('User Login Flow', () => {
  beforeEach(() => {
    cy.visit('/login')
    cy.clearCookies()
    cy.clearLocalStorage()
  })

  it('Should login successfully with valid credentials', () => {
    // Intercept login API
    cy.intercept('POST', '/api/login').as('loginRequest')

    // Fill in credentials
    cy.get('[data-cy=email]').type('kunj@gmail.com')
    cy.get('[data-cy=password]').type('Password123', { log: false })

    // Submit form
    cy.get('[data-cy=submit-button]').click()

    // Wait for API response
    cy.wait('@loginRequest').its('response.statusCode').should('eq', 200)

    // Verify redirect
    cy.url().should('include', '/dashboard')
    cy.contains('Welcome, Kunj').should('be.visible')
  })

  it('Should show error with invalid credentials', () => {
    cy.get('[data-cy=email]').type('invalid@gmail.com')
    cy.get('[data-cy=password]').type('WrongPass')
    cy.get('[data-cy=submit-button]').click()

    cy.get('[data-cy=error-message]')
      .should('be.visible')
      .and('contain', 'Invalid credentials')
  })
})

๐Ÿ“‹ Quick Command Reference

// Navigation
cy.visit(url)
cy.go('back')
cy.reload()

// Querying
cy.get(selector)
cy.contains(text)
cy.find(selector)

// Interactions
cy.click()
cy.type(text)
cy.select(value)
cy.check() / cy.uncheck()

// Assertions
.should('be.visible')
.should('exist')
.should('have.value', value)

// Network
cy.intercept(method, url)
cy.wait('@alias')

// State
cy.clearCookies()
cy.clearLocalStorage()
cy.scrollTo(position)

// Utilities
cy.fixture(file)
.as('alias')
cy.wait(time)


๐Ÿ“– Learning Resources


โš™๏ธ Useful Configuration

cypress.config.js:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
    viewportWidth: 1280,
    viewportHeight: 720,
    video: false,
    screenshotOnRunFailure: true,
    defaultCommandTimeout: 10000,
  },
})

โš™๏ธ CI/CD Integration

Example GitHub Actions workflow:

name: Cypress Tests
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Cypress run
        uses: cypress-io/github-action@v5
        with:
          start: npm start
          wait-on: 'http://localhost:3000'