E2E testing with Cypress


Every programmer likes to write some code, and every professional developer apart from writing code likes to automate his routine work process and prefers the “don’t repeat yourself” rule.

I think everyone would agree that repeating the same things is boring, and every minute and energy would be better spent on learning new things and learning or just focusing on further problems to resolve.

Think how many times when creating a new UI component you repeated tests such as:

  • if each case of validation in the form works correctly and returns correct error message
  • if the cookie bar is working properly and if it saves data in cookies
  • whether all combinations work in the purchasing process for different options, such as different delivery methods, and different payment type, and whether a minor change had an impact on any of these processes

…or if you feel that checking manually is spending too much time on you and you don’t want to repeat it manually every single time.

I guarantee that after reading you will be able to create the first basic end-to-end tests yourself, which will save you a lot of time, improve work and you will be able to use the attention devoted to clicking on them for more valuable tasks.

You will get to know something about the hottest libraries to e2e tests named Cypress. And even it won’t matter if you want to use it with React, Angular, Vue, jQuery and vanilla JavaScript. You will be able to use it even without matter of the backend technology. 

If you agree with everything above and if you are interested in how you can solve it and what are the differences between E2E and unit tests…

…I want to invite you to keep going with the article and I wish you a pleasant reading.


Few words about manual, E2E and unit tests

First of all I want to briefly describe 3 types of testing.

1. Manual testing ($$$)

The most popular type of testing due to its low entry threshold and availability. Manual testing is done by someone who has a certain understanding of how computers and applications work. The test preparation time is short. The manual tester is able to detect additional errors beyond those that are to be checked. Unfortunately, it is also the longest type of test, depending largely on the competencies of the tester. During the tests, the tester may, unfortunately, make mistakes.

2. End-to-End UI testing ($$)

Passing Cypress functional test

E2E Is a type of test that works like a black box in which we specify the activities to be performed and determine their results. We do not need to know the code inside because in this type of test we are interested in the initial and final state of the application.

They differ from manual tests in that they are performed by a computer, not a live person, and their implementation is a bit more complicated. Because you need to define what element is to be clicked, for example, what is to be entered, where the result should be specified.

Their advantage, however, is that each repeated test is carried out with the same precision and takes a similar amount of time, as in the case of a manual test, we do not have such a guarantee. In addition, we don’t have to spend time clicking, so with one click we can significantly reduce our manual tests and minimize the possibility of unreliability.

3. Unit tests ($)

Unit tests are tests prepared inside the code that are the fastest to execute, take the least time and check the smallest parts of the code, their functions, classes, and objects. But there is one gap they may not be able to detect and can miss tests, and this is the situation that if have wired up all of the different tiny parts together of our application correctly. Therefore, the amount of confidence is limited, there’s not enough flexibility, and after some changes, the UI interface may be broken and unit tests will not be able to notice the bug. 

It doesn’t mean that Unit tests are worse but in our goal, we won’t reach the result using this kind of testing.

Anyway, unit tests are important and you can read something more about them in this link.



  • Greater application’s test coverage
  • Lower cost with higher tests quantity
  • The possibility of detecting additional errors during the testing process due to the occurrence of a different result
  • Minimizing the chance of making a mistake by a manual tester
  • A person not involved in the design and operation of the application may conduct a test without knowledge of the code and scenarios
  • Tests can be run on any environment: local, development, production


  • Slow execution comparing to the unit tests
  • Fragile

The example of test scenario

StepExecution stepThe expected result
1Open the browser and enter
2Check if every single required element of newsletter exist on pageTest should be passed
3Try to submit the newsletter without any data.Validation should not pass
4Type in email input some email without @ char and without domainValidation should not pass and error message about wrong email
5Type correct email and submit form.Validation should pass and all error messages should be reseted.
6Check if after submit the form user see correct information about success.Success form messages should match to defined in tests.

Introduction to cypress

Cypress is a complete end-to-end test tool/application.
You can write your own test code in JavaScript and use it on every browser and any platform.
You can set up Cypress very easily without interfering into the project source files. You even be able to do tests on the production server without having access because the test imitates real user steps.
With Cypress dashboard you will be able to run test without run the code. Also you can test your page using many browser types (basically of installed on your system).

Installation & configuration Cypress

1. Create new folder for project with tests.

mkdir test-pagepro-website

2. Go to the project

cd test-pagepro-website

3. In new project directory use below command to create your node project

npm init -y

4. After init the node project install cypress (Cypress require above 12.0 of the engine “node” version)

yarn add [email protected] -D

Note: The article has been prepared for the Cypress of version 9.x before release of 10.x version there may be couple of changes.

5. After cypress installation and run below command

yarn cypress open  

There should appear new window with the interface like on the screen below:

…and a new folder named cypress/ with four subfolders and the one cypress.json file.

Few words about cypress Files structure

  • cypress/fixtures/ – folder for additional JSON data and other type files that will be used for tests, For example in the case if you want send some jpg or gif into form you can put it on fixtures folder also you can keep data in json file here and separate used data from the written tests. If the content will change you will be able to easily manage and change it
  • cypress/integration/ – collection all of all tests for the project, may be separated based on folder. Usually the preferred extension is .js and the test file name format is test-name.spec.js.
  • cypress/plugins/ – folder when you can extend your tests with some useful plugins for example if you want to create event that will take screen shot of the tests results etc
  • cypress/support/ – This folder contains index.js and commands.js files. This index.js file is run before every single spec file. We will use the ‘cypress-real-events/support’ in our case to support imitate real user events
  • cypress.json – in this file we can save the global configuration like the base url, and set the  default behavior for the cypress.

