Skip to content

Commit

Permalink
Add unit tests and test coverage report
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Sergiu Miclea committed Dec 27, 2023
1 parent 6d65172 commit f0349f1
Show file tree
Hide file tree
Showing 168 changed files with 8,215 additions and 3,797 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ cypress/videos
!.yarn/releases
!.yarn/sdks
!.yarn/versions


# testing
coverage
32 changes: 23 additions & 9 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,29 @@ export default {
clearMocks: true,

// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
collectCoverage: true,

// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: undefined,
collectCoverageFrom: [
"**/*.tsx", // Include all .tsx files
"!**/AssessmentModule/**", // Exclude files within the AssessmentModule directory (this is a module that is not used in the app)
"!**/story.tsx", // Exclude all storybook files
"!**/test.tsx", // Exclude old test files
"!**/plugins/**", // Exclude files within the plugins directory
"!src/index.tsx", // Exclude the index.tsx file
"!**/App.tsx", // Exclude the App.tsx file
"!**/smart/**", // Exclude files within the smart directory (this is a directory that contains containers)
// other smart components
"!**/EndpointModal.tsx",
"!**/MinionPoolModal.tsx",
"!**/TransferItemModal.tsx",
"!**/Navigation.tsx",
"!**/NotificationsModule.tsx",
"!**/ProjectModal.tsx",
"!**/ProjectMemberModal.tsx",
"!**/UserModal.tsx",
"!**/WizardPageContent.tsx",
],

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

// Indicates which provider should be used to instrument code for coverage
coverageProvider: "v8",
// coverageProvider: "babel",

// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
coverageReporters: ["html", "text-summary"],

// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"test": "jest",
"e2e": "cypress run",
"test-release": "node ./tests/testRelease",
"test-coverage": "node ./tests/testCoverage",
"storybook": "start-storybook"
},
"devDependencies": {
Expand All @@ -31,7 +30,7 @@
"@types/file-saver": "^2.0.1",
"@types/jest": "^27.0.2",
"@types/js-cookie": "^2.2.6",
"@types/luxon": "^3.3.2",
"@types/luxon": "^3.3.3",
"@types/moment-timezone": "^0.5.13",
"@types/react": "^16.13.1",
"@types/react-collapse": "^5.0.0",
Expand All @@ -51,6 +50,7 @@
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.7",
"jest": "^27.3.1",
"jest-canvas-mock": "^2.5.2",
"nodemon": "^2.0.4",
"prettier": "^2.7.1"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React from "react";
import { render } from "@testing-library/react";
import TestUtils from "@tests/TestUtils";

import { ThemePalette } from "@src/components/Theme";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import DashboardBarChart from ".";
import TestUtils from "@tests/TestUtils";

import DashboardBarChart from "./";

const DATA: DashboardBarChart["props"]["data"] = [
{
Expand Down Expand Up @@ -109,4 +111,139 @@ describe("DashboardBarChart", () => {
);
}
);

it("does not render bars with height of 0%", () => {
const ZERO_DATA = [
{
label: "label 1",
values: [0, 0],
},
{
label: "label 2",
values: [20, 25],
},
];

render(<DashboardBarChart data={ZERO_DATA} yNumTicks={3} />);

const firstStackedBars = TestUtils.selectAll(
"DashboardBarChart__StackedBar-",
TestUtils.selectAll("DashboardBarChart__Bar-")[0]
);
const secondStackedBars = TestUtils.selectAll(
"DashboardBarChart__StackedBar-",
TestUtils.selectAll("DashboardBarChart__Bar-")[1]
);

expect(firstStackedBars.length).toBe(0);
expect(secondStackedBars.length).toBe(ZERO_DATA[1].values.length);
});

it("renders half the bars if available width is less than 30 times the number of items", () => {
const originalInnerWidth = window.innerWidth;
Object.defineProperty(window, "innerWidth", {
writable: true,
configurable: true,
value: 29 * DATA.length,
});

render(<DashboardBarChart data={DATA} yNumTicks={3} />);

const bars = TestUtils.selectAll("DashboardBarChart__Bar-");

expect(bars.length).toBe(DATA.length / 2);

Object.defineProperty(window, "innerWidth", {
writable: true,
configurable: true,
value: originalInnerWidth,
});
});

it("fires the onBarMouseLeave callback on bar mouse leave", () => {
const onBarMouseLeave = jest.fn();

render(
<DashboardBarChart
data={DATA}
yNumTicks={3}
onBarMouseLeave={onBarMouseLeave}
/>
);

const bar = TestUtils.selectAll("DashboardBarChart__StackedBar-")[0];
userEvent.unhover(bar);

expect(onBarMouseLeave).toHaveBeenCalled();
});

it("calculates the correct position for bars", () => {
const onBarMouseEnter = jest.fn();
render(
<DashboardBarChart
data={DATA}
yNumTicks={3}
onBarMouseEnter={onBarMouseEnter}
/>
);

const firstBar = TestUtils.selectAll("DashboardBarChart__StackedBar-")[0];
userEvent.hover(firstBar);

expect(onBarMouseEnter).toHaveBeenCalledWith({ x: 48, y: 65 }, DATA[0]);
});

it("recalculates ticks when new data is received", () => {
const { rerender } = render(
<DashboardBarChart data={DATA} yNumTicks={3} />
);

const bars = TestUtils.selectAll("DashboardBarChart__Bar-");
expect(bars.length).toBe(DATA.length);
expect(bars[0].textContent).toBe("label 1");
expect(bars[1].textContent).toBe("label 2");

const NEW_DATA = [
{
label: "label 3",
values: [10, 30],
data: "data 3",
},
{
label: "label 4",
values: [5, 20],
data: "data 4",
},
];

// Mocking the offset width is necessary due to how the rendered
// output behaves within the @testing-library/react environment
Object.defineProperty(HTMLElement.prototype, "offsetWidth", {
configurable: true,
value: 500,
});
rerender(<DashboardBarChart data={NEW_DATA} yNumTicks={3} />);

const newBars = TestUtils.selectAll("DashboardBarChart__Bar-");
expect(newBars.length).toBe(NEW_DATA.length);
expect(newBars[0].textContent).toBe("label 3");
expect(newBars[1].textContent).toBe("label 4");
});

it("does not fire any function when onBarMouseEnter is not provided", () => {
render(<DashboardBarChart data={DATA} yNumTicks={3} />);

const firstStackedBar = TestUtils.selectAll(
"DashboardBarChart__StackedBar-"
)[0];
const spy = jest.spyOn(console, "error").mockImplementation(() => {});

// Hover over the stacked bar
userEvent.hover(firstStackedBar);

// Assert that there were no console errors
expect(spy).not.toHaveBeenCalled();

spy.mockRestore();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
Copyright (C) 2023 Cloudbase Solutions SRL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React from "react";

import { act, render } from "@testing-library/react";
import TestUtils from "@tests/TestUtils";

import DashboardContent from "./DashboardContent";

jest.mock("react-router-dom", () => ({ Link: "div" }));

describe("DashboardContent", () => {
let resizeWindow: (x: number, y: number) => void;
let defaultProps: DashboardContent["props"];

beforeAll(() => {
resizeWindow = (x, y) => {
window.innerWidth = x;
window.innerHeight = y;
window.dispatchEvent(new Event("resize"));
};
});

beforeEach(() => {
defaultProps = {
replicas: [],
migrations: [],
endpoints: [],
projects: [],
replicasLoading: false,
migrationsLoading: false,
endpointsLoading: false,
usersLoading: false,
projectsLoading: false,
licenceLoading: false,
notificationItemsLoading: false,
users: [],
licence: null,
licenceServerStatus: null,
licenceError: null,
notificationItems: [],
isAdmin: false,
onNewReplicaClick: jest.fn(),
onNewEndpointClick: jest.fn(),
onAddLicenceClick: jest.fn(),
};
});

it("renders modules for non-admin users", () => {
render(<DashboardContent {...defaultProps} />);
expect(
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")
).toHaveLength(3);
expect(
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[0].textContent
).toBe("Replicas");
expect(
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[1].textContent
).toBe("Migrations");
expect(
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[2].textContent
).toBe("Endpoints");
});

it("renders additional modules for admin users", () => {
render(<DashboardContent {...defaultProps} isAdmin />);

expect(
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")
).toHaveLength(5);
expect(
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[0].textContent
).toBe("Replicas");
expect(
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[1].textContent
).toBe("Migrations");
expect(
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[2].textContent
).toBe("Endpoints");
expect(
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[3].textContent
).toBe("Users");
expect(
TestUtils.selectAll("DashboardInfoCount__CountBlockLabel")[4].textContent
).toBe("Projects");
});

it("switches to mobile layout when window width is less than 1120", () => {
resizeWindow(1100, 800);
render(<DashboardContent {...defaultProps} />);

expect(
TestUtils.select("DashboardContent__MiddleMobileLayout")
).toBeTruthy();
});

it("handleResize updates state correctly based on window size", async () => {
resizeWindow(2400, 800);

let instance: DashboardContent | null = null;

const setRef = (componentInstance: DashboardContent) => {
instance = componentInstance;
};

render(<DashboardContent ref={setRef} {...defaultProps} />);

const setStateMock = jest.spyOn(instance!, "setState");

act(() => {
resizeWindow(1000, 800);
});

expect(setStateMock).toHaveBeenCalledWith({ useMobileLayout: true });

setStateMock.mockRestore();
resizeWindow(2400, 800);
});
});
Loading

0 comments on commit f0349f1

Please sign in to comment.