Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: 🎡 Add e2e env var e tests #557

Merged
merged 1 commit into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@ export default defineConfig({
e2e: {
baseUrl: process.env.CYPRESS_BASE_URL || 'http://localhost:3000',
chromeWebSecurity: false,
defaultCommandTimeout: 8000,
defaultCommandTimeout: 15000,
screenshotOnRunFailure: false,
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
video: false,
viewportHeight: 1080,
viewportWidth: 1920
viewportWidth: 1920,
specPattern: (() => {
switch (process.env.TEST_TYPE) {
case 'e2e':
return 'cypress/e2e/**/*.cy.{ts,tsx}';
case 'local':
return 'cypress/local/**/*.cy.{ts,tsx}';
default:
return ['cypress/local/**/*.cy.{ts,tsx}', 'cypress/e2e/**/*.cy.{ts,tsx}'];
}
})()
}
});
82 changes: 82 additions & 0 deletions cypress/e2e/api.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { apiEndpoints } from '../../src/config/api';

describe('API Endpoint Tests', () => {
for (const [name, url] of Object.entries(apiEndpoints)) {
it(`Should GET ${name} (${url}) and verify the response`, () => {
cy.request('GET', url).as(`get${name}`);

cy.get(`@get${name}`).then((response: any) => {

Check warning on line 8 in cypress/e2e/api.cy.ts

View workflow job for this annotation

GitHub Actions / build-and-deploy

Unexpected any. Specify a different type
expect(response.status).to.eq(200);
expect(response.body).to.be.an('object');
expect(response.body).to.have.property('count');
expect(response.body).to.have.property('timeRangeCount');
expect(response.body).to.have.property('results');

expect(response).to.have.property('headers');
expect(response.headers).to.have.property('content-type');
expect(response.headers['content-type']).to.contain('application/json');
});
});
}

it(`Should handle the Network error error for ${apiEndpoints.SITES}`, () => {
cy.intercept('GET', apiEndpoints.SITES, { forceNetworkError: true }).as(apiEndpoints.SITES);

cy.visit('/');

cy.wait(`@${apiEndpoints.SITES}`).then(() => {
cy.get(`body`).contains('ERR_NETWORK').should('be.visible');
});
});

it(`Should handle 500 error for ${apiEndpoints.SITES}`, () => {
cy.intercept('GET', apiEndpoints.SITES, { statusCode: 500 }).as(apiEndpoints.SITES);

cy.visit('/');

cy.wait(`@${apiEndpoints.SITES}`).its('response.statusCode').should('eq', 500);
cy.get(`body`).contains('500: Internal Server Error').should('be.visible');
});

it(`Should handle 404 error for ${apiEndpoints.SITES}`, () => {
cy.intercept('GET', apiEndpoints.SITES, { statusCode: 404 }).as(apiEndpoints.SITES);

cy.visit('/');

cy.wait(`@${apiEndpoints.SITES}`).its('response.statusCode').should('eq', 404);
cy.get(`body`).contains('404: Not Found').should('be.visible');
});
});

describe('API Resilience Tests', () => {
it('Should recover after temporary service unavailability, clicking on Try again button', () => {
cy.intercept('GET', apiEndpoints.SITES, { forceNetworkError: true }).as(apiEndpoints.SITES);
cy.visit('/');
cy.get(`body`).contains('ERR_NETWORK').should('be.visible');

cy.intercept('GET', apiEndpoints.SITES, { forceNetworkError: false }).as('secondTry');

cy.get(`body`).contains('Try again').should('be.visible').click();

cy.wait('@secondTry').its('response.statusCode').should('eq', 200);
});
});

describe('API Concurrency Tests', () => {
it('Should handle multiple concurrent requests', () => {
const endpoints = Object.values(apiEndpoints);

const requests = endpoints.map(
(url) =>
new Cypress.Promise((resolve) => {
cy.request('GET', url).then(resolve);
})
);

cy.wrap(Promise.all(requests)).then((responses: any) => {

Check warning on line 76 in cypress/e2e/api.cy.ts

View workflow job for this annotation

GitHub Actions / build-and-deploy

Unexpected any. Specify a different type
responses.forEach((response) => {
expect(response.status).to.eq(200);
});
});
});
});
56 changes: 56 additions & 0 deletions cypress/e2e/navigation-consistency.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { apiEndpoints } from '../../src/config/api';
import { getTestsIds } from '../../src/config/testIds';
import { ComponentRoutesPaths } from '../../src/pages/Components/Components.enum';
import { ProcessesRoutesPaths } from '../../src/pages/Processes/Processes.enum';
import { SitesRoutesPaths } from '../../src/pages/Sites/Sites.enum';

const sitesPath = `/#${SitesRoutesPaths.Sites}`;
const componentsPath = `/#${ComponentRoutesPaths.Components}`;
const processesPath = `/#${ProcessesRoutesPaths.Processes}`;