Writing Your First E2E Test

We will prepare the E2E test to verify form validation of the newsletter on the website.

1. Remove two folders with the examples in the integration folder and create there file named “contact-form.spec.js”


2. Install utility named “cypress-real-events” it will support to imitate the real events.

yarn add cypress-real-events -D

3. Go to the support/index.js file and on the end of the document add the below import to include the “cypress-real-events”

import "cypress-real-events/support";

4. Go to the intergration/contact-form.spec.js and paste below code:

const form = 'form[name="contact-form"]';
const fullNameInput = "#contact_full-name";
const emailInput = "#contact_email";
const companyInput = "#contact_company";
const phoneInput = "#contact_phone";
const descriptionTextarea = "#contact_description";
const actionButton = 'form[name="contact-form"] button';

const checkFieldVisibility = (element) => cy.get(element).should("be.visible");

const checkFieldValidationPass = (element) => {
  cy.get(element).should("not.have.class", "parsley-error");
  cy.get(element).parents(".f-field").should("not.have.class", "has-error");

const checkFieldValidationFailure = (element) => {
  cy.get(element).should("have.class", "parsley-error");
  cy.get(element).parents(".f-field").should("have.class", "has-error");

The above code has included consts with the selectors to easier manage the elements and three utility functions “checkFieldVisibility”, “checkFieldValidationPass” and “checkFieldValidationFailure”. This is not required but in this case, we have functions that will check:

  • if the element exists on the page
  • if the element has the correct validation message
  • if the element loses the error message
  • if the inputs have correct values.

Otherwise, the code inside the function would be repeated, but we want to avoid it.

The new thing that I want you to focus on is:

  cy.get(element).should("not.have.class", "parsley-error");

This line is calling the cypress (“cy”) instance and trying to find elements using “get” method. And then in the next chained method named “should” where the first argument as string determinate the condition and the next one argument is the value that “should or not to be“.

In this case we check if the current element has not included class named “parsley-error“. It will be helpful to check if the validation functionality has added the error to the node element.

For this moment you get to know how to create basic single test for element.

5. Add the code below to the intergration/contact-form.spec.js after the existing code:

describe("Contact form 1", () => {
  it("Check contact form elements exists", () => {

    ].forEach((element) => checkFieldVisibility(element));

As you can see there are functions named “describe” and “it“. These functions provide a way to keep tests easier to read and organized. Cypress gets to know how to interpret the tests. In both functions, the first argument is the string to describe the current process.

In the nested function, we need to use “cy.visit()” function that will open the Pagepro website and will proceed with the test. And because “cy.visit()” is used inside the “it” function that does it, we have to include it only once.
In this case, I used forEach array function and check if the page includes every single element that is needed in this form.

6. Insert inside into the “describe” function next to the “it” function below code:

  it("Check base emptiness validation for inputs", () => {

    [fullNameInput, emailInput, descriptionTextarea].forEach((element) => {

  it("Check validation of the fullname input after fill", () => {
    cy.get(fullNameInput).type("test fullname");

  it("Check validation of the email for not finished value", () => {
    // Fill the email with normal text with space and check error state

  it("Check validation of the email with @ char but without domain", () => {
    // Fill the email with @ but without domain

  it("Check validation if the email has correct value", () => {
    // Fill the email with correct email
    cy.get(emailInput).clear().type("[email protected]");

  it("Check validation of the empty description", () => {

  it("Check validation after fill description input", () => {
    cy.get(descriptionTextarea).clear().type("[email protected]");

As you learned up in previous steps, “cy.get()” catches the target element, and in this case, there is a few very simple methods:

  • type() – imitate the typing on the keyboard passed string value
  • clear() – clear the value inside the inputs etc.
  • click() – base click functionality
  • realClick() – the utility function from the library that we included in support, there is some situation that base click() doesn’t work for making a sure I propose to use realClick() from “cypress-real-events/support

I want you to analyze the code above by yourself. It will be a short exercise by practice to improve your skills how to write and analyze the tests.

7. Try and find out if the tests are working.

yarn cypress open

Then you should see a small window appearing, with the “contact-form-spec.js“.

Click on the “contact-form.spec.js” and then Cypress will run the E2E test in the new window browser. You will be able to see how the program find elements, and then type and click their defined valid requirements in the components testing.

On the left panel, you can find out if everything is passed. And because Cypress uses a browser you can open developer tools, remove, for example, the text input, and then repeat the test to see what happened, or if some test didn’t pass.


In conclusion, while E2E tests may be expensive to maintain, I believe that the taken cost is smaller with each and single further test.
The developer won’t need to repeat and repeat manual test anymore.

We can also avoid the mistakes of missing some step during the testing.

Energy and time spent on manual tests may be used for some other more important things like increasing knowledge.

Also, as you can see, verifying the occurrence of 7 elements, and checking their validation requirements on many data types and values takes only 8 seconds!

I’m sure there is no human, who could test it all faster than… Cypress…

Marek Jakimiuk

Marek, a Frontend Developer at Pagepro since 2017, brings a unique blend of engineering precision and design passion to his role. Marek is known for his advocacy of user experience (UX). He consistently pushes for designs and functionalities that prioritise the user, making him a valuable asset to any project focused on delivering superior digital experiences.

Article link copied

Close button