diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..27bf1b1 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,118 @@ +image: node:latest + +stages: + - build + - test + - test:build + - deploy + +merge:build: + stage: build + script: + - npm install + - cp example.env.local .env.local + - cp example.env.local .env.test + only: + - merge_requests + artifacts: + paths: + - node_modules + - .env.local + - .env.test + +merge:test:jest: + stage: test + script: + - npm run test + only: + - merge_requests + artifacts: + paths: + - node_modules + - .env.local + - .env.test + +merge:test:lint: + stage: test + script: + - npm run lint + only: + - merge_requests + artifacts: + paths: + - node_modules + - .env.local + - .env.test + +merge:test:build: + stage: test:build + script: + - npm run build + only: + - merge_requests + artifacts: + paths: + - node_modules + - .env.local + - .env.test + +build: + stage: build + script: + - npm install + - cp example.env.local .env.local + - cp example.env.local .env.test + only: + - develop + - main + artifacts: + paths: + - node_modules + - .env.local + - .env.test + +test:jest: + stage: test + script: + - npm run test + only: + - develop + - main + artifacts: + paths: + - node_modules + - .env.local + - .env.test + +test:lint: + stage: test + script: + - npm run lint + only: + - develop + - main + artifacts: + paths: + - node_modules + - .env.local + - .env.test + +test:build: + stage: test:build + script: + - npm run build + only: + - develop + - main + artifacts: + paths: + - node_modules + - .env.local + - .env.test + +deploy: + stage: deploy + script: + - echo "Run deploy project" + only: + - main \ No newline at end of file diff --git a/mocks/handler.js b/mocks/handler.js index dfac7e8..da1bc4e 100644 --- a/mocks/handler.js +++ b/mocks/handler.js @@ -55,4 +55,9 @@ export const handler = [ ctx.status(200), ); }), + rest.post(`${EXPERT_MANAGEMENT}/:id`, (req, res, ctx) => { + return res( + ctx.status(200), + ); + }), ] \ No newline at end of file diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 89efd13..3774d17 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -174,6 +174,8 @@ "delete_expert": "حذف کارشناس", "are_you_sure_text": "آیا از انجام این عملیات اطمینان دارید؟", "button_delete": "حذف", - "delete_tooltip": "حذف" + "delete_tooltip": "حذف", + "update_tooltip": "ویرایش", + "update_expert": "ویرایش کارشناس" } } diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/__tests__/index.test.js index 0f79edd..913c37a 100644 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/__tests__/index.test.js +++ b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo/__tests__/index.test.js @@ -38,7 +38,7 @@ const CreateFormMock = ({Component}) => { }; -describe("PersonalInfo Component From Expert Management", () => { +describe("PersonalInfo Component From Expert Management (create)", () => { describe("Rendering", () => { it("full_name TextField Rendered", async () => { render(); diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/__tests__/index.test.js index d5c9546..6f32bac 100644 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/__tests__/index.test.js +++ b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole/__tests__/index.test.js @@ -1,11 +1,10 @@ -import {act, findByRole, findByText, fireEvent, render, screen, waitFor} from "@testing-library/react"; +import {fireEvent, render, screen, waitFor} from "@testing-library/react"; import React from 'react'; import {useTranslations} from "next-intl"; import * as Yup from "yup"; import {Formik} from "formik"; import MockAppWithProviders from "../../../../../../../../../../mocks/AppWithProvider"; import PositionAndRole from "../../PositionAndRole"; -import {MenuItem, Select} from "@mui/material"; const CreateFormMock = ({Component}) => { const t = useTranslations(); @@ -28,7 +27,7 @@ const CreateFormMock = ({Component}) => { ); }; -describe("CreateContent Component From Expert Management", () => { +describe("PositionAndRole Component From Expert Management (create)", () => { describe("Rendering", () => { it("Position TextField Rendered", async () => { render(); diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/__tests__/index.test.js index 354df01..87da19f 100644 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/__tests__/index.test.js +++ b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/__tests__/index.test.js @@ -5,8 +5,6 @@ import * as Yup from "yup"; import {Formik} from "formik"; import Province from "../../Province"; import MockAppWithProviders from "../../../../../../../../../../mocks/AppWithProvider"; -import PositionAndRole - from "@/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/PositionAndRole"; const CreateFormMock = ({Component}) => { const t = useTranslations(); @@ -27,7 +25,7 @@ const CreateFormMock = ({Component}) => { ); }; -describe("CreateContent Component From Expert Management", () => { +describe("Province Component From Expert Management (create)", () => { describe("Rendering", () => { it("Province Select Box Rendered", async () => { render(); diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/index.jsx b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/index.jsx index 4c8995c..5ac94b2 100644 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/index.jsx +++ b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province/index.jsx @@ -1,6 +1,5 @@ import {FormControl, FormHelperText, Grid, InputLabel, MenuItem, Select} from "@mui/material"; import {useTranslations} from "next-intl"; -import {useState} from "react"; import useProvince from "@/lib/app/hooks/useProvince"; import useRequest from "@/lib/app/hooks/useRequest"; diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/__test__/index.test.js b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/__test__/index.test.js index 7f10a99..519cf9d 100644 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/__test__/index.test.js +++ b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/UserInfo/__test__/index.test.js @@ -5,7 +5,6 @@ import {useTranslations} from "next-intl"; import * as Yup from "yup"; import {Formik} from "formik"; import UserInfo from "../../UserInfo"; -import PersonalInfo from "@/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo"; const CreateFormMock = ({Component}) => { const t = useTranslations(); @@ -33,7 +32,7 @@ const CreateFormMock = ({Component}) => { ); }; -describe("CreateContent Component From Expert Management", () => { +describe("UserInfo Component From Expert Management (create)", () => { describe("Rendering", () => { it("UserName TextField Rendered", async () => { render(); @@ -98,7 +97,7 @@ describe("CreateContent Component From Expert Management", () => { render(); const passwordInput = screen.queryByLabelText('رمز عبور'); - + // check without text or symbol fireEvent.change(passwordInput, {target: {value: '12345678'}}); fireEvent.blur(passwordInput); diff --git a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/__tests__/index.test.js index 1e71174..85751c5 100644 --- a/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/__tests__/index.test.js +++ b/src/components/dashboard/expert-management/Form/CreateForm/CreateContent/__tests__/index.test.js @@ -2,8 +2,6 @@ import {fireEvent, render, screen, waitFor} from "@testing-library/react"; import React from 'react'; import CreateContent from "../../CreateContent"; import MockAppWithProviders from "../../../../../../../../mocks/AppWithProvider"; -import userEvent from '@testing-library/user-event'; -import CreateForm from "@/components/dashboard/expert-management/Form/CreateForm"; function selectDropdownItem(screen, openerTestId, itemText) { const opener = screen.getByTestId(openerTestId); @@ -16,16 +14,16 @@ function setInputValue(screen, inputElement, value) { fireEvent.change(inputElement, {target: {value}}); } -describe("CreateContent Component From Expert Management", () => { +describe("CreateContent Component From Expert Management (create)", () => { describe("Rendering", () => { - it("Password TextField Rendered", async () => { + it("close button Rendered", async () => { render(); await waitFor(() => { const cancelBtn = screen.queryByText('بستن'); expect(cancelBtn).toBeInTheDocument(); }); }); - it("Password TextField Rendered", async () => { + it("confirm button Rendered", async () => { render(); await waitFor(() => { const cancelBtn = screen.queryByText('ثبت'); @@ -33,8 +31,6 @@ describe("CreateContent Component From Expert Management", () => { }); }); }); - describe("Behavioral", () => { - }); describe("Form Submission", () => { it('Should enable the submit button when the inputs are valid', async () => { render(); diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/__tests__/index.test.js new file mode 100644 index 0000000..b8692d5 --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/__tests__/index.test.js @@ -0,0 +1,219 @@ +import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; +import React from 'react'; +import MockAppWithProviders from "../../../../../../../../../mocks/AppWithProvider"; +import * as Yup from "yup"; +import {Formik} from "formik"; +import PersonalInfo from "@/components/dashboard/expert-management/Form/CreateForm/CreateContent/PersonalInfo"; +import {useTranslations} from "next-intl"; +import Province from "@/components/dashboard/expert-management/Form/CreateForm/CreateContent/RestInfo/Province"; + + +const CreateFormMock = ({Component}) => { + const t = useTranslations(); + const validationSchema = Yup.object().shape({ + full_name: Yup.string().required(t("ExpertMangement.error_message_full_name")), + email: Yup.string().required(t("ExpertMangement.error_message_email")), + phone_number: Yup.string().required(t("ExpertMangement.error_message_phone_number")), + telephone_id: Yup.string().required(t("ExpertMangement.error_message_telephone_id")), + national_id: Yup.string().required(t("ExpertMangement.error_message_national_id")), + gender: Yup.string().required(t("ExpertMangement.error_message_gender")) + }); + return ( + { + }} + > + {formikProps => ()} + + ); +}; + + +describe("PersonalInfo Component From Expert Management (update)", () => { + describe("Rendering", () => { + it("full_name TextField Rendered", async () => { + render(); + await waitFor(() => { + const fullNameTextField = screen.queryByLabelText('نام کامل'); + expect(fullNameTextField).toBeInTheDocument(); + }); + }); + it("Email TextField Rendered", async () => { + render(); + await waitFor(() => { + const emailTextField = screen.queryByLabelText('پست الکترونیک'); + expect(emailTextField).toBeInTheDocument(); + }); + }); + it("Phone Number TextField Rendered", async () => { + render(); + await waitFor(() => { + const phoneNumberTextField = screen.queryByLabelText('شماره همراه'); + expect(phoneNumberTextField).toBeInTheDocument(); + }); + }); + it("telephone id TextField Rendered", async () => { + render(); + await waitFor(() => { + const telephoneIdTextField = screen.queryByLabelText('کد تلفن'); + expect(telephoneIdTextField).toBeInTheDocument(); + }); + }); + it("National Id TextField Rendered", async () => { + render(); + await waitFor(() => { + const nationalIdTextField = screen.queryByLabelText('کد ملی'); + expect(nationalIdTextField).toBeInTheDocument(); + }); + }); + it("Gender Select Box Rendered", async () => { + render(); + await waitFor(() => { + const genderSelectBox = screen.getByTestId('select-box'); + expect(genderSelectBox).toBeInTheDocument(); + }); + }); + }); + describe("Behavioral", () => { + it("Name TextField Work Correctly", async () => { + render(); + const nameTextField = screen.queryByLabelText('نام کامل'); + fireEvent.change(nameTextField, {target: {value: 'exampleName'}}); + await act(() => { + expect(nameTextField).toHaveValue('exampleName'); + }) + }); + it("Email TextField Work Correctly", async () => { + render(); + const emailTextField = screen.queryByLabelText('پست الکترونیک'); + fireEvent.change(emailTextField, {target: {value: 'exampleEmail'}}); + await act(() => { + expect(emailTextField).toHaveValue('exampleEmail'); + }) + }); + it("Phone Number TextField Work Correctly", async () => { + render(); + const phoneNumberTextField = screen.queryByLabelText('شماره همراه'); + fireEvent.change(phoneNumberTextField, {target: {value: 'examplePhoneNumber'}}); + await act(() => { + expect(phoneNumberTextField).toHaveValue('examplePhoneNumber'); + }) + }); + it("Telephone Id TextField Work Correctly", async () => { + render(); + const telephoneIdTextField = screen.queryByLabelText('کد تلفن'); + fireEvent.change(telephoneIdTextField, {target: {value: 'exampleTelephoneId'}}); + await act(() => { + expect(telephoneIdTextField).toHaveValue('exampleTelephoneId'); + }) + }); + it("National Id TextField Work Correctly", async () => { + render(); + const nationalIdTextField = screen.queryByLabelText('کد ملی'); + fireEvent.change(nationalIdTextField, {target: {value: 'exampleNationalId'}}); + await act(() => { + expect(nationalIdTextField).toHaveValue('exampleNationalId'); + }) + }); + it("Gender Select Box Work Correctly", async () => { + render(); + const genderSelectBox = screen.getByTestId('select-box'); + const genderSelectOpener = await waitFor(() => screen.getByTestId("option-opener-gender")); + + await waitFor(() => { + expect(genderSelectBox).toBeInTheDocument(); + }); + + fireEvent.mouseDown(genderSelectOpener); + await waitFor(() => { + const selectItem = screen.queryByText("مرد"); + expect(selectItem).toBeInTheDocument(); + }); + + }); + }); + describe("validation", () => { + it('Should See Error When Name Input Is Empty', async () => { + render(); + + const nameInput = screen.queryByLabelText('نام کامل'); + + fireEvent.change(nameInput, {target: {value: ''}}); + fireEvent.blur(nameInput); + + await waitFor(() => { + expect(screen.queryByText("نام کامل خود را وارد کنید")).toBeInTheDocument() + }); + }); + it('Should See Error When Email Input Is Empty', async () => { + render(); + + const emailInput = screen.queryByLabelText('پست الکترونیک'); + + fireEvent.change(emailInput, {target: {value: ''}}); + fireEvent.blur(emailInput); + + await waitFor(() => { + expect(screen.queryByText("پست الکترونیک خود را وارد کنید")).toBeInTheDocument() + }); + }); + it('Should See Error When Phone Number Input Is Empty', async () => { + render(); + + const phoneNumberInput = screen.queryByLabelText('شماره همراه'); + + fireEvent.change(phoneNumberInput, {target: {value: ''}}); + fireEvent.blur(phoneNumberInput); + + await waitFor(() => { + expect(screen.queryByText("شماره همراه خود را وارد کنید")).toBeInTheDocument() + }); + }); + it('Should See Error When telephone Id Input Is Empty', async () => { + render(); + + const telephoneIdTextField = screen.queryByLabelText('کد تلفن'); + + fireEvent.change(telephoneIdTextField, {target: {value: ''}}); + fireEvent.blur(telephoneIdTextField); + + await waitFor(() => { + expect(screen.queryByText("کد تلفن خود را وارد کنید")).toBeInTheDocument() + }); + }); + it('Should See Error When National Id Input Is Empty', async () => { + render(); + + const nationalIdInput = screen.queryByLabelText('کد ملی'); + + fireEvent.change(nationalIdInput, {target: {value: ''}}); + fireEvent.blur(nationalIdInput); + + await waitFor(() => { + expect(screen.queryByText("کد ملی خود را وارد کنید")).toBeInTheDocument() + }); + }); + it('Should Select An Item With Valid Value of gender', async () => { + render(); + const genderInput = screen.getByTestId("input-gender") + const genderSelectOpener = await waitFor(() => screen.getByTestId("option-opener-gender")); + + fireEvent.mouseDown(genderSelectOpener); + const selectItem = await waitFor(() => screen.queryByText("مرد")); + expect(selectItem).toBeInTheDocument(); + fireEvent.click(selectItem); + await waitFor(() => { + expect(genderInput.value).toBe('male') + }); + }); + }) +}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/index.jsx new file mode 100644 index 0000000..f92bf38 --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/PersonalInfo/index.jsx @@ -0,0 +1,128 @@ +import {FormControl, FormHelperText, Grid, InputLabel, MenuItem, Select, TextField} from "@mui/material"; +import {useTranslations} from "next-intl"; + +const PersonalInfo = ({formik}) => { + const t = useTranslations(); + + const genderList = [ + {id: 1, name_en: "male", name_fa: "مرد"}, + {id: 2, name_en: "female", name_fa: "زن"} + ] + + return ( + + + + + + + + + + + + + + + + + + + {t("ExpertMangement.text_field_gender")} + + + {formik.touched.gender && formik.errors.gender ? formik.errors.gender : ""} + + + + + ) +} +export default PersonalInfo \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/Index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/Index.jsx new file mode 100644 index 0000000..4fc402a --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/Index.jsx @@ -0,0 +1,75 @@ +import {FormControl, FormHelperText, Grid, InputLabel, MenuItem, Select, TextField} from "@mui/material"; +import {useTranslations} from "next-intl"; +import useRequest from "@/lib/app/hooks/useRequest"; +import useRole from "@/lib/app/hooks/useRole"; +import {log} from "next/dist/server/typescript/utils"; + +const PositionAndRole = ({formik}) => { + const t = useTranslations(); + const requestServer = useRequest() + const {roleList, isLoadingRoleList, errorRoleList} = useRole(); + + return ( + + + + + + + {t("ExpertMangement.text_field_roles")} + + + {formik.touched.roles && formik.errors.roles ? formik.errors.roles : ""} + + + + + ) +} +export default PositionAndRole \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/__tests__/index.test.js new file mode 100644 index 0000000..d6062c5 --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/PositionAndRole/__tests__/index.test.js @@ -0,0 +1,96 @@ +import {fireEvent, render, screen, waitFor} from "@testing-library/react"; +import React from 'react'; +import {useTranslations} from "next-intl"; +import * as Yup from "yup"; +import {Formik} from "formik"; +import MockAppWithProviders from "../../../../../../../../../../mocks/AppWithProvider"; +import PositionAndRole from "../../PositionAndRole"; + +const CreateFormMock = ({Component}) => { + const t = useTranslations(); + const validationSchema = Yup.object().shape({ + position: Yup.string().required(t("ExpertMangement.error_message_position")), + roles: Yup.string().required(t("ExpertMangement.error_message_roles")) + }); + return ( + { + }} + > + {formikProps => ()} + + ); +}; + +describe("PositionAndRole Component From Expert Management (update)", () => { + describe("Rendering", () => { + it("Position TextField Rendered", async () => { + render(); + await waitFor(() => { + const positionTextField = screen.queryByLabelText('سمت'); + expect(positionTextField).toBeInTheDocument(); + }); + }); + it("Role Select Box Rendered", async () => { + render(); + const roleSelect = screen.getByTestId('select-box'); + await waitFor(() => { + expect(roleSelect).toBeInTheDocument(); + }); + }); + }); + describe("Behavioral", () => { + it("Position TextField Work Correctly", async () => { + render(); + const positionTextField = screen.queryByLabelText('سمت'); + fireEvent.change(positionTextField, {target: {value: 'examplePosition'}}); + await waitFor(() => { + expect(positionTextField).toHaveValue('examplePosition'); + }) + }); + it("Role Select Box Work Correctly", async () => { + render(); + const roleSelect = screen.getByTestId("option-opener-role"); + await waitFor(() => { + expect(roleSelect).toBeInTheDocument(); + }); + fireEvent.mouseDown(roleSelect); + await waitFor(() => { + const selectItem = screen.queryByText("ادمین"); + expect(selectItem).toBeInTheDocument(); + }); + }); + }); + describe("validation", () => { + it('Should See Error When Position Input Is Empty', async () => { + render(); + + const positionInput = screen.queryByLabelText('سمت'); + + fireEvent.change(positionInput, {target: {value: ''}}); + fireEvent.blur(positionInput); + + await waitFor(() => { + expect(screen.queryByText("سمت خود را وارد کنید")).toBeInTheDocument() + }); + }); + it('Should Select An Item With Valid Value of role', async () => { + render(); + const roleInput = await waitFor(() => screen.getByTestId("input-role-id")); + const roleSelectOpener = await waitFor(() => screen.getByTestId("option-opener-role")); + + fireEvent.mouseDown(roleSelectOpener); + const selectItem = await waitFor(() => screen.queryByText("ادمین")); + expect(selectItem).toBeInTheDocument(); + fireEvent.click(selectItem); + await waitFor(() => { + expect(roleInput.value).toBe('1') + }); + }); + }) +}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/__tests__/index.test.js new file mode 100644 index 0000000..d727188 --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/__tests__/index.test.js @@ -0,0 +1,71 @@ +import {fireEvent, render, screen, waitFor} from "@testing-library/react"; +import React from 'react'; +import {useTranslations} from "next-intl"; +import * as Yup from "yup"; +import {Formik} from "formik"; +import Province from "../../Province"; +import MockAppWithProviders from "../../../../../../../../../../mocks/AppWithProvider"; + +const CreateFormMock = ({Component}) => { + const t = useTranslations(); + const validationSchema = Yup.object().shape({ + province_id: Yup.string().required(t("ExpertMangement.error_message_province_id")), + }); + return ( + { + }} + > + {formikProps => ()} + + ); +}; + +describe("Province Component From Expert Management (update)", () => { + describe("Rendering", () => { + it("Province Select Box Rendered", async () => { + render(); + await waitFor(() => { + const provinceSelectBox = screen.getByTestId('select-box'); + expect(provinceSelectBox).toBeInTheDocument(); + }); + }); + }); + describe("Behavioral", () => { + it("Province Select Box Work Correctly", async () => { + render(); + const provinceSelectBox = screen.getByTestId('select-box'); + const provinceSelectOpener = await waitFor(() => screen.getByTestId("option-opener-province")); + + await waitFor(() => { + expect(provinceSelectBox).toBeInTheDocument(); + }); + + fireEvent.mouseDown(provinceSelectOpener); + await waitFor(() => { + const selectItem = screen.queryByText("آذربایجان شرقی"); + expect(selectItem).toBeInTheDocument(); + }); + + }); + }); + describe("validation", () => { + it('Should Select An Item With Valid Value of province', async () => { + render(); + const provinceInput = screen.getByTestId("input-province-id") + const provinceSelectOpener = await waitFor(() => screen.getByTestId("option-opener-province")); + + fireEvent.mouseDown(provinceSelectOpener); + const selectItem = await waitFor(() => screen.queryByText("آذربایجان شرقی")); + expect(selectItem).toBeInTheDocument(); + fireEvent.click(selectItem); + await waitFor(() => { + expect(provinceInput.value).toBe('1') + }); + }); + }) +}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/index.jsx new file mode 100644 index 0000000..763465e --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/Province/index.jsx @@ -0,0 +1,61 @@ +import {FormControl, FormHelperText, Grid, InputLabel, MenuItem, Select} from "@mui/material"; +import {useTranslations} from "next-intl"; +import useProvince from "@/lib/app/hooks/useProvince"; +import useRequest from "@/lib/app/hooks/useRequest"; + +const Province = ({formik}) => { + const t = useTranslations(); + const requestServer = useRequest() + const {provinceList, isLoadingProvinceList, errorProvinceList} = useProvince(); + + return ( + + + + {t("ExpertMangement.text_field_province_id")} + + + {formik.touched.province_id && formik.errors.province_id ? formik.errors.province_id : ""} + + + + + ) +} +export default Province \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/index.jsx new file mode 100644 index 0000000..072c096 --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/RestInfo/index.jsx @@ -0,0 +1,12 @@ +import Province from "./Province"; +import PositionAndRole from "./PositionAndRole"; + +const RestInfo = ({formik}) => { + return ( + <> + + + + ) +} +export default RestInfo \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/__tests__/index.test.js new file mode 100644 index 0000000..3fd341d --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/__tests__/index.test.js @@ -0,0 +1,62 @@ +import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; +import React from 'react'; +import MockAppWithProviders from "../../../../../../../../../mocks/AppWithProvider"; +import {useTranslations} from "next-intl"; +import * as Yup from "yup"; +import {Formik} from "formik"; +import UserInfo from "../../UserInfo"; + +const CreateFormMock = ({Component}) => { + const t = useTranslations(); + const validationSchema = Yup.object().shape({ + username: Yup.string().required(t("ExpertMangement.error_message_username")), + }); + return ( + { + }} + > + {formikProps => ()} + + ); +}; + +describe("UserInfo Component From Expert Management (update)", () => { + describe("Rendering", () => { + it("UserName TextField Rendered", async () => { + render(); + await waitFor(() => { + const usernameTextField = screen.queryByLabelText('نام کاربری'); + expect(usernameTextField).toBeInTheDocument(); + }); + }); + }); + describe("Behavioral", () => { + it("Username TextField Work Correctly", async () => { + render(); + const usernameTextField = screen.queryByLabelText('نام کاربری'); + fireEvent.change(usernameTextField, {target: {value: 'exampleUsername'}}); + await act(() => { + expect(usernameTextField).toHaveValue('exampleUsername'); + }) + }); + }); + describe("validation", () => { + it('Should See Error When Username Input Is Empty', async () => { + render(); + + const usernameInput = screen.queryByLabelText('نام کاربری'); + + fireEvent.change(usernameInput, {target: {value: ''}}); + fireEvent.blur(usernameInput); + + await waitFor(() => { + expect(screen.queryByText("نام کاربری خود را وارد کنید")).toBeInTheDocument() + }); + }); + }) +}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/index.jsx new file mode 100644 index 0000000..d592ea5 --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/UserInfo/index.jsx @@ -0,0 +1,29 @@ +import {Grid, TextField} from "@mui/material"; +import {useTranslations} from "next-intl"; + +const UserInfo = ({formik}) => { + const t = useTranslations(); + + return ( + <> + + + + + + + ) +} +export default UserInfo \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/__tests__/index.test.js new file mode 100644 index 0000000..765bc09 --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/__tests__/index.test.js @@ -0,0 +1,87 @@ +import {fireEvent, render, screen, waitFor} from "@testing-library/react"; +import React from 'react'; +import MockAppWithProviders from "../../../../../../../../mocks/AppWithProvider"; +import UpdateContent from "../../UpdateContent"; + +const row = { + original: { + avatar: null, + created_at: "2023-10-10T13:05:01.000000Z", + email: "fisher.sigrid@yahoo.com", + full_name: "Anibal Labadie", + gender: "female", + id: 85, + national_id: "5944646138", + phone_number: "09731311720", + position: "boss1", + province_fa: "اردبیل", + province_id: 3, + roles: [{ + created_at: "2023-10-10T13:03:18.000000Z", + guard_name: "api", + id: 2, + name: "manager", + name_fa: "مدیر" + }], + telephone_id: "tel-3110", + updated_at: "2023-10-17T10:57:12.000000Z", + username: "florence.kihn", + } +} + +function selectDropdownItem(screen, openerTestId, itemText) { + const opener = screen.getByTestId(openerTestId); + fireEvent.mouseDown(opener); + const selectItem = screen.queryByText(itemText); + fireEvent.click(selectItem); +} + +function setInputValue(screen, inputElement, value) { + fireEvent.change(inputElement, {target: {value}}); +} + +describe("UpdateContent Component From Expert Management (update)", () => { + describe("Rendering", () => { + it("close button Rendered", async () => { + render(); + await waitFor(() => { + const cancelBtn = screen.queryByText('بستن'); + expect(cancelBtn).toBeInTheDocument(); + }); + }); + it("confirm button Rendered", async () => { + render(); + await waitFor(() => { + const confirmBtn = screen.queryByText('ثبت'); + expect(confirmBtn).toBeInTheDocument(); + }); + }); + }); + describe("Form Submission", () => { + it('Should enable the submit button when the inputs are valid', async () => { + render(); + + const submitButton = screen.queryByText('ثبت'); + + const nameInput = screen.queryByLabelText('نام کامل'); + const usernameInput = screen.queryByLabelText('نام کاربری'); + const emailInput = screen.queryByLabelText('پست الکترونیک'); + const phoneNumberInput = screen.queryByLabelText('شماره همراه'); + const telephoneIdInput = screen.queryByLabelText('کد تلفن'); + const nationalIdInput = screen.queryByLabelText('کد ملی'); + const positionInput = screen.queryByLabelText('سمت'); + + setInputValue(screen, nameInput, 'nameTest'); + setInputValue(screen, usernameInput, 'usernameTest'); + setInputValue(screen, emailInput, 'emailTest'); + setInputValue(screen, phoneNumberInput, 'phoneNumberTest'); + setInputValue(screen, telephoneIdInput, 'telephoneIdTest'); + setInputValue(screen, nationalIdInput, 'nationalIdTest'); + setInputValue(screen, positionInput, 'positionTest'); + + await waitFor(() => { + expect(submitButton).not.toBeDisabled(); + }); + }); + }) +}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/index.jsx new file mode 100644 index 0000000..3b60506 --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/UpdateContent/index.jsx @@ -0,0 +1,108 @@ +import {Box, Button, Chip, DialogActions, DialogContent, Divider,} from "@mui/material"; +import * as Yup from "yup"; +import {useTranslations} from "next-intl"; +import {useFormik} from "formik"; +import useRequest from "@/lib/app/hooks/useRequest"; +import {EXPERT_MANAGEMENT} from "@/core/data/apiRoutes"; +import PersonalInfo from "./PersonalInfo"; +import UserInfo from "./UserInfo"; +import RestInfo from "./RestInfo"; + +const UpdateContent = ({row, fetchUrl, mutate, setOpenUpdateDialog}) => { + const t = useTranslations(); + const requestServer = useRequest() + + const validationSchema = Yup.object().shape({ + full_name: Yup.string().required(t("ExpertMangement.error_message_full_name")), + username: Yup.string().required(t("ExpertMangement.error_message_username")), + email: Yup.string().required(t("ExpertMangement.error_message_email")), + phone_number: Yup.string().required(t("ExpertMangement.error_message_phone_number")), + telephone_id: Yup.string().required(t("ExpertMangement.error_message_telephone_id")), + gender: Yup.string().required(t("ExpertMangement.error_message_gender")), + national_id: Yup.string().required(t("ExpertMangement.error_message_national_id")), + position: Yup.string().required(t("ExpertMangement.error_message_position")), + province_id: Yup.string().required(t("ExpertMangement.error_message_province_id")), + roles: Yup.string().required(t("ExpertMangement.error_message_roles")) + }); + + const formik = useFormik({ + initialValues: { + full_name: row.original.full_name, + username: row.original.username, + email: row.original.email, + phone_number: row.original.phone_number, + telephone_id: row.original.telephone_id, + gender: row.original.gender, + national_id: row.original.national_id, + position: row.original.position, + province_id: row.original.province_id, + roles: row.original.roles[0].id + }, + validationSchema, + onSubmit: (values, {setSubmitting}) => { + const formData = new FormData(); + formData.append("full_name", values.full_name); + formData.append("national_id", values.national_id); + formData.append("phone_number", values.phone_number); + formData.append("telephone_id", values.telephone_id); + formData.append("gender", values.gender); + formData.append("email", values.email); + formData.append("username", values.username); + formData.append("province_id", values.province_id); + formData.append("position", values.position); + formData.append("roles", values.roles); + + requestServer(`${EXPERT_MANAGEMENT}/${row.original.id}`, 'post', {auth: true, data: formData}) + .then((response) => { + setOpenUpdateDialog(false) + mutate(fetchUrl) + }).catch(() => { + }).finally(() => { + setSubmitting(false); + }); + }, + }); + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default UpdateContent \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/__tests__/index.test.js b/src/components/dashboard/expert-management/Form/UpdateForm/__tests__/index.test.js new file mode 100644 index 0000000..f824d6d --- /dev/null +++ b/src/components/dashboard/expert-management/Form/UpdateForm/__tests__/index.test.js @@ -0,0 +1,147 @@ +import {act, fireEvent, render, screen, waitFor} from "@testing-library/react"; +import React from 'react'; +import MockAppWithProviders from "../../../../../../../mocks/AppWithProvider"; +import UpdateForm from "../../UpdateForm"; + +const row = { + original: { + avatar: null, + created_at: "2023-10-10T13:05:01.000000Z", + email: "fisher.sigrid@yahoo.com", + full_name: "Anibal Labadie", + gender: "female", + id: 85, + national_id: "5944646138", + phone_number: "09731311720", + position: "boss1", + province_fa: "اردبیل", + province_id: 3, + roles: [{ + created_at: "2023-10-10T13:03:18.000000Z", + guard_name: "api", + id: 2, + name: "manager", + name_fa: "مدیر" + }], + telephone_id: "tel-3110", + updated_at: "2023-10-17T10:57:12.000000Z", + username: "florence.kihn", + } +} + +function selectDropdownItem(screen, openerTestId, itemText) { + const opener = screen.getByTestId(openerTestId); + fireEvent.mouseDown(opener); + const selectItem = screen.queryByText(itemText); + fireEvent.click(selectItem); +} + +function setInputValue(screen, inputElement, value) { + fireEvent.change(inputElement, {target: {value}}); +} + +describe("CreateForm Component From Expert Management", () => { + describe("Rendering", () => { + it("Update Expert Button Rendered", () => { + render(); + const UpdateButton = screen.getByTestId('update-button'); + expect(UpdateButton).toBeInTheDocument(); + }); + }); + describe("Behavioral", () => { + it("by Clicking Update Button Dialog Should Append To Document", async () => { + render(); + const UpdateButton = screen.getByTestId('update-button'); + fireEvent.click(UpdateButton); + await act(() => { + const UpdateDialog = screen.getByRole('dialog', {name: "ویرایش کارشناس"}) + expect(UpdateDialog).toBeInTheDocument(); + }); + }); + }); + describe("Form Submission", () => { + it('Should request to api and if get success close dialog', async () => { + render(); + + const UpdateButton = screen.getByTestId('update-button'); + fireEvent.click(UpdateButton); + + const submitButton = screen.queryByText('ثبت'); + + const nameInput = screen.queryByLabelText('نام کامل'); + const usernameInput = screen.queryByLabelText('نام کاربری'); + const emailInput = screen.queryByLabelText('پست الکترونیک'); + const phoneNumberInput = screen.queryByLabelText('شماره همراه'); + const telephoneIdInput = screen.queryByLabelText('کد تلفن'); + const nationalIdInput = screen.queryByLabelText('کد ملی'); + const positionInput = screen.queryByLabelText('سمت'); + + selectDropdownItem(screen, "option-opener-province", "آذربایجان شرقی"); + selectDropdownItem(screen, "option-opener-role", "ادمین"); + selectDropdownItem(screen, "option-opener-gender", "مرد"); + + setInputValue(screen, nameInput, 'nameTest'); + setInputValue(screen, usernameInput, 'usernameTest'); + setInputValue(screen, emailInput, 'email@gmail.com'); + setInputValue(screen, phoneNumberInput, '0914577458'); + setInputValue(screen, telephoneIdInput, '091'); + setInputValue(screen, nationalIdInput, 'nationalIdTest'); + setInputValue(screen, positionInput, 'positionTest'); + + expect(submitButton).not.toBeDisabled(); + fireEvent.click(submitButton); + + await waitFor(() => { + const dialogContent = screen.queryByTestId('update-dialog-content'); + expect(dialogContent).not.toBeInTheDocument(); + }); + }); + it('Should request to api and if get error keep previous data', async () => { + render(); + + const UpdateButton = screen.getByTestId('update-button'); + fireEvent.click(UpdateButton); + + const submitButton = screen.queryByText('ثبت'); + + const nameInput = screen.queryByLabelText('نام کامل'); + const usernameInput = screen.queryByLabelText('نام کاربری'); + const emailInput = screen.queryByLabelText('پست الکترونیک'); + const phoneNumberInput = screen.queryByLabelText('شماره همراه'); + const telephoneIdInput = screen.queryByLabelText('کد تلفن'); + const nationalIdInput = screen.queryByLabelText('کد ملی'); + const positionInput = screen.queryByLabelText('سمت'); + const provinceInput = screen.getByTestId("input-province-id") + const roleInput = screen.getByTestId("input-role-id") + const genderInput = screen.getByTestId("input-gender") + + selectDropdownItem(screen, "option-opener-province", "آذربایجان شرقی"); + selectDropdownItem(screen, "option-opener-role", "ادمین"); + selectDropdownItem(screen, "option-opener-gender", "مرد"); + + setInputValue(screen, nameInput, 'nameTest'); + setInputValue(screen, usernameInput, 'usernameTest'); + setInputValue(screen, emailInput, 'emailTest@gmail.com'); + setInputValue(screen, phoneNumberInput, '0914577458'); + setInputValue(screen, telephoneIdInput, '091'); + setInputValue(screen, nationalIdInput, 'nationalIdTest'); + setInputValue(screen, positionInput, 'positionTest'); + + expect(submitButton).not.toBeDisabled(); + await fireEvent.click(submitButton); + + await waitFor(() => { + expect(nameInput).toHaveValue('nameTest'); + expect(usernameInput).toHaveValue('usernameTest'); + expect(emailInput).toHaveValue('emailTest@gmail.com'); + expect(phoneNumberInput).toHaveValue('0914577458'); + expect(telephoneIdInput).toHaveValue('091'); + expect(nationalIdInput).toHaveValue('nationalIdTest'); + expect(positionInput).toHaveValue('positionTest'); + expect(provinceInput).toHaveValue('1'); + expect(roleInput).toHaveValue('1'); + expect(genderInput).toHaveValue('male'); + }); + }); + }) +}); \ No newline at end of file diff --git a/src/components/dashboard/expert-management/Form/UpdateForm/index.jsx b/src/components/dashboard/expert-management/Form/UpdateForm/index.jsx index 59da7cd..55e7429 100644 --- a/src/components/dashboard/expert-management/Form/UpdateForm/index.jsx +++ b/src/components/dashboard/expert-management/Form/UpdateForm/index.jsx @@ -1,7 +1,33 @@ -const Update = () => { - return ( - <> - ) -} +import {useTranslations} from "next-intl"; +import {Dialog, DialogTitle, IconButton, Tooltip} from "@mui/material"; +import EditIcon from '@mui/icons-material/Edit'; +import {useState} from "react"; +import UpdateContent from "./UpdateContent"; -export default Update \ No newline at end of file + +const Update = ({row, fetchUrl, mutate}) => { + const t = useTranslations(); + const [openUpdateDialog, setOpenUpdateDialog] = useState(false); + + return ( + <> + + { + setOpenUpdateDialog(true) + }} + > + + + + + {t("ExpertMangement.update_expert")} + + + + ); +}; +export default Update; \ No newline at end of file diff --git a/src/lib/app/hooks/useProvince.jsx b/src/lib/app/hooks/useProvince.jsx index 359eff4..2bc8c6b 100644 --- a/src/lib/app/hooks/useProvince.jsx +++ b/src/lib/app/hooks/useProvince.jsx @@ -16,7 +16,8 @@ const useProvince = () => { const {data, isLoading} = useSWR(GET_PROVINCE_LIST, fetcher, { revalidateIfStale: false, revalidateOnFocus: false, - revalidateOnReconnect: false + revalidateOnReconnect: false, + keepPreviousData: true }); return { diff --git a/src/lib/app/hooks/useRole.jsx b/src/lib/app/hooks/useRole.jsx index fc3dd18..b4c231c 100644 --- a/src/lib/app/hooks/useRole.jsx +++ b/src/lib/app/hooks/useRole.jsx @@ -16,7 +16,8 @@ const useRole = () => { const {data, isLoading} = useSWR(GET_ROLE_LIST, fetcher, { revalidateIfStale: false, revalidateOnFocus: false, - revalidateOnReconnect: false + revalidateOnReconnect: false, + keepPreviousData: true }); return {