Gleb Bahmutov PRO
JavaScript ninja, image processing expert, software quality fanatic
Suggested resource: https://slides.com/bahmutov/decks/cypress-introduction
source: https://star-history.t9t.io/
People testing with Selenium / WebDriver / X
Cypress users?
No E2E tests
DOM
storage
location
cookies
Cypress tests run in the same browser
DOM
storage
location
cookies
Cypress acts as a proxy for your app
if (navigator.battery) {
readBattery(navigator.battery)
} else if (navigator.getBattery) {
navigator.getBattery().then(readBattery)
} else {
document.querySelector('.not-support').removeAttribute('hidden')
}
window.onload = function () {
// show updated status when the battery changes
battery.addEventListener('chargingchange', function () {
readBattery()
})
battery.addEventListener('levelchange', function () {
readBattery()
})
}
window.navigator.battery
context('navigator.battery', () => {
it('shows battery status of 50%', function () {
cy.visit('/', {
onBeforeLoad (win) {
// mock "navigator.battery" property
// returning mock charge object
win.navigator.battery = {
level: 0.5,
charging: false,
chargingTime: Infinity,
dischargingTime: 3600, // seconds
addEventListener: () => {}
}
}
})
// now we can assert actual text - we are charged at 50%
cy.get('.battery-percentage')
.should('be.visible')
.and('have.text', '50%')
// not charging means running on battery
cy.contains('.battery-status', 'Battery').should('be.visible')
// and has enough juice for 1 hour
cy.contains('.battery-remaining', '1:00').should('be.visible')
})
})
context('navigator.battery', () => {
it('shows battery status of 50%', function () {
cy.visit('/', {
onBeforeLoad (win) {
// mock "navigator.battery" property
// returning mock charge object
win.navigator.battery = {
level: 0.5,
charging: false,
chargingTime: Infinity,
dischargingTime: 3600, // seconds
addEventListener: () => {}
}
}
})
// now we can assert actual text - we are charged at 50%
cy.get('.battery-percentage')
.should('be.visible')
.and('have.text', '50%')
// not charging means running on battery
cy.contains('.battery-status', 'Battery').should('be.visible')
// and has enough juice for 1 hour
cy.contains('.battery-remaining', '1:00').should('be.visible')
})
})
it('deletes all heroes through UI', () => {
cy.visit('/heroes')
// confirm the heroes have loaded and select "delete" buttons
cy.get('ul.heroes li button.delete')
.should('have.length.gt', 0)
// and delete all heroes
.click({ multiple: true })
})
it('deletes all heroes through UI', () => {
cy.visit('/heroes')
// confirm the heroes have loaded and select "delete" buttons
cy.get('ul.heroes li button.delete')
.should('have.length.gt', 0)
// and delete all heroes
.click({ multiple: true })
})
// App code
constructor(private heroService: HeroService) {
// @ts-ignore
if (window.Cypress) {
// @ts-ignore
window.HeroesComponent = this
}
}
const getHeroesComponent = () =>
cy.window()
.should('have.property', 'HeroesComponent')
const getHeroes = () =>
getHeroesComponent().should('have.property', 'heroes')
const clearHeroes = () =>
getHeroes()
.then(heroes => {
cy.log(`clearing ${heroes.length} heroes`)
// @ts-ignore
heroes.length = 0
})
it('deletes all heroes through app action', () => {
cy.visit('/heroes')
getHeroes().should('have.length.gt', 0)
clearHeroes()
})
Application fetches a new list of fruits every 30 seconds
How do we:
a) confirm the displayed fruits?
b) run the test quickly?
/// <reference types="Cypress" />
describe('intercept', () => {
it('returns different fruits every 30 seconds', () => {
cy.clock()
// return difference responses on each call
// notice the order of the intercepts
cy.intercept('/favorite-fruits', ['kiwi 🥝']) // 3rd, 4th, etc
cy.intercept('/favorite-fruits', { times: 1 }, ['grapes 🍇']) // 2nd
cy.intercept('/favorite-fruits', { times: 1 }, ['apples 🍎']) // 1st
cy.visit('/fruits.html')
cy.contains('apples 🍎')
cy.tick(30000)
cy.contains('grapes 🍇')
// after using the first two intercepts
// forever reply with "kiwi" stub
cy.tick(30000)
cy.contains('kiwi 🥝')
cy.tick(30000)
cy.contains('kiwi 🥝')
cy.tick(30000)
cy.contains('kiwi 🥝')
})
})
Time-traveling debugger
cypress run --record --parallel
{
"retries": {
// Configure retry attempts for `cypress run`
// Default is 0
"runMode": 2,
// Configure retry attempts for `cypress open`
// Default is 0
"openMode": 0
}
}
npm install -D @cypress/code-coverage
// cypress/support/index.js
import '@cypress/code-coverage/support'
// cypress/plugins/index.js
module.exports = (on, config) => {
require('@cypress/code-coverage/task')(on, config)
// add other tasks to be registered here
// IMPORTANT to return the config object
// with the any changed environment variables
return config
}
$ open coverage/lcov-report/index.html
Visit perfecto.io or follow @perfectomobile
By Gleb Bahmutov
Everyone’s talking about Cypress. But they don’t know what they don’t know… Get the inside scoop on Cypress from Distinguished Engineer Gleb Bahmutov. He’ll be joined by Eran Kinsbruner, DevOps Chief Evangelist at Perforce. Together, they’ll dive into how dev teams are testing, learning, and optimizing with one of the fastest-growing front-end automation frameworks. You’ll Learn: - Most advanced features for automating with Cypress. - Best practices for scaling & optimizing with Cypress. - When and how to use Cypress with commercial tools. - What to expect on the roadmap for Cypress.
JavaScript ninja, image processing expert, software quality fanatic