Kaynağa Gözat

unit tests for candidates page on fe

pull/105/head
Dzenis Hadzifejzovic 3 yıl önce
ebeveyn
işleme
4c095f946d

+ 7
- 7
package-lock.json Dosyayı Görüntüle

@@ -13,7 +13,7 @@
"@mui/icons-material": "^5.0.5",
"@mui/material": "^5.0.6",
"@mui/x-data-grid": "^5.0.1",
"@mui/x-date-pickers": "^5.0.9",
"@mui/x-date-pickers": "^5.0.10",
"@reduxjs/toolkit": "^1.5.1",
"@testing-library/jest-dom": "^5.13.0",
"@testing-library/user-event": "^12.8.3",
@@ -3054,9 +3054,9 @@
}
},
"node_modules/@mui/x-date-pickers": {
"version": "5.0.9",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.9.tgz",
"integrity": "sha512-PM3RU8MiwDVi+dSDGJ7ylI0hCe79wSCDfrjghS8ApGGFn/n87S8pUZxsZ5czw3mVRN6VfS2C19peo4nM1Tx+nA==",
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.10.tgz",
"integrity": "sha512-k+SKBqlpYsY49JVs7PORDvnfoXw9nJELfImR/3jTgHqP8hQQ6loFjB9ZFvc5NjV4Jf2NIJFmdcp6g8cO31Wmbg==",
"dependencies": {
"@babel/runtime": "^7.18.9",
"@date-io/core": "^2.15.0",
@@ -26924,9 +26924,9 @@
}
},
"@mui/x-date-pickers": {
"version": "5.0.9",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.9.tgz",
"integrity": "sha512-PM3RU8MiwDVi+dSDGJ7ylI0hCe79wSCDfrjghS8ApGGFn/n87S8pUZxsZ5czw3mVRN6VfS2C19peo4nM1Tx+nA==",
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.10.tgz",
"integrity": "sha512-k+SKBqlpYsY49JVs7PORDvnfoXw9nJELfImR/3jTgHqP8hQQ6loFjB9ZFvc5NjV4Jf2NIJFmdcp6g8cO31Wmbg==",
"requires": {
"@babel/runtime": "^7.18.9",
"@date-io/core": "^2.15.0",

+ 1
- 1
package.json Dosyayı Görüntüle

@@ -8,7 +8,7 @@
"@mui/icons-material": "^5.0.5",
"@mui/material": "^5.0.6",
"@mui/x-data-grid": "^5.0.1",
"@mui/x-date-pickers": "^5.0.9",
"@mui/x-date-pickers": "^5.0.10",
"@reduxjs/toolkit": "^1.5.1",
"@testing-library/jest-dom": "^5.13.0",
"@testing-library/user-event": "^12.8.3",

+ 91
- 0
src/__tests__/ReduxTests/candidatesPageReducer.test.js Dosyayı Görüntüle

@@ -0,0 +1,91 @@
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 CandidatesPage from "../../pages/CandidatesPage/CandidatesPage";
import * as api from "../../request/technologiesRequest";
import { runSaga } from "redux-saga";
import { FETCH_TECHNOLOGIES_REQ } from "../../store/actions/technologies/technologiesActionConstants";
import { getTechnologies } from "../../store/saga/technologiesSaga";
import {
setTechnologies,
setTechnologiesError,
} from "../../store/actions/technologies/technologiesActions";

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

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValueOnce(mockState.technologies.technologies);
// Mock useDispatch hook
spyOnUseDispatch = jest.spyOn(redux, "useDispatch");

// Mock dispatch function returned from useDispatch
mockDispatch = jest.fn();
spyOnUseDispatch.mockReturnValue(mockDispatch);
});

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

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

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

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

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

await runSaga(fakeStore, getTechnologies).done;
expect(api.getAllTechnologies.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setTechnologies(mockedCall.data));
});

// it("should handle technologies load errors in case of failure", async () => {
// const dispatchedActions = [];

// const error = {
// response: {
// data: { message: mockState.technologies.fetchTecnologiesErrorMessage },
// },
// };
// api.getAllTechnologies = jest.fn(() => Promise.reject(error));

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

// await runSaga(fakeStore, getTechnologies).done;

// expect(api.getAllTechnologies.mock.calls.length).toBe(1);
// expect(dispatchedActions).toContainEqual(
// setTechnologiesError(error.response.data.message)
// );
// });
});

+ 69
- 0
src/__tests__/UITests/candidatesPageUI.test.js Dosyayı Görüntüle

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

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

let spyOnUseSelector;

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

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

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

it("Should render button responsible for showing different components inside page", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("candidate-btn")[0]).toBeDefined();
});

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

it("Should render filter button", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("candidate-btn")[2]).toBeDefined();
});

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

// it("input for searching by name should be shown after clicking button for first time",() => {
// const { container } = render(cont);
// fireEvent.click(container.getElementsByClassName("candidate-btn")[1])
// expect(container.getElementsByClassName("proba")[0].style.visibility).toBe('vissible');
// })

