Quellcode durchsuchen

Merge branch 'feature/fe_tests_e' of Neca/HRCenter into FE_dev

pull/116/head
safet.purkovic vor 3 Jahren
Ursprung
Commit
9ebccd67ab

+ 137
- 0
src/__tests__/ReduxTests/adsReducer.test.js Datei anzeigen

@@ -0,0 +1,137 @@
import * as redux from "react-redux";
import store from "../../store";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import { mockState } from "../../mockState";
import { render } from "@testing-library/react";
import AdsPage from "../../pages/AdsPage/AdsPage";
import * as api from "../../request/adsRequest";
import { runSaga } from "redux-saga";
import { FETCH_ADS_REQ } from "../../store/actions/ads/adsActionConstants";
import ColorModeProvider from "../../context/ColorModeContext";
import * as fc from "../../store/saga/adsSaga";
import { setAds, setFilteredAds } from "../../store/actions/ads/adsAction";
import { setArchiveAds } from "../../store/actions/archiveAds/archiveAdsActions";
import { setAd } from "../../store/actions/ad/adActions";

describe("Ads reducer tests", () => {
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<AdsPage />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValueOnce(mockState.ads);

spyOnUseDispatch = jest.spyOn(redux, "useDispatch");

mockDispatch = jest.fn();
spyOnUseDispatch.mockReturnValue(mockDispatch);
});

afterEach(() => {
jest.restoreAllMocks();
});

it("Should dispatch get ads request when rendered", () => {
render(cont);
expect(mockDispatch).toHaveBeenCalledWith({
type: FETCH_ADS_REQ,
});
});

it("Should load and handle ads in case of success", async () => {
const dispatchedActions = [];

const mockedCall = { data: mockState.ads.ads };
api.getAllAds = jest.fn(() => Promise.resolve(mockedCall));

const fakeStore = {
getState: () => mockState.ads.ads,
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, fc.getAds, {}).done;
expect(api.getAllAds.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setAds(mockedCall.data));
});

it("Should load and handle filtered ads in case of success", async () => {
const dispatchedActions = [];
const filter = {
minimumExperience: 0,
maximumExperience: 0,
technologies: [1],
workHour: "FullTime",
employmentType: "Work",
};

const filteredData = mockState.ads.ads.filter(
(ad) =>
ad.minimumExperience >= filter.minimumExperience &&
ad.minimumExperience <= filter.maximumExperience &&
ad.workHour === filter.workHour &&
ad.employmentType === filter.employmentType
);

const mockedCall = { data: filteredData };
api.getAllFilteredAds = jest.fn(() => Promise.resolve(mockedCall));

const fakeStore = {
getState: () => mockState.ads.ads,
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, fc.getFilteredAds, filter).done;
expect(api.getAllFilteredAds.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setFilteredAds(mockedCall.data));
});

it("Should load and handle archived ads in case of success", async () => {
const dispatchedActions = [];
const date = new Date();

const filteredData = mockState.ads.ads.filter((ad) => ad.expiredAt < date);

const mockedCall = { data: filteredData };
api.getAllArchiveAds = jest.fn(() => Promise.resolve(mockedCall));

const fakeStore = {
getState: () => mockState.ads.ads,
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, fc.getArchiveAds, {}).done;
expect(api.getAllArchiveAds.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setArchiveAds(mockedCall.data));
});

it("Should load and handle ad by id in case of success", async () => {
const dispatchedActions = [];
const id = 1;

const filteredData = mockState.ads.ads.filter((ad) => ad.id === id);

const mockedCall = { data: filteredData };
api.getAdDetailsById = jest.fn(() => Promise.resolve(mockedCall));

const fakeStore = {
getState: () => mockState.ads.ads,
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, fc.getAd, { payload: id }).done;
expect(api.getAdDetailsById.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setAd(mockedCall.data));
});
});

+ 142
- 0
src/__tests__/ReduxTests/patternsReducer.test.js Datei anzeigen

@@ -0,0 +1,142 @@
import * as redux from "react-redux";
import store from "../../store";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import { mockState } from "../../mockState";
import { render } from "@testing-library/react";
import PatternsPage from "../../pages/PatternsPage/PatternsPage";
import * as api from "../../request/patternsRequest";
import { runSaga } from "redux-saga";
import { FETCH_PATTERNS_REQ } from "../../store/actions/patterns/patternsActionConstants";
import ColorModeProvider from "../../context/ColorModeContext";
import * as fc from "../../store/saga/patternsSaga";
import { setFilteredPatterns, setPatterns } from "../../store/actions/patterns/patternsActions";
import { setPattern } from "../../store/actions/pattern/patternActions";
import { setPatternApplicants } from "../../store/actions/patternApplicants/patternApplicantsActions";

