Преглед изворни кода

removed unnecessary files and fix some small bugs

FE_dev
Dzenis Hadzifejzovic пре 2 година
родитељ
комит
089cb3ff58
100 измењених фајлова са 1285 додато и 12882 уклоњено
  1. 1259
    90
      package-lock.json
  2. 5
    59
      src/AppRoutes.js
  3. 0
    27
      src/__tests__/ReduxTests/Reducers/ad/adReducer.test.js
  4. 0
    46
      src/__tests__/ReduxTests/Reducers/ad/adsReducer.test.js
  5. 0
    26
      src/__tests__/ReduxTests/Reducers/ad/archiveActiveAdReducer.test.js
  6. 0
    30
      src/__tests__/ReduxTests/Reducers/ad/archiveAdsReducer.test.js
  7. 0
    30
      src/__tests__/ReduxTests/Reducers/ad/createAdReducer.test.js
  8. 0
    68
      src/__tests__/ReduxTests/Reducers/addAddTechnologiesReducer.test.js
  9. 0
    26
      src/__tests__/ReduxTests/Reducers/applyForAdReducer.test.js
  10. 0
    35
      src/__tests__/ReduxTests/Reducers/candidateOptionsReducer.test.js
  11. 0
    75
      src/__tests__/ReduxTests/Reducers/candidateReducer.test.js
  12. 0
    66
      src/__tests__/ReduxTests/Reducers/candidatesReducer.test.js
  13. 0
    32
      src/__tests__/ReduxTests/Reducers/patterns/createPatternReducer.test.js
  14. 0
    32
      src/__tests__/ReduxTests/Reducers/patterns/patternApplicantsReducer.test.js
  15. 0
    32
      src/__tests__/ReduxTests/Reducers/patterns/patternReducer.test.js
  16. 0
    50
      src/__tests__/ReduxTests/Reducers/patterns/patternsReducer.test.js
  17. 0
    51
      src/__tests__/ReduxTests/Reducers/patterns/scheduleAppointmentReducer.test.js
  18. 0
    32
      src/__tests__/ReduxTests/Reducers/patterns/updatePatternReducer.test.js
  19. 0
    32
      src/__tests__/ReduxTests/Reducers/processes/applicantWithProcessesReducer.test.js
  20. 0
    37
      src/__tests__/ReduxTests/Reducers/processes/interviewerUpdateReducer.test.js
  21. 0
    36
      src/__tests__/ReduxTests/Reducers/processes/processReducer.test.js
  22. 0
    32
      src/__tests__/ReduxTests/Reducers/processes/processesReducer.test.js
  23. 0
    66
      src/__tests__/ReduxTests/Reducers/processes/statusReducer.test.js
  24. 0
    37
      src/__tests__/ReduxTests/Reducers/processes/statusUpdateReducer.test.js
  25. 0
    22
      src/__tests__/ReduxTests/Reducers/scheduleReducer.test.js
  26. 0
    21
      src/__tests__/ReduxTests/Reducers/screeningTestsReducer.test.js
  27. 0
    29
      src/__tests__/ReduxTests/Reducers/statsReducer.test.js
  28. 0
    34
      src/__tests__/ReduxTests/Reducers/statusUpdateReducer.test.js
  29. 0
    66
      src/__tests__/ReduxTests/Reducers/technologiesReducer.test.js
  30. 0
    106
      src/__tests__/ReduxTests/adsCandidatesPageReducer.test.js
  31. 0
    407
      src/__tests__/ReduxTests/adsReducer.test.js
  32. 0
    103
      src/__tests__/ReduxTests/applicantsSaga.test.js
  33. 0
    182
      src/__tests__/ReduxTests/candidateDetailsPageReducer.test.js
  34. 0
    91
      src/__tests__/ReduxTests/candidatesPageReducer.test.js
  35. 0
    407
      src/__tests__/ReduxTests/candidatesSaga.test.js
  36. 0
    431
      src/__tests__/ReduxTests/patternsReducer.test.js
  37. 0
    566
      src/__tests__/ReduxTests/processesReducer.test.js
  38. 0
    112
      src/__tests__/ReduxTests/schedulePageReducer.test.js
  39. 0
    109
      src/__tests__/ReduxTests/screeningTestsSaga.test.js
  40. 0
    97
      src/__tests__/ReduxTests/statsPageReducer.test.js
  41. 0
    127
      src/__tests__/ReduxTests/tableViewPageReducer.test.js
  42. 0
    80
      src/__tests__/UITests/SelectionCardUI.test.js
  43. 0
    143
      src/__tests__/UITests/SelectionComponentUI.test.js
  44. 0
    62
      src/__tests__/UITests/adDetailsCandidateCardUI.test.js
  45. 0
    104
      src/__tests__/UITests/adDetailsPageUI.test.js
  46. 0
    101
      src/__tests__/UITests/adsCandidatesPageUI.test.js
  47. 0
    154
      src/__tests__/UITests/adsPageUI.test.js
  48. 0
    53
      src/__tests__/UITests/applicantSelectionPage.test.js
  49. 0
    110
      src/__tests__/UITests/applyForAdFirstStageUI.test.js
  50. 0
    106
      src/__tests__/UITests/applyForAdFourthStageUI.test.js
  51. 0
    125
      src/__tests__/UITests/applyForAdSecondStageUI.test.js
  52. 0
    110
      src/__tests__/UITests/applyForAdThirdStageUI.test.js
  53. 0
    199
      src/__tests__/UITests/candidateDetailsPageUI.test.js
  54. 0
    72
      src/__tests__/UITests/candidateFilterUI.test.js
  55. 0
    120
      src/__tests__/UITests/candidatesPageUI.test.js
  56. 0
    86
      src/__tests__/UITests/createAdPageUI.test.js
  57. 0
    46
      src/__tests__/UITests/dayComponentUI.test.js
  58. 0
    88
      src/__tests__/UITests/dayDetailsComponentUI.test.js
  59. 0
    121
      src/__tests__/UITests/patternDetailsPageUI.test.js
  60. 0
    129
      src/__tests__/UITests/patternsPageUI.test.js
  61. 0
    110
      src/__tests__/UITests/schedulePageUI.test.js
  62. 0
    143
      src/__tests__/UITests/selectionProcessesPageUI.test.js
  63. 0
    57
      src/__tests__/UITests/statsAdComponentUI.test.js
  64. 0
    92
      src/__tests__/UITests/statsPageUI.test.js
  65. 0
    149
      src/__tests__/UITests/tableViewPageUI.test.js
  66. 11
    1219
      src/assets/styles/components/_ads.scss
  67. 0
    73
      src/assets/styles/components/_candidate-card.scss
  68. 0
    577
      src/assets/styles/components/_candidatePage.scss
  69. 0
    244
      src/assets/styles/components/_candidatesPage.scss
  70. 0
    95
      src/assets/styles/components/_day-component.scss
  71. 3
    197
      src/assets/styles/components/_day-details-component.scss
  72. 0
    461
      src/assets/styles/components/_patterns.scss
  73. 0
    74
      src/assets/styles/components/_schedulePage.scss
  74. 4
    580
      src/assets/styles/components/_selectionProcessPage.scss
  75. 0
    238
      src/assets/styles/components/_statistics.scss
  76. 0
    81
      src/components/Ads/Ad.js
  77. 0
    65
      src/components/Ads/AdDetailsCandidateCard.js
  78. 0
    215
      src/components/Ads/AdFilters.js
  79. 0
    176
      src/components/Ads/ApplyForAd.js
  80. 0
    124
      src/components/Ads/ApplyForAdFirstStage.js
  81. 0
    119
      src/components/Ads/ApplyForAdFourthStage.js
  82. 0
    150
      src/components/Ads/ApplyForAdSecondStage.js
  83. 0
    101
      src/components/Ads/ApplyForAdThirdStage.js
  84. 0
    52
      src/components/Ads/ArchiveAd.js
  85. 0
    55
      src/components/Ads/StatsAd.js
  86. 0
    33
      src/components/Candidates/CandidateAd.js
  87. 0
    66
      src/components/Candidates/CandidateCard.js
  88. 0
    320
      src/components/Candidates/CandidateFilters.js
  89. 0
    1
      src/components/Files/TreeViewFiles.js
  90. 1
    1
      src/components/MUI/NavbarComponent.js
  91. 0
    53
      src/components/Patterns/PatternCard.js
  92. 0
    149
      src/components/Patterns/PatternFilters.js
  93. 0
    45
      src/components/Schedules/DayComponent.js
  94. 0
    208
      src/components/Schedules/DayDetailsComponent.js
  95. 2
    2
      src/components/Section/MainContainer.js
  96. 0
    91
      src/components/Selection/ApplicantSelection.js
  97. 0
    159
      src/components/Selection/Selection.js
  98. 0
    195
      src/components/Selection/SelectionCard.js
  99. 0
    146
      src/components/Selection/SelectionFilter.js
  100. 0
    0
      src/constants/keyCodeConstants.js

+ 1259
- 90
package-lock.json
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку


+ 5
- 59
src/AppRoutes.js Прегледај датотеку

@@ -5,8 +5,6 @@ import { refreshUserToken } from "./store/actions/login/loginActions";
import { useLocation } from "react-router-dom";

import {
// ADS_PAGE,
// AD_DETAILS_PAGE,
FORGOT_PASSWORD_PAGE,
FORGOT_PASSWORD_CONFIRMATION_PAGE,
NOT_FOUND_PAGE,
@@ -14,25 +12,14 @@ import {
BASE_PAGE,
RESET_PASSWORD_PAGE,
USERS_PAGE,
// CANDIDATES_PAGE,
USER_DETAILS_PAGE,
// CANDIDATES_DETAILS_PAGE,
// SELECTION_PROCESS_PAGE,
// SELECTION_PROCESS_OF_APPLICANT_PAGE,
// PATTERNS_PAGE,
// PATTERN_DETAILS_PAGE,
// SCHEDULE_PAGE,
// STATS_PAGE,
REGISTER_PAGE,
// CREATE_AD_PAGE,
// FILES_VIEW_PAGE,
ADD_FILE,
FILES_PAGE,
FILES_DEPTH_PAGE
FILES_DEPTH_PAGE,
REGISTER_PAGE,
} from "./constants/pages";

import LoginPage from "./pages/LoginPage/LoginPageMUI";
// import AdsPage from "./pages/AdsPage/AdsPage";
import NotFoundPage from "./pages/ErrorPages/NotFoundPage";
import ErrorPage from "./pages/ErrorPages/ErrorPage";
import ForgotPasswordPage from "./pages/ForgotPasswordPage/ForgotPasswordPageMUI";
@@ -40,19 +27,8 @@ import PrivateRoute from "./components/Router/PrivateRoute";
import ForgotPasswordConfirmationPage from "./pages/ForgotPasswordPage/ForgotPasswordConfirmationPageMUI";
import ResetPasswordPage from "./pages/ForgotPasswordPage/ResetPasswordPageMUI";
import UsersPage from "./pages/UsersPage/UsersPage";
// import CandidatesPage from "./pages/CandidatesPage/CandidatesPage";
// import AdDetailsPage from "./pages/AdsPage/AdDetailsPage";
import UserDetails from "./pages/UsersPage/UserDetails";
// import CandidateDetailsPage from "./pages/CandidatesPage/CandidateDetailsPage";
// import SelectionProcessPage from "./pages/SelectionProcessPage/SelectionProcessPage";
// import SelectionProcessOfApplicantPage from "./pages/SelectionProcessPage/SelectionProcessOfApplicantPage";
// import PatternsPage from "./pages/PatternsPage/PatternsPage";
// import PatternDetailsPage from "./pages/PatternsPage/PatternDetailsPage";
// import SchedulePage from "./pages/SchedulePage/SchedulePage";
// import StatsPage from "./pages/StatsPage/StatsPage";
import RegisterPage from "./pages/RegisterPage/RegisterPage";
// import CreateAdPage from "./pages/AdsPage/CreateAdPage";
// import FilesViewPage from "./pages/FilesPage/FilesViewPage";
import AddFile from "./pages/FilesPage/AddFile";
import FilesPage from "./pages/FilesPage/FilesPage";

@@ -70,7 +46,6 @@ const AppRoutes = () => {
<Switch>
<Route exact path={BASE_PAGE} component={LoginPage} />
<Route path={NOT_FOUND_PAGE} component={NotFoundPage} />
{/* <Route path={USERS_PAGE} component={UsersPage} /> */}
<Route path={ERROR_PAGE} component={ErrorPage} />
<Route path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} />
<Route
@@ -79,40 +54,11 @@ const AppRoutes = () => {
/>
<Route exact path={REGISTER_PAGE} component={RegisterPage} />
<Route path={RESET_PASSWORD_PAGE} component={ResetPasswordPage} />
{/* <PrivateRoute exact path={ADS_PAGE} component={AdsPage} /> */}
{/* <PrivateRoute exact path={AD_DETAILS_PAGE} component={AdDetailsPage} /> */}
<PrivateRoute exact path={USER_DETAILS_PAGE} component={UserDetails} />
<PrivateRoute exact path={USERS_PAGE} component={UsersPage} />
{/* <PrivateRoute exact path={CANDIDATES_PAGE} component={CandidatesPage} />
<PrivateRoute exact path={CREATE_AD_PAGE} component={CreateAdPage} /> */}
{/* <PrivateRoute exact path={FILES_PAGE} component={FilesPage} /> */}
{/* <PrivateRoute exact path={FILES_VIEW_PAGE} component={FilesViewPage} /> */}
<PrivateRoute exact path={ADD_FILE} component={AddFile}/>
<PrivateRoute exact path={FILES_PAGE} component={FilesPage}/>
<PrivateRoute exact path={FILES_DEPTH_PAGE} component={FilesPage}/>
{/* <PrivateRoute
exact
path={CANDIDATES_DETAILS_PAGE}
component={CandidateDetailsPage}
/>
<PrivateRoute
exact
path={SELECTION_PROCESS_PAGE}
component={SelectionProcessPage}
/>
<PrivateRoute
exact
path={SELECTION_PROCESS_OF_APPLICANT_PAGE}
component={SelectionProcessOfApplicantPage}
/>
<PrivateRoute
exact
path={PATTERN_DETAILS_PAGE}
component={PatternDetailsPage}
/>
<PrivateRoute exact path={PATTERNS_PAGE} component={PatternsPage} />
<PrivateRoute exact path={SCHEDULE_PAGE} component={SchedulePage} />
<PrivateRoute exact path={STATS_PAGE} component={StatsPage} /> */}
<PrivateRoute exact path={ADD_FILE} component={AddFile} />
<PrivateRoute exact path={FILES_PAGE} component={FilesPage} />
<PrivateRoute exact path={FILES_DEPTH_PAGE} component={FilesPage} />
<Redirect from="*" to={NOT_FOUND_PAGE} />
</Switch>
);

+ 0
- 27
src/__tests__/ReduxTests/Reducers/ad/adReducer.test.js Прегледај датотеку

@@ -1,27 +0,0 @@
import reducer from "../../../../store/reducers/ad/adReducer";
import expect from "expect";
import { setAd, setAdError } from "../../../../store/actions/ad/adActions";
import { mockState } from "../../../../mockState";

describe("ad reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
ad: null,
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, setAdError("Error"))).toEqual({
ad: null,
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(reducer(undefined, setAd(mockState.ads.ads[0]))).toEqual({
ad: mockState.ads.ads[0],
errorMessage: "",
});
});
});

+ 0
- 46
src/__tests__/ReduxTests/Reducers/ad/adsReducer.test.js Прегледај датотеку

@@ -1,46 +0,0 @@
import reducer from "../../../../store/reducers/ad/adsReducer";
import expect from "expect";
import {
setAds,
setAdsError,
setFilteredAds,
setFilteredAdsError,
} from "../../../../store/actions/ads/adsAction";
import { mockState } from "../../../../mockState";

describe("ads reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
ads: [],
errorMessage: "",
});
});

it("should set the state error when setAdsError is called", () => {
expect(reducer(undefined, setAdsError("Error"))).toEqual({
ads: [],
errorMessage: "Error",
});
});

it("should set ads when setAds is called", () => {
expect(reducer(undefined, setAds(mockState.ads.ads[0]))).toEqual({
ads: mockState.ads.ads[0],
errorMessage: "",
});
});

it("should set the state error when setFilteredAdsError is called", () => {
expect(reducer(undefined, setFilteredAdsError("Error"))).toEqual({
ads: [],
errorMessage: "Error",
});
});

it("should set ads when setFilteredAds is called", () => {
expect(reducer(undefined, setFilteredAds(mockState.ads.ads[0]))).toEqual({
ads: mockState.ads.ads[0],
errorMessage: "",
});
});
});

+ 0
- 26
src/__tests__/ReduxTests/Reducers/ad/archiveActiveAdReducer.test.js Прегледај датотеку

@@ -1,26 +0,0 @@
import reducer from "../../../../store/reducers/ad/archiveActiveAdReducer";
import expect from "expect";
import {
archiveActiveAd,
archiveActiveAdError,
} from "../../../../store/actions/archiveActiveAd/archiveActiveAdActions";

describe("archiveActiveAd reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, archiveActiveAdError("Error"))).toEqual({
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(reducer(undefined, archiveActiveAd())).toEqual({
errorMessage: "",
});
});
});

+ 0
- 30
src/__tests__/ReduxTests/Reducers/ad/archiveAdsReducer.test.js Прегледај датотеку

@@ -1,30 +0,0 @@
import reducer from "../../../../store/reducers/ad/archiveAdsReducer";
import expect from "expect";
import {
setArchiveAds,
setArchiveAdsError,
} from "../../../../store/actions/archiveAds/archiveAdsActions";
import { mockState } from "../../../../mockState";

describe("archiveAdsReducer reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
archiveAds: [],
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, setArchiveAdsError("Error"))).toEqual({
archiveAds: [],
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(reducer(undefined, setArchiveAds(mockState.ads.ads))).toEqual({
archiveAds: mockState.ads.ads,
errorMessage: "",
});
});
});

+ 0
- 30
src/__tests__/ReduxTests/Reducers/ad/createAdReducer.test.js Прегледај датотеку

@@ -1,30 +0,0 @@
import reducer from "../../../../store/reducers/ad/createAdReducer";
import expect from "expect";
import {
setCreateAd,
setCreateAdError,
} from "../../../../store/actions/createAd/createAdActions";
import { mockState } from "../../../../mockState";

describe("createAd reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
ad: null,
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, setCreateAdError("Error"))).toEqual({
ad: null,
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(reducer(undefined, setCreateAd(mockState.ads.ads[0]))).toEqual({
ad: mockState.ads.ads[0],
errorMessage: "",
});
});
});

+ 0
- 68
src/__tests__/ReduxTests/Reducers/addAddTechnologiesReducer.test.js Прегледај датотеку

@@ -1,68 +0,0 @@
import reducer from "../../../store/reducers/technology/addAddTechnologiesReducer";
import expect from "expect";
import {
changeIsCheckedAddAdValue,
resetIsCheckedAddAdValue,
setTechnologiesAddAd,
setTechnologiesAddAdError,
} from "../../../store/actions/addAdTechnologies/addAdTechnologiesActions";

describe("ad technologies reducer", () => {
it("should set techologies", () => {
expect(
reducer(undefined, setTechnologiesAddAd(["tech1", "tech2"]))
).toEqual({
technologies: ["tech1", "tech2"],
errorMessage: "",
});
});

it("should set error", () => {
expect(reducer(undefined, setTechnologiesAddAdError("Error"))).toEqual({
technologies: [],
errorMessage: "Error",
});
});

it("should check tech", () => {
expect(
reducer(
{
technologies: [
{ technologyId: 1, name: "T1", isChecked: false },
{ technologyId: 2, name: "T2", isChecked: false },
],
errorMessage: "",
},
changeIsCheckedAddAdValue(1)
)
).toEqual({
technologies: [
{ technologyId: 1, name: "T1", isChecked: true },
{ technologyId: 2, name: "T2", isChecked: false },
],
errorMessage: "",
});
});

it("should reset checked tech", () => {
expect(
reducer(
{
technologies: [
{ technologyId: 1, name: "T1", isChecked: true },
{ technologyId: 2, name: "T2", isChecked: true },
],
errorMessage: "",
},
resetIsCheckedAddAdValue()
)
).toEqual({
technologies: [
{ technologyId: 1, name: "T1", isChecked: false },
{ technologyId: 2, name: "T2", isChecked: false },
],
errorMessage: "",
});
});
});

+ 0
- 26
src/__tests__/ReduxTests/Reducers/applyForAdReducer.test.js Прегледај датотеку

@@ -1,26 +0,0 @@
import reducer from "../../../store/reducers/applicants/applyForAdReducer";
import expect from "expect";
import {
applyForAd,
applyForAdError,
} from "../../../store/actions/applyForAd/applyForAdActions";

describe("applyForAd reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, applyForAdError("Error"))).toEqual({
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(reducer(undefined, applyForAd())).toEqual({
errorMessage: "",
});
});
});

+ 0
- 35
src/__tests__/ReduxTests/Reducers/candidateOptionsReducer.test.js Прегледај датотеку

@@ -1,35 +0,0 @@
import reducer from "../../../store/reducers/candidates/candidateOptionsReducer";
import expect from "expect";
import {
fetchCandidateOptionsSuccess,
fetchCandidateOptionsError,
} from "../../../store/actions/candidates/candidatesActions";
import { mockState } from "../../../mockState";

describe("candidatesOptions reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
options: [],
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, fetchCandidateOptionsError("Error"))).toEqual({
options: [],
errorMessage: "Error",
});
});

it("should set options", () => {
expect(
reducer(
undefined,
fetchCandidateOptionsSuccess(mockState.options.options)
)
).toEqual({
options: mockState.options.options,
errorMessage: "",
});
});
});

+ 0
- 75
src/__tests__/ReduxTests/Reducers/candidateReducer.test.js Прегледај датотеку

@@ -1,75 +0,0 @@
import reducer from "../../../store/reducers/candidate/candidateReducer";
import expect from "expect";
import {
fetchCandidateSuccess,
fetchCandidateError,
createCandidateCommentSuccess,
createCandidateCommentError,
deleteCandidateError,
deleteCandidateSuccess,
} from "../../../store/actions/candidate/candidateActions";
import { mockState } from "../../../mockState";

describe("candidate reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
candidate: {},
errorMessage: "",
});
});

it("should set the state error when fetchCandidateError is called", () => {
expect(reducer(undefined, fetchCandidateError("Error"))).toEqual({
candidate: {},
errorMessage: "Error",
});
});

it("should set candidate when fetchCandidateSuccess is called", () => {
expect(
reducer(undefined, fetchCandidateSuccess(mockState.candidate.candidate))
).toEqual({
candidate: mockState.candidate.candidate,
errorMessage: "",
});
});

it("should set the state error when createCandidateCommentError is called", () => {
expect(reducer(undefined, createCandidateCommentError("Error"))).toEqual({
candidate: {},
errorMessage: "Error",
});
});

// problem with comparing dateOfSending property of two states
// it("should add new comment for canidate", () => {
// const obj = {
// myObj: { content: "sfsdfsd" },
// user: mockState.user.user,
// };
// expect(
// reducer(
// { candidate: mockState.candidate.candidate, errorMessage: "" },
// createCandidateCommentSuccess(obj)
// )
// ).toEqual({
// candidate: { ...mockState.candidate.candidate, obj },
// errorMessage: "",
// });
// });

it("should set the state error when deleteCandidateError is called", () => {
expect(reducer(undefined, deleteCandidateError("Error"))).toEqual({
candidate: {},
errorMessage: "Error",
});
});

it("should set candidate when deleteCandidateSuccess is called", () => {
fetchCandidateSuccess(mockState.candidate.candidate);
expect(reducer(undefined, deleteCandidateSuccess())).toEqual({
candidate: {},
errorMessage: "",
});
});
});

+ 0
- 66
src/__tests__/ReduxTests/Reducers/candidatesReducer.test.js Прегледај датотеку

@@ -1,66 +0,0 @@
import reducer from "../../../store/reducers/candidates/candidatesReducer";
import expect from "expect";
import {
filterCandidatesError,
filterCandidatesSuccess,
fetchAdsCandidatesSuccess,
fetchAdsCandidatesError,
} from "../../../store/actions/candidates/candidatesActions";
import { mockState } from "../../../mockState";

describe("candidates reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
candidates: [],
adsCandidates: [],
errorMessage: "",
pagination: 0,
});
});

it("should set the state error when filterCandidates is called", () => {
expect(reducer(undefined, filterCandidatesError("Error"))).toEqual({
candidates: [],
adsCandidates: [],
errorMessage: "Error",
pagination: 0,
});
});

it("should set candidates and pagination", () => {
expect(
reducer(
undefined,
filterCandidatesSuccess({ items: [mockState.candidates[0]], total: 1 })
)
).toEqual({
candidates: [mockState.candidates[0]],
adsCandidates: [],
errorMessage: "",
pagination: 1,
});
});

it("should set the state error when fetchAdsCandidates is called ", () => {
expect(reducer(undefined, fetchAdsCandidatesError("Error"))).toEqual({
candidates: [],
adsCandidates: [],
errorMessage: "Error",
pagination: 0,
});
});

it("should set adsCandidates", () => {
expect(
reducer(
undefined,
fetchAdsCandidatesSuccess(mockState.candidates.adsCandidates)
)
).toEqual({
candidates: [],
adsCandidates: mockState.candidates.adsCandidates,
errorMessage: "",
pagination: 0,
});
});
});

+ 0
- 32
src/__tests__/ReduxTests/Reducers/patterns/createPatternReducer.test.js Прегледај датотеку

@@ -1,32 +0,0 @@
import reducer from "../../../../store/reducers/pattern/createPatternReducer";
import expect from "expect";
import {
createPattern,
createPatternError,
} from "../../../../store/actions/createPattern/createPatternActions";
import { mockState } from "../../../../mockState";

describe("createPattern reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
pattern: null,
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, createPatternError("Error"))).toEqual({
pattern: null,
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(
reducer(undefined, createPattern(mockState.patterns.patterns))
).toEqual({
pattern: mockState.patterns.patterns,
errorMessage: "",
});
});
});

+ 0
- 32
src/__tests__/ReduxTests/Reducers/patterns/patternApplicantsReducer.test.js Прегледај датотеку

@@ -1,32 +0,0 @@
import reducer from "../../../../store/reducers/pattern/patternApplicantsReducer";
import expect from "expect";
import {
setPatternApplicants,
setPatternApplicantsError,
} from "../../../../store/actions/patternApplicants/patternApplicantsActions";
import { mockState } from "../../../../mockState";

describe("patternApplicants reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
patternApplicants: [],
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, setPatternApplicantsError("Error"))).toEqual({
patternApplicants: [],
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(
reducer(undefined, setPatternApplicants(mockState.patterns.patterns))
).toEqual({
patternApplicants: mockState.patterns.patterns,
errorMessage: "",
});
});
});

+ 0
- 32
src/__tests__/ReduxTests/Reducers/patterns/patternReducer.test.js Прегледај датотеку

@@ -1,32 +0,0 @@
import reducer from "../../../../store/reducers/pattern/patternReducer";
import expect from "expect";
import {
setPattern,
setPatternError,
} from "../../../../store/actions/pattern/patternActions";
import { mockState } from "../../../../mockState";

describe("pattern reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
pattern: null,
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, setPatternError("Error"))).toEqual({
pattern: null,
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(
reducer(undefined, setPattern(mockState.patterns.patterns[0]))
).toEqual({
pattern: mockState.patterns.patterns[0],
errorMessage: "",
});
});
});

+ 0
- 50
src/__tests__/ReduxTests/Reducers/patterns/patternsReducer.test.js Прегледај датотеку

@@ -1,50 +0,0 @@
import reducer from "../../../../store/reducers/pattern/patternsReducer";
import expect from "expect";
import {
setPatterns,
setPatternsError,
setFilteredPatterns,
setFilteredPatternsError,
} from "../../../../store/actions/patterns/patternsActions";
import { mockState } from "../../../../mockState";

describe("patterns reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
patterns: [],
errorMessage: "",
});
});

it("should set the state error when setPatternsError is called", () => {
expect(reducer(undefined, setPatternsError("Error"))).toEqual({
patterns: [],
errorMessage: "Error",
});
});

it("should set the state success when setPatterns is called", () => {
expect(
reducer(undefined, setPatterns(mockState.patterns.patterns))
).toEqual({
patterns: mockState.patterns.patterns,
errorMessage: "",
});
});

it("should set the state error when setFilteredPatternsError is called", () => {
expect(reducer(undefined, setFilteredPatternsError("Error"))).toEqual({
patterns: [],
errorMessage: "Error",
});
});

it("should set the state success when setFilteredPatterns is called", () => {
expect(
reducer(undefined, setFilteredPatterns(mockState.patterns.patterns))
).toEqual({
patterns: mockState.patterns.patterns,
errorMessage: "",
});
});
});

+ 0
- 51
src/__tests__/ReduxTests/Reducers/patterns/scheduleAppointmentReducer.test.js Прегледај датотеку

@@ -1,51 +0,0 @@
import reducer from "../../../../store/reducers/pattern/scheduleAppointmentReducer";
import expect from "expect";
import {
scheduleAppointment,
scheduleAppointmentError,
clearNotSentEmailsArray,
} from "../../../../store/actions/scheduleAppointment/scheduleAppointmentActions";

describe("patterns reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
notSentEmails: null,
errorMessage: "",
});
});

it("should set the state error when setScheduleAppointmentErrorMessage is called", () => {
expect(reducer(undefined, scheduleAppointmentError("Error"))).toEqual({
notSentEmails: null,
errorMessage: "Error",
});
});

it("should set the state success when scheduleAppointment with an empty array as argument is called", () => {
expect(
reducer(undefined, scheduleAppointment({ notSentEmails: [] }))
).toEqual({
notSentEmails: [],
errorMessage: "",
});
});

it("should set the state success when scheduleAppointment with an null as argument is called", () => {
expect(reducer(undefined, scheduleAppointment(null))).toEqual({
notSentEmails: null,
errorMessage: "",
});
});

it("should set the state success when clearNotSentEmailsArray is called", () => {
expect(
reducer(
{ notSentEmails: [], errorMessage: "" },
clearNotSentEmailsArray()
)
).toEqual({
notSentEmails: null,
errorMessage: "",
});
});
});

+ 0
- 32
src/__tests__/ReduxTests/Reducers/patterns/updatePatternReducer.test.js Прегледај датотеку

