Cypress Lesson 1 of 6 ⏱️ 4 min read

Cypress Introduction

Installing Cypress, the Test Runner, and writing your first end-to-end test against a real page.

Updated Oct 30, 2026

What is Cypress?

Cypress is a JavaScript end-to-end testing tool for web applications. Unlike Selenium, which controls the browser from outside, Cypress runs inside the browser alongside your app. That gives it:

  • Automatic waiting and retries — commands and assertions retry until they pass or time out, so you rarely write waits.
  • Time-travel debugging — the Test Runner shows a snapshot of the page at every command.
  • Network control — spy on and stub API calls with cy.intercept.
  • Fast feedback — tests re-run the moment you save a file.

Cypress tests are written in JavaScript or TypeScript with Mocha's describe/it and Chai assertions.

Installing Cypress

You need Node.js 18 or newer.

Bash
mkdir cypress-demo && cd cypress-demo
npm init -y
npm install --save-dev cypress
npx cypress open

cypress open launches the Cypress app. Choose E2E Testing, accept the default configuration files, pick a browser, and Cypress creates this structure:

Text
cypress/
  e2e/            ← your test files (*.cy.js)
  fixtures/       ← static test data (JSON)
  support/
    commands.js   ← custom commands
    e2e.js        ← runs before every test file
cypress.config.js

Configuration

Set a baseUrl so tests can use relative paths:

Javascript
// cypress.config.js
const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    baseUrl: 'https://example.cypress.io',
    viewportWidth: 1280,
    viewportHeight: 800,
  },
});

https://example.cypress.io is Cypress's official demo app — perfect for practice.

Your first test

Javascript
// cypress/e2e/first.cy.js
describe('Kitchen sink actions', () => {
  it('navigates to the actions page and types an email', () => {
    cy.visit('/');

    cy.contains('type').click();
    cy.url().should('include', '/commands/actions');

    cy.get('.action-email')
      .type('fake@email.com')
      .should('have.value', 'fake@email.com');
  });

  it('submits a form and shows a confirmation', () => {
    cy.visit('/commands/actions');

    cy.get('.action-form').find('[type="text"]').type('HALFOFF');
    cy.get('.action-form').submit()
      .next()
      .should('contain', 'Your form has been submitted!');
  });
});

Select first.cy.js in the Test Runner and watch it run. Hover over each command in the left panel to see the page as it was at that moment.

How Cypress commands work

Cypress commands don't run immediately — they're queued and executed in order. That's why you can't do this:

Javascript
// ✘ Wrong: cy.get() doesn't return the element
const button = cy.get('button');
button.click();

Chain commands instead, and use .then() when you need the actual value:

Javascript
// ✔ Right
cy.get('.action-email').type('ali@example.com');

cy.get('.action-email').invoke('val').then((value) => {
  expect(value).to.equal('ali@example.com');
});

Running headlessly

Bash
npx cypress run                                   # all specs, headless Electron
npx cypress run --browser chrome                  # in Chrome
npx cypress run --spec "cypress/e2e/first.cy.js"  # one file

Add npm scripts for convenience:

JSON
{
  "scripts": {
    "cy:open": "cypress open",
    "cy:run": "cypress run"
  }
}

Testing a real login

Change baseUrl to https://the-internet.herokuapp.com (a public practice site) and add:

Javascript
// cypress/e2e/login.cy.js
describe('Login', () => {
  beforeEach(() => {
    cy.visit('/login');
  });

  it('logs in with valid credentials', () => {
    cy.get('#username').type('tomsmith');
    cy.get('#password').type('SuperSecretPassword!', { log: false });
    cy.get('button[type="submit"]').click();

    cy.location('pathname').should('eq', '/secure');
    cy.get('#flash').should('contain', 'You logged into a secure area!');
  });

  it('rejects a wrong password', () => {
    cy.get('#username').type('tomsmith');
    cy.get('#password').type('wrong-password');
    cy.get('button[type="submit"]').click();

    cy.get('#flash').should('contain', 'Your password is invalid!');
    cy.location('pathname').should('eq', '/login');
  });
});

{ log: false } keeps the password out of the command log and screenshots. Next: choosing selectors that don't break and writing strong assertions in Selectors & Assertions.

Finished this lesson?
Tick it off to track your Cypress progress.
Next lesson