describe("Patterns reducer tests", () => {
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<PatternsPage />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValueOnce(mockState.patterns);

spyOnUseDispatch = jest.spyOn(redux, "useDispatch");
mockDispatch = jest.fn();
spyOnUseDispatch.mockReturnValue(mockDispatch);
});

afterEach(() => {
jest.restoreAllMocks();
});

it("Should dispatch get patterns request when rendered", () => {
render(cont);
expect(mockDispatch).toHaveBeenCalledWith({
type: FETCH_PATTERNS_REQ,
});
});

it("Should load and handle patterns in case of success", async () => {
const dispatchedActions = [];

const mockedCall = { data: mockState.patterns.patterns };
api.getAllPatterns = jest.fn(() => Promise.resolve(mockedCall));

const fakeStore = {
getState: () => mockState.patterns.patterns,
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, fc.getPatterns, {}).done;
expect(api.getAllPatterns.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setPatterns(mockedCall.data));
});

it("Should load and handle pattern by id in case of success", async () => {
const dispatchedActions = [];
const id = 1;

const filteredData = mockState.patterns.patterns.filter(
(pattern) => pattern.id === id
);

const mockedCall = { data: filteredData };
api.getPatternById = jest.fn(() => Promise.resolve(mockedCall));

const fakeStore = {
getState: () => mockState.patterns.patterns,
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, fc.getPattern, { payload: id }).done;
expect(api.getPatternById.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setPattern(mockedCall.data));
});

it("Should load and handle pattern applicants in case of success", async () => {
const dispatchedActions = [];
const id = 1;

const filteredData = mockState.patterns.patterns.filter(
(pattern) => pattern.id === id
);

const mockedCall = {
data: {
...filteredData[0].selectionLevel.selectionProcesses[0].applicant,
},
};
api.getPatternApplicantsById = jest.fn(() => Promise.resolve(mockedCall));

const fakeStore = {
getState: () => mockState.patterns.patterns,
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, fc.getPatternApplicants, { payload: id }).done;
expect(api.getPatternApplicantsById.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setPatternApplicants(mockedCall.data));
});

it("Should load and handle filtered patterns in case of success", async () => {
const dispatchedActions = [];
const filters = {
fromDate: new Date("2-2-2021"),
toDate: new Date("3-3-2023"),
selectionLevels: [1, 2],
};

const filteredData = mockState.patterns.patterns.filter(
(pattern) =>
pattern.createdAt >= filters.fromDate &&
pattern.createdAt <= filters.toDate
);

const mockedCall = {
data: filteredData,
};
api.getFilteredPatterns = jest.fn(() => Promise.resolve(mockedCall));

const fakeStore = {
getState: () => mockState.patterns.patterns,
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, fc.filterPatterns, filters).done;
expect(api.getFilteredPatterns.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setFilteredPatterns(mockedCall.data));
});
});

+ 91
- 0
src/__tests__/UITests/adsPageUI.test.js Datei anzeigen

@@ -0,0 +1,91 @@
import { render, screen, fireEvent } from "@testing-library/react";
import * as redux from "react-redux";
import AdsPage from "../../pages/AdsPage/AdsPage";
import store from "../../store";
import { mockState } from "../../mockState";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import ColorModeProvider from "../../context/ColorModeContext";

describe("AdsPage render tests", () => {
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<AdsPage />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;

beforeEach(() => {
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValueOnce(mockState.ads.ads);
});

afterEach(() => {
jest.resetAllMocks();
});

it("Should render ads", () => {
render(cont);
expect(screen.getByTestId("ads-page")).toBeDefined();
});

it("Should be rendered button which is used for showing input responsible for searching by name", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("ads-page-btn")[0]).toBeDefined();
});

it("Should be rendered button for toggling filters modal", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("ads-page-btn")[1]).toBeDefined();
});

it("Should be rendered button for adding new ad", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("ads-page-btn")[2]).toBeDefined();
});

it("Input for searching by title should not be shown when component is initialy rendered", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("ads-page-search-by-title")[0].style
.visibility
).toBe("hidden");
});

