Feature(FE): cypress base test case are updated (#275)

* chore: video config is updated as it will not generate any video while running cypress

* chore: cypress.env.json is added in the env file

* chore: tsConfig is updated

* feature: Cypress is updated with some of the test cases

* chore: default test case is removed

* chore: convertToNanoSecondsToSecond function is updated

* chore: lock files, node_modules are ignored in git

* test: metric are updated

Co-authored-by: Ankit Nayan <ankit@signoz.io>
This commit is contained in:
palash-signoz 2021-08-27 12:21:24 +05:30 committed by GitHub
parent e0be48a527
commit 66b423588e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 197 additions and 184 deletions

6
.gitignore vendored
View File

@ -1,3 +1,6 @@
node_modules
yarn.lock
deploy/docker/environment_tiny/common_test deploy/docker/environment_tiny/common_test
frontend/node_modules frontend/node_modules
frontend/.pnp frontend/.pnp
@ -23,6 +26,9 @@ frontend/yarn-error.log*
frontend/src/constants/env.ts frontend/src/constants/env.ts
frontend/cypress/**/*.mp4 frontend/cypress/**/*.mp4
# env file for cypress
frontend/cypress.env.json
.idea .idea
**/.vscode **/.vscode

View File

@ -1 +1,3 @@
{} {
"video": false
}

View File

@ -0,0 +1,45 @@
import ROUTES from "constants/routes";
const Login = ({ email, name }: LoginProps): void => {
const emailInput = cy.findByPlaceholderText("mike@netflix.com");
emailInput.then((emailInput) => {
const element = emailInput[0];
// element is present
expect(element).not.undefined;
expect(element.nodeName).to.be.equal("INPUT");
});
emailInput.type(email).then((inputElements) => {
const inputElement = inputElements[0];
const inputValue = inputElement.getAttribute("value");
expect(inputValue).to.be.equals(email);
});
const firstNameInput = cy.findByPlaceholderText("Mike");
firstNameInput.then((firstNameInput) => {
const element = firstNameInput[0];
// element is present
expect(element).not.undefined;
expect(element.nodeName).to.be.equal("INPUT");
});
firstNameInput.type(name).then((inputElements) => {
const inputElement = inputElements[0];
const inputValue = inputElement.getAttribute("value");
expect(inputValue).to.be.equals(name);
});
const gettingStartedButton = cy.get("button");
gettingStartedButton.click();
cy.location("pathname").then((e) => {
expect(e).to.be.equal(ROUTES.APPLICATION);
});
};
export interface LoginProps {
email: string;
name: string;
}
export default Login;

View File

@ -0,0 +1,35 @@
[
{
"serviceName": "frontend",
"p99": 1134610000,
"avgDuration": 744523000,
"numCalls": 267,
"callRate": 0.89,
"numErrors": 0,
"errorRate": 0,
"num4XX": 0,
"fourXXRate": 0
},
{
"serviceName": "customer",
"p99": 734422400,
"avgDuration": 348678530,
"numCalls": 267,
"callRate": 0.89,
"numErrors": 0,
"errorRate": 0,
"num4XX": 0,
"fourXXRate": 0
},
{
"serviceName": "driver",
"p99": 239234080,
"avgDuration": 204662290,
"numCalls": 267,
"callRate": 0.89,
"numErrors": 0,
"errorRate": 0,
"num4XX": 0,
"fourXXRate": 0
}
]

View File

@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@ -1,152 +0,0 @@
/// <reference types="cypress" />
// Welcome to Cypress!
//
// This spec file contains a variety of sample tests
// for a todo list app that are designed to demonstrate
// the power of writing tests in Cypress.
//
// To learn more about how Cypress works and
// what makes it such an awesome testing tool,
// please read our getting started guide:
// https://on.cypress.io/introduction-to-cypress
describe('example to-do app', () => {
beforeEach(() => {
// Cypress starts out with a blank slate for each test
// so we must tell it to visit our website with the `cy.visit()` command.
// Since we want to visit the same URL at the start of all our tests,
// we include it in our beforeEach function so that it runs before each test
cy.visit('https://example.cypress.io/todo');
});
it('displays two todo items by default', () => {
// We use the `cy.get()` command to get all elements that match the selector.
// Then, we use `should` to assert that there are two matched items,
// which are the two default items.
cy.get('.todo-list li').should('have.length', 2);
// We can go even further and check that the default todos each contain
// the correct text. We use the `first` and `last` functions
// to get just the first and last matched elements individually,
// and then perform an assertion with `should`.
cy.get('.todo-list li').first().should('have.text', 'Pay electric bill');
cy.get('.todo-list li').last().should('have.text', 'Walk the dog');
});
it('can add new todo items', () => {
// We'll store our item text in a variable so we can reuse it
const newItem = 'Feed the cat';
// Let's get the input element and use the `type` command to
// input our new list item. After typing the content of our item,
// we need to type the enter key as well in order to submit the input.
// This input has a data-test attribute so we'll use that to select the
// element in accordance with best practices:
// https://on.cypress.io/selecting-elements
cy.get('[data-test=new-todo]').type(`${newItem}{enter}`);
// Now that we've typed our new item, let's check that it actually was added to the list.
// Since it's the newest item, it should exist as the last element in the list.
// In addition, with the two default items, we should have a total of 3 elements in the list.
// Since assertions yield the element that was asserted on,
// we can chain both of these assertions together into a single statement.
cy
.get('.todo-list li')
.should('have.length', 3)
.last()
.should('have.text', newItem);
});
it('can check off an item as completed', () => {
// In addition to using the `get` command to get an element by selector,
// we can also use the `contains` command to get an element by its contents.
// However, this will yield the <label>, which is lowest-level element that contains the text.
// In order to check the item, we'll find the <input> element for this <label>
// by traversing up the dom to the parent element. From there, we can `find`
// the child checkbox <input> element and use the `check` command to check it.
cy
.contains('Pay electric bill')
.parent()
.find('input[type=checkbox]')
.check();
// Now that we've checked the button, we can go ahead and make sure
// that the list element is now marked as completed.
// Again we'll use `contains` to find the <label> element and then use the `parents` command
// to traverse multiple levels up the dom until we find the corresponding <li> element.
// Once we get that element, we can assert that it has the completed class.
cy
.contains('Pay electric bill')
.parents('li')
.should('have.class', 'completed');
});
context('with a checked task', () => {
beforeEach(() => {
// We'll take the command we used above to check off an element
// Since we want to perform multiple tests that start with checking
// one element, we put it in the beforeEach hook
// so that it runs at the start of every test.
cy
.contains('Pay electric bill')
.parent()
.find('input[type=checkbox]')
.check();
});
it('can filter for uncompleted tasks', () => {
// We'll click on the "active" button in order to
// display only incomplete items
cy.contains('Active').click();
// After filtering, we can assert that there is only the one
// incomplete item in the list.
cy
.get('.todo-list li')
.should('have.length', 1)
.first()
.should('have.text', 'Walk the dog');
// For good measure, let's also assert that the task we checked off
// does not exist on the page.
cy.contains('Pay electric bill').should('not.exist');
});
it('can filter for completed tasks', () => {
// We can perform similar steps as the test above to ensure
// that only completed tasks are shown
cy.contains('Completed').click();
cy
.get('.todo-list li')
.should('have.length', 1)
.first()
.should('have.text', 'Pay electric bill');
cy.contains('Walk the dog').should('not.exist');
});
it('can delete all completed tasks', () => {
// First, let's click the "Clear completed" button
// `contains` is actually serving two purposes here.
// First, it's ensuring that the button exists within the dom.
// This button only appears when at least one task is checked
// so this command is implicitly verifying that it does exist.
// Second, it selects the button so we can click it.
cy.contains('Clear completed').click();
// Then we can make sure that there is only one element
// in the list and our element does not exist
cy
.get('.todo-list li')
.should('have.length', 1)
.should('not.have.text', 'Pay electric bill');
// Finally, make sure that the clear button no longer exists.
cy.contains('Clear completed').should('not.exist');
});
});
});
export {};

View File

@ -0,0 +1,26 @@
/// <reference types="cypress" />
import ROUTES from "constants/routes";
describe("App Layout", () => {
beforeEach(() => {
cy.visit(Cypress.env("baseUrl"));
});
it("Check the user is in Logged Out State", async () => {
cy.location("pathname").then((e) => {
expect(e).to.be.equal(ROUTES.SIGN_UP);
});
});
it("Logged In State", () => {
const testEmail = "test@test.com";
const firstName = "Test";
cy.login({
email: testEmail,
name: firstName,
});
});
});
export {};

View File

@ -0,0 +1,62 @@
/// <reference types="cypress" />
import defaultApps from "../../fixtures/defaultApp.json";
import convertToNanoSecondsToSecond from "lib/convertToNanoSecondsToSecond";
describe("Metrics", () => {
beforeEach(() => {
cy.visit(Cypress.env("baseUrl"));
const testEmail = "test@test.com";
const firstName = "Test";
cy
.intercept("GET", "/api/v1//services?start*", { fixture: "defaultApp.json" })
.as("defaultApps");
cy.login({
email: testEmail,
name: firstName,
});
});
it("Default Apps", () => {
cy.wait("@defaultApps");
cy.get("tbody").then((elements) => {
const trElements = elements.children();
expect(trElements.length).to.be.equal(defaultApps.length);
const getChildren = (row: Element): Element => {
if (row.children.length === 0) {
return row;
}
return getChildren(row.children[0]);
};
// this is row element
trElements.map((index, element) => {
const [
applicationElement,
p99Element,
errorRateElement,
rpsElement,
] = element.children;
const applicationName = getChildren(applicationElement).innerHTML;
const p99Name = getChildren(p99Element).innerHTML;
const errorRateName = getChildren(errorRateElement).innerHTML;
const rpsName = getChildren(rpsElement).innerHTML;
const { serviceName, p99, errorRate, callRate } = defaultApps[index];
expect(applicationName).to.be.equal(serviceName);
expect(p99Name).to.be.equal(convertToNanoSecondsToSecond(p99).toString());
expect(errorRateName).to.be.equals(
parseFloat(errorRate.toString()).toFixed(2),
);
expect(rpsName).to.be.equals(callRate.toString());
});
});
});
});
export {};

View File

@ -1,26 +1,12 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
import '@testing-library/cypress/add-commands'; import '@testing-library/cypress/add-commands';
import Login, { LoginProps } from '../CustomFunctions/Login';
Cypress.Commands.add('login', Login);
declare global {
namespace Cypress {
interface Chainable<Subject> {
login(props: LoginProps): void;
}
}
}

View File

@ -1,5 +1,7 @@
{ {
"extends": "../tsconfig.json", "extends": "../tsconfig.json",
"target": "es5",
"lib": ["es5", "dom"],
"compilerOptions": { "compilerOptions": {
"noEmit": true, "noEmit": true,
// be explicit about types included // be explicit about types included

View File

@ -0,0 +1,5 @@
const convertToNanoSecondsToSecond = (number: number) => {
return parseFloat((number / 1000000).toString()).toFixed(2);
};
export default convertToNanoSecondsToSecond;

View File

@ -17,6 +17,7 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"noEmit": true, "noEmit": true,
"baseUrl": "./src" "baseUrl": "./src",
"downlevelIteration": true
} }
} }