diff --git a/__mocks__/db/users.ts b/__mocks__/db/users.ts index 554431b46..b90eb8616 100644 --- a/__mocks__/db/users.ts +++ b/__mocks__/db/users.ts @@ -4,7 +4,7 @@ const usersData = [ 'incompleteUserDetails': false, 'website': '.', 'roles': { - 'archived': false + 'archived': false, }, 'linkedin_id': '.', 'last_name': 'Musab', @@ -193,6 +193,27 @@ const usersData = [ 'first_name': 'Arpit', 'username': 'Arpit_02' }, + { + 'id': 'iMxYUdnETvu8JOmDq03p', + 'incompleteUserDetails': false, + 'website': '.', + 'roles': { + 'archived': false, + 'admin': true, + 'super_user': true, + }, + 'linkedin_id': '.', + 'last_name': 'Musab', + 'yoe': 1, + 'instagram_id': '.', + 'github_display_name': null, + 'company': 'NUST', + 'github_id': 'Muhammad-Musab', + 'designation': 'student', + 'twitter_id': 'direksh', + 'first_name': 'Muhammad', + 'username': 'muhammadmusab' + }, ]; export default usersData; \ No newline at end of file diff --git a/__mocks__/handlers.ts b/__mocks__/handlers.ts index 5c00a5993..98c6149e8 100644 --- a/__mocks__/handlers.ts +++ b/__mocks__/handlers.ts @@ -6,7 +6,6 @@ import levelsHandler from './handlers/levels.handler'; import usersHandler from './handlers/users.handler'; import taskTagsHandler from './handlers/taskTags.handler'; import { taskDetailsHandler } from './handlers/task-details.handler'; -import userHandler from './handlers/user.handler'; import issuesHandler from './handlers/issues.handler'; import standupHandler from './handlers/standup.handler'; import { prsHandler } from './handlers/pull-requests.handler'; @@ -24,7 +23,6 @@ const handlers = [ ...usersHandler, ...taskTagsHandler, ...taskDetailsHandler, - ...userHandler, ...issuesHandler, ...standupHandler, ...prsHandler, diff --git a/__mocks__/handlers/self.handler.js b/__mocks__/handlers/self.handler.js index e561f9da1..f6c94e7ce 100644 --- a/__mocks__/handlers/self.handler.js +++ b/__mocks__/handlers/self.handler.js @@ -1,3 +1,5 @@ +import { USER_SELF } from '@/constants/url'; +import usersData from '../../__mocks__/db/users'; import { rest } from 'msw'; const URL = process.env.NEXT_PUBLIC_BASE_URL; @@ -29,4 +31,10 @@ const selfHandler = [ }), ]; +export const adminUserHandler = [ + rest.get(USER_SELF, (_, res, ctx) => { + return res(ctx.status(200), ctx.json(usersData[11])); + }), +]; + export default selfHandler; diff --git a/__mocks__/handlers/user.handler.ts b/__mocks__/handlers/user.handler.ts deleted file mode 100644 index 7e99b6aae..000000000 --- a/__mocks__/handlers/user.handler.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { USERS_URL, USER_SELF } from '@/constants/url'; -import usersData from '../db/users'; -import { rest } from 'msw'; - -const userHandler = [ - rest.get(USER_SELF, (_, res, ctx) => { - return res(ctx.status(200), ctx.json(usersData[0])); - }), -]; - -export default userHandler; diff --git a/__tests__/Unit/Components/Tasks/Card.test.tsx b/__tests__/Unit/Components/Tasks/Card.test.tsx index 439573873..e4549dd5c 100644 --- a/__tests__/Unit/Components/Tasks/Card.test.tsx +++ b/__tests__/Unit/Components/Tasks/Card.test.tsx @@ -1,4 +1,5 @@ -import { fireEvent, screen } from '@testing-library/react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { act } from '@testing-library/react-hooks'; import Card from '@/components/tasks/card/index'; import { store } from '@/app/store'; @@ -10,6 +11,11 @@ import { } from '@/test_utils/createMockRouter'; import { NextRouter } from 'next/router'; import { TASK_STATUS } from '@/interfaces/task-status'; +import useUserData from '@/hooks/useUserData'; +import { setupServer } from 'msw/node'; +import { adminUserHandler } from '../../../../__mocks__/handlers/self.handler'; + +const server = setupServer(); const DEFAULT_PROPS = { content: { @@ -40,19 +46,6 @@ const DEFAULT_PROPS = { onContentChange: jest.fn(), }; -jest.mock('@/hooks/useUserData', () => { - return () => ({ - data: { - roles: { - admin: true, - super_user: true, - }, - }, - isUserAuthorized: true, - isSuccess: true, - }); -}); - const getFirestoreDateNDaysBefore = (n = 1) => { const d = new Date(); d.setDate(d.getDate() - n); @@ -61,6 +54,13 @@ const getFirestoreDateNDaysBefore = (n = 1) => { jest.useFakeTimers(); describe('Task card', () => { + beforeAll(() => { + server.listen(); + }); + + afterEach(() => server.resetHandlers()); + afterAll(() => server.close()); + test('Should render card', () => { const { getByText } = renderWithRouter( @@ -123,7 +123,8 @@ describe('Task card', () => { expect(queryByTestId('task-card')).toBeInTheDocument(); }); - test('should show edit button when ALT key is long pressed', () => { + test('should show edit button when ALT key is long pressed', async () => { + server.use(...adminUserHandler); const { getByTestId, queryByTestId } = renderWithRouter( @@ -138,7 +139,9 @@ describe('Task card', () => { jest.advanceTimersByTime(300); }); - expect(queryByTestId('edit-button')).toBeInTheDocument(); + await waitFor(() => { + expect(queryByTestId('edit-button')).toBeInTheDocument(); + }); }); test('task should be editable if edit button clicked', async () => { const { getByTestId, queryByTestId } = renderWithRouter( diff --git a/__tests__/Unit/Components/Tasks/TaskDetails.test.tsx b/__tests__/Unit/Components/Tasks/TaskDetails.test.tsx index 035c04585..2689f7096 100644 --- a/__tests__/Unit/Components/Tasks/TaskDetails.test.tsx +++ b/__tests__/Unit/Components/Tasks/TaskDetails.test.tsx @@ -12,6 +12,8 @@ import { ButtonProps, TextAreaProps } from '@/interfaces/taskDetails.type'; import { ToastContainer } from 'react-toastify'; import * as progressQueries from '@/app/services/progressesApi'; import Details from '@/components/taskDetails/Details'; +import { adminUserHandler } from '../../../../__mocks__/handlers/self.handler'; +import { mockGetTaskProgress } from '__mocks__/db/progresses'; const details = { url: 'https://realdevsquad.com/tasks/6KhcLU3yr45dzjQIVm0J/details', @@ -21,25 +23,11 @@ const details = { const server = setupServer(...handlers); beforeAll(() => { - server.listen({ - onUnhandledRequest: 'error', - }); + server.listen(); }); afterEach(() => server.resetHandlers()); afterAll(() => server.close()); -jest.mock('@/hooks/useUserData', () => { - return () => ({ - data: { - roles: { - admin: true, - super_user: false, - }, - }, - isUserAuthorized: true, - isSuccess: true, - }); -}); const mockNavigateToUpdateProgressPage = jest.fn(); describe('TaskDetails Page', () => { it('Should render title', async () => { @@ -54,6 +42,7 @@ describe('TaskDetails Page', () => { }); it('should show edit button when superuser is viewing', async () => { + server.use(...adminUserHandler); const { getByRole } = renderWithRouter( @@ -65,7 +54,197 @@ describe('TaskDetails Page', () => { }); }); - it('Should render Description available for a task', async () => { + it('Should render No Description available for a task without description', async () => { + const { getByText } = renderWithRouter( + + + + ); + await waitFor(() => { + expect(getByText('No description available')).toBeInTheDocument(); + }); + }); + it('Renders Task Type', async () => { + const { getByText } = renderWithRouter( + + + + ); + await waitFor(() => { + expect(getByText('feature')).toBeInTheDocument(); + }); + }); + it('Renders Task Priority', async () => { + const { getByText } = renderWithRouter( + + + + ); + await waitFor(() => { + expect(getByText('high')).toBeInTheDocument(); + }); + }); + it('Renders Task Status', async () => { + const { getByText } = renderWithRouter( + + + + ); + await waitFor(() => { + expect(getByText('assigned')).toBeInTheDocument(); + }); + }); + it('Renders Task Link', async () => { + const { getByText } = renderWithRouter( + + + + ); + await waitFor(() => { + expect(getByText('https://www.sampleUrl.com')).toBeInTheDocument(); + }); + }); + it('Renders Task Assignee', async () => { + const { getByText } = renderWithRouter( + + + + ); + await waitFor(() => { + expect(getByText('ankur')).toBeInTheDocument(); + }); + }); + it('Renders Task Reporter', async () => { + const { getByText } = renderWithRouter( + + + + ); + await waitFor(() => { + expect(getByText('Ankush')).toBeInTheDocument(); + }); + }); + it('Renders Task Started-on Date', async () => { + const { getByText } = renderWithRouter( + +
+ + ); + await waitFor(() => { + expect(getByText('3/30/2021, 12:00:00 AM')).toBeInTheDocument(); + }); + }); + it('Renders Task Ends-on Date', async () => { + const { getByText } = renderWithRouter( + +
+ + ); + await waitFor(() => { + expect(getByText('4/19/2021, 12:00:10 AM')).toBeInTheDocument(); + }); + }); + + test('should update taskDetails and editedDetails correctly on input change', async () => { + server.use(...adminUserHandler); + renderWithRouter( + + + , + {} + ); + + await waitFor(() => { + const editButton = screen.getByRole('button', { name: 'Edit' }); + fireEvent.click(editButton); + }); + const textareaElement = screen.getByTestId('title-textarea'); + + fireEvent.change(textareaElement, { + target: { name: 'title', value: 'New Title' }, + }); + + expect(textareaElement).toHaveValue('New Title'); + }); + test('should call onCancel and reset state when clicked', async () => { + server.use(...adminUserHandler); + renderWithRouter( + + + , + {} + ); + + await waitFor(() => { + const editButton = screen.getByRole('button', { name: 'Edit' }); + fireEvent.click(editButton); + }); + + await waitFor(() => { + const cancelButton = screen.getByRole('button', { name: 'Cancel' }); + fireEvent.click(cancelButton); + const editButton = screen.getByRole('button', { name: 'Edit' }); + expect(editButton).toBeInTheDocument(); + }); + }); + + test('should call onSave and reset state when clicked', async () => { + server.use(...adminUserHandler); + renderWithRouter( + + + , + {} + ); + + await waitFor(() => { + const editButton = screen.getByRole('button', { name: 'Edit' }); + fireEvent.click(editButton); + }); + + await waitFor(() => { + const saveButton = screen.getByRole('button', { name: 'Save' }); + fireEvent.click(saveButton); + const editButton = screen.getByRole('button', { name: 'Edit' }); + expect(editButton).toBeInTheDocument(); + }); + }); + + test('should update the title and description with the new values', async () => { + server.use(...adminUserHandler); + const { getByText } = renderWithRouter( + + + + , + { query: { dev: 'true' }, push: mockNavigateToUpdateProgressPage } + ); + await waitFor(() => { + const buttonElement = getByText(/Update Progress/i); + expect(buttonElement).toBeInTheDocument(); + }); + }); + + it('should show edit button when superuser is viewing', async () => { + server.use(...adminUserHandler); + const { getByRole } = renderWithRouter( + + + , + {} + ); + await waitFor(() => { + expect(getByRole('button', { name: 'Edit' })).toBeInTheDocument(); + }); + }); + + it('Should render No Description available for a task without description', async () => { const { getByText } = renderWithRouter( @@ -185,6 +364,7 @@ describe('TaskDetails Page', () => { }); test('should update taskDetails and editedDetails correctly on input change', async () => { + server.use(...adminUserHandler); renderWithRouter( @@ -205,6 +385,7 @@ describe('TaskDetails Page', () => { expect(textareaElement).toHaveValue('New Title'); }); test('should call onCancel and reset state when clicked', async () => { + server.use(...adminUserHandler); renderWithRouter( @@ -226,6 +407,7 @@ describe('TaskDetails Page', () => { }); test('should call onSave and reset state when clicked', async () => { + server.use(...adminUserHandler); renderWithRouter( @@ -247,6 +429,7 @@ describe('TaskDetails Page', () => { }); test('should update the title and description with the new values', async () => { + server.use(...adminUserHandler); renderWithRouter( diff --git a/__tests__/Unit/Components/Tasks/TaskLevelMap.test.tsx b/__tests__/Unit/Components/Tasks/TaskLevelMap.test.tsx index 86da97dbf..34fc5da6d 100644 --- a/__tests__/Unit/Components/Tasks/TaskLevelMap.test.tsx +++ b/__tests__/Unit/Components/Tasks/TaskLevelMap.test.tsx @@ -1,24 +1,26 @@ import React from 'react'; -import { fireEvent, screen, render } from '@testing-library/react'; +import { fireEvent, screen, render, waitFor } from '@testing-library/react'; import { TaskLevelMap } from '@/components/tasks/card/TaskLevelMap'; import taskItem from '@/interfaces/taskItem.type'; - import { renderWithProviders } from '@/test-utils/renderWithProvider'; +import handlers from '../../../../__mocks__/handlers'; +import { setupServer } from 'msw/node'; +import selfHandler, { + adminUserHandler, +} from '../../../../__mocks__/handlers/self.handler'; -jest.mock('@/hooks/useUserData', () => { - return () => ({ - data: { - roles: { - admin: true, - super_user: false, - }, - }, - isUserAuthorized: true, - isSuccess: true, - }); -}); +const server = setupServer(); describe('TaskLevelMap', () => { + beforeAll(() => { + server.listen({ + onUnhandledRequest: 'warn', + }); + }); + + afterEach(() => server.resetHandlers()); + afterAll(() => server.close()); + const taskTagLevel: taskItem[] = [ { tagId: '1', @@ -59,10 +61,12 @@ describe('TaskLevelMap', () => { expect(levelElements[1]).toHaveTextContent('LVL:2'); }); - it('renders a list of task tags with remove button when shouldEdit and isUserAuthorized are true', () => { + it('renders a list of task tags with remove button when shouldEdit and isUserAuthorized are true', async () => { + server.use(...adminUserHandler); const deleteTaskTagLevel = jest.fn(); - render( + renderWithProviders( + // utility { const tagElements = screen.getAllByTestId('tag-name'); expect(tagElements).toHaveLength(2); - const removeButtons = screen.getAllByTestId('delete-btn'); - expect(removeButtons).toHaveLength(2); - - fireEvent.click(removeButtons[0]); - + await waitFor(() => { + const removeButtons = screen.getAllByTestId('delete-btn'); + expect(removeButtons).toHaveLength(2); + fireEvent.click(removeButtons[0]); + }); expect(deleteTaskTagLevel).toHaveBeenCalledWith({ taskItemToDelete: taskTagLevel[0], itemId: '1', }); }); + + it('renders a list of task tags without the remove button when shouldEdit is true but isUserAuthorized is false', async () => { + server.use(...selfHandler); + const deleteTaskTagLevel = jest.fn(); + + renderWithProviders( + + ); + + const tagElements = screen.getAllByTestId('tag-name'); + expect(tagElements).toHaveLength(2); + + await waitFor(() => { + const removeButtons = screen.queryAllByTestId('delete-btn'); + expect(removeButtons).toHaveLength(0); + }); + }); }); diff --git a/__tests__/Unit/pages/issues/Issues.test.tsx b/__tests__/Unit/pages/issues/Issues.test.tsx index 75344ee3e..0bc5f03c9 100644 --- a/__tests__/Unit/pages/issues/Issues.test.tsx +++ b/__tests__/Unit/pages/issues/Issues.test.tsx @@ -10,7 +10,7 @@ import { issuesNoDataFoundHandler } from '../../../../__mocks__/handlers/issues. const server = setupServer(...handlers); beforeAll(() => { - server.listen({ onUnhandledRequest: 'error' }); + server.listen(); }); afterEach(() => server.resetHandlers()); afterAll(() => server.close()); diff --git a/src/components/tasks/card/index.tsx b/src/components/tasks/card/index.tsx index 7f88f8b53..e7b58c9ca 100644 --- a/src/components/tasks/card/index.tsx +++ b/src/components/tasks/card/index.tsx @@ -102,7 +102,6 @@ const Card: FC = ({ const localStartedOn = new Date(parseInt(cardDetails.startedOn, 10) * 1000); const fromNowStartedOn = moment(localStartedOn).fromNow(); - const localEndsOn = new Date(parseInt(cardDetails.endsOn, 10) * 1000); const fromNowEndsOn = moment(localEndsOn).fromNow(); const statusFontColor = !statusRedList.includes( diff --git a/src/hooks/useUserData.ts b/src/hooks/useUserData.ts index 3e4d307a4..5de6221e8 100644 --- a/src/hooks/useUserData.ts +++ b/src/hooks/useUserData.ts @@ -1,7 +1,7 @@ import { useGetUserQuery } from '@/app/services/userApi'; const useUserData = () => { - const { data, isSuccess } = useGetUserQuery(); + const { data, isSuccess, isLoading } = useGetUserQuery(); const adminData = data?.roles.admin; const superUserData = data?.roles.super_user; const isUserAuthorized = !!adminData || !!superUserData;