it("Should be rendered ad cards", () => {
const { container } = render(cont);
expect(container.getElementsByClassName('ad-card').length).toBeGreaterThan(0);
});

it("Should be rendered archive ad cards", () => {
const { container } = render(cont);
expect(container.getElementsByClassName('archive-ad').length).toBe(0);
});

it("Should render filter drawer after click filter button", () => {
const { container } = render(cont);
fireEvent.click(container.getElementsByClassName("fltr-btn")[0]);
expect(screen.getByTestId('ad-filters-drawer')).toBeDefined();
});

it("Should render modal after add ad button clicked", () => {
const { container } = render(cont);
fireEvent.click(container.getElementsByClassName("ads-page-btn")[2]);
expect(screen.getByTestId('add-ad-modal')).toBeDefined();
});

it("Should render arrow buttons for active ads slider", () => {
const { container } = render(cont);
expect(container.getElementsByClassName('active-ads-ads-arrows')).toBeDefined();
});

it("Should render arrow buttons for archived ads slider", () => {
const { container } = render(cont);
expect(container.getElementsByClassName('archived-ads-ads-arrows')).toBeDefined();
});
});

+ 77
- 0
src/__tests__/UITests/patternsPageUI.test.js Datei anzeigen

@@ -0,0 +1,77 @@
import { render, screen, fireEvent } from "@testing-library/react";
import * as redux from "react-redux";
import PatternsPage from "../../pages/PatternsPage/PatternsPage";
import store from "../../store";
import { mockState } from "../../mockState";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import PatternCard from "../../components/Patterns/PatternCard";

describe("PatternsPage render tests", () => {
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<PatternsPage />
</Router>
</redux.Provider>
);

let spyOnUseSelector;

beforeEach(() => {
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValueOnce(mockState.patterns.patterns);
});

afterEach(() => {
jest.resetAllMocks();
});

it("Should render patterns page", () => {
render(cont);
expect(screen.getByTestId("patterns-page")).toBeDefined();
});

it("Should render edit mode button", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("editEnableBtn")[0]).toBeDefined();
});

it("Should render edit mode button on card", () => {
const { container } = render(cont);
var btn = container.getElementsByClassName("c-icon-button")[0];
fireEvent.click(btn);
var btn1 = container.getElementsByClassName("c-icon-button")[1];
expect(btn1).toBeDefined();
});

it("Should render pattern cards", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("pattern-card-parent").length
).toBeGreaterThan(0);
});

it("Should render add pattern button", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("add-ad-btn")[0]).toBeDefined();
});

it("Should render add pattern modal", () => {
const { container } = render(cont);
fireEvent.click(container.getElementsByClassName("add-pattern-btn")[0]);
expect(
container.getElementsByClassName("add-pattern-btn")[0]
).toBeDefined();
});

it("Should render edit pattern modal when click on edit pattern button", () => {
const { container } = render(cont);
var btn = container.getElementsByClassName("c-icon-button")[0];
fireEvent.click(btn);
var btn1 = container.getElementsByClassName("c-icon-button")[1];
fireEvent.click(btn1);
var modal = screen.getByTestId("custom-modal-test-id");
expect(modal).toBeDefined();
});
});

+ 1
- 1
src/components/Ads/AdFilters.js Datei anzeigen

