diff --git a/src/screens/UserPortal/Donate/Donate.spec.tsx b/src/screens/UserPortal/Donate/Donate.spec.tsx index b13056f5f9..2f235c9a01 100644 --- a/src/screens/UserPortal/Donate/Donate.spec.tsx +++ b/src/screens/UserPortal/Donate/Donate.spec.tsx @@ -5,7 +5,7 @@ * under various scenarios. */ import React, { act } from 'react'; -import { render, screen } from '@testing-library/react'; +import { render, screen, fireEvent } from '@testing-library/react'; import { MockedProvider } from '@apollo/react-testing'; import { I18nextProvider } from 'react-i18next'; import { vi } from 'vitest'; @@ -23,6 +23,12 @@ import userEvent from '@testing-library/user-event'; import useLocalStorage from 'utils/useLocalstorage'; import { DONATE_TO_ORGANIZATION } from 'GraphQl/Mutations/mutations'; import { toast } from 'react-toastify'; +import * as errorHandlerModule from 'utils/errorHandler'; + +// Mock the errorHandler module +vi.mock('utils/errorHandler', () => ({ + errorHandler: vi.fn(), +})); const MOCKS = [ { @@ -157,8 +163,8 @@ describe('Testing Donate Screen [User Portal]', () => { matches: false, media: query, onchange: null, - addListener: vi.fn(), // Deprecated - removeListener: vi.fn(), // Deprecated + addListener: vi.fn(), + removeListener: vi.fn(), addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn(), @@ -188,6 +194,237 @@ describe('Testing Donate Screen [User Portal]', () => { expect(screen.getByPlaceholderText('Amount')).toBeInTheDocument(); }); + test('Donation amount input should update state', async () => { + render( + + + + + + + + + , + ); + + const amountInput = screen.getByPlaceholderText('Amount'); + fireEvent.change(amountInput, { target: { value: '100' } }); + expect(amountInput).toHaveValue('100'); + }); + + test('Currency dropdown should update state', async () => { + render( + + + + + + + + + , + ); + + const currencyButton = screen.getByTestId('changeCurrencyBtn'); + fireEvent.click(currencyButton); + const currencyOption = screen.getByText('EUR'); + fireEvent.click(currencyOption); + expect(currencyButton).toHaveTextContent('EUR'); + }); + + test('should handle search input changes', async () => { + render( + + + + + + + + + , + ); + + await wait(); + const searchInput = screen.getByTestId('searchByName'); + fireEvent.change(searchInput, { target: { value: 'test search' } }); + expect(searchInput).toHaveValue('test search'); + }); + + test('should render pagination list when donations are present', async () => { + render( + + + + + + + + + , + ); + + await wait(); + const paginationList = screen.getByRole('table'); + expect(paginationList).toBeInTheDocument(); + }); + + test('handles pagination changes for rows per page', async () => { + render( + + + + + + + + + , + ); + + await wait(); + const paginationComponent = screen.getByRole('combobox'); + fireEvent.change(paginationComponent, { target: { value: '10' } }); + + expect(screen.getByRole('combobox')).toHaveValue('10'); + }); + + test('pagination shows correct number of donations per page', async () => { + const multipleDonationsMock = { + request: { + query: ORGANIZATION_DONATION_CONNECTION_LIST, + variables: { orgId: '' }, + }, + result: { + data: { + getDonationByOrgIdConnection: Array(10).fill({ + _id: '123', + nameOfUser: 'Test User', + amount: '100', + userId: '456', + payPalId: 'paypal123', + updatedAt: new Date().toISOString(), + }), + }, + }, + }; + + const paginationLink = new StaticMockLink([multipleDonationsMock], true); + + render( + + + + + + + + + , + ); + + await wait(); + + const donationCards = screen.getAllByTestId('donationCard'); + expect(donationCards).toHaveLength(5); // Default rowsPerPage is 5 + }); + + test('search button is present and clickable', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + const searchButton = screen.getByTestId('searchButton'); + expect(searchButton).toBeInTheDocument(); + expect(searchButton).toBeEnabled(); + }); + + test('donation form elements are properly initialized', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + expect(screen.getByTestId('changeCurrencyBtn')).toHaveTextContent('USD'); + expect(screen.getByTestId('donationAmount')).toHaveValue(''); + expect(screen.getByTestId('donateBtn')).toBeEnabled(); + }); + test('displays loading state while fetching donations', async () => { + render( + + + + + + + + + , + ); + + // Find loading text within the specific container + const loadingElement = screen.getByTestId('loading-state'); + expect(loadingElement).toHaveTextContent('Loading...'); + + await wait(); + }); + + test('displays "nothing to show" when no donations exist', async () => { + const emptyMocks = [ + { + request: { + query: ORGANIZATION_DONATION_CONNECTION_LIST, + variables: { orgId: '' }, + }, + result: { + data: { + getDonationByOrgIdConnection: [], + }, + }, + }, + ...MOCKS.slice(1), // Keep other mocks + ]; + + render( + + + + + + + + + , + ); + + // Wait for loading to complete + await wait(); + + // Assuming i18nForTest translates 'nothingToShow' to the actual text + // If using the translation key directly: + const nothingToShowElement = screen.getByText(/nothing to show/i); + expect(nothingToShowElement).toBeInTheDocument(); + }); + test('Currency is swtiched to USD', async () => { render( @@ -393,4 +630,324 @@ describe('Testing Donate Screen [User Portal]', () => { 'Please enter a numerical value for the donation amount.', ); }); + + test('handles donation with whitespace in amount', async () => { + const { setItem } = useLocalStorage(); + setItem('userId', '123'); + setItem('name', 'name'); + + render( + + + + + + + + + , + ); + + await wait(); + + userEvent.type(screen.getByTestId('donationAmount'), ' 123 '); + userEvent.click(screen.getByTestId('donateBtn')); + + await wait(); + expect(toast.success).toHaveBeenCalled(); + }); + + test('handles null donation data from query', async () => { + const nullDonationMock = { + request: { + query: ORGANIZATION_DONATION_CONNECTION_LIST, + variables: { orgId: '' }, + }, + result: { + data: { + getDonationByOrgIdConnection: null, + }, + }, + }; + + const nullDataLink = new StaticMockLink( + [nullDonationMock, ...MOCKS.slice(1)], + true, + ); + + render( + + + + + + + + + , + ); + + await wait(); + expect(screen.getByText(/nothing to show/i)).toBeInTheDocument(); + }); + test('handles zero rows per page in pagination', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + const paginationComponent = screen.getByRole('combobox'); + fireEvent.change(paginationComponent, { target: { value: '0' } }); + + await wait(); + const donationCards = screen.getAllByTestId('donationCard'); + expect(donationCards.length).toBeGreaterThan(0); // Should show all donations + }); + test('donateToOrg validation - empty amount shows error toast', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + // Leave amount empty + userEvent.click(screen.getByTestId('donateBtn')); + + expect(toast.error).toHaveBeenCalledWith( + 'Please enter a numerical value for the donation amount.', + ); + }); + test('setPage updates the page number correctly', async () => { + // Setup mock data with multiple donations to trigger pagination + const multipleDonationsMock = { + request: { + query: ORGANIZATION_DONATION_CONNECTION_LIST, + variables: { orgId: '' }, + }, + result: { + data: { + getDonationByOrgIdConnection: Array(15).fill({ + _id: '123', + nameOfUser: 'Test User', + amount: '100', + userId: '456', + payPalId: 'paypal123', + updatedAt: new Date().toISOString(), + }), + }, + }, + }; + + const paginationLink = new StaticMockLink([multipleDonationsMock], true); + + render( + + + + + + + + + , + ); + + await wait(); + + // Find and click the next page button + const nextButton = screen.getByRole('button', { name: /next/i }); + fireEvent.click(nextButton); + + // Verify page change using donation cards + const donationCards = screen.getAllByTestId('donationCard'); + expect(donationCards.length).toBe(5); // Should show 5 items per page + }); + test('donateToOrg validation - non-numeric amount shows error toast', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + userEvent.type(screen.getByTestId('donationAmount'), 'abc'); + userEvent.click(screen.getByTestId('donateBtn')); + + expect(toast.error).toHaveBeenCalledWith( + 'Please enter a numerical value for the donation amount.', + ); + }); + + test('donateToOrg validation - amount less than minimum shows error toast', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + userEvent.type(screen.getByTestId('donationAmount'), '0.5'); + userEvent.click(screen.getByTestId('donateBtn')); + + expect(toast.error).toHaveBeenCalledWith( + 'Donation amount must be between 1 and 10000000.', + ); + }); + + test('donateToOrg validation - amount greater than maximum shows error toast', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + userEvent.type(screen.getByTestId('donationAmount'), '10000001'); + userEvent.click(screen.getByTestId('donateBtn')); + + expect(toast.error).toHaveBeenCalledWith( + 'Donation amount must be between 1 and 10000000.', + ); + }); + + test('donateToOrg - handles donation mutation error', async () => { + const mockErrorHandler = vi.fn(); + vi.spyOn(errorHandlerModule, 'errorHandler').mockImplementation( + mockErrorHandler, + ); + + const mocks = [ + { + request: { + query: DONATE_TO_ORGANIZATION, + variables: { + userId: '123', + createDonationOrgId2: '', + payPalId: 'paypalId', + nameOfUser: 'name', + amount: 100, + nameOfOrg: 'anyOrganization2', + }, + }, + error: new Error('Failed to process donation'), + }, + ...MOCKS.slice(1), + ]; + + const { setItem } = useLocalStorage(); + setItem('userId', '123'); + setItem('name', 'name'); + + render( + + + + + + + + + , + ); + + await wait(); + + userEvent.type(screen.getByTestId('donationAmount'), '100'); + userEvent.click(screen.getByTestId('donateBtn')); + + await wait(500); + expect(mockErrorHandler).toHaveBeenCalledWith( + expect.any(Function), + expect.any(Error), + ); + }); + + test('donateToOrg - handles GraphQL errors', async () => { + const mockErrorHandler = vi.fn(); + vi.spyOn(errorHandlerModule, 'errorHandler').mockImplementation( + mockErrorHandler, + ); + + const mocks = [ + { + request: { + query: DONATE_TO_ORGANIZATION, + variables: { + userId: '123', + createDonationOrgId2: '', + payPalId: 'paypalId', + nameOfUser: 'name', + amount: 100, + nameOfOrg: 'anyOrganization2', + }, + }, + result: { + errors: [{ message: 'GraphQL Error' }], + }, + }, + ...MOCKS.slice(1), + ]; + + const { setItem } = useLocalStorage(); + setItem('userId', '123'); + setItem('name', 'name'); + + render( + + + + + + + + + , + ); + + await wait(); + + userEvent.type(screen.getByTestId('donationAmount'), '100'); + userEvent.click(screen.getByTestId('donateBtn')); + + await wait(500); + expect(mockErrorHandler).toHaveBeenCalled(); + }); }); diff --git a/src/screens/UserPortal/Donate/Donate.tsx b/src/screens/UserPortal/Donate/Donate.tsx index c6e6d918d5..04353523ba 100644 --- a/src/screens/UserPortal/Donate/Donate.tsx +++ b/src/screens/UserPortal/Donate/Donate.tsx @@ -97,7 +97,6 @@ export default function donate(): JSX.Element { const [donate] = useMutation(DONATE_TO_ORGANIZATION); - /* istanbul ignore next */ const handleChangePage = ( _event: React.MouseEvent | null, newPage: number, @@ -105,7 +104,6 @@ export default function donate(): JSX.Element { setPage(newPage); }; - /* istanbul ignore next */ const handleChangeRowsPerPage = ( event: React.ChangeEvent, ): void => { @@ -127,7 +125,7 @@ export default function donate(): JSX.Element { } }, [donationData]); - const donateToOrg = (): void => { + const donateToOrg = async (): Promise => { // check if the amount is non empty and is a number if (amount === '' || Number.isNaN(Number(amount))) { toast.error(t(`invalidAmount`)); @@ -151,7 +149,7 @@ export default function donate(): JSX.Element { const formattedAmount = Number(amount.trim()); try { - donate({ + await donate({ variables: { userId, createDonationOrgId2: organizationId, @@ -164,7 +162,6 @@ export default function donate(): JSX.Element { refetch(); toast.success(t(`success`) as string); } catch (error: unknown) { - /* istanbul ignore next */ errorHandler(t, error); } }; @@ -259,7 +256,10 @@ export default function donate(): JSX.Element { >
{loading ? ( -
+
Loading...
) : ( @@ -270,8 +270,7 @@ export default function donate(): JSX.Element { page * rowsPerPage, page * rowsPerPage + rowsPerPage, ) - : /* istanbul ignore next */ - donations + : donations ).map((donation: InterfaceDonation, index) => { const cardProps: InterfaceDonationCardProps = { name: donation.nameOfUser, @@ -297,10 +296,7 @@ export default function donate(): JSX.Element {