describe('Data Consistency Tests', () => {
it('Should maintain data consistency across sites and site detail', () => {
let data;

cy.intercept('GET', apiEndpoints.SITES).as('section');

cy.visit(sitesPath);
cy.wait('@section').then((interception) => {
const response = interception.response.body;
data = response.results[0];

cy.visit(`${sitesPath}/${data.name}@${data.identity}`);
cy.get(`[data-testid=${getTestsIds.siteView(data.identity)}]`).should('be.visible');
});
});

it('Should maintain data consistency across components and component detail', () => {
let data;

cy.intercept('GET', `${apiEndpoints.COMPONENTS}*`).as('section');

cy.visit(componentsPath);
cy.wait('@section').then((interception) => {
const response = interception.response.body;
data = response.results[0];

cy.visit(`${componentsPath}/${data.name}@${data.identity}`);
cy.get(`[data-testid=${getTestsIds.componentView(data.identity)}]`).should('be.visible');
});
});

it('Should maintain data consistency across processes and process detail', () => {
let data;

cy.intercept('GET', `${apiEndpoints.PROCESSES}*`).as('section');

cy.visit(processesPath);
cy.wait('@section').then((interception) => {
const response = interception.response.body;
data = response.results[0];

cy.visit(`${processesPath}/${data.name}@${data.identity}`);
cy.get(`[data-testid=${getTestsIds.processView(data.identity)}]`).should('be.visible');
});
});
});
10 changes: 0 additions & 10 deletions cypress/e2e/skupper-console/shared/errors-pages.cy.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getTestsIds } from '../../../../src/config/testIds';
import { getTestsIds } from '../../../src/config/testIds';

context('Navigation', () => {
beforeEach(() => {
Expand Down Expand Up @@ -56,6 +56,7 @@ context('Navigation', () => {

it('should reload the page and stay on the last page selected', () => {
cy.get(`[data-testid=${getTestsIds.navbarComponent()}]`).contains('Sites').click({ force: true });
cy.wait(500);
cy.reload();
cy.location('hash').should('include', 'sites');

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import processesData from '../../../../mocks/data/PROCESSES.json';
import sitesData from '../../../../mocks/data/SITES.json';
import { getTestsIds } from '../../../../src/config/testIds';
import processesData from '../../../mocks/data/PROCESSES.json';
import sitesData from '../../../mocks/data/SITES.json';
import { getTestsIds } from '../../../src/config/testIds';

const site = sitesData.results[0];
const sitePath = `#/sites/${encodeURIComponent(site.name)}@${encodeURIComponent(site.identity)}`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sitesData from '../../../../mocks/data/SITES.json';
import sitesData from '../../../mocks/data/SITES.json';

context('Sites', () => {
beforeEach(() => {
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@
"test": "yarn test:watch --run",
"test:watch": "USE_MOCK_SERVER=true vitest",
"coverage": "yarn test --coverage",
"cy:open": "cypress open",
"cy:run": "cypress run",
"ci": "start-test 3000 cy:run",
"cy:dev": "cypress open",
"cy:run": "cypress run",
"ci": "TEST_TYPE=local start-test 3000 cy:run",
"ci:e2e": "TEST_TYPE=e2e cypress run",
"lint": "TIMING=1 eslint --cache",
"lint-fix": "yarn lint --fix",
"format": "prettier --write '*/**/*.{ts,tsx,json,css}'",
Expand Down
1 change: 1 addition & 0 deletions src/config/testIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const getTestsIds = {
serviceView: (id: string) => `sk-service-view-${id}`,
topologyView: () => 'sk-topology-view',
notFoundView: () => `sk-not-found-view`,
networkErrorView: () => `sk-network-error-view`,
siteView: (id: string) => `sk-site-view-${id}`,
componentView: (id: string) => `sk-component-view-${id}`,
navbarComponent: () => 'sk-nav-bar-component',
Expand Down
7 changes: 4 additions & 3 deletions src/pages/shared/Errors/Http/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import { FC, MouseEventHandler } from 'react';
import { Button, Divider, List, ListItem, PageSection, Content, ContentVariants, Title } from '@patternfly/react-core';

import { Labels } from '../../../../config/labels';
import { getTestsIds } from '../../../../config/testIds';

const ErrorHttp: FC<{ code?: string; message?: string; onReset?: MouseEventHandler<HTMLButtonElement> }> = function ({
code,
message,
onReset
}) {
return (
<PageSection hasBodyWrapper={false}>
<Content>
<PageSection hasBodyWrapper={false} data-testid={getTestsIds.networkErrorView()}>
<div data-testid={getTestsIds.networkErrorView}>
<Title headingLevel="h1">{message || Labels.HttpError}</Title>
<Title headingLevel="h2">{code || ''}</Title>
<Divider />
Expand Down Expand Up @@ -39,7 +40,7 @@ const ErrorHttp: FC<{ code?: string; message?: string; onReset?: MouseEventHandl
</Button>
</ListItem>
</List>
</Content>
</div>
</PageSection>
);
};
Expand Down
5 changes: 3 additions & 2 deletions src/pages/shared/Errors/NotFound/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Bullseye, PageSection, Content, ContentVariants } from '@patternfly/react-core';

import { Labels } from '../../../../config/labels';
import { getTestsIds } from '../../../../config/testIds';

const NotFound = function () {
return (
<PageSection hasBodyWrapper={false}>
<Bullseye data-testid="sk-not-found-view">
<PageSection hasBodyWrapper={false} data-testid={getTestsIds.notFoundView()}>
<Bullseye>
<Content>
<Content component={ContentVariants.h1}>{Labels.NoFoundError}</Content>
</Content>
Expand Down
Loading