Getting Started with Cypress
Tags: javascript, webdev, beginners
Published: 2021-08-18 20:07:44

**What's Cypress?**
Cypress is a JavaScript-based front-end testing tool. It helps you test the journey that users take from once they visit your website till they leave. You can use Cypress to test what users will see when they visit the website for the first time. Or where they should be redirected to when they click some link or another. Or if cookies get saved when they signup or login. And so many other things. 

**Installation**
To run Cypress, you need to install it as a `dev` dependency into your project and then run `yarn run cypress open`. 
```javascript 
yarn add cypress --dev
yarn run cypress open
```
<small>**Note**: You can also download it from [Cypress.io](https://www.cypress.io/), double click `Cypess.exe` and then select your project.</small>

Once you add Cypress to your project, `cypress` folder and other sup-directories will be added to the root of your project.
What we care about in these sub-directories is integration. You can delete all the files created under that directory and create a new file called first_test.spec.js. This is where we're going to add our first test.

Let's write a test for a function that returns random numbers between 0-90.

```javascript
// 1
const getRandomNum = () => Math.floor(Math.random() * 100);
// 2
describe("test getRandomNum", () => {
  // 3 
  it("should be less than 100", () => {
    // 4
    for (let i = 0; i < 10; i++) {
      expect(getRandomNum()).lessThan(100);
    }
  });
});
```
1- So, we have a function somewhere that returns a number between 0-9.
2- We first write a `describe` block to group all the tests related to `getRandomNum` and name it `test getRandomNum`.
3- Then, we write the `it` block in which we write our test.
4- Finally, we run a for loop to test our function. We should `expect` a number `lessThan` 100 every time we call `getRandomNum()`.

If everything goes well, the test should pass.

![the test successed](https://i.ibb.co/3WCtCc0/success.png)

Now, what if we refactored our code and accidentally made a tiny mistake.
```javascript
function getRandomNum() {
  return Math.floor(Math.random() * 100 + 100); // 100 was added to the number by mistake
}
```
If we run the test again, it should fail. 
![a red failed test](https://i.ibb.co/RgcHYn0/fail.png)

The cool part is that Cypress tells you exactly where the test fails and why it fails. Here in our test, we told Cypress to `expect` `getRandomNum()` to return a number `lessThan` 100, but instead we got 190. So Cypress complained about it to let us know that something went wrong after we refactored the code.  

Cypress is much more powerful than just testing a function that generates random numbers, especially for applications built in React -- for example. It provides you with tools that help you mount your components and test their behavior. It's exactly like simulating how a user uses your website.

Let's pretend that we have a website and that we want to test that the `Contact` link gets redirected to the contact page `onClick`.
```javascript
// 1
describe.only("test contact link", () => {
  // 2
  it("should be redirected to a contact page", () => {
    // 3
    cy.visit("http://localhost:3000");
    // 4
    cy.contains("Contact").click();
    // 5
    cy.url().should("include", "/contact");
  });
});
```
1- Add a `describe` block, and since we `only` care about testing the contact link right now, run that test only.
2- Start the `it` block.
3- First, ask Cypress (cy) to visit/go to the application.
4*- Then we tell it to find the first element that has the text `Contact` and clicks it.
5- Finally, Cypress tests if the the current `url` contains `/contact`.

**Note***: `contains` is case sensitive. If the text is "Contact" but you write "contact"(lower case), the link won't be found and you'll have `a Timed out error`. You can override that behavior by passing `{ matchCase: false }` as an option to `contains`. 
```javascript 
cy.contains("Contact", { matchCase: false }).click()
``` 
Also, note that `contains` finds the first element that has the text "Contact", so if you have two "Contact" links on the page you'll need to use `get` with `contains`. 
```javascript 
cy.get("nav").contains("Contact") // to get the link in the nav
cy.get("footer").contains("Contact") // to get the link in the footer
//Or
cy.contains("nav", "Contact").click();
cy.contains("footer", "Contact").click();
```

What if we have a login page where people can login or see an error message if the login fails... let's test that.

```javascript
describe("Login", () => {
  // we write the code we want to run before each test in a beforeEach block
  beforeEach(() => {
    cy.visit("http://localhost:3000/login");
  });

  it("should succeed with correct credentials ", () => {
    // find the element (input) with id:username and type hajarNasr
    cy.get("#username").type("hajarNasr");
    // find the element (input) with id:password and type uniquePassword
    cy.get("#password").type("uniquePassword");
    // find the element (button) with id:submit-btn and click it
    cy.get("#submit-btn").click();

    // those two lines confirm that the user has logged successfully in and got redirected to homepage
    cy.contains("Welcome hajarNasr");
    cy.url().should("include", "/home");  });
});

// To test that an error msg gets displayed when user enters wrong data.

  it("should fail with wrong credentials ", () => {
    cy.get("#username").type("fakeUser");
    cy.get("#password").type("fakePassword");
    cy.get("#submit-btn").click();

    // this line confirms that user has not logged in because the page doen't contain a welcome message
    cy.should("not.contain", "Welcome fakeUser");
    // and this should confirm that we're still in the login page
    cy.url().should("include", "/login");
    // and that an element with a className error is displayed in red.
    cy.get(".error").should("have.css", "color", "rgb(255, 0, 0)");
  });
```

**References**:
- [docs.cypress](https://docs.cypress.io/guides/overview/why-cypress)