Cypress 🤖

Hero image for Cypress 🤖

The team prefers using Cypress for end-to-end testing on their front-end applications. It is easy to set up, and it provides some modern features which are convenient while writing end-to-end tests.

Configuration

  • The team uses Typescript when building JavaScript applications. After installing Cypress and Typescript, configure Cypress to use Typescript. A sample tsconfig.json file looks like below

    {
      "compilerOptions": {
        "target": "es5",
        "lib": ["es5", "dom"],
        "types": ["cypress"],
        "moduleResolution": "node",
        "resolveJsonModule": true
      },
      "include": [
        "**/*.ts"
      ],
      "files": [
        "./support/index.d.ts"
      ]
    }
    
  • Some scripts should be added to the package.json file for easier usage of Cypress

    "scripts": {
      "cypress:open": "cypress open",
      "cypress:run": "cypress run"
    }
    
  • Base URL must be specified in the cypress.json to avoid repetitive definition of base URL in each test.

    {
      "baseUrl": "http://localhost:3000",
    }
    

Formatting

  • Use soft-tabs with a two-space indent.
  • Limit each line of code to fewer than 130 characters.
  • End each file with a newline.
  • Do not leave line breaks after context or describe blocks.

    describe('User authentication', () => {
    
      context('given valid user credentials', () => {
    
        it('redirects to the landing page', () => {})
      })
    })
    
    describe('User authentication', () => {
      context('given valid user credentials', () => {
        it('redirects to the landing page', () => {})
      })
    })
    
  • Leave one line return around context and it blocks.

    describe('User authentication', () => {
      context('given valid user credentials', () => {
        it('redirects to the landing page', () => {
          ...
        })
        it('shows user menu', () => {
          ...
        })
      })
      context('given INVALID user credentials', () => {
        it('shows login error', () => {
          ...
        })
      })
    })
    
    describe('User authentication', () => {
      context('given valid user credentials', () => {
        it('redirects to the landing page', () => {
          ...
        })
    
        it('shows user menu', () => {
          ...
        })
      })
    
      context('given INVALID user credentials', () => {
        it('shows login error', () => {
          ...
        })
      })
    })
    

Naming

  • Use camelCase for file names.
  • Each test file name must have the suffix .spec to distinguish test files from source files.

    integration/
    ├── UserAuthentication/
    │   ├── login.spec.ts
    │   ├── signup.spec.ts
    ├── UserSearch/
    │   ├── searchByEmail.spec.ts
    support/
    ├── commands.ts/
    

    To enforce this rule a configuration should be added in the cypress.json file

    {
       "testFiles": ["**/*.spec.ts"]
    }
    
  • Use describe to group tests using the file name for the description.

    // searchByEmail.spec.json
    
    describe('Search by email', () => {})
    
  • Use context to describe testing preconditions. context block descriptions must always start with when or given, and be in the form of a sentence with proper grammar.

    it('redirects to landing page if user credentials are valid', () => {})
    
    context('given valid user credentials', () => {
      it('redirects to the landing page', () => {})
    })
    
  • Do NOT prefix it block descriptions with should. Use the imperative tone instead.

    it('should redirect to landing page', () => {})
    
    it('redirects to the landing page', () => {})
    

Element selector

Avoid using highly brittle HTML selectors that are subject to change with target elements. Use data-test-id attributes to provide context to the selectors and insulate them from CSS or JS changes.

<div data-test-id="userProfile"></div>

Functions

Use ES6 arrow function for describe/context/it block

describe('User authentication', function() {
  context('given valid user credentials', function() {
    it('redirects to the landing page', function() {})
  })
})
describe('User authentication', () => {
  context('given valid user credentials', () => {
    it('redirects to the landing page', () => {})
  })
})

Stub HTTP requests/responses

From Cypress v6.0.0 a new command cy.intercept() has been added to stub HTTP requests and responses. This command can be used to specify HTTP fixtures as well.

cy.intercept('http://example.com/widgets', { fixture: 'widgets.json' })

All information about this command can be found in the documentation.

Custom commands

Cypress supports user-defined commands. Custom commands are beneficial for automating a workflow repeated in tests. It can significantly DRY-up tests.

If an application requires login on each page, then this can be made into a custom command

Cypress.Commands.add('login', () => {
  cy.visit('/login')
  cy.get('input[name=email]').type('[email protected]')
  cy.get('input[name=password]').type(`'password'{enter}`)
})

Then this command can be easily used in tests as cy.login(). When using typescript adding a custom command requires a TypeScript definitions file. More can be found in the documentation.