UI automation is the most important part of the CI/CD(continuous integration and continuous delivery) process. Integration testing helps you to avoid manual regression testing and improve the application quality. In this post I will explain how to implement UI automation testing to existing Angular projects using the Cypress tool, this is a great alternative for Protractor. Take a look at the video tutorials for better understanding.
GitHub Source
System Requirements
- Node - 16.10.0 +
- Angular Cli - 13
- Cypress latest
Install Cypress Globally
npm install -g cypress
Angular Project
Clone the existing Angular project. Start the project using ng serve command. Angular Routing with Lazy Loading Design Pattern
$git clone new https://github.com/srinivastamada/angular-routing.git
$cd create-angular-project
$cd create-angular-project
Add Cypress Plugin
npm install cypress --save-dev
Cypress Setup
Execute the following command inside the project directory. Angular
npx cypress open
React
yarn run cypress open
This will generate a new cypress folder with cypress.json. Here cypress.json is a configuration file.
Delete existing example tests files.
cypress.json
This is a configuration file for Cypress, you can enable and disable the options. Here I have configured default browser view port window to 1200x800 pixels and testFiles pointed to integration folder. Configure baseUrl value based on the project.
{
"baseUrl": "http://localhost:4200",
"testFiles": ["*.*"],
"viewportWidth": 1200,
"viewportHeight": 800,
"screenshotOnRunFailure": true,
"video": true,
"watchForFileChanges": false,
"chromeWebSecurity": false,
"pageLoadTimeout": 60000
}
Note: Video tutorial will give better explanation.
.gitignore
Ignore videos and screenshots to Git.
/cypress/screenshots
/cypress/videos
/cypress/videos
Login Page
Demo Angular routing project by default navigates to #/login route.
login.spec.js
Create a login.spec.js file under the integration folder. Here the test is verifying the page title and route name.
describe('Login page', () => {
beforeEach(() => {
cy.visit('http://localhost:4200')
})
it('should display the login page', () => {
cy.url().should('include', '/login');
cy.get('h1').contains('Login');
});
});
Run Cypress
Go to project path and execute the following command. Make sure the project is up and running with ng server.
$cypress run
Test Success
Success test result.
Test Fail
Just modified cy.get('h1').contains('Sign In') to break the test.
Here you will find the test video and screenshot. I would recommend disable video option, because more tests will create large video recording file. Go to cypress.json and change the video value to false
Login Page Desktop
You will find this login screen for Angular routing demo project.
login.spec.ts
We are implementing more tests to verify page labels and form validations.
describe('Login page', () => {
beforeEach(() => {
cy.visit('http://localhost:4200')
})
it('should display the login page', () => {
cy.url().should('include', '/login');
cy.get('h1').contains('Login');
});
it('should verify page labels', () => {
cy.get('h1').contains('Login');
cy.get('#forgotLink').contains('Forgot password?');
cy.get('#loginButton').contains('Login');
cy.get('#signupMenuLink').contains('Signup');
cy.get('#loginMenuLink').contains('Login');
});
it('should verify login validation', () => {
cy.get('#loginButton').click();
cy.get('.has-text-danger').contains('Please give valid data');
});
it('should check with invalid login inputs', () => {
cy.get('#loginEmail').focus().clear().type('srinivas');
cy.get('#loginPassword').focus().clear().type('12345');
cy.get('#loginButton').click();
cy.get('.has-text-danger').contains('Please give valid data');
});
it('should check with valid login inputs', () => {
cy.get('#loginPassword').focus().clear().type('12345');
cy.get('#loginButton').click();
});
it('should navigate to dashboard page', () => {
cy.get('h1').contains('Dashboard');
});
});
Test Result
Mobile View
The login page has different design for mobile/iPad screens.
Test for Multiple Views
We can add tests based on the context. Here you will find two different contexts for Mobile and Desktop versions.
describe("Login page", () => {
const baseUrl = Cypress.config().baseUrl;
context("Mobile/Ipad view", () => {
beforeEach(() => {
cy.viewport(550, 750);
cy.visit(baseUrl);
});
it("should verify page labels for mobile/ipad view", () => {
cy.visit("http://localhost:4200");
cy.get("h1").contains("Login");
cy.get("#forgotLink").contains("Forgot password?");
cy.get("#loginButton").contains("Login");
cy.get(".navbar-burger").should("be.visible");
});
});
context("Desktop", () => {
beforeEach(() => {
cy.visit(baseUrl);
});
it("should display the login page", () => {
cy.url().should("include", "/login");
cy.get("h1").contains("Login");
});
it("should verify page labels", () => {
cy.get("h1").contains("Login");
cy.get("#forgotLink").contains("Forgot password?");
cy.get("#loginButton").contains("Login");
cy.get("#signupMenuLink").contains("Signup");
cy.get("#loginMenuLink").contains("Login");
cy.get(".navbar-burger").should("not.visible");
});
it("should verify login validation", () => {
cy.get("#loginButton").click();
cy.get(".has-text-danger").contains("Please give valid data");
});
it("should check with invalid login inputs", () => {
cy.get("#loginEmail").focus().clear().type("srinivas");
cy.get("#loginPassword").focus().clear().type("12345");
cy.get("#loginButton").click();
cy.get(".has-text-danger").contains("Please give valid data");
});
it("should check with valid login inputs", () => {
cy.get("#loginPassword").focus().clear().type("12345");
cy.get("#loginButton").click();
});
it("should navigate to dashboard page", () => {
cy.get("h1").contains("Dashboard");
});
});
});
commands.js
Cypress global commands file. Here you implement your own commands for reusable actions. Here we implemented a login command for upcoming tests.
Cypress.Commands.add("login", (email, password) => {
const baseUrl = Cypress.config().baseUrl;
cy.visit(baseUrl);
cy.get("#loginEmail").focus().clear().type(email);
cy.get("#loginPassword").focus().clear().type(password);
cy.get("#loginButton").click();
});
Dashboard Page
Accessing the Dashboard page needed a user authentication. You have to use cy.login action to navigate the dashboard page.
dashboard.spec.js
describe("Dashboard page", () => {
beforeEach(() => {
});
it("should display the dashboard page", () => {
cy.get("h1").contains("Dashboard");
});
Settings Page
This is similar like the dashboard page. Initially the page is landing on dashboard page and based on the menu link settings link action, the settings page will appear. settings.spec.js
describe("Settings page", () => {
beforeEach(() => {
cy.get("#settingsLink").click();
});
it("should display the settings page", () => {
cy.wait(500);
cy.url().should("include", "/settings");
cy.get("h1").contains("Settings");
});
});
This way you can implement tests for your components to product your work. When you are working the project with multiple developers.
Thanks, now I am going to use cypress 😀
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThank you for sharing so knowledgeable information !
ReplyDeleteIn 7 ways cypress is different
Cypress does not use Selenium.
Cypress focuses on doing end-to-end testing REALLY well.
Cypress works on any front-end framework or website.
Cypress tests are only written in JavaScript.
Cypress is all in one.
Cypress is for developers and QA engineers.
Cypress runs much, much faster.
Your blog is very informative and I appreciate you sharing it with us! Please keep up the good work, because this information about cypress will be helpful for many people.
ReplyDelete