Skip to content

Commit e610d98

Browse files
Sergiu MicleaDany9966
Sergiu Miclea
authored andcommitted
Add unit tests and test coverage report
The test coverage is automatically generated when running `npm run test`. A summary of the report is displayed in the console, and a more detailed report is generated in the `coverage` folder.
1 parent 6d65172 commit e610d98

File tree

168 files changed

+8215
-3797
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

168 files changed

+8215
-3797
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ cypress/videos
2020
!.yarn/releases
2121
!.yarn/sdks
2222
!.yarn/versions
23+
24+
25+
# testing
26+
coverage

jest.config.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,29 @@ export default {
1717
clearMocks: true,
1818

1919
// Indicates whether the coverage information should be collected while executing the test
20-
// collectCoverage: false,
20+
collectCoverage: true,
2121

2222
// An array of glob patterns indicating a set of files for which coverage information should be collected
23-
// collectCoverageFrom: undefined,
23+
collectCoverageFrom: [
24+
"**/*.tsx", // Include all .tsx files
25+
"!**/AssessmentModule/**", // Exclude files within the AssessmentModule directory (this is a module that is not used in the app)
26+
"!**/story.tsx", // Exclude all storybook files
27+
"!**/test.tsx", // Exclude old test files
28+
"!**/plugins/**", // Exclude files within the plugins directory
29+
"!src/index.tsx", // Exclude the index.tsx file
30+
"!**/App.tsx", // Exclude the App.tsx file
31+
"!**/smart/**", // Exclude files within the smart directory (this is a directory that contains containers)
32+
// other smart components
33+
"!**/EndpointModal.tsx",
34+
"!**/MinionPoolModal.tsx",
35+
"!**/TransferItemModal.tsx",
36+
"!**/Navigation.tsx",
37+
"!**/NotificationsModule.tsx",
38+
"!**/ProjectModal.tsx",
39+
"!**/ProjectMemberModal.tsx",
40+
"!**/UserModal.tsx",
41+
"!**/WizardPageContent.tsx",
42+
],
2443

2544
// The directory where Jest should output its coverage files
2645
// coverageDirectory: undefined,
@@ -31,15 +50,10 @@ export default {
3150
// ],
3251

3352
// Indicates which provider should be used to instrument code for coverage
34-
coverageProvider: "v8",
53+
// coverageProvider: "babel",
3554

3655
// A list of reporter names that Jest uses when writing coverage reports
37-
// coverageReporters: [
38-
// "json",
39-
// "text",
40-
// "lcov",
41-
// "clover"
42-
// ],
56+
coverageReporters: ["html", "text-summary"],
4357

4458
// An object that configures minimum threshold enforcement for coverage results
4559
// coverageThreshold: undefined,

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
"test": "jest",
1919
"e2e": "cypress run",
2020
"test-release": "node ./tests/testRelease",
21-
"test-coverage": "node ./tests/testCoverage",
2221
"storybook": "start-storybook"
2322
},
2423
"devDependencies": {
@@ -31,7 +30,7 @@
3130
"@types/file-saver": "^2.0.1",
3231
"@types/jest": "^27.0.2",
3332
"@types/js-cookie": "^2.2.6",
34-
"@types/luxon": "^3.3.2",
33+
"@types/luxon": "^3.3.3",
3534
"@types/moment-timezone": "^0.5.13",
3635
"@types/react": "^16.13.1",
3736
"@types/react-collapse": "^5.0.0",
@@ -51,6 +50,7 @@
5150
"eslint-plugin-prettier": "^4.2.1",
5251
"eslint-plugin-react": "^7.31.7",
5352
"jest": "^27.3.1",
53+
"jest-canvas-mock": "^2.5.2",
5454
"nodemon": "^2.0.4",
5555
"prettier": "^2.7.1"
5656
},

src/components/modules/DashboardModule/DashboardBarChart/DashboardBarChart.spec.tsx

Lines changed: 140 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1313
*/
1414

1515
import React from "react";
16-
import { render } from "@testing-library/react";
17-
import TestUtils from "@tests/TestUtils";
16+
1817
import { ThemePalette } from "@src/components/Theme";
18+
import { render } from "@testing-library/react";
1919
import userEvent from "@testing-library/user-event";
20-
import DashboardBarChart from ".";
20+
import TestUtils from "@tests/TestUtils";
21+
22+
import DashboardBarChart from "./";
2123

2224
const DATA: DashboardBarChart["props"]["data"] = [
2325
{
@@ -109,4 +111,139 @@ describe("DashboardBarChart", () => {
109111
);
110112
}
111113
);
114+
115+
it("does not render bars with height of 0%", () => {
116+
const ZERO_DATA = [
117+
{
118+
label: "label 1",
119+
values: [0, 0],
120+
},
121+
{
122+
label: "label 2",
123+
values: [20, 25],
124+
},
125+
];
126+
127+
render(<DashboardBarChart data={ZERO_DATA} yNumTicks={3} />);
128+
129+
const firstStackedBars = TestUtils.selectAll(
130+
"DashboardBarChart__StackedBar-",
131+
TestUtils.selectAll("DashboardBarChart__Bar-")[0]
132+
);
133+
const secondStackedBars = TestUtils.selectAll(
134+
"DashboardBarChart__StackedBar-",
135+
TestUtils.selectAll("DashboardBarChart__Bar-")[1]
136+
);
137+
138+
expect(firstStackedBars.length).toBe(0);
139+
expect(secondStackedBars.length).toBe(ZERO_DATA[1].values.length);
140+
});
141+
142+
it("renders half the bars if available width is less than 30 times the number of items", () => {
143+
const originalInnerWidth = window.innerWidth;
144+
Object.defineProperty(window, "innerWidth", {
145+
writable: true,
146+
configurable: true,
147+
value: 29 * DATA.length,
148+
});
149+
150+
render(<DashboardBarChart data={DATA} yNumTicks={3} />);
151+
152+
const bars = TestUtils.selectAll("DashboardBarChart__Bar-");
153+
154+
expect(bars.length).toBe(DATA.length / 2);
155+
156+
Object.defineProperty(window, "innerWidth", {
157+
writable: true,
158+
configurable: true,
159+
value: originalInnerWidth,
160+
});
161+
});
162+
163+
it("fires the onBarMouseLeave callback on bar mouse leave", () => {
164+
const onBarMouseLeave = jest.fn();
165+
166+
render(
167+
<DashboardBarChart
168+
data={DATA}
169+
yNumTicks={3}
170+
onBarMouseLeave={onBarMouseLeave}
171+
/>
172+
);
173+
174+
const bar = TestUtils.selectAll("DashboardBarChart__StackedBar-")[0];
175+
userEvent.unhover(bar);
176+
177+
expect(onBarMouseLeave).toHaveBeenCalled();
178+
});
179+
180+
it("calculates the correct position for bars", () => {
181+
const onBarMouseEnter = jest.fn();
182+
render(
183+
<DashboardBarChart
184+
data={DATA}
185+
yNumTicks={3}
186+
onBarMouseEnter={onBarMouseEnter}
187+
/>
188+
);
189+
190+
const firstBar = TestUtils.selectAll("DashboardBarChart__StackedBar-")[0];
191+
userEvent.hover(firstBar);
192+
193+
expect(onBarMouseEnter).toHaveBeenCalledWith({ x: 48, y: 65 }, DATA[0]);
194+
});
195+
196+
it("recalculates ticks when new data is received", () => {
197+
const { rerender } = render(
198+
<DashboardBarChart data={DATA} yNumTicks={3} />
199+
);
200+
201+
const bars = TestUtils.selectAll("DashboardBarChart__Bar-");
202+
expect(bars.length).toBe(DATA.length);
203+
expect(bars[0].textContent).toBe("label 1");
204+
expect(bars[1].textContent).toBe("label 2");
205+
206+
const NEW_DATA = [
207+
{
208+
label: "label 3",
209+
values: [10, 30],
210+
data: "data 3",
211+
},
212+
{
213+
label: "label 4",
214+
values: [5, 20],
215+
data: "data 4",
216+
},
217+
];
218+
219+
// Mocking the offset width is necessary due to how the rendered
220+
// output behaves within the @testing-library/react environment
221+
Object.defineProperty(HTMLElement.prototype, "offsetWidth", {
222+
configurable: true,
223+
value: 500,
224+
});
225+
rerender(<DashboardBarChart data={NEW_DATA} yNumTicks={3} />);
226+
227+
const newBars = TestUtils.selectAll("DashboardBarChart__Bar-");
228+
expect(newBars.length).toBe(NEW_DATA.length);
229+
expect(newBars[0].textContent).toBe("label 3");
230+
expect(newBars[1].textContent).toBe("label 4");
231+
});
232+
233+
it("does not fire any function when onBarMouseEnter is not provided", () => {
234+
render(<DashboardBarChart data={DATA} yNumTicks={3} />);
235+
236+
const firstStackedBar = TestUtils.selectAll(
237+
"DashboardBarChart__StackedBar-"
238+
)[0];
239+
const spy = jest.spyOn(console, "error").mockImplementation(() => {});
240+
241+
// Hover over the stacked bar
242+
userEvent.hover(firstStackedBar);
243+
244+
// Assert that there were no console errors
245+
expect(spy).not.toHaveBeenCalled();
246+
247+
spy.mockRestore();
248+
});
112249
});
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
Copyright (C) 2023 Cloudbase Solutions SRL
3+
This program is free software: you can redistribute it and/or modify
4+
it under the terms of the GNU Affero General Public License as
5+
published by the Free Software Foundation, either version 3 of the
6+
License, or (at your option) any later version.
7+
This program is distributed in the hope that it will be useful,
8+
but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
GNU Affero General Public License for more details.
11+
You should have received a copy of the GNU Affero General Public License
12+
along with this program. If not, see <http://www.gnu.org/licenses/>.
13+
*/
14+
15+
import React from "react";
16+
17+
import { act, render } from "@testing-library/react";
18+
import TestUtils from "@tests/TestUtils";
19+
20+
import DashboardContent from "./DashboardContent";
21+
22+
jest.mock("react-router-dom", () => ({ Link: "div" }));
23+
24+
describe("DashboardContent", () => {
25+
let resizeWindow: (x: number, y: number) => void;
26+
let defaultProps: DashboardContent["props"];
27+
28+
beforeAll(() => {
29+
resizeWindow = (x, y) => {
30+
window.innerWidth = x;
31+
window.innerHeight = y;
32+
window.dispatchEvent(new Event("resize"));
33+
};
34+
});
35+
36+
beforeEach(() => {
37+
defaultProps = {
38+
replicas: [],
39+
migrations: [],
40+
endpoints: [],
41+
projects: [],
42+
replicasLoading: false,
43+
migrationsLoading: false,
44+
endpointsLoading: false,
45+
usersLoading: false,
46+
projectsLoading: false,
47+
licenceLoading: false,
48+
notificationItemsLoading: false,
49+
users: [],
50+
licence: null,
51+
licenceServerStatus: null,
52+
licenceError: null,
53+
notificationItems: [],
54+
isAdmin: false,
55+
onNewReplicaClick: jest.fn(),
56+
onNewEndpointClick: jest.fn(),
57+
onAddLicenceClick: jest.fn(),
58+
};
59+
});
60+
61+
it("renders modules for non-admin users", () => {
62+
render(<DashboardContent {...defaultProps} />);
63+
expect(
64+
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")
65+
).toHaveLength(3);
66+
expect(
67+
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[0].textContent
68+
).toBe("Replicas");
69+
expect(
70+
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[1].textContent
71+
).toBe("Migrations");
72+
expect(
73+
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[2].textContent
74+
).toBe("Endpoints");
75+
});
76+
77+
it("renders additional modules for admin users", () => {
78+
render(<DashboardContent {...defaultProps} isAdmin />);
79+
80+
expect(
81+
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")
82+
).toHaveLength(5);
83+
expect(
84+
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[0].textContent
85+
).toBe("Replicas");
86+
expect(
87+
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[1].textContent
88+
).toBe("Migrations");
89+
expect(
90+
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[2].textContent
91+
).toBe("Endpoints");
92+
expect(
93+
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[3].textContent
94+
).toBe("Users");
95+
expect(
96+
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[4].textContent
97+
).toBe("Projects");
98+
});
99+
100+
it("switches to mobile layout when window width is less than 1120", () => {
101+
resizeWindow(1100, 800);
102+
render(<DashboardContent {...defaultProps} />);
103+
104+
expect(
105+
TestUtils.select("DashboardContent__MiddleMobileLayout")
106+
).toBeTruthy();
107+
});
108+
109+
it("handleResize updates state correctly based on window size", async () => {
110+
resizeWindow(2400, 800);
111+
112+
let instance: DashboardContent | null = null;
113+
114+
const setRef = (componentInstance: DashboardContent) => {
115+
instance = componentInstance;
116+
};
117+
118+
render(<DashboardContent ref={setRef} {...defaultProps} />);
119+
120+
const setStateMock = jest.spyOn(instance!, "setState");
121+
122+
act(() => {
123+
resizeWindow(1000, 800);
124+
});
125+
126+
expect(setStateMock).toHaveBeenCalledWith({ useMobileLayout: true });
127+
128+
setStateMock.mockRestore();
129+
resizeWindow(2400, 800);
130+
});
131+
});

0 commit comments

Comments
 (0)