mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-11 23:49:00 +08:00
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:
parent
e0be48a527
commit
66b423588e
6
.gitignore
vendored
6
.gitignore
vendored
@ -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
|
||||||
|
@ -1 +1,3 @@
|
|||||||
{}
|
{
|
||||||
|
"video": false
|
||||||
|
}
|
||||||
|
45
frontend/cypress/CustomFunctions/Login.ts
Normal file
45
frontend/cypress/CustomFunctions/Login.ts
Normal 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;
|
35
frontend/cypress/fixtures/defaultApp.json
Normal file
35
frontend/cypress/fixtures/defaultApp.json
Normal 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
|
||||||
|
}
|
||||||
|
]
|
@ -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"
|
|
||||||
}
|
|
@ -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 {};
|
|
26
frontend/cypress/integration/appLayout/index.spec.ts
Normal file
26
frontend/cypress/integration/appLayout/index.spec.ts
Normal 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 {};
|
62
frontend/cypress/integration/metrics/index.spec.ts
Normal file
62
frontend/cypress/integration/metrics/index.spec.ts
Normal 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 {};
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
5
frontend/src/lib/convertToNanoSecondsToSecond.ts
Normal file
5
frontend/src/lib/convertToNanoSecondsToSecond.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const convertToNanoSecondsToSecond = (number: number) => {
|
||||||
|
return parseFloat((number / 1000000).toString()).toFixed(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default convertToNanoSecondsToSecond;
|
@ -17,6 +17,7 @@
|
|||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"baseUrl": "./src"
|
"baseUrl": "./src",
|
||||||
|
"downlevelIteration": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user