// it("should be rendered AdsCandidatesPage component when button for switching to another view is clicked for the first time",() => {
// const { container } = render(cont);
// fireEvent.click(container.getElementsByClassName("candidate-btn")[0])
// expect(container.getElementsByClassName("ads-candidates-container")[0]).toBeDefined();
// })
});

+ 17
- 1
src/assets/styles/components/_candidatesPage.scss Dosyayı Görüntüle

@@ -219,7 +219,7 @@
background-color: white;
position: absolute;
top: 112px;
left: 800px;
right: 70px;
z-index: 1000;
}

@@ -229,6 +229,22 @@
font-style: italic;
}

.cls1 .slick-track{
margin-left: 30px !important;
}

.cls2 .slick-track{
margin-left: 20 !important;
}

.cls3 .slick-track{
margin-left: 20 !important;
}

.cls4 .slick-track{
margin-left: 45px !important;
}

@media only screen and (max-width: 600px) {
.ads-candidates-title {
font-size: 18px;

+ 5
- 5
src/components/IconButton/IconButton.js Dosyayı Görüntüle

@@ -1,20 +1,20 @@
import React, { useRef } from 'react';
import PropType from 'prop-types';
import React, { useRef } from "react";
import PropType from "prop-types";

const IconButton = ({ children, onClick, className }) => {
const buttonRef = useRef(null);

function handleClick(e) {
e.stopPropagation()
e.stopPropagation();
buttonRef.current.blur();
if (typeof onClick === 'function') {
if (typeof onClick === "function") {
onClick();
}
}

return (
<button
data-testid="btn-testid"
data-testid="btn-testid"
type="button"
ref={buttonRef}
onClick={handleClick}

+ 108
- 9
src/mockState.js Dosyayı Görüntüle

@@ -1,4 +1,102 @@
export const mockState = {
technologies: {
technologies: [
{
technologyId: 1,
technologyType: "Backend",
name: ".NET",
isChecked:false
},
{
technologyId: 2,
technologyType: "Other",
name: "Git",
isChecked:false
},
{
technologyId: 3,
technologyType: "Frontend",
name: "HTML/CSS",
isChecked:false
},
],
fetchTecnologiesErrorMessage: "Server error",
},
candidates: {
total: 2,
items: [
{
applicantId: 1,
firstName: "Dzenis",
lastName: "Hadzifejzovic",
position: ".NET Developer",
dateOfApplication: "2022-02-11T00:00:00",
cv: "link",
email: "dzenis@gmail.com",
phoneNumber: "774567",
linkedlnLink: "link1",
githubLink: "link2",
bitBucketLink: null,
experience: 1,
applicationChannel: null,
typeOfEmployment: "Posao",
technologyApplicants: [
{
technology: {
technologyId: 1,
technologyType: "Backend",
name: ".NET",
},
},
],
comments: [],
ads: [
{
id: 10,
title: ".NET Intern",
minimumExperience: 2,
createdAt: "2022-11-14T08:23:00.772",
expiredAt: "2024-12-06T09:53:42.439572",
keyResponsibilities: "KR|KR|KR|KR",
requirements: "RQ|RQ|RQ|RQ",
offer: "OF|OF|OF|OF",
technologies: [],
workHour: "FullTime",
employmentType: "Intership",
},
],
selectionProcesses: [],
},
{
applicantId: 2,
firstName: "Ermin",
lastName: "Bronja",
position: ".NET Developer",
dateOfApplication: "2022-02-11T00:00:00",
cv: "link",
email: "ermin@gmail.com",
phoneNumber: "342424",
linkedlnLink: "link1",
githubLink: "link2",
bitBucketLink: null,
experience: 3,
applicationChannel: null,
typeOfEmployment: "Posao",
technologyApplicants: [
{
technology: {
technologyId: 1,
technologyType: "Backend",
name: ".NET",
},
},
],
comments: [],
ads: [],
selectionProcesses: [],
},
],
},
users: {
users: [
{
@@ -28,7 +126,7 @@ export const mockState = {
toggleEnableErrorMessage: "",
},
selections: {
process:{doneProcess: false},
process: { doneProcess: false },
processes: [
{
id: 1,
@@ -103,7 +201,7 @@ export const mockState = {
firstName: "Meris",
lastName: "Ahmatovic",
},
}
},
],
},
{
@@ -143,14 +241,15 @@ export const mockState = {
},
},
],
}
},
],
selected: {},
fetchSelectionsErrorMessage: "Server error",
statuses:[
{"isChecked":false,"name":"Zakazan"},
{"isChecked":false,"name":"Odrađen"},
{"isChecked":false,"name":"Čeka na zakazivanje"},
{"isChecked":false,"name":"Čeka se odgovor"}]
}
statuses: [
{ isChecked: false, name: "Zakazan" },
{ isChecked: false, name: "Odrađen" },
{ isChecked: false, name: "Čeka na zakazivanje" },
{ isChecked: false, name: "Čeka se odgovor" },
],
},
};

+ 32
- 22
src/pages/CandidatesPage/AdsCandidatesPage.js Dosyayı Görüntüle

@@ -65,7 +65,7 @@ const AdsCandidatesPage = ({ history, search }) => {
settings: {
slidesToShow: 1,
slidesToScroll: 1,
initialSlide:0
initialSlide: 0,
},
},
],
@@ -91,6 +91,14 @@ const AdsCandidatesPage = ({ history, search }) => {
getRef(index.toString()).current.slickNext();
};

