Chapter I - Introduction to Cypress
(avr. time for this chapter: 1 day)
Cypress is a modern, JavaScript-based end-to-end testing framework built for the modern web. Unlike Selenium-based tools, Cypress runs directly in the browser, providing faster execution and better debugging capabilities.
Why Cypress?
Advantages
- Fast: Runs directly in the browser without network lag
- Real-time Reloads: Tests reload automatically on file changes
- Time Travel: Snapshots at each step for debugging
- Automatic Waiting: No need for explicit waits or sleeps
- Network Control: Easy stubbing of API responses
- Screenshots & Videos: Automatic capture on failures
When to Use Cypress
- Testing modern web applications (React, Vue, Angular)
- E2E testing of user workflows
- Component testing
- API testing
- Visual regression testing (with plugins)
Setting Up Cypress
Prerequisites
- Node.js (v18 or higher)
- npm or yarn
- A web application to test
Installation
Create a new project and install Cypress:
# Create project directory
mkdir cypress-demo
cd cypress-demo
# Initialize npm project
npm init -y
# Install Cypress
npm install cypress --save-dev
Opening Cypress
# Open Cypress Test Runner
npx cypress open
On first run, Cypress will create a folder structure:
cypress/
├── e2e/ # Your test files
├── fixtures/ # Test data files
├── support/ # Custom commands and setup
│ ├── commands.js # Custom commands
│ └── e2e.js # Support file loaded before tests
└── downloads/ # Downloaded files during tests
Configuration
Create or modify cypress.config.js:
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 1280,
viewportHeight: 720,
video: true,
screenshotOnRunFailure: true,
defaultCommandTimeout: 10000,
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});
Your First Test
Create a file cypress/e2e/first-test.cy.js:
describe('My First Test', () => {
it('visits the app', () => {
cy.visit('/');
cy.contains('Welcome');
});
});
Running Tests
# Run in interactive mode
npx cypress open
# Run in headless mode (CI)
npx cypress run
# Run specific test file
npx cypress run --spec "cypress/e2e/first-test.cy.js"
Cypress Architecture
Understanding how Cypress works helps write better tests:
┌─────────────────────────────────────────┐
│ Node.js Server │
│ (Runs in background, handles files) │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Browser │
│ ┌─────────────────────────────────┐ │
│ │ Your Application │ │
│ │ (iframe) │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ Cypress Test Runner │ │
│ │ (Same origin) │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
Key points: - Cypress runs inside the browser - Tests have direct access to the DOM - No network latency between test and app - Can intercept and modify network requests
Basic Commands
Navigation
// Visit a URL
cy.visit('/');
cy.visit('https://example.com');
// Go back/forward
cy.go('back');
cy.go('forward');
// Reload page
cy.reload();
Selecting Elements
// By CSS selector
cy.get('.button');
cy.get('#submit-btn');
cy.get('[data-testid="login-form"]');
// By content
cy.contains('Submit');
cy.contains('button', 'Submit');
// By attribute
cy.get('[type="email"]');
cy.get('[name="username"]');
Interacting with Elements
// Click
cy.get('button').click();
cy.get('button').dblclick();
cy.get('button').rightclick();
// Type
cy.get('input').type('Hello World');
cy.get('input').type('{enter}');
cy.get('input').clear().type('New text');
// Select
cy.get('select').select('Option 1');
cy.get('select').select(['Option 1', 'Option 2']);
// Checkbox/Radio
cy.get('[type="checkbox"]').check();
cy.get('[type="checkbox"]').uncheck();
cy.get('[type="radio"]').check('value');
Assertions
// Should assertions
cy.get('h1').should('be.visible');
cy.get('h1').should('contain', 'Welcome');
cy.get('input').should('have.value', 'test@example.com');
cy.get('.error').should('not.exist');
// Multiple assertions
cy.get('button')
.should('be.visible')
.and('be.enabled')
.and('contain', 'Submit');
// Expect assertions
cy.get('h1').then(($el) => {
expect($el.text()).to.equal('Welcome');
});
Exercise: Test a Login Page
Create a simple HTML login page to test:
Step 1: Create Test Application
Create index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login Page</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 400px;
margin: 50px auto;
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
button {
width: 100%;
padding: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
.error {
color: red;
margin-top: 10px;
display: none;
}
.success {
color: green;
margin-top: 10px;
display: none;
}
</style>
</head>
<body>
<h1>Login</h1>
<form id="login-form">
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" data-testid="email-input" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" data-testid="password-input" required>
</div>
<button type="submit" data-testid="login-button">Login</button>
</form>
<p class="error" data-testid="error-message">Invalid email or password</p>
<p class="success" data-testid="success-message">Login successful! Redirecting...</p>
<script>
document.getElementById('login-form').addEventListener('submit', function(e) {
e.preventDefault();
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
document.querySelector('.error').style.display = 'none';
document.querySelector('.success').style.display = 'none';
if (email === 'test@example.com' && password === 'password123') {
document.querySelector('.success').style.display = 'block';
} else {
document.querySelector('.error').style.display = 'block';
}
});
</script>
</body>
</html>
Step 2: Serve the Application
# Install a simple server
npm install -g http-server
# Serve the application
http-server -p 3000
Step 3: Write Cypress Tests
Create cypress/e2e/login.cy.js:
describe('Login Page', () => {
beforeEach(() => {
cy.visit('/');
});
describe('Page Elements', () => {
it('should display the login form', () => {
// TODO: Verify all form elements are visible
});
it('should have empty inputs initially', () => {
// TODO: Verify inputs are empty
});
});
describe('Form Validation', () => {
it('should show error for invalid credentials', () => {
// TODO: Test invalid login
});
it('should require email field', () => {
// TODO: Test email validation
});
it('should require password field', () => {
// TODO: Test password validation
});
});
describe('Successful Login', () => {
it('should show success message with valid credentials', () => {
// TODO: Test successful login
});
});
});
Step 4: Complete the Tests
Fill in the TODO sections with actual test code. Use the commands learned in this chapter.
Self-Assessment
After completing this chapter, you should be able to:
- [ ] Install and configure Cypress
- [ ] Understand Cypress architecture
- [ ] Write basic tests with navigation and assertions
- [ ] Select elements using various strategies
- [ ] Interact with form elements
- [ ] Run tests in interactive and headless modes
Next Steps
Continue to Chapter II - Writing Tests to learn more advanced Cypress patterns and best practices.