@@ -78,7 +78,7 @@ const AdFilters = ({ open, handleClose, technologies }) => {
// onClick={handleClose}
onKeyDown={handleClose}
>
<div>
<div data-testid="ad-filters-drawer">
<div className="ad-filters-header-container">
<div className="ad-filters-header">
<img src={filterIcon} alt="filter_icon" />

+ 5
- 1
src/components/Ads/AddAdModal.js Datei anzeigen

@@ -82,7 +82,11 @@ const AddAdModal = ({ open, handleClose }) => {
aria-labelledby="parent-modal-title"
aria-describedby="parent-modal-description"
>
<Box sx={{ ...style, width: 512 }} className="add-ad-modal">
<Box
sx={{ ...style, width: 512 }}
className="add-ad-modal"
data-testid="add-ad-modal"
>
<div className="add-ad-modal-header">
<div className="add-ad-modal-header-title">
<img src={plus} alt="plus" />

+ 1
- 1
src/components/Button/FilterButton.js Datei anzeigen

@@ -11,7 +11,7 @@ const FilterButton = ({ onShowFilters }) => {

return (
<IconButton
className={"c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding"}
className={"c-btn--primary-outlined ads-page-btn fltr-btn c-btn userPageBtn ml-20px no-padding"}
onClick={onShowFilters}
>
{!matches && "Filteri"}

+ 1
- 1
src/components/Patterns/PatternCard.js Datei anzeigen

@@ -1,7 +1,7 @@
import React from "react";
import PropTypes from "prop-types";
import editIcon from "../../assets/images/edit.png";
import { IconButton } from "@mui/material";
import IconButton from "../IconButton/IconButton";

const PatternCard = ({
createdAt,

+ 1
- 0
src/components/UI/CustomModal.js Datei anzeigen

@@ -24,6 +24,7 @@ const CustomModal = ({ open, onCloseModal, children, classes }) => {
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
data-testid="custom-modal-test-id"
>
<Box sx={style} className={`custom-modal ${classes}`}>
{children}

+ 124
- 0
src/mockState.js Datei anzeigen

@@ -733,4 +733,128 @@ export const mockState = {
{ isChecked: false, name: "Čeka se odgovor" },
],
},
ads: {
ads: [
{
id: 1,
title: "React Developer",
minimumExperience: 0,
createdAt: new Date(),
expiredAt: new Date("9-9-2023"),
keyResponsibilities: "K|K|K",
requirements: "R|R|R",
offer: "O|O|O",
technologies: [
{
technologyId: 1,
technologyType: "Backend",
name: ".NET",
isChecked: false,
},
{
technologyId: 2,
technologyType: "Other",
name: "Git",
isChecked: false,
},
],
applicants: [],
workHour: "FullTime",
employmentType: "Work",
},
{
id: 2,
title: ".NET Developer",
minimumExperience: 3,
createdAt: new Date(),
expiredAt: new Date("5-5-2021"),
keyResponsibilities: "K|K|K",
requirements: "R|R|R",
offer: "O|O|O",
technologies: [
{
technologyId: 1,
technologyType: "Backend",
name: ".NET",
isChecked: false,
},
{
technologyId: 3,
technologyType: "Frontend",
name: "HTML/CSS",
isChecked: false,
},
],
applicants: [],
workHour: "FullTime",
employmentType: "Intership",
},
],
},
patterns: {
patterns: [
{
id: 1,
title: "Uspesan korak",
createdAt: new Date(),
selectionLevelId: 1,
selectionLevel: {
id: 1,
name: "Screening test",
selectionProcesses: [
{
id: 1,
name: "Some random name",
status: "Čeka na zakazivanje",
date: new Date(),
link: "link",
applicant: {
applicantId: 1,
firstName: "Ermin",
lastName: "Bronja",
email: "ermin.bronja@dilig.net",
},
selectionLevelId: 1,
},
],
},
message: "Poruka",
},
{
id: 2,
title: "Neuspesan korak",
createdAt: new Date(),
selectionLevelId: 2,
selectionLevel: {
id: 2,
name: "Konacna odluka",
selectionProcesses: [],
},
message: "Poruka2",
},
{
id: 3,
title: "Zakazivanje termina",
createdAt: new Date(),
selectionLevelId: 3,
selectionLevel: {
id: 3,
name: "HR intervju",
selectionProcesses: [],
},
message: "Poruka3",
},
],
processes: [
{
id: 1,
name: "Some random name",
status: "Zakazan",
date: new Date(),
link: "link",
applicant: {},
selectionLevelId: 1,
},
],
},
};

+ 6
- 6
src/pages/AdsPage/AdsPage.js Datei anzeigen

@@ -217,7 +217,7 @@ const AdsPage = ({ history }) => {
);

return (
<>
<div data-testid="ads-page">
<div className="l-t-rectangle"></div>
<div className="r-b-rectangle"></div>
{/* <AdFilters /> */}
@@ -229,7 +229,7 @@ const AdsPage = ({ history }) => {
<div style={{ postion: "absolute" }}>
{!matches && (
<>
<Fade in={isSearchFieldVisible} timeout={500}>
<Fade in={isSearchFieldVisible} timeout={500}className="ads-page-search-by-title">
{inputNormal}
</Fade>
<Fade in={isSearchFieldVisible} timeout={500}>
@@ -275,7 +275,7 @@ const AdsPage = ({ history }) => {
<di className="active-ads-header-buttons">
<IconButton
className={
"c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding"
"c-btn--primary-outlined c-btn ads-page-btn userPageBtn ml-20px no-padding"
}
onClick={() => handleChangeVisibility(true)}
>
@@ -359,7 +359,7 @@ const AdsPage = ({ history }) => {
<p>Uvek možete dodati novi u samo par jednostavnih koraka</p>
<div className="add-ad add-ad-no-ads">
<IconButton
className="c-btn c-btn--primary add-ad-btn"
className="c-btn ads-page-btn c-btn--primary add-ad-btn"
onClick={handleToggleModal}
>
Dodaj Oglas
@@ -437,14 +437,14 @@ const AdsPage = ({ history }) => {
{ads && ads.length > 0 && (
<div className="add-ad">
<IconButton
className="c-btn c-btn--primary add-ad-btn"
className="c-btn ads-page-btn c-btn--primary add-ad-btn"
onClick={handleToggleModal}
>
+ Oglas
</IconButton>
</div>
)}
</>
</div>
);
};


+ 20
- 17
src/pages/PatternsPage/PatternsPage.js Datei anzeigen

@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
import FilterButton from "../../components/Button/FilterButton";
import { useTheme } from "@mui/system";
import { IconButton, useMediaQuery } from "@mui/material";
import { useMediaQuery } from "@mui/material";
import userPageBtnIcon from "../../assets/images/userPageBtnIcon.png";
import PatternCard from "../../components/Patterns/PatternCard";
import { useDispatch, useSelector } from "react-redux";
@@ -17,6 +17,7 @@ import { setProcessesReq } from "../../store/actions/processes/processesAction";
import { createPatternReq } from "../../store/actions/createPattern/createPatternActions";
import { updatePatternReq } from "../../store/actions/updatePattern/updatePatternActions";
import PatternFilters from "../../components/Patterns/PatternFilters";
import IconButton from "../../components/IconButton/IconButton";

const PatternsPage = ({ history }) => {
const theme = useTheme();
@@ -40,7 +41,7 @@ const PatternsPage = ({ history }) => {
}, []);

useEffect(() => {
if (processes.length > 0) {
if (processes && processes.length > 0) {
setAddPatternCategory(processes[0].id);
const tmpSelectionLevelFilter = processes.map((level) => ({
...level,
@@ -115,7 +116,7 @@ const PatternsPage = ({ history }) => {
};

return (
<>
<div data-testid="patterns-page">
<PatternFilters
openFilterDrawer={openFilterDrawer}
handleClose={closeFilterDrawerHandler}
@@ -230,11 +231,12 @@ const PatternsPage = ({ history }) => {
value={addPatternCategory}
onChange={(e) => setAddPatternCategory(e.target.value)}
>
{processes.map((process) => (
<option key={process.id} value={process.id}>
{process.name}
</option>
))}
{processes &&
processes.map((process) => (
<option key={process.id} value={process.id}>
{process.name}
</option>
))}
</select>
</div>
<div className="add-pattern-modal-form-control">
@@ -307,11 +309,12 @@ const PatternsPage = ({ history }) => {
}))
}
>
{processes.map((process) => (
<option key={process.id} value={process.id}>
{process.name}
</option>
))}
{processes &&
processes.map((process) => (
<option key={process.id} value={process.id}>
{process.name}
</option>
))}
</select>
</div>
<div className="edit-pattern-modal-form-control">
@@ -337,10 +340,10 @@ const PatternsPage = ({ history }) => {
<div>
<h1>Napravljeni Šabloni</h1>
</div>
<div>
<div style={{ display: "flex" }}>
<IconButton
onClick={() => setIsShownEdit((oldState) => !oldState)}
className={`c-btn--primary-outlined editEnableBtn c-btn userPageBtn ${
className={`c-btn--primary-outlined c-btn editEnableBtn userPageBtn ${
isShownEdit && "pattern-header-active-button"
}`}
>
@@ -389,14 +392,14 @@ const PatternsPage = ({ history }) => {
</div>
<div className="patterns-button">
<IconButton
className="c-btn c-btn--primary add-ad-btn"
className="c-btn c-btn--primary add-ad-btn add-pattern-btn"
onClick={() => setOpenAddPatternModal(true)}
>
Dodaj Šablon
</IconButton>
</div>
</div>
</>
</div>
);
};


Laden…
Abbrechen
Speichern