const filterByName = (adCandidates) => {
return adCandidates.applicants.filter((candidate) =>
(candidate.firstName + " " + candidate.lastName)
.toLowerCase()
.includes(search.toLowerCase())
);
};

return (
<div className="ads-candidates-container top-cnd">
{adsCandidates.map((adCandidates, index) => (
@@ -102,11 +110,12 @@ const AdsCandidatesPage = ({ history, search }) => {
| {adCandidates.nubmerOfApplicants} prijavljenih
</p>
</div>
<div
className="ads-candidates-slider"
>
{adCandidates.applicants.length > 3 && (
<div className="active-ads-ads-arrows" style={matches ? {marginLeft:36} : {marginLeft:0}}>
<div className="ads-candidates-slider">
{filterByName(adCandidates).length > 3 && (
<div
className="active-ads-ads-arrows"
style={matches ? { marginLeft: 36 } : { marginLeft: 0 }}
>
<button onClick={() => activeAdsArrowLeftHandler(index)}>
<img src={arrow_left} alt="arrow-left" />
</button>
@@ -118,23 +127,24 @@ const AdsCandidatesPage = ({ history, search }) => {
<Slider
{...settings}
ref={setRef(index.toString())}
style={{ width: "100%",marginLeft:adCandidates.applicants.length > 3 ? (matches ? 30 : 20) : (matches ? 30 : 66) }}
className='left-move-candidateAd-page'
className={`left-move-candidateAd-page ${
matches
? filterByName(adCandidates).length > 3
? "cls1"
: "cls2"
: filterByName(adCandidates).length > 3
? "cls3"
: "cls4"
}`}
>
{adCandidates.applicants
.filter((candidate) =>
(candidate.firstName + " " + candidate.lastName)
.toLowerCase()
.includes(search.toLowerCase())
)
.map((candidate, index) => (
<CandidateCard
key={index}
candidate={candidate}
history={history}
/>
))}
{adCandidates.applicants.length <= 4 &&
{filterByName(adCandidates).map((candidate, index) => (
<CandidateCard
key={index}
candidate={candidate}
history={history}
/>
))}
{filterByName(adCandidates).length <= 4 &&
getDummyCandidates(adCandidates.applicants.length)}
</Slider>
</div>

+ 4
- 3
src/pages/CandidatesPage/CandidatesPage.js Dosyayı Görüntüle

@@ -55,7 +55,7 @@ const CandidatesPage = ({ history }) => {
onChange={(e) => setSearch(e.target.value)}
className="candidate-search-field"
onClick={stopPropagation}
style={{ zIndex: 1000 }}
role="input"
/>
</div>
);
@@ -64,6 +64,7 @@ const CandidatesPage = ({ history }) => {
<div
className="main-candidates-container"
onClick={() => setIsSearchFieldVisible(false)}
data-testid="candidates-page"
>
<CandidateFilters
open={toggleFiltersDrawer}
@@ -84,7 +85,7 @@ const CandidatesPage = ({ history }) => {
</p>
)}
<div style={{ postion: "relative" }}>
<Fade in={isSearchFieldVisible} timeout={500}>
<Fade in={isSearchFieldVisible} timeout={500} className="proba">
{input}
</Fade>
<Fade in={isSearchFieldVisible} timeout={500}>
@@ -92,7 +93,7 @@ const CandidatesPage = ({ history }) => {
style={{
position: "absolute",
zIndex: 10000,
marginLeft: 300,
right: 95,
marginTop: 15,
}}
>

+ 11
- 1
src/setupTests.js Dosyayı Görüntüle

@@ -2,4 +2,14 @@
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
import "@testing-library/jest-dom";

window.matchMedia =
window.matchMedia ||
function () {
return {
matches: false,
addListener: function () {},
removeListener: function () {},
};
};

+ 4
- 4
yarn.lock Dosyayı Görüntüle

@@ -1760,10 +1760,10 @@
"prop-types" "^15.8.1"
"reselect" "^4.1.6"

"@mui/x-date-pickers@^5.0.9":
"integrity" "sha512-PM3RU8MiwDVi+dSDGJ7ylI0hCe79wSCDfrjghS8ApGGFn/n87S8pUZxsZ5czw3mVRN6VfS2C19peo4nM1Tx+nA=="
"resolved" "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.9.tgz"
"version" "5.0.9"
"@mui/x-date-pickers@^5.0.10":
"integrity" "sha512-k+SKBqlpYsY49JVs7PORDvnfoXw9nJELfImR/3jTgHqP8hQQ6loFjB9ZFvc5NjV4Jf2NIJFmdcp6g8cO31Wmbg=="
"resolved" "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.10.tgz"
"version" "5.0.10"
dependencies:
"@babel/runtime" "^7.18.9"
"@date-io/core" "^2.15.0"

Loading…
İptal
Kaydet