From fbc01ffed197eed9d2e47651bc3755e5c87e3c47 Mon Sep 17 00:00:00 2001 From: vivekbisen04 Date: Tue, 31 Dec 2024 16:03:46 +0530 Subject: [PATCH 1/2] Improve Code Coverage in src/components/UserPortal/CommentCard/CommentCard.tsx --- .../CommentCard/CommentCard.spec.tsx | 345 +++++++++++++++++- .../UserPortal/CommentCard/CommentCard.tsx | 9 +- 2 files changed, 347 insertions(+), 7 deletions(-) diff --git a/src/components/UserPortal/CommentCard/CommentCard.spec.tsx b/src/components/UserPortal/CommentCard/CommentCard.spec.tsx index 112fd1bcf5..5f8468a87d 100644 --- a/src/components/UserPortal/CommentCard/CommentCard.spec.tsx +++ b/src/components/UserPortal/CommentCard/CommentCard.spec.tsx @@ -1,6 +1,6 @@ import React, { act } from 'react'; import { MockedProvider } from '@apollo/react-testing'; -import { render, screen } from '@testing-library/react'; +import { render, screen} from '@testing-library/react'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; @@ -12,6 +12,7 @@ import userEvent from '@testing-library/user-event'; import { LIKE_COMMENT, UNLIKE_COMMENT } from 'GraphQl/Mutations/mutations'; import useLocalStorage from 'utils/useLocalstorage'; import { vi } from 'vitest'; +import {toast} from 'react-toastify'; /** * Unit tests for the CommentCard component. @@ -26,6 +27,13 @@ import { vi } from 'vitest'; */ const { getItem, setItem } = useLocalStorage(); +vi.mock('react-toastify', () => ({ + toast: { + error: vi.fn(), + }, +})); + + async function wait(ms = 100): Promise { await act(() => { return new Promise((resolve) => { @@ -67,11 +75,31 @@ const MOCKS = [ }, ]; + +const defaultProps = { + id: '1', + creator: { + id: '1', + firstName: 'test', + lastName: 'user', + email: 'test@user.com', + }, + likeCount: 1, + likedBy: [{ id: '1' }], + text: 'testComment', + handleLikeComment: vi.fn(), + handleDislikeComment: vi.fn(), +}; + const handleLikeComment = vi.fn(); const handleDislikeComment = vi.fn(); const link = new StaticMockLink(MOCKS, true); describe('Testing CommentCard Component [User Portal]', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + afterEach(async () => { await act(async () => { await i18nForTest.changeLanguage('en'); @@ -249,4 +277,319 @@ describe('Testing CommentCard Component [User Portal]', () => { setItem('userId', beforeUserId); } }); + + it('should handle like mutation error correctly', async () => { + const errorMock = { + request: { + query: LIKE_COMMENT, + variables: { commentId: '1' }, + }, + error: new Error('Failed to like comment'), + }; + + const link = new StaticMockLink([errorMock], true); + setItem('userId', '2'); // Set user as not having liked the comment + + render( + + + + + + + + + + ); + + await wait(); + + userEvent.click(screen.getByTestId('likeCommentBtn')); + await wait(); + + expect(toast.error).toHaveBeenCalled(); + expect(defaultProps.handleLikeComment).not.toHaveBeenCalled(); + }); + + it('should handle unlike mutation error correctly', async () => { + const errorMock = { + request: { + query: UNLIKE_COMMENT, + variables: { commentId: '1' }, + }, + error: new Error('Failed to unlike comment'), + }; + + const link = new StaticMockLink([errorMock], true); + setItem('userId', '1'); // Set user as having liked the comment + + render( + + + + + + + + + + ); + + await wait(); + + userEvent.click(screen.getByTestId('likeCommentBtn')); + await wait(); + + expect(toast.error).toHaveBeenCalled(); + expect(defaultProps.handleDislikeComment).not.toHaveBeenCalled(); + }); + + it('should successfully like a comment and update UI', async () => { + const successMock = { + request: { + query: LIKE_COMMENT, + variables: { commentId: '1' }, + }, + result: { + data: { + likeComment: { + _id: '1', + }, + }, + }, + }; + + const link = new StaticMockLink([successMock], true); + setItem('userId', '2'); // Set user as not having liked the comment + + const { container } = render( + + + + + + + + + + ); + + await wait(); + + const initialLikes = container.textContent?.match(/\d+ Likes/)?.[0]; + userEvent.click(screen.getByTestId('likeCommentBtn')); + await wait(); + + const updatedLikes = container.textContent?.match(/\d+ Likes/)?.[0]; + expect(updatedLikes).not.toBe(initialLikes); + expect(defaultProps.handleLikeComment).toHaveBeenCalledWith('1'); + }); + + it('should successfully unlike a comment and update UI', async () => { + const successMock = { + request: { + query: UNLIKE_COMMENT, + variables: { commentId: '1' }, + }, + result: { + data: { + unlikeComment: { + _id: '1', + }, + }, + }, + }; + + const link = new StaticMockLink([successMock], true); + setItem('userId', '1'); // Set user as having liked the comment + + const { container } = render( + + + + + + + + + + ); + + await wait(); + + const initialLikes = container.textContent?.match(/\d+ Likes/)?.[0]; + userEvent.click(screen.getByTestId('likeCommentBtn')); + await wait(); + + const updatedLikes = container.textContent?.match(/\d+ Likes/)?.[0]; + expect(updatedLikes).not.toBe(initialLikes); + expect(defaultProps.handleDislikeComment).toHaveBeenCalledWith('1'); + }); + + it('should show loading state while mutation is in progress', async () => { + const slowMock = { + request: { + query: LIKE_COMMENT, + variables: { commentId: '1' }, + }, + result: { + data: { + likeComment: { + _id: '1', + }, + }, + }, + delay: 100, + }; + + const link = new StaticMockLink([slowMock], true); + setItem('userId', '2'); // Set user as not having liked the comment + + render( + + + + + + + + + + ); + + await wait(); + + userEvent.click(screen.getByTestId('likeCommentBtn')); + + // HourglassBottomIcon should be visible during loading + expect(document.querySelector('[data-testid="HourglassBottomIcon"]')).toBeInTheDocument(); + + await wait(150); + + // After loading, ThumbUpIcon should be visible + expect(document.querySelector('[data-testid="ThumbUpIcon"]')).toBeInTheDocument(); + }); + + it('should not update state if mutation returns no data', async () => { + const noDataMock = { + request: { + query: LIKE_COMMENT, + variables: { commentId: '1' }, + }, + result: { + data: null, + }, + }; + + const link = new StaticMockLink([noDataMock], true); + setItem('userId', '2'); // Set user as not having liked the comment + + const { container } = render( + + + + + + + + + + ); + + await wait(); + + const initialLikes = container.textContent?.match(/\d+ Likes/)?.[0]; + userEvent.click(screen.getByTestId('likeCommentBtn')); + await wait(); + + const updatedLikes = container.textContent?.match(/\d+ Likes/)?.[0]; + expect(updatedLikes).toBe(initialLikes); + expect(defaultProps.handleLikeComment).not.toHaveBeenCalled(); + }); + + it('should handle successful mutation with empty data for unlike', async () => { + const emptyDataMock = { + request: { + query: UNLIKE_COMMENT, + variables: { commentId: '1' }, + }, + result: { + data: { + unlikeComment: null + }, + }, + }; + + const link = new StaticMockLink([emptyDataMock], true); + setItem('userId', '1'); // Set user as having liked the comment + + const { container } = render( + + + + + + + + + + ); + + const initialLikes = container.textContent?.match(/\d+ Likes/)?.[0]; + + await wait(); + + userEvent.click(screen.getByTestId('likeCommentBtn')); + await wait(); + + // Verify that the likes count hasn't changed + const updatedLikes = container.textContent?.match(/\d+ Likes/)?.[0]; + expect(updatedLikes).toBe(initialLikes); + + // Verify that the callback wasn't called + expect(defaultProps.handleDislikeComment).not.toHaveBeenCalled(); + }); + + it('should handle successful mutation with empty data for like', async () => { + const emptyDataMock = { + request: { + query: LIKE_COMMENT, + variables: { commentId: '1' }, + }, + result: { + data: { + likeComment: null + }, + }, + }; + + const link = new StaticMockLink([emptyDataMock], true); + setItem('userId', '2'); // Set user as not having liked the comment + + const { container } = render( + + + + + + + + + + ); + + const initialLikes = container.textContent?.match(/\d+ Likes/)?.[0]; + + await wait(); + + userEvent.click(screen.getByTestId('likeCommentBtn')); + await wait(); + + // Verify that the likes count hasn't changed + const updatedLikes = container.textContent?.match(/\d+ Likes/)?.[0]; + expect(updatedLikes).toBe(initialLikes); + + // Verify that the callback wasn't called + expect(defaultProps.handleLikeComment).not.toHaveBeenCalled(); + }); }); diff --git a/src/components/UserPortal/CommentCard/CommentCard.tsx b/src/components/UserPortal/CommentCard/CommentCard.tsx index 9e8c46d241..ace89d33d0 100644 --- a/src/components/UserPortal/CommentCard/CommentCard.tsx +++ b/src/components/UserPortal/CommentCard/CommentCard.tsx @@ -84,14 +84,12 @@ function commentCard(props: InterfaceCommentCardProps): JSX.Element { commentId: props.id, }, }); - /* istanbul ignore next */ - if (data) { + if (data && data.unlikeComment && data.unlikeComment._id) { setLikes((likes) => likes - 1); setIsLikedByUser(false); props.handleDislikeComment(props.id); } } catch (error: unknown) { - /* istanbul ignore next */ toast.error(error as string); } } else { @@ -101,14 +99,13 @@ function commentCard(props: InterfaceCommentCardProps): JSX.Element { commentId: props.id, }, }); - /* istanbul ignore next */ - if (data) { + + if (data && data.likeComment && data.likeComment._id) { setLikes((likes) => likes + 1); setIsLikedByUser(true); props.handleLikeComment(props.id); } } catch (error: unknown) { - /* istanbul ignore next */ toast.error(error as string); } } From a349b1bafdaa58659c7fbc1dea8afcc5a99996bc Mon Sep 17 00:00:00 2001 From: vivekbisen04 Date: Tue, 31 Dec 2024 16:07:34 +0530 Subject: [PATCH 2/2] Improve Code Coverage in src/components/UserPortal/CommentCard/CommentCard.tsx --- .../CommentCard/CommentCard.spec.tsx | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/components/UserPortal/CommentCard/CommentCard.spec.tsx b/src/components/UserPortal/CommentCard/CommentCard.spec.tsx index 5f8468a87d..57a708f793 100644 --- a/src/components/UserPortal/CommentCard/CommentCard.spec.tsx +++ b/src/components/UserPortal/CommentCard/CommentCard.spec.tsx @@ -1,6 +1,6 @@ import React, { act } from 'react'; import { MockedProvider } from '@apollo/react-testing'; -import { render, screen} from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; @@ -12,7 +12,7 @@ import userEvent from '@testing-library/user-event'; import { LIKE_COMMENT, UNLIKE_COMMENT } from 'GraphQl/Mutations/mutations'; import useLocalStorage from 'utils/useLocalstorage'; import { vi } from 'vitest'; -import {toast} from 'react-toastify'; +import { toast } from 'react-toastify'; /** * Unit tests for the CommentCard component. @@ -33,7 +33,6 @@ vi.mock('react-toastify', () => ({ }, })); - async function wait(ms = 100): Promise { await act(() => { return new Promise((resolve) => { @@ -75,7 +74,6 @@ const MOCKS = [ }, ]; - const defaultProps = { id: '1', creator: { @@ -299,11 +297,11 @@ describe('Testing CommentCard Component [User Portal]', () => { - + , ); await wait(); - + userEvent.click(screen.getByTestId('likeCommentBtn')); await wait(); @@ -332,11 +330,11 @@ describe('Testing CommentCard Component [User Portal]', () => { - + , ); await wait(); - + userEvent.click(screen.getByTestId('likeCommentBtn')); await wait(); @@ -371,11 +369,11 @@ describe('Testing CommentCard Component [User Portal]', () => { - + , ); await wait(); - + const initialLikes = container.textContent?.match(/\d+ Likes/)?.[0]; userEvent.click(screen.getByTestId('likeCommentBtn')); await wait(); @@ -412,11 +410,11 @@ describe('Testing CommentCard Component [User Portal]', () => { - + , ); await wait(); - + const initialLikes = container.textContent?.match(/\d+ Likes/)?.[0]; userEvent.click(screen.getByTestId('likeCommentBtn')); await wait(); @@ -454,20 +452,24 @@ describe('Testing CommentCard Component [User Portal]', () => { - + , ); await wait(); - + userEvent.click(screen.getByTestId('likeCommentBtn')); - + // HourglassBottomIcon should be visible during loading - expect(document.querySelector('[data-testid="HourglassBottomIcon"]')).toBeInTheDocument(); - + expect( + document.querySelector('[data-testid="HourglassBottomIcon"]'), + ).toBeInTheDocument(); + await wait(150); - + // After loading, ThumbUpIcon should be visible - expect(document.querySelector('[data-testid="ThumbUpIcon"]')).toBeInTheDocument(); + expect( + document.querySelector('[data-testid="ThumbUpIcon"]'), + ).toBeInTheDocument(); }); it('should not update state if mutation returns no data', async () => { @@ -493,11 +495,11 @@ describe('Testing CommentCard Component [User Portal]', () => { - + , ); await wait(); - + const initialLikes = container.textContent?.match(/\d+ Likes/)?.[0]; userEvent.click(screen.getByTestId('likeCommentBtn')); await wait(); @@ -515,7 +517,7 @@ describe('Testing CommentCard Component [User Portal]', () => { }, result: { data: { - unlikeComment: null + unlikeComment: null, }, }, }; @@ -532,20 +534,20 @@ describe('Testing CommentCard Component [User Portal]', () => { - + , ); const initialLikes = container.textContent?.match(/\d+ Likes/)?.[0]; - + await wait(); - + userEvent.click(screen.getByTestId('likeCommentBtn')); await wait(); // Verify that the likes count hasn't changed const updatedLikes = container.textContent?.match(/\d+ Likes/)?.[0]; expect(updatedLikes).toBe(initialLikes); - + // Verify that the callback wasn't called expect(defaultProps.handleDislikeComment).not.toHaveBeenCalled(); }); @@ -558,7 +560,7 @@ describe('Testing CommentCard Component [User Portal]', () => { }, result: { data: { - likeComment: null + likeComment: null, }, }, }; @@ -575,20 +577,20 @@ describe('Testing CommentCard Component [User Portal]', () => { - + , ); const initialLikes = container.textContent?.match(/\d+ Likes/)?.[0]; - + await wait(); - + userEvent.click(screen.getByTestId('likeCommentBtn')); await wait(); // Verify that the likes count hasn't changed const updatedLikes = container.textContent?.match(/\d+ Likes/)?.[0]; expect(updatedLikes).toBe(initialLikes); - + // Verify that the callback wasn't called expect(defaultProps.handleLikeComment).not.toHaveBeenCalled(); });