Photo by Philip VeaterΒ on Unsplash
Request
Response
Request
Response
npm install cypress --save-dev
π cypress.jsonΒ - all Cypress settings
π cypress/integrationΒ - test files (specs)
π cypress/fixturesΒ - mock data
π cypress/pluginsΒ - extending Cypress
π cypress/supportΒ - shared commands, utilities
npx cypress open
npx cypress run
/// <reference types="cypress" />
// @ts-check
describe('My first test', () => {
it('the home page loads', () => {
cy.visit('localhost:3000')
// use ("selector", "text") arguments
// to "cy.contains"
cy.contains('h1', 'todos')
// or can use regular expression
cy.contains('h1', /^todos$/)
// also good practice is to use data
// attributes specifically for testing
cy.contains('[data-cy=app-title]', 'todos')
})
})
/// <reference types="cypress" />
describe('Adding items to the todo list', () => {
it('adds two items', () => {
cy.visit('localhost:3000')
cy.get('.new-todo').type('first item{enter}')
cy.contains('li.todo', 'first item').should('be.visible')
cy.get('.new-todo').type('second item{enter}')
cy.contains('li.todo', 'second item').should('be.visible')
})
})
/// <reference types="cypress" />
describe('Adding items to the todo list', () => {
beforeEach(() => {
cy.visit('localhost:3000')
})
it('adds two items', () => {
cy.get('.new-todo').type('first item{enter}')
cy.contains('li.todo', 'first item').should('be.visible')
cy.get('.new-todo').type('second item{enter}')
cy.contains('li.todo', 'second item').should('be.visible')
})
})
const addItem = text => {
cy.get('.new-todo').type(`${text}{enter}`)
}
it('can delete an item', () => {
// adds a few items
addItem('simple')
addItem('hard')
// deletes the first item
cy.contains('li.todo', 'simple')
.should('exist')
.find('.destroy')
// use force: true because we don't want to hover
.click({ force: true })
// confirm the deleted item is gone from the dom
cy.contains('li.todo', 'simple').should('not.exist')
// confirm the other item still exists
cy.contains('li.todo', 'hard').should('exist')
})
{
"baseUrl": "http://localhost:3000",
}
beforeEach(() => {
cy.visit('/')
})
{
"todos": [
{
"title": "first item",
"completed": false,
"id": "8872768047"
},
{
"title": "second item",
"completed": false,
"id": "6777843890"
}
]
}
{
"todos": []
}
describe('reset data using XHR call', () => {
// you can use separate "beforeEach"
// hooks or a single one
beforeEach(() => {
cy.request('POST', '/reset', {
todos: []
})
})
beforeEach(() => {
cy.visit('/')
})
it('adds two items', () => {
addItem('first item')
addItem('second item')
cy.get('li.todo').should('have.length', 2)
})
})
describe('reset data using cy.writeFile', () => {
beforeEach(() => {
const emptyTodos = {
todos: []
}
const str = JSON.stringify(emptyTodos, null, 2) + '\n'
// file path is relative to the project's root folder
// where cypress.json is located
cy.writeFile('todomvc/data.json', str, 'utf8')
cy.visit('/')
})
it('adds two items', () => {
addItem('first item')
addItem('second item')
cy.get('li.todo').should('have.length', 2)
})
})
module.exports = (on, config) => {
on('task', {
resetData(dataToSet = DEFAULT_DATA) {
if (!dataToSet) {
dataToSet = DEFAULT_DATA
}
const dbFilename = getDbFilename()
debug('reset data file %s with %o', dbFilename, dataToSet)
if (!dataToSet) {
console.error('Cannot save empty object in %s', dbFilename)
throw new Error('Cannot save empty object in resetData')
}
const str = JSON.stringify(dataToSet, null, 2) + '\n'
fs.writeFileSync(dbFilename, str, 'utf8')
return null
},
...
describe('reset data using a task', () => {
beforeEach(() => {
cy.task('resetData')
cy.visit('/')
})
it('adds two items', () => {
addItem('first item')
addItem('second item')
cy.get('li.todo').should('have.length', 2)
})
})
it('starts with zero items (waits)', () => {
cy.visit('/')
/* eslint-disable-next-line cypress/no-unnecessary-waiting */
cy.wait(1000)
cy.get('li.todo').should('have.length', 0)
})
it('starts with zero items', () => {
// start Cypress network server
// spy on route `GET /todos`
// THEN visit the page
cy.intercept('GET', '/todos').as('todos')
cy.visit('/')
cy.wait('@todos') // wait for `GET /todos` response
// inspect the server's response
.its('response.body')
.should('have.length', 0)
// then check the DOM
// note that we don't have to use
// "cy.wait(...).then(...)"
// because all Cypress commands are flattened
// into a single chain automatically.
// Thus just write "cy.wait(); cy.get();" naturally
cy.get('li.todo').should('have.length', 0)
})
it('starts with zero items (stubbed response)', () => {
// start Cypress network server
// spy on route `GET /todos`
// THEN visit the page
cy.intercept('GET', '/todos', []).as('todos')
cy.visit('/')
cy.wait('@todos') // wait for `GET /todos` response
// inspect the server's response
.its('response.body')
.should('have.length', 0)
// then check the DOM
cy.get('li.todo').should('have.length', 0)
})
it('starts with zero items (fixture)', () => {
// start Cypress network server
// stub route `GET /todos`, return data from fixture file
// THEN visit the page
cy.intercept('GET', '/todos', { fixture: 'empty-list.json' }).as('todos')
cy.visit('/')
cy.wait('@todos') // wait for `GET /todos` response
// inspect the server's response
.its('response.body')
.should('have.length', 0)
// then check the DOM
cy.get('li.todo').should('have.length', 0)
})
it('posts new item to the server', () => {
cy.intercept('POST', '/todos').as('new-item')
cy.visit('/')
cy.get('.new-todo').type('test api{enter}')
cy.wait('@new-item')
.its('request.body')
.should('have.contain', {
title: 'test api',
completed: false
})
})
it('posts new item to the server response', () => {
cy.intercept('POST', '/todos').as('new-item')
cy.visit('/')
cy.get('.new-todo').type('test api{enter}')
cy.wait('@new-item')
.its('response.body')
.should('have.contain', {
title: 'test api',
completed: false
})
})
it('shows loading element', () => {
// delay XHR to "/todos" by a few seconds
// and respond with an empty list
cy.intercept(
{
method: 'GET',
pathname: '/todos'
},
{
body: [],
delayMs: 2000
}
).as('loading')
cy.visit('/')
// shows Loading element
cy.get('.loading').should('be.visible')
// wait for the network call to complete
cy.wait('@loading')
// now the Loading element should go away
cy.get('.loading').should('not.be.visible')
})