Cypress Introduction
Installing Cypress, the Test Runner, and writing your first end-to-end test against a real page.
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.
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:
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:
// 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
// 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:
// ✘ 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:
// ✔ 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
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:
{
"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:
// 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.