From aab7c8cd336989e29b2ecd3d19bc78ced7bb0123 Mon Sep 17 00:00:00 2001 From: DDeviak <117632082+DDeviak@users.noreply.github.com> Date: Thu, 22 Aug 2024 01:39:22 +0300 Subject: [PATCH] feat: Extended unit tests for NewsModal component --- __mocks__/@stores/root-store.tsx | 2 +- .../NewsModal/NewsModal.component.spec.tsx | 177 +++++++++++------- 2 files changed, 106 insertions(+), 73 deletions(-) diff --git a/__mocks__/@stores/root-store.tsx b/__mocks__/@stores/root-store.tsx index a28ff9c59..8c2bbe282 100644 --- a/__mocks__/@stores/root-store.tsx +++ b/__mocks__/@stores/root-store.tsx @@ -32,7 +32,7 @@ export const useMobx = () => ({ newsStore: { updateNews: mockUpdateNews, createNews: mockCreateNews, - getNewsArray: [ + NewsArray: [ { id: 1, title: 'title', diff --git a/src/features/AdminPage/NewsPage/NewsModal/NewsModal.component.spec.tsx b/src/features/AdminPage/NewsPage/NewsModal/NewsModal.component.spec.tsx index 1228cc38c..7a0e3fe63 100644 --- a/src/features/AdminPage/NewsPage/NewsModal/NewsModal.component.spec.tsx +++ b/src/features/AdminPage/NewsPage/NewsModal/NewsModal.component.spec.tsx @@ -1,14 +1,18 @@ /* eslint-disable no-restricted-syntax */ /* eslint-disable no-restricted-imports */ -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import dayjs from 'dayjs'; import '@testing-library/jest-dom/extend-expect'; - -import UploadMock from '../../../../../__mocks__/antd/es/upload/upload'; +import 'jest-canvas-mock'; import NewsModal from './NewsModal.component'; +import Image, { ImageCreate } from '@/models/media/image.model'; +import { useEffect } from 'react'; +import { Form } from 'antd'; +import { mockCreateNews, mockUpdateNews } from '../../../../../__mocks__/@stores/root-store'; +import News from '@/models/news/news.model'; Object.defineProperty(window, 'matchMedia', { writable: true, @@ -16,19 +20,52 @@ Object.defineProperty(window, 'matchMedia', { matches: false, media: query, onchange: null, - addListener: () => {}, - removeListener: () => {}, - addEventListener: () => {}, - removeEventListener: () => {}, - dispatchEvent: () => {}, + addListener: () => { }, + removeListener: () => { }, + addEventListener: () => { }, + removeEventListener: () => { }, + dispatchEvent: () => { }, }), }); - -jest.mock('antd/es/upload', () => ({ - __esModule: true, - default: UploadMock, +window.URL.createObjectURL = jest.fn(); + +jest.mock("@/app/api/media/images.api", () => ({ + create: (image: ImageCreate) => ( + Promise.resolve({ + id: 999, + base64: image.baseFormat, + blobName: image.title, + mimeType: image.mimeType, + alt: image.alt + } as Image) + ), })); +jest.mock('antd', () => { + const antd = jest.requireActual('antd'); + const message = antd; + + return { + ...antd, + DatePicker: (props: any) => { + const form = Form.useFormInstance(); + useEffect(() => { + form.setFieldsValue({ + creationDate: "2000-01-01", + }); + }, []); + return ; + }, + message: { + ...message, + loading: jest.fn(), + success: jest.fn(), + config: jest.fn(), + error: jest.fn(), + }, + }; +}); + jest.mock('@/app/common/components/Editor/QEditor.component', () => ({ __esModule: true, default: jest.fn((props) => { @@ -50,9 +87,29 @@ jest.mock('@/app/common/components/Editor/QEditor.component', () => ({ })); describe('NewsModal', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + cleanup(); + }); + it('should render component', () => { const setIsModalOpen = jest.fn(); render(); + + expect(screen.getByRole('button', { name: /Зберегти/i })).toBeDisabled(); + }); + + it('should close when clicked on close button', () => { + const setIsModalOpen = jest.fn(); + render(); + + const closeButton = screen.getByRole('button', { name: /Close/i }); + userEvent.click(closeButton); + + expect(setIsModalOpen).toHaveBeenCalled(); }); it('should be filled with required values and submited', async () => { @@ -70,49 +127,33 @@ describe('NewsModal', () => { const titleInput = screen.getByLabelText('Заголовок:') as HTMLInputElement; const urlInput = screen.getByLabelText('Посилання:') as HTMLInputElement; const textInput = screen.getByTestId('mockEditor') as HTMLTextAreaElement; - const dateInput = screen.getByLabelText( - 'Дата створення:', - ) as HTMLInputElement; - const fileUpload = screen.getByTestId('file-input') as HTMLInputElement; + const fileUpload = screen.getByTestId('fileuploader') as HTMLInputElement; + const button = screen.getByRole('button', { name: 'Зберегти' }); const file = new File(['test'], 'test.png', { type: 'image/png' }); - const dateValue = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss'); await waitFor(() => { userEvent.type(titleInput, 'Test Title'); - userEvent.type(urlInput, 'Test Url'); + userEvent.type(urlInput, 'testurl'); userEvent.type(textInput, 'This is a test text'); userEvent.upload(fileUpload, file); - fireEvent.mouseDown(dateInput); - fireEvent.change(dateInput, { - target: { value: dateValue }, - }); }); + await act(async () => { await new Promise((r) => setTimeout(r, 2000)) }); // There is no need to repeat this part of code for edit test (logic is the same). // Once here is enough to check that we don`t submit empty strings. expect(titleInput).toHaveValue('Test Title'); - expect(urlInput).toHaveValue('Test Url'); + expect(urlInput).toHaveValue('testurl'); expect(textInput).toHaveValue('This is a test text'); - expect(dateInput).toHaveValue(dateValue); if (fileUpload.files) expect(fileUpload.files[0]).toStrictEqual(file); - const newsToCreate = { - title: titleInput.value, - url: urlInput.value, - text: textInput.value, - creationDate: dateInput.value, - }; - - afterSubmitMock(newsToCreate); + userEvent.click(button); await waitFor(() => { + expect(mockCreateNews).toHaveBeenCalled(); + expect(mockUpdateNews).not.toHaveBeenCalled(); expect(afterSubmitMock).toHaveBeenCalled(); }); - - await waitFor(() => { - expect(afterSubmitMock).toHaveBeenCalledWith(newsToCreate); - }); }, 10000); it('should not submit when required fields are empty', async () => { @@ -147,12 +188,12 @@ describe('NewsModal', () => { }); it('should truncate inputs when exceeding maximum characters/files', async () => { - render( {}} />); + render( { }} />); const titleInput = screen.getByLabelText('Заголовок:') as HTMLInputElement; const urlInput = screen.getByLabelText('Посилання:') as HTMLInputElement; const textInput = screen.getByTestId('mockEditor') as HTMLTextAreaElement; - const fileUpload = screen.getByTestId('file-input') as HTMLInputElement; + const fileUpload = screen.getByTestId('fileuploader') as HTMLInputElement; const a = 'uxlprrbyrugcjgplxivhpsducilsafcnheueosipnqutahqdgss'; const tooLongTitle = a.repeat(2); @@ -201,12 +242,11 @@ describe('NewsModal', () => { const titleInput = screen.getByLabelText('Заголовок:') as HTMLInputElement; const urlInput = screen.getByLabelText('Посилання:') as HTMLInputElement; - const dateInput = screen.getByLabelText( - 'Дата створення:', - ) as HTMLInputElement; + const dateInput = screen.getByRole('textbox', { name: "date" }) as HTMLInputElement; + const button = screen.getByRole('button', { name: 'Зберегти' }); userEvent.clear(titleInput); - userEvent.type(titleInput, 'Updated Title'); + userEvent.type(titleInput, 'Test Title'); userEvent.clear(urlInput); userEvent.type(urlInput, 'updated-url'); @@ -216,24 +256,19 @@ describe('NewsModal', () => { fireEvent.change(dateInput, { target: { value: dateValue }, }); + const file = new File(['test'], 'test.png', { type: 'image/png' }); + const fileUpload = screen.getByTestId('fileuploader') as HTMLInputElement; + userEvent.upload(fileUpload, file); + await act(async () => { await new Promise((r) => setTimeout(r, 2000)) }); - const updatedNewsItem = { - ...newsToEdit, - title: titleInput.value, - url: urlInput.value, - creationDate: dateInput.value, - }; - - afterSubmitMock(updatedNewsItem); + userEvent.click(button); await waitFor(() => { + expect(mockUpdateNews).toHaveBeenCalled(); + expect(mockCreateNews).not.toHaveBeenCalled(); expect(afterSubmitMock).toHaveBeenCalled(); }); - - await waitFor(() => { - expect(afterSubmitMock).toHaveBeenCalledWith(updatedNewsItem); - }); - }); + }, 10000); it('should update existing news when required fields match', async () => { const existingNews = [ @@ -265,10 +300,8 @@ describe('NewsModal', () => { const titleInput = screen.getByLabelText('Заголовок:') as HTMLInputElement; const urlInput = screen.getByLabelText('Посилання:') as HTMLInputElement; const textInput = screen.getByTestId('mockEditor') as HTMLTextAreaElement; - const dateInput = screen.getByLabelText( - 'Дата створення:', - ) as HTMLInputElement; - const fileUpload = screen.getByTestId('file-input') as HTMLInputElement; + const dateInput = screen.getByRole('textbox', { name: "date" }) as HTMLInputElement; + const fileUpload = screen.getByTestId('fileuploader') as HTMLInputElement; await waitFor(() => { userEvent.clear(titleInput); @@ -287,19 +320,19 @@ describe('NewsModal', () => { }; const newFields: { - title: string; - url: string; - date: string; - text: string; - image: string; - [key: string]: string; - } = { - title: titleInput.value, - url: urlInput.value, - date: dateInput.value, - text: textInput.value, - image: fileUpload.value, - }; + title: string; + url: string; + date: string; + text: string; + image: string; + [key: string]: string; + } = { + title: titleInput.value, + url: urlInput.value, + date: dateInput.value, + text: textInput.value, + image: fileUpload.value, + }; let newFieldsWithoutId: any;