diff --git a/dashboard/package.json b/dashboard/package.json index a373f6882f..2b4a931bc7 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -40,6 +40,13 @@ "test": "react-scripts test", "eject": "react-scripts eject" }, + "jest":{ + "transform": { + "^.+\\.[t|j]sx?$": "babel-jest" + }, + "transformIgnorePatterns":["node_modules/(?!@patternfly)/"] + } + , "eslintConfig": { "extends": [ "react-app", @@ -59,7 +66,10 @@ ] }, "devDependencies": { + "@babel/preset-env": "^7.17.10", + "babel-jest": "^28.1.0", "css-loader": "^6.7.1", + "jest": "^28.1.0", "less": "^4.1.2", "less-loader": "^10.2.0", "style-loader": "^3.3.1" diff --git a/dashboard/public/index.html b/dashboard/public/index.html index 896deda7e5..a386a682bc 100644 --- a/dashboard/public/index.html +++ b/dashboard/public/index.html @@ -30,7 +30,7 @@ Notice the use of %PUBLIC_URL% in the tags above. It will be replaced with the URL of the `public` folder during the build. Only files inside the `public` folder can be referenced from the HTML. - + Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. diff --git a/dashboard/src/App.js b/dashboard/src/App.js index 5301b7cce5..c9e3f58991 100644 --- a/dashboard/src/App.js +++ b/dashboard/src/App.js @@ -10,7 +10,7 @@ import MainLayout from 'modules/containers/MainLayout'; function App() { useEffect(() => { const faviconLogo = document.getElementById("favicon"); - faviconLogo.setAttribute("href", favicon); + faviconLogo?.setAttribute("href", favicon); }, []); return ( diff --git a/dashboard/src/App.test.js b/dashboard/src/App.test.js deleted file mode 100644 index 1f03afeece..0000000000 --- a/dashboard/src/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/dashboard/src/modules/components/AlertComponent/Alert.test.js b/dashboard/src/modules/components/AlertComponent/Alert.test.js new file mode 100644 index 0000000000..e70e15ed3b --- /dev/null +++ b/dashboard/src/modules/components/AlertComponent/Alert.test.js @@ -0,0 +1,26 @@ +import { Provider } from "react-redux"; +import store from "store/store"; +import App from "../../../App"; +const { render, screen, fireEvent } = require("@testing-library/react"); +const AppWrapper = () => { + return ( + + + + ); +}; +test("Alert message is displayed on initial load", () => { + render(); + const alert = screen.getByText(/want to see your own data/i); + expect(alert).toBeInTheDocument(); +}); + +test("Alert message is closed on clicking close button", () => { + render(); + const alert = screen.getByText(/want to see your own data/i); + const closeButton = screen.getByRole("button", { + name: /close info alert/i, + }); + fireEvent.click(closeButton); + expect(alert).not.toBeVisible(); +}); diff --git a/dashboard/src/modules/components/DatePickerComponent/DatePicker.test.js b/dashboard/src/modules/components/DatePickerComponent/DatePicker.test.js new file mode 100644 index 0000000000..e7711ccfd6 --- /dev/null +++ b/dashboard/src/modules/components/DatePickerComponent/DatePicker.test.js @@ -0,0 +1,30 @@ +import { Provider } from "react-redux"; +import store from "store/store"; +import { MOCK_DATA } from "utils/mockData"; +import App from "../../../App"; +const { render, screen, fireEvent } = require("@testing-library/react"); +const AppWrapper = () => { + return ( + + + + ); +}; +jest.mock("utils/api", () => { + return { + get: () => ({ + data: MOCK_DATA, + }), + }; +}); +test("data is filtered based on date range selected from date picker", async () => { + render(); + await screen.findByText("dhcp1"); + const datePickerInput = screen.getAllByPlaceholderText(/yyyy-mm-dd/i); + fireEvent.change(datePickerInput[0], { target: { value: "2022-02-16" } }); + fireEvent.change(datePickerInput[1], { target: { value: "2022-02-20" } }); + const updateBtn = screen.getByRole("button", { name: /update/i }); + fireEvent.click(updateBtn); + const cells = screen.getAllByRole("cell"); + expect(cells).toHaveLength(12); +}); diff --git a/dashboard/src/modules/components/HeadingComponent/Heading.test.js b/dashboard/src/modules/components/HeadingComponent/Heading.test.js new file mode 100644 index 0000000000..d890b7f119 --- /dev/null +++ b/dashboard/src/modules/components/HeadingComponent/Heading.test.js @@ -0,0 +1,16 @@ +import { Provider } from "react-redux"; +import store from "store/store"; +import App from "../../../App"; +const { render, screen } = require("@testing-library/react"); +const AppWrapper = () => { + return ( + + + + ); +}; +test("Page heading is displayed on initial load", () => { + render(); + const heading = screen.getByRole("heading", { name: /controllers/i }); + expect(heading).toBeInTheDocument(); +}); diff --git a/dashboard/src/modules/components/SearchComponent/Search.test.js b/dashboard/src/modules/components/SearchComponent/Search.test.js new file mode 100644 index 0000000000..bb587fe755 --- /dev/null +++ b/dashboard/src/modules/components/SearchComponent/Search.test.js @@ -0,0 +1,33 @@ +import { Provider } from "react-redux"; +import store from "store/store"; +import { MOCK_DATA } from "utils/mockData"; +import App from "../../../App"; +const { render, screen, fireEvent } = require("@testing-library/react"); +const AppWrapper = () => { + return ( + + + + ); +}; +jest.mock("utils/api", () => { + return { + get: () => ({ + data: MOCK_DATA, + }), + }; +}); +test("data is filtered based on value in search box", async () => { + render(); + await screen.findByText("dhcp1"); + const searchBox = screen.getByPlaceholderText(/search controllers/i); + fireEvent.change(searchBox, { target: { value: "dhcp2" } }); + const searchBtn = screen.getByRole("button", { + name: /searchButton/i, + }); + fireEvent.click(searchBtn); + const controllerTwo = screen.queryByText("dhcp2"); + const controllerThree = screen.queryByText("dhcp3"); + expect(controllerTwo).toBeInTheDocument(); + expect(controllerThree).not.toBeInTheDocument(); +}); diff --git a/dashboard/src/modules/components/SearchComponent/index.jsx b/dashboard/src/modules/components/SearchComponent/index.jsx new file mode 100644 index 0000000000..ef8da69d36 --- /dev/null +++ b/dashboard/src/modules/components/SearchComponent/index.jsx @@ -0,0 +1,32 @@ +import React,{useState} from "react"; +import "./index.css" +import { InputGroup, TextInput, Button } from "@patternfly/react-core"; +import SearchIcon from "@patternfly/react-icons/dist/esm/icons/search-icon"; +import { filterData } from "utils/filterDataset"; +function SearchBox({ + dataArray, + setPublicData, + startDate, + endDate, + setControllerName, +}) { + const [controllerValue, setControllerValue] = useState(""); + const searchController = () => { + setPublicData(filterData(dataArray, startDate, endDate, controllerValue)); + setControllerName(controllerValue); + }; + return ( + + setControllerValue(e)} + > + + + ); +} + +export default SearchBox; diff --git a/dashboard/src/modules/components/TableComponent/Table.test.js b/dashboard/src/modules/components/TableComponent/Table.test.js new file mode 100644 index 0000000000..b83254ee44 --- /dev/null +++ b/dashboard/src/modules/components/TableComponent/Table.test.js @@ -0,0 +1,48 @@ +import { Provider } from "react-redux"; +import store from "store/store"; +import { MOCK_DATA } from "utils/mockData"; +import App from "../../../App"; +const { + render, + screen, + waitFor, + fireEvent, +} = require("@testing-library/react"); +const AppWrapper = () => { + return ( + + + + ); +}; +jest.mock("utils/api", () => { + return { + get: () => ({ + data: MOCK_DATA, + }), + }; +}); + +test("data from API is displayed on initial load", async () => { + render(); + const benchmarkName = await screen.findByText("pbench_user_benchmark1"); + const cells = await screen.findAllByRole("cell"); + await waitFor(() => expect(benchmarkName).toBeInTheDocument()); + await waitFor(() => expect(cells).toHaveLength(20)); +}); + +test("row is favorited after clicking on favorite icon", async () => { + render(); + await screen.findByText("dhcp1"); + const starBtn = screen.getAllByRole("button", { + name: /not starred/i, + }); + fireEvent.click(starBtn[0]); + fireEvent.click(starBtn[1]); + const favoriteBtn = screen.getByRole("button", { + name: /see favorites button/i, + }); + fireEvent.click(favoriteBtn); + const favoriteCell = screen.getAllByRole("cell"); + expect(favoriteCell).toHaveLength(8); +}); diff --git a/dashboard/src/utils/mockData.js b/dashboard/src/utils/mockData.js new file mode 100644 index 0000000000..42a055f3b1 --- /dev/null +++ b/dashboard/src/utils/mockData.js @@ -0,0 +1,37 @@ +export const MOCK_DATA = [ + { + controller: "dhcp1", + name: "pbench_user_benchmark1", + metadata: { + "dataset.created": "2022-02-16T13:21:29+00:00", + }, + }, + { + controller: "dhcp2", + name: "pbench_user_benchmark2", + metadata: { + "dataset.created": "2022-02-18T13:21:29+00:00", + }, + }, + { + controller: "dhcp3", + name: "pbench_user_benchmark3", + metadata: { + "dataset.created": "2022-02-20T13:21:29+00:00", + }, + }, + { + controller: "dhcp4", + name: "pbench_user_benchmark4", + metadata: { + "dataset.created": "2022-02-25T13:21:29+00:00", + }, + }, + { + controller: "dhcp5", + name: "pbench_user_benchmark5", + metadata: { + "dataset.created": "2022-03-08T13:21:29+00:00", + }, + }, +];