@@ -1,32 +0,0 @@
import reducer from "../../../../store/reducers/pattern/updatePatternReducer";
import expect from "expect";
import {
updatePattern,
updatePatternError,
} from "../../../../store/actions/updatePattern/updatePatternActions";
import { mockState } from "../../../../mockState";

describe("updatePattern reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
pattern: null,
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, updatePatternError("Error"))).toEqual({
pattern: null,
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(
reducer(undefined, updatePattern(mockState.patterns.patterns[0]))
).toEqual({
pattern: mockState.patterns.patterns[0],
errorMessage: "",
});
});
});

+ 0
- 32
src/__tests__/ReduxTests/Reducers/processes/applicantWithProcessesReducer.test.js Прегледај датотеку

@@ -1,32 +0,0 @@
import reducer from "../../../../store/reducers/processes/applicantWithProcessesReducer";
import expect from "expect";
import {
setApplicant,
setApplicantError
} from "../../../../store/actions/processes/applicantAction";
import { mockState } from "../../../../mockState";

describe("createPattern reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
applicant: {},
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, setApplicantError("Error"))).toEqual({
applicant: {},
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(
reducer(undefined, setApplicant(mockState.candidate.candidate))
).toEqual({
applicant: mockState.candidate.candidate,
errorMessage: "",
});
});
});

+ 0
- 37
src/__tests__/ReduxTests/Reducers/processes/interviewerUpdateReducer.test.js Прегледај датотеку

@@ -1,37 +0,0 @@
import reducer from "../../../../store/reducers/processes/interviewerUpdateReducer";
import expect from "expect";
import {
setUpdateInterviewerSucc,
setUpdateInterviewerReq,
setUpdateInterviewerErr
} from "../../../../store/actions/processes/processAction";

describe("interviewerUpdateReducer reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
updateSuccess: false,
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, setUpdateInterviewerErr("Error"))).toEqual({
updateSuccess: false,
errorMessage: "Error",
});
});

it("should set the updateSuccess to true", () => {
expect(reducer(undefined, setUpdateInterviewerSucc())).toEqual({
updateSuccess: true,
errorMessage: "",
});
});

it("should set the updateSuccess to false", () => {
expect(reducer(undefined, setUpdateInterviewerReq())).toEqual({
updateSuccess: false,
errorMessage: "",
});
});
});

+ 0
- 36
src/__tests__/ReduxTests/Reducers/processes/processReducer.test.js Прегледај датотеку

@@ -1,36 +0,0 @@
import reducer from "../../../../store/reducers/processes/processReducer";
import expect from "expect";
import {
setDoneProcess,
setDoneProcessError,
} from "../../../../store/actions/processes/processAction";

describe("process reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
doneProcess: false,
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, setDoneProcessError("Error"))).toEqual({
doneProcess: false,
errorMessage: "Error",
});
});

it("should set the doneProcess to true", () => {
expect(reducer(undefined, setDoneProcess(true))).toEqual({
doneProcess: true,
errorMessage: "",
});
});

it("should set the doneProcess to false", () => {
expect(reducer(undefined, setDoneProcess(false))).toEqual({
doneProcess: false,
errorMessage: "",
});
});
});

+ 0
- 32
src/__tests__/ReduxTests/Reducers/processes/processesReducer.test.js Прегледај датотеку

@@ -1,32 +0,0 @@
import reducer from "../../../../store/reducers/processes/processesReducer";
import expect from "expect";
import {
setProcesses,
setProcessesError,
} from "../../../../store/actions/processes/processesAction";
import { mockState } from "../../../../mockState";

describe("processes reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
processes: [],
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, setProcessesError("Error"))).toEqual({
processes: [],
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(
reducer(undefined, setProcesses(mockState.patterns.processes))
).toEqual({
processes: mockState.patterns.processes,
errorMessage: "",
});
});
});

+ 0
- 66
src/__tests__/ReduxTests/Reducers/processes/statusReducer.test.js Прегледај датотеку

@@ -1,66 +0,0 @@
import reducer from "../../../../store/reducers/processes/statusReducer";
import expect from "expect";
import {
setStatuses,
setStatusesError,
changeStatusIsCheckedValue,
} from "../../../../store/actions/processes/statusAction";

describe("status reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
statuses: [],
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, setStatusesError("Error"))).toEqual({
statuses: [],
errorMessage: "Error",
});
});

it("should set the state success", () => {
expect(reducer(undefined, setStatuses(["option1", "option2"]))).toEqual({
statuses: ["option1", "option2"],
errorMessage: "",
});
});

it("should not change status because there is no option with name which we send as argument", async () => {
expect(
reducer(
{
statuses: [{ name: "option1" }, { name: "option2" }],
errorMessage: "",
},
changeStatusIsCheckedValue("option3")
)
).toEqual({
statuses: [{ name: "option1" }, { name: "option2" }],
errorMessage: "",
});
});

it("should not change status because there is option with name which we send as argument", async () => {
expect(
reducer(
{
statuses: [
{ name: "option1", isChecked: false },
{ name: "option2", isChecked: false },
],
errorMessage: "",
},
changeStatusIsCheckedValue("option1")
)
).toEqual({
statuses: [
{ name: "option1", isChecked: true },
{ name: "option2", isChecked: false },
],
errorMessage: "",
});
});
});

+ 0
- 37
src/__tests__/ReduxTests/Reducers/processes/statusUpdateReducer.test.js Прегледај датотеку

@@ -1,37 +0,0 @@
import reducer from "../../../../store/reducers/processes/statusUpdateReducer";
import expect from "expect";
import {
setUpdateStatusReq,
setUpdateStatusSucc,
setUpdateStatusErr,
} from "../../../../store/actions/processes/processAction";

describe("process reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
success: false,
errorMessage: "",
});
});

it("should set the state error", () => {
expect(reducer(undefined, setUpdateStatusErr("Error"))).toEqual({
success: false,
errorMessage: "Error",
});
});

it("should set the success to true", () => {
expect(reducer(undefined, setUpdateStatusSucc())).toEqual({
success: true,
errorMessage: "",
});
});

it("should set the success to false", () => {
expect(reducer(undefined, setUpdateStatusReq())).toEqual({
success: false,
errorMessage: "",
});
});
});

+ 0
- 22
src/__tests__/ReduxTests/Reducers/scheduleReducer.test.js Прегледај датотеку

@@ -1,22 +0,0 @@
import reducer from "../../../store/reducers/schedule/scheduleReducer";
import expect from "expect";
import {
fetchScheduleError,
fetchScheduleSuccess,
} from "../../../store/actions/schedule/scheduleActions";

describe("schedule reducer", () => {
it("should set schedule", () => {
expect(reducer(undefined, fetchScheduleSuccess(["1", "2"]))).toEqual({
schedule: ["1", "2"],
errorMessage: "",
});
});

it("should set error", () => {
expect(reducer(undefined, fetchScheduleError("Error"))).toEqual({
schedule: [],
errorMessage: "Error",
});
});
});

+ 0
- 21
src/__tests__/ReduxTests/Reducers/screeningTestsReducer.test.js Прегледај датотеку

@@ -1,21 +0,0 @@
import reducer from "../../../store/reducers/screeningTests/screeningTestsReducer";
import expect from "expect";
import { fetchScreeningTestsError, fetchScreeningTestsSuccess } from "../../../store/actions/screeningTests/screeningTestActions";

describe("screening tests reducer", () => {
it("should set tests", () => {
expect(
reducer(undefined, fetchScreeningTestsSuccess(["test1", "test2"]))
).toEqual({
screeningTests: ["test1", "test2"],
errorMessage: "",
});
});

it("should set error", () => {
expect(reducer(undefined, fetchScreeningTestsError("Error"))).toEqual({
screeningTests: [],
errorMessage: "Error",
});
});
});

+ 0
- 29
src/__tests__/ReduxTests/Reducers/statsReducer.test.js Прегледај датотеку

@@ -1,29 +0,0 @@
import reducer from "../../../store/reducers/stats/statsReducer";
import expect from "expect";
import {
getStatsError,
getStatsSuccess,
} from "../../../store/actions/stats/statsActions";

describe("stats reducer", () => {
it("should return the initial state", () => {
expect(reducer(undefined, {})).toEqual({
fetchStatsErrorMessage: "",
stats: {},
});
});

it("should set error message", () => {
expect(reducer(undefined, getStatsError("Error"))).toEqual({
stats: {},
fetchStatsErrorMessage: "Error",
});
});

it("Should set state stats", () => {
expect(reducer(undefined, getStatsSuccess({ data: "mockData" }))).toEqual({
stats: { data: "mockData" },
fetchStatsErrorMessage: "",
});
});
});

+ 0
- 34
src/__tests__/ReduxTests/Reducers/statusUpdateReducer.test.js Прегледај датотеку

@@ -1,34 +0,0 @@
import reducer from "../../../store/reducers/processes/statusUpdateReducer";
import expect from "expect";
import {
registerError,
registerSuccess,
} from "../../../store/actions/register/registerActions";
import {
setUpdateStatusErr,
setUpdateStatusReq,
setUpdateStatusSucc,
} from "../../../store/actions/processes/processAction";

describe("status update reducer", () => {
it("should set success", () => {
expect(reducer(undefined, setUpdateStatusSucc())).toEqual({
success: true,
errorMessage: "",
});
});

it("should set error", () => {
expect(reducer(undefined, setUpdateStatusErr("Error"))).toEqual({
success: false,
errorMessage: "Error",
});
});

it("should set error", () => {
expect(reducer(undefined, setUpdateStatusReq("any"))).toEqual({
success: false,
errorMessage: "",
});
});
});

+ 0
- 66
src/__tests__/ReduxTests/Reducers/technologiesReducer.test.js Прегледај датотеку

@@ -1,66 +0,0 @@
import reducer from "../../../store/reducers/technology/technologiesReducer";
import expect from "expect";
import {
changeIsCheckedValue,
resetIsCheckedValue,
setTechnologies,
setTechnologiesError,
} from "../../../store/actions/technologies/technologiesActions";

describe("technologies reducer", () => {
it("should set techologies", () => {
expect(reducer(undefined, setTechnologies(["tech1", "tech2"]))).toEqual({
technologies: ["tech1", "tech2"],
errorMessage: "",
});
});

it("should set error", () => {
expect(reducer(undefined, setTechnologiesError("Error"))).toEqual({
technologies: [],
errorMessage: "Error",
});
});

it("should check tech", () => {
expect(
reducer(
{
technologies: [
{ id: 1, name: "T1", isChecked: false },
{ id: 2, name: "T2", isChecked: false },
],
errorMessage: "",
},
changeIsCheckedValue("T1")
)
).toEqual({
technologies: [
{ id: 1, name: "T1", isChecked: true },
{ id: 2, name: "T2", isChecked: false },
],
errorMessage: "",
});
});

it("should reset checked techs", () => {
expect(
reducer(
{
technologies: [
{ id: 1, name: "T1", isChecked: true },
{ id: 2, name: "T2", isChecked: true },
],
errorMessage: "",
},
resetIsCheckedValue("T1")
)
).toEqual({
technologies: [
{ id: 1, name: "T1", isChecked: false },
{ id: 2, name: "T2", isChecked: false },
],
errorMessage: "",
});
});
});

+ 0
- 106
src/__tests__/ReduxTests/adsCandidatesPageReducer.test.js Прегледај датотеку

@@ -1,106 +0,0 @@
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 * as api from "../../request/candidatesRequest";
import { runSaga } from "redux-saga";
import { ADS_CANDIDATES_FETCH } from "../../store/actions/candidates/candidatesActionConstants";
import { getAdsCandidates } from "../../store/saga/candidatesSaga";
import {
fetchAdsCandidatesError,
fetchAdsCandidatesSuccess,
} from "../../store/actions/candidates/candidatesActions";
import AdsCandidatesPage from "../../pages/CandidatesPage/AdsCandidatesPage";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";

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

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

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

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

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

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

it("Should dispatch get adsCandidates request when rendered", () => {
render(cont);
expect(mockDispatch).toHaveBeenCalledWith({
type: ADS_CANDIDATES_FETCH,
payload: {
currentPage: 0,
employmentType: "",
maxDateOfApplication: "",
maxExperience: 0,
minDateOfApplication: "",
minExperience: 0,
pageSize: 0,
technologies: [],
},
});
});

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

const mockedCall = { data: mockState.candidates.adsCandidates };
api.getFilteredAdsCandidates = jest.fn(() => Promise.resolve(mockedCall));

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

await runSaga(fakeStore, getAdsCandidates, {}).done;
expect(api.getFilteredAdsCandidates.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
fetchAdsCandidatesSuccess(mockedCall.data)
);
});

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

helper.rejectErrorCodeHelper = jest.fn(() => mockState.technologies.fetchTecnologiesErrorMessage);

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

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

await runSaga(fakeStore, getAdsCandidates, {}).done;

expect(api.getFilteredAdsCandidates.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
fetchAdsCandidatesError(error.response.data.message)
);
});
});

+ 0
- 407
src/__tests__/ReduxTests/adsReducer.test.js Прегледај датотеку

@@ -1,407 +0,0 @@
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 { setCreateAd } from "../../store/actions/createAd/createAdActions";
import { setAd, setAdError } from "../../store/actions/ad/adActions";
import { archiveActiveAd } from "../../store/actions/archiveActiveAd/archiveActiveAdActions";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";

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));
});

it("Should create ad", async () => {
const dispatchedActions = [];

const mockedCall = {
data: {
title: "React Developer",
minimumExperience: 1,
createdAt: new Date(),
expiredAt: new Date("2023-5-5"),
keyResponsibilities: "key responsibilities",
requirements: "requirements",
offer: "offer",
workHour: "PartTime",
employmentType: "Intership",
technologiesIds: [1, 2],
onSuccessAddAd: jest.fn,
},
};
api.createNewAd = jest.fn(() => Promise.resolve(mockedCall));

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

await runSaga(fakeStore, fc.createAd, {
payload: {
title: "React Developer",
minimumExperience: 1,
createdAt: new Date(),
expiredAt: new Date("2023-5-5"),
keyResponsibilities: "key responsibilities",
requirements: "requirements",
offer: "offer",
workHour: "PartTime",
employmentType: "Intership",
technologiesIds: [1, 2],
onSuccessAddAd: jest.fn,
},
}).done;

expect(api.createNewAd.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setCreateAd(mockedCall.data));
});

it("Archive ad", async () => {
const dispatchedActions = [];

const mockedCall = {
data: {
id: 1,
navigateToAds: jest.fn,
},
};
api.archiveActiveAdRequest = jest.fn(() => Promise.resolve(mockedCall));

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

await runSaga(fakeStore, fc.archiveActiveAdSaga, {
payload: {
id: 1,
navigateToAds: jest.fn,
},
}).done;

expect(api.archiveActiveAdRequest.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(archiveActiveAd(mockedCall.data));
});

it("Should not return ads when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.getAllAds = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.getAds, {}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("Should not return filtered ads when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
const filter = {
minimumExperience: 0,
maximumExperience: 0,
technologies: [1],
workHour: "FullTime",
employmentType: "Work",
};

api.getFilteredAds = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.getFilteredAds, filter).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("Should not return ad when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.getAd = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.getAd, {
payload: {
id: 1,
},
}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("Should not return archived ad when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.getAd = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.getArchiveAds, {}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("Should not create ad when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.createNewAd = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.createAd, {
payload: {
title: "React Developer",
minimumExperience: 1,
createdAt: new Date(),
expiredAt: new Date("2023-5-5"),
keyResponsibilities: "key responsibilities",
requirements: "requirements",
offer: "offer",
workHour: "PartTime",
employmentType: "Intership",
technologiesIds: [1, 2],
onSuccessAddAd: jest.fn,
},
}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("Should archive active ad when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.createNewAd = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.archiveActiveAdSaga, {
payload: {
id: 1,
navigateToAds: jest.fn,
},
}).done;

expect(mockfn).not.toHaveBeenCalled();
});

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

helper.rejectErrorCodeHelper = jest.fn(() => "Error");

const error = {
response: {
data: { message: "Error" },
},
};

api.getAd = jest.fn(() => Promise.reject(error));

const fakeStore = {
getState: () => mockState.ads.ads,

dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, fc.getAd, {
payload: {
id: 1,
},
}).done;

expect(api.getAdDetailsById.mock.calls.length).toBe(1);

expect(dispatchedActions).toContainEqual(
setAdError(error.response.data.message)
);
});
});

+ 0
- 103
src/__tests__/ReduxTests/applicantsSaga.test.js Прегледај датотеку

@@ -1,103 +0,0 @@
import { runSaga } from "redux-saga";
import * as api from "../../request/applicantRequest";
import * as redux from "react-redux";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";
import { applyForAd, applyForAdError } from "../../store/actions/applyForAd/applyForAdActions";
import { applyForAdSaga } from "../../store/saga/applicantsSaga";

describe("applicants tests saga tests", () => {
let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValueOnce("anything");

// 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 handle applyForAdSaga saga with actions", async () => {
const dispatchedActions = [];

const mockedCall = { data: "Data" };
api.applyForAdRequest = jest.fn(() => Promise.resolve(mockedCall));

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

const mockfn = jest.fn()

await runSaga(fakeStore, applyForAdSaga, {
payload: {
adId: "",
firstName: "",
lastName: "",
gender: "",
dateOfBirth: "",
phoneNumber: "",
professionalQualification: "",
technologiesIds: "",
experience: "",
linkedinLink: "",
githubLink: "",
bitBucketLink: "",
email: "",
coverLetter: "",
pdfFile: "",
handleApiResponseSuccess: mockfn
},
}).done;
expect(api.applyForAdRequest.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(applyForAd(mockedCall.data));
expect(mockfn).toHaveBeenCalled();
});

it("Should handle apply for ad saga with errors", async () => {
const dispatchedActions = [];

helper.rejectErrorCodeHelper = jest.fn(() => "Error");

const mockedCall = { response: { data: { message: "Error" } } };
api.applyForAdRequest = jest.fn(() => Promise.reject(mockedCall));

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

await runSaga(fakeStore, applyForAdSaga, {
payload: {
adId: "",
firstName: "",
lastName: "",
gender: "",
dateOfBirth: "",
phoneNumber: "",
professionalQualification: "",
technologiesIds: "",
experience: "",
linkedinLink: "",
githubLink: "",
bitBucketLink: "",
email: "",
coverLetter: "",
pdfFile: "",
},
}).done;
expect(api.applyForAdRequest.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(applyForAdError("Error"));
});
});

+ 0
- 182
src/__tests__/ReduxTests/candidateDetailsPageReducer.test.js Прегледај датотеку

@@ -1,182 +0,0 @@
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 * as api from "../../request/candidatesRequest";
import * as api2 from '../../request/usersRequest';
import { runSaga } from "redux-saga";
import { CANDIDATE_FETCH } from "../../store/actions/candidate/candidateActionConstants";
import { FETCH_USERS_REQ } from "../../store/actions/users/usersActionConstants";
import { getSingleCandidate } from "../../store/saga/candidatesSaga";
import {getUsers} from '../../store/saga/usersSaga'
import {
fetchCandidateSuccess,
fetchCandidateError,
} from "../../store/actions/candidate/candidateActions";
import {
setUsers,
setUsersError
} from "../../store/actions/users/usersActions";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";
import CandidateDetailsPage from "../../pages/CandidatesPage/CandidateDetailsPage";

const mockHistoryPush = jest.fn();

// mock param which we send as part of URL
jest.mock("react-router-dom", () => ({
...jest.requireActual("react-router-dom"),
useHistory: () => ({
push: mockHistoryPush,
}),
useParams: () => ({
id: 1,
}),
}));

describe("CandidateDetailsPage render tests", () => {
var props = {
history: {
replace: jest.fn(),
push: jest.fn(),
location: {
pathname: "",
},
},
};
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<CandidateDetailsPage {...props} />
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

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

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

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

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

it("Should dispatch fetch candidate request when rendered", () => {
render(cont);
expect(mockDispatch).toHaveBeenCalledWith({
payload: { id: 1 },
type: CANDIDATE_FETCH,
});
});

it("Should dispatch fetch users request when rendered", () => {
render(cont);
expect(mockDispatch).toHaveBeenCalledWith({
type: FETCH_USERS_REQ,
});
});

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

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

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

await runSaga(fakeStore, getSingleCandidate, { payload: { id: 1 } }).done;
expect(api.getCandidate.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
fetchCandidateSuccess(mockedCall.data)
);
});

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

const mockedCall = { data: mockState.users.users };
api2.getAllUsers = jest.fn(() => Promise.resolve(mockedCall));

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

await runSaga(fakeStore, getUsers).done;
expect(api2.getAllUsers.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
setUsers(mockedCall.data)
);
});

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

helper.rejectErrorCodeHelper = jest.fn(
() => mockState.candidate.fetchCandidateErrorMessage
);

const error = {
response: {
data: { message: mockState.candidate.fetchCandidateErrorMessage },
},
};
api.getCandidate = jest.fn(() => Promise.reject(error));

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

await runSaga(fakeStore, getSingleCandidate, { payload: { id: 1 } }).done;

expect(api.getCandidate.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
fetchCandidateError(error.response.data.message)
);
});

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

helper.rejectErrorCodeHelper = jest.fn(
() => mockState.users.fetchUsersErrorMessage
);

const error = {
response: {
data: { message: mockState.users.fetchUsersErrorMessage },
},
};
api2.getAllUsers = jest.fn(() => Promise.reject(error));

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

await runSaga(fakeStore, getUsers).done;

expect(api2.getAllUsers.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
setUsersError(error.response.data.message)
);
});
});

+ 0
- 91
src/__tests__/ReduxTests/candidatesPageReducer.test.js Прегледај датотеку

@@ -1,91 +0,0 @@
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";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";

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

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValueOnce(mockState.technologies.technologies);
spyOnUseDispatch = jest.spyOn(redux, "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 = [];

helper.rejectErrorCodeHelper = jest.fn(() => mockState.candidates.fetchCandidatesErrorMessage);

const error = {
response: {
data: { message: mockState.candidates.fetchCandidatesErrorMessage },
},
};
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)
);
});
});

+ 0
- 407
src/__tests__/ReduxTests/candidatesSaga.test.js Прегледај датотеку

@@ -1,407 +0,0 @@
import * as redux from "react-redux";
import { mockState } from "../../mockState";
import * as api from "../../request/candidatesRequest";
import {
filterCandidatesError,
filterCandidatesSuccess,
fetchCandidateOptionsSuccess,
fetchCandidateOptionsError,
fetchInitProcessError,
fetchInitProcessSuccess,
} from "../../store/actions/candidates/candidatesActions";
import {
fetchCandidateError,
fetchCandidateSuccess,
deleteCandidateError,
deleteCandidateSuccess,
createCandidateComment,
createCandidateCommentError,
createCandidateCommentSuccess,
} from "../../store/actions/candidate/candidateActions";
import { runSaga } from "redux-saga";
import {
filterCandidates as k,
getSingleCandidate,
deleteSingleCandidate,
getOptions,
initializeProcess,
addComment,
} from "../../store/saga/candidatesSaga";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";

describe("candidatesSaga reducer tests", () => {
let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

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

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

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

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

// filterCandidates

it("should run filterCandidates saga function with actions", async () => {
const dispatchedActions = [];

api.getFilteredCandidates = jest.fn(() => Promise.resolve({}));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, k, {
payload: {
data: {},
},
}).done;

expect(api.getFilteredCandidates.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(filterCandidatesSuccess());
});

it("should call handleApiResponseSuccess inside filterCandidates saga function", async () => {
const dispatchedActions = [];

api.getFilteredCandidates = jest.fn(() => Promise.resolve({}));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

const mockFn = jest.fn();

await runSaga(fakeStore, k, {
payload: {
data: {},
handleApiResponseSuccess: mockFn,
},
}).done;

expect(mockFn).toHaveBeenCalled();
});

it("should handle error inside filterCandidates saga function", async () => {
const dispatchedActions = [];

const error = { response: { data: { message: "Error" } } };
helper.rejectErrorCodeHelper = jest.fn(() => "Error");
api.getFilteredCandidates = jest.fn(() => Promise.reject(error));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, k, {
payload: {
data: {},
},
}).done;

expect(api.getFilteredCandidates.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(filterCandidatesError("Error"));
});

// getSingleCandidate

it("should run getSingleCandidate saga function with actions", async () => {
const dispatchedActions = [];

api.getCandidate = jest.fn(() => Promise.resolve({}));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, getSingleCandidate, {
payload: {
data: {},
},
}).done;

expect(api.getCandidate.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(fetchCandidateSuccess());
});

it("should handle error inside getSingleCandidate saga function", async () => {
const dispatchedActions = [];

const error = { response: { data: { message: "Error" } } };
helper.rejectErrorCodeHelper = jest.fn(() => "Error");
api.getCandidate = jest.fn(() => Promise.reject(error));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, getSingleCandidate, {
payload: {
data: {},
},
}).done;

expect(api.getCandidate.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(fetchCandidateError("Error"));
});

// addComment

// it("should run addComment saga function with actions", async () => {
// const dispatchedActions = [];

// api.createComment = jest.fn(() =>
// Promise.resolve({ payload: { user: {} } })
// );

// const fakeStore = {
// getState: () => ({
// isSuccess: false,
// errorMessage: "",
// }),
// dispatch: (action) => dispatchedActions.push(action),
// };

// await runSaga(fakeStore, addComment, {
// payload: { user: {} },
// }).done;

// expect(api.createComment.mock.calls.length).toBe(1);
// expect(dispatchedActions).toContainEqual(createCandidateCommentSuccess());
// });

it("should handle error inside getSingleCandidate saga function", async () => {
const dispatchedActions = [];

const error = { response: { data: { message: "Error" } } };
helper.rejectErrorCodeHelper = jest.fn(() => "Error");
api.createComment = jest.fn(() => Promise.reject(error));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, addComment, {
payload: {
data: {},
},
}).done;

expect(api.createComment.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
createCandidateCommentError("Error")
);
});

// deleteSingleCandidate

it("should run deleteSingleCandidate saga function with actions", async () => {
const dispatchedActions = [];

api.deleteCandidate = jest.fn(() => Promise.resolve({}));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, deleteSingleCandidate, {
payload: {
data: {},
},
}).done;

expect(api.deleteCandidate.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(deleteCandidateSuccess());
});

it("should call handleApiResponseSuccess inside filterCandidates saga function", async () => {
const dispatchedActions = [];

api.deleteCandidate = jest.fn(() => Promise.resolve({}));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

const mockFn = jest.fn();

await runSaga(fakeStore, deleteSingleCandidate, {
payload: {
data: {},
handleApiResponseSuccess: mockFn,
},
}).done;

expect(mockFn).toHaveBeenCalled();
});

it("should handle error inside deleteSingleCandidate saga function", async () => {
const dispatchedActions = [];

const error = { response: { data: { message: "Error" } } };
helper.rejectErrorCodeHelper = jest.fn(() => "Error");
api.deleteCandidate = jest.fn(() => Promise.reject(error));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, deleteSingleCandidate, {
payload: {
data: {},
},
}).done;

expect(api.deleteCandidate.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(deleteCandidateError("Error"));
});

// getOptions

it("should run getOptions saga function with actions", async () => {
const dispatchedActions = [];

api.getCandidateOptions = jest.fn(() => Promise.resolve({}));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, getOptions, {
payload: {
data: {},
},
}).done;

expect(api.getCandidateOptions.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(fetchCandidateOptionsSuccess());
});

it("should handle error inside getOptions saga function", async () => {
const dispatchedActions = [];

const error = { response: { data: { message: "Error" } } };
helper.rejectErrorCodeHelper = jest.fn(() => "Error");
api.getCandidateOptions = jest.fn(() => Promise.reject(error));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, getOptions, {
payload: {
data: {},
},
}).done;

expect(api.getCandidateOptions.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
fetchCandidateOptionsError("Error")
);
});

// initializeProcess

it("should run initializeProcess saga function with actions", async () => {
const dispatchedActions = [];

api.initializeProcessRequest = jest.fn(() => Promise.resolve({}));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, initializeProcess, {
payload: {
data: {},
},
}).done;

expect(api.initializeProcessRequest.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(fetchInitProcessSuccess());
});

it("should handle error inside initializeProcess saga function", async () => {
const dispatchedActions = [];

const error = { response: { data: { message: "Error" } } };
helper.rejectErrorCodeHelper = jest.fn(() => "Error");
api.initializeProcessRequest = jest.fn(() => Promise.reject(error));

const fakeStore = {
getState: () => ({
isSuccess: false,
errorMessage: "",
}),
dispatch: (action) => dispatchedActions.push(action),
};

await runSaga(fakeStore, initializeProcess, {
payload: {
data: {},
},
}).done;

expect(api.initializeProcessRequest.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(fetchInitProcessError("Error"));
});
});

+ 0
- 431
src/__tests__/ReduxTests/patternsReducer.test.js Прегледај датотеку

@@ -1,431 +0,0 @@
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";
import { createPattern } from "../../store/actions/createPattern/createPatternActions";
import { updatePattern } from "../../store/actions/updatePattern/updatePatternActions";
import { scheduleAppointment } from "../../store/actions/scheduleAppointment/scheduleAppointmentActions";

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)
);
});

