From eb7b68ce4868bf89c777c975a96dd7b67b0d9a3e Mon Sep 17 00:00:00 2001 From: Yaroslav Chuiko <32570823+YaroslavChuiko@users.noreply.github.com> Date: Thu, 26 Sep 2024 18:42:53 +0300 Subject: [PATCH] Binded confirmation modal on resources edit (#2554) * binded confirmation modal on resources edit * deleted skip test * fixed test * increased tests coverage --- .../ChangeResourceConfirmModal.tsx | 15 ++- .../course-section/CourseSectionContainer.tsx | 19 +++- .../my-quizzes/QuizzesContainer.tsx | 22 ++++- .../CreateOrEditQuizContainer.tsx | 26 +----- .../AttachmentsContainer.tsx | 20 +++- .../lessons-container/LessonsContainer.tsx | 22 ++++- .../CreateOrEditLesson.tsx | 24 +---- src/pages/lesson-details/LessonDetails.tsx | 17 +++- .../AttachmentsContainer.spec.jsx | 91 ++++++++----------- .../my-quizzes/QuizzesContainer.spec.jsx | 44 ++++++++- .../my-resources/LessonsContainer.spec.jsx | 69 ++++++-------- .../CreateOrEditLessonWithId.spec.jsx | 2 +- .../lesson-details/LessonDetails.spec.jsx | 10 +- 13 files changed, 215 insertions(+), 166 deletions(-) diff --git a/src/containers/change-resource-confirm-modal/ChangeResourceConfirmModal.tsx b/src/containers/change-resource-confirm-modal/ChangeResourceConfirmModal.tsx index 0a0f01fbc..76111cc75 100644 --- a/src/containers/change-resource-confirm-modal/ChangeResourceConfirmModal.tsx +++ b/src/containers/change-resource-confirm-modal/ChangeResourceConfirmModal.tsx @@ -1,3 +1,4 @@ +import { useCallback, useEffect, useMemo } from 'react' import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' import { useTranslation } from 'react-i18next' @@ -17,7 +18,6 @@ import { useModalContext } from '~/context/modal-context' import Loader from '~/components/loader/Loader' import useAxios from '~/hooks/use-axios' import { CourseService } from '~/services/course-service' -import { useMemo } from 'react' interface ChangeResourceConfirmModalProps { resourceId?: string @@ -61,10 +61,16 @@ const ChangeResourceConfirmModal = ({ ) ////////////////////////////////////! - const handleConfirm = () => { - onConfirm?.() + const handleConfirm = useCallback(() => { closeModal() - } + onConfirm?.() + }, [closeModal, onConfirm]) + + useEffect(() => { + if (!loading && !courseList?.length) { + handleConfirm() + } + }, [courseList, handleConfirm, loading]) if (loading) { return ( @@ -75,7 +81,6 @@ const ChangeResourceConfirmModal = ({ } if (!loading && !courseList?.length) { - handleConfirm() return null } diff --git a/src/containers/course-section/CourseSectionContainer.tsx b/src/containers/course-section/CourseSectionContainer.tsx index 09414ba0f..8dc05fbae 100644 --- a/src/containers/course-section/CourseSectionContainer.tsx +++ b/src/containers/course-section/CourseSectionContainer.tsx @@ -54,6 +54,7 @@ import { useModalContext } from '~/context/modal-context' import useAxios from '~/hooks/use-axios' import useMenu from '~/hooks/use-menu' +import ChangeResourceConfirmModal from '../change-resource-confirm-modal/ChangeResourceConfirmModal' interface SectionProps extends CourseSectionHandlers { sectionData: CourseSection @@ -173,12 +174,22 @@ const CourseSectionContainer: FC = ({ if (!resourceType) return if (resourceType === ResourceType.Attachment) { + const handleConfirm = () => + openModal({ + component: ( + + ) + }) openModal({ component: ( - ) }) diff --git a/src/containers/my-quizzes/QuizzesContainer.tsx b/src/containers/my-quizzes/QuizzesContainer.tsx index 121d3a311..771ff236c 100644 --- a/src/containers/my-quizzes/QuizzesContainer.tsx +++ b/src/containers/my-quizzes/QuizzesContainer.tsx @@ -12,6 +12,7 @@ import useBreakpoints from '~/hooks/use-breakpoints' import useAxios from '~/hooks/use-axios' import usePagination from '~/hooks/table/use-pagination' import { authRoutes } from '~/router/constants/authRoutes' +import { useModalContext } from '~/context/modal-context' import { defaultResponses, snackbarVariants } from '~/constants' import { @@ -35,6 +36,7 @@ import { } from '~/utils/helper-functions' import { openAlert } from '~/redux/features/snackbarSlice' import { getErrorKey } from '~/utils/get-error-key' +import ChangeResourceConfirmModal from '../change-resource-confirm-modal/ChangeResourceConfirmModal' const QuizzesContainer = () => { const dispatch = useAppDispatch() @@ -44,6 +46,7 @@ const QuizzesContainer = () => { const searchTitle = useRef('') const breakpoints = useBreakpoints() const [selectedItems, setSelectedItems] = useState([]) + const { openModal } = useModalContext() const { sort } = sortOptions const itemsPerPage = getScreenBasedLimit(breakpoints, itemsLoadLimit) @@ -82,10 +85,6 @@ const QuizzesContainer = () => { [] ) - const onEdit = (id: string) => { - navigate(createUrlPath(authRoutes.myResources.editQuiz.path, id)) - } - const { response, loading, fetchData } = useAxios< ItemsWithCount, GetResourcesParams @@ -95,6 +94,21 @@ const QuizzesContainer = () => { onResponseError }) + const onEdit = (id: string) => { + const resource = response.items.find((item) => item._id === id) + openModal({ + component: ( + + navigate(createUrlPath(authRoutes.myResources.editQuiz.path, id)) + } + resourceId={id} + title={resource?.title} + /> + ) + }) + } + const props = { columns: columnsToShow, data: { response, getData: fetchData }, diff --git a/src/containers/my-quizzes/create-or-edit-quiz-container/CreateOrEditQuizContainer.tsx b/src/containers/my-quizzes/create-or-edit-quiz-container/CreateOrEditQuizContainer.tsx index 67d8f716c..e63fdc859 100644 --- a/src/containers/my-quizzes/create-or-edit-quiz-container/CreateOrEditQuizContainer.tsx +++ b/src/containers/my-quizzes/create-or-edit-quiz-container/CreateOrEditQuizContainer.tsx @@ -1,10 +1,4 @@ -import { - ChangeEvent, - MouseEventHandler, - useCallback, - useEffect, - useState -} from 'react' +import { ChangeEvent, useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useNavigate, useParams } from 'react-router-dom' import { AxiosResponse } from 'axios' @@ -56,7 +50,6 @@ import { createUrlPath } from '~/utils/helper-functions' import { styles } from '~/containers/my-quizzes/create-or-edit-quiz-container/CreateOrEditQuizContainer.styles' import { openAlert } from '~/redux/features/snackbarSlice' import { getErrorKey } from '~/utils/get-error-key' -import ChangeResourceConfirmModal from '~/containers/change-resource-confirm-modal/ChangeResourceConfirmModal' const CreateOrEditQuizContainer = ({ title, @@ -222,21 +215,6 @@ const CreateOrEditQuizContainer = ({ resourceType: ResourceType.Quiz }) - const openChangeResourceConfirmModal: MouseEventHandler = ( - e - ) => { - e.preventDefault() - openModal({ - component: ( - onSaveQuiz()} - resourceId={id} - title={title} - /> - ) - }) - } - if (getQuizLoading) { return } @@ -322,7 +300,7 @@ const CreateOrEditQuizContainer = ({ {t('common.cancel')} diff --git a/src/containers/my-resources/attachments-container/AttachmentsContainer.tsx b/src/containers/my-resources/attachments-container/AttachmentsContainer.tsx index 89544c9c8..62e02215b 100644 --- a/src/containers/my-resources/attachments-container/AttachmentsContainer.tsx +++ b/src/containers/my-resources/attachments-container/AttachmentsContainer.tsx @@ -36,6 +36,7 @@ import { styles } from '~/containers/my-resources/attachments-container/Attachme import { useAppDispatch } from '~/hooks/use-redux' import { openAlert } from '~/redux/features/snackbarSlice' import { getErrorKey } from '~/utils/get-error-key' +import ChangeResourceConfirmModal from '~/containers/change-resource-confirm-modal/ChangeResourceConfirmModal' const AttachmentsContainer = () => { const { t } = useTranslation() @@ -137,12 +138,23 @@ const AttachmentsContainer = () => { const onEdit = (id: string) => { const attachment = response.items.find((item) => item._id === id) + const handleConfirm = () => + openModal({ + component: ( + + ) + }) + openModal({ component: ( - ) }) diff --git a/src/containers/my-resources/lessons-container/LessonsContainer.tsx b/src/containers/my-resources/lessons-container/LessonsContainer.tsx index 51b6e4a01..d834a2eb6 100644 --- a/src/containers/my-resources/lessons-container/LessonsContainer.tsx +++ b/src/containers/my-resources/lessons-container/LessonsContainer.tsx @@ -34,10 +34,13 @@ import { import { useAppDispatch } from '~/hooks/use-redux' import { openAlert } from '~/redux/features/snackbarSlice' import { getErrorKey } from '~/utils/get-error-key' +import { useModalContext } from '~/context/modal-context' +import ChangeResourceConfirmModal from '~/containers/change-resource-confirm-modal/ChangeResourceConfirmModal' const LessonsContainer = () => { const dispatch = useAppDispatch() const navigate = useNavigate() + const { openModal } = useModalContext() const { page, handleChangePage } = usePagination() const sortOptions = useSort({ initialSort }) const searchTitle = useRef('') @@ -81,10 +84,6 @@ const LessonsContainer = () => { [] ) - const onEdit = (id: string) => { - navigate(createUrlPath(authRoutes.myResources.editLesson.path, id)) - } - const { response, loading, fetchData } = useAxios< ItemsWithCount, GetResourcesParams @@ -94,6 +93,21 @@ const LessonsContainer = () => { onResponseError }) + const onEdit = (id: string) => { + const resource = response.items.find((item) => item._id === id) + openModal({ + component: ( + + navigate(createUrlPath(authRoutes.myResources.editLesson.path, id)) + } + resourceId={id} + title={resource?.title} + /> + ) + }) + } + const props = { columns: columnsToShow, data: { response, getData: fetchData }, diff --git a/src/pages/create-or-edit-lesson/CreateOrEditLesson.tsx b/src/pages/create-or-edit-lesson/CreateOrEditLesson.tsx index 59970bdb3..56ef8bd96 100644 --- a/src/pages/create-or-edit-lesson/CreateOrEditLesson.tsx +++ b/src/pages/create-or-edit-lesson/CreateOrEditLesson.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, SyntheticEvent, useEffect } from 'react' +import { SyntheticEvent, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { useNavigate, useParams } from 'react-router-dom' import { AxiosResponse } from 'axios' @@ -51,7 +51,6 @@ import { } from '~/types' import { openAlert } from '~/redux/features/snackbarSlice' import { getErrorKey } from '~/utils/get-error-key' -import ChangeResourceConfirmModal from '~/containers/change-resource-confirm-modal/ChangeResourceConfirmModal' const CreateOrEditLesson = () => { const { t } = useTranslation() @@ -192,21 +191,6 @@ const CreateOrEditLesson = () => { onResponseError: handleResponseError }) - const openChangeResourceConfirmModal: MouseEventHandler = ( - e - ) => { - e.preventDefault() - openModal({ - component: ( - handleSubmit()} - resourceId={id} - title={data.title} - /> - ) - }) - } - useEffect(() => { if (id) { void fetchDataLesson(id) @@ -282,11 +266,7 @@ const CreateOrEditLesson = () => { > {t('common.cancel')} - + {t('common.save')} diff --git a/src/pages/lesson-details/LessonDetails.tsx b/src/pages/lesson-details/LessonDetails.tsx index e7d298225..e3c393bb1 100644 --- a/src/pages/lesson-details/LessonDetails.tsx +++ b/src/pages/lesson-details/LessonDetails.tsx @@ -24,12 +24,15 @@ import { styles } from '~/pages/lesson-details/LessonsDetails.styles' import { Lesson, TypographyVariantEnum } from '~/types' import { createUrlPath } from '~/utils/helper-functions' import { useAppSelector } from '~/hooks/use-redux' +import { useModalContext } from '~/context/modal-context' +import ChangeResourceConfirmModal from '~/containers/change-resource-confirm-modal/ChangeResourceConfirmModal' const LessonDetails = () => { const { lessonId } = useParams() const navigate = useNavigate() const { t } = useTranslation() const { userId } = useAppSelector((state) => state.appMain) + const { openModal } = useModalContext() const [expandedItems, handleAccordionChange] = useAccordion({ initialState: 0, @@ -56,7 +59,19 @@ const LessonDetails = () => { } const handleEditLesson = () => { - navigate(createUrlPath(authRoutes.myResources.editLesson.path, lessonId)) + openModal({ + component: ( + + navigate( + createUrlPath(authRoutes.myResources.editLesson.path, lessonId) + ) + } + resourceId={lessonId} + title={response.title} + /> + ) + }) } const attachmentsList = response.attachments.map((attachment) => ( diff --git a/tests/unit/containers/AttachmentsContainer/AttachmentsContainer.spec.jsx b/tests/unit/containers/AttachmentsContainer/AttachmentsContainer.spec.jsx index 8e886e681..818bd612b 100644 --- a/tests/unit/containers/AttachmentsContainer/AttachmentsContainer.spec.jsx +++ b/tests/unit/containers/AttachmentsContainer/AttachmentsContainer.spec.jsx @@ -3,6 +3,33 @@ import AttachmentsContainer from '~/containers/my-resources/attachments-containe import { mockAxiosClient, renderWithProviders } from '~tests/test-utils' import { URLs } from '~/constants/request' +vi.mock( + '~/containers/my-resources/my-resources-table/MyResourcesTable', + () => ({ + default: ({ actions }) => ( +
+ +
+ ) + }) +) + +vi.mock( + '~/containers/my-resources/edit-attachment-modal/EditAttachmentModal', + () => ({ + default: () =>
+ }) +) + +vi.mock( + '~/containers/change-resource-confirm-modal/ChangeResourceConfirmModal', + () => ({ + default: () =>
+ }) +) + const attachmentDataMock = { _id: '64cd12f1fad091e0ee719830', author: '6494128829631adbaf5cf615', @@ -27,20 +54,6 @@ const attachmentMockData = { items: responseItemsMock } -const responseItemsMockCategory = Array(20) - .fill() - .map((_, index) => ({ - ...attachmentDataMock, - category: { id: '64fb2c33eba89699411d22bb', name: 'New Category' }, - _id: `${index}`, - fileName: index + attachmentDataMock.fileName - })) - -const attachmentMockDataCategory = { - count: 20, - items: responseItemsMockCategory -} - describe('AttachmentContainer renders correct data', () => { beforeEach(async () => { await waitFor(() => { @@ -56,53 +69,25 @@ describe('AttachmentContainer renders correct data', () => { mockAxiosClient.reset() }) - it('should render table', async () => { - const title = await screen.findByText( - 'myResourcesPage.attachments.attachmentName' - ) + it('should render "New attachment" button', () => { + const addBtn = screen.getByText('myResourcesPage.attachments.addBtn') - expect(title).toBeInTheDocument() + expect(addBtn).toBeInTheDocument() }) - it('should correctly shows filename of attachment', async () => { - const fileName = await screen.findByText('1spanish.pdf') + it('should render table with questions', async () => { + const table = await screen.findByTestId('testTable') - expect(fileName).toBeInTheDocument() + expect(table).toBeInTheDocument() }) - it('should show pagination', async () => { - const secondButton = await screen.findByLabelText('Go to page 2') + it('should run onEdit action', async () => { + const editButton = await screen.findByTestId('editButton') - expect(secondButton).not.toHaveAttribute('aria-current') + fireEvent.click(editButton) - waitFor(() => { - fireEvent.click(secondButton) - }) - - expect(secondButton).toHaveAttribute('aria-current', 'true') - }) -}) - -describe('QuestionsContainer test', () => { - beforeEach(async () => { - await waitFor(() => { - mockAxiosClient - .onGet(URLs.resources.attachments.get) - .reply(200, attachmentMockDataCategory) - renderWithProviders() - }) - }) - - afterEach(() => { - vi.clearAllMocks() - mockAxiosClient.reset() - }) - - it('should render correct category', async () => { - const category = await screen.findByText( - 'myResourcesPage.categories.category' - ) + const modal = await screen.findByTestId('confirmModal') - expect(category).toBeInTheDocument() + expect(modal).toBeInTheDocument() }) }) diff --git a/tests/unit/containers/my-quizzes/QuizzesContainer.spec.jsx b/tests/unit/containers/my-quizzes/QuizzesContainer.spec.jsx index ffee7ee85..febd057c8 100644 --- a/tests/unit/containers/my-quizzes/QuizzesContainer.spec.jsx +++ b/tests/unit/containers/my-quizzes/QuizzesContainer.spec.jsx @@ -1,4 +1,4 @@ -import { screen, waitFor } from '@testing-library/react' +import { fireEvent, screen, waitFor } from '@testing-library/react' import QuizzesContainer from '~/containers/my-quizzes/QuizzesContainer' import { mockAxiosClient, @@ -7,6 +7,26 @@ import { } from '~tests/test-utils' import { URLs } from '~/constants/request' +vi.mock( + '~/containers/my-resources/my-resources-table/MyResourcesTable', + () => ({ + default: ({ actions }) => ( +
+ +
+ ) + }) +) + +vi.mock( + '~/containers/change-resource-confirm-modal/ChangeResourceConfirmModal', + () => ({ + default: () =>
+ }) +) + const quizzesMock = { _id: '64ca5914b57f2442403394a5', title: 'First question', @@ -52,10 +72,26 @@ describe('QuizzesContainer component with data', () => { mockAxiosClient.reset() }) - it('should render table', async () => { - const title = await screen.findByText(responseQuizzesMock.items[0].title) + it('should render "New quiz" button', () => { + const addBtn = screen.getByText('myResourcesPage.quizzes.addBtn') + + expect(addBtn).toBeInTheDocument() + }) + + it('should render table with questions', async () => { + const table = await screen.findByTestId('testTable') + + expect(table).toBeInTheDocument() + }) + + it('should run onEdit action', async () => { + const editButton = await screen.findByTestId('editButton') + + fireEvent.click(editButton) + + const modal = await screen.findByTestId('confirmModal') - expect(title).toBeInTheDocument() + expect(modal).toBeInTheDocument() }) }) diff --git a/tests/unit/containers/my-resources/LessonsContainer.spec.jsx b/tests/unit/containers/my-resources/LessonsContainer.spec.jsx index a911bddb1..1e0a1d8d4 100644 --- a/tests/unit/containers/my-resources/LessonsContainer.spec.jsx +++ b/tests/unit/containers/my-resources/LessonsContainer.spec.jsx @@ -1,10 +1,30 @@ -import { screen, waitFor } from '@testing-library/react' +import { fireEvent, screen, waitFor } from '@testing-library/react' import LessonsContainer from '~/containers/my-resources/lessons-container/LessonsContainer' import { mockAxiosClient, renderWithProviders } from '~tests/test-utils' import { URLs } from '~/constants/request' +vi.mock( + '~/containers/my-resources/my-resources-table/MyResourcesTable', + () => ({ + default: ({ actions }) => ( +
+ +
+ ) + }) +) + +vi.mock( + '~/containers/change-resource-confirm-modal/ChangeResourceConfirmModal', + () => ({ + default: () =>
+ }) +) + const lessonMock = { _id: '64e49ce305b3353b2ae6309e', author: '648afee884936e09a37deaaa', @@ -28,20 +48,6 @@ const lessonResponseMock = { items: responseItemsMock } -const responseItemsMockCategory = Array(10) - .fill() - .map((_, index) => ({ - ...lessonMock, - category: { id: '64fb2c33eba89699411d22bb', name: 'New Category' }, - _id: `${index}`, - title: index + lessonMock.title - })) - -const lessonResponseMockCategory = { - count: 10, - items: responseItemsMockCategory -} - describe('LessonContainer test', () => { beforeEach(async () => { await waitFor(() => { @@ -62,35 +68,20 @@ describe('LessonContainer test', () => { expect(addBtn).toBeInTheDocument() }) - it('should render table with lessons', async () => { - const columnLabel = await screen.findByText('myResourcesPage.lessons.title') - const lessonTitle = await screen.findByText(responseItemsMock[5].title) - expect(columnLabel).toBeInTheDocument() - expect(lessonTitle).toBeInTheDocument() - }) -}) + it('should render table with questions', async () => { + const table = await screen.findByTestId('testTable') -describe('Lessons category test', () => { - beforeEach(async () => { - await waitFor(() => { - mockAxiosClient - .onGet(URLs.resources.lessons.get) - .reply(200, lessonResponseMockCategory) - renderWithProviders() - }) + expect(table).toBeInTheDocument() }) - afterEach(() => { - vi.clearAllMocks() - mockAxiosClient.reset() - }) + it('should run onEdit action', async () => { + const editButton = await screen.findByTestId('editButton') - it('should render correct category', async () => { - const category = await screen.findByText( - 'myResourcesPage.categories.category' - ) + fireEvent.click(editButton) + + const modal = await screen.findByTestId('confirmModal') - expect(category).toBeInTheDocument() + expect(modal).toBeInTheDocument() }) }) diff --git a/tests/unit/pages/create-or-edit-lesson/CreateOrEditLessonWithId.spec.jsx b/tests/unit/pages/create-or-edit-lesson/CreateOrEditLessonWithId.spec.jsx index 923c2defa..f0125122b 100644 --- a/tests/unit/pages/create-or-edit-lesson/CreateOrEditLessonWithId.spec.jsx +++ b/tests/unit/pages/create-or-edit-lesson/CreateOrEditLessonWithId.spec.jsx @@ -40,7 +40,7 @@ describe('CreateOrEditLesson component with id', () => { expect(titleInput).toBeInTheDocument() }) - it.skip('should edit a lesson', async () => { + it('should edit a lesson', async () => { mockAxiosClient.onPatch(URLs.resources.lessons.patch).reply(200) const editLessonSpy = vi.spyOn(ResourceService, 'editLesson') const titleInput = await screen.findByDisplayValue(mockLesson.title) diff --git a/tests/unit/pages/lesson-details/LessonDetails.spec.jsx b/tests/unit/pages/lesson-details/LessonDetails.spec.jsx index 6fc983031..fa60ea9ce 100644 --- a/tests/unit/pages/lesson-details/LessonDetails.spec.jsx +++ b/tests/unit/pages/lesson-details/LessonDetails.spec.jsx @@ -19,6 +19,13 @@ vi.mock('react-router-dom', async () => { } }) +vi.mock( + '~/containers/change-resource-confirm-modal/ChangeResourceConfirmModal', + () => ({ + default: () =>
+ }) +) + const userId = '6477007a6fa4d05e1a800ce5' const mockState = { appMain: { userId: userId, userRole: 'tutor' } @@ -90,7 +97,8 @@ describe('LessonDetails', () => { const editButton = screen.getByText('common.edit') fireEvent.click(editButton) + const modal = await screen.findByTestId('testModal') - expect(mockNavigate).toHaveBeenCalled() + expect(modal).toBeInTheDocument() }) })