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
Cypressto useTypescript. A sampletsconfig.jsonfile 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.jsonfile for easier usage ofCypress"scripts": { "cypress:open": "cypress open", "cypress:run": "cypress run" } -
Base URL must be specified in the
cypress.jsonto 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 new line.
-
Do not leave line breaks after
contextordescribeblocks.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
contextanditblocks.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
camelCasefor file names. -
Each test file name must have the suffix
.specto 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.jsonfile{ "testFiles": ["**/*.spec.ts"] } -
Use
describeto group tests using the file name for the description.// searchByEmail.spec.json describe('Search by email', () => {}) -
Use
contextto describe testing preconditions.contextblock descriptions must always start withwhenorgiven, 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
itblock descriptions withshould. 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.