it("Should create new pattern", async () => {
const dispatchedActions = [];

const mockedCall = {
data: {
title: "Neuspesan korak",
selectionLevelId: 1,
message: "Poruka",
},
};
api.createPatternRequest = jest.fn(() => Promise.resolve(mockedCall));

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

await runSaga(fakeStore, fc.createPatternSaga, {
payload: {
title: "Neuspesan korak",
selectionLevelId: 1,
message: "Poruka",
},
}).done;

expect(api.createPatternRequest.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(createPattern(mockedCall.data));
});

it("Should update pattern", async () => {
const dispatchedActions = [];

const mockedCall = {
data: {
id: 1,
title: "Zakazan intervju",
createdAt: new Date(),
selectionLevelId: 1,
message: "Message",
},
};
api.updatePatternRequest = jest.fn(() => Promise.resolve(mockedCall));

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

await runSaga(fakeStore, fc.updatePatternSaga, {
payload: {
id: 1,
title: "Zakazan intervju",
createdAt: new Date(),
selectionLevelId: 1,
message: "Message",
},
}).done;

expect(api.updatePatternRequest.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(updatePattern(mockedCall.data));
});

it("Should schedule appointment", async () => {
const dispatchedActions = [];

const mockedCall = {
data: {
emails: ["ermin.bronja@dilig.net"],
patternId: 1,
handleApiResponseSuccess: jest.fn,
},
};
api.scheduleAppointmentRequest = jest.fn(() => Promise.resolve(mockedCall));

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

await runSaga(fakeStore, fc.scheduleAppointmentSaga, {
payload: {
emails: ["ermin.bronja@dilig.net"],
patternId: 1,
handleApiResponseSuccess: jest.fn,
},
}).done;

expect(api.scheduleAppointmentRequest.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
scheduleAppointment(mockedCall.data)
);
});

it("Should not return patterns when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.getAllPatterns = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.getPatterns, {}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("Should not return pattern when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.getPatternById = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.getPattern, {
payload: {
id: 1,
},
}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("Should not return pattern applicants when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.getPatternApplicants = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.getPatternApplicants, {
payload: {
id: 1,
},
}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("Should not return filtered patterns when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.getFilteredPatterns = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.filterPatterns, {
fromDate: new Date("2-2-2021"),
toDate: new Date("3-3-2023"),
selectionLevels: [1, 2],
}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("Should not create pattern when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.createPatternRequest = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.createPatternSaga, {
payload: {
title: "Neuspesan korak",
selectionLevelId: 1,
message: "Poruka",
},
}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("Should not update pattern when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.updatePatternRequest = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.updatePatternSaga, {
payload: {
id: 1,
title: "Zakazan intervju",
createdAt: new Date(),
selectionLevelId: 1,
message: "Message",
},
}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("Should not chedule appointment when exception was thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: "Error" },
},
};
api.scheduleAppointmentRequest = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, fc.scheduleAppointmentSaga, {
payload: {
emails: ["ermin.bronja@dilig.net"],
patternId: 1,
handleApiResponseSuccess: jest.fn,
},
}).done;

expect(mockfn).not.toHaveBeenCalled();
});
});

+ 0
- 566
src/__tests__/ReduxTests/processesReducer.test.js Прегледај датотеку

@@ -1,566 +0,0 @@
import { runSaga } from "redux-saga";
import * as api from "../../request/processesReguest";
import { render } from "@testing-library/react";
import * as redux from "react-redux";
import SelectionProcessPage from "../../pages/selectionProcessPage/selectionProcessPage";
("../../pages/SelectionProcessPage/SelectionProcessPage");
import store from "../../store";
import "../../i18n";
import { mockState } from "../../mockState";
import { FETCH_PROCESSES_REQ } from "../../store/actions/processes/processesActionConstants";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import {
getProcesses,
getFilteredProcesses,
finishProcess,
changeStatus,
changeInterviewer,
getApplicantProcesses,
} from "../../store/saga/processSaga";
import {
setProcesses,
setProcessesError,
} from "../../store/actions/processes/processesAction";
import { SelectionProvider } from "../../context/SelectionContext";
import {
setDoneProcess,
setDoneProcessError,
setUpdateInterviewerErr,
setUpdateInterviewerSucc,
setUpdateStatusErr,
setUpdateStatusSucc,
} from "../../store/actions/processes/processAction";
import {
setApplicant,
setApplicantError,
} from "../../store/actions/processes/applicantAction";

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

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector
.mockReturnValueOnce(mockState.selections)
.mockReturnValueOnce(mockState.selections.processes)
.mockReturnValueOnce(mockState.selections.statuses);
// 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 processes request when rendered", () => {
render(cont);
expect(mockDispatch).toHaveBeenCalledWith({
type: FETCH_PROCESSES_REQ,
});
});

it("should load and handle levels with processes in case of success", async () => {
// we push all dispatched actions to make assertions easier
// and our tests less brittle
const dispatchedActions = [];

// we don't want to perform an actual api call in our tests
// so we will mock the getAllUsers api with jest
// this will mutate the dependency which we may reset if other tests
// are dependent on it
const mockedCall = { data: mockState.selections.processes };
api.getAllLevels = jest.fn(() => Promise.resolve(mockedCall));

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

// wait for saga to complete
await runSaga(fakeStore, getProcesses).done;
expect(api.getAllLevels.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setProcesses(mockedCall.data));
});

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

// we simulate an error by rejecting the promise
// then we assert if our saga dispatched the action(s) correctly
const error = {
response: {
data: { message: mockState.selections.fetchSelectionsErrorMessage },
},
};
api.getAllLevels = jest.fn(() => Promise.reject(error));

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

await runSaga(fakeStore, getProcesses).done;

expect(api.getAllLevels.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
setProcessesError(error.response.data.message)
);
});

it("should load and handle levels with filtered processes in case of success", async () => {
// we push all dispatched actions to make assertions easier
// and our tests less brittle
const dispatchedActions = [];
const filter = {
statuses: ["Zakazan", "Odrađen"],
dateStart: new Date(2023, 0, 5),
dateEnd: new Date(2024, 1, 1),
};
const filteredData = [];
mockState.selections.processes.forEach((level) => {
const filteredLevel = level;
filteredLevel.selectionProcesses = level.selectionProcesses.filter(
(v) =>
v.date >= filter.dateStart &&
v.date <= filter.dateEnd &&
filter.statuses.includes(v.status)
);
filteredData.push(filteredLevel);
});
// we don't want to perform an actual api call in our tests
// so we will mock the getAllUsers api with jest
// this will mutate the dependency which we may reset if other tests
// are dependent on it
const mockedCall = { data: filteredData };
api.getAllFilteredProcessesReq = jest.fn(() => Promise.resolve(mockedCall));

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

// wait for saga to complete
await runSaga(fakeStore, getFilteredProcesses, filter).done;
expect(api.getAllFilteredProcessesReq.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setProcesses(filteredData));
});

it("should handle error in case of exception", async () => {
const dispatchedActions = [];

const filter = {
statuses: ["Zakazan", "Odrađen"],
dateStart: new Date(2023, 0, 5),
dateEnd: new Date(2024, 1, 1),
};

const error = {
response: {
data: { message: mockState.selections.fetchSelectionsErrorMessage },
},
};
api.getAllFilteredProcessesReq = jest.fn(() => Promise.reject(error));

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

// wait for saga to complete
await runSaga(fakeStore, getFilteredProcesses, filter).done;
expect(dispatchedActions).toContainEqual(
setProcessesError(error.response.data.message)
);
});

it("should handle process to set it done in case of success", async () => {
// we push all dispatched actions to make assertions easier
// and our tests less brittle
const dispatchedActions = [];
const filter = {
statuses: ["Zakazan", "Odrađen"],
dateStart: new Date(2023, 0, 5),
dateEnd: new Date(2024, 1, 1),
};
const filteredData = [];
mockState.selections.processes.forEach((level) => {
const filteredLevel = level;
filteredLevel.selectionProcesses = level.selectionProcesses.filter(
(v) =>
v.date >= filter.dateStart &&
v.date <= filter.dateEnd &&
filter.statuses.includes(v.status)
);
filteredData.push(filteredLevel);
});
// we don't want to perform an actual api call in our tests
// so we will mock the getAllUsers api with jest
// this will mutate the dependency which we may reset if other tests
// are dependent on it
const mockedCall = { data: filteredData };
api.getAllFilteredProcessesReq = jest.fn(() => Promise.resolve(mockedCall));

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

// wait for saga to complete
await runSaga(fakeStore, getFilteredProcesses, filter).done;
expect(api.getAllFilteredProcessesReq.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setProcesses(filteredData));
});

it("should handle process to set it done in case of finish success", async () => {
const dispatchedActions = [];
const mockedCall = { data: { isSuccess: true } };
api.doneProcess = jest.fn(() => Promise.resolve(mockedCall));

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

// wait for saga to complete
await runSaga(fakeStore, finishProcess, {
payload: {
id: 1,
name: "Some random name",
applicantId: 5,
schedulerId: 10,
},
}).done;

expect(api.doneProcess.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setDoneProcess(mockedCall.data));
});

it("should handle error in case of finish process exception", async () => {
const dispatchedActions = [];

const error = {
response: {
data: { message: mockState.selections.fetchSelectionsErrorMessage },
},
};
api.doneProcess = jest.fn(() => Promise.reject(error));

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

// wait for saga to complete
await runSaga(fakeStore, finishProcess, {
payload: {
id: 1,
name: "Some random name",
applicantId: 5,
schedulerId: 10,
},
}).done;

expect(api.doneProcess.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
setDoneProcessError(error.response.data.message)
);
});

it("should handle process status update", async () => {
const dispatchedActions = [];
const mockedCall = { data: { isSuccess: true } };
api.updateStatus = jest.fn(() => Promise.resolve(mockedCall));

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

// wait for saga to complete
await runSaga(fakeStore, changeStatus, {
payload: {
data: {
schedulerId: 3,
appointment: "22-10-2023",
newStatus: "Odrađen",
processId: 1,
},
},
}).done;

expect(api.updateStatus.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setUpdateStatusSucc());
});

it("should call responsehandler while handling process status update", async () => {
const dispatchedActions = [];
const mockedCall = { data: { isSuccess: true } };
api.updateStatus = jest.fn(() => Promise.resolve(mockedCall));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, changeStatus, {
payload: {
data: {
schedulerId: 3,
appointment: "22-10-2023",
newStatus: "Odrađen",
processId: 1,
},
responseHandler: mockfn,
},
}).done;

expect(mockfn).toHaveBeenCalled();
});

it("should handle process status update error if exception is thrown", async () => {
const dispatchedActions = [];

const error = {
response: {
data: { message: mockState.selections.fetchSelectionsErrorMessage },
},
};

api.updateStatus = jest.fn(() => Promise.reject(error));

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

// wait for saga to complete
await runSaga(fakeStore, changeStatus, {
payload: {
data: {
schedulerId: 3,
appointment: "22-10-2023",
newStatus: "Odrađen",
processId: 1,
},
},
}).done;

expect(api.updateStatus.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
setUpdateStatusErr(error.response.data.message)
);
});

it("should not call responseHandler if exception is thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: mockState.selections.fetchSelectionsErrorMessage },
},
};
api.updateStatus = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, changeStatus, {
payload: {
data: {
schedulerId: 3,
appointment: "22-10-2023",
newStatus: "Odrađen",
processId: 1,
},
responseHandler: mockfn,
},
}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("should handle interviewer update", async () => {
const dispatchedActions = [];
const mockedCall = { data: { isSuccess: true } };
api.updateInterviewer = jest.fn(() => Promise.resolve(mockedCall));

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

// wait for saga to complete
await runSaga(fakeStore, changeInterviewer, {
payload: {
data: {
schedulerId: 1,
processId: 2,
},
// responseHandler: apiSuccess,
},
}).done;

expect(api.updateInterviewer.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setUpdateInterviewerSucc());
});

it("should call response handler on change success", async () => {
const dispatchedActions = [];
const mockedCall = { data: { isSuccess: true } };
api.updateInterviewer = jest.fn(() => Promise.resolve(mockedCall));

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

const mockfn = jest.fn();

// wait for saga to complete
await runSaga(fakeStore, changeInterviewer, {
payload: {
data: {
schedulerId: 1,
processId: 2,
},
responseHandler: mockfn,
},
}).done;

expect(mockfn).toHaveBeenCalled();
});

it("should handle interviewer update error if exception is thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: mockState.selections.fetchSelectionsErrorMessage },
},
};
api.updateInterviewer = jest.fn(() => Promise.reject(error));

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

// wait for saga to complete
await runSaga(fakeStore, changeInterviewer, {
payload: {
data: {
schedulerId: 1,
processId: 2,
},
// responseHandler: mockfn,
},
}).done;

expect(api.updateInterviewer.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
setUpdateInterviewerErr(error.response.data.message)
);
});

it("should not call response handler if exception is thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: mockState.selections.fetchSelectionsErrorMessage },
},
};
api.updateInterviewer = jest.fn(() => Promise.reject(error));

const mockfn = jest.fn();

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

// wait for saga to complete
await runSaga(fakeStore, changeInterviewer, {
payload: {
data: {
schedulerId: 1,
processId: 2,
},
responseHandler: mockfn,
},
}).done;

expect(mockfn).not.toHaveBeenCalled();
});

it("should handle applicant processess if succeeded", async () => {
const dispatchedActions = [];
const mockedCall = { data: {} };
api.getProcessesOfApplicant = jest.fn(() => Promise.resolve(mockedCall));

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

// wait for saga to complete
await runSaga(fakeStore, getApplicantProcesses, {
payload: {
id: 1,
},
}).done;

expect(api.getProcessesOfApplicant.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(setApplicant(mockedCall.data));
});

it("should handle applicant processess error if exception is thrown", async () => {
const dispatchedActions = [];
const error = {
response: {
data: { message: mockState.selections.fetchSelectionsErrorMessage },
},
};
api.getProcessesOfApplicant = jest.fn(() => Promise.reject(error));

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

// wait for saga to complete
await runSaga(fakeStore, getApplicantProcesses, {
payload: {
id: 1,
},
}).done;

expect(api.getProcessesOfApplicant.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
setApplicantError(error.response.data.message)
);
});
});

+ 0
- 112
src/__tests__/ReduxTests/schedulePageReducer.test.js Прегледај датотеку

@@ -1,112 +0,0 @@
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 * as api from "../../request/scheduleRequest";
import { runSaga } from "redux-saga";
import { SCHEDULE_FETCH } from "../../store/actions/schedule/scheduleActionConstants";
import { getSchedule } from "../../store/saga/scheduleSaga";
import {
fetchScheduleSuccess,
fetchScheduleError,
} from "../../store/actions/schedule/scheduleActions";
import SchedulePage from "../../pages/SchedulePage/SchedulePage";
import ColorModeProvider from "../../context/ColorModeContext";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";

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

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

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

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

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

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

it("Should dispatch get schedule request when rendered", () => {
render(cont);
const date = new Date()
expect(mockDispatch).toHaveBeenCalledWith({
type: SCHEDULE_FETCH,
payload: {
month: date.getMonth() + 1,
year: date.getFullYear(),
},
});
});

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

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

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

await runSaga(fakeStore, getSchedule, {
payload: {
month: 12,
year: 2022,
},
}).done;
expect(api.getSpecificSchedule.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(fetchScheduleSuccess(mockedCall.data));
});

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

helper.rejectErrorCodeHelper = jest.fn(
() => mockState.schedule.fetchScheduleErrorMessage
);

const error = {
response: {
data: { message: mockState.schedule.fetchScheduleErrorMessage },
},
};
api.getSpecificSchedule = jest.fn(() => Promise.reject(error));

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

await runSaga(fakeStore, getSchedule, {
payload: {
month: 12,
year: 2022,
},
}).done;

expect(api.getSpecificSchedule.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
fetchScheduleError(error.response.data.message)
);
});
});

+ 0
- 109
src/__tests__/ReduxTests/screeningTestsSaga.test.js Прегледај датотеку

@@ -1,109 +0,0 @@
import { runSaga } from "redux-saga";
import {
createScreeningTestError,
createScreeningTestSuccess,
fetchScreeningTestsError,
fetchScreeningTestsSuccess,
} from "../../store/actions/screeningTests/screeningTestActions";
import {
createScreeningTest,
getScreeningTests,
} from "../../store/saga/screeningTestsSaga";
import * as api from "../../request/screeningTestRequest";
import * as redux from "react-redux";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";

describe("screening tests saga tests", () => {
let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValueOnce("anything");

// 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 handle get tests saga with actions", async () => {
const dispatchedActions = [];

const mockedCall = { data: "Data" };
api.getTests = jest.fn(() => Promise.resolve(mockedCall));

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

await runSaga(fakeStore, getScreeningTests).done;
expect(api.getTests.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
fetchScreeningTestsSuccess(mockedCall.data)
);
});

it("Should handle get tests saga with errors", async () => {
const dispatchedActions = [];

helper.rejectErrorCodeHelper = jest.fn(() => "Error");

const mockedCall = { response: { data: { message: "Error" } } };
api.getTests = jest.fn(() => Promise.reject(mockedCall));

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

await runSaga(fakeStore, getScreeningTests).done;
expect(api.getTests.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(fetchScreeningTestsError("Error"));
});

it("Should handle create screening tests saga with actions", async () => {
const dispatchedActions = [];

const mockedCall = { data: "Data" };
api.createTest = jest.fn(() => Promise.resolve(mockedCall));

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

await runSaga(fakeStore, createScreeningTest, { payload: {} }).done;
expect(api.createTest.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
createScreeningTestSuccess(mockedCall.data)
);
});

it("Should handle create screening tests saga with errors", async () => {
const dispatchedActions = [];

helper.rejectErrorCodeHelper = jest.fn(() => "Error");

const mockedCall = { response: { data: { message: "Error" } } };
api.createTest = jest.fn(() => Promise.reject(mockedCall));

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

await runSaga(fakeStore, createScreeningTest, { payload: {} }).done;
expect(api.createTest.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(createScreeningTestError("Error"));
});
});

+ 0
- 97
src/__tests__/ReduxTests/statsPageReducer.test.js Прегледај датотеку

@@ -1,97 +0,0 @@
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 StatsPage from "../../pages/StatsPage/StatsPage";
import * as api from "../../request/statsRequests";
import { runSaga } from "redux-saga";
import {
getStatsSuccess,
getStatsError,
} from "../../store/actions/stats/statsActions";
import { FETCH_STATS_REQ } from "../../store/actions/stats/statsActionConstants";
import { getAppStats } from "../../store/saga/statsSaga";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";

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

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

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

// 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 stats request when rendered", () => {
render(cont);
expect(mockDispatch).toHaveBeenCalledWith({
type: FETCH_STATS_REQ,
});
});

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

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

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

await runSaga(fakeStore, getAppStats).done;
expect(api.getStats.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(getStatsSuccess(mockedCall.data));
});

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

helper.rejectErrorCodeHelper = jest.fn(
() => mockState.stats.fetchStatsErrorMessage
);

const error = {
response: {
data: { message: mockState.stats.fetchStatsErrorMessage },
},
};
api.getStats = jest.fn(() => Promise.reject(error));

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

await runSaga(fakeStore, getAppStats).done;

expect(api.getStats.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
getStatsError(error.response.data.message)
);
});
});

+ 0
- 127
src/__tests__/ReduxTests/tableViewPageReducer.test.js Прегледај датотеку

@@ -1,127 +0,0 @@
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 { FILTER_CANDIDATES } from "../../store/actions/candidates/candidatesActionConstants";
import TableViewPage from "../../pages/CandidatesPage/TableViewPage";
import * as api from "../../request/candidatesRequest";
import { runSaga } from "redux-saga";

import * as fc from "../../store/saga/candidatesSaga";
import {
filterCandidatesSuccess,
filterCandidatesError,
} from "../../store/actions/candidates/candidatesActions";
import { PAGE_SIZE_CANDIDATES } from "../../constants/keyCodeConstants";
import * as helper from "../../util/helpers/rejectErrorCodeHelper";

describe("TableViewPage render tests", () => {
var props = {
history: {
replace: jest.fn(),
push: jest.fn(),
location: {
pathname: "/candidates",
},
},
setPage: jest.fn(),
sliderValue: [0, 2],
startingDate: "",
endingDate: "",
typesOfEmployments: [],
technologie: [],
page: 1,
search: "",
};
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<TableViewPage {...props} />
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

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

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

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

it("Should dispatch filter candidates request when rendered", () => {
render(cont);
expect(mockDispatch).toHaveBeenCalledWith({
type: FILTER_CANDIDATES,
payload: {
currentPage: 1,
employmentType: "",
maxDateOfApplication: "",
maxExperience: 0,
minDateOfApplication: "",
minExperience: 0,
pageSize: PAGE_SIZE_CANDIDATES,
technologies: [],
},
});
});

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

const mockedCall = { data: mockState.candidates.items };
api.getFilteredCandidates = jest.fn(() => Promise.resolve(mockedCall));

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

await runSaga(fakeStore, fc.filterCandidates, {}).done;
expect(api.getFilteredCandidates.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
filterCandidatesSuccess(mockedCall.data)
);
});

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

helper.rejectErrorCodeHelper = jest.fn(
() => mockState.candidates.fetchCandidatesErrorMessage
);

const error = {
response: {
data: { message: mockState.candidates.fetchCandidatesErrorMessage },
},
};
api.getFilteredCandidates = jest.fn(() => Promise.reject(error));

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

await runSaga(fakeStore, fc.filterCandidates, {}).done;

expect(api.getFilteredCandidates.mock.calls.length).toBe(1);
expect(dispatchedActions).toContainEqual(
filterCandidatesError(error.response.data.message)
);
});
});

+ 0
- 80
src/__tests__/UITests/SelectionCardUI.test.js Прегледај датотеку

@@ -1,80 +0,0 @@
import { Router } from "react-router-dom";
import { SelectionProvider } from "../../context/SelectionContext";
import * as redux from "react-redux";
import { fireEvent, render, screen } from "@testing-library/react";
import store from "../../store";
import history from "../../store/utils/history";
import { mockState } from "../../mockState";
import SelectionCard from "../../components/Selection/SelectionCard";
import userEvent from "@testing-library/user-event";

describe("SelectionProcessPage render tests *first in order", () => {
var props = {
item: mockState.selections.processes[1].selectionProcesses[0],
click: jest.fn(),
dragStart: jest.fn(),
};

const cont = (
<redux.Provider store={store}>
<SelectionProvider>
<Router history={history}>
<SelectionCard {...props} />
</Router>
</SelectionProvider>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");

// Mock useDisptach hook
spyOnUseDispatch = jest.spyOn(redux, "useDispatch");

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

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

it("Should render card", () => {
render(cont);

expect(screen.getByTestId("sel-card")).toBeDefined();
});

it("Should render schedule", () => {
render(cont);

expect(screen.getByTestId("process-date")).toBeDefined();
});

// to be completed
// it("Should dispatch status change action when option 'done' is selected", async () => {
// render(cont);

// fireEvent.click(screen.getByTestId("status-btn"));
// var select = screen.getByTestId("status-select-drop");
// fireEvent.change(select, { target: { value: "Odrađen" } });

// // userEvent.click(screen.getByRole(screen.getByTestId("status-select-drop"), "button"));
// // await waitFor(() => userEvent.click(screen.getByText(/odrađen/i)));
// // expect(screen.getByRole("heading")).toHaveTextContent(/odrađen/i);

// expect(mockDispatch).toHaveBeenCalledWith(
// setDoneProcessReq({
// id: props.item.id,
// name: "Some random name",
// applicantId: props.item.applicant.applicantId,
// })
// );
// });
});

+ 0
- 143
src/__tests__/UITests/SelectionComponentUI.test.js Прегледај датотеку

@@ -1,143 +0,0 @@
import { Router } from "react-router-dom";
import { SelectionProvider } from "../../context/SelectionContext";
import * as redux from "react-redux";
import Selection from "../../components/Selection/Selection";
import { fireEvent, render, screen } from "@testing-library/react";
import store from "../../store";
import history from "../../store/utils/history";

describe("SelectionProcessPage render tests *first in order", () => {
var props = {
history: {
replace: jest.fn(),
push: jest.fn(),
location: {
pathname: "/selectionFlow",
},
},
order: 0,
modalEvent: jest.fn(),
selection: {
id: 1,
name: "HR intervju",
selectionProcesses: [],
// PropTypes.arrayOf(
// PropTypes.shape({
// id: PropTypes.number,
// name: PropTypes.string,
// date: PropTypes.string,
// status: PropTypes.string,
// currentSelection: PropTypes.number,
// map: PropTypes.func,
// applicant: PropTypes.shape({
// firstName: PropTypes.string,
// lastName: PropTypes.string,
// }),
// })
// ),
},
};

const cont = (
<redux.Provider store={store}>
<SelectionProvider>
<Router history={history}>
<Selection {...props} />
</Router>
</SelectionProvider>
</redux.Provider>
);

let spyOnUseSelector;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
});

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

it("Should render add button if selection is first in order", () => {
render(cont);

expect(screen.getByTestId("interview-image")).toBeDefined();
});

it("Should render empty selection message if no proccesses exist", () => {
render(cont);

expect(screen.getByTestId("empty-selection")).toBeDefined();
});

it("Should call modal event if add button has been clicked", () => {
render(cont);
fireEvent.click(screen.getByTestId("interview-image"));
expect(props.modalEvent).toHaveBeenCalled();
});
});

describe("SelectionProcessPage render tests *first in order", () => {
var props = {
history: {
replace: jest.fn(),
push: jest.fn(),
location: {
pathname: "/selectionFlow",
},
},
order: 1,
modalEvent: jest.fn(),
selection: {
id: 1,
name: "HR intervju",
selectionProcesses: [
{
id: 1,
name: "random",
date: "02-05-2022",
status: "Zakazan",
currentSelection: 1,
applicant: {
firstName: "Meris",
lastName: "Ahmatovic",
},
},
],
},
};

const cont = (
<redux.Provider store={store}>
<SelectionProvider>
<Router history={history}>
<Selection {...props} />
</Router>
</SelectionProvider>
</redux.Provider>
);

let spyOnUseSelector;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
});

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

it("Should not render add button if not first in order", () => {
render(cont);
expect(screen.queryByTestId("interview-image")).toBe(null);
});
it("Should render empty selection message if no proccesses exist", () => {
render(cont);

expect(screen.queryByTestId("empty-selection")).toBe(null);
});
});

+ 0
- 62
src/__tests__/UITests/adDetailsCandidateCardUI.test.js Прегледај датотеку

@@ -1,62 +0,0 @@
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
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 ColorModeProvider from "../../context/ColorModeContext";
import AdDetailsCandidateCard from "../../components/Ads/AdDetailsCandidateCard";

describe("Ad details candidate card ui tests", () => {
const props = {
className: "ad-details-card",
id: 1,
firstName: "Ermin",
lastName: "Bronja",
experience: 1,
cv: "http://",
history: {
replace: jest.fn(),

push: jest.fn(),

location: {
pathname: "/ads/1",
},
},
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<AdDetailsCandidateCard {...props} />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;

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

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

it("Should load ad details candidate card component", () => {
render(cont);
expect(screen.getByTestId("ad-details-candidate")).toBeDefined();
});

it("Should navigate on candidates page when button clicked", async () => {
render(cont);
waitFor(() => {
fireEvent.click(screen.getByTestId("ad-details-candidate-title-link"));
expect(props.history.push).toHaveBeenCalledWith("/candidates/1");
});
});
});

+ 0
- 104
src/__tests__/UITests/adDetailsPageUI.test.js Прегледај датотеку

@@ -1,104 +0,0 @@
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import * as redux from "react-redux";
import AdDetailsPage from "../../pages/AdsPage/AdDetailsPage";
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("AdDetailsPage render tests", () => {
var props = {
history: {
replace: jest.fn(),

push: jest.fn(),

location: {
pathname: "/ads/1",
},
},
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<AdDetailsPage {...props} />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;

beforeEach(() => {
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValue(mockState.ads.ads[0]);
});

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

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

it("Should render modal when click archive ad button", () => {
const { container } = render(cont);

fireEvent.click(container.getElementsByClassName("archive-ad-button")[0]);

expect(screen.getByTestId("alert-container")).toBeDefined();
});

it("Should render ads page when confirm archive ad modal", async () => {
const { container } = render(cont);

waitFor(() => {
fireEvent.click(container.getElementsByClassName("archive-ad-button")[0]);

fireEvent.click(container.getElementsByClassName("dialog-btn")[1]);

waitFor(() => expect(props.history.push).toHaveBeenCalledWith('/ads'));
});
});

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

it("Should render apply for ad modal after click button", () => {
const { container } = render(cont);

fireEvent.click(container.getElementsByClassName("apply-for-ad-button")[0]);

const a = screen.getByTestId("custom-modal-test-id");

expect(screen.getByTestId("custom-modal-test-id")).toBeDefined();
});

it("Should render go back button", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("ad-details-buttons-link")[0]
).toBeDefined();
});

it("Should go back when click button", async () => {
const { container } = render(cont);

fireEvent.click(
container.getElementsByClassName("ad-details-buttons-link")[0]
);

const arg = { pathname: "/ads" };

waitFor(() => expect(props.history.push).toHaveBeenCalledWith(arg));
});
});

+ 0
- 101
src/__tests__/UITests/adsCandidatesPageUI.test.js Прегледај датотеку

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

describe("TableViewPage render tests", () => {
var props = {
history: {
replace: jest.fn(),
push: jest.fn(),
location: {
pathname: "/candidates",
},
},
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<AdsCandidatesPage search="" {...props} />
</Router>
</redux.Provider>
);

let spyOnUseSelector;

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

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

it("Should render", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("ads-candidates-container")[0]
).toBeDefined();
});

it("Number of sliders should be equal to length of our adsCandidates array", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("ads-candidates").length).toBe(
mockState.candidates.adsCandidates.length
);
});

it("Number of candidates in slider (vissible and hidden) should be equal to the number of candidates which applied for ad", () => {
const { container } = render(cont);
expect(
container
.getElementsByClassName("ads-candidates")[0]
.getElementsByClassName("slick-slide").length
).toBe(mockState.candidates.adsCandidates[0].applicants.length);
});

it("Number of arrows (left and right) should be 1 because there is more than 4 candidates which applied for ad", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("active-ads-ads-arrows").length
).toBe(1);
});

it("After clicking on right arrow of first slider, first slider should show fifth candidate as forth card of slider", async () => {
const { container } = render(cont);
fireEvent.click(
container
.getElementsByClassName("active-ads-ads-arrows")[0]
.getElementsByTagName("button")[1]
);

await waitFor(() =>
expect(
container
.getElementsByClassName("ads-candidates")[0]
.getElementsByClassName("slick-active")[0]
.getElementsByClassName("candidate-card-container")[0]
.getElementsByClassName("candidate-card-applicant-name")[0]
.textContent
).toBe(
mockState.candidates.adsCandidates[0].applicants[3].firstName +
" " +
mockState.candidates.adsCandidates[0].applicants[3].lastName
)
);
});

it("Should render candidate details page after clicking on candidate card", () => {
const { container } = render(cont);
fireEvent.click(
container.getElementsByClassName("candidate-card-container")[0]
);
const arg = { pathname: "/candidates/1" };
expect(props.history.push).toHaveBeenCalledWith(arg);
});
});

+ 0
- 154
src/__tests__/UITests/adsPageUI.test.js Прегледај датотеку

