in a Nutshell

Amir Rustamzadeh

Sr. Engineer

@amirrustam

WEBCAST

What is Cypress?

A tool for reliably testing anything that runs in web browser.

Free & Open Source

MIT License

Our mission is to build a thriving, open source ecosystem that enhances productivity, makes testing an enjoyable experience, and generates developer happiness. We hold ourselves accountable to champion a testing process that actually works.

Why adopt Cypress?

Non-technical Reasons

Growth in Popularity & Adoption

⭐️ 13K +

πŸ“¦ 530K +

GitHub Stars

NPM Weekly Downloads

Growth in Popularity & Adoption

Growth in Popularity & Adoption

Growing demand for testing skills and specifically Cypress skills within the job marketplace. Β 

Sustainable Open Source

Let's Dive In

Setup & Installation

$ npm install -D cypress
$ npm install -D cypress

Desktop App

CLI

$ npx cypress open

Open Desktop App

  • πŸ“¦ My Project

    • πŸ“ ...

    • πŸ“‚ cypress

      • πŸ“‚ fixtures

      • πŸ“‚ integration

      • πŸ“‚ plugins

      • πŸ“‚ support

Your App

Command Log

Time-Travel

API

Intuitive & English-like

API

cy.<command>

API

cy.get('button')

API

cy.get('button')
Β  .click()
  .should('have.class', 'active')

API

cy.request('/users/1')
Β  .its('body')
  .should('deep.eql',{ name:'Amir'})

API

cy.get('button')
Β  .click()
  .should('have.class', 'active')

Subject is passed

through the chain

it('send email with contact form', () => {
    cy.get('#name-input').type('Amir')
    cy.get('#email-input').type('amir@cypress.io')
    cy.get('form').submit()
    cy.get('#success-message').should('be.visible')
})
βœ…
βœ…
βœ…
βœ…
    cy.get('#name-input').type('Amir')
    cy.get('#email-input').type('amir@cypress.io')
    cy.get('form').submit()
    cy.get('#success-message').should('be.visible')

API

Test commands are executed in a deterministic manner. Resulting in flake-free testing.

API

cy.get('button')
Β  .click()
  .should('have.class', 'active')

Cypress will automatically wait for this assertion (4 seconds by default)

Let's dive deeper

🐦 BirdBoard

The Twitter client no one needs

🐦 BirdBoard

🐦 BirdBoard

it('signup and login user', () => {
  cy.visit('http://localhost:8080/signup')

  cy.get('input[name="email"]').type('amir@cypress.io')
  cy.get('input[name="password"]').type('1234')
  cy.get('input[name="confirm-password"]').type('1234')
  cy.get('#signup-button').click()

  cy.location('pathname').should('eq', '/login')

  cy.get('input[name="email"]').type('amir@cypress.io')
  cy.get('input[name="password"]').type('1234')
  cy.get('#login-button').click()

  cy.location('pathname').should('eq', '/board')
})

API

cy.task

Execute JS on the system (outside of the browser)

// cypress/plugins/index.js

const { clearDatabase } = require('../../server/db')

module.exports = (on, config) => {
  on('task', {
    'clear:db': () => {
      return clearDatabase()
    }
  })
}
context('User setup', () => {
  beforeEach(() => {
    cy.task('clear:db')
  })

  it('signup and login user', () => {
    cy.visit('http://localhost:8080/signup')

    cy.get('input[name="email"]').type('amir@cypress.io')
    cy.get('input[name="password"]').type('1234')
    cy.get('input[name="confirm-password"]').type('1234')
    cy.get('#signup-button').click()

    cy.location('pathname').should('eq', '/login')

    cy.get('input[name="email"]').type('amir@cypress.io')
    cy.get('input[name="password"]').type('1234')
    cy.get('#login-button').click()

    cy.location('pathname').should('eq', '/board')
  })
})
context('User setup', () => {
  beforeEach(() => {
    cy.task('clear:db')
  })

  it('signup and login user', () => {
    cy.visit('http://localhost:8080/signup')

    cy.get('input[name="email"]').type('amir@cypress.io')
    cy.get('input[name="password"]').type('1234')
    cy.get('input[name="confirm-password"]').type('1234')
    cy.get('#signup-button').click()

    cy.location('pathname').should('eq', '/login')

    cy.get('input[name="email"]').type('amir@cypress.io')
    cy.get('input[name="password"]').type('1234')
    cy.get('#login-button').click()

    cy.location('pathname').should('eq', '/board')
  })
})
context('User setup', () => {
  beforeEach(() => {
    cy.task('clear:db')
  })

  it('signup and login user', () => {
    cy.visit('http://localhost:8080/signup')

    cy.get('input[name="email"]').type('amir@cypress.io')
    cy.get('input[name="password"]').type('1234')
    cy.get('input[name="confirm-password"]').type('1234')
    cy.get('#signup-button').click()

    cy.location('pathname').should('eq', '/login')

    cy.login('amir@cypress.io', '1234')

    cy.location('pathname').should('eq', '/board')
  })
})
// cypress/support/commands.js

