Lab 07: Cypress Testing - andreaswissel/design-systems-workshop GitHub Wiki

Hint: if you got lost, you can always check out the corresponding branch for the lab. If you want to start over, you can reset it by typing executing git reset --hard origin/lab-6

Lab

So we have the documentation covered - which is a very good thing! But somehow we need to keep tabs on our components and make sure that they're actually working the way we intend them to. This is where Cypress comes into play.

Adding Cypress

  • We will start by adding Cypress to the project.

Execute the following command in the Angular project:

ng add @cypress/schematic
  • Answer the upcoming prompts with "Yes"

You will be prompted by the CLI:

  1. Answer The package @cypress/[email protected] will be installed and executed. Would you like to proceed? with Yes.
  2. Answer Would you like the default ng e2e command to use Cypress? with Yes.
  3. Answer Would you like to add Cypress component testing? This will add all files needed for Cypress component testing. with Yes.

This will add Cypress to the project and overwrite the default ng e2e command. This is only relevant in projects that use an Angular CLI version prior to version 13.

  • Run cypress with ng e2e

After cypress has started, you should see the Cypress control panel:

In the control panel, you can see a first integration test which was generated by the Cypress setup. In the top right corner, you can select a browser which will be used to execute the tests. Select a browser of your choice!

  • Run your first integration test

Click on cypress/e2e/spec.cy.ts to execute the spec. Cypress will then open up your chosen browser and run the test. This test will fail, but this is intended. Since our page does not contain the sentence specified in the test, it will throw an error.

  • Fix the test

Open up cypress/e2e/spec.cy.ts in your IDE and edit the contents:

describe('My First Test', () => {
  it('Visits the initial project page', () => {
    cy.visit('/')
    cy.contains('Welcome')
-    cy.contains('app is running!')
+    cy.contains('Hello, design-systems')
  })
})

Once you've corrected the application name, run the test again. It should now show up as passed!

Testing the ButtonComponent

Cypress is up and running and you have already wrote a successful integration test. Look at you go! Let's move on to testing our components now.

  • Run Storybook

In a separate terminal instance, run the following command:

npm run storybook

Storybook should now upen up simultaneously.

  • Add a new test

We don't want things to get cluttered, so we will now add a new file for our button component tests. Create a new file called cypress/e2e/button.cy.ts and add the following contents:

describe("The ButtonComponent", () => {
  it("Checks that the button element exists and is visible", () => {
    cy.visit(
      "http://localhost:6006/iframe.html?id=components-button--default-button&viewMode=story"
    );
    cy.get("app-button").should("exist");
    cy.get("app-button").should("be.visible");
  });
});

This code does three main things:

  1. Visit the story in Storybook

Cypress navigates the browser to the ButtonComponent story in our local Storybook instance. There's one key difference to how we usually view stories, though. The link above points to the contents of the Canvas iframe, rather than Storybook as a whole. You can grab this link by clicking the Open canvas in new tab button in Storybook:

  1. Asserts that the button exists

We tell Cypress to get a hold of the <app-button> element and verify that it exists in the DOM.

  1. Asserts that the button is visible

This is different from the previous assertion, in the way that Cypress now checks if the button is actually there from a users perspective. While .should('exist') will probably lead to the same result most of the time, there is some cases, where the result of these two tests will differ from each other. So always make sure to check both!

  • Run the test

In the Cypress control panel, run your new test. Your result should look like this:

Writing a component test

Component tests make it very easy to test components in isolation and are a fast and efficient way to write integration tests for your components. So let's add one!

  • Add a file called button.cy.ts in your component folder

We locate this file right next to the component, since it only relates to this component and, unlike e2e test, doesn't test or touch any other parts of our application.

  • Write the component test
import { ButtonComponent } from './button.component';

describe('The ButtonComponent', () => {
  beforeEach(() => {
    cy.mount(ButtonComponent);
  });

  it('checks that the button element exists and is visible', () => {
    cy.get('button').should('exist');
    cy.get('button').should('be.visible');
  });


  it('should check that the button is rendering correctly', () => {
    cy.get('button').should('have.css', 'background-color', 'rgb(0, 133, 252)');
    cy.get('button').should('have.css', 'border-color', 'rgb(3, 87, 163)');
  });
});

This test verifies that the button exists and has the correct colors. But we learned before that component tests are mainly used for testing the behavior of a component. So let's try to add a label to the button.

  • Control the label property in the component test
import { ButtonComponent } from './button.component';

describe('The ButtonComponent', () => {
  beforeEach(() => {
    cy.mount(ButtonComponent, {
+      componentProperties: {
+        label: 'Click Me',
+      }
    });
  });

  it('checks that the button element exists and is visible', () => {
    cy.get('button').should('exist');
    cy.get('button').should('be.visible');
  });

  it('should check that the button is rendering correctly', () => {
    cy.get('button').should('have.css', 'background-color', 'rgb(0, 133, 252)');
    cy.get('button').should('have.css', 'border-color', 'rgb(3, 87, 163)');
  });

+  it('should check that the button label is rendering correctly', () => {
+    cy.get('button').should('have.text', 'Click Me');
+  });
});

Hint: You need to exclude *.cy.ts files from Storybook. You can do that by editing the .storybook/tsconfig.json-file:

{
  "extends": "../tsconfig.app.json",
  "compilerOptions": {
    "types": [
      "node"
    ],
    "allowSyntheticDefaultImports": true
  },
  "exclude": [
    "../src/test.ts",
    "../src/**/*.spec.ts",
+   "../src/**/*.cy.ts",
    "../projects/**/*.spec.ts"
  ],
  "include": [
    "../src/**/*",
    "../projects/**/*"
  ],
  "files": [
    "./typings.d.ts"
  ]
}

Self check

  • The Cypress app should open up when running ng e2e
  • Your integration test for the ButtonComponent should correctly assert that the button is visible
Button E2E test file

cypress/e2e/button.cy.ts

describe("The ButtonComponent", () => {
  it("Checks that the button element exists and is visible", () => {
    const selector = "app-button";
    cy.visitComponentStory("button", "default-button");
    cy.get(selector).should("exist");
    cy.get(selector).should("be.visible");
  });
});
Button component test file

src/button/button.cy.ts

import { ButtonComponent } from './button.component';

describe('The ButtonComponent', () => {
  beforeEach(() => {
    cy.mount(ButtonComponent, {
      componentProperties: {
        label: 'Click Me',
      }
    });
  });

  it('checks that the button element exists and is visible', () => {
    cy.get('button').should('exist');
    cy.get('button').should('be.visible');
  });

  it('should check that the button is rendering correctly', () => {
    cy.get('button').should('have.css', 'background-color', 'rgb(0, 133, 252)');
    cy.get('button').should('have.css', 'border-color', 'rgb(3, 87, 163)');
  });

  it('should check that the button label is rendering correctly', () => {
    cy.get('button').should('have.text', 'Click Me');
  });
});
⚠️ **GitHub.com Fallback** ⚠️