Skip to content

Commit 0ba8e33

Browse files
authored
Merge pull request #1854 from YoofiTT96/e2e-tests
Introduce E2E Tests for Calm Hub UI
2 parents 17f6c47 + 197e3df commit 0ba8e33

22 files changed

+1882
-18
lines changed

calm-hub-ui/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VITE_BASE_URL=http://localhost:5173

calm-hub-ui/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,23 @@ You may also see any lint errors in the console.
1919
Launches the test runner in the interactive watch mode.\
2020
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
2121

22+
### `npm run start-cypress`
23+
24+
End to End tests are written with cypress. Cypress can be run in headless and headed modes.
25+
The above command runs in headed mode and allows the developer to locally run through the spec
26+
files and observe the test runs. [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) queries and paradigm for testing feature
27+
heavily in these tests as these help maintainability by organising tests around how the UI is used
28+
and not how its implemented. [Cypress and its best practices](https://docs.cypress.io/app/core-concepts/best-practices) are also used in writing and updating
29+
these tests.
30+
31+
You need to set an environment variable VITE_BASE_URL which should be the address where the vite server is being run
32+
A default in .env.example has been added. This can also be set in CI to whatever the intended vite port should be
33+
34+
#### Test Stubbing
35+
The tests are all stubbed to return desired responses. These will need to be maintained in tandem with
36+
calm-hub API. This section can be updated when a different and more reliable integration strategy is devised
37+
38+
2239
### `npm run build`
2340

2441
Builds the app for production to the `build` folder.\

calm-hub-ui/cypress.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from "cypress";
2+
import 'dotenv/config';
3+
4+
export default defineConfig({
5+
e2e: {
6+
baseUrl: process.env.VITE_BASE_URL,
7+
},
8+
});

calm-hub-ui/cypress/e2e/adrs.cy.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const expectedNamespace = "finos"
2+
const expectedAdrId = 1;
3+
const expectedAdrRevision = 2;
4+
5+
describe('ADR Tests', () => {
6+
beforeEach(() => {
7+
cy.intercept("/calm/namespaces", {"values": [expectedNamespace]});
8+
cy.intercept("/calm/namespaces/finos/adrs", {"values": [expectedAdrId]});
9+
cy.intercept("/calm/namespaces/finos/adrs/1/revisions", {"values": [expectedAdrRevision]});
10+
cy.intercept(`/calm/namespaces/finos/adrs/1/revisions/${expectedAdrRevision}`, {
11+
fixture: "example-adr"
12+
});
13+
})
14+
15+
it("Displays ADR JSON successfully", () => {
16+
cy.visit("/");
17+
cy.findByText(expectedNamespace).click();
18+
cy.findByText(/adrs/i).click();
19+
cy.findByText(/1/i).click();
20+
cy.findByText(expectedAdrRevision).click();
21+
22+
cy.fixture('example-adr').then(data => {
23+
cy.contains(/id/i).should("exist");
24+
cy.contains(data.id).should("exist");
25+
26+
cy.contains(/namespace/i).should("exist");
27+
cy.contains(data.namespace).should("exist");
28+
29+
cy.contains(data.revision).should("exist");
30+
31+
cy.contains(data.adr.title).should("exist");
32+
cy.contains(data.adr.status, {
33+
matchCase: false
34+
}).should("exist");
35+
cy.contains(/context and problem/i).should("exist")
36+
cy.contains(data.adr.contextAndProblemStatement).should("exist");
37+
38+
cy.contains(/decision drivers/i).should("exist")
39+
cy.contains(/considered options/i).should("exist")
40+
cy.contains(/decision outcome/i).should("exist")
41+
42+
});
43+
})
44+
})
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const expectedNamespace = "finos"
2+
const expectedArchitectureId = 1;
3+
const expectedArchitectureVersion = "1.0.0";
4+
5+
describe('Architecture Tests', () => {
6+
beforeEach(() => {
7+
cy.intercept("/calm/namespaces", {"values": [expectedNamespace]});
8+
cy.intercept("/calm/namespaces/finos/architectures", {"values": [expectedArchitectureId]});
9+
cy.intercept("/calm/namespaces/finos/architectures/1/versions", {"values": [expectedArchitectureVersion]});
10+
cy.intercept("/calm/namespaces/finos/architectures/1/versions/1.0.0", {
11+
fixture: "three-tier-calm.json"
12+
});
13+
})
14+
15+
it("Displays architecture JSON successfully", () => {
16+
cy.visit("/");
17+
cy.findByText(expectedNamespace).click();
18+
cy.findByText(/architectures/i).click();
19+
cy.findByText(/1/i).click();
20+
cy.findByText(/1.0.0/i).click();
21+
22+
cy.findByText(/relationship descriptions/i).should("exist");
23+
cy.findByText(/node descriptions/i).should("exist");
24+
25+
cy.findByRole("tab", { name: /json/i}).click();
26+
27+
cy.fixture('three-tier-calm').then(data => {
28+
cy.contains(/\$schema/i).should("exist");
29+
cy.contains(data.$schema).should("exist");
30+
cy.contains(/id/i).should("exist");
31+
32+
cy.contains(/nodes/i).should("exist");
33+
34+
Object.entries(data.nodes[0]).forEach(([key, value]) => {
35+
cy.contains(key).should("exist");
36+
cy.contains(value as string).should("exist");
37+
});
38+
39+
cy.contains(/unique-id/i).should("exist");
40+
cy.contains(data.nodes[0]["unique-id"]).should("exist");
41+
42+
cy.contains(/name/i).should("exist");
43+
cy.contains(data.nodes[0]["name"]).should("exist");
44+
45+
cy.contains(/description/i).should("exist");
46+
cy.contains(data.nodes[0]["description"]).should("exist");
47+
48+
cy.contains(/node-type/i).should("exist");
49+
cy.contains(data.nodes[0]["node-type"]).should("exist");
50+
});
51+
})
52+
})
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
const expectedNamespace = "finos"
2+
const expectedFlowId = 1;
3+
const expectedFlowVersion = "1.0.0";
4+
5+
describe('Flow Tests', () => {
6+
beforeEach(() => {
7+
cy.intercept("/calm/namespaces", {"values": [expectedNamespace]});
8+
cy.intercept("/calm/namespaces/finos/flows", {"values": [expectedFlowId]});
9+
cy.intercept("/calm/namespaces/finos/flows/1/versions", {"values": [expectedFlowVersion]});
10+
cy.intercept("/calm/namespaces/finos/flows/1/versions/1.0.0", {
11+
fixture: "update-account-flow"
12+
});
13+
})
14+
15+
it("Displays flow JSON successfully", () => {
16+
cy.visit("/");
17+
cy.findByText(expectedNamespace).click();
18+
cy.findByText(/flows/i).click();
19+
cy.findByText(/1/i).click();
20+
cy.findByText(/1.0.0/i).click();
21+
22+
cy.fixture('update-account-flow').then(data => {
23+
cy.contains(/\$schema/i).should("exist");
24+
cy.contains(data.$schema).should("exist");
25+
26+
cy.contains(/\$id/i).should("exist");
27+
cy.contains(data.$id).should("exist");
28+
29+
cy.contains(/unique-id/i).should("exist");
30+
cy.contains(data["unique-id"]).should("exist");
31+
32+
cy.contains(/name/i).should("exist");
33+
cy.contains(data.name).should("exist");
34+
35+
cy.contains(/description/i).should("exist");
36+
cy.contains(data.description).should("exist");
37+
38+
cy.contains(/transitions/i).should("exist");
39+
40+
Object.entries(data.transitions[0]).forEach(([key, value]) => {
41+
cy.contains(key).should("exist");
42+
cy.contains(value as string).should("exist");
43+
});
44+
45+
});
46+
})
47+
})

calm-hub-ui/cypress/e2e/home.cy.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import {namespaces, resourceTypes} from "../fixtures/constants.js";
2+
3+
describe('Home page tests', () => {
4+
beforeEach(() => {
5+
cy.intercept("/calm/namespaces", {"values": namespaces})
6+
})
7+
8+
it('Loads initial screen successfully', () => {
9+
cy.visit('/');
10+
cy.findByText("Explore").should("exist");
11+
cy.findByText("Namespaces").should("exist");
12+
namespaces.forEach(namespace => cy.findByText(namespace).should("exist"));
13+
cy.findByText(namespaces[0]).click();
14+
resourceTypes.forEach(resourceType => { cy.findByText(resourceType).should("exist"); });
15+
})
16+
17+
context("Wide screen tests", () => {
18+
it("Finds navigation items", () => {
19+
cy.viewport('macbook-16')
20+
cy.visit('/');
21+
cy.findByRole("link", { name: "Hub" })
22+
cy.findByRole("link", { name: "Visualizer" })
23+
})
24+
25+
})
26+
27+
context("Collapsed screen tests", () => {
28+
it("Finds navigation items", () => {
29+
cy.viewport(1000, 600)
30+
cy.visit('/');
31+
cy.findByRole("button", { name: "Open Menu" }).click();
32+
cy.findByRole("link", { name: "Hub" })
33+
cy.findByRole("link", { name: "Visualizer" })
34+
})
35+
})
36+
})
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const expectedNamespace = "finos"
2+
const expectedPatternId = 1;
3+
const expectedPatternVersion = "1.0.0";
4+
5+
describe('Pattern Tests', () => {
6+
beforeEach(() => {
7+
cy.intercept("/calm/namespaces", {"values": [expectedNamespace]});
8+
cy.intercept("/calm/namespaces/finos/patterns", {"values": [expectedPatternId]});
9+
cy.intercept("/calm/namespaces/finos/patterns/1/versions", {"values": [expectedPatternVersion]});
10+
cy.intercept("/calm/namespaces/finos/patterns/1/versions/1.0.0", {
11+
fixture: "conference-signup-pattern"
12+
});
13+
})
14+
15+
it("Displays pattern JSON successfully", () => {
16+
cy.visit("/");
17+
cy.findByText(expectedNamespace).click();
18+
cy.findByText(/patterns/i).click();
19+
cy.findByText(/1/i).click();
20+
cy.findByText(/1.0.0/i).click();
21+
22+
cy.fixture('conference-signup-pattern').then(data => {
23+
cy.contains(/\$schema/i).should("exist");
24+
cy.contains(data.$schema).should("exist");
25+
26+
cy.contains(/\$id/i).should("exist");
27+
cy.contains(data.$id).should("exist");
28+
29+
cy.contains(/title/i).should("exist");
30+
cy.contains(data.title).should("exist");
31+
32+
cy.contains(/description/i).should("exist");
33+
cy.contains(data.description).should("exist");
34+
35+
cy.contains(/minItems/i).should("exist");
36+
cy.contains(data.properties.nodes.minItems).should("exist");
37+
38+
cy.contains(data.properties.nodes.minItems).should("exist");
39+
40+
cy.contains(/prefixItems/i).should("exist");
41+
});
42+
})
43+
})
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
describe("Visualizer page tests", () => {
2+
it("Displays visualizer on navigation", () => {
3+
cy.viewport('macbook-16')
4+
cy.visit("/");
5+
6+
cy.findByRole("link", { name: "Visualizer" }).click();
7+
8+
cy.get('canvas').should("not.exist");
9+
cy.findByText(/drag and drop your file here/i).should("exist");
10+
11+
cy.fixture("three-tier-calm", null).as('architecture');
12+
cy.get('input[type=file]').selectFile("@architecture", {force: true})
13+
14+
cy.get('canvas').should("exist");
15+
cy.findByText(/relationship descriptions/i).should("exist");
16+
cy.findByText(/node descriptions/i).should("exist");
17+
})
18+
})

0 commit comments

Comments
 (0)