@@ -1,154 +0,0 @@
import { render, screen, fireEvent, waitFor } 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", () => {
var props = {
history: {
replace: jest.fn(),

push: jest.fn(),

location: {
pathname: "/ads",
},
},
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<AdsPage {...props} />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;

beforeEach(() => {
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector
.mockReturnValueOnce(mockState.ads.ads)
.mockReturnValueOnce([
{
name: ".NET",
technologyId: 1,
technologyType: "Backend",
isChecked: false,
},
])
});

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("Should render CreateAdPage when click Add Ad button", () => {
const { container } = render(cont);
fireEvent.click(container.getElementsByClassName("ads-page-btn")[2]);

expect(props.history.push).toHaveBeenCalledWith("/create-ad");
});

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 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();
});

it("Should filter ads when click search button", async () => {
const { container } = render(cont);

waitFor(() => {
fireEvent.click(container.getElementsByClassName("fltr-btn")[0]);

fireEvent.click(
container.getElementsByClassName("ad-filters-checkbox")[0]
);

fireEvent.click(container.getByTestId("ad-filters-submit")[0]);

expect(
container.getElementsByClassName("ad-card").length
).toBeGreaterThan(0);
});
});

it("Should render Ads", async () => {
const { container } = render(cont);
waitFor(() =>
expect(
container.getElementsByClassName("ad-card").length
).toBeGreaterThan(0)
);
});

it("Should navigate to ad details when click on ad card", async () => {
const { container } = render(cont);
fireEvent.click(container.getElementsByClassName("ad-card")[0]);

expect(props.history.push).toHaveBeenCalledWith("/ads/1");
});
});

+ 0
- 53
src/__tests__/UITests/applicantSelectionPage.test.js Прегледај датотеку

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

describe("applicant selection process details test", () => {
var cont = (
<Router history={history}>
<Provider store={store}>
<ColorModeProvider>
<SelectionProcessOfApplicantPage />
</ColorModeProvider>
</Provider>
</Router>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValue(mockState.applSelection);

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

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

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

it("should render", () => {
render(cont);
expect(screen.getByTestId("appl-sel")).toBeDefined();
});

// ?
// it("should render applicant selection", () => {
// render(cont);
// expect(screen.getByTestId("appSelection")).toBeDefined();
// });
});

+ 0
- 110
src/__tests__/UITests/applyForAdFirstStageUI.test.js Прегледај датотеку

@@ -1,110 +0,0 @@
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
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 ColorModeProvider from "../../context/ColorModeContext";
import ApplyForAdFirstStage from "../../components/Ads/ApplyForAdFirstStage";

describe("Apply for ad first stage ui tests", () => {
const props = {
firstName: "E",
setFirstName: jest.fn(),
lastName: "B",
gender: "Male",
setGender: jest.fn(),
setLastName: jest.fn(),
dateOfBirth: new Date(),
setDateOfBirth: jest.fn(),
phoneNumber: "0",
setPhoneNumber: jest.fn(),
onIncreaseStage: jest.fn(),
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<ApplyForAdFirstStage {...props} />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;

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

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

it("Should render firstName input", () => {
render(cont);
expect(screen.getByTestId("apply-for-ad-modal-first-name-input")).toBeDefined();
});

it("Should change firstName on changing input", async () => {
render(cont);
fireEvent.change(
screen.getByTestId("apply-for-ad-modal-first-name-input"),
{ target: { value: "Ermin" } }
);

waitFor(() => expect(props.setFirstName).toHaveBeenCalled());
});

it("Should change last on changing input", async () => {
render(cont);
fireEvent.change(screen.getByTestId("apply-for-ad-modal-last-name-input"), {
target: { value: "Bronja" },
});

waitFor(() => expect(props.setLastName).toHaveBeenCalled());
});

it("Should select male gender", async () => {
const { container } = render(cont);
fireEvent.click(
container.getElementsByClassName("apply-for-ad-modal-gender-input")[0]
);

waitFor(() => expect(props.setGender).toHaveBeenCalled());
});

it("Should select female gender", async () => {
const { container } = render(cont);
fireEvent.click(
container.getElementsByClassName("apply-for-ad-modal-gender-input")[1]
);

waitFor(() => expect(props.setGender).toHaveBeenCalled());
});

it("Should change date of birth", async () => {
render(cont);
fireEvent.change(screen.getByTestId("apply-for-ad-modal-date-of-birth"), {
target: { value: "1998-05-05" },
});

waitFor(() => expect(props.setDateOfBirth).toHaveBeenCalled());
});

it("Should change phone number", async () => {
render(cont);
fireEvent.change(screen.getByTestId("apply-for-ad-modal-phone-number"), { target: { value: "0000000000" } });

waitFor(() => expect(props.setPhoneNumber).toHaveBeenCalled());
});

it("Should click go forward button", async () => {
render(cont);
fireEvent.click(screen.getByTestId("apply-for-ad-modal-go-forward-button"));

waitFor(() => expect(props.onIncreaseStage).toHaveBeenCalled());
});
});

+ 0
- 106
src/__tests__/UITests/applyForAdFourthStageUI.test.js Прегледај датотеку

@@ -1,106 +0,0 @@
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
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 ColorModeProvider from "../../context/ColorModeContext";
import ApplyForAdFourthStage from "../../components/Ads/ApplyForAdFourthStage";

describe("Apply for ad fourth stage ui tests", () => {
const props = {
coverLetter: "",
setCoverLetter: jest.fn(),
pdfFile: null,
setPdfFile: jest.fn(),
onDecreaseStage: jest.fn(),
onFinishedFourStages: jest.fn(),
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<ApplyForAdFourthStage {...props} />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let file;

beforeEach(() => {
file = new File(["(⌐□_□)"], "test.png", { type: "image/png" });
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValue(mockState.ads.ads);
});

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

it("Should render apply for ad fourth stage modal", () => {
render(cont);
expect(screen.getByTestId("apply-for-ad-modal-fourth-stage")).toBeDefined();
});

it("Should change pdfFile", async () => {
render(cont);
fireEvent.change(
screen.getByTestId("apply-for-ad-modal-fourth-stage-pdf-input"),
{ target: { files: [file] } }
);

waitFor(() => expect(props.setPdfFile).toHaveBeenCalled());
});

it("Should change coverLetter", async () => {
render(cont);
fireEvent.change(
screen.getByTestId("apply-for-ad-modal-fourth-stage-cover-letter-input"),
{ target: { value: "Cover Letter" } }
);

waitFor(() => expect(props.setPdfFile).toHaveBeenCalled());
});

it("Should go back on click button", async () => {
render(cont);
fireEvent.click(
screen.getByTestId("apply-for-ad-modal-fourth-stage-go-back-button")
);

waitFor(() => expect(props.onDecreaseStage).toHaveBeenCalled());
});

it("Should finish stages on click button", async () => {
render(cont);
fireEvent.click(
screen.getByTestId("apply-for-ad-modal-fourth-stage-go-forward-button")
);

waitFor(() => expect(props.onFinishedFourStages).toHaveBeenCalled());
});

it("Drag and drop", async () => {
const { container } = render(cont);
fireEvent.drop(container.getElementsByClassName("uploadCV-input")[0], {
dataTransfer: { files: [file] },
});
});

it("Drag and drop over", async () => {
const { container } = render(cont);
fireEvent.dragOver(container.getElementsByClassName("uploadCV-input")[0], {
dataTransfer: { files: [file] },
});
});

it("Drag and drop leave", async () => {
const { container } = render(cont);
fireEvent.dragOver(container.getElementsByClassName("uploadCV-input")[0], {
dataTransfer: { files: [file] },
});
});
});

+ 0
- 125
src/__tests__/UITests/applyForAdSecondStageUI.test.js Прегледај датотеку

@@ -1,125 +0,0 @@
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
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 ColorModeProvider from "../../context/ColorModeContext";
import ApplyForAdSecondStage from "../../components/Ads/ApplyForAdSecondStage";

describe("Apply for ad second stage ui tests", () => {
const props = {
professionalQualification: "Professional Qualification",
setProfessionalQualification: jest.fn(),
technologies: [
{
value: ".NET",
isChecked: false,
technologyId: 1,
technologyType: "Backend",
},
{
value: "React",
isChecked: false,
technologyId: 2,
technologyType: "Frontend",
},
{
value: "Git",
isChecked: false,
technologyId: 3,
technologyType: "Other",
},
],
setTechnologies: jest.fn(),
experience: 1,
setExperience: jest.fn(),
onIncreaseStage: jest.fn(),
onDecreaseStage: jest.fn(),
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<ApplyForAdSecondStage {...props} />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;

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

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

it("Should render apply for ad second stage", () => {
render(cont);
expect(screen.getByTestId("apply-for-ad-second-stage")).toBeDefined();
});

it("Should change professional qualification input", () => {
render(cont);
fireEvent.change(
screen.getByTestId(
"apply-for-ad-second-stage-professional-qualification"
),
{
target: { value: "Faculty" },
}
);

expect(props.setProfessionalQualification).toHaveBeenCalled();
});

it("Should render backend technology", () => {
const { container } = render(cont);

expect(
container.getElementsByClassName("apply-for-ad-second-stage-checkbox")[0]
).toBeDefined();
});

it("Should render frontend technology", () => {
const { container } = render(cont);

expect(
container.getElementsByClassName("apply-for-ad-second-stage-checkbox")[1]
).toBeDefined();
});

it("Should render others technology", () => {
const { container } = render(cont);

expect(
container.getElementsByClassName("apply-for-ad-second-stage-checkbox")[2]
).toBeDefined();
});

it("Should change experience", async () => {
render(cont);

fireEvent.change(
screen.getByTestId("apply-for-ad-second-stage-experience-input"),
{ target: { value: 2 } }
);

waitFor(() => expect(props.professionalQualification).toHaveBeenCalled());
});

it("Should change backend technology to checked", async () => {
const { container } = render(cont);

fireEvent.click(
container.getElementsByClassName("apply-for-ad-second-stage-checkbox")[0]
);

waitFor(() => expect(props.technologies[0]).toBe(true));
});
});

+ 0
- 110
src/__tests__/UITests/applyForAdThirdStageUI.test.js Прегледај датотеку

@@ -1,110 +0,0 @@
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
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 ColorModeProvider from "../../context/ColorModeContext";
import ApplyForAdThirdStage from "../../components/Ads/ApplyForAdThirdStage";

describe("Apply for ad third stage ui tests", () => {
const props = {
onIncreaseStage: jest.fn(),
onDecreaseStage: jest.fn(),
linkedinLink: "",
setLinkedinLink: jest.fn(),
githubLink: "",
setGithubLink: jest.fn(),
bitBucketLink: "",
setBitBucketLink: jest.fn(),
email: "",
setEmail: jest.fn(),
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<ApplyForAdThirdStage {...props} />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;

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

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

it("Should change linkedin input", async () => {
render(cont);
fireEvent.change(
screen.getByTestId("apply-for-ad-modal-third-stage-linkedin-input"),
{ target: { value: "https://linkedin" } }
);

waitFor(() => expect(props.linkedinLink).toBe("https://linkedin"));
});

it("Should render linkedin input", () => {
render(cont);

expect(
screen.getByTestId("apply-for-ad-modal-third-stage-linkedin-input")
).toBeDefined();
});

it("Should change github input", async () => {
render(cont);
fireEvent.change(
screen.getByTestId("apply-for-ad-modal-third-stage-github-input"),
{ target: { value: "https://github" } }
);

waitFor(() => expect(props.githubLink).toBe("https://github"));
});

it("Should change bitbucket input", async () => {
render(cont);
fireEvent.change(
screen.getByTestId("apply-for-ad-modal-third-stage-bitbucket-input"),
{ target: { value: "https://bitbucket" } }
);

waitFor(() => expect(props.bitBucketLink).toBe("https://bitbucket"));
});

it("Should change email input", async () => {
render(cont);
fireEvent.change(
screen.getByTestId("apply-for-ad-modal-third-stage-email-input"),
{ target: { value: "ermin.bronja@dilig.net" } }
);

waitFor(() => expect(props.email).toBe("ermin.bronja@dilig.net"));
});

it("Should go back when button clicked", async () => {
render(cont);
fireEvent.click(
screen.getByTestId("apply-for-ad-modal-third-stage-go-back-button")
);

waitFor(() => expect(props.onDecreaseStage).toHaveBeenCalled());
});

it("Should go forward when button clicked", async () => {
render(cont);
fireEvent.click(
screen.getByTestId("apply-for-ad-modal-third-stage-go-forward-button")
);

waitFor(() => expect(props.onIncreaseStage).toHaveBeenCalled());
});
});

+ 0
- 199
src/__tests__/UITests/candidateDetailsPageUI.test.js Прегледај датотеку

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

function createMatchMedia(width) {
return (query) => ({
matches: mediaQuery.match(query, { width }),
addListener: () => {},
removeListener: () => {},
});
}

describe("CandidateDetailsPage render tests", () => {
var props = {
history: {
replace: jest.fn(),
push: jest.fn(),
location: {
pathname: "/candidates/1",
},
},
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<CandidateDetailsPage {...props} />
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
window.matchMedia = createMatchMedia(362);
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector
.mockReturnValueOnce(mockState.users.users)
.mockReturnValueOnce(mockState.users.user)
.mockReturnValueOnce(mockState.candidate.candidate)
.mockReturnValueOnce(mockState.users.users)
.mockReturnValueOnce(mockState.users.user)
.mockReturnValueOnce(mockState.candidate.candidate)
.mockReturnValueOnce(mockState.users.users)
.mockReturnValueOnce(mockState.users.user)
.mockReturnValueOnce(mockState.candidate.candidate);

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

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

it("Should render", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("main-candidate-container")[0]
).toBeDefined();
});

it("Initialy should dispatch two methods", () => {
render(cont);
expect(mockDispatch).toBeCalledTimes(2);
});

it("Should render candidate name", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("candidate-lower-header")[0]
).toBeDefined();
});

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

it("Should represent one year of experience for the candidate", () => {
render(cont);
expect(screen.getByTestId("candidate-experience").textContent).toBe(
"candidates.experience:1"
);
});

it("Should represent all technologies of candidate", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("technology-candidate-card").length
).toBe(2);
});

it("It should show Muski inside paragraph for gender because value of gender property of our candidate is M", () => {
render(cont);
expect(screen.getByTestId("candidate-gender").textContent).toBe("common.male");
});

it("Should render dialog after clicking button for deleting candidate", () => {
const { container } = render(cont);
fireEvent.click(container.getElementsByClassName("candidate-btn")[0]);
expect(screen.getByTestId("alert-container")).toBeDefined();
});

it("Should render input for sending comment", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("comment-input")[0]).toBeDefined();
});

it("Initially mention should not be rendered", async () => {
const { container } = render(cont);
await waitFor(() =>
expect(
container.getElementsByClassName("comment-input_suggestions")[0]
).toBeUndefined()
);
});

it("Should render mention list after entering @ inside our mention input", async () => {
const { container } = render(cont);
const input = container
.getElementsByClassName("comment-input")[0]
.querySelector("textarea");

waitFor(() => {
fireEvent.change(input, { target: { value: "@" } });
expect(
container
.getElementsByClassName("comment-input")[0]
.getElementsByClassName("comment-input_suggestions")[0]
).toBeDefined();
});
});

it("Should render first button for sending commment because width of screen is greater than 361", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("comment-send-btn")[0]
).toBeDefined();
});

it("Clicking button for sending comment should dispatch function", async () => {
const { container } = render(cont);
const input = container
.getElementsByClassName("comment-input")[0]
.querySelector("textarea");
fireEvent.change(input, { target: { value: "some value" } });
fireEvent.click(container.getElementsByClassName("comment-send-btn")[0]);
await waitFor(() => expect(mockDispatch).toBeCalledTimes(3));
});

it("Should not render second button for sending commment because width of screen is greater than 361", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("comment-send-btn-responsive")[0]
).toBeUndefined();
});

it("Should render button for downloading CV", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("applicant-cv-button")[0]
).toBeDefined();
});

it("should not render responsive right and left arrow because screen width is greater than 361", () => {
render(cont);
expect(screen.queryByTestId("candidate-ad-responsive-arrows")).toBeNull();
});

it("Should render two ads for candidate", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("applicant-add").length).toBe(2);
});

it("Should render three comments", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("comment-sub-container").length
).toBe(3);
});

it("Should render page with all candidates", () => {
const { container } = render(cont);
fireEvent.click(
container.getElementsByClassName("applicant-ads-back-button")[0]
);
const arg = { pathname: "/candidates" };
expect(props.history.push).toHaveBeenCalledWith(arg);
});
});

+ 0
- 72
src/__tests__/UITests/candidateFilterUI.test.js Прегледај датотеку

@@ -1,72 +0,0 @@
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
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 ColorModeProvider from "../../context/ColorModeContext";
import CandidateFilters from "../../components/Candidates/CandidateFilters";

describe("Add ad modals ui tests", () => {
const props = {
open: true,
handleClose: jest.fn(),
pageSize: 3,
currentPage: 1,
isTableView: false,
technologies: [
{
technologyId: 1,
name: ".NET",
technologyType: "Backend",
isChecked: true,
},
],
startingDate: "",
endingDate: "",
typesOfEmployments: [],
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<CandidateFilters {...props} />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;

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

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

it("Should check checkbox", async () => {
const { container } = render(cont);

const a = container.getElementsByClassName(
"ad-filters-technologies-checkboxes-checkbox"
)[0];

waitFor(() => {
fireEvent.click(a);
expect(a).toBeDefined();
});
});

it("Should click type of employment button", async () => {
const { container } = render(cont);
const btn = container.getElementsByClassName("type-of-employment-btn");

waitFor(() => {
fireEvent.click(btn[1]);
expect(btn).toBeDefined();
});
});
});

+ 0
- 120
src/__tests__/UITests/candidatesPageUI.test.js Прегледај датотеку

@@ -1,120 +0,0 @@
import { render, screen, fireEvent, waitFor } 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";
import mediaQuery from "css-mediaquery";

function createMatchMedia(width) {
return (query) => ({
matches: mediaQuery.match(query, { width }),
addListener: () => {},
removeListener: () => {},
});
}

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

let spyOnUseSelector;

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

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

it("Should render first header because width of screen is greater than 600", () => {
render(cont);
expect(screen.getByTestId("candidates-header1")).toBeDefined();
});

it("Should render second header because width of screen is greater than 600", () => {
render(cont);
expect(screen.queryByTestId("candidates-header2")).toBeNull();
});

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

it("Should render first button responsible for showing different components inside page because width of screen is greater than 600", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("all-white-btn")[0]).toBeDefined();
});

it("Should not render second button responsible for showing different components inside page because width of screen is greater than 600", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("candidate-btn-view-2")[0]
).toBeUndefined();
});

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 first filter button because width is greater than 600", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("candidate-btn-filters1")[0]
).toBeDefined();
});

it("Should not render second filter button because width is greater than 600", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("candidate-btn-filters2")[0]
).toBeUndefined();
});

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", async () => {
// const { container } = render(cont);
// const button = container.getElementsByClassName("candidate-btn")[1];
// const a = button.innerHTML;
// await waitFor(() => {
// fireEvent.click(button);
// expect(
// container.getElementsByClassName("proba")[0].style.visibility
// ).toBe("visible");
// });
// });

it("Should render TableViewPage component when page is initialy rendered", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("candidates-table")[0]
).toBeDefined();
});

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

+ 0
- 86
src/__tests__/UITests/createAdPageUI.test.js Прегледај датотеку

@@ -1,86 +0,0 @@
import { render, screen, fireEvent } from "@testing-library/react";
import * as redux from "react-redux";
import CreateAdPage from "../../pages/AdsPage/CreateAdPage";
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";
import CreateAdSecondStep from "../../pages/AdsPage/CreateAdSecondStep";

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

let spyOnUseSelector;

beforeEach(() => {
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValue([
{
technologyId: 1,
name: ".NET",
technologyType: "Backend",
isChecked: true,
},
]);
});

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

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

it("Should render go back button", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("create-ad-buttons-back")[0]
).toBeDefined();
});

it("Should render go forward button", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("create-ad-buttons-forward")[0]
).toBeDefined();
});

it("Should render create ad first step form", () => {
render(cont);
expect(screen.getByTestId("create-ad-first-step-form")).toBeDefined();
});

it("Should render sercond step form", () => {
const { container } = render(cont);
const formControls = container.getElementsByClassName(
"create-ad-form-control-first-step-input"
);

fireEvent.change(formControls[0], { target: { value: ".NET DEVELOPER" } });

fireEvent.change(formControls[1], { target: { value: "2020-05-24" } });

fireEvent.click(
container.getElementsByClassName("create-ad-buttons-forward")[0]
);

fireEvent.click(container.getElementsByClassName("create-ad-second-step-checkbox")[0])

fireEvent.click(
container.getElementsByClassName("create-ad-buttons-forward")[0]
);

expect(screen.getByTestId("create-ad-third-step-form")).toBeDefined();
});
});

+ 0
- 46
src/__tests__/UITests/dayComponentUI.test.js Прегледај датотеку

@@ -1,46 +0,0 @@
import * as redux from "react-redux";
import store from "../../store";
import { Router } from "react-router-dom";
import { mockState } from "../../mockState";
import { render } from "@testing-library/react";
import DayComponent from "../../components/Schedules/DayComponent";
import history from "../../store/utils/history";

const props = {
numberOfDay: 1,
nameOfDay: "sre",
interviews: mockState.schedule.schedule,
onClick: jest.fn(),
};

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

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

it("Should render", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("day-component-container")[0]
).toBeDefined();
});

it("Should show only two interviews even if there is more interviews and span element which will tell us how many interviews is there", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("day-component-more")[0]
).toBeDefined();
expect(
container.getElementsByClassName("day-component-interviews-container")
.length
).toBe(2);
});
});

+ 0
- 88
src/__tests__/UITests/dayDetailsComponentUI.test.js Прегледај датотеку

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

const setCurrentlySelected = jest.fn();
const setCurrentlySelectedDay = jest.fn();

const props = {
selectedDate: "20.12.2023",
selectionProcesses: mockState.schedule.schedule,
open: jest.fn(),
onClose: jest.fn(),
setCurrentlySelected: setCurrentlySelected,
setCurrentlySelectedDay: setCurrentlySelectedDay,
currentlySelectedDay: 20,
numberOfDaysInMonth: 31,
history: {
replace: jest.fn(),
push: jest.fn(),
location: {
pathname: "/schedule",
},
},
};

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

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

it("Should render", () => {
render(cont);
expect(screen.getByTestId("day-component-dialog")).toBeDefined();
});

it("Should render left arrow as enabled because currenlty selected day is not 1", () => {
render(cont);
expect(screen.getAllByTestId("day-details-left-arrow")[0]).toBeDefined();
});

it("Should render right arrow as enabled because currently selected day is not 31", () => {
render(cont);
expect(screen.getAllByTestId("day-details-right-arrow")[0]).toBeDefined();
});

it("Should show all interviews which we pass to component", () => {
render(cont);
expect(screen.getAllByTestId("day-details-component-process").length).toBe(
mockState.schedule.schedule.length
);
});

it("Should render candidate details page after clicking on candidate name", () => {
render(cont);
fireEvent.click(screen.getAllByTestId("day-details-applicant")[0]);
const arg = { pathname: "/candidates/1" };
expect(props.history.push).toHaveBeenCalledWith(arg);
});

it("Should call function when we press right arrow", () => {
render(cont);
fireEvent.click(screen.getAllByTestId("day-details-right-arrow")[0]);
expect(setCurrentlySelected.mock.calls).toHaveLength(1);
expect(setCurrentlySelectedDay.mock.calls).toHaveLength(1);
});

it("Should call function when we press right arrow", () => {
render(cont);
fireEvent.click(screen.getAllByTestId("day-details-left-arrow")[0]);
expect(setCurrentlySelected.mock.calls).toHaveLength(1);
expect(setCurrentlySelectedDay.mock.calls).toHaveLength(1);
});
});

+ 0
- 121
src/__tests__/UITests/patternDetailsPageUI.test.js Прегледај датотеку

@@ -1,121 +0,0 @@
import {
render,
screen,
fireEvent,
waitFor,
findByTestId,
} from "@testing-library/react";
import * as redux from "react-redux";
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";
import PatternDetailsPage from "../../pages/PatternsPage/PatternDetailsPage";

describe("PatternDetailsPage render tests", () => {
var props = {
history: {
replace: jest.fn(),
push: jest.fn(),
location: {
pathname: "/patterns/1",
},
},
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<ColorModeProvider>
<PatternDetailsPage {...props} />
</ColorModeProvider>
</Router>
</redux.Provider>
);

let spyOnUseSelector;

beforeEach(() => {
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector
.mockReturnValue(mockState.patterns.patterns[0])
.mockReturnValue([
{
applicantId: 21359,
firstName: "Jelena",
lastName: "Zivkovic",
email: "jelena.d.zivkovic@gmail.com",
},
])
.mockReturnValue(null);
});

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

it("Should render PatternDetaisPage", async () => {
render(cont);
waitFor(() => {
expect(screen.getByTestId("pattern-details")).toBeDefined();
});
});

it("Should render select input for choosing candidate", async () => {
render(cont);
waitFor(() => {
expect(screen.getByTestId("pattern-details-select")).toBeDefined();
});
});

it("Should render add candidate button for choosing email", async () => {
render(cont);
waitFor(() => {
expect(screen.getByTestId("pattern-details-plus")).toBeDefined();
});
});

it("Should render go back button", async () => {
const { container } = render(cont);
waitFor(() => {
expect(
container.getElementsByClassName("ad-details-buttons-link")[0]
).toBeDefined();
});
});

it("Should go to patterns page when click go back button", async () => {
const { container } = render(cont);

waitFor(() => {
fireEvent.click(screen.getByTestId("ad-details-buttons-link"));

expect(props.history.push).toHaveBeenCalledWith("/patterns");
});
});

it("Should render send email button", () => {
render(cont);

waitFor(() => {
expect(screen.getByTestId("pattern-details-send-email")).toBeDefined();
});
});

it("Should render send email modal", () => {
render(cont);

waitFor(() => {
fireEvent.change(screen.getByTestId("pattern-details-select"), {
target: { value: "jelena.d.zivkovic@gmail.com" },
});

fireEvent.click(screen.getByTestId("pattern-details-plus"));

fireEvent.click(screen.getByTestId("pattern-details-send-email"));

expect(props.history.push).toHaveBeenCalledWith("/patterns");
});
});
});

+ 0
- 129
src/__tests__/UITests/patternsPageUI.test.js Прегледај датотеку

@@ -1,129 +0,0 @@
import { render, screen, fireEvent, waitFor } 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";

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.mockReturnValue(mockState.patterns.patterns);
// .mockReturnValueOnce([
// {
// id: 1,
// name: "HR intervju",
// selectionProcesses: [],
// },
// ]);
});

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 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();
});

it("Should render filter button", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("custom-filter-button")
).toBeDefined();
});

it("Should render filters", () => {
const { container } = render(cont);
fireEvent.click(
container.getElementsByClassName("custom-filter-button")[0]
);
expect(screen.getByTestId("pattern-filters")).toBeDefined();
});

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

it("Should submit filters handler", async () => {
const { container } = render(cont);
fireEvent.click(
container.getElementsByClassName("custom-filter-button")[0]
);
waitFor(() => {
fireEvent.click(
container.getElementsByClassName("pattern-filters-checkbox")[0]
);

fireEvent.change(
container.getElementsByClassName(
"custom-drawer-sub-card-content-input-1"
)[0],
{ target: { value: "2024-05-05" } }
);

fireEvent.change(
container.getElementsByClassName(
"custom-drawer-sub-card-content-input-2"
)[0],
{ target: { value: "2025-05-05" } }
);

const searchBtn = screen.getByTestId("custom-drawer-submit-search");

fireEvent.click(searchBtn);
expect(searchBtn).toBeDefined();
});
});
});

+ 0
- 110
src/__tests__/UITests/schedulePageUI.test.js Прегледај датотеку

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

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

let spyOnUseSelector;

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

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

it("Should render", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("schedule-page-container")[0]
).toBeDefined();
});

it("Number of div's should be equal to the number of days in current month", () => {
const { container } = render(cont);
const date = new Date();
const numberOfDaysInCurrentMonth = new Date(
date.getFullYear(),
date.getMonth() + 1,
0
).getDate();
expect(
container.getElementsByClassName("day-component-container").length
).toBe(numberOfDaysInCurrentMonth);
});

it("should render two arrows", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("schedule-page-arrow-container").length
).toBe(2);
});

it("after clicking arrow for going back one month number of div's should be equal to number of days in new month", () => {
const { container } = render(cont);
fireEvent.click(
container.getElementsByClassName("schedule-page-arrow-container")[0]
);
const date = new Date();
const currentMonth = date.getMonth();
const currentYear = date.getFullYear();
const numberOfDaysInCurrentMonth = new Date(
currentMonth - 1 === -1 ? currentYear - 1 : currentYear,
currentMonth - 1 === -1 ? 12 : currentMonth,
0
).getDate();
expect(
container.getElementsByClassName("day-component-container").length
).toBe(numberOfDaysInCurrentMonth);
});

it("after clicking arrow for going forward one month number of div's should be equal to number of days in new month", () => {
const { container } = render(cont);
fireEvent.click(
container.getElementsByClassName("schedule-page-arrow-container")[1]
);
const date = new Date();
const currentMonth = date.getMonth();
const currentYear = date.getFullYear();
const numberOfDaysInCurrentMonth = new Date(
currentMonth + 1 === 12 ? currentYear + 1 : currentYear,
currentMonth + 1 === 12 ? 1 : currentMonth + 2,
0
).getDate();
expect(
container.getElementsByClassName("day-component-container").length
).toBe(numberOfDaysInCurrentMonth);
});

it("When page is initialy rendered DayDetailsComponent should not be rendered", () => {
render(cont);
expect(screen.queryByTestId("day-component-dialog")).toBeNull();
});

it("After clicking on some day DayDetailsComponent should be rendered", () => {
const { container } = render(cont);
fireEvent.click(
container.getElementsByClassName("day-component-container")[0]
);
expect(screen.getByTestId("day-component-dialog")).toBeDefined();
});
});

+ 0
- 143
src/__tests__/UITests/selectionProcessesPageUI.test.js Прегледај датотеку

