Cypress - Automation test with React
Tags: programming, beginners, cypress, typescript
Published: 2022-07-26 09:25:00

Lately at work I was tired of writing code that would end up on a _spaghetti code_ style code base. My code probably would have made a negative contribution to that code base as well.
The basic problem was the company culture. So in the last period I decided to change jobs. Being a frontend developer for a company where '**_value_**' is at the center of everything.

And so I wrote one of my first tests in **Cypress**.
Tests give us reliability and safety, bringing a lot of value. Unfortunately, this is not always immediately perceived by customers or by the companies where one works. They are often seen as a waste of time. But they are a long-term investment.

With Cypress we can write **_e2e tests_** that allow us to verify functional properties and non-functional properties, such as performance or reliability. We can study the flows of our application and verify that everything works as we intended.

Below I will show an example of a test made with Cypress on a _React_ project with _NextJS_.
We install Cypress on our code base. I will be using yarn. 

`yarn add cypress -D`

Add Cypress to the `package.json` scripts field:

`"cypress": "cypress open"`

So we can run in our CLI

`yarn cypress`



![Screen Cypress testing tool](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y4p2uvxwhwbosuje3x63.png)

We choose E2E testing
You can look through the generated examples and the writing your first test.

If we use typescript, under the cypress folder of our project we add a `tsconfig.json` file:

```json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["es5", "dom"],
    "types": ["cypress", "node"]
  },
  "include": ["**/*.ts"]
}

```

![Cypress folder structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3j4z58nlpmvo5qzsux4r.png)

Now let's write a test that simulates adding an item to the cart and checking out the product.


```typescript
/// <reference types="Cypress" />
export {};

context('Step checkout', () => {
  const mockObjectForm = {
    email: 'test@test.com',
    name: 'Pippo',
    surname: 'Pluto',
    address: 'Via Test',
    city: 'Palermo',
    province: 'PA',
    zip: '90100',
    phone: '123456789',
  };

  it('should fill the checkout form and complete the order', () => {
    cy.stepSelectProduct();
    cy.fillFormCheckout(mockObjectForm);

    cy.get('[type="radio"]').check('wire_transfers');
    cy.contains("Conferma l'ordine").click();

    cy.get('.checkout__success__title')
      .should('be.visible')
      .scrollIntoView({ duration: 500 })
      .contains('Il tuo ordine è stato ricevuto');
  });
});

```

`cy.stepSelectProduct()` and `cy.fillFormCheckout(mockObjectForm)` are commands defined on the file `cypress/support/commands.ts`
We can see them as separate functions to be called as needed.

The first is to simulate product selection and addition to the cart. The second is to fill in the shipping address form.

```typescript
/// <reference types="cypress" />

declare global {
  namespace Cypress {
    interface Chainable {
      fillFormCheckout(value: PersonalInfo): Chainable<Element>;
      stepSelectProduct(): Chainable<Element>;
    }
  }
}

export type PersonalInfo = {
  email: string;
  name: string;
  surname: string;
  address: string;
  city: string;
  province: string;
  zip: string;
  phone: string;
};

Cypress.Commands.add('fillFormCheckout', (personal: PersonalInfo) => {
  cy.get('input[name="customerEmail"]').should('be.visible').type(personal.email);

  cy.get('input[name="policyConsent"]').should('be.visible').check();

  cy.get('input[name="shippingAddress.firstName"]').should('be.visible').type(personal.name);
  cy.get('input[name="shippingAddress.lastName"]').should('be.visible').type(personal.surname);
  cy.get('input[name="shippingAddress.line1"]').should('be.visible').type(personal.address);
  cy.get('input[name="shippingAddress.city"]').should('be.visible').type(personal.city);
  cy.get('input[name="shippingAddress.stateCode"]').should('be.visible').type(personal.province);
  cy.get('input[name="shippingAddress.zipCode"]').should('be.visible').type(personal.zip);
  cy.get('input[name="shippingAddress.phone"]').should('be.visible').type(personal.phone);
  cy.contains('Invia').click();
});

Cypress.Commands.add('stepSelectProduct', () => {
  cy.visit('http://localhost:3000/');

  cy.contains('Aggiungi al carrello').scrollIntoView({ duration: 500 }).click();

  cy.contains('Carrello').click();

  cy.get('a[href*="checkout.html"]').scrollIntoView({ duration: 500 }).click();
});

```

`cy.visit` places us on the starting page.
The relative get and contains are used to refer to the elements of the DOM through classes and placeholders and simulate the various events on the page through `click()` and `check()`.
To refer to elements it is recommended to use **placeholders** because they fully reflect the flow that the user will perform when filling out a form or clicking on a button.
Using classes makes our tests more fragile. We should write tests that are closer to the user than the developer.

As an alternative to the 'get' it is possible to use the `testing-library` methods by installing the appropriate library:

`testing-library/cypress
`
In this way we can use for example the `cy.findByPlaceholderText('value')` rather than `cy.get('input [name = "value"]')`

The test will start automatically when the file is saved. If our tests are not long, we will use them more often to test the new features introduced with each new feature or refactoring activity. It is correct to create tests that are not too long and we do not necessarily have to reach 100% coverage.

See ya 👋🏻
Thank you for reading ✨