Cypress.Commands.add('login', (email, password) => {
  cy.get('input[name="email"]').type(email)
  cy.get('input[name="password"]').type(password)
  cy.get('#login-button').click()
})

Custom Commands

context('User setup', () => {
  beforeEach(() => {
    cy.task('clear:db')
  })

  it('signup and login user', () => {
    cy.visit('http://localhost:8080/signup')

    cy.get('input[name="email"]').type('amir@cypress.io')
    cy.get('input[name="password"]').type('1234')
    cy.get('input[name="confirm-password"]').type('1234')
    cy.get('#signup-button').click()

    cy.location('pathname').should('eq', '/login')

    cy.login('amir@cypress.io', '1234')

    cy.location('pathname').should('eq', '/board')
  })
})
// cypress/plugins/index.js

const { clearDatabase, seedDatabase } = require('../../server/db')

module.exports = (on, config) => {
  on('task', {
    'clear:db': () => {
      return clearDatabase()
    }
  })

  on('task', {
    'seed:db': (data) => {
      return seedDatabase(data)
    }
  })
}
const userSeed = require('../../server/seed/users')

context('User setup', () => {
  beforeEach(() => {
    cy.task('clear:db')
    cy.task('seed:db', userSeed.data)
  })

  it('login user', () => {
    cy.visit('http://localhost:8080/login')

    cy.login('amir@cypress.io', '1234')

    cy.location('pathname').should('eq', '/board')
  })
})
// cypress/support/commands.js

Cypress.Commands.add('loginWithUI', (email, password) => {
  cy.get('input[name="email"]').type(email)
  cy.get('input[name="password"]').type(password)
  cy.get('#login-button').click()
})

Cypress.Commands.add('login', (email, password) => {
  return cy.window().then(win => {
    return win.app.$store.dispatch('login', {
      email: 'amir@cypress.io',
      password: '1234'
    })
  })
})

Login Custom Command

const userSeed = require('../../server/seed/users')

context('BirdBoard', () => {
  beforeEach(() => {
    cy.task('clear:db')
    cy.task('seed:db', userSeed.data)

    cy.visit('http://localhost:8080/login')

    cy.login('amir@cypress.io', '1234')
  })

  it('load tweets for selected hashtags', () => {
    cy.server()

    // Fixture is stored in cypress/fixtures/tweets.json
    cy.route('GET', '/tweets*', 'fixture:tweets')
      .as('tweets')

    cy.get('#hashtags')
      .type('javascript{enter}')
      .type('cypressio{enter}')

    cy.window().then(win => {
      cy.wait('@tweets')
        .its('response.body.tweets')
        .should('have.length', win.app.$store.state.tweets.length)
    })
  })
})

Stubbing Network Response with Fixtures

Stubbing Network Response with Fixtures

const userSeed = require('../../server/seed/users')

context('BirdBoard', () => {
   // ....

  it('load tweets for selected hashtags', () => {
    cy.server()

    // Fixture is stored in cypress/fixtures/tweets.json
    cy.fixture('tweets').then((tweets) => {
      cy.route({
        url: '/tweets*',
        response: tweets,
        delay: 3000, // simulate slow response
        status: 404  // simulate error scenarios 
      })
      .as('tweets')
    })

    cy.get('#hashtags')
      .type('javascript{enter}')
      .type('cypressio{enter}')

    cy.window().then(win => {
      cy.wait('@tweets')
        .its('response.body.tweets')
        .should('have.length', win.app.$store.state.tweets.length)
    })
  })
})

Stubbing Network Response with Fixtures

How do you run Cypress in CI?

Headless Mode

$ npx cypress run

Test Run Video Recording

Out-of-the-Box

$ npx cypress run

Record Results to

Cypress Dashboard

$ npx cypress run --record

Cypress Dashboard

Test Run Status & Performance Details

Organized & Shareable Error Reports

Cypress Dashboard

Optimize CI Usage with Parallelization

$ npx cypress run --record --parallel

Docs

πŸ“š docs.cypress.io

Example Recipes

Roadmap

🀝

Community

Gitter

on.cypress.io/chat

in a Nutshell

Amir Rustamzadeh

Sr. Engineer

@amirrustam

WEBCAST

Thank You

Feel free to ask questions on Twitter

or via email amir@cypress.io

Cypress In a Nutshell

By Amir Rustamzadeh

Cypress In a Nutshell

A live webcast to see a bird's eye view of the Cypress landscape, and to help you get up and running with testing a real application with Cypress.

  • 2,927