How to create custom commands with Cypress
Tags: cypress, testing, automtion, e2e
Published: 2021-02-10 22:12:53

## Today in “Pinches of Cypress”, learn how to create reusable code

A lot of people have been asking me to talk about "**Page Objects** or **App Actions**?"

To those who want to understand better about the subject, I recommend the [**official source**](https://www.cypress.io/blog/2019/01/03/stop-using-page-objects-and-start-using-app-actions/) (Cypress.io's blog), to not mention more of the same.

In this post, I won't talk about **Page Objects** versus **App Actions**. The focus here will be **CUSTOM COMMANDS**, where some of them can be considered **App Actions**.

To make things clear, I'll use the [**wlsf82/gitlab-cypress**](https://github.com/wlsf82/gitlab-cypress) project as an example.

In this project, I decided to separate the custom commands between commands that occur at the **API** level (which can be considered App Actions) and those that occur via **GUI** (which are just GUI custom commands.)

Inside the `cypress/support/` directory, I have the following files' structure.

```
cypress/support/
  api_commands.js
  gui_commands.js
  e2e.js
```

> **YES, YOU CAN HAVE AS MANY CUSTOM COMMAND FILES AS YOU WANT!**

And from the `cypress/support/e2e.js` file, I import both `api_commands.js` and `gui_commands.js` files.

```js
import './api_commands'
import './gui_commands'
```

Everything okay so far?

Now I want to add a test that validates a project's creation (via API).

Let's look at the test implementation.

```js
const faker = require('faker')

describe('Create project', () => {
  before(() => cy.api_deleteProjects()) // I'll talk about the before function in another post

  it('successfully', () => {
    const project = {
      name: faker.random.word() // I will talk about the use of faker in another post as well, explaining its use.
    }

    cy.api_createProject(project).then(response => {
      expect(response.status).to.equal(201)
      expect(response.body.name).to.equal(project.name)
    })
  })
})
```

And the app action's implementation (or the custom command that creates such a resource via an API call.)

```js
const accessToken = Cypress.env('gitlab_access_token')

Cypress.Commands.add('api_createProject', project => cy.request({
  method: 'POST',
  url: `/api/v4/projects/?private_token=${accessToken}`,
  body: { name: project.name }
})
```

> To create a custom command, use the `.add` function, from `Cypress.Commands` module, where the first argument is the custom command name, e.g., `api_createProject`; and the second argument is the callback function that implements the logic for doing what the custom command is supposed to do.
>
> In the case of the `api_createProject` custom command, it receives a `project` object as an argument, and runs a `cy.request` command to create such resource via an API call, using the `project`'s `name` attribute.
>
> Also, it uses the `accessToken` variable defined outside of the custom command, so it has the right to make the API call.

Okay, the [**API testing**](https://dev.to/walmyrlimaesilv/how-to-check-the-body-structure-of-a-json-scheme-with-cypress-1lj5) part is covered.

Now, let's look at an example of a **GUI** test, also for project creation.

```js
const faker = require('faker')

describe('Create project', () => {
  beforeEach(() => {
    cy.api_deleteProjects()
    cy.sessionLogin()
  })

  it('successfull', () => {
    const project = {
      name: faker.random.uuid(),
      description: faker.random.words(5)
    }

    cy.gui_createProject(project)

    cy.url().should(
      'be.equal',
      `Cypress.config('baseUrl')${Cypress.env('user_name')}/${project.name}`
    )
    cy.contains(project.name).should('be.visible')
    cy.contains(project.description).should('be.visible')
  })
})
```

And the implementation of the `gui_createProject` custom command.

```js
Cypress.Commands.add('gui_createProject', project => {
  cy.visit('projects/new')

  cy.get('#project_name').type(project.name)
  cy.get('#project_description').type(project.description)
  cy.get('.qa-initialize-with-readme-checkbox').check()
  cy.contains('Create project').click()
})
```

> Similarly to the `api_createProject` custom command, the `gui_createProject` custom command is also defined by the `.add` function of the `Cypress.Commands` module; it also receives a `project` object as argument, but its implementation uses Cypress commands that interact with the app as users would, such as `cy.visit()`, `cy.get(...).type(...)`, `cy.contains(...).click()`, etc.
>
> Notice that it uses the `project`'s `name` and `descriptions` attributes for typing into text input fields.

Done! We now have the project creation functionality covered at both the API and GUI levels!

> For details about the `api_deleteProjects` and `sessionLogin` custom commands, read the [official project's code](https://github.com/wlsf82/gitlab-cypress/tree/main).

___

Any thoughts about the series?

I'm looking forward to hearing your feedback!

___

This post was originally published in Portuguese on the [**Talking About Testing**](https://talkingabouttesting.com/2021/02/10/como-criar-comandos-customizados-com-cypress/) blog.
___

Would you like to learn about **test automation with Cypress**? Get to know my online courses on [**Udemy**](https://udemy.com/user/walmyr).