@@ -1,143 +0,0 @@
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import * as redux from "react-redux";
import SelectionProcessPage from "../../pages/selectionProcessPage/selectionProcessPage";
import store from "../../store";
import "../../i18n";
import { mockState } from "../../mockState";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import { SelectionProvider } from "../../context/SelectionContext";

describe("SelectionProcessPage render tests", () => {
var props = {
history: {
replace: jest.fn(),
push: jest.fn(),
location: {
pathname: "/selectionFlow",
},
},
};

const cont = (
<redux.Provider store={store}>
<SelectionProvider>
<Router history={history}>
<SelectionProcessPage {...props} />
</Router>
</SelectionProvider>
</redux.Provider>
);

let spyOnUseSelector;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector
.mockReturnValueOnce(mockState.selections)
.mockReturnValueOnce(mockState.selections.processes)
.mockReturnValueOnce(mockState.selections.statuses);
// MOZDA VRATITI KANDIDATE i korisnike?
// spyOnUseSelector.mockReturnValue(mockState.selections);
});

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

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

it("Should render a card foreach mocked level", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("selection-card").length).toBe(4);
});

// it("Should render a button with specific class for an enabled user", () => {
// const { container } = render(cont);
// expect(container.getElementsByClassName("td-btn").length).toBe(0);
// });

it("Should render filter button", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("userPageBtn").length).toBe(1);
});

it("Should render date if process is scheduled", () => {
render(cont);
expect(screen.getAllByTestId("process-date")[0]).toBeDefined();
});

it("Should render scheduler if process is scheduled", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("sel-item-scheduler")[0]
).toBeDefined();
});

it("Should render interviewBtn", () => {
render(cont);
expect(screen.getByTestId("interview-image")).toBeDefined();
});

// it("Should render interview dailog when clicked on", () => {
// render(cont);
// fireEvent.click(screen.getByTestId("interview-image"));
// waitFor(() => expect(screen.getByTestId("interview-dialog")).toBeDefined());
// });

it("Should render selection card if process failed", () => {
render(cont);
expect(
screen.getByRole("button", {
name: /neuspešno/i,
})
).toBeDefined();
});

it("Should navigate to applicant selection when status is clicked if status is 'failed'", () => {
render(cont);

var statusBtn = screen.getByRole("button", {
name: /neuspešno/i,
});

fireEvent.click(statusBtn);
// 21364 is the id of the applicant in mockState
expect(props.history.push).toHaveBeenCalledWith("/selectionFlow/21364");
});

it("Should navigate to applicant selection when status is clicked if status is 'done'", () => {
render(cont);

var statusBtn = screen.getAllByRole("button", {
name: /zakazan/i,
})[0];

fireEvent.click(statusBtn);

expect(screen.getByTestId("status-select")).toBeDefined();
});

it("Should not render selection card if process is done", () => {
render(cont);
expect(
screen.queryByRole("button", {
name: /odrađen/i,
})
).toBe(null);
});

it("Drag and drop", () => {
const { container } = render(cont);
fireEvent.drop(container.getElementsByClassName("selection-card")[0], {
dataTransfer: {
getData: (type) =>
'{"id":32,"name":"random","status":"Kandidat primljen","date":null,"link":"link","applicant":{"applicantId":6,"firstName":"Safet","lastName":"Purkovic","position":"React Developer","dateOfApplication":"2021-05-05T00:00:00","cv":"dasdas","email":"safet@gmail.com","phoneNumber":"2313123","linkedlnLink":"sda","githubLink":null,"bitBucketLink":null,"experience":2,"applicationChannel":null,"typeOfEmployment":"Posao","technologyApplicants":[],"comments":[],"ads":[],"selectionProcesses":[{"status":"Kandidat primljen","date":null,"link":"link","scheduler":{"id":7,"firstName":"Safet","lastName":"Purkovic","email":"safet.purkovic@dilig.net","isEnabled":true},"selectionLevel":{"id":4,"name":"Konacna odluka"}}]},"selectionLevelId":4}',
},
});
});
});

+ 0
- 57
src/__tests__/UITests/statsAdComponentUI.test.js Прегледај датотеку

@@ -1,57 +0,0 @@
import * as redux from "react-redux";
import store from "../../store";
import { Router } from "react-router-dom";
import { render } from "@testing-library/react";
import history from "../../store/utils/history";
import StatsAd from "../../components/Ads/StatsAd";

describe("StatsAd render tests", () => {
var props = {
className: "some class",
count: 2,
title: ".NET",
minimumExperience: 1,
createdAt: "20.12.2023",
expiredAt: "28.12.2023",
onShowAdDetails: jest.fn(),
};
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<StatsAd {...props} />
</Router>
</redux.Provider>
);

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

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

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

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

it("Should render that experience is required because minimumExperience is greater than 0", () => {
const { container } = render(cont);
expect(
container
.getElementsByClassName("archive-ad-experience")[0]
.getElementsByTagName("p")[0].textContent
).toBe("1+ common.experience");
});
});

+ 0
- 92
src/__tests__/UITests/statsPageUI.test.js Прегледај датотеку

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

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

let spyOnUseSelector;

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

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

it("Should render", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("stats-section")[0]).toBeDefined();
});

it("Should render 4 levels for selection process section", () => {
render(cont);
expect(screen.getAllByTestId("stats-item").length).toBe(4);
});

it("Should render 4 levels for relationship section", () => {
render(cont);
expect(screen.getAllByTestId("stats-item2").length).toBe(4);
});

it("Should render ads because there is more than 0 ads", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("archived-ads")[0]).toBeDefined();
});

it("Should render only one right arrow and that depends on screen size", () => {
render(cont);
expect(screen.getAllByTestId("right-arrow").length).toBe(1);
});

it("Should render right arrow because there is more than 3 ads", () => {
render(cont);
expect(screen.getAllByTestId("right-arrow")[0]).toBeDefined();
});

it("Should render only one left arrow and that depends on screen size", () => {
render(cont);
expect(screen.getAllByTestId("left-arrow").length).toBe(1);
});

it("Should render all ads in slider", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("stats-ad").length).toBe(
mockState.stats.ads.length
);
});

it("Slider should represent 5 ads", () => {
const { container } = render(cont);
expect(
container
.getElementsByClassName("slick-list")[0]
.getElementsByClassName("slick-active").length
).toBe(5);
});

it("After clicking on right arrow slider should represent ad number six as fifth ad in slider", async () => {
const { container } = render(cont);
fireEvent.click(screen.getAllByTestId("right-arrow")[0]);
await waitFor(() =>
expect(
container
.getElementsByClassName("slick-list")[0]
.getElementsByClassName("slick-active")[4]
.getElementsByClassName("archive-ad-title")[0].textContent
).toBe("React")
);
});
});

+ 0
- 149
src/__tests__/UITests/tableViewPageUI.test.js Прегледај датотеку

@@ -1,149 +0,0 @@
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import * as redux from "react-redux";
import store from "../../store";
import { mockState } from "../../mockState";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import TableViewPage from "../../pages/CandidatesPage/TableViewPage";
import { PAGE_SIZE_CANDIDATES } from "../../constants/keyCodeConstants";
import { getCV } from "../../request/candidatesRequest";

describe("TableViewPage render tests", () => {
var props = {
history: {
replace: jest.fn(),
push: jest.fn(),
location: {
pathname: "/candidates",
},
},
setPage: jest.fn(),
sliderValue: [0, 2],
startingDate: "",
endingDate: "",
typesOfEmployments: [],
technologie: [],
page: 1,
};

const cont = (
<redux.Provider store={store}>
<Router history={history}>
<TableViewPage search="" {...props} />
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

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

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

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

it("Should render", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("candidates-table")[0]
).toBeDefined();
});

it("Should dispatch function when component is rendered", async () => {
render(cont);
await waitFor(() => expect(mockDispatch).toBeCalledTimes(1));
});

it("Should render table", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("usersTable")[0]).toBeDefined();
});

it("Should render pagination component", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("candidates-pagination")[0]
).toBeDefined();
});

it("Here we check if our component is displaying as many pages as it should display", () => {
const { container } = render(cont);
expect(
container
.getElementsByClassName("css-wjh20t-MuiPagination-ul")[0]
.getElementsByTagName("li").length - 2 // we substract because list will contain left and right arrow
).toBe(
parseInt(mockState.candidates.candidates.length) <= PAGE_SIZE_CANDIDATES
? 1
: Math.ceil(
parseInt(mockState.candidates.candidates.length) /
PAGE_SIZE_CANDIDATES
)
);
});

it("The number of table rows should be equal to the number of candidates", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("cadidate-row").length).toBe(
mockState.candidates.candidates.length
);
});

it("Should render candidate details page after clicking on table row", () => {
const { container } = render(cont);
fireEvent.click(container.getElementsByClassName("cadidate-row")[0]);
const arg = { pathname: "/candidates/1" };
expect(props.history.push).toHaveBeenCalledWith(arg);
});

it("Initially CV of candidate isn't displayed", () => {
const { container } = render(cont);
expect(
container.getElementsByClassName("candidates-cv")[0].style.opacity
).toBe("0");
});

// How to mock constant?
// it("When user change table page, function for fetching users should be called", async () => {
// const { container } = render(cont);
// const pag = container
// .getElementsByClassName("MuiPagination-ul")[0]
// .getElementsByTagName("li")[1]
// .querySelector("button");
// fireEvent.click(pag);
// await waitFor(() => expect(mockDispatch).toBeCalledTimes(2));
// });

it("Should render CV of candidate after clicking on CV name", async () => {
let catchFn = jest.fn();
const basse64Pdf = "some base64 string";
getCV("name od pdf file")
.then(() => basse64Pdf)
.catch(catchFn);
const { container } = render(cont);

fireEvent.click(
container
.getElementsByClassName("cadidate-row")[0]
.getElementsByTagName("td")[4]
.getElementsByTagName("span")[0]
);

waitFor(() => {
expect(
container.getElementsByClassName("candidates-cv")[0].style.opacity
).toBe("1");
});
});
});

+ 11
- 1219
src/assets/styles/components/_ads.scss
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку


+ 0
- 73
src/assets/styles/components/_candidate-card.scss Прегледај датотеку

@@ -1,73 +0,0 @@
.candidate-card-container {
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 36px;
gap: 18px;
isolation: isolate;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 18px;
width: 354px;
height: 238px;
margin-right: 27px;
}

@media only screen and (max-width: 480px) {
.candidate-card-container{
width: 300px;
}
}

@media only screen and (max-width: 430px) {
.candidate-card-container{
width: 240px;
}
}

.candidate-card-container:active {
animation-timing-function: ease-in-out;
animation-duration: 300ms;
}

.candidate-card-container:hover{
cursor: pointer;
}

.candidate-card-tecnologies-container {
display: flex;
}

.candidate-card-date {
font-family: Source Sans Pro;
font-size: 12px;
font-weight: 400;
line-height: 15px;
letter-spacing: 0em;
}

.candidate-card-applicant-name {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 600;
line-height: 20px;
letter-spacing: 0em;
color: #226cb0;
}

.candidate-card-years {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
color: #272727;
}

.candidate-card-techologies {
display: flex;
justify-content: space-between;
gap: 18px;
}

+ 0
- 577
src/assets/styles/components/_candidatePage.scss Прегледај датотеку

@@ -1,367 +1,3 @@
.main-candidate-container {
display: flex;
flex-direction: column;
margin-top: 36px;
padding-right: 72px;
}

.top-candidate-container {
display: flex;
justify-content: space-between;
margin-left: 144px;
}

.candidate-header {
height: 36px;
font-family: Source Sans Pro;
font-size: 36px;
font-weight: 600;
line-height: 36px;
letter-spacing: 0.02em;
text-align: left;
color: #272727;
}

.separation-line {
margin-left: 5px;
margin-right: 5px;
font-size: 20px;
align-self: flex-end;
}

.candidate-lower-header {
font-family: Source Sans Pro;
font-size: 24px;
font-weight: 600;
line-height: 32px;
letter-spacing: 0.02em;
text-align: left;
color: #226cb0;
align-self: flex-end;
}
.candidate-option-container {
display: flex;
height: 38px;
}

.content-candidate-container {
display: flex;
justify-content: space-between;
margin-top: 14px;
margin-left: 144px;
}

.technologies-candidate-container {
display: flex;
margin-top: 18px;
}

.technology-candidate-card {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 9px;
gap: 10px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 9px;
}

.technology-candidate-card:not(:last-child) {
margin-right: 18px;
}

.comment-container {
width: 612px;
height: 404px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 12px;
margin-top: 16px;
}

.candidate-informations-container {
display: flex;
flex-direction: column;
}

.candidate-informations-sub-container {
margin-top: 36px;
}

.informations-candidate-header {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 16px;
line-height: 20px;
color: #272727;
}

.candidate-property-container {
display: flex;
flex-direction: column;
}

.candidate-property {
margin-top: 18px;
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0em;
}

.candidate-property-value {
@extend .candidate-property;
color: #1e92d0;
}

.candidate-informations-sub-container {
display: flex;
}

.comment-container {
display: flex;
flex-direction: column;
padding-left: 36px;
padding-right: 36px;
padding-top: 36px;
}

.comment-sub-container {
display: flex;
align-items: center;
}

.comment-sub-container:not(:first-child) {
margin-top: 36px;
}

.comment-sender {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 50%;
box-sizing: border-box;
border: 1px solid;
border-color: #226cb0;
}

.comment-sender p {
color: #226cb0;
}

.comment-message {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-end;
padding: 9px;
gap: 9px;
height: fit-content;
background: #f4f4f4;
border-radius: 12px;
margin-left: 18px;
max-width: 400px;
}

.comment-message-content {
align-self: flex-start;
height: 20px;
font-family: "Source Sans Pro";
font-style: normal;
font-size: 16px;
line-height: 20px;
}

.comment-message-date {
align-self: flex-end;
height: 15px;
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 15px;
color: #272727;
}

.comment-separation-line {
width: 100%;
height: 0px;
border: 1px solid #e4e4e4;
background: #e4e4e4;
}

.send-comment-container {
margin-top: 18px;
}

.send-comment-container p {
font-family: "Source Sans Pro";
font-weight: 400;
font-size: 16px;
line-height: 20px;
color: #9d9d9d;
}

.send-comment-sub-container {
display: flex;
margin-top: 9px;
height: 56px;
margin-bottom: 36px;
}

.comment-send-btn {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 18px 36px;
gap: 10px;
background: #ffffff;
border: 1px solid #226cb0;
border-radius: 9px;
border: 1px solid #226cb0;
width: 156px;
margin-left: 18px;
}

.comment-send-btn-responsive {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background: #ffffff;
border: 1px solid #226cb0;
border-radius: 9px;
border: 1px solid #226cb0;
margin-left: 18px;
width: 54px;
height: 54px;
}

.comment-send-btn:hover {
cursor: pointer;
}

.comment-send-btn-responsive:hover {
cursor: pointer;
}

.comment-send-btn-responsive img {
margin: 0;
}

.comment-send-btn p {
width: 62px;
height: 15px;
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 12px;
line-height: 15px;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #226cb0;
flex: none;
order: 1;
flex-grow: 0;
}

.comment-send-btn img {
width: 12px;
height: 12px;
}

.comment-send-btn-responsive img {
width: 12px;
height: 12px;
}

.candidate-users {
background-color: #f4f4f4;
}

.candidate-user {
color: #226cb0;
}

.applicant-ads-container {
margin-top: 36px;
}

.applicant-ads-container > p {
font-family: "Source Sans Pro";
font-weight: 600;
font-size: 24px;
line-height: 32px;
letter-spacing: 0.02em;
color: #272727;
}

.applicant-ads-sub-container {
margin-top: 18px;
display: flex;
margin-left: -20px;
}

.applicant-add {
display: flex;
flex-direction: column;
align-items: center;
padding: 36px;
gap: 18px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 12px;
width: 247px;
height: 238px;
cursor: pointer;
margin-left: 27px;
}

.applicant-add-date {
font-family: Source Sans Pro;
font-size: 12px;
font-weight: 400;
line-height: 15px;
letter-spacing: 0em;
}

.applicant-add-title {
font-family: "Source Sans Pro";
font-weight: 600;
font-size: 16px;
line-height: 20px;
color: #226cb0;
}

.applicant-add-site {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: #ffffff;
padding: 5px;
border: 1px solid #e4e4e4;
border-radius: 8px;
font-size: 16px;
font-family: "Source Sans Pro";
font-weight: 400;
color: #272727;
}

.applicant-ads-buttons-container {
display: flex;
align-self: flex-end;
align-items: center;
margin-bottom: 54px;
margin-top: 18px;
}

.applicant-cv-button {
display: flex;
@@ -395,130 +31,7 @@
cursor: pointer;
}

.tagStyle {
color: #226cb0;
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 600;
line-height: 20px;
letter-spacing: 0em;
}

.comment-input {
@extend .tagStyle;
min-width: 368px;
max-width: 368px;
}

.comment-input::placeholder {
height: 20px;
font-family: "Source Sans Pro";
font-style: italic;
font-weight: 400;
font-size: 16px;
line-height: 20px;
color: #9d9d9d;
flex: none;
}

.comment-input-list {
@extend .tagStyle;
}

.comment-message-con {
max-width: 400px;
}

.comment-container-header {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 18px;
line-height: 32px;
letter-spacing: 0.02em;
color: #272727;
}

.applicant-ads-container-2 {
display: flex;
align-items: center;
margin-top: 18px;
}

@media only screen and (max-width: 930px) {
.comment-container {
width: 500px;
}
.comment-input {
width: 250px;
}

.comment-message-con {
max-width: 300px;
}
}

@media only screen and (max-width: 820px) {
.comment-container {
width: 400px;
}

.comment-input {
width: 170px;
}
.comment-message-con {
max-width: 200px;
}
.comment-send-btn {
width: 90;
padding: 10px 20px;
}
.send-comment-sub-container {
height: 45px;
margin-bottom: 20px;
}
.comment-input ::placeholder {
font-size: 14px;
line-height: 18px;
padding-top: 2px;
}
}

@media only screen and (max-width: 700px) {
.comment-container {
width: 380px;
}
.comment-message-con {
max-width: 180px;
}
.comment-send-btn {
width: 110;
padding: 10px 20px;
}
}

@media only screen and (max-width: 680px) {
.content-candidate-container {
flex-direction: column;
}
.comment-container {
margin-top: 30px;
}
}

@media only screen and (max-width: 540px) {
.candidate-header {
height: 30px;
font-family: Source Sans Pro;
font-size: 30px;
line-height: 30px;
}

.candidate-lower-header {
font-size: 18px;
line-height: 26px;
}

.applicant-cv-button {
padding: 10px 52px;
gap: 7px;
@@ -531,89 +44,7 @@
}
}

@media only screen and (max-width: 480px) {
.comment-container {
width: 297px;
}

.comment-input {
width: 100px;
}

.comment-input::placeholder {
font-size: 12px;
line-height: 15px;
padding-top: -2px;
}

.comment-message-con {
max-width: 130px;
}
.comment-send-btn {
padding: 5px 5px;
}
.comment-container {
padding: 20px;
}

.candidate-header {
height: 20x;
font-size: 20px;
line-height: 20px;
}

.candidate-lower-header {
font-size: 17px;
line-height: 17px;
}
}

@media only screen and (max-width: 361px) {
.main-candidate-container{
padding-right: 36px;
}
.top-candidate-container {
margin-left: 36px;
}

.content-candidate-container {
margin-left: 36px;
}

.comment-input {
@extend .tagStyle;
min-width: 195px;
max-width: 195px;
height: 54px;
}

.comment-send-btn {
width: fit-content;
}

.comment-input::placeholder {
font-size: 10px;
line-height: 12px;
padding: 14px;
}

.comment-container {
width: 300px;
padding-left: 18px;
}

.content-candidate-container {
justify-content: initial;
}

.applicant-ads-container-2 {
flex-direction: column-reverse;
align-items: flex-start;
}
.slick-list {
padding-left: 0 !important;
}

.applicant-ads-back-button {
font-size: 14px;
line-height: 18px;
@@ -626,12 +57,4 @@
line-height: 15px;
letter-spacing: 0.04em;
}

.active-ads-ads-arrows {
margin-left: -0.75rem;
}

.applicant-ads-buttons-container{
margin-left: 36px;
}
}

+ 0
- 244
src/assets/styles/components/_candidatesPage.scss Прегледај датотеку

@@ -1,18 +1,3 @@
.main-candidates-container {
display: flex;
flex-direction: column;
margin-bottom: 36px;
margin-top: 36px;
padding-left: 72px;
}

.top-candidates-container {
display: flex;
justify-content: space-between;
margin-left: 72px;
margin-right: 72px;
}

.candidate-btn {
margin-left: 20px;
font-family: "Source Sans Pro";
@@ -24,17 +9,6 @@
text-transform: none;
}

.all-white-btn {
background: linear-gradient(0deg, #e8f7ff, #e8f7ff),
linear-gradient(0deg, #226cb0, #226cb0);
}

.candidate-btn-mobile {
@extend .candidate-btn;
// overrride style from candidate-btn class
margin-left: 10px;
}

.invite-btn {
padding: 18px 72px;
gap: 10px;
@@ -47,168 +21,12 @@
margin-bottom: 36px;
}

.candidates-options-container {
display: flex;
}

.candidates-header {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 36px;
line-height: 32px;
letter-spacing: 0.02em;
color: #272727;
}

.candidates-table {
margin-top: 30px;
width: 100%;
display: flex;
flex-direction: column;
min-height: 70vh;
align-items: space-between;
}

.candidates-image {
height: 14px;
width: 16px;
margin-left: 10px;
}

.candidate-image-mobile {
position: "relative";
top: -0.25;
}

.invite-btn-color {
color: #fff;
background-color: #226cb0;
}

.ads-candidates-container {
max-height: 837px;
overflow-y: auto;
overflow-x: hidden;
}

.ads-candidates-slider {
display: flex;
margin-top: 31px;
}

.ads-candidates-slider .slick-track {
margin: 0 !important;
}

.ads-candidates-slider .slick-slider {
width: 100% !important;
}

.ads-candidates-top-container {
display: flex;
align-items: center;
margin-left: 72px;
}

.ads-candidates-title {
font-family: Source Sans Pro;
font-size: 24px;
font-weight: 600;
letter-spacing: 0.02em;
margin-left: 18px;
}

.ads-candidates-numberOfApplicants {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 600;
letter-spacing: 0em;
color: #226cb0;
margin-left: 5px;
}

.ads-candidates-image {
width: 49px;
height: 39px;
}

.top-cnd {
margin-top: 39px;
}

.top-cnd > div:not(:first-child) {
margin-top: 79px;
}

.filter-date-container {
border: none;
box-sizing: border-box;
outline: 0;
position: relative;
width: 100%;

display: flex;
flex-direction: column;
margin-top: 10px;
}

.filter-date-container input[type="date"]::-webkit-calendar-picker-indicator {
background: transparent;
bottom: 0;
color: transparent;
cursor: pointer;
height: auto;
left: 0;
position: absolute;
right: 0;
top: 0;
width: auto;
}

.filter-date-container input {
border: 1px solid gray;
border-radius: 4px;
height: 40px;
padding-left: 12px;
font-family: Source Sans Pro;
font-size: 16px;
font-style: italic;
font-weight: 400;
line-height: 20px;
letter-spacing: 0em;
text-align: left;
color: #9d9d9d;
margin-top: 5px;
}

.left-move-candidateAd {
margin-left: -0.75rem !important;
}

.left-move-candidateAd-page {
margin-left: 16px;
}

.left-move-candidateAd-page-2 {
margin-left: 42px;
}

// .ads-search-field-responsive {
// min-width: 350px;
// border: 1px solid #226cb0;
// border-radius: 10px;
// padding: 20px;
// background-color: white;
// position: absolute;
// top: 100px;
// right: 9px;
// z-index: 1000;
// &.smaller {
// min-width: 250px !important;
// }
// }

.ads-search-field-responsive2 {
border: 1px solid #226cb0;
border-radius: 10px;
@@ -265,70 +83,8 @@
font-style: italic;
}

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

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

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

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

.candidates-pagination {
margin-top: 20px;
align-self: center;
padding-bottom: 20px;
}

.candidates-cv {
width: 500px;
height: 610px;
margin-right: 72px;
}

@media only screen and (max-width: 600px) {
.ads-candidates-title {
font-size: 18px;
margin-left: 10px;
}
.ads-candidates-image {
width: 40px;
height: 30px;
}
.ads-candidates-numberOfApplicants {
font-size: 14px;
}
}

@media only screen and (max-width: 361px) {
.ads-candidates-slider {
flex-direction: column-reverse;
}

.main-candidates-container {
padding-left: 0px;
}

.top-candidates-container {
margin-left: 36px;
margin-right: 29px;
}
.candidates-textField {
margin-left: 36px;
}

.ads-candidates-top-container {
margin-left: 36px;
}

.left-move-candidateAd-page {
margin-left: -12px;
}
}

+ 0
- 95
src/assets/styles/components/_day-component.scss Прегледај датотеку

@@ -1,95 +0,0 @@
.day-component-container {
display: flex;
flex-direction: column;
height: 143px;
width: 178px;
border: 1px solid #f4f4f4;
padding: 17px;
transition: 0.5s;
}

.day-component-container:hover{
cursor: pointer;
scale: 1.02;
animation-timing-function: ease-in-out;
animation-duration: 300ms;
}

.day-component-container:hover {
height: 151px;
width: 184px;
left: 489px;
top: 244px;
border-radius: 12px;
background: linear-gradient(0deg, #e8f7ff, #e8f7ff),
linear-gradient(0deg, #226cb0, #226cb0);
border: 1px solid #226cb0;
}
.day-component-day-informations-container {
display: flex;
height: fit-content;
margin-bottom: 5px;
}

.day-component-day-number {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 600;
line-height: 20px;
letter-spacing: 0em;
text-align: left;
color: linear-gradient(0deg, #353535, #353535),
linear-gradient(0deg, #272727, #272727);
}

.day-component-day-name {
font-family: Source Sans Pro;
font-size: 12px;
font-weight: 400;
line-height: 15px;
letter-spacing: 0em;
text-align: left;
align-self: flex-end;
margin-left: 3px;
}

.day-component-interviews-container {
display: flex;
}

.day-component-interviews-time {
font-family: Source Sans Pro;
font-size: 12px;
font-weight: 400;
line-height: 15px;
letter-spacing: 0em;
text-align: left;
align-self: flex-end;
}

.day-component-interviews-name {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 600;
line-height: 20px;
letter-spacing: 0em;
text-align: left;
color: #353535;
margin-left: 3px;
}

.day-component-more {
height: 15px;
width: fit-content;
font-family: Source Sans Pro;
font-size: 12px;
font-weight: 400;
line-height: 15px;
letter-spacing: 0em;
text-align: left;
color: #226CB0;
font-style: normal;
text-decoration-line: underline;
cursor: pointer;
margin-top: 21px;
}

+ 3
- 197
src/assets/styles/components/_day-details-component.scss Прегледај датотеку

@@ -1,200 +1,6 @@
.day-details-sub-container {
padding-top: 46px;
padding-left: 36px;
padding-right: 36px;
padding-bottom: 36px;
}

.day-datails-title-container {
display: flex;
align-items: center;
height: fit-content;
}

.day-details-calendar-image {
width: 15.75px;
height: 18px;
}

.day-details-main-header {
font-family: Source Sans Pro;
font-size: 24px;
font-weight: 600;
line-height: 32px;
letter-spacing: 0.02em;
text-align: left;
margin-left: 9.25px;
}

.day-details-header {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 600;
line-height: 20px;
letter-spacing: 0em;
text-align: left;
color: #226cb0;
margin-left: 5px;
align-self: flex-end;
}

.day-details-close-btn {
margin-left: 208px;
cursor: pointer;
}

.day-details-time {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0em;
text-align: left;
border: 1px solid #f4f4f4;
padding: 8px;
border-radius: 10px;
}

.day-details-line {
height: 1px;
width: 100%;
background-color: #f4f4f4;
margin-top: 19px;
margin-bottom: 19px;
}

.day-details-name {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0em;
text-align: left;
color: #272727;
margin-left: 6px;
width: 111px;
}

.day-details-applicant {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 600;
line-height: 20px;
letter-spacing: 0em;
text-align: left;
color: #226cb0;
margin-left: 72px;
width: 110px;
cursor: pointer;
text-decoration: none;
}

.day-details-link {
display: flex;
align-items: center;
gap: 10px;
height: 36px;
width: 120px;
border-radius: 9px;
padding: 18px 36px 18px 36px;
color: linear-gradient(0deg, #226cb0, #226cb0),
linear-gradient(0deg, #ffffff, #ffffff);
border: 1px solid #226cb0;
margin-left: 28px;
cursor: pointer;
text-decoration: none;
}

.day-details-link span {
font-family: Source Sans Pro;
font-size: 12px;
font-weight: 600;
line-height: 15px;
letter-spacing: 0.04em;
text-align: left;
color: #226cb0;
text-transform: uppercase;
}

.day-details-content-sub-container > div:first-child {
margin-top: 37px;
}

.day-details-arrow-container {
display: flex;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 7px;
width: 36px;
height: 36px;
justify-content: center;
align-items: center;
cursor: pointer;
}


.day-details-arrow-container-p {
display: flex;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 7px;
width: 36px;
height: 36px;
justify-content: center;
align-items: center;
cursor: pointer;
transform: rotate(-180deg);
}

@media only screen and (max-width: 361px) {
.day-details-applicant {
margin-left: 10px;
}
.day-details-sub-container {
padding: 32px;
}
.day-details-link {
width: 36px;
height: 36px;
gap: 0px;
padding: 11px;
margin-left: 20px;
}
.day-details-name {
width: 66px;
}
.day-details-applicant {
width: 97px;
}
.day-details-close-btn {
margin-left: 12px;
}

.day-details-main-header {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 600;
line-height: 32px;
letter-spacing: 0.02em;
text-align: left;
}

.day-details-header{
font-family: Source Sans Pro;
font-size: 12px;
font-weight: 600;
line-height: 18px;
letter-spacing: 0em;
text-align: left;
}
.day-details-header{
align-self: center;
}
}

.css-ypiqx9-MuiDialogContent-root{
.css-ypiqx9-MuiDialogContent-root {
padding: 0px !important;
}
.css-bdhsul-MuiTypography-root-MuiDialogTitle-root{
.css-bdhsul-MuiTypography-root-MuiDialogTitle-root {
padding: 0px !important;
}
}

+ 0
- 461
src/assets/styles/components/_patterns.scss Прегледај датотеку

@@ -1,352 +1,3 @@
.patterns {
padding: 0 72px;
@include media-below($bp-xl) {
padding: 0 18px;
}
}

.patterns-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16px;
padding-left: calc(144px - 72px);
margin-bottom: 18px !important;
@include media-below($bp-xl) {
padding-left: 18px;
}
}

.pattern-header-active-button {
background-color: $mainBlueLight !important;
}

.patterns-header button {
margin-left: 14px;
}

.patterns-cards {
padding: 0 calc(138px - 72px) 0 calc(144px - 72px);
display: flex;
flex-wrap: wrap;
width: 100% !important;
margin-bottom: 18px !important;
@include media-below($bp-xl) {
padding: 0 18px !important;
flex-direction: column !important;
}
}

.pattern-card-parent {
width: calc(100% / 3) !important;
margin-bottom: 36px;
padding-right: 36px;
@include media-below($bp-xl) {
width: 100% !important;
padding: 0 !important;
}
}

/* PATTERN CARD */
.pattern-card-with-icon {
position: relative;
}

.pattern-card {
position: relative;
box-sizing: border-box;
padding: 72px !important;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 18px;
isolation: isolate;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 18px;
width: 100% !important;
transition: 0.3s;
cursor: pointer;
}

.pattern-card:hover {
scale: 1.05;
border-color: $mainBlue !important;
background-color: $mainBlueLight !important;
}

.pattern-card-edit {
position: absolute;
top: 9px !important;
right: 9px !important;
border-radius: 50% !important;
width: 40px !important;
height: 40px !important;
z-index: 100;
}

.pattern-card-date p {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 15px;
color: #272727;
flex: none;
order: 0;
flex-grow: 0;
z-index: 0;
}

.pattern-card-title p {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 24px;
line-height: 32px;
letter-spacing: 0.02em;
color: $mainBlue;
flex: none;
order: 1;
flex-grow: 0;
z-index: 1;
}

.pattern-card-selection-proccess {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 9px;
gap: 10px;
border: 1px solid #e4e4e4;
border-radius: 9px;
flex: none;
order: 2;
flex-grow: 0;
z-index: 2;
}

.pattern-card-date,
.pattern-card-title,
.pattern-card-selection-proccess {
display: flex !important;
justify-content: center !important;
}

.patterns-button {
padding-bottom: 18px;
display: flex;
justify-content: flex-end;
}

/* AD DETAILS */
.pattern-details {
padding: 42px 36px 72px 36px !important;
@include media-below($bp-xl) {
margin-top: 9px;
}
}

.pattern-details-header {
display: flex;
align-items: center;
justify-content: flex-end;
@include media-below($bp-xl) {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 14px;
justify-content: flex-start;
}
}

.pattern-details-header p {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
text-align: right;
}

.pattern-details-header p span {
color: #9d9d9d;
}

.pattern-details-card {
margin: 41px auto 0 auto !important;
padding: 0 405px !important;
@include media-below($bp-xl) {
margin: 9px auto 0 auto !important;
padding: 0 !important;
}
}

.pattern-details-card-title {
display: flex;
align-items: center;
margin-bottom: 7px;
@include media-below($bp-xl) {
margin-top: 9px;
}
}

.pattern-details-card-title-title h1 {
font-size: 36px;
margin-right: 4px;
@include media-below($bp-xl) {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 18px;
line-height: 32px;
letter-spacing: 0.02em;
}
}

.pattern-details-card-title-sub {
font-size: 24px;
color: $mainBlue;
font-weight: 600;
@include media-below($bp-xl) {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 18px;
line-height: 32px;
letter-spacing: 0.02em;
}
}

.pattern-details-card-sub-card {
margin-bottom: 18px;
}

.pattern-details-card-sub-card-title {
margin-bottom: 10px;
}

.pattern-details-card-screening-title p {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
color: #272727;
}

.pattern-details-card-sub-card-emails {
display: flex;
flex-wrap: wrap;
}

.pattern-details-card-sub-card-emails > div {
margin-right: 9px;
margin-bottom: 4px;
}

.pattern-details-card-sub-card-emails-email {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 9px;
gap: 10px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 9px;
flex: none;
order: 0;
flex-grow: 0;
}

.pattern-details-card-sub-card-add-email {
display: flex;
}

.pattern-details-card-sub-card-add-email input,
.pattern-details-card-sub-card-add-email select {
margin-right: 18px;
flex: 50;
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
padding: 18px;
gap: 10px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 7px;
outline: none;
}

.pattern-details-card-sub-card-add-email button {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 18px;
gap: 10px;
background: #ffffff;
border: 1px solid #226cb0;
border-radius: 9px;
flex: 1;
cursor: pointer;
transition: 0.3s;
}

.pattern-details-card-sub-card-add-email button:hover {
background-color: $mainBlueLight;
}

.pattern-details-card-sub-card-add-email button img {
width: 12px;
height: 12px;
}

.pattern-details-card-sub-card-message-pattern {
display: flex;
flex-direction: column;
height: 256px !important;
border: 2px solid#ccc !important;
background: rgb(224, 224, 224) !important;
border-radius: 6px;
padding: 1rem;
}

.pattern-details-card-sub-card-message-pattern ul {
list-style: disc;
padding: 1rem 2rem;
}

.pattern-details-card-sub-card-message-pattern textarea {
resize: none;
width: 100%;
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: flex-start;
padding: 18px;
gap: 10px;
background: #f4f4f4;
border: 1px solid #e4e4e4;
border-radius: 7px;
min-height: 256px;
}

.pattern-details-card-buttons {
display: flex;
justify-content: flex-end;
align-items: center;
}

.pattern-details-card-buttons > * {
margin-left: 18px !important;
}

.custom-modal {
padding: 36px !important;
@@ -458,118 +109,6 @@
cursor: pointer;
}

.edit-pattern-modal {
width: 512px;
min-height: 618px;
}

.edit-pattern-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 18px;
}

.edit-pattern-modal-header-title {
display: flex;
align-items: center;
}

.edit-pattern-modal-header-title > * {
margin-right: 4px;
}

.edit-pattern-modal-header-title-image img {
width: 18px;
height: 18px;
}

.edit-pattern-modal-header-title-title p {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 24px;
line-height: 32px;
letter-spacing: 0.02em;
color: #272727;
}

.edit-pattern-modal-header-title-sub sub {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
line-height: 32px;
font-size: 18px;
letter-spacing: 0.02em;
color: $mainBlue;
}

.edit-pattern-modal-header-close img {
width: 9px;
height: 10.5px;
cursor: pointer;
}

.edit-pattern-modal-form-control {
display: flex;
flex-direction: column;
margin-bottom: 9px;
}

.edit-pattern-modal-form-control .tox {
height: 300px !important;
}

.edit-pattern-modal-form-control label {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
color: #272727;
margin-bottom: 4.5px;
}

.edit-pattern-modal-form-control input,
.edit-pattern-modal-form-control select,
.edit-pattern-modal-form-control textarea {
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
padding: 18px;
gap: 10px;
border: 1px solid #e4e4e4;
border-radius: 7px;
outline: none;
}

.edit-pattern-modal-form-control textarea {
resize: none;
}

.edit-pattern-modal-form-control input[type="submit"] {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 18px 72px;
gap: 10px;
background: #226cb0;
color: white;
border-radius: 9px;
cursor: pointer;
}

/* CUSTOM-FILTER-DRAWER */
.custom-drawer {
display: flex;
height: 100% !important;
flex-direction: column;
justify-content: space-between;
}

.custom-filter-drawer-header-container {
display: flex;
justify-content: space-between;

+ 0
- 74
src/assets/styles/components/_schedulePage.scss Прегледај датотеку

@@ -1,74 +0,0 @@
.schedule-page-container {
display: flex;
flex-direction: column;
padding-bottom: 46px;
overflow-x: auto;
}

.schedule-page-main-header {
font-family: Source Sans Pro;
font-size: 36px;
font-weight: 600;
line-height: 36px;
letter-spacing: 0.02em;
text-align: left;
}

.schedule-page-header {
font-family: Source Sans Pro;
font-size: 24px;
font-weight: 600;
line-height: 32px;
letter-spacing: 0.02em;
text-align: left;
color: #226cb0;
align-self: flex-end;
margin-left: 5px;
}

.schedule-page-arrows-container {
display: flex;
gap: 18px;
margin-top: 32px;
}

.schedule-page-arrow-container {
display: flex;
height: 45px;
width: 45px;
border-radius: 9px;
border: 1px solid #e4e4e4;
justify-content: center;
align-items: center;
cursor: pointer;
}

.schedule-page-content-container {
display: grid;
grid-template-columns: auto auto auto auto auto auto auto;
padding-top: 18px;
padding-right: 59px;
padding-bottom: 10px;
grid-gap: 0px;
width: 1305px;
}

@media only screen and (max-width: 361px) {
.schedule-page-main-header {
font-size: 18px;
}

.schedule-page-header{
font-size: 14px;
}

.schedule-page-arrow-container{
width: 36px;
height: 36px;
}

.schedule-page-content-container{
padding-right: 36px;
width: 1250px;
}
}

+ 4
- 580
src/assets/styles/components/_selectionProcessPage.scss Прегледај датотеку

@@ -1,58 +1,3 @@
.selections {
padding-left: 153px;

@include media-below($bp-xl) {
padding-left: 36px !important;
}
}

.level-header {
padding-top: 72px;
padding-bottom: 39px;
display: flex;
justify-content: space-between;
align-items: center;
// background-color: red;

@include media-below($bp-xl) {
padding: 20px 0.75rem 39px 0.75em !important;
}
.userPageBtn {
margin-right: 72px;
@include media-below($bp-xl) {
margin-right: 10px;
}
}
}

.selection-levels {
overflow-x: scroll;
padding-bottom: 100px;
}

.fixed-right {
text-align: right;
margin-left: auto;
position: absolute;
right: 100px;
top: 123px;
}

.level-header-subheader {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 24px;
line-height: 36px;
padding-left: 0.3rem;
color: #226cb0;
letter-spacing: 0.02em;

@include media-below($bp-xl) {
font-size: 14px;
}
}

h1,
h3 {
@include media-below($bp-xl) {
@@ -60,536 +5,15 @@ h3 {
}
}

.level-header-spliter {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 24px;
line-height: 36px;
padding-left: 0.3rem;
color: #272727;
letter-spacing: 0.02em;

@include media-below($bp-xl) {
font-size: 18px;
}
}

.selection-levels-processes {
display: flex;
position: relative;
min-height: 53vh;
}

.selection-levels-processes-process {
display: flex;

@include media-below($bp-xl) {
padding-left: 0;
}
}

.selection-card {
display: flex;
flex-direction: column;
justify-content: start;
align-items: left;
height: fit-content;
padding: 36px;
background: #f4f4f4;
border: 1px solid #e4e4e4;
border-radius: 18px;
gap: 18px;
margin-right: 36px;
transition: background-color 0.35s ease;
@include media-below($bp-xl) {
margin-right: 20px !important;
padding: 18px !important;
}
&.over{
background-color: $mainBlueLight !important;
transition: background-color 0.35s ease;
}
}

.sel-item:hover {
transition: .25s;
scale: 1.05;
border-color:$mainBlue;
.sel-item-inner{
background-color: $mainBlueLight !important;
transition: .25s;
.status button{
transition: .25s;
&.unsucc{
background: #FFEAEE !important;
color: #D72228 !important;
}
background-color: $mainBlueLight !important;
}}

}

.bg-danger {
background-color: #272727;
}

.grey {
color: #e4e4e4;
}

.selection-item {
display: flex;
flex-direction: row;
justify-content: left;
vertical-align: top;
align-items: left;
width: 400px;
padding: 18px 36px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 18px;
gap: 18px;
margin-right: 36px;
}

.selection-item-date p {
text-align: right;
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 15px;
color: #272727;
flex: none;
order: 4;
flex-grow: 0;
}

.selection-card-title {
display: flex;
justify-content: space-between;
img {
width: 12px;
}
.c-btn {
background-color: white !important;
}
h3 {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 32px;
line-height: 32px;
letter-spacing: 0.02em;
color: #272727;
flex: none;
order: 0;
flex-grow: 0;

@include media-below($bp-xl) {
font-size: 18px;
}
}
}

.selection-item-name,
.selection-item-date {
margin: auto 0 !important;
}

.selection-item-name p {
height: 20px;
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 16px;
line-height: 20px;
text-align: right;
color: #226cb0;
flex: none;
order: 2;
flex-grow: 0;
}

.selection-item-buttons button {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: center;
font-size: 16px;
align-items: center;
padding: 9px;
gap: 10px;
min-width: 76px;
height: 38px;
border: 1px solid #e4e4e4;
border-radius: 9px;
flex: none;
order: 0;
flex-grow: 0;
}

.sel-item{
border-radius: 18px;
border: 1px solid #e4e4e4;
overflow: hidden;
transition: .25s;
}
.sel-item-scheduler{
display: flex;
justify-content: space-between;
background: #E4E4E4;

p{
padding: 12.5px 25px 12.5px 0px !important;
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
font-size: 16px;
line-height: 20px;
text-align: right;
color: #272727;
}
}
.sel-item-inner {
transition: .25s;
display: flex;
flex-direction: row;
align-items: center;
padding: 18px 36px;
gap: 18px;
cursor: pointer;
width: 458px;
background: #ffffff;
// transition: 0.3s;
@include media-below($bp-xl) {
justify-content: space-between;
padding: 18px;
width: 303px;
}
.status button{ transition: .25s;}
}

.sel-item-inner-no-data {
display: flex;
flex-direction: row;
align-items: center;
padding: 18px 36px;
gap: 18px;
width: 458px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 18px;
transition: 0.3s;
@include media-below($bp-xl) {
justify-content: space-between;
padding: 18px;
width: 303px;
}
}

.sel-item-inner .status {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
color: #272727;
flex: none;
order: 0;
flex-grow: 0;
@include media-below($bp-xl) {
font-size: 14px;
order: 1;
}
}

.sel-item-inner .date-name {
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 18px;
cursor: pointer;
width: 100%;
order: 1;
@include media-below($bp-xl) {
flex-direction: column;
font-size: 14px;
order: 0;
flex-grow: 0;
gap: 4px;
}
}

.sel-item-inner .date-name .date {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
color: #272727;
flex: none;
font-size: 14px !important;
order: 0;
}

.full-name {
height: 20px;
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 16px;
text-align: right;
color: #226cb0;
flex: 3 0 auto;
order: 1;
@include media-below($bp-xl) {
text-align: left !important;
font-size: 14px !important;
}
}

.full-name p,
.sel-item-inner .date-name .date p {
@include media-below($bp-xl) {
font-size: 14px !important;
}
}

.sel-item-inner .status button {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: center;
font-size: 16px;
align-items: center;
padding: 9px;
gap: 10px;
min-width: 76px;
height: 38px;
border: 1px solid #e4e4e4;
background: white;
border-radius: 9px;
flex: none;
order: 0;
flex-grow: 0;
@include media-below($bp-xl) {
font-size: 14px !important;
}
}

.active-process {
scale: 1.05;
border-color: $mainBlue !important;
background-color: $mainBlueLight !important;
}

.active-process-card {
display: flex;
flex-direction: column;
justify-content: start;
align-items: flex-start;
padding: 18px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 18px;
gap: 18px;
margin-right: 36px;
@include media-below($bp-xl) {
margin-right: 20px !important;
padding: 36px !important;
}
}

.active-process-tip {
display: flex;
flex-direction: column;
justify-content: start;
align-items: flex-start;
padding: 91.5px;
width: 800px;
background: #ffffff;
gap: 18px;
margin-right: 36px;
@include media-below($bp-xl) {
margin-right: 20px !important;
padding: 36px !important;
width: 100%;
}
}

.active-process-tip h3 {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 16px;
color: #272727;
}

.active-process-tip p {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
color: #272727;
}

.active-process-card-header {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
align-items: center;
padding: 0px;
gap: 18px;
flex-wrap: wrap;
}

.active-process-card-body {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
align-items: center;
padding: 34px;
gap: 18px;

@include media-below($bp-xl) {
margin-right: 20px !important;
padding: 36px !important;
}
}

.active-process-card-date p {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
color: #272727;
flex: none;
order: 0;
flex-grow: 0;
z-index: 0;
}

.active-process-card-number p {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
font-size: 16px;
line-height: 20px;
text-align: left;
background: conic-gradient(
from 73.66deg at 50% 50%,
#226cb0 0deg,
#ba6fb9 106.88deg,
#5e9fdb 228.75deg,
#226cb0 360deg
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
flex: none;
order: 6;
flex-grow: 0;
z-index: 6;
}
.change-interbtn{
display: flex;
align-items: center;
justify-content: center;
padding-left: 35px;
}
.interbtn{
height: 27.5px;
background-color: $mainBlue !important;
color: white !important;
font-size: 12px !important;
border-radius: 7.5px !important;
font-weight: 500 !important;
text-transform: capitalize !important;
}
.unsucc{
background: #FFEAEE !important;
color: #D72228 !important;
}

.active-process-card-buttons {
overflow: hidden;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: center;
padding: 0px;
gap: 18px;
flex: none;
order: 4;
flex-grow: 0;

@include media-below($bp-xl) {
gap: 9px !important;
}
}

.active-process-card-buttons button {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 9px;
gap: 10px;
height: 38px;
background: transparent;
border: 1px solid #e4e4e4;
border-radius: 9px;
flex: none;
order: 0;
flex-grow: 0;
}

.active-process-card-link {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 12px;
text-decoration-line: underline;
color: #226cb0;
flex: none;
order: 4;
flex-grow: 0;
z-index: 4;
}

.active-process-card-logo img {
flex: none;
order: 0;
flex-grow: 0;
}

.active-process-card-logo {
margin-left: auto;
flex: none;
order: 1;
flex-grow: 0;
}
.modal-content.interviewDialog{
display: flex;
gap: 15px;
flex-direction: column;
margin: 20px 0px 25px 0px;
}
.interview-btn{
.interview-btn {
width: 220px !important;
}
.css-11u53oe-MuiSelect-select-MuiInputBase-input-MuiOutlinedInput-input.MuiSelect-select{

.css-11u53oe-MuiSelect-select-MuiInputBase-input-MuiOutlinedInput-input.MuiSelect-select {
text-align: left;
padding-left: 20px;
}
}

+ 0
- 238
src/assets/styles/components/_statistics.scss Прегледај датотеку

@@ -1,238 +0,0 @@
.px36-heading {
padding-top: 36px !important;
padding-left: 144px;
line-height: 32px;

@include media-below($bp-xl) {
padding-left: 36px !important;
}
}
.section-header {
padding-bottom: 27px !important;
}
.stats-section:not(:last-of-type) {
padding-left: 144px;
padding-right: 72px;
margin-top: 83px;

@include media-below($bp-xl) {
padding: 20px 36px !important;
}

h3 {
font-size: 24px;
}

.stats-items {
display: flex;
justify-content: flex-start;
border: 1px solid #e4e4e4;
border-radius: 18px;
width: 920px;
overflow: hidden;

.stats-item {
width: 230px;
position: relative;

&:not(:last-of-type):after {
content: "";
position: absolute;
right: 0;
bottom: 0;
height: 60%;
width: 1.75px;
}

&:first-of-type:after {
background-color: #bfdbf5;
}
&:nth-of-type(2):after {
background-color: #1e92d0;
}
&:nth-of-type(3):after {
background-color: $mainBlue;
}

.stats-item-content {
padding: 36px;
background-color: white;

p {
margin-top: 9px;
font-size: 16px;
color: $mainBlue;
font-weight: 600;
}
}

.bottom-static {
height: 35px;
width: 100%;
}

&:first-of-type {
.bottom-static {
background-color: $mainBlueLight !important;
}
}
&:nth-of-type(2) {
.bottom-static {
background-color: #bfdbf5 !important;
}
}
&:nth-of-type(3) {
.bottom-static {
background-color: #1e92d0 !important;
}
}
&:last-of-type {
.bottom-static {
background-color: $mainBlue !important;
}
}
}
}

.stats-items-dynamic {
display: flex;
justify-content: flex-start;
// margin-top: 27px;
border: 1px solid #e4e4e4;
border-radius: 18px;
width: 1160px;
overflow: hidden;

.stats-item {
width: 290px;
position: relative;

&:not(:last-of-type):after {
content: "";
position: absolute;
right: 0;
bottom: 0;
height: 60%;
width: 1.75px;
}

&:first-of-type:after {
background-color: #bfdbf5;
}
&:nth-of-type(2):after {
background-color: #1e92d0;
}
&:nth-of-type(3):after {
background-color: $mainBlue;
}

.stats-item-content {
padding: 36px;
background-color: white;

p {
margin-top: 9px;
font-size: 16px;
color: $mainBlue;
font-weight: 600;
}
}

.bottom-dynamic {
height: 35px;
width: 100%;
}

.bottom-dynamic {
display: flex;
background-color: $white;
border-top: 1px solid #e4e4e4;
.bottom-loader-indicator {
// width: 85%;
border-bottom-right-radius: 18px;
}
}

&:first-of-type {
.bottom-dynamic {
.bottom-loader-indicator {
background-color: $mainBlueLight !important;
}
}
}
&:nth-of-type(2) {
.bottom-dynamic {
.bottom-loader-indicator {
background-color: #bfdbf5 !important;
}
}
}
&:nth-of-type(3) {
.bottom-dynamic {
.bottom-loader-indicator {
background-color: #1e92d0 !important;
}
}
}
&:last-of-type {
.bottom-dynamic {
.bottom-loader-indicator {
background-color: $mainBlue !important;
}
}
}
}
}
}

.stats-section:first-of-type {
margin-top: 35px !important;
}
.stats-section {
@include media-below($bp-xl) {
margin-top: 20px !important;
}
}
.stats-section:not(:last-of-type) {
@include media-below($bp-xl) {
overflow-x: auto;
}
}
.stats-section:last-of-type {
padding-bottom: 0px !important;
margin-top: 83px;
}
.stats-section:last-of-type .section-header {
padding: 0px !important;
margin-bottom: 0px;
padding-left: 144px;
@include media-below($bp-xl) {
padding: 20px 36px !important;
}
}
.ad-count {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 9px;
gap: 10px;

width: 113px;
height: 38px;

/* Gray E4 */

border: 1px solid #e4e4e4;
border-radius: 9px;

/* Inside auto layout */

flex: none;
order: 0;
flex-grow: 0;
}
.stat-ads {
margin-bottom: 0 !important;
padding-bottom: 0 !important;
}

+ 0
- 81
src/components/Ads/Ad.js Прегледај датотеку

@@ -1,81 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
// import logoReact from "../../assets/images/logo_react.png";
import { useTheme } from "@emotion/react";
import { useMediaQuery } from "@mui/material";
import linkedin from "../../assets/images/linkedin.png";
import facebook from "../../assets/images/facebook.png";
import instagram from "../../assets/images/instagram.png";
import { selectLogo } from "../../util/helpers/technologiesLogos";
import { useTranslation } from "react-i18next";

const Ad = ({
title,
minimumExperience,
createdAt,
expiredAt,
onShowAdDetails,
className,
}) => {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("sm"));
const { t } = useTranslation();

return (
<div className={`ad-card ${className}`} onClick={onShowAdDetails}>
<div className="ad-card-date">
<p>
{new Date(createdAt).toLocaleDateString()} -{" "}
{new Date(expiredAt).toLocaleDateString()}
</p>
</div>

<div className="ad-card-title">
<h3>{title}</h3>
</div>

<div className="ad-card-logo">
<img src={selectLogo(title)} alt="logo-react" />
</div>

<div className="ad-card-experience">
<p>
{minimumExperience}+ {t("common.experience")}
</p>
</div>

{!matches && (
<div className="ad-card-buttons">
<button>LinkedIn</button>
<button>Facebook</button>
<button disabled>Instagram</button>
</div>
)}
{matches && (
<div className="ad-card-buttons">
<button className="ad-card-buttons-button">
<img src={linkedin} />
</button>
<button className="ad-card-buttons-button">
<img src={facebook} />
</button>
<button disabled className="ad-card-buttons-button">
<img src={instagram} />
</button>
</div>
)}
</div>
);
};

Ad.propTypes = {
id: PropTypes.number,
title: PropTypes.string,
minimumExperience: PropTypes.number,
createdAt: PropTypes.any,
expiredAt: PropTypes.any,
onShowAdDetails: PropTypes.func,
className: PropTypes.any,
};

export default Ad;

+ 0
- 65
src/components/Ads/AdDetailsCandidateCard.js Прегледај датотеку

@@ -1,65 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { CANDIDATES_DETAILS_PAGE } from "../../constants/pages";
import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";

const AdDetailsCandidateCard = ({
className,
id,
firstName,
lastName,
experience,
cv,
}) => {
const history = useHistory();
const { t } = useTranslation();
return (
<div
data-testid="ad-details-candidate"
className={`ad-details-candidate ${className}`}
>
<div className="ad-details-candidate-date">
<p>{new Date().toLocaleDateString()}</p>
</div>
<div className="ad-details-candidate-title">
<h3
data-testid="ad-details-candidate-title-link"
onClick={() =>
history.push(CANDIDATES_DETAILS_PAGE.replace(":id", id))
}
>
{firstName} {lastName}
</h3>
</div>
<div className="ad-details-candidate-experience">
{experience > 0 ? (
<p>
{experience}+ {t("common.experience")}
</p>
) : (
<p>{t("common.noExperience")}</p>
)}
</div>
<div className="ad-details-candidate-buttons">
<button>React</button>
<button>.NET</button>
<button>Angular</button>
</div>
<div className="ad-details-candidate-cv">
<a href="#">{cv}</a>
</div>
</div>
);
};

AdDetailsCandidateCard.propTypes = {
className: PropTypes.any,
id: PropTypes.number,
firstName: PropTypes.string,
lastName: PropTypes.string,
experience: PropTypes.number,
cv: PropTypes.string,
};

export default AdDetailsCandidateCard;

+ 0
- 215
src/components/Ads/AdFilters.js Прегледај датотеку

@@ -1,215 +0,0 @@
import React, { useState } from "react";
import PropType from "prop-types";
import Box from "@mui/material/Box";
import Drawer from "@mui/material/Drawer";
import FormGroup from "@mui/material/FormGroup";
import FormControlLabel from "@mui/material/FormControlLabel";
import Checkbox from "@mui/material/Checkbox";
import Slider from "@mui/material/Slider";
import filterIcon from "../../assets/images/filter_vector.png";
import x from "../../assets/images/x.png";
import { changeIsCheckedValue } from "../../store/actions/technologies/technologiesActions";
import { useDispatch } from "react-redux";
import { setFilteredAdsReq } from "../../store/actions/ads/adsAction";
import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";

const AdFilters = ({ open, handleClose, technologies }) => {
const [sliderValue, setSliderValue] = useState([0, 10]);
const [employmentType, setEmploymentType] = useState("Work");
const [workHour, setWorkHour] = useState("FullTime");
const dispatch = useDispatch();
const history = useHistory();
const { t } = useTranslation();

const handleSliderChange = (_, newValue) => {
setSliderValue(newValue);
};

const onSubmitFilters = () => {
const tech = technologies
.filter((tech) => tech.isChecked === true)
.map((tech) => tech.name);

dispatch(
setFilteredAdsReq({
minExperience: sliderValue[0],
maxExperience: sliderValue[1],
technologies: tech,
workHour,
employmentType,
})
);

let technologiesQuery = "";
for (let i = 0; i < tech.length; i++) {
technologiesQuery += `technologies=${tech[i]}&`;
}

history.push({
pathname: "/ads",
search: `?minExperience=${sliderValue[0]}&maxExperience=${sliderValue[1]}&workHour=${workHour}&employmentType=${employmentType}&${technologiesQuery}`,
});

handleClose();
};

const handleCheckboxes = (e) => {
const { value } = e.target;

dispatch(changeIsCheckedValue(value));
};

const employmentTypeHandler = (type) => {
setEmploymentType(type);
};

const workHourHandler = (type) => {
setWorkHour(type);
};

const list = () => (
<Box
sx={{
width: 360,
height: "100%",
borderRadius: "18px 0 0 18px",
padding: "36px",
}}
role="presentation"
// onClick={handleClose}
onKeyDown={handleClose}
>
<div data-testid="ad-filters-drawer">
<div className="ad-filters-header-container">
<div className="ad-filters-header">
<img src={filterIcon} alt="filter_icon" />
<h3>{t("filters.filters")}</h3>
<p>
<sub>| {t("ads.ads")}</sub>
</p>
</div>
<div className="ad-filters-header-close" onClick={handleClose}>
<img src={x} alt="x" />
</div>
</div>
<div className="ad-filters-experience">
<div className="ad-filters-sub-title">
<p>{t("filters.experience")}</p>
</div>
<div className="ad-filters-experience-slider">
<Slider
getAriaLabel={() => "Temperature range"}
value={sliderValue}
onChange={handleSliderChange}
valueLabelDisplay="auto"
max={20}
/>
</div>
</div>
<div className="ad-filters-technologies">
<div className="ad-filters-sub-title">
<p>{t("filters.tecnologies")}</p>
</div>
<div className="ad-filters-technologies-checkboxes">
<FormGroup>
{technologies?.map((technology, index) => (
<FormControlLabel
key={index}
control={
<Checkbox
onChange={handleCheckboxes}
value={technology.name}
checked={technology.isChecked}
className="ad-filters-checkbox"
/>
}
label={technology.name}
/>
))}
</FormGroup>
</div>
</div>
<div className="ad-filters-technologies">
<div className="ad-filters-sub-title">
<p>{t("filters.employmentType")}</p>
</div>
<div className="ad-filters-employment-type">
<button
className={`c-btn ${
employmentType === "Intership"
? "c-btn c-btn--primary"
: "c-btn--primary-outlined"
}`}
onClick={employmentTypeHandler.bind(this, "Intership")}
>
{t("filters.internship")}
</button>
<button
className={`c-btn ${
employmentType === "Work"
? "c-btn c-btn--primary"
: "c-btn--primary-outlined"
}`}
onClick={employmentTypeHandler.bind(this, "Work")}
>
{t("filters.work")}
</button>
</div>
</div>
<div className="ad-filters-technologies">
<div className="ad-filters-sub-title">
<p>{t("filters.workHour")}</p>
</div>
<div className="ad-filters-employment-type">
<button
className={`c-btn ${
workHour === "PartTime"
? "c-btn c-btn--primary"
: "c-btn--primary-outlined"
}`}
onClick={workHourHandler.bind(this, "PartTime")}
>
{t("filters.partTime")}
</button>
<button
className={`c-btn ${
workHour === "FullTime"
? "c-btn c-btn--primary"
: "c-btn--primary-outlined"
}`}
onClick={workHourHandler.bind(this, "FullTime")}
>
{t("filters.fullTime")}
</button>
</div>
</div>
<div className="ad-filters-search">
<button
onClick={onSubmitFilters}
className="c-btn c-btn--primary"
data-testid="ad-filters-submit"
>
{t("filters.search")}
</button>
</div>
</div>
</Box>
);

return (
<div>
<Drawer anchor="right" open={open} onClose={handleClose}>
{list()}
</Drawer>
</div>
);
};

AdFilters.propTypes = {
open: PropType.any,
handleClose: PropType.func,
technologies: PropType.any,
};

export default AdFilters;

+ 0
- 176
src/components/Ads/ApplyForAd.js Прегледај датотеку

@@ -1,176 +0,0 @@
import React, { useState } from "react";
import ApplyForAdFirstStage from "./ApplyForAdFirstStage";
import PropTypes from "prop-types";
import briefcaseIcon from "../../assets/images/briefcase1.png";
import xIcon from "../../assets/images/x.png";
import CustomModal from "../UI/CustomModal";
import ApplyForAdSecondStage from "./ApplyForAdSecondStage";
import { setTechnologiesReq } from "../../store/actions/technologies/technologiesActions";
import { selectTechnologies } from "../../store/selectors/technologiesSelectors";
import { useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";
import ApplyForAdThirdStage from "./ApplyForAdThirdStage";
import ApplyForAdFourthStage from "./ApplyForAdFourthStage";
import { applyForAdReq } from "../../store/actions/applyForAd/applyForAdActions";
import { useHistory } from "react-router-dom";
import { ADS_PAGE } from "../../constants/pages";
import { useTranslation } from "react-i18next";

const ApplyForAd = ({ open, title, adId, onCloseModal }) => {
const [stage, setStage] = useState(1);
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [gender, setGender] = useState("");
const [dateOfBirth, setDateOfBirth] = useState("");
const [phoneNumber, setPhoneNumber] = useState("");
const [mappedTechnologies, setMappedTechnologies] = useState([]);
const [experience, setExperience] = useState(0);
const [professionalQualification, setProfessionalQualification] =
useState("");
const [linkedinLink, setLinkedinLink] = useState("");
const [githubLink, setGithubLink] = useState("");
const [email, setEmail] = useState("");
const [bitBucketLink, setBitBucketLink] = useState("");
const [coverLetter, setCoverLetter] = useState("");
const [pdfFile, setPdfFile] = useState(null);

const technologies = useSelector(selectTechnologies);
const dispatch = useDispatch();
const history = useHistory();
const { t } = useTranslation();

useEffect(() => {
dispatch(setTechnologiesReq());
}, []);

useEffect(() => {
if (technologies && technologies.length > 0) {
const tech = technologies.map((t) => ({ ...t, isChecked: false }));
setMappedTechnologies(tech);
}
}, [technologies]);

const increaseStageHandler = () => {
setStage((oldValue) => oldValue + 1);
};

const decreaseStageHandler = () => {
setStage((oldValue) => oldValue - 1);
};

const handleApiResponseSuccess = () => {
onCloseModal();
history.push(ADS_PAGE);
};

const finishedFourStagesHandler = () => {
const technologiesIds = mappedTechnologies
.filter((tech) => tech.isChecked === true)
.map((x) => x.technologyId);

dispatch(
applyForAdReq({
adId,
firstName,
lastName,
gender,
dateOfBirth,
phoneNumber,
technologiesIds,
experience,
professionalQualification,
linkedinLink,
githubLink,
bitBucketLink,
email,
coverLetter,
pdfFile,
handleApiResponseSuccess,
})
);
};

return (
<CustomModal open={open} onCloseModal={onCloseModal} classes="apply-for-ad">
<div className="apply-for-ad-header">
<div className="apply-for-ad-header-left">
<div className="apply-for-ad-header-left-image">
<img src={briefcaseIcon} alt="plus" />
</div>
<div className="apply-for-ad-header-left-image-title">
<p>{t("ads.signUp")}</p>
</div>
<div className="apply-for-ad-header-left-image-title-sub">
<sub> | {title}</sub>
</div>
</div>
<div className="apply-for-ad-header-right">
<button onClick={onCloseModal}>
<img src={xIcon} alt="x" />
</button>
</div>
</div>
{stage === 1 && (
<ApplyForAdFirstStage
onCloseModal={onCloseModal}
firstName={firstName}
setFirstName={setFirstName}
lastName={lastName}
setLastName={setLastName}
gender={gender}
setGender={setGender}
dateOfBirth={dateOfBirth}
setDateOfBirth={setDateOfBirth}
phoneNumber={phoneNumber}
setPhoneNumber={setPhoneNumber}
onIncreaseStage={increaseStageHandler}
/>
)}
{stage === 2 && (
<ApplyForAdSecondStage
technologies={mappedTechnologies}
setTechnologies={setMappedTechnologies}
experience={experience}
professionalQualification={professionalQualification}
setProfessionalQualification={setProfessionalQualification}
setExperience={setExperience}
onIncreaseStage={increaseStageHandler}
onDecreaseStage={decreaseStageHandler}
/>
)}
{stage === 3 && (
<ApplyForAdThirdStage
linkedinLink={linkedinLink}
setLinkedinLink={setLinkedinLink}
githubLink={githubLink}
setGithubLink={setGithubLink}
bitBucketLink={bitBucketLink}
setBitBucketLink={setBitBucketLink}
email={email}
setEmail={setEmail}
onIncreaseStage={increaseStageHandler}
onDecreaseStage={decreaseStageHandler}
/>
)}
{stage === 4 && (
<ApplyForAdFourthStage
pdfFile={pdfFile}
coverLetter={coverLetter}
setCoverLetter={setCoverLetter}
setPdfFile={setPdfFile}
onDecreaseStage={decreaseStageHandler}
onFinishedFourStages={finishedFourStagesHandler}
/>
)}
</CustomModal>
);
};

ApplyForAd.propTypes = {
open: PropTypes.bool,
title: PropTypes.string,
adId: PropTypes.string,
onCloseModal: PropTypes.func,
};

export default ApplyForAd;

+ 0
- 124
src/components/Ads/ApplyForAdFirstStage.js Прегледај датотеку

@@ -1,124 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";

const ApplyForAdFirstStage = ({
firstName,
setFirstName,
lastName,
gender,
setGender,
setLastName,
dateOfBirth,
setDateOfBirth,
phoneNumber,
setPhoneNumber,
onIncreaseStage,
}) => {
const disabled =
firstName.length === 0 ||
lastName.length === 0 ||
dateOfBirth === "" ||
phoneNumber.length === 0 ||
gender.length === 0;

const { t } = useTranslation();

return (
<div>
<div className="apply-for-ad-modal-form-control">
<label>{t("common.name")}</label>
<input
type="text"
placeholder="ex. Petar"
value={firstName}
data-testid="apply-for-ad-modal-first-name-input"
onChange={(e) => setFirstName(e.target.value)}
/>
</div>
<div className="apply-for-ad-modal-form-control">
<label>{t("common.lastName")}</label>
<input
type="text"
placeholder="ex. Petrovic"
value={lastName}
data-testid="apply-for-ad-modal-last-name-input"
onChange={(e) => setLastName(e.target.value)}
/>
</div>
<div className="apply-for-ad-modal-form-control">
<label>{t("common.gender")}</label>
<div style={{ display: "flex" }}>
<div style={{ display: "flex" }}>
<input
type="radio"
name="gender"
value="Muski"
className="apply-for-ad-modal-gender-input"
checked={gender === "Muski"}
onChange={(e) => setGender(e.target.value)}
/>
<p style={{ marginLeft: "5px" }}>{t("common.male")}</p>
</div>
<div style={{ display: "flex", marginLeft: "50px" }}>
<input
type="radio"
name="gender"
value="Zenski"
className="apply-for-ad-modal-gender-input"
checked={gender === "Zenski"}
onChange={(e) => setGender(e.target.value)}
/>
<p style={{ marginLeft: "5px" }}>{t("common.female")}</p>
</div>
</div>
</div>
<div className="apply-for-ad-modal-form-control">
<label>{t("common.dateOfBirth")}</label>
<input
type="date"
placeholder="ex. Datum rodjenja"
data-testid="apply-for-ad-modal-date-of-birth"
value={dateOfBirth}
onChange={(e) => setDateOfBirth(e.target.value)}
/>
</div>
<div className="apply-for-ad-modal-form-control">
<label>{t("common.phoneNumber")}</label>
<input
type="text"
placeholder="ex. +381/60/000/0000"
data-testid="apply-for-ad-modal-phone-number"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
/>
</div>
<div className="apply-for-ad-buttons">
<button disabled>{t("common.back")}</button>
<button
disabled={disabled}
data-testid="apply-for-ad-modal-go-forward-button"
onClick={() => onIncreaseStage()}
>
{t("common.continue")}
</button>
</div>
</div>
);
};

ApplyForAdFirstStage.propTypes = {
firstName: PropTypes.string,
setFirstName: PropTypes.any,
lastName: PropTypes.string,
gender: PropTypes.string,
setGender: PropTypes.func,
setLastName: PropTypes.any,
dateOfBirth: PropTypes.any,
setDateOfBirth: PropTypes.any,
phoneNumber: PropTypes.string,
setPhoneNumber: PropTypes.any,
onIncreaseStage: PropTypes.func,
};

export default ApplyForAdFirstStage;

+ 0
- 119
src/components/Ads/ApplyForAdFourthStage.js Прегледај датотеку

@@ -1,119 +0,0 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import uploadIcon from "../../assets/images/upload.png";
import { useTranslation } from "react-i18next";

const ApplyForAdFourthStage = ({
coverLetter,
setCoverLetter,
pdfFile,
setPdfFile,
onDecreaseStage,
onFinishedFourStages,
}) => {
const [dropzoneActive, setDropzoneActive] = useState(false);
const {t} = useTranslation()

const disabled = pdfFile === null || coverLetter === "";

const handleDrop = (e) => {
e.preventDefault();
setPdfFile(e.dataTransfer.files[0]);
};

return (
<div data-testid="apply-for-ad-modal-fourth-stage">
<div className="apply-for-ad-modal-form-control">
<label>CV</label>
<div
className="uploadCV-input"
onDragOver={(e) => {
setDropzoneActive(true);
e.preventDefault();
}}
onDragLeave={(e) => {
setDropzoneActive(false);
e.preventDefault();
}}
onDrop={(e) => handleDrop(e)}
style={{
backgroundColor: dropzoneActive ? "#F4F4F4" : "#ffffff",
}}
>
<div className="uploadCV-input-sub-container">
<img src={uploadIcon} />
<div className="uploadCV-input-sub-container">
{pdfFile !== null ? (
<p>{pdfFile.name}</p>
) : (
<>
<p>
{t("ads.dragPdf1")} &nbsp;
<label
htmlFor="upload-file"
style={{
cursor: "pointer",
textDecoration: "underline",
color: "#1E92D0",
}}
>
{t("common.search")}
</label>
&nbsp;{t("ads.dragPdf2")}
</p>
<input
type="file"
name="photo"
id="upload-file"
style={{ display: "none", zIndex: -1 }}
value={pdfFile}
data-testid="apply-for-ad-modal-fourth-stage-pdf-input"
onChange={(e) => {
setPdfFile(e.target.files[0]);
}}
/>
</>
)}
</div>
</div>
</div>
</div>
<div className="apply-for-ad-modal-form-control">
<label>{t("ads.coverLetter")}</label>
<textarea
value={coverLetter}
onChange={(e) => setCoverLetter(e.target.value)}
data-testid="apply-for-ad-modal-fourth-stage-cover-letter-input"
placeholder="ex. Kao student Elektronskog fakulteta u Nišu..."
rows={5}
></textarea>
</div>
<div className="apply-for-ad-buttons">
<button
onClick={onDecreaseStage}
data-testid="apply-for-ad-modal-fourth-stage-go-back-button"
>
{t("common.back")}
</button>
<button
disabled={disabled}
data-testid="apply-for-ad-modal-fourth-stage-go-forward-button"
onClick={onFinishedFourStages}
>
{t("ads.signUp")}
</button>
</div>
</div>
);
};

ApplyForAdFourthStage.propTypes = {
coverLetter: PropTypes.string,
setCoverLetter: PropTypes.any,
pdfFile: PropTypes.any,
setPdfFile: PropTypes.any,
onDecreaseStage: PropTypes.func,
onFinishedFourStages: PropTypes.func,
};

export default ApplyForAdFourthStage;

+ 0
- 150
src/components/Ads/ApplyForAdSecondStage.js Прегледај датотеку

@@ -1,150 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { Checkbox, FormControlLabel } from "@mui/material";
import { useTranslation } from "react-i18next";

const ApplyForAdSecondStage = ({
professionalQualification,
setProfessionalQualification,
technologies,
setTechnologies,
experience,
setExperience,
onIncreaseStage,
onDecreaseStage,
}) => {
const {t} = useTranslation()
let disabled = true;
let isTechnologySelected = false;
if (technologies.length > 0) {
isTechnologySelected = technologies.some((x) => x.isChecked === true);
}
if (isTechnologySelected) {
if (professionalQualification.length > 0) {
disabled = false;
}
}

const handleCheckboxes = (technologyId) => {
const tmpTechnologies = technologies.map((tech) =>
tech.technologyId === technologyId
? { ...tech, isChecked: !tech.isChecked }
: tech
);
setTechnologies(tmpTechnologies);
};

return (
<div data-testid="apply-for-ad-second-stage">
<div className="apply-for-ad-header-title">
<p>{t("ads.professionalQualification")}</p>
</div>
<div className="apply-for-ad-modal-form-control">
<input
type="text"
placeholder="ex. Elektrotehnicki fakultet"
data-testid="apply-for-ad-second-stage-professional-qualification"
value={professionalQualification}
onChange={(e) => setProfessionalQualification(e.target.value)}
/>
</div>
<div className="apply-for-ad-header-title">
<p>{t("ads.technologies")}</p>
</div>
<div className="apply-for-ad-header-sub">
<div className="apply-for-ad-header-sub-group">
<label>Back-End</label>
<div className="apply-for-ad-header-sub-group-checkboxes">
{technologies
.filter((x) => x.technologyType === "Backend")
.map((x) => (
<FormControlLabel
key={x.technologyId}
control={
<Checkbox
className="apply-for-ad-second-stage-checkbox"
value={x.name}
checked={x.isChecked}
onChange={handleCheckboxes.bind(this, x.technologyId)}
/>
}
label={x.name}
/>
))}
</div>
</div>
<div className="apply-for-ad-header-sub-group">
<label>Front-End</label>
<div className="apply-for-ad-header-sub-group-checkboxes">
{technologies
.filter((x) => x.technologyType === "Frontend")
.map((x) => (
<FormControlLabel
key={x.technologyId}
control={
<Checkbox
className="apply-for-ad-second-stage-checkbox"
value={x.name}
checked={x.isChecked}
onChange={handleCheckboxes.bind(this, x.technologyId)}
/>
}
label={x.name}
/>
))}
</div>
</div>
<div className="apply-for-ad-header-sub-group">
<label>{t("ads.others")}</label>
<div className="apply-for-ad-header-sub-group-checkboxes">
{technologies
.filter((x) => x.technologyType === "Other")
.map((x) => (
<FormControlLabel
key={x.technologyId}
control={
<Checkbox
className="apply-for-ad-second-stage-checkbox"
value={x.name}
checked={x.isChecked}
onChange={handleCheckboxes.bind(this, x.technologyId)}
/>
}
label={x.name}
/>
))}
</div>
</div>
</div>
<div className="apply-for-ad-modal-form-control">
<label>{t("filters.experience")}</label>
<input
type="number"
placeholder="ex. 3 godine iskustva"
value={experience}
data-testid="apply-for-ad-second-stage-experience-input"
onChange={(e) => setExperience(e.target.value)}
/>
</div>
<div className="apply-for-ad-buttons">
<button onClick={onDecreaseStage}>{t("common.back")}</button>
<button onClick={onIncreaseStage} disabled={disabled}>
{t("common.continue")}
</button>
</div>
</div>
);
};

ApplyForAdSecondStage.propTypes = {
professionalQualification: PropTypes.string,
setProfessionalQualification: PropTypes.func,
technologies: PropTypes.any,
setTechnologies: PropTypes.any,
experience: PropTypes.number,
setExperience: PropTypes.any,
onIncreaseStage: PropTypes.func,
onDecreaseStage: PropTypes.func,
};

export default ApplyForAdSecondStage;

+ 0
- 101
src/components/Ads/ApplyForAdThirdStage.js Прегледај датотеку

@@ -1,101 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { useTranslation } from "react-i18next";

const ApplyForAdThirdStage = ({
onIncreaseStage,
onDecreaseStage,
linkedinLink,
setLinkedinLink,
githubLink,
setGithubLink,
bitBucketLink,
setBitBucketLink,
email,
setEmail,
}) => {
const disabled =
linkedinLink.length === 0 ||
githubLink.length === 0 ||
bitBucketLink.length === 0;

const {t} = useTranslation()

return (
<div>
<div className="apply-for-ad-header-title">
<p>{t("common.socialNetwork")}</p>
</div>
<div className="apply-for-ad-modal-form-control">
<label>LinkedIn</label>
<input
type="text"
value={linkedinLink}
data-testid="apply-for-ad-modal-third-stage-linkedin-input"
onChange={(e) => setLinkedinLink(e.target.value)}
placeholder="ex. https://www.linkedin.com/in/petar-petrovic"
/>
</div>
<div className="apply-for-ad-modal-form-control">
<label>GitHub</label>
<input
type="text"
value={githubLink}
data-testid="apply-for-ad-modal-third-stage-github-input"
onChange={(e) => setGithubLink(e.target.value)}
placeholder="ex. https://www.github.com/petarpetrovic"
/>
</div>
<div className="apply-for-ad-modal-form-control">
<label>BitBucket</label>
<input
type="text"
value={bitBucketLink}
data-testid="apply-for-ad-modal-third-stage-bitbucket-input"
onChange={(e) => setBitBucketLink(e.target.value)}
placeholder="ex. https://developer.atlassian.com/user/petarapetrovic"
/>
</div>
<div className="apply-for-ad-modal-form-control">
<label>Email</label>
<input
type="email"
value={email}
data-testid="apply-for-ad-modal-third-stage-email-input"
onChange={(e) => setEmail(e.target.value)}
placeholder="ex. petar.petrovic@dilig.net"
/>
</div>
<div className="apply-for-ad-buttons">
<button
onClick={onDecreaseStage}
data-testid="apply-for-ad-modal-third-stage-go-back-button"
>
{t("common.back")}
</button>
<button
onClick={onIncreaseStage}
disabled={disabled}
data-testid="apply-for-ad-modal-third-stage-go-forward-button"
>
{t("common.continue")}
</button>
</div>
</div>
);
};

ApplyForAdThirdStage.propTypes = {
linkedinLink: PropTypes.string,
setLinkedinLink: PropTypes.any,
githubLink: PropTypes.string,
setGithubLink: PropTypes.any,
bitBucketLink: PropTypes.string,
setBitBucketLink: PropTypes.any,
email: PropTypes.string,
setEmail: PropTypes.any,
onIncreaseStage: PropTypes.any,
onDecreaseStage: PropTypes.any,
};

export default ApplyForAdThirdStage;

+ 0
- 52
src/components/Ads/ArchiveAd.js Прегледај датотеку

@@ -1,52 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { selectLogo } from "../../util/helpers/technologiesLogos";
import { useTranslation } from "react-i18next";

const ArchiveAd = ({
className,
title,
minimumExperience,
createdAt,
expiredAt,
onShowAdDetails,
}) => {
const { t } = useTranslation();
return (
<div className={`archive-ad ${className}`} onClick={onShowAdDetails}>
<div className="archive-ad-date">
<p>
{new Date(createdAt).toLocaleDateString()} -{" "}
{new Date(expiredAt).toLocaleDateString()}
</p>
</div>
<div className="archive-ad-title">
<h3>{title}</h3>
</div>
<div className="archive-ad-image">
<img src={selectLogo(title)} alt=".net icon" />
</div>
<div className="archive-ad-experience">
{minimumExperience > 0 ? (
<p>
{minimumExperience}+ {t("common.experience")}
</p>
) : (
<p>{t("ads.noExperience")}</p>
)}
</div>
</div>
);
};

ArchiveAd.propTypes = {
id: PropTypes.number,
title: PropTypes.string,
minimumExperience: PropTypes.number,
createdAt: PropTypes.any,
expiredAt: PropTypes.any,
onShowAdDetails: PropTypes.func,
className: PropTypes.any,
};

export default ArchiveAd;

+ 0
- 55
src/components/Ads/StatsAd.js Прегледај датотеку

@@ -1,55 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { selectLogo } from "../../util/helpers/technologiesLogos";
import { useTranslation } from "react-i18next";

const StatsAd = ({
className,
count,
title,
minimumExperience,
createdAt,
expiredAt,
onShowAdDetails,
}) => {
const {t} = useTranslation()
return (
<div className={`archive-ad stats-ad ${className}`} onClick={onShowAdDetails}>
<div className="ad-count">
{count} {t("ads.registered")}
</div>
<div className="archive-ad-date">
<p>
{new Date(createdAt).toLocaleDateString()} -{" "}
{new Date(expiredAt).toLocaleDateString()}
</p>
</div>
<div className="archive-ad-title">
<h3>{title}</h3>
</div>
<div className="archive-ad-image">
<img src={selectLogo(title)} alt=".net icon" />
</div>
<div className="archive-ad-experience">
{minimumExperience > 0 ? (
<p>{minimumExperience}+ {t("common.experience")}</p>
) : (
<p>{t("ads.noExperience2")}</p>
)}
</div>
</div>
);
};

StatsAd.propTypes = {
id: PropTypes.number,
count: PropTypes.number,
title: PropTypes.string,
minimumExperience: PropTypes.number,
createdAt: PropTypes.any,
expiredAt: PropTypes.any,
onShowAdDetails: PropTypes.func,
className: PropTypes.any,
};

export default StatsAd;

+ 0
- 33
src/components/Candidates/CandidateAd.js Прегледај датотеку

@@ -1,33 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { formatDate } from "../../util/helpers/dateHelpers";
import dotnetImage from "../../assets/images/.net_icon.png";

const CandidateAd = ({ add, className, onclick }) => {
return add === undefined ? (
<p></p>
) : (
<div
onClick={onclick}
style={{ cursor: "pointer" }}
className={`applicant-add ${className !== undefined ? className : ""}`}
>
<p className="applicant-add-date">{formatDate(add.createdAt)}</p>
<p className="applicant-add-title">{add.title}</p>
<img
src={dotnetImage}
alt="technology-image"
className="applicant-add-technology-image"
/>
<div className="applicant-add-site">dilig.net</div>
</div>
);
};

CandidateAd.propTypes = {
add: PropTypes.object,
className: PropTypes.any,
onclick: PropTypes.func,
};

export default CandidateAd;

+ 0
- 66
src/components/Candidates/CandidateCard.js Прегледај датотеку

@@ -1,66 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { formatDate } from "../../util/helpers/dateHelpers";
import { CANDIDATES_PAGE } from "../../constants/pages";
import { useTranslation } from "react-i18next";

const CandidateCard = ({ candidate, className, history }) => {
const { t } = useTranslation();
const navigateToDetailsPage = () => {
history.push({
pathname: CANDIDATES_PAGE + "/" + candidate.applicantId,
});
};

return candidate == null ? (
<p></p>
) : (
<div
onClick={navigateToDetailsPage}
className={`candidate-card-container ${
className !== undefined ? className : ""
}`}
>
<p className="candidate-card-date">
{formatDate(candidate.dateOfApplication)}
</p>
<p className="candidate-card-applicant-name">
{candidate.firstName} {candidate.lastName}
</p>
<p className="candidate-card-years">
{candidate.experience === 0
? t("common.noExperience")
: candidate.experience + " " + t("common.experience")}
</p>
<div className="candidate-card-tecnologies-container">
{candidate.technologyApplicants.map((technology, index) => (
<p key={index}>{technology.name}</p>
))}
</div>
<div className="candidate-card-techologies">
{candidate.technologyApplicants.map((technology, index) => (
<div key={index} className="applicant-add-site">
{technology.technology.name}
</div>
))}
</div>
<a className="cvLink">
CV {candidate.firstName} {candidate.lastName}.pdf
</a>
</div>
);
};

CandidateCard.propTypes = {
candidate: PropTypes.object,
className: PropTypes.any,
history: PropTypes.shape({
replace: PropTypes.func,
push: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string,
}),
}),
};

export default CandidateCard;

+ 0
- 320
src/components/Candidates/CandidateFilters.js Прегледај датотеку

@@ -1,320 +0,0 @@
import React, { useEffect, useState } from "react";
import PropType from "prop-types";
import Box from "@mui/material/Box";
import Drawer from "@mui/material/Drawer";
import FormGroup from "@mui/material/FormGroup";
import FormControlLabel from "@mui/material/FormControlLabel";
import Checkbox from "@mui/material/Checkbox";
import Slider from "@mui/material/Slider";
import filterIcon from "../../assets/images/filter_vector.png";
import x from "../../assets/images/x.png";
import {
filterCandidates,
fetchAdsCandidates,
} from "../../store/actions/candidates/candidatesActions";
import { useDispatch } from "react-redux";
import { formatDateInput } from "../../util/helpers/dateHelpers";
import {
changeIsCheckedValue,
resetIsCheckedValue,
} from "../../store/actions/technologies/technologiesActions";
import { useTranslation } from "react-i18next";

const CandidateFilters = ({
open,
handleClose,
pageSize,
currentPage,
isTableView,
technologies,
sliderValue,
startingDate,
endingDate,
typesOfEmployments,
setSliderValue,
setStartingDate,
setEndingDate,
setTypesOfEmployments,
}) => {
const dispatch = useDispatch();
const [isInitial, setIsInitial] = useState(true);
const { t } = useTranslation();

useEffect(() => {}, [isTableView]);

const handleSliderChange = (_, newValue) => {
setSliderValue(newValue);
};

const resetFilters = () => {
setTypesOfEmployments([
{ name: "Posao", isChecked: false },
{ name: "Intership", isChecked: false },
]);
setSliderValue([0, 0]);
setEndingDate("");
setStartingDate("");
};

useEffect(() => {
if (isInitial) {
setIsInitial(false);
return;
}
dispatch(resetIsCheckedValue());
resetFilters();
}, [isTableView]);

const updateTypeState = (name, value) => {
const newState = typesOfEmployments.map((obj) => {
if (obj.name === name) {
return { ...obj, isChecked: value };
} else {
return { ...obj, isChecked: false };
}
});
setTypesOfEmployments(newState);
};

const handleTechologiesChange = (e) => {
const { name } = e.target;

dispatch(changeIsCheckedValue(name));
};

const getSelectedEmploymentType = () => {
return typesOfEmployments.filter((e) => e.isChecked === true);
};

const handleChangeTypeOfEmployment = (name) => {
updateTypeState(name, true);
};

const handleApiResponseSuccess = () => {
handleClose();
};

const isThereSelectedFilter = () => {
let tech = technologies.filter((t) => t.isChecked === true);
if (tech && tech.length > 0) {
return true;
}
let k = typesOfEmployments.filter((te) => te.isChecked === true);
if (k && k.length > 0) {
return true;
}

if (sliderValue[0] !== 0 || sliderValue[1] !== 0) {
return true;
}
if (startingDate !== "" || endingDate !== "") {
return true;
}

return false;
};

const fiterItems = () => {
const tech = technologies
.filter((tech) => tech.isChecked === true)
.map((tech) => tech.name);

const selectedEmploymentType = getSelectedEmploymentType();

isTableView
? dispatch(
filterCandidates({
pageSize,
currentPage,
minExperience: sliderValue[0],
maxExperience: sliderValue[1],
employmentType:
selectedEmploymentType && selectedEmploymentType.length === 0
? ""
: selectedEmploymentType[0].name,
minDateOfApplication: startingDate,
maxDateOfApplication: endingDate,
technologies: tech,
handleApiResponseSuccess,
})
)
: dispatch(
fetchAdsCandidates({
pageSize,
currentPage,
minExperience: sliderValue[0],
maxExperience: sliderValue[1],
employmentType:
selectedEmploymentType && selectedEmploymentType.length === 0
? ""
: selectedEmploymentType[0].name,
minDateOfApplication: startingDate,
maxDateOfApplication: endingDate,
technologies: tech,
handleApiResponseSuccess,
})
);
};

const handleChangeStartingDate = (event) => {
setStartingDate(event.target.value);
};

const handleChangeEndingDate = (event) => {
setEndingDate(event.target.value);
};

const getDayBeforeToday = (date = new Date()) => {
const previous = new Date(date.getTime());
previous.setDate(date.getDate() - 1);
return previous;
};

const list = () => (
<Box
sx={{
width: 360,
height: "100%",
borderRadius: "18px 0 0 18px",
padding: "36px",
}}
role="presentation"
onKeyDown={handleClose}
>
<div>
<div className="ad-filters-header-container">
<div className="ad-filters-header">
<img src={filterIcon} alt="filter_icon" />
<h3>{t("filters.filters")}</h3>
<p>
<sub>| {t("candidates.candidates")}</sub>
</p>
</div>
<div className="ad-filters-header-close" onClick={handleClose}>
<img src={x} alt="x" />
</div>
</div>
<div className="ad-filters-experience">
<div className="ad-filters-sub-title">
<p>{t("filters.experience")}</p>
</div>
<div className="ad-filters-experience-slider">
<Slider
getAriaLabel={() => "Temperature range"}
value={sliderValue}
onChange={handleSliderChange}
valueLabelDisplay="auto"
max={20}
/>
</div>
</div>
<div className="ad-filters-technologies">
<div className="ad-filters-sub-title">
<p>{t("filters.technologies")}</p>
</div>
<div className="ad-filters-technologies-checkboxes">
<FormGroup>
{technologies.map((technology, index) => (
<FormControlLabel
key={index}
control={
<Checkbox
checked={technology.isChecked}
name={technology.name}
className="ad-filters-technologies-checkboxes-checkbox"
onChange={handleTechologiesChange}
/>
}
label={technology.name}
/>
))}
</FormGroup>
</div>
</div>
<div className="ad-filters-technologies">
<div className="ad-filters-sub-title">
<p>{t("filters.employmentType")}</p>
</div>
<div className="ad-filters-employment-type">
{typesOfEmployments.map((type, index) => (
<button
key={index}
className={
type.isChecked === false
? "c-btn c-btn--primary-outlined type-of-employment-btn"
: "c-btn c-btn--primary type-of-employment-btn"
}
onClick={() => handleChangeTypeOfEmployment(type.name)}
>
{type.name}
</button>
))}
</div>
</div>
<div className="ad-filters-technologies" style={{ marginTop: "35px" }}>
<div className="ad-filters-sub-title">
<p>{t("filters.dateOfApplication")}</p>
<div className="filter-date-container">
<p>{t("common.from")}</p>
<input
type="date"
id="start"
max={formatDateInput(getDayBeforeToday())}
value={startingDate}
onChange={handleChangeStartingDate}
/>
</div>
<div className="filter-date-container">
<p>{t("common.to")}</p>
<input
type="date"
id="start"
data-testid="filter-date-container-ending-date"
max={formatDateInput(new Date())}
value={endingDate}
onChange={handleChangeEndingDate}
/>
</div>
</div>
</div>
<div className="ad-filters-search" style={{ marginTop: "45px" }}>
<button
className="c-btn c-btn--primary"
onClick={fiterItems}
disabled={!isThereSelectedFilter()}
>
{t("common.search")}
</button>
</div>
</div>
</Box>
);

return (
<div>
<Drawer anchor="right" open={open} onClose={handleClose}>
{list()}
</Drawer>
</div>
);
};

CandidateFilters.propTypes = {
open: PropType.any,
handleClose: PropType.func,
candidates: PropType.array,
currentPage: PropType.number,
pageSize: PropType.number,
isTableView: PropType.bool,
technologies: PropType.array,
sliderValue: PropType.array,
startingDate: PropType.string,
endingDate: PropType.string,
typesOfEmployments: PropType.array,
setSliderValue: PropType.func,
setStartingDate: PropType.func,
setEndingDate: PropType.func,
setTypesOfEmployments: PropType.func,
};

export default CandidateFilters;

+ 0
- 1
src/components/Files/TreeViewFiles.js Прегледај датотеку

@@ -39,7 +39,6 @@ function TreeViewFiles({ onSelectCategory }) {
sx={{
flexGrow: 1,
overflowY: "auto",
zIndex: 100000,
position: "relative",
}}
>

+ 1
- 1
src/components/MUI/NavbarComponent.js Прегледај датотеку

@@ -162,7 +162,7 @@ const NavbarComponent = () => {
<div className="hr" style={{ width: "90%", marginTop: "18px" }}></div>
</div>
<List>
{navItems.map((n) => (
{filterNavItems().map((n) => (
<ListItemButton key={n} onClick={handleToggleDrawer}>
<ListItemIcon>
<ListItemText

+ 0
- 53
src/components/Patterns/PatternCard.js Прегледај датотеку

@@ -1,53 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import editIcon from "../../assets/images/edit.png";
import IconButton from "../IconButton/IconButton";

const PatternCard = ({
createdAt,
title,
selectionProcess,
isShownEdit,
onShowPatternDetails,
onOpenEditModal,
}) => {
return (
<div className="pattern-card-with-icon">
{isShownEdit && (
<div className="pattern-card-edit">
<IconButton
onClick={onOpenEditModal}
className={`c-btn--primary-outlined c-btn pattern-card-edit`}
>
<img
style={{ width: "16px !important", height: "16px !important" }}
src={editIcon}
/>
</IconButton>
</div>
)}
<div className="pattern-card" onClick={onShowPatternDetails}>
<div className="pattern-card-date">
<p>{new Date(createdAt).toLocaleDateString()}</p>
</div>
<div className="pattern-card-title">
<p>{title}</p>
</div>
<div className="pattern-card-selection-proccess">
<p>{selectionProcess}</p>
</div>
</div>
</div>
);
};

PatternCard.propTypes = {
createdAt: PropTypes.any,
title: PropTypes.string,
selectionProcess: PropTypes.string,
isShownEdit: PropTypes.bool,
onShowPatternDetails: PropTypes.func,
onOpenEditModal: PropTypes.func,
};

export default PatternCard;

+ 0
- 149
src/components/Patterns/PatternFilters.js Прегледај датотеку

@@ -1,149 +0,0 @@
import React, { useState } from "react";
import CustomDrawer from "../UI/CustomDrawer";
import { Checkbox, FormControlLabel, FormGroup } from "@mui/material";
import PropTypes from "prop-types";
import { useDispatch } from "react-redux";
import { setFilteredPatternsReq } from "../../store/actions/patterns/patternsActions";
import { useTranslation } from "react-i18next";

const PatternFilters = ({
openFilterDrawer,
handleClose,
selectionLevelFilter,
onChangeFilterCheckboxes,
}) => {
const [fromDate, setFromDate] = useState("");
const [toDate, setToDate] = useState("");
const dispatch = useDispatch();
const { t } = useTranslation();

const submitFiltersHandler = (e) => {
e.preventDefault();

const selectionLevels = selectionLevelFilter
.filter((level) => level.isChecked === true)
.map((level) => level.id);

dispatch(
setFilteredPatternsReq({
fromDate: fromDate === "" ? null : fromDate,
toDate: toDate === "" ? null : toDate,
selectionLevels,
handleApiResponseSuccess,
})
);
};

const handleApiResponseSuccess = () => {
handleClose();
};

const hasSelectedFilters = () => {
const checked = selectionLevelFilter.filter(
(level) => level.isChecked === true
);

if (checked.length > 0) {
return true;
}

if (fromDate !== "") {
return true;
}

if (toDate !== "") {
return true;
}

return false;
};

return (
<CustomDrawer
title="Šabloni"
open={openFilterDrawer}
onCloseDrawer={handleClose}
>
<form onSubmit={submitFiltersHandler}>
<div className="custom-drawer">
<div>
<div className="custom-drawer-sub-card">
<div className="custom-drawer-sub-card-label">
<p>{t("filters.category")}</p>
</div>
<div className="custom-drawer-sub-card-content">
<FormGroup>
{selectionLevelFilter.map((process) => (
<FormControlLabel
key={process.id}
control={
<Checkbox
value={process.name}
checked={process.isChecked}
onChange={() => onChangeFilterCheckboxes(process.id)}
className="pattern-filters-checkbox"
/>
}
label={process.name}
/>
))}
</FormGroup>
</div>
</div>
<div className="custom-drawer-sub-card">
<div className="custom-drawer-sub-card-label">
<p>{t("filters.creationDate")}</p>
</div>
<div className="custom-drawer-sub-card-content">
<FormGroup style={{ marginBottom: "9px" }}>
<div className="custom-drawer-sub-card-content-sub">
<label className="custom-drawer-sub-card-content-sub-label">
{t("common.from")}
</label>
<input
type="date"
className="custom-drawer-sub-card-content-input-1"
onChange={(e) => setFromDate(e.target.value)}
value={fromDate}
/>
</div>
</FormGroup>
<FormGroup>
<div className="custom-drawer-sub-card-content-sub">
<label className="custom-drawer-sub-card-content-sub-label">
{t("common.to")}
</label>
<input
type="date"
className="custom-drawer-sub-card-content-input-2"
onChange={(e) => setToDate(e.target.value)}
value={toDate}
/>
</div>
</FormGroup>
</div>
</div>
</div>
<div className="custom-drawer-submit">
<button
className="c-btn c-btn--primary"
data-testid="custom-drawer-submit-search"
disabled={!hasSelectedFilters()}
>
{t("common.search")}
</button>
</div>
</div>
</form>
</CustomDrawer>
);
};

PatternFilters.propTypes = {
openFilterDrawer: PropTypes.bool,
handleClose: PropTypes.func,
selectionLevelFilter: PropTypes.any,
onChangeFilterCheckboxes: PropTypes.func,
};

export default PatternFilters;

+ 0
- 45
src/components/Schedules/DayComponent.js Прегледај датотеку

@@ -1,45 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { formatTimeSrb } from "../../util/helpers/dateHelpers";
import { useTranslation } from "react-i18next";

const DayComponent = ({ numberOfDay, nameOfDay, interviews, onClick }) => {
const { t } = useTranslation();
return (
<div className="day-component-container" onClick={onClick}>
<div className="day-component-day-informations-container">
<p className="day-component-day-number">{numberOfDay}</p>
<p className="day-component-day-name">{nameOfDay}</p>
</div>
{interviews &&
interviews.slice(0, 2).map((interview, index) => (
<div
key={index}
className="day-component-interviews-container"
style={{ marginTop: "4px" }}
>
<p className="day-component-interviews-time">
{formatTimeSrb(interview.date)}
</p>
<p className="day-component-interviews-name">
{interview.selectionLevel.name}
</p>
</div>
))}
{interviews && interviews.length > 2 && (
<span className="day-component-more">
+{interviews.length - 2} {t("schedule.items")}
</span>
)}
</div>
);
};

DayComponent.propTypes = {
numberOfDay: PropTypes.number,
nameOfDay: PropTypes.string,
interviews: PropTypes.array,
onClick: PropTypes.func,
};

export default DayComponent;

+ 0
- 208
src/components/Schedules/DayDetailsComponent.js Прегледај датотеку

@@ -1,208 +0,0 @@
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import { Dialog, DialogContent, DialogTitle } from "@mui/material";
import calendar from "../../../src/assets/images/calendar2.png";
import arrowLeftDisabled from "../../../src/assets/images/arrow_left.png";
import arrowLeft from "../../../src/assets/images/arrow_left2.png";
import arrowRight from "../../../src/assets/images/arrow_right.png";
import x from "../../../src/assets/images/x.png";
import meet from "../../../src/assets/images/meet.png";
import { formatTimeSrb } from "../../util/helpers/dateHelpers";
import { CANDIDATES_PAGE } from "../../constants/pages";
import { useTheme } from "@emotion/react";
import { useMediaQuery } from "@mui/material";
import { useTranslation } from "react-i18next";

const DayDetailsComponent = ({
selectedDate,
selectionProcesses,
open,
onClose,
setCurrentlySelected,
setCurrentlySelectedDay,
currentlySelectedDay,
numberOfDaysInMonth,
history,
}) => {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("361"));
const [isLeftArrowDisabled, setIsLeftArrowDisabled] = useState(false);
const [isRightArrowDisabled, setIsRightArrowDisabled] = useState(false);
const { t } = useTranslation();
const handleClose = () => {
onClose();
};

useEffect(() => {
setIsLeftArrowDisabled(currentlySelectedDay === 1 ? true : false);
setIsRightArrowDisabled(
currentlySelectedDay === numberOfDaysInMonth ? true : false
);
}, [currentlySelectedDay, isLeftArrowDisabled, isRightArrowDisabled]);

const goForwardOneDay = () => {
const date = selectedDate.split(".");
const day = date[0];
const month = date[1];
const year = date[2];
const newDay = parseInt(day) + 1;
if (day === numberOfDaysInMonth) {
setIsRightArrowDisabled(true);
return;
}
setCurrentlySelected(newDay + "." + month + "." + year);
setCurrentlySelectedDay(newDay);
setIsLeftArrowDisabled(true);
};

const goBackOneDay = () => {
const date = selectedDate.split(".");
const day = date[0];
const month = date[1];
const year = date[2];
if (day === 1) {
setIsLeftArrowDisabled(true);
return;
}
const newDay = parseInt(day) - 1;
setCurrentlySelected(newDay + "." + month + "." + year);
setCurrentlySelectedDay(newDay);
};

const navigateToCandidateDetailsPage = (applicantId) => {
history.push({
pathname: CANDIDATES_PAGE + "/" + applicantId,
});
};

return (
<Dialog
onClose={handleClose}
open={open}
maxWidth={!matches ? "549" : "339"}
>
<div
className="day-details-sub-container"
data-testid="day-component-dialog"
>
<DialogTitle className="day-datails-title-container">
<img src={calendar} className="day-details-calendar-image" />
<p className="day-details-main-header">{t("schedule.planner")}</p>
<p className="day-details-header">| {selectedDate}.</p>
<img
src={x}
className="day-details-close-btn"
onClick={handleClose}
/>
</DialogTitle>
<DialogContent
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<div className="day-details-content-sub-container">
{selectionProcesses &&
selectionProcesses.map((selectionProcess, index) => (
<div key={index} data-testid="day-details-component-process">
<div style={{ display: "flex", alignItems: "center" }}>
<p className="day-details-time">
{formatTimeSrb(selectionProcess.date)}h
</p>
<div
style={{
display: "flex",
alignItems: !matches ? "center" : "flex-start",
flexDirection: !matches ? "row" : "column",
}}
>
<p className="day-details-name">
{selectionProcess.selectionLevel.name}
</p>
<p
className="day-details-applicant"
data-testid="day-details-applicant"
onClick={() =>
navigateToCandidateDetailsPage(
selectionProcess.applicant.applicantId
)
}
>
{selectionProcess.applicant.firstName}{" "}
{selectionProcess.applicant.lastName}
</p>
</div>
<a
className="day-details-link"
href={selectionProcess.link}
target="_blank"
rel="noreferrer"
>
<img src={meet} />
{!matches && <span>Link</span>}
</a>
</div>
<div className="day-details-line" />
</div>
))}
</div>
<div
style={{
display: "flex",
gap: "18px",
marginTop: selectionProcesses.length === 0 ? "18px" : "0px",
}}
>
{isLeftArrowDisabled === true ? (
<div className="day-details-arrow-container">
<img src={arrowLeftDisabled} />
</div>
) : (
<div
className="day-details-arrow-container"
data-testid="day-details-left-arrow"
onClick={goBackOneDay}
>
<img src={arrowLeft} />
</div>
)}
{isRightArrowDisabled === true ? (
<div className="day-details-arrow-container-p">
<img src={arrowLeftDisabled} />
</div>
) : (
<div
className="day-details-arrow-container"
onClick={goForwardOneDay}
data-testid="day-details-right-arrow"
>
<img src={arrowRight} />
</div>
)}
</div>
</DialogContent>
</div>
</Dialog>
);
};

DayDetailsComponent.propTypes = {
selectedDate: PropTypes.string,
selectionProcesses: PropTypes.array,
open: PropTypes.bool,
onClose: PropTypes.func,
setCurrentlySelected: PropTypes.func,
setCurrentlySelectedDay: PropTypes.func,
currentlySelectedDay: PropTypes.number,
numberOfDaysInMonth: PropTypes.number,
history: PropTypes.shape({
replace: PropTypes.func,
push: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string,
}),
}),
};

export default DayDetailsComponent;

+ 2
- 2
src/components/Section/MainContainer.js Прегледај датотеку

@@ -4,7 +4,7 @@ import { useLocation } from "react-router-dom";
import Navbar from "../../components/MUI/NavbarComponent";
import { FormProvider } from "../../context/FormContext";
import { SelectionProvider } from "../../context/SelectionContext";
import { CREATE_AD_PAGE } from "../../constants/pages";
import { FILES_PAGE } from "../../constants/pages";
// import AppRoutes from "../../AppRoutes";

const urls = [
@@ -35,7 +35,7 @@ const MainContainer = ({ children }) => {
) : (
<div className="">
<Navbar />
<div className={pathname === CREATE_AD_PAGE ? "" : "h-withHeader"}>
<div className={pathname === FILES_PAGE ? "" : "h-withHeader"}>
{children}
</div>
</div>

+ 0
- 91
src/components/Selection/ApplicantSelection.js Прегледај датотеку

@@ -1,91 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { formatDate } from "../../util/helpers/dateHelpers";
import { Link } from "react-router-dom";
import success from "../../assets/images/svg/success.svg";
import unsucc from "../../assets/images/unsucc.png";
import { useTranslation } from "react-i18next";

const ApplicantSelection = ({
levelNumber,
levelName,
schedguler,
link,
date,
status,
id,
onShowAdDetails,
className,
}) => {
const { t } = useTranslation();
return (
<div
data-testid="appSelection"
className={`active-process-card ${className}`}
onClick={onShowAdDetails}
>
<div className="active-process-card-header">
<div className="active-process-card-number">
<p>{levelNumber}</p>
</div>
{status === "Odrađen" && (
<div className="active-process-card-logo">
<img src={success} alt="success-image" />
</div>
)}
{status === "Neuspešno" && (
<div className="active-process-card-logo">
<img src={unsucc} alt="success-image" />
</div>
)}
</div>

<div className="active-process-card-body">
<div className="active-process-card-date">
<p>{date && date !== "" && formatDate(new Date(date))}</p>
</div>

<div className="ad-card-title">
<h3>{levelName}</h3>
</div>

<div className="active-process-card-date">
<p>{schedguler}</p>
</div>

<div className="active-process-card-buttons">
<button className={status === "Neuspešno" && "unsucc"}>
{status}
</button>
</div>

<div className="active-process-card-link">
{status === "Odrađen" && (
<Link className="ad-details-buttons-link" to={"/candidates/" + id}>
{t("selection.report")}
</Link>
)}
{link && status !== "Odrađen" && (
<a href={link} className="ad-details-buttons-link">
{t("selection.link")}
</a>
)}
</div>
</div>
</div>
);
};

ApplicantSelection.propTypes = {
id: PropTypes.string,
levelNumber: PropTypes.number,
levelName: PropTypes.string,
schedguler: PropTypes.string,
link: PropTypes.string,
date: PropTypes.any,
status: PropTypes.string,
onShowAdDetails: PropTypes.func,
className: PropTypes.any,
};

export default ApplicantSelection;

+ 0
- 159
src/components/Selection/Selection.js Прегледај датотеку

@@ -1,159 +0,0 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { selectDoneProcessError } from "../../store/selectors/processSelectors";
import { selectAuthUser } from "../../store/selectors/userSelectors";
import { setDoneProcessReq } from "../../store/actions/processes/processAction";
import { useDispatch, useSelector } from "react-redux";
// import { formatDateSrb, formatTimeSrb } from "../../util/helpers/dateHelpers";
import { SELECTION_PROCESS_OF_APPLICANT_PAGE } from "../../constants/pages";
import { PUT_PROCESS_LOADING } from "../../store/actions/processes/processesActionConstants";
import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
import Backdrop from "../../components/MUI/BackdropComponent";
import { IconButton } from "@mui/material";
import plus from "../../assets/images/plus.png";
import SelectionCard from "./SelectionCard";
import { useTranslation } from "react-i18next";

const Selection = (props) => {
const [over, setOver] = useState(false);
const allApplicants = props.selection.selectionProcesses;
const errorMessage = useSelector(selectDoneProcessError);
const dispatch = useDispatch();
const user = useSelector(selectAuthUser);
const { t } = useTranslation();

const dropItem = (e, selId) => {
setOver(false);
var data = e.dataTransfer.getData("text/plain");
const selectionProcess = JSON.parse(data);

if (
selectionProcess.selectionLevelId < selId &&
selectionProcess.status !== "Neuspešno"
) {
dispatch(
setDoneProcessReq({
id: selectionProcess.id,
name: "Some random name",
applicantId: selectionProcess.applicant.applicantId,
schedulerId: user.id,
})
);
}

if (errorMessage) {
console.log(errorMessage);
}
};

const dragStart = (e, applicant) => {
e.dataTransfer.setData("text/plain", JSON.stringify(applicant));
};

const dragOver = (e) => {
e.preventDefault();
setOver(true);
};

const isLoading = useSelector(
selectIsLoadingByActionType(PUT_PROCESS_LOADING)
);

const handleOpenDetails = (id) => {
props.history.push(SELECTION_PROCESS_OF_APPLICANT_PAGE.replace(":id", id));
};

const applicants = allApplicants?.filter(
(a) => a.status !== "Odrađen" || a.selectionLevelId === 4
);

const renderList = applicants?.map((item, index) => {
return (
<SelectionCard
key={index}
item={item}
dragStart={(e) => dragStart(e, item)}
click={() => handleOpenDetails(item.applicant.applicantId)}
/>
);
});

return (
<div
data-testid="selection-level"
dropppable="true"
id={props.selection.id}
className={`selection-card ${over ? "over" : ""}`}
onDragOver={(e) => dragOver(e)}
onDragLeave={() => setOver(false)}
onDrop={(e) => dropItem(e, props.selection.id)}
>
<div className="selection-card-title">
<h3 style={{ marginRight: "50px" }}>{props.selection.name}</h3>
{props.order === 0 ? (
<IconButton
sx={{ marginRight: "35px" }}
className={`c-btn--primary-outlined c-btn td-btn`}
onClick={props.modalEvent}
>
<img
style={{
position: "relative",
}}
src={plus}
data-testid="interview-image"
/>
</IconButton>
) : (
""
)}
</div>
<Backdrop position="absolute" isLoading={isLoading} />
{applicants &&
applicants !== null &&
applicants?.length > 0 &&
renderList}
{applicants && applicants !== null && applicants?.length === 0 && (
<div className="sel-item-no-data">
<div className="date">
<p data-testid="empty-selection" style={{ width: "240px" }}>
{t("selection.noCandidates")}
</p>
</div>
</div>
)}
</div>
);
};

Selection.propTypes = {
history: PropTypes.shape({
replace: PropTypes.func,
push: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string,
}),
}),
order: PropTypes.number,
modalEvent: PropTypes.func,
selection: PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
selectionProcesses: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
date: PropTypes.string,
status: PropTypes.string,
currentSelection: PropTypes.number,
map: PropTypes.func,
applicant: PropTypes.shape({
firstName: PropTypes.string,
lastName: PropTypes.string,
}),
})
),
}),
};

export default Selection;

+ 0
- 195
src/components/Selection/SelectionCard.js Прегледај датотеку

@@ -1,195 +0,0 @@
import React, { useContext, useEffect, useState } from "react";
import PropTypes from "prop-types";
import { formatDateSrb, formatTimeSrb } from "../../util/helpers/dateHelpers";
import {
Button,
FormControl,
InputLabel,
MenuItem,
Select,
} from "@mui/material";
import { SelectionContext } from "../../context/SelectionContext";
import { useDispatch, useSelector } from "react-redux";
import {
setDoneProcessReq,
setUpdateStatusReq,
} from "../../store/actions/processes/processAction";
import { useTranslation } from "react-i18next";
// import Button from "../Button/Button";

const options = ["Zakazan", "Odrađen", "Čeka na zakazivanje", "Neuspešno"];

const SelectionCard = (props) => {
const [showForm, setShowForm] = useState(false);
const [selected, setSelected] = useState(props.item.status);
const { t } = useTranslation();

const { success } = useSelector((s) => s.statusUpdate);
const {
setActiveProcess,
setActiveInterview,
setActiveProcessUnsuccess,
// activeProcessUnsuccess,
} = useContext(SelectionContext);
const dispatch = useDispatch();

const statusChange = (e) => {
if (props.item.status !== "Odrađen") {
e.stopPropagation();
setShowForm(true);
}
};

useEffect(() => {
setShowForm(false);
}, [success]);

const select = (e) => {
e.stopPropagation();
if (e.target.value === "Zakazan") {
// setovanje context state-a
setActiveProcess({ process: props.item, status: "Zakazan" });
}
// poseban blok u slucaju da treba prikazati odredjeni modal kada je izabrano 'NEUSPESNO'
else if (e.target.value === "Neuspešno") {
setActiveProcessUnsuccess(props.item);
} else if (e.target.value === "Odrađen") {
// ukoliko nije zadnji nivo selekcije kreirati proces za sledeci nivo
// u suprotnom samo promeniti status u odradjeno
if (props.item.selectionLevelId !== 4)
dispatch(
setDoneProcessReq({
id: props.item.id,
name: "Some random name",
applicantId: props.item.applicant.applicantId,
})
);
else {
// pozvati nasu custom metodu za promenu statusa bez prebacivanja u veci nivo
// promeni status u odradjeno
dispatch(
setUpdateStatusReq({
data: {
newStatus: "Odrađen",
processId: props.item.id,
},
})
);
}
}
setSelected(e.target.value);
};

const clickHandler = () => {
if (showForm) {
setSelected(props.item.status);
setShowForm(false);
} else props.click();
};

const changeInterviewerHandler = (e) => {
e.stopPropagation();
setActiveInterview(props.item);
};

return (
<div
draggable
className="sel-item"
onDragStart={props.dragStart}
onClick={clickHandler}
data-testid="sel-card"
>
{" "}
<div
className={`sel-item-inner ${props.item.scheduler && "withScheduler"}`}
>
{showForm ? (
<form data-testid="status-select">
<FormControl>
<InputLabel id="demo-simple-select-label">Status</InputLabel>
<Select
data-testid="status-select-drop"
label="Status"
onChange={(e) => {
select(e);
}}
onClick={(e) => e.stopPropagation()}
value={selected}
sx={{
height: "40px",
fontSize: "14px",
paddingRight: "5px",
}}
>
{options.map((n, index) => (
<MenuItem key={index} sx={{ textAlign: "left" }} value={n}>
{n}
</MenuItem>
))}
</Select>
</FormControl>
</form>
) : (
<div
className="status"
onClick={(e) => {
if (props.item.status !== "Neuspešno") statusChange(e);
}}
>
<button
data-testid="status-btn"
className={props.item.status === "Neuspešno" ? "unsucc" : ""}
>
{props.item.status}
</button>
</div>
)}
<div className="date-name">
<div className="date">
{props.item.date !== null && props.item.date !== "" && (
<p data-testid="process-date">
{formatDateSrb(props.item.date)} <span className="grey">|</span>{" "}
{formatTimeSrb(props.item.date)}
</p>
)}
</div>
<div className="full-name">
<p>
{props.item.applicant.firstName +
" " +
props.item.applicant.lastName}
</p>
</div>
</div>
</div>
{props.item.scheduler &&
props.item.status !== "Neuspešno" &&
props.item.status !== "Odrađen" ? (
<div className="sel-item-scheduler">
<div className="change-interbtn">
<Button
className="interbtn"
onClick={(e) => changeInterviewerHandler(e)}
>
{t("common.change")}
</Button>
</div>
<p>
{t("selection.interviewer")}: {props.item.scheduler.firstName}{" "}
{props.item.scheduler.lastName}
</p>
</div>
) : (
""
)}
</div>
);
};

SelectionCard.propTypes = {
item: PropTypes.any,
click: PropTypes.func,
dragStart: PropTypes.func,
};
export default SelectionCard;

+ 0
- 146
src/components/Selection/SelectionFilter.js Прегледај датотеку

@@ -1,146 +0,0 @@
import React, { useState } from "react";
import PropType from "prop-types";
import Box from "@mui/material/Box";
import Drawer from "@mui/material/Drawer";
import FormGroup from "@mui/material/FormGroup";
import FormControlLabel from "@mui/material/FormControlLabel";
import Checkbox from "@mui/material/Checkbox";
import filterIcon from "../../assets/images/filter_vector.png";
import x from "../../assets/images/x.png";
import { changeStatusIsCheckedValue } from "../../store/actions/processes/statusAction";
import { useDispatch } from "react-redux";
import { setFilteredProcessesReq } from "../../store/actions/processes/processesAction";
import { useTranslation } from "react-i18next";

const SelectionFilter = ({ open, handleClose, statuses }) => {
const [startAt, setStartAt] = useState("");
const [endAt, setEndAt] = useState("");
const { t } = useTranslation();

const dispatch = useDispatch();

const onSubmitFilters = () => {
const status = statuses
.filter((status) => status.isChecked === true)
.map((status) => status.name);

dispatch(
setFilteredProcessesReq({
statuses: status,
startAt,
endAt
})
);

handleClose();
};

const handleCheckboxes = (e) => {
const { value } = e.target;
dispatch(changeStatusIsCheckedValue(value));
};

const list = () => (
<Box
sx={{
width: 360,
height: "100%",
borderRadius: "18px 0 0 18px",
padding: "36px",
}}
role="presentation"
// onClick={handleClose}
onKeyDown={handleClose}
>
<div>
<div className="ad-filters-header-container">
<div className="ad-filters-header">
<img src={filterIcon} alt="filter_icon" />
<h3>{t("filters.filters")}</h3>
<p>
<sub>| {t("selection.title")}</sub>
</p>
</div>
<div className="ad-filters-header-close" onClick={handleClose}>
<img src={x} alt="x" />
</div>
</div>
<div className="ad-filters-technologies">
<div className="ad-filters-sub-title">
<p>Status</p>
</div>
<div className="ad-filters-technologies-checkboxes">
<FormGroup>
{statuses?.map((technology, index) => (
<FormControlLabel
key={index}
control={
<Checkbox
onChange={handleCheckboxes}
value={technology.name}
checked={technology.isChecked}
/>
}
label={technology.name}
/>
))}
</FormGroup>
</div>
</div>
<div className="ad-filters-technologies">
{/* <div className="ad-filters-sub-title">
<p>Datum isteka oglasa</p>
</div>
<input
type="date"
placeholder="ex"
value={expiredAt}
onChange={(e) => setExpiredAt(e.target.value)}
/> */}
<div className="ad-filters-sub-title">
<p>{t("selection.filterDate")}</p>
</div>
<div className="add-ad-modal-stage-sub-card">
<label>{t("common.from")}</label>
<input
type="date"
placeholder="ex"
value={startAt}
onChange={(e) => setStartAt(e.target.value)}
/>
</div>
<div className="add-ad-modal-stage-sub-card">
<label>{t("common.to")}</label>
<input
type="date"
placeholder="ex"
value={endAt}
onChange={(e) => setEndAt(e.target.value)}
/>
</div>
</div>
<div className="ad-filters-search">
<button onClick={onSubmitFilters} className="c-btn c-btn--primary">
{t("common.search")}
</button>
</div>
</div>
</Box >
);

return (
<div>
<Drawer anchor="right" open={open} onClose={handleClose}>
{list()}
</Drawer>
</div>
);
};

SelectionFilter.propTypes = {
open: PropType.any,
handleClose: PropType.func,
statuses: PropType.any,
};

export default SelectionFilter;

+ 0
- 0
src/constants/keyCodeConstants.js Прегледај датотеку


Неке датотеке нису приказане због велике количине промена

Loading…
Откажи
Сачувај