+
-
-
-
- {eventData?.event?.startTime !== null
- ? `${formatTime(eventData?.event?.startTime)}`
- : ''}
- {' '}
-
- {formatDate(eventData?.event?.startDate)}{' '}
-
-
-
{t('to')}
-
-
- {eventData?.event?.endTime !== null
- ? `${formatTime(eventData?.event?.endTime)}`
- : ``}
- {' '}
-
- {formatDate(eventData?.event?.endDate)}{' '}
-
-
-
-
{eventData?.event?.title}
-
- {eventData?.event?.description}
+
+
+
+
+ {eventData.event.title}
+
+
+ {eventData.event.description}
-
- Location: {eventData?.event?.location}
+
+ Location: {eventData.event.location}
-
+
Registrants: {' '}
{eventData?.event?.attendees?.length}
-
+
+ Recurring Event: {' '}
+
+ {eventData.event.recurring ? 'Active' : 'Inactive'}
+
+
+
+
+
+
+ {eventData.event.startTime !== null
+ ? `${formatTime(eventData.event.startTime)}`
+ : ``}
+ {' '}
+
+ {formatDate(eventData.event.startDate)}{' '}
+
+
+
{t('to')}
+
+
+ {eventData.event.endTime !== null
+ ? `${formatTime(eventData.event.endTime)}`
+ : ``}
+ {' '}
+
+ {formatDate(eventData.event.endDate)}{' '}
+
+
diff --git a/src/components/EventManagement/EventAgendaItems/EventAgendaItems.module.css b/src/components/EventManagement/EventAgendaItems/EventAgendaItems.module.css
deleted file mode 100644
index 9d1c32b766..0000000000
--- a/src/components/EventManagement/EventAgendaItems/EventAgendaItems.module.css
+++ /dev/null
@@ -1,22 +0,0 @@
-.eventAgendaItemContainer h2 {
- margin: 0.6rem 0;
-}
-
-.btnsContainer {
- display: flex;
- gap: 10px;
-}
-
-@media (max-width: 768px) {
- .btnsContainer {
- margin-bottom: 0;
- display: flex;
- flex-direction: column;
- }
-
- .createAgendaItemButton {
- position: absolute;
- top: 1rem;
- right: 2rem;
- }
-}
diff --git a/src/components/EventManagement/EventAgendaItems/EventAgendaItems.test.tsx b/src/components/EventManagement/EventAgendaItems/EventAgendaItems.spec.tsx
similarity index 87%
rename from src/components/EventManagement/EventAgendaItems/EventAgendaItems.test.tsx
rename to src/components/EventManagement/EventAgendaItems/EventAgendaItems.spec.tsx
index 3bce7ad11e..fabd3312dd 100644
--- a/src/components/EventManagement/EventAgendaItems/EventAgendaItems.test.tsx
+++ b/src/components/EventManagement/EventAgendaItems/EventAgendaItems.spec.tsx
@@ -7,9 +7,7 @@ import {
waitForElementToBeRemoved,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import 'jest-localstorage-mock';
import { MockedProvider } from '@apollo/client/testing';
-import 'jest-location-mock';
import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
@@ -17,11 +15,10 @@ import i18n from 'utils/i18nForTest';
// import { toast } from 'react-toastify';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
-
import { store } from 'state/store';
import { StaticMockLink } from 'utils/StaticMockLink';
-
import EventAgendaItems from './EventAgendaItems';
+import { vi, describe, expect, it, beforeEach } from 'vitest';
import {
MOCKS,
@@ -29,21 +26,20 @@ import {
// MOCKS_ERROR_MUTATION,
} from './EventAgendaItemsMocks';
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ error: vi.fn(),
},
}));
-jest.mock('react-router-dom', () => ({
- ...jest.requireActual('react-router-dom'),
- useParams: () => ({ eventId: '123' }),
+vi.mock('react-router-dom', async () => ({
+ ...(await vi.importActual('react-router-dom')),
}));
//temporarily fixes react-beautiful-dnd droppable method's depreciation error
//needs to be fixed in React 19
-jest.spyOn(console, 'error').mockImplementation((message) => {
+vi.spyOn(console, 'error').mockImplementation((message) => {
if (message.includes('Support for defaultProps will be removed')) {
return;
}
@@ -78,8 +74,18 @@ describe('Testing Agenda Items Components', () => {
attachments: [],
urls: [],
};
- test('Component loads correctly', async () => {
- window.location.assign('/event/111/123');
+
+ beforeEach(() => {
+ Object.defineProperty(window, 'location', {
+ configurable: true,
+ value: {
+ reload: vi.fn(),
+ href: 'https://localhost:4321/event/111/123',
+ },
+ });
+ });
+
+ it('Component loads correctly', async () => {
const { getByText } = render(
@@ -99,8 +105,7 @@ describe('Testing Agenda Items Components', () => {
});
});
- test('render error component on unsuccessful agenda item query', async () => {
- window.location.assign('/event/111/123');
+ it('render error component on unsuccessful agenda item query', async () => {
const { queryByText } = render(
@@ -122,8 +127,7 @@ describe('Testing Agenda Items Components', () => {
});
});
- test('opens and closes the create agenda item modal', async () => {
- window.location.assign('/event/111/123');
+ it('opens and closes the create agenda item modal', async () => {
render(
@@ -156,8 +160,7 @@ describe('Testing Agenda Items Components', () => {
screen.queryByTestId('createAgendaItemModalCloseBtn'),
);
});
- test('creates new agenda item', async () => {
- window.location.assign('/event/111/123');
+ it('creates new agenda item', async () => {
render(
diff --git a/src/components/EventManagement/EventAgendaItems/EventAgendaItems.tsx b/src/components/EventManagement/EventAgendaItems/EventAgendaItems.tsx
index b49ade4626..9b758a555a 100644
--- a/src/components/EventManagement/EventAgendaItems/EventAgendaItems.tsx
+++ b/src/components/EventManagement/EventAgendaItems/EventAgendaItems.tsx
@@ -20,7 +20,7 @@ import type {
import AgendaItemsContainer from 'components/AgendaItems/AgendaItemsContainer';
import AgendaItemsCreateModal from 'components/AgendaItems/AgendaItemsCreateModal';
-import styles from './EventAgendaItems.module.css';
+import styles from '../../../style/app.module.css';
import Loader from 'components/Loader/Loader';
/**
diff --git a/src/components/EventManagement/EventAttendance/Attendance.mocks.ts b/src/components/EventManagement/EventAttendance/Attendance.mocks.ts
new file mode 100644
index 0000000000..2dc8c89571
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/Attendance.mocks.ts
@@ -0,0 +1,62 @@
+import { EVENT_ATTENDEES } from 'GraphQl/Queries/Queries';
+
+export const MOCKS = [
+ {
+ request: {
+ query: EVENT_ATTENDEES,
+ variables: {}, // Removed id since it's not required based on error
+ },
+ result: {
+ data: {
+ event: {
+ attendees: [
+ {
+ _id: '6589386a2caa9d8d69087484',
+ firstName: 'Bruce',
+ lastName: 'Garza',
+ gender: null,
+ birthDate: null,
+ createdAt: '2023-04-13T10:23:17.742',
+ eventsAttended: [
+ {
+ __typename: 'Event',
+ _id: '660fdf7d2c1ef6c7db1649ad',
+ },
+ {
+ __typename: 'Event',
+ _id: '660fdd562c1ef6c7db1644f7',
+ },
+ ],
+ __typename: 'User',
+ },
+ {
+ _id: '6589386a2caa9d8d69087485',
+ firstName: 'Jane',
+ lastName: 'Smith',
+ gender: null,
+ birthDate: null,
+ createdAt: '2023-04-13T10:23:17.742',
+ eventsAttended: [
+ {
+ __typename: 'Event',
+ _id: '660fdf7d2c1ef6c7db1649ad',
+ },
+ ],
+ __typename: 'User',
+ },
+ ],
+ },
+ },
+ },
+ },
+];
+
+export const MOCKS_ERROR = [
+ {
+ request: {
+ query: EVENT_ATTENDEES,
+ variables: {},
+ },
+ error: new Error('An error occurred'),
+ },
+];
diff --git a/src/components/EventManagement/EventAttendance/AttendedEventList.spec.tsx b/src/components/EventManagement/EventAttendance/AttendedEventList.spec.tsx
new file mode 100644
index 0000000000..b365ba89ff
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/AttendedEventList.spec.tsx
@@ -0,0 +1,120 @@
+import React from 'react';
+import { render, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import AttendedEventList from './AttendedEventList';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import { BrowserRouter } from 'react-router-dom';
+import { I18nextProvider } from 'react-i18next';
+import i18nForTest from 'utils/i18nForTest';
+import { formatDate } from 'utils/dateFormatter';
+import { describe, expect, it } from 'vitest';
+
+const mockEvent = {
+ _id: 'event123',
+ title: 'Test Event',
+ description: 'This is a test event description',
+ startDate: '2023-05-01',
+ endDate: '2023-05-02',
+ startTime: '09:00:00',
+ endTime: '17:00:00',
+ allDay: false,
+ location: 'Test Location',
+ recurring: true,
+ baseRecurringEvent: {
+ _id: 'recurringEvent123',
+ },
+ organization: {
+ _id: 'org456',
+ members: [
+ { _id: 'member1', firstName: 'John', lastName: 'Doe' },
+ { _id: 'member2', firstName: 'Jane', lastName: 'Smith' },
+ ],
+ },
+ attendees: [{ _id: 'user1' }, { _id: 'user2' }],
+};
+
+const mocks = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: mockEvent,
+ },
+ },
+ },
+];
+
+describe('Testing AttendedEventList', () => {
+ const props = {
+ eventId: 'event123',
+ };
+
+ it('Component renders and displays event details correctly', async () => {
+ const { queryByText, queryByTitle } = render(
+
+
+
+
+
+
+ ,
+ );
+
+ expect(queryByText('Loading...')).toBeInTheDocument();
+
+ await waitFor(() => {
+ expect(queryByText('Test Event')).toBeInTheDocument();
+ expect(queryByText(formatDate(mockEvent.startDate))).toBeInTheDocument();
+ expect(queryByTitle('Event Date')).toBeInTheDocument();
+ });
+ });
+
+ it('Component handles error state gracefully', async () => {
+ const errorMock = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ error: new Error('An error occurred'),
+ },
+ ];
+
+ const { queryByText } = render(
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(queryByText('Loading...')).not.toBeInTheDocument();
+ // The component doesn't explicitly render an error message, so we just check that the event details are not rendered
+ expect(queryByText('Test Event')).not.toBeInTheDocument();
+ });
+ });
+
+ it('Component renders link with correct URL', async () => {
+ const { container } = render(
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ const link = container.querySelector('a');
+ expect(link).not.toBeNull();
+ expect(link).toHaveAttribute('href', expect.stringContaining('/event/'));
+ });
+ });
+});
diff --git a/src/components/EventManagement/EventAttendance/AttendedEventList.tsx b/src/components/EventManagement/EventAttendance/AttendedEventList.tsx
new file mode 100644
index 0000000000..2d0286feb0
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/AttendedEventList.tsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import { TableBody, TableCell, TableRow, Table } from '@mui/material';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import { useQuery } from '@apollo/client';
+import { Link, useParams } from 'react-router-dom';
+import { formatDate } from 'utils/dateFormatter';
+import DateIcon from 'assets/svgs/cardItemDate.svg?react';
+interface InterfaceEventsAttended {
+ eventId: string;
+}
+/**
+ * Component to display a list of events attended by a member
+ * @param eventId - The ID of the event to display details for
+ * @returns A table row containing event details with a link to the event
+ */
+const AttendedEventList: React.FC = ({ eventId }) => {
+ const { orgId: currentOrg } = useParams();
+ const { data, loading, error } = useQuery(EVENT_DETAILS, {
+ variables: { id: eventId },
+ fetchPolicy: 'cache-first',
+ errorPolicy: 'all',
+ });
+
+ if (error || data?.error) {
+ return Error loading event details. Please try again later.
;
+ }
+
+ const event = data?.event ?? null;
+
+ if (loading) return Loading...
;
+ return (
+
+
+
+ {event && (
+
+
+
+
+
+
{event.title}
+
{formatDate(event.startDate)}
+
+
+
+
+ )}
+
+
+
+ );
+};
+export default AttendedEventList;
diff --git a/src/components/EventManagement/EventAttendance/EventAttendance.spec.tsx b/src/components/EventManagement/EventAttendance/EventAttendance.spec.tsx
new file mode 100644
index 0000000000..bff1553cc0
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/EventAttendance.spec.tsx
@@ -0,0 +1,147 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import type { RenderResult } from '@testing-library/react';
+import {
+ render,
+ screen,
+ fireEvent,
+ cleanup,
+ waitFor,
+} from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { I18nextProvider } from 'react-i18next';
+import EventAttendance from './EventAttendance';
+import { store } from 'state/store';
+import userEvent from '@testing-library/user-event';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import i18n from 'utils/i18nForTest';
+import { MOCKS } from './Attendance.mocks';
+import { vi, describe, beforeEach, afterEach, expect, it } from 'vitest';
+
+const link = new StaticMockLink(MOCKS, true);
+
+async function wait(): Promise {
+ await waitFor(() => {
+ return Promise.resolve();
+ });
+}
+vi.mock('react-chartjs-2', () => ({
+ Line: () => null,
+ Bar: () => null,
+ Pie: () => null,
+}));
+
+const renderEventAttendance = (): RenderResult => {
+ return render(
+
+
+
+
+
+
+
+
+ ,
+ );
+};
+
+describe('Event Attendance Component', () => {
+ beforeEach(() => {
+ vi.mock('react-router-dom', async () => ({
+ ...(await vi.importActual('react-router-dom')),
+ }));
+ });
+
+ afterEach(() => {
+ vi.clearAllMocks();
+ cleanup();
+ });
+
+ it('Component loads correctly with table headers', async () => {
+ renderEventAttendance();
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('table-header-row')).toBeInTheDocument();
+ expect(screen.getByTestId('header-member-name')).toBeInTheDocument();
+ expect(screen.getByTestId('header-status')).toBeInTheDocument();
+ });
+ });
+
+ it('Renders attendee data correctly', async () => {
+ renderEventAttendance();
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('attendee-name-0')).toBeInTheDocument();
+ expect(screen.getByTestId('attendee-name-1')).toHaveTextContent(
+ 'Jane Smith',
+ );
+ });
+ });
+
+ it('Search filters attendees by name correctly', async () => {
+ renderEventAttendance();
+
+ await wait();
+
+ const searchInput = screen.getByTestId('searchByName');
+ fireEvent.change(searchInput, { target: { value: 'Bruce' } });
+
+ await waitFor(() => {
+ const filteredAttendee = screen.getByTestId('attendee-name-0');
+ expect(filteredAttendee).toHaveTextContent('Bruce Garza');
+ });
+ });
+
+ it('Sort functionality changes attendee order', async () => {
+ renderEventAttendance();
+
+ await wait();
+
+ const sortDropdown = screen.getByTestId('sort-dropdown');
+ userEvent.click(sortDropdown);
+ userEvent.click(screen.getByText('Sort'));
+
+ await waitFor(() => {
+ const attendees = screen.getAllByTestId('attendee-name-0');
+ expect(attendees[0]).toHaveTextContent('Bruce Garza');
+ });
+ });
+
+ it('Date filter shows correct number of attendees', async () => {
+ renderEventAttendance();
+
+ await wait();
+
+ userEvent.click(screen.getByText('Filter: All'));
+ userEvent.click(screen.getByText('This Month'));
+
+ await waitFor(() => {
+ expect(screen.getByText('Attendees not Found')).toBeInTheDocument();
+ });
+ });
+ it('Statistics modal opens and closes correctly', async () => {
+ renderEventAttendance();
+ await wait();
+
+ expect(screen.queryByTestId('attendance-modal')).not.toBeInTheDocument();
+
+ const statsButton = screen.getByTestId('stats-modal');
+ userEvent.click(statsButton);
+
+ await waitFor(() => {
+ expect(screen.getByTestId('attendance-modal')).toBeInTheDocument();
+ });
+
+ const closeButton = screen.getByTestId('close-button');
+ userEvent.click(closeButton);
+
+ await waitFor(() => {
+ expect(screen.queryByTestId('attendance-modal')).not.toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/components/EventManagement/EventAttendance/EventAttendance.tsx b/src/components/EventManagement/EventAttendance/EventAttendance.tsx
new file mode 100644
index 0000000000..06b047a973
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/EventAttendance.tsx
@@ -0,0 +1,376 @@
+import React, { useEffect, useMemo, useState } from 'react';
+import { BiSearch as Search } from 'react-icons/bi';
+import {
+ Paper,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Tooltip,
+} from '@mui/material';
+import {
+ Button,
+ Dropdown,
+ DropdownButton,
+ Table,
+ FormControl,
+} from 'react-bootstrap';
+import styles from '../../../style/app.module.css';
+import { useLazyQuery } from '@apollo/client';
+import { EVENT_ATTENDEES } from 'GraphQl/Queries/Queries';
+import { useParams, Link } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+import { AttendanceStatisticsModal } from './EventStatistics';
+import AttendedEventList from './AttendedEventList';
+import type { InterfaceMember } from './InterfaceEvents';
+enum FilterPeriod {
+ ThisMonth = 'This Month',
+ ThisYear = 'This Year',
+ All = 'All',
+}
+/**
+ * Component to manage and display event attendance information
+ * Includes filtering and sorting functionality for attendees
+ * @returns JSX element containing the event attendance interface
+ */
+function EventAttendance(): JSX.Element {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'eventAttendance',
+ });
+ const { eventId } = useParams<{ eventId: string }>();
+ const { orgId: currentUrl } = useParams();
+ const [filteredAttendees, setFilteredAttendees] = useState(
+ [],
+ );
+ const [sortOrder, setSortOrder] = useState<'ascending' | 'descending'>(
+ 'ascending',
+ );
+ const [filteringBy, setFilteringBy] = useState(
+ FilterPeriod.All,
+ );
+ const [show, setShow] = useState(false);
+
+ const sortAttendees = (attendees: InterfaceMember[]): InterfaceMember[] => {
+ return [...attendees].sort((a, b) => {
+ const nameA = `${a.firstName} ${a.lastName}`.toLowerCase();
+ const nameB = `${b.firstName} ${b.lastName}`.toLowerCase();
+ return sortOrder === 'ascending'
+ ? nameA.localeCompare(nameB)
+ : /*istanbul ignore next*/
+ nameB.localeCompare(nameA);
+ });
+ };
+
+ const filterAttendees = (attendees: InterfaceMember[]): InterfaceMember[] => {
+ const now = new Date();
+ return filteringBy === 'All'
+ ? attendees
+ : attendees.filter((attendee) => {
+ const attendeeDate = new Date(attendee.createdAt);
+ const isSameYear = attendeeDate.getFullYear() === now.getFullYear();
+ return filteringBy === 'This Month'
+ ? isSameYear && attendeeDate.getMonth() === now.getMonth()
+ : /*istanbul ignore next*/
+ isSameYear;
+ });
+ };
+
+ const filterAndSortAttendees = (
+ attendees: InterfaceMember[],
+ ): InterfaceMember[] => {
+ return sortAttendees(filterAttendees(attendees));
+ };
+ const searchEventAttendees = (value: string): void => {
+ const searchValueLower = value.toLowerCase().trim();
+
+ const filtered = (memberData?.event?.attendees ?? []).filter(
+ (attendee: InterfaceMember) => {
+ const fullName =
+ `${attendee.firstName} ${attendee.lastName}`.toLowerCase();
+ return (
+ attendee.firstName?.toLowerCase().includes(searchValueLower) ||
+ attendee.lastName?.toLowerCase().includes(searchValueLower) ||
+ attendee.email?.toLowerCase().includes(searchValueLower) ||
+ fullName.includes(searchValueLower)
+ );
+ },
+ );
+
+ const finalFiltered = filterAndSortAttendees(filtered);
+ setFilteredAttendees(finalFiltered);
+ };
+ const showModal = (): void => setShow(true);
+ const handleClose = (): void => setShow(false);
+
+ const statistics = useMemo(() => {
+ const totalMembers = filteredAttendees.length;
+ const membersAttended = filteredAttendees.filter(
+ (member) => member?.eventsAttended && member.eventsAttended.length > 0,
+ ).length;
+ const attendanceRate =
+ totalMembers > 0
+ ? Number(((membersAttended / totalMembers) * 100).toFixed(2))
+ : 0;
+
+ return { totalMembers, membersAttended, attendanceRate };
+ }, [filteredAttendees]);
+
+ const [getEventAttendees, { data: memberData, loading, error }] =
+ useLazyQuery(EVENT_ATTENDEES, {
+ variables: {
+ id: eventId,
+ },
+ fetchPolicy: 'cache-and-network',
+ nextFetchPolicy: 'cache-first',
+ errorPolicy: 'all',
+ notifyOnNetworkStatusChange: true,
+ });
+
+ useEffect(() => {
+ if (memberData?.event?.attendees) {
+ const updatedAttendees = filterAndSortAttendees(
+ memberData.event.attendees,
+ );
+ setFilteredAttendees(updatedAttendees);
+ }
+ }, [sortOrder, filteringBy, memberData]);
+
+ useEffect(() => {
+ getEventAttendees();
+ }, [eventId, getEventAttendees]);
+
+ if (loading) return {t('loading')}
;
+ /*istanbul ignore next*/
+ if (error) return {error.message}
;
+
+ return (
+
+
+
+
+
+ {t('historical_statistics')}
+
+
+
+
+ searchEventAttendees(e.target.value)}
+ />
+
+
+
+
+
+
+
+ Filter: {filteringBy}
+ >
+ }
+ onSelect={(eventKey) => setFilteringBy(eventKey as FilterPeriod)}
+ >
+ This Month
+ This Year
+ All
+
+
+
+ Sort
+ >
+ }
+ onSelect={
+ /*istanbul ignore next*/
+ (eventKey) => setSortOrder(eventKey as 'ascending' | 'descending')
+ }
+ >
+ Ascending
+ Descending
+
+
+
+ {/*
{totalMembers} */}
+
+
+
+
+
+ #
+
+
+ {t('Member Name')}
+
+
+ {t('Status')}
+
+
+ {t('Events Attended')}
+
+
+ {t('Task Assigned')}
+
+
+
+
+ {filteredAttendees.length === 0 ? (
+
+
+ {t('noAttendees')}
+
+
+ ) : (
+ filteredAttendees.map(
+ (member: InterfaceMember, index: number) => (
+
+
+ {index + 1}
+
+
+
+ {member.firstName} {member.lastName}
+
+
+
+ {member.__typename === 'User' ? t('Member') : t('Admin')}
+
+ (
+
+ ),
+ )}
+ >
+
+
+ {member.eventsAttended
+ ? member.eventsAttended.length
+ : /*istanbul ignore next*/
+ '0'}
+
+
+
+
+ {member.tagsAssignedWith ? (
+ /*istanbul ignore next*/
+ member.tagsAssignedWith.edges.map(
+ /*istanbul ignore next*/
+ (
+ edge: { node: { name: string } },
+ tagIndex: number,
+ ) => {edge.node.name}
,
+ )
+ ) : (
+ None
+ )}
+
+
+ ),
+ )
+ )}
+
+
+
+
+ );
+}
+
+export default EventAttendance;
diff --git a/src/components/EventManagement/EventAttendance/EventStatistics.spec.tsx b/src/components/EventManagement/EventAttendance/EventStatistics.spec.tsx
new file mode 100644
index 0000000000..38379b54fb
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/EventStatistics.spec.tsx
@@ -0,0 +1,363 @@
+import React from 'react';
+import { act, render, screen, waitFor } from '@testing-library/react';
+import { AttendanceStatisticsModal } from './EventStatistics';
+import { MockedProvider } from '@apollo/client/testing';
+import { EVENT_DETAILS, RECURRING_EVENTS } from 'GraphQl/Queries/Queries';
+import userEvent from '@testing-library/user-event';
+import { exportToCSV } from 'utils/chartToPdf';
+import { vi, describe, expect, it } from 'vitest';
+import type { Mock } from 'vitest';
+
+// Mock chart.js to avoid canvas errors
+vi.mock('react-chartjs-2', async () => ({
+ ...(await vi.importActual('react-chartjs-2')),
+ Line: () => null,
+ Bar: () => null,
+}));
+// Mock react-router-dom
+vi.mock('react-router-dom', async () => ({
+ ...(await vi.importActual('react-router-dom')),
+ useParams: () => ({
+ orgId: 'org123',
+ eventId: 'event123',
+ }),
+}));
+vi.mock('utils/chartToPdf', async () => ({
+ ...(await vi.importActual('utils/chartToPdf')),
+ exportToCSV: vi.fn(),
+}));
+const mocks = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: {
+ _id: 'event123',
+ title: 'Test Event',
+ description: 'Test Description',
+ startDate: '2023-01-01',
+ endDate: '2023-01-02',
+ startTime: '09:00',
+ endTime: '17:00',
+ allDay: false,
+ location: 'Test Location',
+ recurring: true,
+ baseRecurringEvent: { _id: 'base123' },
+ organization: {
+ _id: 'org123',
+ members: [
+ { _id: 'user1', firstName: 'John', lastName: 'Doe' },
+ { _id: 'user2', firstName: 'Jane', lastName: 'Smith' },
+ ],
+ },
+ attendees: [
+ {
+ _id: 'user1',
+ gender: 'MALE',
+ },
+ {
+ _id: 'user2',
+ gender: 'FEMALE',
+ },
+ ],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: RECURRING_EVENTS,
+ variables: { baseRecurringEventId: 'base123' },
+ },
+ result: {
+ data: {
+ getRecurringEvents: [
+ {
+ _id: 'event123',
+ startDate: '2023-01-01',
+ title: 'Test Event 1',
+ attendees: [
+ { _id: 'user1', gender: 'MALE' },
+ { _id: 'user2', gender: 'FEMALE' },
+ ],
+ },
+ {
+ _id: 'event456',
+ startDate: '2023-01-08',
+ title: 'Test Event 2',
+ attendees: [
+ { _id: 'user1', gender: 'MALE' },
+ { _id: 'user3', gender: 'OTHER' },
+ ],
+ },
+ {
+ _id: 'event789',
+ startDate: '2023-01-15',
+ title: 'Test Event 3',
+ attendees: [
+ { _id: 'user2', gender: 'FEMALE' },
+ { _id: 'user3', gender: 'OTHER' },
+ ],
+ },
+ ],
+ },
+ },
+ },
+];
+
+const mockMemberData = [
+ {
+ _id: 'user1',
+ firstName: 'John',
+ lastName: 'Doe',
+ gender: 'MALE',
+ birthDate: new Date('1990-01-01'),
+ email: 'john@example.com' as `${string}@${string}.${string}`,
+ createdAt: '2023-01-01',
+ __typename: 'User',
+ tagsAssignedWith: {
+ edges: [],
+ },
+ },
+ {
+ _id: 'user2',
+ firstName: 'Jane',
+ lastName: 'Smith',
+ gender: 'FEMALE',
+ birthDate: new Date('1985-05-05'),
+ email: 'jane@example.com' as `${string}@${string}.${string}`,
+ createdAt: '2023-01-01',
+ __typename: 'User',
+ tagsAssignedWith: {
+ edges: [],
+ },
+ },
+];
+
+const mockStatistics = {
+ totalMembers: 2,
+ membersAttended: 1,
+ attendanceRate: 50,
+};
+
+describe('AttendanceStatisticsModal', () => {
+ it('renders modal with correct initial state', async () => {
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('attendance-modal')).toBeInTheDocument();
+ expect(screen.getByTestId('gender-button')).toBeInTheDocument();
+ expect(screen.getByTestId('age-button')).toBeInTheDocument();
+ });
+ });
+
+ it('switches between gender and age demographics', async () => {
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ const genderButton = screen.getByTestId('gender-button');
+ const ageButton = screen.getByTestId('age-button');
+
+ userEvent.click(ageButton);
+ expect(ageButton).toHaveClass('btn-success');
+ expect(genderButton).toHaveClass('btn-light');
+
+ userEvent.click(genderButton);
+ expect(genderButton).toHaveClass('btn-success');
+ expect(ageButton).toHaveClass('btn-light');
+ });
+ });
+
+ it('handles data demographics export functionality', async () => {
+ const mockExportToCSV = vi.fn();
+ (exportToCSV as Mock).mockImplementation(mockExportToCSV);
+
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.getByRole('button', { name: 'Export Data' }),
+ ).toBeInTheDocument();
+ });
+
+ await act(async () => {
+ const exportButton = screen.getByRole('button', { name: 'Export Data' });
+ await userEvent.click(exportButton);
+ });
+
+ await act(async () => {
+ const demographicsExport = screen.getByTestId('demographics-export');
+ await userEvent.click(demographicsExport);
+ });
+
+ expect(mockExportToCSV).toHaveBeenCalled();
+ });
+ it('handles data trends export functionality', async () => {
+ const mockExportToCSV = vi.fn();
+ (exportToCSV as Mock).mockImplementation(mockExportToCSV);
+
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.getByRole('button', { name: 'Export Data' }),
+ ).toBeInTheDocument();
+ });
+
+ await act(async () => {
+ const exportButton = screen.getByRole('button', { name: 'Export Data' });
+ await userEvent.click(exportButton);
+ });
+
+ await act(async () => {
+ const demographicsExport = screen.getByTestId('trends-export');
+ await userEvent.click(demographicsExport);
+ });
+
+ expect(mockExportToCSV).toHaveBeenCalled();
+ });
+
+ it('displays recurring event data correctly', async () => {
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('today-button')).toBeInTheDocument();
+ });
+ });
+ it('handles pagination and today button correctly', async () => {
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ // Wait for initial render
+ await waitFor(() => {
+ expect(screen.getByTestId('today-button')).toBeInTheDocument();
+ });
+
+ // Test pagination
+ await act(async () => {
+ const nextButton = screen.getByAltText('right-arrow');
+ await userEvent.click(nextButton);
+ });
+
+ await act(async () => {
+ const prevButton = screen.getByAltText('left-arrow');
+ await userEvent.click(prevButton);
+ });
+
+ // Test today button
+ await act(async () => {
+ const todayButton = screen.getByTestId('today-button');
+ await userEvent.click(todayButton);
+ });
+
+ // Verify buttons are present and interactive
+ expect(screen.getByAltText('right-arrow')).toBeInTheDocument();
+ expect(screen.getByAltText('left-arrow')).toBeInTheDocument();
+ expect(screen.getByTestId('today-button')).toBeInTheDocument();
+ });
+
+ it('handles pagination in recurring events view', async () => {
+ render(
+
+ {}}
+ statistics={mockStatistics}
+ memberData={mockMemberData}
+ t={(key) => key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ const nextButton = screen.getByAltText('right-arrow');
+ const prevButton = screen.getByAltText('left-arrow');
+
+ userEvent.click(nextButton);
+ userEvent.click(prevButton);
+ });
+ });
+
+ it('closes modal correctly', async () => {
+ const handleClose = vi.fn();
+ render(
+
+ key}
+ />
+ ,
+ );
+
+ await waitFor(() => {
+ const closeButton = screen.getByTestId('close-button');
+ userEvent.click(closeButton);
+ expect(handleClose).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/components/EventManagement/EventAttendance/EventStatistics.tsx b/src/components/EventManagement/EventAttendance/EventStatistics.tsx
new file mode 100644
index 0000000000..686ad049fa
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/EventStatistics.tsx
@@ -0,0 +1,580 @@
+import React, { useEffect, useState, useMemo, useCallback } from 'react';
+import {
+ Modal,
+ Button,
+ ButtonGroup,
+ Tooltip,
+ OverlayTrigger,
+ Dropdown,
+} from 'react-bootstrap';
+import 'react-datepicker/dist/react-datepicker.css';
+import {
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ PointElement,
+ LineElement,
+ BarElement,
+ Title,
+ Tooltip as ChartToolTip,
+ Legend,
+} from 'chart.js';
+import { Bar, Line } from 'react-chartjs-2';
+import { useParams } from 'react-router-dom';
+import { EVENT_DETAILS, RECURRING_EVENTS } from 'GraphQl/Queries/Queries';
+import { useLazyQuery } from '@apollo/client';
+import { exportToCSV } from 'utils/chartToPdf';
+import type { ChartOptions, TooltipItem } from 'chart.js';
+import type {
+ InterfaceAttendanceStatisticsModalProps,
+ InterfaceEvent,
+ InterfaceRecurringEvent,
+} from './InterfaceEvents';
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ PointElement,
+ LineElement,
+ BarElement,
+ Title,
+ ChartToolTip,
+ Legend,
+);
+/**
+ * Component to display statistical information about event attendance
+ * Shows metrics like total attendees, filtering options, and attendance trends
+ * @returns JSX element with event statistics dashboard
+ */
+
+export const AttendanceStatisticsModal: React.FC<
+ InterfaceAttendanceStatisticsModalProps
+> = ({ show, handleClose, statistics, memberData, t }): JSX.Element => {
+ const [selectedCategory, setSelectedCategory] = useState('Gender');
+ const { orgId, eventId } = useParams();
+ const [currentPage, setCurrentPage] = useState(0);
+ const eventsPerPage = 10;
+ const [loadEventDetails, { data: eventData }] = useLazyQuery(EVENT_DETAILS);
+ const [loadRecurringEvents, { data: recurringData }] =
+ useLazyQuery(RECURRING_EVENTS);
+ const isEventRecurring = eventData?.event?.recurring;
+ const currentEventIndex = useMemo(() => {
+ if (!recurringData?.getRecurringEvents || !eventId) return -1;
+ return recurringData.getRecurringEvents.findIndex(
+ (event: InterfaceEvent) => event._id === eventId,
+ );
+ }, [recurringData, eventId]);
+ useEffect(() => {
+ if (currentEventIndex >= 0) {
+ const newPage = Math.floor(currentEventIndex / eventsPerPage);
+ setCurrentPage(newPage);
+ }
+ }, [currentEventIndex, eventsPerPage]);
+ const filteredRecurringEvents = useMemo(
+ () => recurringData?.getRecurringEvents || [],
+ [recurringData],
+ );
+ const totalEvents = filteredRecurringEvents.length;
+ const totalPages = Math.ceil(totalEvents / eventsPerPage);
+
+ const paginatedRecurringEvents = useMemo(() => {
+ const startIndex = currentPage * eventsPerPage;
+ const endIndex = Math.min(startIndex + eventsPerPage, totalEvents);
+ return filteredRecurringEvents.slice(startIndex, endIndex);
+ }, [filteredRecurringEvents, currentPage, eventsPerPage, totalEvents]);
+
+ const attendeeCounts = useMemo(
+ () =>
+ paginatedRecurringEvents.map(
+ (event: InterfaceEvent) => event.attendees.length,
+ ),
+ [paginatedRecurringEvents],
+ );
+ const chartOptions: ChartOptions<'line'> = {
+ responsive: true,
+ maintainAspectRatio: false,
+ animation: false,
+ scales: { y: { beginAtZero: true } },
+ plugins: {
+ tooltip: {
+ callbacks: {
+ label:
+ /*istanbul ignore next*/
+ (context: TooltipItem<'line'>) => {
+ const label = context.dataset.label || '';
+ const value = context.parsed.y;
+ const isCurrentEvent =
+ paginatedRecurringEvents[context.dataIndex]._id === eventId;
+ return isCurrentEvent
+ ? `${label}: ${value} (Current Event)`
+ : `${label}: ${value}`;
+ },
+ },
+ },
+ },
+ };
+ const eventLabels = useMemo(
+ () =>
+ paginatedRecurringEvents.map((event: InterfaceEvent) => {
+ const date = (() => {
+ try {
+ const eventDate = new Date(event.startDate);
+ if (Number.isNaN(eventDate.getTime())) {
+ console.error(`Invalid date for event: ${event._id}`);
+
+ return 'Invalid date';
+ }
+ return eventDate.toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ });
+ } catch (error) {
+ console.error(
+ `Error formatting date for event: ${event._id}`,
+ error,
+ );
+
+ return 'Invalid date';
+ }
+ })();
+ // Highlight the current event in the label
+ return event._id === eventId ? `→ ${date}` : date;
+ }),
+ [paginatedRecurringEvents, eventId],
+ );
+
+ const maleCounts = useMemo(
+ () =>
+ paginatedRecurringEvents.map(
+ (event: InterfaceEvent) =>
+ event.attendees.filter((attendee) => attendee.gender === 'MALE')
+ .length,
+ ),
+ [paginatedRecurringEvents],
+ );
+
+ const femaleCounts = useMemo(
+ () =>
+ paginatedRecurringEvents.map(
+ (event: InterfaceEvent) =>
+ event.attendees.filter((attendee) => attendee.gender === 'FEMALE')
+ .length,
+ ),
+ [paginatedRecurringEvents],
+ );
+
+ const otherCounts = useMemo(
+ () =>
+ paginatedRecurringEvents.map(
+ (event: InterfaceEvent) =>
+ event.attendees.filter(
+ (attendee) =>
+ attendee.gender === 'OTHER' || attendee.gender === null,
+ ).length,
+ ),
+ [paginatedRecurringEvents],
+ );
+
+ const chartData = useMemo(
+ () => ({
+ labels: eventLabels,
+ datasets: [
+ {
+ label: 'Attendee Count',
+ data: attendeeCounts,
+ fill: true,
+ borderColor: '#008000',
+ pointRadius: paginatedRecurringEvents.map(
+ (event: InterfaceRecurringEvent) => (event._id === eventId ? 8 : 3),
+ ),
+ pointBackgroundColor: paginatedRecurringEvents.map(
+ (event: InterfaceRecurringEvent) =>
+ event._id === eventId ? '#008000' : 'transparent',
+ ),
+ },
+ {
+ label: 'Male Attendees',
+ data: maleCounts,
+ fill: false,
+ borderColor: '#0000FF',
+ },
+ {
+ label: 'Female Attendees',
+ data: femaleCounts,
+ fill: false,
+ borderColor: '#FF1493',
+ },
+ {
+ label: 'Other Attendees',
+ data: otherCounts,
+ fill: false,
+ borderColor: '#FFD700',
+ },
+ ],
+ }),
+ [eventLabels, attendeeCounts, maleCounts, femaleCounts, otherCounts],
+ );
+
+ const handlePreviousPage = useCallback(
+ /*istanbul ignore next*/
+ () => {
+ setCurrentPage((prevPage) => Math.max(prevPage - 1, 0));
+ },
+ [],
+ );
+
+ const handleNextPage = useCallback(
+ /*istanbul ignore next*/
+ () => {
+ if (currentPage < totalPages - 1) {
+ setCurrentPage((prevPage) => prevPage + 1);
+ }
+ },
+ [currentPage, totalPages],
+ );
+
+ const handleDateChange = useCallback((date: Date | null) => {
+ if (date) {
+ setCurrentPage(0);
+ }
+ }, []);
+ const categoryLabels = useMemo(
+ () =>
+ selectedCategory === 'Gender'
+ ? ['Male', 'Female', 'Other']
+ : ['Under 18', '18-40', 'Over 40'],
+ [selectedCategory],
+ );
+
+ const categoryData = useMemo(
+ () =>
+ selectedCategory === 'Gender'
+ ? [
+ memberData.filter((member) => member.gender === 'MALE').length,
+ memberData.filter((member) => member.gender === 'FEMALE').length,
+ memberData.filter(
+ (member) =>
+ member.gender === 'OTHER' ||
+ member.gender === null ||
+ member.gender === '',
+ ).length,
+ ]
+ : [
+ memberData.filter((member) => {
+ const today = new Date();
+ const birthDate = new Date(member.birthDate);
+ let age = today.getFullYear() - birthDate.getFullYear();
+ const monthDiff = today.getMonth() - birthDate.getMonth();
+ if (
+ monthDiff < 0 ||
+ (monthDiff === 0 && today.getDate() < birthDate.getDate())
+ ) {
+ /*istanbul ignore next*/
+ age--;
+ }
+ return age < 18;
+ }).length,
+ memberData.filter((member) => {
+ const age =
+ new Date().getFullYear() -
+ new Date(member.birthDate).getFullYear();
+ return age >= 18 && age <= 40;
+ }).length,
+ memberData.filter(
+ (member) =>
+ new Date().getFullYear() -
+ new Date(member.birthDate).getFullYear() >
+ 40,
+ ).length,
+ ],
+ [selectedCategory, memberData],
+ );
+
+ const handleCategoryChange = useCallback((category: string): void => {
+ setSelectedCategory(category);
+ }, []);
+
+ const exportTrendsToCSV = useCallback(() => {
+ const headers = [
+ 'Date',
+ 'Attendee Count',
+ 'Male Attendees',
+ 'Female Attendees',
+ 'Other Attendees',
+ ];
+ const data = [
+ headers,
+ ...eventLabels.map((label: string, index: number) => [
+ label,
+ attendeeCounts[index],
+ maleCounts[index],
+ femaleCounts[index],
+ otherCounts[index],
+ ]),
+ ];
+ exportToCSV(data, 'attendance_trends.csv');
+ }, [eventLabels, attendeeCounts, maleCounts, femaleCounts, otherCounts]);
+
+ const exportDemographicsToCSV = useCallback(() => {
+ const headers = [selectedCategory, 'Count'];
+ const data = [
+ headers,
+ ...categoryLabels.map((label, index) => [label, categoryData[index]]),
+ ];
+ exportToCSV(data, `${selectedCategory.toLowerCase()}_demographics.csv`);
+ }, [selectedCategory, categoryLabels, categoryData]);
+
+ const handleExport = (eventKey: string | null): void => {
+ switch (eventKey) {
+ case 'trends':
+ try {
+ exportTrendsToCSV();
+ } catch (error) {
+ console.error('Failed to export trends:', error);
+ }
+ break;
+ case 'demographics':
+ try {
+ exportDemographicsToCSV();
+ } catch (error) {
+ console.error('Failed to export demographics:', error);
+ }
+ break;
+ default:
+ return;
+ }
+ };
+ useEffect(() => {
+ if (eventId) {
+ loadEventDetails({ variables: { id: eventId } });
+ }
+ }, [eventId, loadEventDetails]);
+ useEffect(() => {
+ if (eventId && orgId && eventData?.event?.baseRecurringEvent?._id) {
+ loadRecurringEvents({
+ variables: {
+ baseRecurringEventId: eventData?.event?.baseRecurringEvent?._id,
+ },
+ });
+ }
+ }, [eventId, orgId, eventData, loadRecurringEvents]);
+ return (
+
+
+
+ {t('historical_statistics')}
+
+
+
+
+
+ {isEventRecurring ? (
+
+
+
+
+
Previous Page}
+ >
+
+
+
+
+
handleDateChange(new Date())}
+ aria-label="Go to today"
+ >
+ Today
+
+
Next Page}
+ >
+ = totalPages - 1}
+ aria-label="Next page"
+ >
+
+
+
+
+
+ ) : (
+
+
+ {statistics.totalMembers}
+
+
+
+ )}
+
+
+ handleCategoryChange('Gender')}
+ >
+ Gender
+
+ handleCategoryChange('Age')}
+ >
+ Age
+
+
+
+
+
+
+
+
+
+
+ Export Data
+
+
+ {isEventRecurring && (
+
+ Trends
+
+ )}
+
+ Demographics
+
+
+
+
+ Close
+
+
+
+ );
+};
diff --git a/src/components/EventManagement/EventAttendance/InterfaceEvents.ts b/src/components/EventManagement/EventAttendance/InterfaceEvents.ts
new file mode 100644
index 0000000000..7fc75ae4af
--- /dev/null
+++ b/src/components/EventManagement/EventAttendance/InterfaceEvents.ts
@@ -0,0 +1,82 @@
+export interface InterfaceAttendanceStatisticsModalProps {
+ show: boolean;
+ handleClose: () => void;
+ statistics: {
+ totalMembers: number;
+ membersAttended: number;
+ attendanceRate: number;
+ };
+ memberData: InterfaceMember[];
+ t: (key: string) => string;
+}
+
+export interface InterfaceMember {
+ createdAt: string;
+ firstName: string;
+ lastName: string;
+ email: `${string}@${string}.${string}`;
+ gender: string;
+ eventsAttended?: {
+ _id: string;
+ }[];
+ birthDate: Date;
+ __typename: string;
+ _id: string;
+ tagsAssignedWith: {
+ edges: {
+ cursor: string;
+ node: {
+ name: string;
+ };
+ }[];
+ };
+}
+
+export interface InterfaceEvent {
+ _id: string;
+ title: string;
+ description: string;
+ startDate: string;
+ endDate: string;
+ location: string;
+ startTime: string;
+ endTime: string;
+ allDay: boolean;
+ recurring: boolean;
+ recurrenceRule: {
+ recurrenceStartDate: string;
+ recurrenceEndDate?: string | null;
+ frequency: string;
+ weekDays: string[];
+ interval: number;
+ count?: number;
+ weekDayOccurenceInMonth?: number;
+ };
+ isRecurringEventException: boolean;
+ isPublic: boolean;
+ isRegisterable: boolean;
+ attendees: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ email: string;
+ gender: string;
+ birthDate: string;
+ }[];
+ __typename: string;
+}
+
+export interface InterfaceRecurringEvent {
+ _id: string;
+ title: string;
+ startDate: string;
+ endDate: string;
+ frequency: InterfaceEvent['recurrenceRule']['frequency'];
+ interval: InterfaceEvent['recurrenceRule']['interval'];
+ attendees: {
+ _id: string;
+ gender: 'MALE' | 'FEMALE' | 'OTHER' | 'PREFER_NOT_TO_SAY';
+ }[];
+ isPublic: boolean;
+ isRegisterable: boolean;
+}
diff --git a/src/components/EventManagement/EventRegistrant/EventRegistrants.spec.tsx b/src/components/EventManagement/EventRegistrant/EventRegistrants.spec.tsx
new file mode 100644
index 0000000000..828362706a
--- /dev/null
+++ b/src/components/EventManagement/EventRegistrant/EventRegistrants.spec.tsx
@@ -0,0 +1,264 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import type { RenderResult } from '@testing-library/react';
+import { render, screen, cleanup, waitFor } from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { I18nextProvider } from 'react-i18next';
+import EventRegistrants from './EventRegistrants';
+import { store } from 'state/store';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import i18n from 'utils/i18nForTest';
+import { REGISTRANTS_MOCKS } from './Registrations.mocks';
+import { MOCKS as ATTENDEES_MOCKS } from '../EventAttendance/Attendance.mocks';
+import { vi } from 'vitest';
+import { EVENT_REGISTRANTS, EVENT_ATTENDEES } from 'GraphQl/Queries/Queries';
+
+const COMBINED_MOCKS = [...REGISTRANTS_MOCKS, ...ATTENDEES_MOCKS];
+
+const link = new StaticMockLink(COMBINED_MOCKS, true);
+
+async function wait(): Promise {
+ await waitFor(() => {
+ return Promise.resolve();
+ });
+}
+
+const renderEventRegistrants = (): RenderResult => {
+ return render(
+
+
+
+
+
+
+
+
+ ,
+ );
+};
+
+describe('Event Registrants Component', () => {
+ beforeEach(() => {
+ vi.mock('react-router-dom', async () => {
+ const actual = await vi.importActual('react-router-dom');
+ return {
+ ...actual,
+ useParams: () => ({ eventId: 'event123', orgId: 'org123' }),
+ useNavigate: vi.fn(),
+ };
+ });
+ });
+
+ afterEach(() => {
+ vi.clearAllMocks();
+ cleanup();
+ });
+
+ test('Component loads correctly with table headers', async () => {
+ renderEventRegistrants();
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('table-header-serial')).toBeInTheDocument();
+ expect(screen.getByTestId('table-header-registrant')).toBeInTheDocument();
+ expect(screen.getByTestId('table-header-created-at')).toBeInTheDocument();
+ expect(
+ screen.getByTestId('table-header-add-registrant'),
+ ).toBeInTheDocument();
+ });
+ });
+
+ test('Renders registrants button correctly', async () => {
+ renderEventRegistrants();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('stats-modal')).toBeInTheDocument();
+ expect(screen.getByTestId('filter-button')).toBeInTheDocument();
+ });
+ });
+
+ test('Handles empty registrants list', async () => {
+ const emptyMocks = [
+ {
+ request: {
+ query: EVENT_REGISTRANTS,
+ variables: { eventId: '660fdf7d2c1ef6c7db1649ad' },
+ },
+ result: {
+ data: {
+ getEventAttendeesByEventId: [],
+ },
+ },
+ },
+ {
+ request: {
+ query: EVENT_ATTENDEES,
+ variables: { id: '660fdf7d2c1ef6c7db1649ad' },
+ },
+ result: {
+ data: {
+ event: {
+ attendees: [],
+ },
+ },
+ },
+ },
+ ];
+
+ const customLink = new StaticMockLink(emptyMocks, true);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('no-registrants')).toBeInTheDocument();
+ });
+ });
+
+ test('Successfully combines and displays registrant and attendee data', async () => {
+ const mockData = [
+ {
+ request: {
+ query: EVENT_REGISTRANTS,
+ variables: { eventId: 'event123' },
+ },
+ result: {
+ data: {
+ getEventAttendeesByEventId: [
+ {
+ _id: '1',
+ userId: 'user1',
+ isRegistered: true,
+ __typename: 'EventAttendee',
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: EVENT_ATTENDEES,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: {
+ attendees: [
+ {
+ _id: 'user1',
+ firstName: 'John',
+ lastName: 'Doe',
+ createdAt: '2023-09-25T10:00:00.000Z',
+ __typename: 'User',
+ },
+ ],
+ },
+ },
+ },
+ },
+ ];
+
+ const customLink = new StaticMockLink(mockData, true);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('registrant-row-0')).toBeInTheDocument();
+ });
+
+ // Validate mapped data
+ expect(screen.getByTestId('attendee-name-0')).toHaveTextContent('John Doe');
+ expect(screen.getByTestId('registrant-registered-at-0')).toHaveTextContent(
+ '2023-09-25',
+ );
+ expect(screen.getByTestId('registrant-created-at-0')).toHaveTextContent(
+ '10:00:00',
+ );
+ });
+
+ test('Handles missing attendee data with fallback values', async () => {
+ const mocksWithMissingFields = [
+ {
+ request: {
+ query: EVENT_REGISTRANTS,
+ variables: { eventId: 'event123' },
+ },
+ result: {
+ data: {
+ getEventAttendeesByEventId: [
+ {
+ _id: '1',
+ userId: 'user1',
+ isRegistered: true,
+ __typename: 'EventAttendee',
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: EVENT_ATTENDEES,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: {
+ attendees: [
+ {
+ _id: 'user1',
+ firstName: 'Jane',
+ lastName: 'Doe',
+ createdAt: null,
+ __typename: 'User',
+ },
+ ],
+ },
+ },
+ },
+ },
+ ];
+
+ const customLink = new StaticMockLink(mocksWithMissingFields, true);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByTestId('registrant-row-0')).toBeInTheDocument();
+ });
+
+ // Validate fallback values
+ expect(screen.getByTestId('attendee-name-0')).toHaveTextContent('Jane Doe');
+ expect(screen.getByTestId('registrant-created-at-0')).toHaveTextContent(
+ 'N/A',
+ );
+ });
+});
diff --git a/src/components/EventManagement/EventRegistrant/EventRegistrants.tsx b/src/components/EventManagement/EventRegistrant/EventRegistrants.tsx
new file mode 100644
index 0000000000..efcc2e91f7
--- /dev/null
+++ b/src/components/EventManagement/EventRegistrant/EventRegistrants.tsx
@@ -0,0 +1,227 @@
+import React, { useEffect, useState, useCallback } from 'react';
+import {
+ Paper,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ TableBody,
+} from '@mui/material';
+import { Button, Table } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+import style from '../../../style/app.module.css';
+import { useLazyQuery } from '@apollo/client';
+import { EVENT_ATTENDEES, EVENT_REGISTRANTS } from 'GraphQl/Queries/Queries';
+import { useParams } from 'react-router-dom';
+import type { InterfaceMember } from '../EventAttendance/InterfaceEvents';
+import { EventRegistrantsWrapper } from 'components/EventRegistrantsModal/EventRegistrantsWrapper';
+import { CheckInWrapper } from 'components/CheckIn/CheckInWrapper';
+/**
+ * Interface for user data
+ */
+interface InterfaceUser {
+ _id: string;
+ userId: string;
+ isRegistered: boolean;
+ __typename: string;
+ time: string;
+}
+/**
+ * Component to manage and display event registrant information
+ * Includes adding new registrants and check-in functionality for registrants
+ * @returns JSX element containing the event attendance interface
+ */
+function EventRegistrants(): JSX.Element {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'eventRegistrant',
+ });
+ const { orgId, eventId } = useParams<{ orgId: string; eventId: string }>();
+ const [registrants, setRegistrants] = useState([]);
+ const [attendees, setAttendees] = useState([]);
+ const [combinedData, setCombinedData] = useState<
+ (InterfaceUser & Partial)[]
+ >([]);
+ // Fetch registrants
+ const [getEventRegistrants] = useLazyQuery(EVENT_REGISTRANTS, {
+ variables: { eventId },
+ fetchPolicy: 'cache-and-network',
+ onCompleted: (data) => {
+ if (data?.getEventAttendeesByEventId) {
+ const mappedData = data.getEventAttendeesByEventId.map(
+ (attendee: InterfaceUser) => ({
+ _id: attendee._id,
+ userId: attendee.userId,
+ isRegistered: attendee.isRegistered,
+ __typename: attendee.__typename,
+ }),
+ );
+ setRegistrants(mappedData);
+ }
+ },
+ });
+ // Fetch attendees
+ const [getEventAttendees] = useLazyQuery(EVENT_ATTENDEES, {
+ variables: { id: eventId },
+ fetchPolicy: 'cache-and-network',
+ onCompleted: (data) => {
+ if (data?.event?.attendees) {
+ setAttendees(data.event.attendees);
+ }
+ },
+ });
+ // callback function to refresh the data
+ const refreshData = useCallback(() => {
+ getEventRegistrants();
+ getEventAttendees();
+ }, [getEventRegistrants, getEventAttendees]);
+ useEffect(() => {
+ refreshData();
+ }, [refreshData]);
+ // Combine registrants and attendees data
+ useEffect(() => {
+ if (registrants.length > 0 && attendees.length > 0) {
+ const mergedData = registrants.map((registrant) => {
+ const matchedAttendee = attendees.find(
+ (attendee) => attendee._id === registrant.userId,
+ );
+ const [date, timeWithMilliseconds] = matchedAttendee?.createdAt
+ ? matchedAttendee.createdAt.split('T')
+ : ['N/A', 'N/A'];
+ const [time] =
+ timeWithMilliseconds !== 'N/A'
+ ? timeWithMilliseconds.split('.')
+ : ['N/A'];
+ return {
+ ...registrant,
+ firstName: matchedAttendee?.firstName || 'N/A',
+ lastName: matchedAttendee?.lastName || 'N/A',
+ createdAt: date,
+ time: time,
+ };
+ });
+ setCombinedData(mergedData);
+ }
+ }, [registrants, attendees]);
+ return (
+
+
+ {eventId ? (
+
+ ) : (
+
+ )}
+
+
+ {t('allRegistrants')}
+
+
+
+
+
+
+
+ {t('serialNumber')}
+
+
+ {t('registrant')}
+
+
+ {t('registeredAt')}
+
+
+ {t('createdAt')}
+
+
+ {t('addRegistrant')}
+
+
+
+
+ {combinedData.length === 0 ? (
+
+
+ {t('noRegistrantsFound')}
+
+
+ ) : (
+ combinedData.map((data, index) => (
+
+
+ {index + 1}
+
+
+ {data.firstName} {data.lastName}
+
+
+ {data.createdAt}
+
+
+ {data.time}
+
+
+ {eventId && orgId && (
+
+ )}
+
+
+ ))
+ )}
+
+
+
+
+ );
+}
+export default EventRegistrants;
diff --git a/src/components/EventManagement/EventRegistrant/Registrations.mocks.ts b/src/components/EventManagement/EventRegistrant/Registrations.mocks.ts
new file mode 100644
index 0000000000..d71714459e
--- /dev/null
+++ b/src/components/EventManagement/EventRegistrant/Registrations.mocks.ts
@@ -0,0 +1,67 @@
+import { EVENT_REGISTRANTS, EVENT_ATTENDEES } from 'GraphQl/Queries/Queries';
+
+export const REGISTRANTS_MOCKS = [
+ {
+ request: {
+ query: EVENT_REGISTRANTS,
+ variables: { eventId: '660fdf7d2c1ef6c7db1649ad' },
+ },
+ result: {
+ data: {
+ getEventAttendeesByEventId: [
+ {
+ _id: '6589386a2caa9d8d69087484',
+ userId: '6589386a2caa9d8d69087484',
+ isRegistered: true,
+ __typename: 'EventAttendee',
+ },
+ {
+ _id: '6589386a2caa9d8d69087485',
+ userId: '6589386a2caa9d8d69087485',
+ isRegistered: true,
+ __typename: 'EventAttendee',
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: EVENT_ATTENDEES,
+ variables: { id: '660fdf7d2c1ef6c7db1649ad' },
+ },
+ result: {
+ data: {
+ event: {
+ attendees: [
+ {
+ _id: '6589386a2caa9d8d69087484',
+ firstName: 'Bruce',
+ lastName: 'Garza',
+ createdAt: '2023-04-13T10:23:17.742Z',
+ __typename: 'User',
+ },
+ {
+ _id: '6589386a2caa9d8d69087485',
+ firstName: 'Jane',
+ lastName: 'Smith',
+ createdAt: '2023-04-13T10:23:17.742Z',
+ __typename: 'User',
+ },
+ ],
+ },
+ },
+ },
+ },
+];
+
+export const REGISTRANTS_MOCKS_ERROR = [
+ {
+ // Error mock for EVENT_REGISTRANTS query
+ request: {
+ query: EVENT_REGISTRANTS,
+ variables: { eventId: 'event123' },
+ },
+ error: new Error('An error occurred while fetching registrants'),
+ },
+];
diff --git a/src/components/EventRegistrantsModal/AddOnSpotAttendee.spec.tsx b/src/components/EventRegistrantsModal/AddOnSpotAttendee.spec.tsx
new file mode 100644
index 0000000000..66f0dda38b
--- /dev/null
+++ b/src/components/EventRegistrantsModal/AddOnSpotAttendee.spec.tsx
@@ -0,0 +1,181 @@
+import React from 'react';
+import { render, fireEvent, screen, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/react-testing';
+import { BrowserRouter } from 'react-router-dom';
+import { SIGNUP_MUTATION } from 'GraphQl/Mutations/mutations';
+import AddOnSpotAttendee from './AddOnSpotAttendee';
+import userEvent from '@testing-library/user-event';
+import type { RenderResult } from '@testing-library/react';
+import { toast } from 'react-toastify';
+import { Provider } from 'react-redux';
+import { I18nextProvider } from 'react-i18next';
+import { store } from 'state/store';
+import i18nForTest from '../../utils/i18nForTest';
+import { describe, expect, vi } from 'vitest';
+
+vi.mock('react-toastify', () => ({
+ toast: {
+ success: vi.fn(),
+ error: vi.fn(),
+ },
+}));
+
+const mockProps = {
+ show: true,
+ handleClose: vi.fn(),
+ reloadMembers: vi.fn(),
+};
+vi.mock('react-router-dom', async () => {
+ const actual = await vi.importActual('react-router-dom');
+ return {
+ ...actual,
+ useParams: () => ({ eventId: '123', orgId: '123' }),
+ };
+});
+
+const MOCKS = [
+ {
+ request: {
+ query: SIGNUP_MUTATION,
+ variables: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'john@example.com',
+ phoneNo: '1234567890',
+ gender: 'Male',
+ password: '123456',
+ orgId: '123',
+ },
+ },
+ result: {
+ data: {
+ signUp: {
+ user: {
+ _id: '1',
+ },
+ accessToken: 'mock-access-token',
+ refreshToken: 'mock-refresh-token',
+ },
+ },
+ },
+ },
+];
+
+const ERROR_MOCKS = [
+ {
+ ...MOCKS[0],
+ error: new Error('Failed to add attendee'),
+ },
+];
+
+const renderAddOnSpotAttendee = (): RenderResult => {
+ return render(
+
+
+
+
+
+
+
+
+ ,
+ );
+};
+
+describe('AddOnSpotAttendee Component', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('renders the component with all form fields', async () => {
+ renderAddOnSpotAttendee();
+
+ expect(screen.getByText('On-spot Attendee')).toBeInTheDocument();
+ expect(screen.getByLabelText('First Name')).toBeInTheDocument();
+ expect(screen.getByLabelText('Last Name')).toBeInTheDocument();
+ expect(screen.getByLabelText('Email')).toBeInTheDocument();
+ expect(screen.getByLabelText('Phone No.')).toBeInTheDocument();
+ expect(screen.getByLabelText('Gender')).toBeInTheDocument();
+ });
+
+ it('handles form input changes correctly', async () => {
+ renderAddOnSpotAttendee();
+
+ const firstNameInput = screen.getByLabelText('First Name');
+ const lastNameInput = screen.getByLabelText('Last Name');
+ const emailInput = screen.getByLabelText('Email');
+
+ userEvent.type(firstNameInput, 'John');
+ userEvent.type(lastNameInput, 'Doe');
+ userEvent.type(emailInput, 'john@example.com');
+
+ expect(firstNameInput).toHaveValue('John');
+ expect(lastNameInput).toHaveValue('Doe');
+ expect(emailInput).toHaveValue('john@example.com');
+ });
+
+ it('submits form successfully and calls necessary callbacks', async () => {
+ renderAddOnSpotAttendee();
+
+ userEvent.type(screen.getByLabelText('First Name'), 'John');
+ userEvent.type(screen.getByLabelText('Last Name'), 'Doe');
+ userEvent.type(screen.getByLabelText('Email'), 'john@example.com');
+ userEvent.type(screen.getByLabelText('Phone No.'), '1234567890');
+ const genderSelect = screen.getByLabelText('Gender');
+ fireEvent.change(genderSelect, { target: { value: 'Male' } });
+
+ fireEvent.submit(screen.getByTestId('onspot-attendee-form'));
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalled();
+ expect(mockProps.reloadMembers).toHaveBeenCalled();
+ expect(mockProps.handleClose).toHaveBeenCalled();
+ });
+ });
+
+ it('displays error when organization ID is missing', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ fireEvent.submit(screen.getByTestId('onspot-attendee-form'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+ it('displays error when required fields are missing', async () => {
+ renderAddOnSpotAttendee();
+
+ fireEvent.submit(screen.getByTestId('onspot-attendee-form'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+
+ it('handles mutation error appropriately', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ userEvent.type(screen.getByLabelText('First Name'), 'John');
+ userEvent.type(screen.getByLabelText('Last Name'), 'Doe');
+ fireEvent.submit(screen.getByTestId('onspot-attendee-form'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/components/EventRegistrantsModal/AddOnSpotAttendee.tsx b/src/components/EventRegistrantsModal/AddOnSpotAttendee.tsx
new file mode 100644
index 0000000000..6de839ce04
--- /dev/null
+++ b/src/components/EventRegistrantsModal/AddOnSpotAttendee.tsx
@@ -0,0 +1,209 @@
+import { SIGNUP_MUTATION } from 'GraphQl/Mutations/mutations';
+import React, { useState } from 'react';
+import { Modal, Form, Button, Spinner } from 'react-bootstrap';
+import { useParams } from 'react-router-dom';
+import { useMutation } from '@apollo/client';
+import { toast } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
+import type {
+ InterfaceAddOnSpotAttendeeProps,
+ InterfaceFormData,
+} from 'utils/interfaces';
+import { useTranslation } from 'react-i18next';
+import { errorHandler } from 'utils/errorHandler';
+/**
+ * Modal component for adding on-spot attendees to an event
+ * @param show - Boolean to control modal visibility
+ * @param handleClose - Function to handle modal close
+ * @param reloadMembers - Function to refresh member list after adding attendee
+ * @returns Modal component with form for adding new attendee
+ */
+const AddOnSpotAttendee: React.FC = ({
+ show,
+ handleClose,
+ reloadMembers,
+}) => {
+ const [formData, setFormData] = useState({
+ firstName: '',
+ lastName: '',
+ email: '',
+ phoneNo: '',
+ gender: '',
+ });
+ const { t } = useTranslation('translation', { keyPrefix: 'onSpotAttendee' });
+ const { t: tCommon } = useTranslation('common');
+ const { orgId } = useParams<{ orgId: string }>();
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [addSignUp] = useMutation(SIGNUP_MUTATION);
+ const validateForm = (): boolean => {
+ if (!formData.firstName || !formData.lastName || !formData.email) {
+ toast.error(t('invalidDetailsMessage'));
+ return false;
+ }
+ return true;
+ };
+
+ const resetForm = (): void => {
+ setFormData({
+ firstName: '',
+ lastName: '',
+ email: '',
+ phoneNo: '',
+ gender: '',
+ });
+ };
+
+ const handleChange = (
+ e: React.ChangeEvent<
+ HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
+ >,
+ ): void => {
+ const target = e.target as
+ | HTMLInputElement
+ | HTMLSelectElement
+ | HTMLTextAreaElement;
+ setFormData((prev) => ({
+ ...prev,
+ [target.name]: target.value,
+ }));
+ };
+
+ const handleSubmit = async (
+ e: React.FormEvent,
+ ): Promise => {
+ e.preventDefault();
+
+ if (!validateForm()) {
+ return;
+ }
+
+ setIsSubmitting(true);
+
+ try {
+ const response = await addSignUp({
+ variables: {
+ ...formData,
+ password: '123456',
+ orgId,
+ },
+ });
+
+ if (response.data?.signUp) {
+ toast.success(t('attendeeAddedSuccess'));
+ resetForm();
+ reloadMembers();
+ handleClose();
+ }
+ } catch (error) {
+ /* istanbul ignore next */
+ errorHandler(t, error as Error);
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+ return (
+ <>
+
+
+ {t('title')}
+
+
+
+ {t('phoneNumber')}
+
+
+
+
+ {tCommon('email')}
+
+
+
+
+ {tCommon('gender')}
+
+ {t('selectGender')}
+ {t('male')}
+ {t('female')}
+ {t('other')}
+
+
+
+
+ {isSubmitting ? (
+ <>
+
+ {t('addingAttendee')}
+ >
+ ) : (
+ 'Add'
+ )}
+
+
+
+
+ >
+ );
+};
+
+export default AddOnSpotAttendee;
diff --git a/src/components/EventRegistrantsModal/EventRegistrantsModal.test.tsx b/src/components/EventRegistrantsModal/EventRegistrantsModal.spec.tsx
similarity index 77%
rename from src/components/EventRegistrantsModal/EventRegistrantsModal.test.tsx
rename to src/components/EventRegistrantsModal/EventRegistrantsModal.spec.tsx
index 8a084fef24..4f422ceb7f 100644
--- a/src/components/EventRegistrantsModal/EventRegistrantsModal.test.tsx
+++ b/src/components/EventRegistrantsModal/EventRegistrantsModal.spec.tsx
@@ -15,6 +15,7 @@ import i18nForTest from 'utils/i18nForTest';
import { ToastContainer } from 'react-toastify';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { describe, test, expect, vi } from 'vitest';
const queryMockWithoutRegistrant = [
{
@@ -41,7 +42,19 @@ const queryMockWithRegistrant = [
result: {
data: {
event: {
- attendees: [{ _id: 'user1', firstName: 'John', lastName: 'Doe' }],
+ attendees: [
+ {
+ _id: 'user1',
+ firstName: 'John',
+ lastName: 'Doe',
+ createdAt: '2023-01-01',
+ gender: 'Male',
+ birthDate: '1990-01-01',
+ eventsAttended: {
+ _id: 'event123',
+ },
+ },
+ ],
},
},
},
@@ -64,9 +77,9 @@ const queryMockOrgMembers = [
_id: 'user1',
firstName: 'John',
lastName: 'Doe',
- email: 'johndoe@palisadoes.com',
- image: '',
- createdAt: '12/12/22',
+ image: null,
+ email: 'johndoe@example.com',
+ createdAt: '2023-01-01',
organizationsBlockedBy: [],
},
],
@@ -76,6 +89,24 @@ const queryMockOrgMembers = [
},
},
];
+const queryMockWithoutOrgMembers = [
+ {
+ request: {
+ query: MEMBERS_LIST,
+ variables: { id: 'org123' },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ _id: 'org123',
+ members: [],
+ },
+ ],
+ },
+ },
+ },
+];
const successfulAddRegistrantMock = [
{
@@ -130,7 +161,7 @@ describe('Testing Event Registrants Modal', () => {
show: true,
eventId: 'event123',
orgId: 'org123',
- handleClose: jest.fn(),
+ handleClose: vi.fn(),
};
test('The modal should be rendered, correct text must be displayed when there are no attendees and add attendee mutation must function properly', async () => {
@@ -287,7 +318,7 @@ describe('Testing Event Registrants Modal', () => {
});
test('Delete attendee mutation must fail properly', async () => {
- const { queryByText, queryByTestId } = render(
+ const { queryByText, getByTestId } = render(
{
await waitFor(() => expect(queryByText('John Doe')).toBeInTheDocument());
- fireEvent.click(queryByTestId('CancelIcon') as Element);
+ const deleteButton = getByTestId('CancelIcon');
+ fireEvent.click(deleteButton);
await waitFor(() =>
expect(queryByText('Removing the attendee...')).toBeInTheDocument(),
@@ -325,4 +357,48 @@ describe('Testing Event Registrants Modal', () => {
expect(queryByText('Error removing attendee')).toBeInTheDocument(),
);
});
+ test('Autocomplete functionality works correctly', async () => {
+ const { getByTitle, getByText, getByPlaceholderText } = render(
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ // Wait for loading state to finish
+ await waitFor(() => {
+ const autocomplete = getByPlaceholderText(
+ 'Choose the user that you want to add',
+ );
+ expect(autocomplete).toBeInTheDocument();
+ });
+
+ // Test empty state with no options
+ const autocomplete = getByPlaceholderText(
+ 'Choose the user that you want to add',
+ );
+ fireEvent.change(autocomplete, { target: { value: 'NonexistentUser' } });
+
+ await waitFor(() => {
+ expect(getByText('No Registrations found')).toBeInTheDocument();
+ expect(getByText('Add Onspot Registration')).toBeInTheDocument();
+ });
+
+ // Test clicking "Add Onspot Registration"
+ fireEvent.click(getByText('Add Onspot Registration'));
+ expect(getByText('Add Onspot Registration')).toBeInTheDocument();
+ const closeButton = getByTitle('Close');
+ fireEvent.click(closeButton);
+ });
});
diff --git a/src/components/EventRegistrantsModal/EventRegistrantsModal.tsx b/src/components/EventRegistrantsModal/EventRegistrantsModal.tsx
index bd2adc8a2c..d48d3b7439 100644
--- a/src/components/EventRegistrantsModal/EventRegistrantsModal.tsx
+++ b/src/components/EventRegistrantsModal/EventRegistrantsModal.tsx
@@ -14,6 +14,7 @@ import Stack from '@mui/material/Stack';
import TextField from '@mui/material/TextField';
import Autocomplete from '@mui/material/Autocomplete';
import { useTranslation } from 'react-i18next';
+import AddOnSpotAttendee from './AddOnSpotAttendee';
// Props for the EventRegistrantsModal component
type ModalPropType = {
@@ -43,6 +44,7 @@ interface InterfaceUser {
export const EventRegistrantsModal = (props: ModalPropType): JSX.Element => {
const { eventId, orgId, handleClose, show } = props;
const [member, setMember] = useState(null);
+ const [open, setOpen] = useState(false);
// Hooks for mutation operations
const [addRegistrantMutation] = useMutation(ADD_EVENT_ATTENDEE);
@@ -125,6 +127,19 @@ export const EventRegistrantsModal = (props: ModalPropType): JSX.Element => {
return (
<>
+ setOpen(false)
+ }
+ reloadMembers={
+ /*istanbul ignore next */
+ () => {
+ attendeesRefetch();
+ }
+ }
+ />
Event Registrants
@@ -153,6 +168,19 @@ export const EventRegistrantsModal = (props: ModalPropType): JSX.Element => {
onChange={(_, newMember): void => {
setMember(newMember);
}}
+ noOptionsText={
+
+
No Registrations found
+
{
+ setOpen(true);
+ }}
+ >
+ Add Onspot Registration
+
+
+ }
options={memberData.organizations[0].members}
getOptionLabel={(member: InterfaceUser): string =>
`${member.firstName} ${member.lastName}`
@@ -160,6 +188,7 @@ export const EventRegistrantsModal = (props: ModalPropType): JSX.Element => {
renderInput={(params): React.ReactNode => (
diff --git a/src/components/EventRegistrantsModal/EventRegistrantsWrapper.module.css b/src/components/EventRegistrantsModal/EventRegistrantsWrapper.module.css
deleted file mode 100644
index 59b31333af..0000000000
--- a/src/components/EventRegistrantsModal/EventRegistrantsWrapper.module.css
+++ /dev/null
@@ -1,13 +0,0 @@
-button .iconWrapper {
- width: 36px;
- padding-right: 4px;
- margin-right: 4px;
- transform: translateY(4px);
-}
-
-button .iconWrapperSm {
- width: 36px;
- display: flex;
- justify-content: center;
- align-items: center;
-}
diff --git a/src/components/EventRegistrantsModal/EventRegistrantsWrapper.test.tsx b/src/components/EventRegistrantsModal/EventRegistrantsWrapper.spec.tsx
similarity index 95%
rename from src/components/EventRegistrantsModal/EventRegistrantsWrapper.test.tsx
rename to src/components/EventRegistrantsModal/EventRegistrantsWrapper.spec.tsx
index d1707e8520..97a0d1f00f 100644
--- a/src/components/EventRegistrantsModal/EventRegistrantsWrapper.test.tsx
+++ b/src/components/EventRegistrantsModal/EventRegistrantsWrapper.spec.tsx
@@ -11,6 +11,7 @@ import i18nForTest from 'utils/i18nForTest';
import { ToastContainer } from 'react-toastify';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { describe, test, expect } from 'vitest';
const queryMock = [
{
@@ -77,7 +78,7 @@ describe('Testing Event Registrants Wrapper', () => {
);
// Open the modal
- fireEvent.click(queryByText('Show Registrants') as Element);
+ fireEvent.click(queryByText('Add Registrants') as Element);
await waitFor(() =>
expect(queryByText('Event Registrants')).toBeInTheDocument(),
diff --git a/src/components/EventRegistrantsModal/EventRegistrantsWrapper.tsx b/src/components/EventRegistrantsModal/EventRegistrantsWrapper.tsx
index b198fcdd6d..36b6679a9f 100644
--- a/src/components/EventRegistrantsModal/EventRegistrantsWrapper.tsx
+++ b/src/components/EventRegistrantsModal/EventRegistrantsWrapper.tsx
@@ -1,13 +1,13 @@
import React, { useState } from 'react';
import { EventRegistrantsModal } from './EventRegistrantsModal';
import { Button } from 'react-bootstrap';
-import IconComponent from 'components/IconComponent/IconComponent';
-import styles from './EventRegistrantsWrapper.module.css';
+import style from '../../style/app.module.css';
// Props for the EventRegistrantsWrapper component
type PropType = {
eventId: string;
orgId: string;
+ onUpdate?: () => void;
};
/**
@@ -20,37 +20,37 @@ type PropType = {
export const EventRegistrantsWrapper = ({
eventId,
orgId,
+ onUpdate,
}: PropType): JSX.Element => {
// State to control the visibility of the modal
const [showModal, setShowModal] = useState(false);
+ const handleClose = (): void => {
+ setShowModal(false);
+ // Call onUpdate after modal is closed
+ if (onUpdate) {
+ onUpdate();
+ }
+ };
return (
<>
{/* Button to open the event registrants modal */}
{
setShowModal(true); // Show the modal when button is clicked
}}
>
-
-
-
- Show Registrants
+ Add Registrants
{/* Render the EventRegistrantsModal if showModal is true */}
{showModal && (
{
- setShowModal(false); // Hide the modal when closed
- }}
+ handleClose={handleClose}
eventId={eventId}
orgId={orgId}
/>
diff --git a/src/components/GroupChatDetails/GroupChatDetails.module.css b/src/components/GroupChatDetails/GroupChatDetails.module.css
new file mode 100644
index 0000000000..61119875f0
--- /dev/null
+++ b/src/components/GroupChatDetails/GroupChatDetails.module.css
@@ -0,0 +1,94 @@
+.groupInfo {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.memberList {
+ max-height: 300px;
+ overflow: scroll;
+}
+
+.memberList::-webkit-scrollbar {
+ display: none;
+}
+
+.listItem {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.groupMembersList {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.groupMembersList p {
+ margin: 0;
+ color: #959595;
+}
+
+.membersImage {
+ width: 40px !important;
+}
+
+.groupImage {
+ margin-bottom: 10px;
+}
+
+.editImgBtn {
+ padding: 2px 6px 6px 8px;
+ border-radius: 100%;
+ background-color: white;
+ border: 1px solid #959595;
+ color: #959595;
+ outline: none;
+ position: relative;
+ top: -40px;
+ left: 40px;
+}
+
+.chatImage {
+ height: 120px;
+ border-radius: 100%;
+ width: 120px;
+}
+
+.editChatNameContainer {
+ display: flex;
+ gap: 15px;
+ align-items: center;
+ font-size: 20px;
+ margin-bottom: 10px;
+}
+
+.editChatNameContainer input {
+ border: none;
+ border-bottom: 1px solid rgb(171, 171, 171);
+ outline: none;
+ padding: 0px 5px;
+}
+
+.editChatNameContainer h3 {
+ margin: 0;
+}
+
+.cancelIcon {
+ color: rgb(197, 42, 42);
+ cursor: pointer;
+ font-size: 16px;
+}
+
+.checkIcon {
+ color: rgb(42, 197, 42);
+ cursor: pointer;
+}
+
+.chatUserDetails {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+}
diff --git a/src/components/GroupChatDetails/GroupChatDetails.test.tsx b/src/components/GroupChatDetails/GroupChatDetails.test.tsx
new file mode 100644
index 0000000000..4a4aa73b61
--- /dev/null
+++ b/src/components/GroupChatDetails/GroupChatDetails.test.tsx
@@ -0,0 +1,603 @@
+import React from 'react';
+import {
+ render,
+ screen,
+ // fireEvent,
+ // waitFor,
+ act,
+ fireEvent,
+ waitFor,
+} from '@testing-library/react';
+import GroupChatDetails from './GroupChatDetails';
+import { MockedProvider } from '@apollo/client/testing';
+import {
+ ADD_USER_TO_GROUP_CHAT,
+ UPDATE_CHAT,
+} from 'GraphQl/Mutations/OrganizationMutations';
+import { USERS_CONNECTION_LIST } from 'GraphQl/Queries/Queries';
+import { I18nextProvider, initReactI18next } from 'react-i18next';
+import i18n from 'i18next';
+import { useLocalStorage } from 'utils/useLocalstorage';
+
+const { setItem } = useLocalStorage();
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+i18n.use(initReactI18next).init({
+ lng: 'en',
+ resources: {
+ en: {
+ translation: {
+ // Add your translations here
+ },
+ },
+ },
+});
+
+const mockChat = {
+ _id: '1',
+ isGroup: true,
+ name: 'Test Group',
+ image: '',
+ messages: [],
+ admins: [
+ {
+ _id: 'hbjguyt7y9890i9otyugttiyuh',
+ firstName: 'Admin',
+ lastName: 'User',
+ email: 'admin@example.com',
+ },
+ ],
+ users: [
+ {
+ _id: 'hbjguyt7y9890i9otyugttiyuh',
+ firstName: 'Admin',
+ lastName: 'User',
+ email: 'admin@example.com',
+ },
+ {
+ _id: 'gjhnbbjku68979089ujhvserty',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ },
+ ],
+ unseenMessagesByUsers: JSON.parse(
+ '{"hbjguyt7y9890i9otyugttiyuh": 0, "gjhnbbjku68979089ujhvserty": 0}',
+ ),
+ description: 'Test Description',
+};
+
+const mocks = [
+ {
+ request: {
+ query: USERS_CONNECTION_LIST,
+ variables: {
+ firstName_contains: '',
+ lastName_contains: '',
+ },
+ },
+ result: {
+ data: {
+ users: [
+ {
+ user: {
+ firstName: 'Deanne',
+ lastName: 'Marks',
+ image: null,
+ _id: '6589389d2caa9d8d69087487',
+ email: 'testuser8@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ organizationsBlockedBy: [],
+ joinedOrganizations: [
+ {
+ _id: '6537904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Queens',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 Coffee Street',
+ line2: 'Apartment 501',
+ postalCode: '11427',
+ sortingCode: 'ABC-133',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6637904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Staten Island',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 church Street',
+ line2: 'Apartment 499',
+ postalCode: '10301',
+ sortingCode: 'ABC-122',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6737904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Brooklyn',
+ countryCode: 'US',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Main Street',
+ line2: 'Apt 456',
+ postalCode: '10004',
+ sortingCode: 'ABC-789',
+ state: 'NY',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6437904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Bronx',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 Random Street',
+ line2: 'Apartment 456',
+ postalCode: '10451',
+ sortingCode: 'ABC-123',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ ],
+ __typename: 'User',
+ },
+ appUserProfile: {
+ _id: '64378abd85308f171cf2993d',
+ adminFor: [],
+ isSuperAdmin: false,
+ createdOrganizations: [],
+ createdEvents: [],
+ eventAdmin: [],
+ __typename: 'AppUserProfile',
+ },
+ __typename: 'UserData',
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: USERS_CONNECTION_LIST,
+ variables: {
+ firstName_contains: 'Disha',
+ lastName_contains: '',
+ },
+ },
+ result: {
+ data: {
+ users: [
+ {
+ user: {
+ firstName: 'Deanne',
+ lastName: 'Marks',
+ image: null,
+ _id: '6589389d2caa9d8d69087487',
+ email: 'testuser8@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ organizationsBlockedBy: [],
+ joinedOrganizations: [
+ {
+ _id: '6537904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Queens',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 Coffee Street',
+ line2: 'Apartment 501',
+ postalCode: '11427',
+ sortingCode: 'ABC-133',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6637904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Staten Island',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 church Street',
+ line2: 'Apartment 499',
+ postalCode: '10301',
+ sortingCode: 'ABC-122',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6737904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Brooklyn',
+ countryCode: 'US',
+ dependentLocality: 'Sample Dependent Locality',
+ line1: '123 Main Street',
+ line2: 'Apt 456',
+ postalCode: '10004',
+ sortingCode: 'ABC-789',
+ state: 'NY',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ {
+ _id: '6437904485008f171cf29924',
+ name: 'Unity Foundation',
+ image: null,
+ address: {
+ city: 'Bronx',
+ countryCode: 'US',
+ dependentLocality: 'Some Dependent Locality',
+ line1: '123 Random Street',
+ line2: 'Apartment 456',
+ postalCode: '10451',
+ sortingCode: 'ABC-123',
+ state: 'NYC',
+ __typename: 'Address',
+ },
+ createdAt: '2023-04-13T05:16:52.827Z',
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ __typename: 'Organization',
+ },
+ ],
+ __typename: 'User',
+ },
+ appUserProfile: {
+ _id: '64378abd85308f171cf2993d',
+ adminFor: [],
+ isSuperAdmin: false,
+ createdOrganizations: [],
+ createdEvents: [],
+ eventAdmin: [],
+ __typename: 'AppUserProfile',
+ },
+ __typename: 'UserData',
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: ADD_USER_TO_GROUP_CHAT,
+ variables: { userId: '6589389d2caa9d8d69087487', chatId: '1' },
+ },
+ result: {
+ data: {
+ addUserToGroupChat: {
+ _id: '1',
+ success: true,
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_CHAT,
+ variables: { input: { _id: '1', image: '', name: 'New Group name' } },
+ },
+ result: {
+ data: {
+ updateChat: {
+ _id: '1',
+ success: true,
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_CHAT,
+ variables: {},
+ },
+ result: {
+ data: {
+ updateChat: {
+ _id: '1',
+ success: true,
+ },
+ },
+ },
+ },
+];
+
+describe('GroupChatDetails', () => {
+ it('renders correctly', async () => {
+ const chat = {
+ _id: '1',
+ isGroup: true,
+ name: 'Test Group',
+ image: '',
+ messages: [],
+ admins: [],
+ users: [],
+ unseenMessagesByUsers: JSON.parse(
+ '{"hbjguyt7y9890i9otyugttiyuh": 0, "gjhnbbjku68979089ujhvserty": 0}',
+ ),
+ description: 'Test Description',
+ };
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ expect(screen.getByText('Test Group')).toBeInTheDocument();
+ expect(screen.getByText('Test Description')).toBeInTheDocument();
+ const closeButton = screen.getByRole('button', { name: /close/i });
+ expect(closeButton).toBeInTheDocument();
+
+ fireEvent.click(closeButton);
+ });
+
+ it('edit chat title', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+ await act(async () => {
+ fireEvent.click(await screen.findByTestId('editTitleBtn'));
+ });
+
+ await waitFor(async () => {
+ expect(await screen.findByTestId('chatNameInput')).toBeInTheDocument();
+ });
+ await act(async () => {
+ fireEvent.change(await screen.findByTestId('chatNameInput'), {
+ target: { value: 'New Group name' },
+ });
+ });
+
+ await act(async () => {
+ fireEvent.click(await screen.findByTestId('updateTitleBtn'));
+ });
+
+ await wait();
+
+ await waitFor(
+ async () => {
+ expect(await screen.findByTestId('editTitleBtn')).toBeInTheDocument();
+ },
+ { timeout: 5000 },
+ );
+
+ await act(async () => {
+ fireEvent.click(await screen.findByTestId('editTitleBtn'));
+ });
+
+ await waitFor(async () => {
+ expect(await screen.findByTestId('cancelEditBtn')).toBeInTheDocument();
+ });
+
+ act(() => {
+ fireEvent.click(screen.getByTestId('cancelEditBtn'));
+ });
+
+ await waitFor(
+ async () => {
+ expect(await screen.findByTestId('editTitleBtn')).toBeInTheDocument();
+ },
+ { timeout: 5000 },
+ );
+ });
+
+ it('add user to group chat', async () => {
+ setItem('userId', 'hbjguyt7y9890i9otyugttiyuh');
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+ await act(async () => {
+ fireEvent.click(await screen.findByTestId('addMembers'));
+ });
+
+ await waitFor(async () => {
+ expect(await screen.findByTestId('searchUser')).toBeInTheDocument();
+ });
+
+ await act(async () => {
+ fireEvent.change(await screen.findByTestId('searchUser'), {
+ target: { value: 'Disha' },
+ });
+ });
+
+ await act(async () => {
+ fireEvent.click(await screen.findByTestId('searchBtn'));
+ });
+
+ await wait();
+
+ await waitFor(
+ async () => {
+ expect(await screen.findByTestId('user')).toBeInTheDocument();
+ },
+ { timeout: 5000 },
+ );
+
+ await act(async () => {
+ fireEvent.click(await screen.findByTestId('addUserBtn'));
+ });
+
+ await wait(10000);
+ });
+ // test case for updating group chat image
+ it('update group chat image', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ await waitFor(
+ async () => {
+ expect(await screen.findByTestId('editImageBtn')).toBeInTheDocument();
+ },
+ { timeout: 5000 },
+ );
+
+ await act(async () => {
+ fireEvent.click(await screen.findByTestId('editImageBtn'));
+ });
+
+ const fileInput = screen.getByTestId('fileInput');
+ const smallFile = new File(['small-file-content'], 'small-file.jpg'); // Small file
+
+ Object.defineProperty(fileInput, 'files', {
+ value: [smallFile],
+ });
+
+ fireEvent.change(fileInput);
+
+ await wait(10000);
+ });
+});
diff --git a/src/components/GroupChatDetails/GroupChatDetails.tsx b/src/components/GroupChatDetails/GroupChatDetails.tsx
new file mode 100644
index 0000000000..e252a286be
--- /dev/null
+++ b/src/components/GroupChatDetails/GroupChatDetails.tsx
@@ -0,0 +1,442 @@
+import { Paper, TableBody } from '@mui/material';
+import React, { useRef, useState } from 'react';
+import { Button, Form, ListGroup, Modal } from 'react-bootstrap';
+import styles from './GroupChatDetails.module.css';
+import type { ApolloQueryResult } from '@apollo/client';
+import { useMutation, useQuery } from '@apollo/client';
+import {
+ ADD_USER_TO_GROUP_CHAT,
+ UPDATE_CHAT,
+} from 'GraphQl/Mutations/OrganizationMutations';
+import Table from '@mui/material/Table';
+import TableCell, { tableCellClasses } from '@mui/material/TableCell';
+import TableContainer from '@mui/material/TableContainer';
+import TableHead from '@mui/material/TableHead';
+import TableRow from '@mui/material/TableRow';
+import { styled } from '@mui/material/styles';
+import type { InterfaceQueryUserListItem } from 'utils/interfaces';
+import { USERS_CONNECTION_LIST } from 'GraphQl/Queries/Queries';
+import Loader from 'components/Loader/Loader';
+import { Search, Add } from '@mui/icons-material';
+import { useTranslation } from 'react-i18next';
+import Avatar from 'components/Avatar/Avatar';
+import { FiEdit } from 'react-icons/fi';
+import { FaCheck, FaX } from 'react-icons/fa6';
+import convertToBase64 from 'utils/convertToBase64';
+import useLocalStorage from 'utils/useLocalstorage';
+
+type DirectMessage = {
+ _id: string;
+ createdAt: Date;
+ sender: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ image: string;
+ };
+ replyTo:
+ | {
+ _id: string;
+ createdAt: Date;
+ sender: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ image: string;
+ };
+ messageContent: string;
+ receiver: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ };
+ }
+ | undefined;
+ messageContent: string;
+ media: string;
+};
+
+type Chat = {
+ _id: string;
+ isGroup: boolean;
+ name?: string;
+ image?: string;
+ messages: DirectMessage[];
+ admins: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ email: string;
+ }[];
+ users: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ email: string;
+ }[];
+ unseenMessagesByUsers: string;
+ description: string;
+};
+
+interface InterfaceGoroupChatDetailsProps {
+ toggleGroupChatDetailsModal: () => void;
+ groupChatDetailsModalisOpen: boolean;
+ chat: Chat;
+ chatRefetch: (
+ variables?:
+ | Partial<{
+ id: string;
+ }>
+ | undefined,
+ ) => Promise>;
+}
+
+const StyledTableCell = styled(TableCell)(({ theme }) => ({
+ [`&.${tableCellClasses.head}`]: {
+ backgroundColor: ['#31bb6b', '!important'],
+ color: theme.palette.common.white,
+ },
+ [`&.${tableCellClasses.body}`]: {
+ fontSize: 14,
+ },
+}));
+
+const StyledTableRow = styled(TableRow)(() => ({
+ '&:last-child td, &:last-child th': {
+ border: 0,
+ },
+}));
+
+/**
+ * Component for displaying and managing group chat details.
+ *
+ * @param props - The component props.
+ * @param toggleGroupChatDetailsModal - Function to toggle the group chat details modal.
+ * @param groupChatDetailsModalisOpen - Boolean indicating if the group chat details modal is open.
+ * @param chat - The chat object containing details about the group chat.
+ * @param chatRefetch - Function to refetch the chat data.
+ * @returns The rendered component.
+ */
+export default function groupChatDetails({
+ toggleGroupChatDetailsModal,
+ groupChatDetailsModalisOpen,
+ chat,
+ chatRefetch,
+}: InterfaceGoroupChatDetailsProps): JSX.Element {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'userChat',
+ });
+
+ const [userName, setUserName] = useState('');
+ const { getItem } = useLocalStorage();
+
+ const userId = getItem('userId');
+
+ const [editChatTitle, setEditChatTitle] = useState(false);
+ const [chatName, setChatName] = useState(chat?.name || '');
+
+ const [addUser] = useMutation(ADD_USER_TO_GROUP_CHAT);
+
+ const [addUserModalisOpen, setAddUserModalisOpen] = useState(false);
+
+ function openAddUserModal(): void {
+ setAddUserModalisOpen(true);
+ }
+
+ const toggleAddUserModal = (): void =>
+ setAddUserModalisOpen(!addUserModalisOpen);
+
+ const {
+ data: allUsersData,
+ loading: allUsersLoading,
+ refetch: allUsersRefetch,
+ } = useQuery(USERS_CONNECTION_LIST, {
+ variables: {
+ firstName_contains: '',
+ lastName_contains: '',
+ },
+ });
+
+ const addUserToGroupChat = async (userId: string): Promise => {
+ await addUser({
+ variables: {
+ userId,
+ chatId: chat._id,
+ },
+ });
+ };
+
+ const handleUserModalSearchChange = (e: React.FormEvent): void => {
+ e.preventDefault();
+ /* istanbul ignore next */
+ const [firstName, lastName] = userName.split(' ');
+
+ const newFilterData = {
+ firstName_contains: firstName || '',
+ lastName_contains: lastName || '',
+ };
+
+ allUsersRefetch({
+ ...newFilterData,
+ });
+ };
+
+ const [selectedImage, setSelectedImage] = useState('');
+
+ const [updateChat] = useMutation(UPDATE_CHAT);
+
+ const fileInputRef = useRef(null);
+
+ const handleImageClick = (): void => {
+ fileInputRef?.current?.click();
+ };
+
+ const handleImageChange = async (
+ e: React.ChangeEvent,
+ ): Promise => {
+ const file = e.target.files?.[0];
+ if (file) {
+ const base64 = await convertToBase64(file);
+ setSelectedImage(base64);
+ await updateChat();
+ await chatRefetch();
+ setSelectedImage('');
+ }
+ };
+
+ return (
+ <>
+
+
+ {t('groupInfo')}
+
+
+
+
+ {chat?.image ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {editChatTitle ? (
+
+ {
+ setChatName(e.target.value);
+ }}
+ />
+ {
+ await updateChat({
+ variables: {
+ input: {
+ _id: chat._id,
+ image: selectedImage ? selectedImage : '',
+ name: chatName,
+ },
+ },
+ });
+ setEditChatTitle(false);
+ await chatRefetch();
+ }}
+ />
+ {
+ setEditChatTitle(false);
+ setChatName(chat.name || '');
+ }}
+ />
+
+ ) : (
+
+
{chat?.name}
+ {
+ setEditChatTitle(true);
+ }}
+ />
+
+ )}
+
+
+ {chat?.users.length} {t('members')}
+
+
{chat?.description}
+
+
+
+
+ {chat.users.length} {t('members')}
+
+
+ {chat.admins.map((admin) => admin._id).includes(userId) && (
+ {
+ openAddUserModal();
+ }}
+ >
+ {t('addMembers')}
+
+ )}
+ {chat.users.map(
+ (user: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ }) => (
+
+
+
+ {user.firstName} {user.lastName}
+
+
+
+ {chat.admins
+ .map((admin) => admin._id)
+ .includes(user._id) && (
+
Admin
+ )}
+
+
+ ),
+ )}
+
+
+
+
+
+
+ {'Chat'}
+
+
+ {allUsersLoading ? (
+ <>
+
+ >
+ ) : (
+ <>
+
+
{
+ const { value } = e.target;
+ setUserName(value);
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+ #
+ {'user'}
+ {'Chat'}
+
+
+
+ {console.log(allUsersData)}
+ {allUsersData &&
+ allUsersData.users.length > 0 &&
+ allUsersData.users.map(
+ (
+ userDetails: InterfaceQueryUserListItem,
+ index: number,
+ ) => (
+
+
+ {index + 1}
+
+
+ {userDetails.user.firstName +
+ ' ' +
+ userDetails.user.lastName}
+
+ {userDetails.user.email}
+
+
+ {
+ await addUserToGroupChat(
+ userDetails.user._id,
+ );
+ toggleAddUserModal();
+ chatRefetch({ id: chat._id });
+ }}
+ data-testid="addUserBtn"
+ >
+ {t('add')}
+
+
+
+ ),
+ )}
+
+
+
+ >
+ )}
+
+
+ >
+ );
+}
diff --git a/src/components/IconComponent/IconComponent.test.tsx b/src/components/IconComponent/IconComponent.spec.tsx
similarity index 90%
rename from src/components/IconComponent/IconComponent.test.tsx
rename to src/components/IconComponent/IconComponent.spec.tsx
index 686a8e6f75..b398fe7986 100644
--- a/src/components/IconComponent/IconComponent.test.tsx
+++ b/src/components/IconComponent/IconComponent.spec.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import IconComponent from './IconComponent';
+import { describe, it, expect } from 'vitest';
const screenTestIdMap: Record> = {
MyOrganizations: {
@@ -83,6 +84,14 @@ const screenTestIdMap: Record> = {
name: 'My Pledges',
testId: 'Icon-Component-My-Pledges',
},
+ LeaveOrganization: {
+ name: 'Leave Organization',
+ testId: 'Icon-Component-Leave-Organization',
+ },
+ Volunteer: {
+ name: 'Volunteer',
+ testId: 'Icon-Component-Volunteer',
+ },
default: {
name: 'default',
testId: 'Icon-Component-DefaultIcon',
diff --git a/src/components/IconComponent/IconComponent.tsx b/src/components/IconComponent/IconComponent.tsx
index 4708dc7966..dd104c0408 100644
--- a/src/components/IconComponent/IconComponent.tsx
+++ b/src/components/IconComponent/IconComponent.tsx
@@ -19,6 +19,8 @@ import PostsIcon from 'assets/svgs/posts.svg?react';
import SettingsIcon from 'assets/svgs/settings.svg?react';
import VenueIcon from 'assets/svgs/venues.svg?react';
import RequestsIcon from 'assets/svgs/requests.svg?react';
+import ExitToAppIcon from '@mui/icons-material/ExitToApp';
+import { MdOutlineVolunteerActivism } from 'react-icons/md';
import React from 'react';
@@ -133,6 +135,22 @@ const iconComponent = (props: InterfaceIconComponent): JSX.Element => {
stroke={props.fill}
/>
);
+ case 'Leave Organization':
+ return (
+
+ );
+ case 'Volunteer':
+ return (
+
+ );
default:
return (
{
+ test('Component should be rendered properly', () => {
+ render( );
+
+ expect(screen.getByTestId('infiniteScrollLoader')).toBeInTheDocument();
+ expect(
+ screen.getByTestId('infiniteScrollLoaderSpinner'),
+ ).toBeInTheDocument();
+ });
+});
diff --git a/src/components/InfiniteScrollLoader/InfiniteScrollLoader.tsx b/src/components/InfiniteScrollLoader/InfiniteScrollLoader.tsx
new file mode 100644
index 0000000000..7846889cdb
--- /dev/null
+++ b/src/components/InfiniteScrollLoader/InfiniteScrollLoader.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import styles from './InfiniteScrollLoader.module.css';
+
+/**
+ * A Loader for infinite scroll.
+ */
+
+const InfiniteScrollLoader = (): JSX.Element => {
+ return (
+
+ );
+};
+
+export default InfiniteScrollLoader;
diff --git a/src/components/LeftDrawer/LeftDrawer.module.css b/src/components/LeftDrawer/LeftDrawer.module.css
deleted file mode 100644
index a9d330339f..0000000000
--- a/src/components/LeftDrawer/LeftDrawer.module.css
+++ /dev/null
@@ -1,239 +0,0 @@
-.leftDrawer {
- width: calc(300px + 2rem);
- position: fixed;
- top: 0;
- bottom: 0;
- z-index: 100;
- display: flex;
- flex-direction: column;
- padding: 1rem 1rem 0 1rem;
- background-color: var(--bs-white);
- transition: 0.5s;
- font-family: var(--bs-leftDrawer-font-family);
-}
-
-.activeDrawer {
- width: calc(300px + 2rem);
- position: fixed;
- top: 0;
- left: 0;
- bottom: 0;
- animation: comeToRightBigScreen 0.5s ease-in-out;
-}
-
-.inactiveDrawer {
- position: fixed;
- top: 0;
- left: calc(-300px - 2rem);
- bottom: 0;
- animation: goToLeftBigScreen 0.5s ease-in-out;
-}
-
-.leftDrawer .talawaLogo {
- width: 100%;
- height: 65px;
-}
-
-.leftDrawer .talawaText {
- font-size: 20px;
- text-align: center;
- font-weight: 500;
-}
-
-.leftDrawer .titleHeader {
- margin: 2rem 0 1rem 0;
- font-weight: 600;
-}
-
-.leftDrawer .optionList button {
- display: flex;
- align-items: center;
- width: 100%;
- text-align: start;
- margin-bottom: 0.8rem;
- border-radius: 16px;
- outline: none;
- border: none;
-}
-
-.leftDrawer .optionList button .iconWrapper {
- width: 36px;
-}
-
-.leftDrawer .profileContainer {
- border: none;
- width: 100%;
- padding: 2.1rem 0.5rem;
- height: 52px;
- display: flex;
- align-items: center;
- background-color: var(--bs-white);
-}
-
-.leftDrawer .profileContainer:focus {
- outline: none;
- background-color: var(--bs-gray-100);
-}
-
-.leftDrawer .imageContainer {
- width: 68px;
-}
-
-.leftDrawer .profileContainer img {
- height: 52px;
- width: 52px;
- border-radius: 50%;
-}
-
-.leftDrawer .profileContainer .profileText {
- flex: 1;
- text-align: start;
-}
-
-.leftDrawer .profileContainer .profileText .primaryText {
- font-size: 1.1rem;
- font-weight: 600;
-}
-
-.leftDrawer .profileContainer .profileText .secondaryText {
- font-size: 0.8rem;
- font-weight: 400;
- color: var(--bs-secondary);
- display: block;
- text-transform: capitalize;
-}
-
-@media (max-width: 1120px) {
- .leftDrawer {
- width: calc(250px + 2rem);
- padding: 1rem 1rem 0 1rem;
- }
-}
-
-/* For tablets */
-@media (max-width: 820px) {
- .hideElemByDefault {
- display: none;
- }
-
- .leftDrawer {
- width: 100%;
- left: 0;
- right: 0;
- }
-
- .inactiveDrawer {
- opacity: 0;
- left: 0;
- z-index: -1;
- animation: closeDrawer 0.4s ease-in-out;
- }
-
- .activeDrawer {
- display: flex;
- z-index: 100;
- animation: openDrawer 0.6s ease-in-out;
- }
-
- .logout {
- margin-bottom: 2.5rem !important;
- }
-}
-
-@keyframes goToLeftBigScreen {
- from {
- left: 0;
- }
-
- to {
- opacity: 0.1;
- left: calc(-300px - 2rem);
- }
-}
-
-/* Webkit prefix for older browser compatibility */
-@-webkit-keyframes goToLeftBigScreen {
- from {
- left: 0;
- }
-
- to {
- opacity: 0.1;
- left: calc(-300px - 2rem);
- }
-}
-
-@keyframes comeToRightBigScreen {
- from {
- opacity: 0.4;
- left: calc(-300px - 2rem);
- }
-
- to {
- opacity: 1;
- left: 0;
- }
-}
-
-/* Webkit prefix for older browser compatibility */
-@-webkit-keyframes comeToRightBigScreen {
- from {
- opacity: 0.4;
- left: calc(-300px - 2rem);
- }
-
- to {
- opacity: 1;
- left: 0;
- }
-}
-
-@keyframes closeDrawer {
- from {
- left: 0;
- opacity: 1;
- }
-
- to {
- left: -1000px;
- opacity: 0;
- }
-}
-
-/* Webkit prefix for older browser compatibility */
-@-webkit-keyframes closeDrawer {
- from {
- left: 0;
- opacity: 1;
- }
-
- to {
- left: -1000px;
- opacity: 0;
- }
-}
-
-@keyframes openDrawer {
- from {
- opacity: 0;
- left: -1000px;
- }
-
- to {
- left: 0;
- opacity: 1;
- }
-}
-
-/* Webkit prefix for older browser compatibility */
-@-webkit-keyframes openDrawer {
- from {
- opacity: 0;
- left: -1000px;
- }
-
- to {
- left: 0;
- opacity: 1;
- }
-}
diff --git a/src/components/LeftDrawer/LeftDrawer.test.tsx b/src/components/LeftDrawer/LeftDrawer.test.tsx
index 6701d2d4bb..a4ad1cc47a 100644
--- a/src/components/LeftDrawer/LeftDrawer.test.tsx
+++ b/src/components/LeftDrawer/LeftDrawer.test.tsx
@@ -95,7 +95,7 @@ describe('Testing Left Drawer component for SUPERADMIN', () => {
});
expect(
- orgsBtn.className.includes('text-white btn btn-success'),
+ orgsBtn.className.includes('text-black btn btn-success'),
).toBeTruthy();
expect(rolesBtn.className.includes('text-secondary btn')).toBeTruthy();
expect(
@@ -179,9 +179,7 @@ describe('Testing Left Drawer component for SUPERADMIN', () => {
orgsBtn.click();
});
- expect(
- orgsBtn.className.includes('text-white btn btn-success'),
- ).toBeTruthy();
+ expect(orgsBtn.className.includes('text-black')).toBeTruthy();
});
});
@@ -211,7 +209,7 @@ describe('Testing Left Drawer component for ADMIN', () => {
});
expect(
- orgsBtn.className.includes('text-white btn btn-success'),
+ orgsBtn.className.includes('text-black btn btn-success'),
).toBeTruthy();
// These screens aren't meant for admins, so they should not be present
diff --git a/src/components/LeftDrawer/LeftDrawer.tsx b/src/components/LeftDrawer/LeftDrawer.tsx
index 4628603ba1..eabf9722f8 100644
--- a/src/components/LeftDrawer/LeftDrawer.tsx
+++ b/src/components/LeftDrawer/LeftDrawer.tsx
@@ -6,7 +6,7 @@ import OrganizationsIcon from 'assets/svgs/organizations.svg?react';
import RolesIcon from 'assets/svgs/roles.svg?react';
import SettingsIcon from 'assets/svgs/settings.svg?react';
import TalawaLogo from 'assets/svgs/talawa.svg?react';
-import styles from './LeftDrawer.module.css';
+import styles from 'style/app.module.css';
import useLocalStorage from 'utils/useLocalstorage';
export interface InterfaceLeftDrawerProps {
@@ -63,17 +63,20 @@ const leftDrawer = ({
{t('my organizations')}
diff --git a/src/components/LeftDrawerOrg/LeftDrawerOrg.module.css b/src/components/LeftDrawerOrg/LeftDrawerOrg.module.css
index e792ef34bb..6296b1aa73 100644
--- a/src/components/LeftDrawerOrg/LeftDrawerOrg.module.css
+++ b/src/components/LeftDrawerOrg/LeftDrawerOrg.module.css
@@ -8,7 +8,7 @@
display: flex;
flex-direction: column;
padding: 0.8rem 0rem 0 1rem;
- background-color: var(--bs-white);
+ background-color: #f6f8fc;
transition: 0.5s;
font-family: var(--bs-leftDrawer-font-family);
}
@@ -105,7 +105,7 @@
}
.leftDrawer .organizationContainer .profileContainer {
- background-color: #31bb6b33;
+ background-color: #e0e9ff;
padding-right: 10px;
}
@@ -155,7 +155,7 @@
.leftDrawer .profileContainer .profileText .secondaryText {
font-size: 0.8rem;
font-weight: 400;
- color: var(--bs-secondary);
+ /* color: var(--bs-secondary); */
display: block;
text-transform: capitalize;
}
diff --git a/src/components/LeftDrawerOrg/LeftDrawerOrg.test.tsx b/src/components/LeftDrawerOrg/LeftDrawerOrg.spec.tsx
similarity index 88%
rename from src/components/LeftDrawerOrg/LeftDrawerOrg.test.tsx
rename to src/components/LeftDrawerOrg/LeftDrawerOrg.spec.tsx
index 2a0ef3815d..3fa6c0205e 100644
--- a/src/components/LeftDrawerOrg/LeftDrawerOrg.test.tsx
+++ b/src/components/LeftDrawerOrg/LeftDrawerOrg.spec.tsx
@@ -1,9 +1,9 @@
+import '@testing-library/jest-dom';
import React, { act } from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import 'jest-localstorage-mock';
import { I18nextProvider } from 'react-i18next';
-import { BrowserRouter } from 'react-router-dom';
+import { BrowserRouter, MemoryRouter } from 'react-router-dom';
import i18nForTest from 'utils/i18nForTest';
import type { InterfaceLeftDrawerProps } from './LeftDrawerOrg';
@@ -15,12 +15,17 @@ import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries';
import { StaticMockLink } from 'utils/StaticMockLink';
import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
import useLocalStorage from 'utils/useLocalstorage';
+import { vi, describe, test, expect } from 'vitest';
const { setItem } = useLocalStorage();
const props: InterfaceLeftDrawerProps = {
orgId: '123',
targets: [
+ {
+ name: 'Admin Profile',
+ url: '/member/123',
+ },
{
name: 'Dashboard',
url: '/orgdash/123',
@@ -61,7 +66,7 @@ const props: InterfaceLeftDrawerProps = {
},
],
hideDrawer: false,
- setHideDrawer: jest.fn(),
+ setHideDrawer: vi.fn(),
};
const MOCKS = [
@@ -215,6 +220,20 @@ const MOCKS_EMPTY = [
},
];
+const MOCKS_EMPTY_ORGID = [
+ {
+ request: {
+ query: ORGANIZATIONS_LIST,
+ variables: { id: '' },
+ },
+ result: {
+ data: {
+ organizations: [],
+ },
+ },
+ },
+];
+
const defaultScreens = [
'Dashboard',
'People',
@@ -226,11 +245,11 @@ const defaultScreens = [
'All Organizations',
];
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- warn: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ warn: vi.fn(),
+ error: vi.fn(),
},
}));
@@ -257,13 +276,14 @@ beforeEach(() => {
});
afterEach(() => {
- jest.clearAllMocks();
+ vi.clearAllMocks();
localStorage.clear();
});
const link = new StaticMockLink(MOCKS, true);
const linkImage = new StaticMockLink(MOCKS_WITH_IMAGE, true);
const linkEmpty = new StaticMockLink(MOCKS_EMPTY, true);
+const linkEmptyOrgId = new StaticMockLink(MOCKS_EMPTY_ORGID, true);
describe('Testing LeftDrawerOrg component for SUPERADMIN', () => {
test('Component should be rendered properly', async () => {
@@ -308,6 +328,29 @@ describe('Testing LeftDrawerOrg component for SUPERADMIN', () => {
expect(screen.getByTestId(/orgBtn/i)).toBeInTheDocument();
});
+ test('Should not show org not found error when viewing admin profile', async () => {
+ setItem('UserImage', '');
+ setItem('SuperAdmin', true);
+ setItem('FirstName', 'John');
+ setItem('LastName', 'Doe');
+ setItem('id', '123');
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ expect(
+ screen.queryByText(/Error occured while loading Organization data/i),
+ ).not.toBeInTheDocument();
+ });
+
test('Testing Menu Buttons', async () => {
setItem('UserImage', '');
setItem('SuperAdmin', true);
diff --git a/src/components/LeftDrawerOrg/LeftDrawerOrg.tsx b/src/components/LeftDrawerOrg/LeftDrawerOrg.tsx
index 3a3ba378cf..a687351c98 100644
--- a/src/components/LeftDrawerOrg/LeftDrawerOrg.tsx
+++ b/src/components/LeftDrawerOrg/LeftDrawerOrg.tsx
@@ -3,16 +3,17 @@ import { WarningAmberOutlined } from '@mui/icons-material';
import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries';
import CollapsibleDropdown from 'components/CollapsibleDropdown/CollapsibleDropdown';
import IconComponent from 'components/IconComponent/IconComponent';
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
import Button from 'react-bootstrap/Button';
import { useTranslation } from 'react-i18next';
-import { NavLink } from 'react-router-dom';
+import { NavLink, useLocation } from 'react-router-dom';
import type { TargetsType } from 'state/reducers/routesReducer';
import type { InterfaceQueryOrganizationsListObject } from 'utils/interfaces';
import AngleRightIcon from 'assets/svgs/angleRight.svg?react';
import TalawaLogo from 'assets/svgs/talawa.svg?react';
import styles from './LeftDrawerOrg.module.css';
import Avatar from 'components/Avatar/Avatar';
+import useLocalStorage from 'utils/useLocalstorage';
export interface InterfaceLeftDrawerProps {
orgId: string;
@@ -38,10 +39,20 @@ const leftDrawerOrg = ({
}: InterfaceLeftDrawerProps): JSX.Element => {
const { t: tCommon } = useTranslation('common');
const { t: tErrors } = useTranslation('errors');
+ const location = useLocation();
+ const { getItem } = useLocalStorage();
+ const userId = getItem('id');
+ const getIdFromPath = (pathname: string): string => {
+ if (!pathname) return '';
+ const segments = pathname.split('/');
+ // Index 2 (third segment) represents the ID in paths like /member/{userId}
+ return segments.length > 2 ? segments[2] : '';
+ };
+ const [isProfilePage, setIsProfilePage] = useState(false);
const [showDropdown, setShowDropdown] = useState(false);
-
- const [organization, setOrganization] =
- useState();
+ const [organization, setOrganization] = useState<
+ InterfaceQueryOrganizationsListObject | undefined
+ >(undefined);
const {
data,
loading,
@@ -54,11 +65,24 @@ const leftDrawerOrg = ({
variables: { id: orgId },
});
+ // Get the ID from the current path
+ const pathId = useMemo(
+ () => getIdFromPath(location.pathname),
+ [location.pathname],
+ );
+ // Check if the current page is admin profile page
+ useEffect(() => {
+ // if param id is equal to userId, then it is a profile page
+ setIsProfilePage(pathId === userId);
+ }, [location, userId]);
+
// Set organization data when query data is available
useEffect(() => {
let isMounted = true;
if (data && isMounted) {
setOrganization(data?.organizations[0]);
+ } else {
+ setOrganization(undefined);
}
return () => {
isMounted = false;
@@ -104,7 +128,7 @@ const leftDrawerOrg = ({
/>
>
) : organization == undefined ? (
- <>
+ !isProfilePage && (
{tErrors('errorLoading', { entity: 'Organization' })}
- >
+ )
) : (
@@ -151,8 +175,11 @@ const leftDrawerOrg = ({
@@ -160,7 +187,7 @@ const leftDrawerOrg = ({
name={name == 'Membership Requests' ? 'Requests' : name}
fill={
isActive === true
- ? 'var(--bs-white)'
+ ? 'var(--bs-black)'
: 'var(--bs-secondary)'
}
/>
diff --git a/src/components/Loader/Loader.test.tsx b/src/components/Loader/Loader.spec.tsx
similarity index 76%
rename from src/components/Loader/Loader.test.tsx
rename to src/components/Loader/Loader.spec.tsx
index c512b480e3..ab312a9155 100644
--- a/src/components/Loader/Loader.test.tsx
+++ b/src/components/Loader/Loader.spec.tsx
@@ -1,23 +1,24 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import Loader from './Loader';
+import { describe, it, expect } from 'vitest';
describe('Testing Loader component', () => {
- test('Component should be rendered properly', () => {
+ it('Component should be rendered properly', () => {
render(
);
expect(screen.getByTestId('spinner-wrapper')).toBeInTheDocument();
expect(screen.getByTestId('spinner')).toBeInTheDocument();
});
- test('Component should render on custom sizes', () => {
+ it('Component should render on custom sizes', () => {
render(
);
expect(screen.getByTestId('spinner-wrapper')).toBeInTheDocument();
expect(screen.getByTestId('spinner')).toBeInTheDocument();
});
- test('Component should render with large size', () => {
+ it('Component should render with large size', () => {
render(
);
expect(screen.getByTestId('spinner-wrapper')).toBeInTheDocument();
diff --git a/src/components/LoginPortalToggle/LoginPortalToggle.module.css b/src/components/LoginPortalToggle/LoginPortalToggle.module.css
index 5983e20905..227f65aa9a 100644
--- a/src/components/LoginPortalToggle/LoginPortalToggle.module.css
+++ b/src/components/LoginPortalToggle/LoginPortalToggle.module.css
@@ -24,11 +24,11 @@
.activeLink {
color: var(--bs-white);
border: 1px solid var(--bs-primary);
- background-color: var(--bs-primary);
+ background-color: var(--active-button-bg);
}
.activeLink:hover {
color: var(--bs-white);
- background-color: var(--bs-primary);
+ background-color: var(--active-hover);
border: 1px solid var(--bs-primary);
}
diff --git a/src/components/LoginPortalToggle/LoginPortalToggle.test.tsx b/src/components/LoginPortalToggle/LoginPortalToggle.spec.tsx
similarity index 92%
rename from src/components/LoginPortalToggle/LoginPortalToggle.test.tsx
rename to src/components/LoginPortalToggle/LoginPortalToggle.spec.tsx
index 327e6cccea..eee24f6376 100644
--- a/src/components/LoginPortalToggle/LoginPortalToggle.test.tsx
+++ b/src/components/LoginPortalToggle/LoginPortalToggle.spec.tsx
@@ -6,6 +6,7 @@ import { I18nextProvider } from 'react-i18next';
import LoginPortalToggle from './LoginPortalToggle';
import { store } from 'state/store';
import i18nForTest from 'utils/i18nForTest';
+import { describe, test, vi } from 'vitest';
async function wait(ms = 100): Promise
{
await act(() => {
@@ -17,7 +18,7 @@ async function wait(ms = 100): Promise {
describe('Testing LoginPortalToggle component', () => {
test('Component Should be rendered properly', async () => {
- const mockOnToggle = jest.fn();
+ const mockOnToggle = vi.fn();
render(
diff --git a/src/components/MemberDetail/EventsAttendedByMember.spec.tsx b/src/components/MemberDetail/EventsAttendedByMember.spec.tsx
new file mode 100644
index 0000000000..23ae0efa3b
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedByMember.spec.tsx
@@ -0,0 +1,101 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { MockedProvider } from '@apollo/client/testing';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import EventsAttendedByMember from './EventsAttendedByMember';
+import { BrowserRouter } from 'react-router-dom';
+
+const mockEventData = {
+ event: {
+ _id: 'event123',
+ title: 'Test Event',
+ description: 'Test Description',
+ startDate: '2023-01-01',
+ endDate: '2023-01-02',
+ startTime: '09:00',
+ endTime: '17:00',
+ allDay: false,
+ location: 'Test Location',
+ recurring: true,
+ baseRecurringEvent: { _id: 'base123' },
+ organization: {
+ _id: 'org123',
+ members: [
+ { _id: 'user1', firstName: 'John', lastName: 'Doe' },
+ { _id: 'user2', firstName: 'Jane', lastName: 'Smith' },
+ ],
+ },
+ attendees: [
+ { _id: 'user1', gender: 'MALE' },
+ { _id: 'user2', gender: 'FEMALE' },
+ ],
+ },
+};
+
+const mocks = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: mockEventData,
+ },
+ },
+];
+
+const errorMocks = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ error: new Error('An error occurred'),
+ },
+];
+
+describe('EventsAttendedByMember', () => {
+ test('renders loading state initially', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByTestId('loading')).toBeInTheDocument();
+ expect(screen.getByText('Loading event details...')).toBeInTheDocument();
+ });
+
+ test('renders error state when query fails', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ const errorMessage = await screen.findByTestId('error');
+ expect(errorMessage).toBeInTheDocument();
+ expect(
+ screen.getByText('Unable to load event details. Please try again later.'),
+ ).toBeInTheDocument();
+ });
+
+ test('renders event card with correct data when query succeeds', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await screen.findByTestId('EventsAttendedCard');
+
+ expect(screen.getByText('Test Event')).toBeInTheDocument();
+ expect(screen.getByText('Test Location')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/MemberDetail/EventsAttendedByMember.tsx b/src/components/MemberDetail/EventsAttendedByMember.tsx
new file mode 100644
index 0000000000..ce926d84eb
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedByMember.tsx
@@ -0,0 +1,54 @@
+import React from 'react';
+import { useQuery } from '@apollo/client';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import EventAttendedCard from './EventsAttendedCardItem';
+import { Spinner } from 'react-bootstrap';
+/**
+ * Component to display events attended by a specific member
+ * @param eventsId - ID of the event to fetch and display details for
+ * @returns Event card component with event details
+ */
+interface InterfaceEventsAttendedByMember {
+ eventsId: string;
+}
+
+function EventsAttendedByMember({
+ eventsId,
+}: InterfaceEventsAttendedByMember): JSX.Element {
+ const {
+ data: events,
+ loading,
+ error,
+ } = useQuery(EVENT_DETAILS, {
+ variables: { id: eventsId },
+ });
+
+ if (loading)
+ return (
+
+
+
Loading event details...
+
+ );
+ if (error)
+ return (
+
+
Unable to load event details. Please try again later.
+
+ );
+
+ const { organization, _id, startDate, title, location } = events.event;
+
+ return (
+
+ );
+}
+
+export default EventsAttendedByMember;
diff --git a/src/components/MemberDetail/EventsAttendedCardItem.spec.tsx b/src/components/MemberDetail/EventsAttendedCardItem.spec.tsx
new file mode 100644
index 0000000000..8694426d58
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedCardItem.spec.tsx
@@ -0,0 +1,64 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { BrowserRouter } from 'react-router-dom';
+import EventAttendedCard from './EventsAttendedCardItem';
+import { vi } from 'vitest';
+
+interface InterfaceEventAttendedCardProps {
+ type: 'Event';
+ title: string;
+ startdate: string;
+ time: string;
+ location: string;
+ orgId: string;
+ eventId: string;
+}
+
+describe('EventAttendedCard', () => {
+ const mockProps: InterfaceEventAttendedCardProps = {
+ type: 'Event' as const,
+ title: 'Test Event',
+ startdate: '2023-05-15',
+ time: '14:00',
+ location: 'Test Location',
+ orgId: 'org123',
+ eventId: 'event456',
+ };
+
+ const renderComponent = (props = mockProps): void => {
+ render(
+
+
+ ,
+ );
+ };
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('renders event details correctly', () => {
+ renderComponent();
+
+ expect(screen.getByText('Test Event')).toBeInTheDocument();
+ expect(screen.getByText('MAY')).toBeInTheDocument();
+ expect(screen.getByText('15')).toBeInTheDocument();
+ expect(screen.getByText('Test Location')).toBeInTheDocument();
+ });
+
+ it('renders link with correct path', () => {
+ renderComponent();
+ const link = screen.getByRole('link');
+ expect(link).toHaveAttribute('href', '/event/org123/event456');
+ });
+
+ it('renders location icon', () => {
+ renderComponent();
+ expect(screen.getByTestId('LocationOnIcon')).toBeInTheDocument();
+ });
+
+ it('renders chevron right icon', () => {
+ renderComponent();
+ expect(screen.getByTestId('ChevronRightIcon')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/MemberDetail/EventsAttendedCardItem.tsx b/src/components/MemberDetail/EventsAttendedCardItem.tsx
new file mode 100644
index 0000000000..cfed19e4dd
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedCardItem.tsx
@@ -0,0 +1,77 @@
+import React from 'react';
+import dayjs from 'dayjs';
+import { Card, Row, Col } from 'react-bootstrap';
+import { MdChevronRight, MdLocationOn } from 'react-icons/md';
+import { Link } from 'react-router-dom';
+/**
+ * Card component to display individual event attendance information
+ * Shows event details including title, date, location and organization
+ * @param orgId - Organization ID
+ * @param eventId - Event ID
+ * @param startdate - Event start date
+ * @param title - Event title
+ * @param location - Event location
+ * @returns Card component with formatted event information
+ */
+export interface InterfaceCardItem {
+ title: string;
+ time?: string;
+ startdate?: string;
+ creator?: string;
+ location?: string;
+ eventId?: string;
+ orgId?: string;
+}
+
+const EventAttendedCard = (props: InterfaceCardItem): JSX.Element => {
+ const { title, startdate, location, orgId, eventId } = props;
+
+ return (
+
+
+
+
+
+ {startdate && dayjs(startdate).isValid() ? (
+ <>
+
+ {dayjs(startdate).format('MMM').toUpperCase()}
+
+
+ {dayjs(startdate).format('D')}
+
+ >
+ ) : (
+ /*istanbul ignore next*/
+
Date N/A
+ )}
+
+
+
+ {title}
+
+
+ {location}
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default EventAttendedCard;
diff --git a/src/components/MemberDetail/EventsAttendedMemberModal.spec.tsx b/src/components/MemberDetail/EventsAttendedMemberModal.spec.tsx
new file mode 100644
index 0000000000..fdec64e5f0
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedMemberModal.spec.tsx
@@ -0,0 +1,134 @@
+import React from 'react';
+import { render, screen, fireEvent } from '@testing-library/react';
+import { MockedProvider } from '@apollo/client/testing';
+import { BrowserRouter } from 'react-router-dom';
+import EventsAttendedMemberModal from './EventsAttendedMemberModal';
+import { vi } from 'vitest';
+
+/**
+ * Mock the `react-i18next` module to provide translation functionality.
+ */
+
+vi.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key: string) => key,
+ i18n: { changeLanguage: () => Promise.resolve() },
+ }),
+}));
+
+/**
+ * Mock the `CustomTableCell` component for testing.
+ */
+
+vi.mock('./customTableCell', () => ({
+ CustomTableCell: ({ eventId }: { eventId: string }) => (
+
+ {`Event ${eventId}`}
+ 2024-03-14
+ Yes
+ 5
+
+ ),
+}));
+
+const mockEvents = Array.from({ length: 6 }, (_, index) => ({
+ _id: `${index + 1}`,
+ name: `Event ${index + 1}`,
+ date: '2024-03-14',
+ isRecurring: true,
+ attendees: 5,
+}));
+
+describe('EventsAttendedMemberModal', () => {
+ const defaultProps = {
+ eventsAttended: mockEvents,
+ setShow: vi.fn(),
+ show: true,
+ };
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ test('renders modal with correct title when show is true', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByText('Events Attended List')).toBeInTheDocument();
+ expect(screen.getByText('Showing 1 - 5 of 6 Events')).toBeInTheDocument();
+ });
+
+ test('displays empty state message when no events', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByText('noeventsAttended')).toBeInTheDocument();
+ });
+
+ test('renders correct number of events per page', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ const eventRows = screen.getAllByTestId('event-row');
+ expect(eventRows).toHaveLength(5);
+ expect(screen.getByText('Event 1')).toBeInTheDocument();
+ expect(screen.getByText('Event 5')).toBeInTheDocument();
+ });
+
+ test('handles pagination correctly', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByRole('button', { name: 'Go to next page' }));
+ expect(screen.getByText('Event 6')).toBeInTheDocument();
+ });
+
+ test('closes modal when close button is clicked', () => {
+ const mockSetShow = vi.fn();
+ render(
+
+
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByRole('button', { name: 'Close' }));
+ expect(mockSetShow).toHaveBeenCalledWith(false);
+ expect(mockSetShow).toHaveBeenCalledTimes(1);
+ });
+
+ test('displays correct pagination info', () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ expect(screen.getByText('Showing 1 - 5 of 6 Events')).toBeInTheDocument();
+ fireEvent.click(screen.getByRole('button', { name: 'Go to next page' }));
+ expect(screen.getByText('Showing 6 - 6 of 6 Events')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/MemberDetail/EventsAttendedMemberModal.tsx b/src/components/MemberDetail/EventsAttendedMemberModal.tsx
new file mode 100644
index 0000000000..69913998b1
--- /dev/null
+++ b/src/components/MemberDetail/EventsAttendedMemberModal.tsx
@@ -0,0 +1,131 @@
+import React, { useState, useMemo } from 'react';
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Paper,
+ Pagination,
+} from '@mui/material';
+import { Modal } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+import styles from '../../screens/MemberDetail/MemberDetail.module.css';
+import { CustomTableCell } from './customTableCell';
+/**
+ * Modal component to display paginated list of events attended by a member
+ * @param eventsAttended - Array of events attended by the member
+ * @param setShow - Function to control modal visibility
+ * @param show - Boolean to control modal visibility
+ * @param eventsPerPage - Number of events to display per page
+ * @returns Modal component with paginated events list
+ */
+interface InterfaceEvent {
+ _id: string;
+ name: string;
+ date: string;
+ isRecurring: boolean;
+ attendees: number;
+}
+
+interface InterfaceEventsAttendedMemberModalProps {
+ eventsAttended: InterfaceEvent[];
+ setShow: (show: boolean) => void;
+ show: boolean;
+ eventsPerPage?: number;
+}
+
+const EventsAttendedMemberModal: React.FC<
+ InterfaceEventsAttendedMemberModalProps
+> = ({ eventsAttended, setShow, show, eventsPerPage = 5 }) => {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'memberDetail',
+ });
+ const [page, setPage] = useState(1);
+
+ const handleClose = (): void => {
+ setShow(false);
+ };
+
+ const handleChangePage = (
+ event: React.ChangeEvent,
+ newPage: number,
+ ): void => {
+ setPage(newPage);
+ };
+
+ const totalPages = useMemo(
+ () => Math.ceil(eventsAttended.length / eventsPerPage),
+ [eventsAttended.length, eventsPerPage],
+ );
+
+ const paginatedEvents = useMemo(
+ () =>
+ eventsAttended.slice((page - 1) * eventsPerPage, page * eventsPerPage),
+ [eventsAttended, page, eventsPerPage],
+ );
+
+ return (
+
+
+ Events Attended List
+
+
+ {eventsAttended.length === 0 ? (
+ {t('noeventsAttended')}
+ ) : (
+ <>
+
+ Showing {(page - 1) * eventsPerPage + 1} -{' '}
+ {Math.min(page * eventsPerPage, eventsAttended.length)} of{' '}
+ {eventsAttended.length} Events
+
+
+
+
+
+
+ Event Name
+
+
+ Date of Event
+
+
+ Recurring Event
+
+
+ Attendees
+
+
+
+
+ {paginatedEvents.map((event) => (
+
+ ))}
+
+
+
+
+
+
{
+ if (type === 'page') return `Go to page ${page}`;
+ return `Go to ${type} page`;
+ }}
+ />
+
+
+ >
+ )}
+
+
+ );
+};
+
+export default EventsAttendedMemberModal;
diff --git a/src/components/MemberDetail/customTableCell.spec.tsx b/src/components/MemberDetail/customTableCell.spec.tsx
new file mode 100644
index 0000000000..81a088d5bb
--- /dev/null
+++ b/src/components/MemberDetail/customTableCell.spec.tsx
@@ -0,0 +1,160 @@
+import React from 'react';
+import { render, screen, waitFor } from '@testing-library/react';
+import { MockedProvider } from '@apollo/client/testing';
+import { BrowserRouter } from 'react-router-dom';
+import { CustomTableCell } from './customTableCell';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import { vi } from 'vitest';
+vi.mock('react-toastify', () => ({
+ toast: {
+ success: vi.fn(),
+ error: vi.fn(),
+ },
+}));
+
+const mocks = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: {
+ _id: 'event123',
+ title: 'Test Event',
+ description: 'This is a test event description',
+ startDate: '2023-05-01',
+ endDate: '2023-05-02',
+ startTime: '09:00:00',
+ endTime: '17:00:00',
+ allDay: false,
+ location: 'Test Location',
+ recurring: true,
+ baseRecurringEvent: {
+ _id: 'recurringEvent123',
+ },
+ organization: {
+ _id: 'org456',
+ members: [
+ { _id: 'member1', firstName: 'John', lastName: 'Doe' },
+ { _id: 'member2', firstName: 'Jane', lastName: 'Smith' },
+ ],
+ },
+ attendees: [{ _id: 'user1' }, { _id: 'user2' }],
+ },
+ },
+ },
+ },
+];
+
+describe('CustomTableCell', () => {
+ it('renders event details correctly', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => screen.getByTestId('custom-row'));
+
+ expect(screen.getByText('Test Event')).toBeInTheDocument();
+ expect(
+ screen.getByText(
+ new Date('2023-05-01').toLocaleDateString(undefined, {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ timeZone: 'UTC',
+ }),
+ ),
+ ).toBeInTheDocument();
+ expect(screen.getByText('Yes')).toBeInTheDocument();
+ expect(screen.getByText('2')).toBeInTheDocument();
+
+ const link = screen.getByRole('link', { name: 'Test Event' });
+ expect(link).toHaveAttribute('href', '/event/org456/event123');
+ });
+
+ it('displays loading state', () => {
+ render(
+
+
+ ,
+ );
+
+ expect(screen.getByRole('progressbar')).toBeInTheDocument();
+ });
+
+ it('displays error state', async () => {
+ const errorMock = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ error: new Error('An error occurred'),
+ },
+ ];
+
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.getByText(
+ 'Unable to load event details. Please try again later.',
+ ),
+ ).toBeInTheDocument();
+ });
+ });
+
+ it('displays no event found message', async () => {
+ const noEventMock = [
+ {
+ request: {
+ query: EVENT_DETAILS,
+ variables: { id: 'event123' },
+ },
+ result: {
+ data: {
+ event: null,
+ },
+ },
+ },
+ ];
+
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.getByText('Event not found or has been deleted'),
+ ).toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/components/MemberDetail/customTableCell.tsx b/src/components/MemberDetail/customTableCell.tsx
new file mode 100644
index 0000000000..b8cc2bdd98
--- /dev/null
+++ b/src/components/MemberDetail/customTableCell.tsx
@@ -0,0 +1,78 @@
+import { useQuery } from '@apollo/client';
+import { CircularProgress, TableCell, TableRow } from '@mui/material';
+import { EVENT_DETAILS } from 'GraphQl/Queries/Queries';
+import React from 'react';
+import styles from '../../screens/MemberDetail/MemberDetail.module.css';
+import { Link } from 'react-router-dom';
+/**
+ * Custom table cell component to display event details
+ * @param eventId - ID of the event to fetch and display
+ * @returns TableRow component with event information
+ */
+
+export const CustomTableCell: React.FC<{ eventId: string }> = ({ eventId }) => {
+ const { data, loading, error } = useQuery(EVENT_DETAILS, {
+ variables: {
+ id: eventId,
+ },
+ errorPolicy: 'all',
+ fetchPolicy: 'cache-first',
+ nextFetchPolicy: 'cache-and-network',
+ pollInterval: 30000,
+ });
+
+ if (loading)
+ return (
+
+
+
+
+
+ );
+ /*istanbul ignore next*/
+ if (error) {
+ return (
+
+
+ {`Unable to load event details. Please try again later.`}
+
+
+ );
+ }
+ const event = data?.event;
+ /*istanbul ignore next*/
+ if (!event) {
+ return (
+
+
+ Event not found or has been deleted
+
+
+ );
+ }
+
+ return (
+
+
+
+ {event.title}
+
+
+
+ {new Date(event.startDate).toLocaleDateString(undefined, {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ timeZone: 'UTC',
+ })}
+
+ {event.recurring ? 'Yes' : 'No'}
+
+ {event.attendees?.length ?? 0}
+
+
+ );
+};
diff --git a/src/components/MemberRequestCard/MemberRequestCard.test.tsx b/src/components/MemberRequestCard/MemberRequestCard.spec.tsx
similarity index 97%
rename from src/components/MemberRequestCard/MemberRequestCard.test.tsx
rename to src/components/MemberRequestCard/MemberRequestCard.spec.tsx
index a38a046ea2..5779d3688a 100644
--- a/src/components/MemberRequestCard/MemberRequestCard.test.tsx
+++ b/src/components/MemberRequestCard/MemberRequestCard.spec.tsx
@@ -11,6 +11,7 @@ import {
import MemberRequestCard from './MemberRequestCard';
import i18nForTest from 'utils/i18nForTest';
import { StaticMockLink } from 'utils/StaticMockLink';
+import { describe, vi, expect } from 'vitest';
const MOCKS = [
{
@@ -65,7 +66,7 @@ describe('Testing Member Request Card', () => {
email: 'johndoe@gmail.com',
};
- global.alert = jest.fn();
+ global.alert = vi.fn();
it('should render props and text elements test for the page component', async () => {
global.confirm = (): boolean => true;
diff --git a/src/components/NotFound/NotFound.test.tsx b/src/components/NotFound/NotFound.spec.tsx
similarity index 94%
rename from src/components/NotFound/NotFound.test.tsx
rename to src/components/NotFound/NotFound.spec.tsx
index 54c0bcfe4a..a70e355f7a 100644
--- a/src/components/NotFound/NotFound.test.tsx
+++ b/src/components/NotFound/NotFound.spec.tsx
@@ -1,9 +1,9 @@
import React from 'react';
import { I18nextProvider } from 'react-i18next';
import i18nForTest from 'utils/i18nForTest';
-
import { render, screen } from '@testing-library/react';
import NotFound from './NotFound';
+import { expect, it, describe } from 'vitest';
describe('Tesing the NotFound Component', () => {
it('renders the component with the correct title for posts', () => {
diff --git a/src/components/OrgAdminListCard/OrgAdminListCard.test.tsx b/src/components/OrgAdminListCard/OrgAdminListCard.spec.tsx
similarity index 82%
rename from src/components/OrgAdminListCard/OrgAdminListCard.test.tsx
rename to src/components/OrgAdminListCard/OrgAdminListCard.spec.tsx
index 7baea946d2..a68b253c0a 100644
--- a/src/components/OrgAdminListCard/OrgAdminListCard.test.tsx
+++ b/src/components/OrgAdminListCard/OrgAdminListCard.spec.tsx
@@ -9,6 +9,7 @@ import OrgAdminListCard from './OrgAdminListCard';
import i18nForTest from 'utils/i18nForTest';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { StaticMockLink } from 'utils/StaticMockLink';
+import { vi, beforeEach, afterEach, expect, it, describe } from 'vitest';
const MOCKS = [
{
@@ -57,27 +58,28 @@ const renderOrgAdminListCard = (props: {
,
);
};
-jest.mock('i18next-browser-languagedetector', () => ({
- init: jest.fn(),
+vi.mock('i18next-browser-languagedetector', async () => ({
+ ...(await vi.importActual('i18next-browser-languagedetector')),
+ init: vi.fn(),
type: 'languageDetector',
- detect: jest.fn(() => 'en'),
- cacheUserLanguage: jest.fn(),
+ detect: vi.fn(() => 'en'),
+ cacheUserLanguage: vi.fn(),
}));
describe('Testing Organization Admin List Card', () => {
- global.alert = jest.fn();
+ global.alert = vi.fn();
beforeEach(() => {
Object.defineProperty(window, 'location', {
writable: true,
- value: { reload: jest.fn() },
+ value: { reload: vi.fn() },
});
});
afterEach(() => {
- jest.restoreAllMocks();
+ vi.restoreAllMocks();
});
- test('should render props and text elements test for the page component', async () => {
+ it('should render props and text elements test for the page component', async () => {
const props = {
toggleRemoveModal: () => true,
id: '456',
@@ -92,7 +94,7 @@ describe('Testing Organization Admin List Card', () => {
await wait(2000);
});
- test('Should not render text elements when props value is not passed', async () => {
+ it('Should not render text elements when props value is not passed', async () => {
const props = {
toggleRemoveModal: () => true,
id: undefined,
diff --git a/src/components/OrgContriCards/OrgContriCards.module.css b/src/components/OrgContriCards/OrgContriCards.module.css
deleted file mode 100644
index d20b696621..0000000000
--- a/src/components/OrgContriCards/OrgContriCards.module.css
+++ /dev/null
@@ -1,22 +0,0 @@
-.cards {
- width: 45%;
- background: #fcfcfc;
- margin: 10px 20px;
- padding: 20px 30px;
- border-radius: 5px;
- border: 1px solid #e8e8e8;
- box-shadow: 0 3px 5px #c9c9c9;
- margin-right: 40px;
- color: #737373;
-}
-.cards > h2 {
- font-size: 19px;
-}
-.cards > h3 {
- font-size: 17px;
-}
-.cards > p {
- font-size: 14px;
- margin-top: -5px;
- margin-bottom: 7px;
-}
diff --git a/src/components/OrgContriCards/OrgContriCards.test.tsx b/src/components/OrgContriCards/OrgContriCards.spec.tsx
similarity index 97%
rename from src/components/OrgContriCards/OrgContriCards.test.tsx
rename to src/components/OrgContriCards/OrgContriCards.spec.tsx
index 4f202cd355..57a85dc451 100644
--- a/src/components/OrgContriCards/OrgContriCards.test.tsx
+++ b/src/components/OrgContriCards/OrgContriCards.spec.tsx
@@ -7,7 +7,7 @@ import { I18nextProvider } from 'react-i18next';
import OrgContriCards from './OrgContriCards';
import i18nForTest from 'utils/i18nForTest';
import { BACKEND_URL } from 'Constant/constant';
-
+import { describe, expect } from 'vitest';
const client: ApolloClient = new ApolloClient({
cache: new InMemoryCache(),
uri: BACKEND_URL,
diff --git a/src/components/OrgContriCards/OrgContriCards.tsx b/src/components/OrgContriCards/OrgContriCards.tsx
index 6635be09b8..84237013c8 100644
--- a/src/components/OrgContriCards/OrgContriCards.tsx
+++ b/src/components/OrgContriCards/OrgContriCards.tsx
@@ -3,7 +3,7 @@ import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import { useTranslation } from 'react-i18next';
-import styles from './OrgContriCards.module.css';
+import styles from '../../style/app.module.css';
/**
* Props for the OrgContriCards component
diff --git a/src/components/OrgDelete/OrgDelete.test.tsx b/src/components/OrgDelete/OrgDelete.spec.tsx
similarity index 86%
rename from src/components/OrgDelete/OrgDelete.test.tsx
rename to src/components/OrgDelete/OrgDelete.spec.tsx
index b9b9ca2572..23f8dcdde5 100644
--- a/src/components/OrgDelete/OrgDelete.test.tsx
+++ b/src/components/OrgDelete/OrgDelete.spec.tsx
@@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react';
import type { NormalizedCacheObject } from '@apollo/client';
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
import { I18nextProvider } from 'react-i18next';
-
+import { describe, it, expect } from 'vitest';
import OrgDelete from './OrgDelete';
import i18nForTest from 'utils/i18nForTest';
import { BACKEND_URL } from 'Constant/constant';
@@ -14,7 +14,7 @@ const client: ApolloClient = new ApolloClient({
});
describe('Testing Organization People List Card', () => {
- test('should render props and text elements test for the page component', () => {
+ it('should render props and text elements test for the page component', () => {
render(
diff --git a/src/components/OrgListCard/OrgListCard.module.css b/src/components/OrgListCard/OrgListCard.module.css
index 430ea318d6..f242b39f01 100644
--- a/src/components/OrgListCard/OrgListCard.module.css
+++ b/src/components/OrgListCard/OrgListCard.module.css
@@ -55,6 +55,9 @@
display: flex;
justify-content: space-around;
width: 8rem;
+ background-color: var(--grey-bg-color) !important;
+ color: black !important;
+ border: 1px solid var(--dropdown-border-color);
}
.orgName {
diff --git a/src/components/OrgPeopleListCard/OrgPeopleListCard.test.tsx b/src/components/OrgPeopleListCard/OrgPeopleListCard.spec.tsx
similarity index 96%
rename from src/components/OrgPeopleListCard/OrgPeopleListCard.test.tsx
rename to src/components/OrgPeopleListCard/OrgPeopleListCard.spec.tsx
index 7cee31107f..0fb2c39599 100644
--- a/src/components/OrgPeopleListCard/OrgPeopleListCard.test.tsx
+++ b/src/components/OrgPeopleListCard/OrgPeopleListCard.spec.tsx
@@ -9,7 +9,7 @@ import { REMOVE_MEMBER_MUTATION } from 'GraphQl/Mutations/mutations';
import i18nForTest from 'utils/i18nForTest';
import { BrowserRouter } from 'react-router-dom';
import { StaticMockLink } from 'utils/StaticMockLink';
-
+import { describe, test, expect, vi } from 'vitest';
const MOCKS = [
{
request: {
@@ -41,7 +41,7 @@ describe('Testing Organization People List Card', () => {
toggleRemoveModal: () => true,
id: '1',
};
- global.alert = jest.fn();
+ global.alert = vi.fn();
test('should render props and text elements test for the page component', async () => {
global.confirm = (): boolean => true;
diff --git a/src/components/OrgPeopleListCard/OrgPeopleListCard.tsx b/src/components/OrgPeopleListCard/OrgPeopleListCard.tsx
index d24773c02a..dca72b84e7 100644
--- a/src/components/OrgPeopleListCard/OrgPeopleListCard.tsx
+++ b/src/components/OrgPeopleListCard/OrgPeopleListCard.tsx
@@ -7,6 +7,8 @@ import { useTranslation } from 'react-i18next';
import { REMOVE_MEMBER_MUTATION } from 'GraphQl/Mutations/mutations';
import { useParams, Navigate } from 'react-router-dom';
import { errorHandler } from 'utils/errorHandler';
+import styles from '../../style/app.module.css';
+import { Close } from '@mui/icons-material';
/**
* Props for the OrgPeopleListCard component
@@ -74,20 +76,28 @@ function orgPeopleListCard(
{t('removeMember')}
{/* Button to close the modal */}
-
-
+
+
{t('removeMemberMsg')}
{/* Button to cancel the removal action */}
-
+
{tCommon('no')}
{/* Button to confirm the removal action */}
diff --git a/src/components/OrgPostCard/OrgPostCard.test.tsx b/src/components/OrgPostCard/OrgPostCard.spec.tsx
similarity index 86%
rename from src/components/OrgPostCard/OrgPostCard.test.tsx
rename to src/components/OrgPostCard/OrgPostCard.spec.tsx
index 7105e5e8f2..5364766aee 100644
--- a/src/components/OrgPostCard/OrgPostCard.test.tsx
+++ b/src/components/OrgPostCard/OrgPostCard.spec.tsx
@@ -10,7 +10,6 @@ import { MockedProvider } from '@apollo/react-testing';
import OrgPostCard from './OrgPostCard';
import { I18nextProvider } from 'react-i18next';
import userEvent from '@testing-library/user-event';
-import 'jest-localstorage-mock';
import {
DELETE_POST_MUTATION,
UPDATE_POST_MUTATION,
@@ -21,6 +20,36 @@ import { StaticMockLink } from 'utils/StaticMockLink';
import convertToBase64 from 'utils/convertToBase64';
import { BrowserRouter } from 'react-router-dom';
import useLocalStorage from 'utils/useLocalstorage';
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for the OrgPostCard component, which displays organization posts with various interactions.
+ *
+ * These tests verify:
+ * - Basic rendering and display functionality:
+ * - Proper rendering of post content (title, text, images, videos)
+ * - "Read more" toggle button behavior
+ * - Image and video display handling
+ * - Fallback behavior when media is missing
+ *
+ * - Modal interactions:
+ * - Opening/closing primary modal on post click
+ * - Secondary modal functionality for edit/delete operations
+ * - Form validation in edit modal
+ * - Media upload handling in edit modal
+ *
+ * - Post management operations:
+ * - Creating and updating posts
+ * - Deleting posts
+ * - Pinning/unpinning posts
+ * - Error handling for failed operations
+ *
+ * - Media handling:
+ * - Image upload and preview
+ * - Video upload and preview
+ * - Auto-play behavior on hover
+ * - Clearing uploaded media
+ */
const { setItem } = useLocalStorage();
@@ -71,19 +100,23 @@ const MOCKS = [
},
},
];
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- warn: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ warn: vi.fn(),
+ error: vi.fn(),
},
}));
-jest.mock('i18next-browser-languagedetector', () => ({
- init: jest.fn(),
- type: 'languageDetector',
- detect: jest.fn(() => 'en'),
- cacheUserLanguage: jest.fn(),
-}));
+vi.mock('i18next-browser-languagedetector', () => {
+ return {
+ default: {
+ init: vi.fn(),
+ type: 'languageDetector',
+ detect: vi.fn(() => 'en'),
+ cacheUserLanguage: vi.fn(),
+ },
+ };
+});
const link = new StaticMockLink(MOCKS, true);
async function wait(ms = 100): Promise {
await act(() => {
@@ -99,7 +132,7 @@ describe('Testing Organization Post Card', () => {
Object.defineProperty(window, 'location', {
configurable: true,
value: {
- reload: jest.fn(),
+ reload: vi.fn(),
},
});
});
@@ -122,20 +155,16 @@ describe('Testing Organization Post Card', () => {
pinned: false,
};
- jest.mock('react-toastify', () => ({
+ vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- warn: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ warn: vi.fn(),
+ error: vi.fn(),
},
}));
- jest.mock('react', () => ({
- ...jest.requireActual('react'),
- useRef: jest.fn(),
- }));
- global.alert = jest.fn();
+ global.alert = vi.fn();
- test('Opens post on image click', () => {
+ it('Opens post on image click', () => {
const { getByTestId, getByAltText } = render(
@@ -149,7 +178,7 @@ describe('Testing Organization Post Card', () => {
expect(getByTestId('card-title')).toBeInTheDocument();
expect(getByAltText('image')).toBeInTheDocument();
});
- test('renders with default props', () => {
+ it('renders with default props', () => {
const { getByAltText, getByTestId } = render(
@@ -161,7 +190,7 @@ describe('Testing Organization Post Card', () => {
expect(getByTestId('card-title')).toBeInTheDocument();
expect(getByAltText('image')).toBeInTheDocument();
});
- test('toggles "Read more" button', () => {
+ it('toggles "Read more" button', () => {
const { getByTestId } = render(
@@ -176,7 +205,7 @@ describe('Testing Organization Post Card', () => {
fireEvent.click(toggleButton);
expect(toggleButton).toHaveTextContent('Read more');
});
- test('opens and closes edit modal', async () => {
+ it('opens and closes edit modal', async () => {
setItem('id', '123');
render(
@@ -196,7 +225,7 @@ describe('Testing Organization Post Card', () => {
userEvent.click(createOrgBtn);
userEvent.click(screen.getByTestId('closeOrganizationModal'));
});
- test('Should render text elements when props value is not passed', async () => {
+ it('Should render text elements when props value is not passed', async () => {
global.confirm = (): boolean => false;
render(
@@ -209,7 +238,7 @@ describe('Testing Organization Post Card', () => {
userEvent.click(screen.getByAltText('image'));
expect(screen.getByAltText('Post Image')).toBeInTheDocument();
});
- test('Testing post updating after post is updated', async () => {
+ it('Testing post updating after post is updated', async () => {
const { getByTestId } = render(
@@ -265,7 +294,7 @@ describe('Testing Organization Post Card', () => {
await waitFor(() => {
convertToBase64(file); // Replace with the expected base64-encoded image
});
- document.getElementById = jest.fn(() => input);
+ document.getElementById = vi.fn(() => input);
const clearImageButton = getByTestId('closeimage');
fireEvent.click(clearImageButton);
}
@@ -278,7 +307,7 @@ describe('Testing Organization Post Card', () => {
{ timeout: 2500 },
);
});
- test('Testing post updating functionality fail case', async () => {
+ it('Testing post updating functionality fail case', async () => {
const props2 = {
id: '',
postID: '123',
@@ -343,7 +372,7 @@ describe('Testing Organization Post Card', () => {
await waitFor(() => {
convertToBase64(file); // Replace with the expected base64-encoded image
});
- document.getElementById = jest.fn(() => input);
+ document.getElementById = vi.fn(() => input);
const clearImageButton = getByTestId('closeimage');
fireEvent.click(clearImageButton);
}
@@ -356,7 +385,7 @@ describe('Testing Organization Post Card', () => {
{ timeout: 2500 },
);
});
- test('Testing pin post functionality', async () => {
+ it('Testing pin post functionality', async () => {
render(
@@ -378,7 +407,7 @@ describe('Testing Organization Post Card', () => {
{ timeout: 3000 },
);
});
- test('Testing pin post functionality fail case', async () => {
+ it('Testing pin post functionality fail case', async () => {
const props2 = {
id: '',
postID: '123',
@@ -403,7 +432,7 @@ describe('Testing Organization Post Card', () => {
userEvent.click(screen.getByTestId('moreiconbtn'));
userEvent.click(screen.getByTestId('pinpostBtn'));
});
- test('Testing post delete functionality', async () => {
+ it('Testing post delete functionality', async () => {
render(
@@ -429,7 +458,7 @@ describe('Testing Organization Post Card', () => {
{ timeout: 3000 },
);
});
- test('Testing post delete functionality fail case', async () => {
+ it('Testing post delete functionality fail case', async () => {
const props2 = {
id: '',
postID: '123',
@@ -458,7 +487,7 @@ describe('Testing Organization Post Card', () => {
userEvent.click(screen.getByTestId('deletePostModalBtn'));
fireEvent.click(screen.getByTestId('deletePostBtn'));
});
- test('Testing close functionality of primary modal', async () => {
+ it('Testing close functionality of primary modal', async () => {
render(
@@ -475,7 +504,7 @@ describe('Testing Organization Post Card', () => {
//Primary Modal is closed
expect(screen.queryByTestId('moreiconbtn')).not.toBeInTheDocument();
});
- test('Testing close functionality of secondary modal', async () => {
+ it('Testing close functionality of secondary modal', async () => {
render(
@@ -496,7 +525,7 @@ describe('Testing Organization Post Card', () => {
expect(screen.queryByTestId('pinpostBtn')).not.toBeInTheDocument();
expect(screen.queryByTestId('closebtn')).not.toBeInTheDocument();
});
- test('renders without "Read more" button when postInfo length is less than or equal to 43', () => {
+ it('renders without "Read more" button when postInfo length is less than or equal to 43', () => {
render(
@@ -506,7 +535,7 @@ describe('Testing Organization Post Card', () => {
);
expect(screen.queryByTestId('toggleBtn')).not.toBeInTheDocument();
});
- test('renders with "Read more" button when postInfo length is more than 43', () => {
+ it('renders with "Read more" button when postInfo length is more than 43', () => {
const props2 = {
id: '12',
postID: '123',
@@ -529,7 +558,7 @@ describe('Testing Organization Post Card', () => {
expect(screen.getByTestId('toggleBtn')).toBeInTheDocument();
});
- test('updates state variables correctly when handleEditModal is called', () => {
+ it('updates state variables correctly when handleEditModal is called', () => {
const link2 = new StaticMockLink(MOCKS, true);
render(
@@ -555,7 +584,7 @@ describe('Testing Organization Post Card', () => {
expect(screen.queryByTestId('pinpostBtn')).not.toBeInTheDocument();
expect(screen.queryByTestId('closebtn')).not.toBeInTheDocument();
});
- test('updates state variables correctly when handleDeleteModal is called', () => {
+ it('updates state variables correctly when handleDeleteModal is called', () => {
const link2 = new StaticMockLink(MOCKS, true);
render(
@@ -581,7 +610,7 @@ describe('Testing Organization Post Card', () => {
expect(screen.queryByTestId('pinpostBtn')).not.toBeInTheDocument();
expect(screen.queryByTestId('closebtn')).not.toBeInTheDocument();
});
- test('clears postvideo state and resets file input value', async () => {
+ it('clears postvideo state and resets file input value', async () => {
const { getByTestId } = render(
@@ -615,7 +644,7 @@ describe('Testing Organization Post Card', () => {
userEvent.click(screen.getByTestId('closePreview'));
}
});
- test('clears postimage state and resets file input value', async () => {
+ it('clears postimage state and resets file input value', async () => {
const { getByTestId } = render(
@@ -647,12 +676,12 @@ describe('Testing Organization Post Card', () => {
await waitFor(() => {
convertToBase64(file); // Replace with the expected base64-encoded image
});
- document.getElementById = jest.fn(() => input);
+ document.getElementById = vi.fn(() => input);
const clearImageButton = getByTestId('closeimage');
fireEvent.click(clearImageButton);
}
});
- test('clears postitle state and resets file input value', async () => {
+ it('clears postitle state and resets file input value', async () => {
const { getByTestId } = render(
@@ -677,7 +706,7 @@ describe('Testing Organization Post Card', () => {
expect(screen.getByTestId('closeOrganizationModal')).toBeInTheDocument();
expect(screen.getByTestId('updatePostBtn')).toBeInTheDocument();
});
- test('clears postinfo state and resets file input value', async () => {
+ it('clears postinfo state and resets file input value', async () => {
const { getByTestId } = render(
@@ -702,7 +731,7 @@ describe('Testing Organization Post Card', () => {
expect(screen.getByTestId('closeOrganizationModal')).toBeInTheDocument();
expect(screen.getByTestId('updatePostBtn')).toBeInTheDocument();
});
- test('Testing create organization modal', async () => {
+ it('Testing create organization modal', async () => {
setItem('id', '123');
render(
@@ -724,7 +753,7 @@ describe('Testing Organization Post Card', () => {
userEvent.click(createOrgBtn);
userEvent.click(screen.getByTestId('closeOrganizationModal'));
});
- test('should toggle post pin when pin button is clicked', async () => {
+ it('should toggle post pin when pin button is clicked', async () => {
const { getByTestId } = render(
@@ -744,6 +773,12 @@ describe('Testing Organization Post Card', () => {
});
});
test('testing video play and pause on mouse enter and leave events', async () => {
+ const playMock = vi.fn();
+ const pauseMock = vi.fn();
+
+ HTMLMediaElement.prototype.play = playMock;
+ HTMLMediaElement.prototype.pause = pauseMock;
+
const { getByTestId } = render(
@@ -754,16 +789,14 @@ describe('Testing Organization Post Card', () => {
const card = getByTestId('cardVid');
- HTMLVideoElement.prototype.play = jest.fn();
- HTMLVideoElement.prototype.pause = jest.fn();
-
fireEvent.mouseEnter(card);
- expect(HTMLVideoElement.prototype.play).toHaveBeenCalled();
+ expect(playMock).toHaveBeenCalledTimes(1);
fireEvent.mouseLeave(card);
- expect(HTMLVideoElement.prototype.pause).toHaveBeenCalled();
+ expect(pauseMock).toHaveBeenCalledTimes(1);
});
- test('for rendering when no image and no video is available', async () => {
+
+ it('for rendering when no image and no video is available', async () => {
const props2 = {
id: '',
postID: '123',
diff --git a/src/components/OrgSettings/ActionItemCategories/CategoryModal.test.tsx b/src/components/OrgSettings/ActionItemCategories/CategoryModal.spec.tsx
similarity index 85%
rename from src/components/OrgSettings/ActionItemCategories/CategoryModal.test.tsx
rename to src/components/OrgSettings/ActionItemCategories/CategoryModal.spec.tsx
index 39d4884e8b..e4b5663788 100644
--- a/src/components/OrgSettings/ActionItemCategories/CategoryModal.test.tsx
+++ b/src/components/OrgSettings/ActionItemCategories/CategoryModal.spec.tsx
@@ -14,11 +14,23 @@ import { MOCKS, MOCKS_ERROR } from './OrgActionItemCategoryMocks';
import type { InterfaceActionItemCategoryModal } from './CategoryModal';
import CategoryModal from './CategoryModal';
import { toast } from 'react-toastify';
-
-jest.mock('react-toastify', () => ({
+import { vi } from 'vitest';
+/**
+ * This file contains unit tests for the `CategoryModal` component.
+ *
+ * The tests cover:
+ * - Proper rendering of the component in various scenarios, including `create` and `edit` modes, mock data, and error states.
+ * - Handling user interactions with form fields, such as updating the category name and toggling the `isDisabled` switch.
+ * - Ensuring form submissions trigger appropriate callbacks (e.g., `refetchCategories` and `hide`) and display correct toast notifications.
+ * - Simulating GraphQL query and mutation operations with mocked data to validate behavior in success and error cases.
+ * - Testing edge cases, such as submitting without changes, invalid inputs, and handling API errors gracefully.
+ * - Verifying proper integration of internationalization, Redux state, routing, and toast notifications for success and error feedback.
+ */
+
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ error: vi.fn(),
},
}));
@@ -37,8 +49,8 @@ const translations = {
const categoryProps: InterfaceActionItemCategoryModal[] = [
{
isOpen: true,
- hide: jest.fn(),
- refetchCategories: jest.fn(),
+ hide: vi.fn(),
+ refetchCategories: vi.fn(),
orgId: 'orgId',
mode: 'create',
category: {
@@ -51,8 +63,8 @@ const categoryProps: InterfaceActionItemCategoryModal[] = [
},
{
isOpen: true,
- hide: jest.fn(),
- refetchCategories: jest.fn(),
+ hide: vi.fn(),
+ refetchCategories: vi.fn(),
orgId: 'orgId',
mode: 'edit',
category: {
diff --git a/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.test.tsx b/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.spec.tsx
similarity index 80%
rename from src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.test.tsx
rename to src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.spec.tsx
index d3698bf346..27eec94851 100644
--- a/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.test.tsx
+++ b/src/components/OrgSettings/ActionItemCategories/OrgActionItemCategories.spec.tsx
@@ -12,19 +12,36 @@ import i18n from 'utils/i18nForTest';
import type { ApolloLink } from '@apollo/client';
import { MOCKS, MOCKS_EMPTY, MOCKS_ERROR } from './OrgActionItemCategoryMocks';
import OrgActionItemCategories from './OrgActionItemCategories';
-
-jest.mock('react-toastify', () => ({
+import { vi } from 'vitest';
+
+/**
+ * This file contains unit tests for the `OrgActionItemCategories` component.
+ *
+ * The tests cover:
+ * - Proper rendering of the component under different conditions, including scenarios with populated categories, empty categories, and API errors.
+ * - User interactions such as searching, filtering, sorting categories, and opening/closing modals for creating or editing categories.
+ * - Verification of GraphQL query and mutation behaviors using mock data, ensuring correct functionality in both success and error cases.
+ * - Handling edge cases like no input, invalid input, and form resets.
+ * - Integration tests for Redux state, routing, internationalization, and toast notifications.
+ * - Ensuring sorting functionality reflects the `createdAt` property both in ascending and descending order.
+ * - Testing the modal interactions for creating and editing categories, ensuring proper lifecycle (open/close) and state updates.
+ * - Checking the rendering of error messages and placeholders when no data is available or an error occurs.
+ * - Validation of search functionality for categories by name, including clearing the search input and using keyboard shortcuts like `Enter`.
+ */
+
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ error: vi.fn(),
},
}));
-jest.mock('@mui/x-date-pickers/DateTimePicker', () => {
+vi.mock('@mui/x-date-pickers/DateTimePicker', async () => {
+ const dateTimePickerModule = await vi.importActual(
+ '@mui/x-date-pickers/DesktopDateTimePicker',
+ );
return {
- DateTimePicker: jest.requireActual(
- '@mui/x-date-pickers/DesktopDateTimePicker',
- ).DesktopDateTimePicker,
+ DateTimePicker: dateTimePickerModule.DesktopDateTimePicker,
};
});
@@ -216,11 +233,15 @@ describe('Testing Organisation Action Item Categories', () => {
const searchInput = await screen.findByTestId('searchByName');
expect(searchInput).toBeInTheDocument();
- userEvent.type(searchInput, 'Category 1');
- userEvent.type(searchInput, '{enter}');
+ // Simulate typing and pressing ENTER
+ userEvent.type(searchInput, 'Category 1{enter}');
+
+ // Wait for the filtering to complete
await waitFor(() => {
- expect(screen.getByText('Category 1')).toBeInTheDocument();
- expect(screen.queryByText('Category 2')).toBeNull();
+ // Assert only "Category 1" is visible
+ const categories = screen.getAllByTestId('categoryName');
+ expect(categories).toHaveLength(1);
+ expect(categories[0]).toHaveTextContent('Category 1');
});
});
diff --git a/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.test.tsx b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.spec.tsx
similarity index 76%
rename from src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.test.tsx
rename to src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.spec.tsx
index da92dfd201..9b69a0e331 100644
--- a/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.test.tsx
+++ b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryCreateModal.spec.tsx
@@ -1,30 +1,40 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
import { MockedProvider } from '@apollo/react-testing';
import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { store } from 'state/store';
import i18nForTest from 'utils/i18nForTest';
-
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
-
import AgendaCategoryCreateModal from './AgendaCategoryCreateModal';
+import { vi } from 'vitest';
+/**
+ * This file contains unit tests for the `AgendaCategoryCreateModal` component.
+ *
+ * The tests cover:
+ * - Rendering of the modal, ensuring all elements such as form fields, buttons, and labels are displayed correctly.
+ * - Behavior of form inputs, including updating the `formState` when the `name` and `description` fields are changed.
+ * - Proper invocation of the `createAgendaCategoryHandler` when the form is submitted.
+ * - Integration of Redux state, routing, localization (i18n), and date-picker utilities to ensure compatibility and proper rendering.
+ * - Validations for form controls to check user interactions, including typing and submitting the form.
+ * - Mock function verifications for `setFormState`, `hideCreateModal`, and other handlers to ensure state changes and actions are triggered appropriately.
+ * - Handling edge cases, such as empty fields or invalid data, ensuring graceful degradation of functionality.
+ */
const mockFormState = {
name: 'Test Name',
description: 'Test Description',
createdBy: 'Test User',
};
-const mockHideCreateModal = jest.fn();
-const mockSetFormState = jest.fn();
-const mockCreateAgendaCategoryHandler = jest.fn();
+const mockHideCreateModal = vi.fn();
+const mockSetFormState = vi.fn();
+const mockCreateAgendaCategoryHandler = vi.fn();
const mockT = (key: string): string => key;
describe('AgendaCategoryCreateModal', () => {
- test('renders modal correctly', () => {
+ it('renders modal correctly', () => {
render(
@@ -54,7 +64,7 @@ describe('AgendaCategoryCreateModal', () => {
screen.getByTestId('createAgendaCategoryModalCloseBtn'),
).toBeInTheDocument();
});
- test('tests the condition for formState.name and formState.description', () => {
+ it('tests the condition for formState.name and formState.description', () => {
const mockFormState = {
name: 'Test Name',
description: 'Test Description',
@@ -97,7 +107,7 @@ describe('AgendaCategoryCreateModal', () => {
description: 'New description',
});
});
- test('calls createAgendaCategoryHandler when form is submitted', () => {
+ it('calls createAgendaCategoryHandler when form is submitted', () => {
render(
diff --git a/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.test.tsx b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.spec.tsx
similarity index 82%
rename from src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.test.tsx
rename to src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.spec.tsx
index 168b97abd3..8be982271c 100644
--- a/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.test.tsx
+++ b/src/components/OrgSettings/AgendaItemCategories/AgendaCategoryUpdateModal.spec.tsx
@@ -7,24 +7,36 @@ import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { store } from 'state/store';
import i18nForTest from 'utils/i18nForTest';
-
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
-
import AgendaCategoryUpdateModal from './AgendaCategoryUpdateModal';
+import { vi } from 'vitest';
+
+/**
+ * Unit tests for `AgendaCategoryUpdateModal`:
+ *
+ * - **Rendering**: Verifies key elements (e.g., text, buttons) render correctly.
+ * - **Close Button**: Ensures `hideUpdateModal` is called on close.
+ * - **Form Inputs**: Confirms `setFormState` updates with new `name` and `description`.
+ * - **Submission**: Checks `updateAgendaCategoryHandler` triggers on submit.
+ * - **Integration**: Validates compatibility with Redux, routing, i18n, and MUI date-picker.
+ * - **Mocks**: Ensures handlers (`setFormState`, `hideUpdateModal`, `updateAgendaCategoryHandler`) are called with correct arguments.
+ *
+ * This suite ensures component reliability and behavior consistency.
+ */
const mockFormState = {
name: 'Test Name',
description: 'Test Description',
createdBy: 'Test User',
};
-const mockHideUpdateModal = jest.fn();
-const mockSetFormState = jest.fn();
-const mockUpdateAgendaCategoryHandler = jest.fn();
+const mockHideUpdateModal = vi.fn();
+const mockSetFormState = vi.fn();
+const mockUpdateAgendaCategoryHandler = vi.fn();
const mockT = (key: string): string => key;
describe('AgendaCategoryUpdateModal', () => {
- test('renders modal correctly', () => {
+ it('renders modal correctly', () => {
render(
@@ -53,7 +65,7 @@ describe('AgendaCategoryUpdateModal', () => {
).toBeInTheDocument();
});
- test('calls hideUpdateModal when close button is clicked', () => {
+ it('calls hideUpdateModal when close button is clicked', () => {
render(
@@ -79,7 +91,7 @@ describe('AgendaCategoryUpdateModal', () => {
expect(mockHideUpdateModal).toHaveBeenCalledTimes(1);
});
- test('tests the condition for formState.name and formState.description', () => {
+ it('tests the condition for formState.name and formState.description', () => {
const mockFormState = {
name: 'Test Name',
description: 'Test Description',
@@ -123,7 +135,7 @@ describe('AgendaCategoryUpdateModal', () => {
});
});
- test('calls updateAgendaCategoryHandler when form is submitted', () => {
+ it('calls updateAgendaCategoryHandler when form is submitted', () => {
render(
diff --git a/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.test.tsx b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.spec.tsx
similarity index 80%
rename from src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.test.tsx
rename to src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.spec.tsx
index e05edc665d..a2bd6d0130 100644
--- a/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.test.tsx
+++ b/src/components/OrgSettings/AgendaItemCategories/OrganizationAgendaCategory.spec.tsx
@@ -7,9 +7,7 @@ import {
waitForElementToBeRemoved,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import 'jest-localstorage-mock';
import { MockedProvider } from '@apollo/client/testing';
-import 'jest-location-mock';
import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
@@ -22,23 +20,38 @@ import { store } from 'state/store';
import { StaticMockLink } from 'utils/StaticMockLink';
import OrganizationAgendaCategory from './OrganizationAgendaCategory';
-import {
- MOCKS_ERROR_AGENDA_ITEM_CATEGORY_LIST_QUERY,
- MOCKS_ERROR_MUTATION,
-} from './OrganizationAgendaCategoryErrorMocks';
+import { MOCKS_ERROR_AGENDA_ITEM_CATEGORY_LIST_QUERY } from './OrganizationAgendaCategoryErrorMocks';
import { MOCKS } from './OrganizationAgendaCategoryMocks';
-
-jest.mock('react-toastify', () => ({
+import { vi } from 'vitest';
+
+/**
+ * Unit Tests for `OrganizationAgendaCategory` Component
+ *
+ * - **Load Component**: Verifies successful rendering of key elements like `createAgendaCategory`.
+ * - **Error Handling**: Confirms error view appears when agenda category list query fails.
+ * - **Modal Functionality**:
+ * - Opens and closes the create agenda category modal.
+ * - Ensures `createAgendaCategoryModalCloseBtn` disappears on close.
+ * - **Create Agenda Category**:
+ * - Simulates filling the form and submission.
+ * - Verifies success toast on successful creation (`agendaCategoryCreated`).
+ * - **Integration**: Validates compatibility with Redux, Apollo, i18n, and MUI date-picker.
+ */
+
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ error: vi.fn(),
},
}));
-jest.mock('react-router-dom', () => ({
- ...jest.requireActual('react-router-dom'),
- useParams: () => ({ orgId: '123' }),
-}));
+vi.mock('react-router-dom', async () => {
+ const actual = await vi.importActual('react-router-dom');
+ return {
+ ...actual,
+ useParams: () => ({ orgId: '123' }),
+ };
+});
async function wait(ms = 100): Promise {
await act(() => {
@@ -53,8 +66,6 @@ const link2 = new StaticMockLink(
MOCKS_ERROR_AGENDA_ITEM_CATEGORY_LIST_QUERY,
true,
);
-const link3 = new StaticMockLink(MOCKS_ERROR_MUTATION, true);
-
const translations = {
...JSON.parse(
JSON.stringify(
@@ -70,7 +81,7 @@ describe('Testing Agenda Categories Component', () => {
description: 'Test Description',
createdBy: 'Test User',
};
- test('Component loads correctly', async () => {
+ it('Component loads correctly', async () => {
const { getByText } = render(
@@ -90,7 +101,7 @@ describe('Testing Agenda Categories Component', () => {
});
});
- test('render error component on unsuccessful agenda category list query', async () => {
+ it('render error component on unsuccessful agenda category list query', async () => {
const { queryByText } = render(
@@ -112,7 +123,7 @@ describe('Testing Agenda Categories Component', () => {
});
});
- test('opens and closes the create agenda category modal', async () => {
+ it('opens and closes the create agenda category modal', async () => {
render(
@@ -145,7 +156,7 @@ describe('Testing Agenda Categories Component', () => {
screen.queryByTestId('createAgendaCategoryModalCloseBtn'),
);
});
- test('creates new agenda cagtegory', async () => {
+ it('creates new agenda cagtegory', async () => {
render(
@@ -185,7 +196,9 @@ describe('Testing Agenda Categories Component', () => {
userEvent.click(screen.getByTestId('createAgendaCategoryFormSubmitBtn'));
await waitFor(() => {
- expect(toast.success).toBeCalledWith(translations.agendaCategoryCreated);
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.agendaCategoryCreated,
+ );
});
});
diff --git a/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.test.tsx b/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.spec.tsx
similarity index 80%
rename from src/components/OrgSettings/General/DeleteOrg/DeleteOrg.test.tsx
rename to src/components/OrgSettings/General/DeleteOrg/DeleteOrg.spec.tsx
index 77ffe65c08..e911d195dc 100644
--- a/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.test.tsx
+++ b/src/components/OrgSettings/General/DeleteOrg/DeleteOrg.spec.tsx
@@ -1,11 +1,9 @@
import React, { act } from 'react';
import { MockedProvider } from '@apollo/react-testing';
import { render, screen, waitFor } from '@testing-library/react';
-import 'jest-location-mock';
import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
-
import {
DELETE_ORGANIZATION_MUTATION,
REMOVE_SAMPLE_ORGANIZATION_MUTATION,
@@ -17,6 +15,24 @@ import DeleteOrg from './DeleteOrg';
import { ToastContainer, toast } from 'react-toastify';
import { IS_SAMPLE_ORGANIZATION_QUERY } from 'GraphQl/Queries/Queries';
import useLocalStorage from 'utils/useLocalstorage';
+import { vi } from 'vitest';
+
+/**
+ * Unit Tests for `DeleteOrg` Component
+ *
+ * - **Toggle Modal**: Verifies the ability to open and close the delete organization modal for both sample and non-sample organizations.
+ * - **Delete Organization**:
+ * - Simulates deleting a non-sample organization and ensures the correct GraphQL mutation is triggered.
+ * - Confirms navigation occurs after a sample organization is deleted.
+ * - **Error Handling**:
+ * - Handles errors from `DELETE_ORGANIZATION_MUTATION` and `IS_SAMPLE_ORGANIZATION_QUERY`.
+ * - Verifies `toast.error` is called with appropriate error messages when mutations fail.
+ * - **Mocks**:
+ * - Mocks GraphQL queries and mutations using `StaticMockLink` for different success and error scenarios.
+ * - Uses `useParams` to simulate URL parameters (`orgId`).
+ * - Mocks `useNavigate` to check navigation after successful deletion.
+ * - **Toast Notifications**: Ensures `toast.success` or `toast.error` is triggered based on success or failure of actions.
+ */
const { setItem } = useLocalStorage();
@@ -98,13 +114,16 @@ const MOCKS_WITH_ERROR = [
},
];
-const mockNavgatePush = jest.fn();
+const mockNavgatePush = vi.fn();
let mockURL = '123';
-jest.mock('react-router-dom', () => ({
- ...jest.requireActual('react-router-dom'),
- useParams: () => ({ orgId: mockURL }),
- useNavigate: () => mockNavgatePush,
-}));
+vi.mock('react-router-dom', async () => {
+ const actual = await vi.importActual('react-router-dom');
+ return {
+ ...actual,
+ useParams: () => ({ orgId: mockURL }),
+ useNavigate: () => mockNavgatePush,
+ };
+});
const link = new StaticMockLink(MOCKS, true);
const link2 = new StaticMockLink(MOCKS_WITH_ERROR, true);
@@ -114,7 +133,7 @@ afterEach(() => {
});
describe('Delete Organization Component', () => {
- test('should be able to Toggle Delete Organization Modal', async () => {
+ it('should be able to Toggle Delete Organization Modal', async () => {
mockURL = '456';
setItem('SuperAdmin', true);
await act(async () => {
@@ -143,7 +162,7 @@ describe('Delete Organization Component', () => {
});
});
- test('should be able to Toggle Delete Organization Modal When Organization is Sample Organization', async () => {
+ it('should be able to Toggle Delete Organization Modal When Organization is Sample Organization', async () => {
mockURL = '123';
setItem('SuperAdmin', true);
await act(async () => {
@@ -173,7 +192,7 @@ describe('Delete Organization Component', () => {
});
});
- test('Delete organization functionality should work properly', async () => {
+ it('Delete organization functionality should work properly', async () => {
mockURL = '456';
setItem('SuperAdmin', true);
await act(async () => {
@@ -201,7 +220,7 @@ describe('Delete Organization Component', () => {
});
});
- test('Delete organization functionality should work properly for sample org', async () => {
+ it('Delete organization functionality should work properly for sample org', async () => {
mockURL = '123';
setItem('SuperAdmin', true);
await act(async () => {
@@ -234,10 +253,10 @@ describe('Delete Organization Component', () => {
expect(mockNavgatePush).toHaveBeenCalledWith('/orglist');
});
- test('Error handling for IS_SAMPLE_ORGANIZATION_QUERY mock', async () => {
+ it('Error handling for IS_SAMPLE_ORGANIZATION_QUERY mock', async () => {
mockURL = '123';
setItem('SuperAdmin', true);
- jest.spyOn(toast, 'error');
+ vi.spyOn(toast, 'error');
await act(async () => {
render(
@@ -270,10 +289,10 @@ describe('Delete Organization Component', () => {
});
});
- test('Error handling for DELETE_ORGANIZATION_MUTATION mock', async () => {
+ it('Error handling for DELETE_ORGANIZATION_MUTATION mock', async () => {
mockURL = '456';
setItem('SuperAdmin', true);
- jest.spyOn(toast, 'error');
+ vi.spyOn(toast, 'error');
await act(async () => {
render(
diff --git a/src/components/OrgSettings/General/GeneralSettings.tsx b/src/components/OrgSettings/General/GeneralSettings.tsx
index 4dbca1b6eb..739456150a 100644
--- a/src/components/OrgSettings/General/GeneralSettings.tsx
+++ b/src/components/OrgSettings/General/GeneralSettings.tsx
@@ -1,6 +1,6 @@
import React, { type FC } from 'react';
import { Card, Col, Form, Row } from 'react-bootstrap';
-import styles from 'screens/OrgSettings/OrgSettings.module.css';
+import styles from '../../../../src/style/app.module.css';
import OrgProfileFieldSettings from './OrgProfileFieldSettings/OrgProfileFieldSettings';
import ChangeLanguageDropDown from 'components/ChangeLanguageDropdown/ChangeLanguageDropDown';
import DeleteOrg from './DeleteOrg/DeleteOrg';
diff --git a/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.test.tsx b/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.spec.tsx
similarity index 86%
rename from src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.test.tsx
rename to src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.spec.tsx
index 8db8773381..45bdfbc122 100644
--- a/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.test.tsx
+++ b/src/components/OrgSettings/General/OrgProfileFieldSettings/OrgProfileFieldSettings.spec.tsx
@@ -12,6 +12,18 @@ import {
} from 'GraphQl/Mutations/mutations';
import { ORGANIZATION_CUSTOM_FIELDS } from 'GraphQl/Queries/Queries';
import { ToastContainer, toast } from 'react-toastify';
+import { vi } from 'vitest';
+
+/**
+ * Unit Tests for `OrgProfileFieldSettings` Component
+ *
+ * - Saving Custom Field: Verifies success and failure of adding a custom field.
+ * - Typing Custom Field Name: Ensures input updates correctly.
+ * - Handling No Custom Fields: Displays message when no custom fields exist.
+ * - Removing Custom Field: Verifies success and failure of removing a custom field.
+ * - Error Handling: Ensures error messages for GraphQL mutations are displayed.
+ * - Mock GraphQL Responses: Mocks GraphQL queries and mutations for different scenarios.
+ */
const MOCKS = [
{
@@ -161,7 +173,7 @@ async function wait(ms = 100): Promise {
}
describe('Testing Save Button', () => {
- test('Testing Failure Case For Fetching Custom field', async () => {
+ it('Testing Failure Case For Fetching Custom field', async () => {
render(
{
screen.queryByText('Failed to fetch custom field'),
).toBeInTheDocument();
});
- test('Saving Organization Custom Field', async () => {
+ it('Saving Organization Custom Field', async () => {
render(
@@ -195,7 +207,7 @@ describe('Testing Save Button', () => {
expect(screen.queryByText('Field added successfully')).toBeInTheDocument();
});
- test('Testing Failure Case For Saving Custom Field', async () => {
+ it('Testing Failure Case For Saving Custom Field', async () => {
render(
@@ -218,7 +230,7 @@ describe('Testing Save Button', () => {
).toBeInTheDocument();
});
- test('Testing Typing Organization Custom Field Name', async () => {
+ it('Testing Typing Organization Custom Field Name', async () => {
const { getByTestId } = render(
@@ -232,7 +244,7 @@ describe('Testing Save Button', () => {
const fieldNameInput = getByTestId('customFieldInput');
userEvent.type(fieldNameInput, 'Age');
});
- test('When No Custom Data is Present', async () => {
+ it('When No Custom Data is Present', async () => {
const { getByText } = render(
@@ -244,7 +256,7 @@ describe('Testing Save Button', () => {
await wait();
expect(getByText('No custom fields available')).toBeInTheDocument();
});
- test('Testing Remove Custom Field Button', async () => {
+ it('Testing Remove Custom Field Button', async () => {
render(
@@ -262,8 +274,8 @@ describe('Testing Save Button', () => {
).toBeInTheDocument();
});
- test('Testing Failure Case For Removing Custom Field', async () => {
- const toastSpy = jest.spyOn(toast, 'error');
+ it('Testing Failure Case For Removing Custom Field', async () => {
+ const toastSpy = vi.spyOn(toast, 'error');
render(
diff --git a/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.test.tsx b/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.spec.tsx
similarity index 90%
rename from src/components/OrgSettings/General/OrgUpdate/OrgUpdate.test.tsx
rename to src/components/OrgSettings/General/OrgUpdate/OrgUpdate.spec.tsx
index 6304bb3ec9..2a6496d69a 100644
--- a/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.test.tsx
+++ b/src/components/OrgSettings/General/OrgUpdate/OrgUpdate.spec.tsx
@@ -11,6 +11,18 @@ import {
MOCKS_ERROR_ORGLIST,
MOCKS_ERROR_UPDATE_ORGLIST,
} from './OrgUpdateMocks';
+import { vi } from 'vitest';
+
+/**
+ * Unit Tests for `OrgUpdate` Component
+ *
+ * - Rendering Component with Props: Verifies if labels and input fields are correctly rendered based on mock data.
+ * - Updating Organization: Ensures the form updates with new data and saves changes correctly.
+ * - Error Handling: Verifies error messages when organization cannot be found or updated.
+ * - Toast on Error: Verifies that an error toast is shown when the update fails.
+ * - Form Field Values: Ensures form values are correctly displayed and updated.
+ * - GraphQL Mock Responses: Mocks GraphQL responses for success and error scenarios.
+ */
const link = new StaticMockLink(MOCKS, true);
@@ -45,9 +57,9 @@ describe('Testing Organization Update', () => {
isVisible: true,
};
- global.alert = jest.fn();
+ global.alert = vi.fn();
- test('should render props and text elements test for the page component along with mock data', async () => {
+ it('should render props and text elements test for the page component along with mock data', async () => {
act(() => {
render(
@@ -95,7 +107,7 @@ describe('Testing Organization Update', () => {
expect(isVisible).not.toBeChecked();
});
- test('Should Update organization properly', async () => {
+ it('Should Update organization properly', async () => {
await act(async () => {
render(
@@ -168,7 +180,7 @@ describe('Testing Organization Update', () => {
expect(isVisible).toBeChecked();
});
- test('Should render error occured text when Organization Could not be found', async () => {
+ it('Should render error occured text when Organization Could not be found', async () => {
act(() => {
render(
@@ -182,7 +194,7 @@ describe('Testing Organization Update', () => {
expect(screen.getByText(/Mock Graphql Error/i)).toBeInTheDocument();
});
- test('Should show error occured toast when Organization could not be updated', async () => {
+ it('Should show error occured toast when Organization could not be updated', async () => {
await act(async () => {
render(
diff --git a/src/components/OrganizationCard/OrganizationCard.test.tsx b/src/components/OrganizationCard/OrganizationCard.spec.tsx
similarity index 66%
rename from src/components/OrganizationCard/OrganizationCard.test.tsx
rename to src/components/OrganizationCard/OrganizationCard.spec.tsx
index e4abf3a1fd..c557253d59 100644
--- a/src/components/OrganizationCard/OrganizationCard.test.tsx
+++ b/src/components/OrganizationCard/OrganizationCard.spec.tsx
@@ -2,8 +2,18 @@ import React from 'react';
import { render, screen } from '@testing-library/react';
import OrganizationCard from './OrganizationCard';
+/**
+ * This file contains unit tests for the `OrganizationCard` component.
+ *
+ * The tests cover:
+ * - Rendering the component with all provided props and verifying the correct display of text elements.
+ * - Ensuring the component handles cases where certain props (like image) are not provided.
+ *
+ * These tests utilize the React Testing Library for rendering and querying DOM elements.
+ */
+
describe('Testing the Organization Card', () => {
- test('should render props and text elements test for the page component', () => {
+ it('should render props and text elements test for the page component', () => {
const props = {
id: '123',
image: 'https://via.placeholder.com/80',
@@ -20,7 +30,7 @@ describe('Testing the Organization Card', () => {
expect(screen.getByText(props.lastName)).toBeInTheDocument();
});
- test('Should render text elements when props value is not passed', () => {
+ it('Should render text elements when props value is not passed', () => {
const props = {
id: '123',
image: '',
diff --git a/src/components/OrganizationCardStart/OrganizationCardStart.test.tsx b/src/components/OrganizationCardStart/OrganizationCardStart.spec.tsx
similarity index 76%
rename from src/components/OrganizationCardStart/OrganizationCardStart.test.tsx
rename to src/components/OrganizationCardStart/OrganizationCardStart.spec.tsx
index dd65c8649e..71263fb65b 100644
--- a/src/components/OrganizationCardStart/OrganizationCardStart.test.tsx
+++ b/src/components/OrganizationCardStart/OrganizationCardStart.spec.tsx
@@ -1,9 +1,10 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import OrganizationCardStart from './OrganizationCardStart';
+import { describe, expect } from 'vitest';
describe('Testing the Organization Cards', () => {
- test('should render props and text elements test for the page component', () => {
+ it('should render props and text elements test for the page component', () => {
const props = {
id: '123',
image: 'https://via.placeholder.com/80',
@@ -15,7 +16,7 @@ describe('Testing the Organization Cards', () => {
expect(screen.getByText(props.name)).toBeInTheDocument();
});
- test('Should render text elements when props value is not passed', () => {
+ it('Should render text elements when props value is not passed', () => {
const props = {
id: '123',
image: '',
diff --git a/src/components/OrganizationDashCards/CardItem.module.css b/src/components/OrganizationDashCards/CardItem.module.css
deleted file mode 100644
index bfb85cb1bb..0000000000
--- a/src/components/OrganizationDashCards/CardItem.module.css
+++ /dev/null
@@ -1,81 +0,0 @@
-.cardItem {
- position: relative;
- display: flex;
- align-items: center;
- border: 1px solid var(--bs-gray-200);
- border-radius: 8px;
- margin-top: 20px;
-}
-
-.cardItem .iconWrapper {
- position: relative;
- height: 40px;
- width: 40px;
- display: flex;
- justify-content: center;
- align-items: center;
-}
-
-.cardItem .iconWrapper .themeOverlay {
- background: var(--bs-primary);
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- opacity: 0.12;
- border-radius: 50%;
-}
-
-.cardItem .iconWrapper .dangerOverlay {
- background: var(--bs-danger);
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- opacity: 0.12;
- border-radius: 50%;
-}
-
-.cardItem .title {
- font-size: 1rem;
- flex: 1;
- overflow: hidden;
- display: -webkit-box;
- -webkit-line-clamp: 1;
- line-clamp: 1;
- -webkit-box-orient: vertical;
- margin-left: 3px;
-}
-
-.cardItem .location {
- font-size: 0.9rem;
- color: var(--bs-primary);
- overflow: hidden;
- display: -webkit-box;
- -webkit-line-clamp: 1;
- line-clamp: 1;
- -webkit-box-orient: vertical;
-}
-
-.cardItem .time {
- font-size: 0.9rem;
- color: var(--bs-secondary);
-}
-
-.cardItem .creator {
- font-size: 1rem;
- color: rgb(33, 208, 21);
-}
-
-.rightCard {
- display: flex;
- gap: 7px;
- min-width: 170px;
- justify-content: center;
- flex-direction: column;
- margin-left: 10px;
- overflow-x: hidden;
- width: 210px;
-}
diff --git a/src/components/OrganizationDashCards/CardItem.test.tsx b/src/components/OrganizationDashCards/CardItem.spec.tsx
similarity index 98%
rename from src/components/OrganizationDashCards/CardItem.test.tsx
rename to src/components/OrganizationDashCards/CardItem.spec.tsx
index 31f3474607..71ff22774b 100644
--- a/src/components/OrganizationDashCards/CardItem.test.tsx
+++ b/src/components/OrganizationDashCards/CardItem.spec.tsx
@@ -1,8 +1,8 @@
-import React from 'react';
import { render, screen } from '@testing-library/react';
import CardItem from './CardItem';
import type { InterfaceCardItem } from './CardItem';
import dayjs from 'dayjs';
+import React from 'react';
describe('Testing the Organization Card', () => {
test('Should render props and text elements For event card', () => {
@@ -36,7 +36,6 @@ describe('Testing the Organization Card', () => {
email: 'johndoe@example.com',
firstName: 'John',
lastName: 'Doe',
- __typename: 'User',
_id: '1',
},
};
diff --git a/src/components/OrganizationDashCards/CardItem.tsx b/src/components/OrganizationDashCards/CardItem.tsx
index a7cfaa0f57..24e8182913 100644
--- a/src/components/OrganizationDashCards/CardItem.tsx
+++ b/src/components/OrganizationDashCards/CardItem.tsx
@@ -5,7 +5,7 @@ import MarkerIcon from 'assets/svgs/cardItemLocation.svg?react';
import DateIcon from 'assets/svgs/cardItemDate.svg?react';
import UserIcon from 'assets/svgs/user.svg?react';
import dayjs from 'dayjs';
-import styles from './CardItem.module.css';
+import styles from '../../style/app.module.css';
import { PersonAddAlt1Rounded } from '@mui/icons-material';
/**
@@ -17,7 +17,12 @@ export interface InterfaceCardItem {
time?: string;
startdate?: string;
enddate?: string;
- creator?: any;
+ creator?: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ email: string;
+ };
location?: string;
}
@@ -27,7 +32,7 @@ export interface InterfaceCardItem {
* @param props - Props for the CardItem component.
* @returns JSX element representing the card item.
*/
-const cardItem = (props: InterfaceCardItem): JSX.Element => {
+const CardItem = (props: InterfaceCardItem): JSX.Element => {
const { creator, type, title, startdate, time, enddate, location } = props;
return (
<>
@@ -120,4 +125,4 @@ const cardItem = (props: InterfaceCardItem): JSX.Element => {
);
};
-export default cardItem;
+export default CardItem;
diff --git a/src/components/OrganizationDashCards/CardItemLoading.spec.tsx b/src/components/OrganizationDashCards/CardItemLoading.spec.tsx
new file mode 100644
index 0000000000..8a132657a7
--- /dev/null
+++ b/src/components/OrganizationDashCards/CardItemLoading.spec.tsx
@@ -0,0 +1,23 @@
+import CardItemLoading from './CardItemLoading';
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import styles from '../../style/app.module.css';
+describe('Test the CardItemLoading component', () => {
+ test('Should render the component', () => {
+ render( );
+ expect(screen.getByTestId('cardItemLoading')).toBeInTheDocument();
+ });
+
+ test('Should render all required child elements', () => {
+ render( );
+
+ const cardItemLoading = screen.getByTestId('cardItemLoading');
+ expect(cardItemLoading).toBeInTheDocument();
+
+ const iconWrapper = cardItemLoading.querySelector(`.${styles.iconWrapper}`);
+ expect(iconWrapper).toBeInTheDocument();
+
+ const title = cardItemLoading.querySelector(`.${styles.title}`);
+ expect(title).toBeInTheDocument();
+ });
+});
diff --git a/src/components/OrganizationDashCards/CardItemLoading.tsx b/src/components/OrganizationDashCards/CardItemLoading.tsx
index 2c57bb70b3..9db7c333c8 100644
--- a/src/components/OrganizationDashCards/CardItemLoading.tsx
+++ b/src/components/OrganizationDashCards/CardItemLoading.tsx
@@ -1,14 +1,17 @@
import React from 'react';
-import styles from './CardItem.module.css';
+import styles from '../../style/app.module.css';
/**
* CardItemLoading component is a loading state for the card item. It is used when the data is being fetched.
* @returns JSX.Element
*/
-const cardItemLoading = (): JSX.Element => {
+const CardItemLoading = (): JSX.Element => {
return (
<>
-
+
@@ -25,4 +28,4 @@ const cardItemLoading = (): JSX.Element => {
);
};
-export default cardItemLoading;
+export default CardItemLoading;
diff --git a/src/components/OrganizationDashCards/DashBoardCardLoading.spec.tsx b/src/components/OrganizationDashCards/DashBoardCardLoading.spec.tsx
new file mode 100644
index 0000000000..f1cb2e56fa
--- /dev/null
+++ b/src/components/OrganizationDashCards/DashBoardCardLoading.spec.tsx
@@ -0,0 +1,28 @@
+import DashBoardCardLoading from './DashboardCardLoading';
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import styles from '../../style/app.module.css';
+describe('Testing the DashBoardCardLoading component', () => {
+ beforeEach(() => {
+ render(
);
+ });
+ test('should render the component', () => {
+ expect(screen.getByTestId('Card')).toBeInTheDocument();
+ });
+
+ test('should render every children elements of the component', () => {
+ const Card = screen.queryByTestId('Card');
+ const CardBody = Card?.querySelector(`.${styles.cardBody}`);
+ expect(CardBody).toBeInTheDocument();
+ const iconWrapper = Card?.querySelector(`.${styles.iconWrapper}`);
+ expect(iconWrapper).toBeInTheDocument();
+ const themeOverlay = Card?.querySelector(`.${styles.themeOverlay}`);
+ expect(themeOverlay).toBeInTheDocument();
+ const textWrapper = Card?.querySelector(`.${styles.textWrapper}`);
+ expect(textWrapper).toBeInTheDocument();
+ const primaryText = Card?.querySelector(`.${styles.primaryText}`);
+ expect(primaryText).toBeInTheDocument();
+ const secondaryText = Card?.querySelector(`.${styles.secondaryText}`);
+ expect(secondaryText).toBeInTheDocument();
+ });
+});
diff --git a/src/components/OrganizationDashCards/DashboardCard.test.tsx b/src/components/OrganizationDashCards/DashboardCard.spec.tsx
similarity index 99%
rename from src/components/OrganizationDashCards/DashboardCard.test.tsx
rename to src/components/OrganizationDashCards/DashboardCard.spec.tsx
index 71e5e1fed0..50a47f69d5 100644
--- a/src/components/OrganizationDashCards/DashboardCard.test.tsx
+++ b/src/components/OrganizationDashCards/DashboardCard.spec.tsx
@@ -1,7 +1,6 @@
-import React from 'react';
import { render, screen } from '@testing-library/react';
import DashboardCard from './DashboardCard';
-
+import React from 'react';
describe('Testing the Dashboard Card', () => {
test('should render props and text elements For event card', () => {
const props = {
diff --git a/src/components/OrganizationDashCards/DashboardCard.tsx b/src/components/OrganizationDashCards/DashboardCard.tsx
index 4a29eafc57..0f0222ba29 100644
--- a/src/components/OrganizationDashCards/DashboardCard.tsx
+++ b/src/components/OrganizationDashCards/DashboardCard.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { Card, Row } from 'react-bootstrap';
import Col from 'react-bootstrap/Col';
-import styles from './Dashboardcard.module.css';
+import styles from '../../style/app.module.css';
/** Dashboard card component is used to display the card with icon, title and count.
* @param icon - Icon for the card
diff --git a/src/components/OrganizationDashCards/DashboardCardLoading.tsx b/src/components/OrganizationDashCards/DashboardCardLoading.tsx
index b37c1e2065..4882e906d2 100644
--- a/src/components/OrganizationDashCards/DashboardCardLoading.tsx
+++ b/src/components/OrganizationDashCards/DashboardCardLoading.tsx
@@ -1,15 +1,15 @@
import React from 'react';
import { Card, Row } from 'react-bootstrap';
import Col from 'react-bootstrap/Col';
-import styles from './Dashboardcard.module.css';
+import styles from '../../style/app.module.css';
/**
* Dashboard card loading component is a loading state for the dashboard card. It is used when the data is being fetched.
* @returns JSX.Element
*/
-const dashBoardCardLoading = (): JSX.Element => {
+const DashBoardCardLoading = (): JSX.Element => {
return (
-
+
@@ -37,4 +37,4 @@ const dashBoardCardLoading = (): JSX.Element => {
);
};
-export default dashBoardCardLoading;
+export default DashBoardCardLoading;
diff --git a/src/components/OrganizationDashCards/Dashboardcard.module.css b/src/components/OrganizationDashCards/Dashboardcard.module.css
deleted file mode 100644
index 365657fb4f..0000000000
--- a/src/components/OrganizationDashCards/Dashboardcard.module.css
+++ /dev/null
@@ -1,60 +0,0 @@
-.cardBody {
- padding: 1.25rem 1.5rem;
-}
-
-.cardBody .iconWrapper {
- position: relative;
- height: 48px;
- width: 48px;
- display: flex;
- justify-content: center;
- align-items: center;
-}
-
-.cardBody .iconWrapper .themeOverlay {
- background: var(--bs-primary);
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- opacity: 0.12;
- border-radius: 50%;
-}
-
-.cardBody .textWrapper .primaryText {
- font-size: 24px;
- font-weight: bold;
- display: block;
-}
-
-.cardBody .textWrapper .secondaryText {
- font-size: 14px;
- display: block;
- color: var(--bs-secondary);
-}
-
-@media (max-width: 600px) {
- .cardBody {
- min-height: 120px;
- }
-
- .cardBody .iconWrapper {
- position: absolute;
- top: 1rem;
- left: 1rem;
- }
-
- .cardBody .textWrapper {
- margin-top: calc(0.5rem + 36px);
- text-align: right;
- }
-
- .cardBody .textWrapper .primaryText {
- font-size: 1.5rem;
- }
-
- .cardBody .textWrapper .secondaryText {
- font-size: 1rem;
- }
-}
diff --git a/src/components/OrganizationScreen/OrganizationScreen.spec.tsx b/src/components/OrganizationScreen/OrganizationScreen.spec.tsx
new file mode 100644
index 0000000000..e6a75c46d8
--- /dev/null
+++ b/src/components/OrganizationScreen/OrganizationScreen.spec.tsx
@@ -0,0 +1,97 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+import OrganizationScreen from './OrganizationScreen';
+import { ORGANIZATION_EVENT_LIST } from 'GraphQl/Queries/Queries';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import styles from './OrganizationScreen.module.css';
+import { vi } from 'vitest';
+const mockID: string | undefined = '123';
+vi.mock('react-router-dom', async () => ({
+ ...(await vi.importActual('react-router-dom')),
+ useParams: () => ({ orgId: mockID }),
+ useMatch: () => ({ params: { eventId: 'event123', orgId: '123' } }),
+}));
+
+const MOCKS = [
+ {
+ request: {
+ query: ORGANIZATION_EVENT_LIST,
+ variables: { id: '123' },
+ },
+ result: {
+ data: {
+ eventsByOrganization: [
+ {
+ _id: 'event123',
+ title: 'Test Event Title',
+ description: 'Test Description',
+ startDate: '2024-01-01',
+ endDate: '2024-01-02',
+ location: 'Test Location',
+ startTime: '09:00',
+ endTime: '17:00',
+ allDay: false,
+ recurring: false,
+ isPublic: true,
+ isRegisterable: true,
+ },
+ ],
+ },
+ },
+ },
+];
+
+const link = new StaticMockLink(MOCKS, true);
+
+describe('Testing OrganizationScreen', () => {
+ const renderComponent = (): void => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ };
+
+ test('renders correctly with event title', async () => {
+ renderComponent();
+
+ await waitFor(() => {
+ const mainPage = screen.getByTestId('mainpageright');
+ expect(mainPage).toBeInTheDocument();
+ });
+ });
+
+ test('handles drawer toggle correctly', () => {
+ renderComponent();
+
+ const closeButton = screen.getByTestId('closeMenu');
+ fireEvent.click(closeButton);
+
+ expect(screen.getByTestId('mainpageright')).toHaveClass(styles.expand);
+
+ const openButton = screen.getByTestId('openMenu');
+ fireEvent.click(openButton);
+
+ // Check for expand class after opening
+ expect(screen.getByTestId('mainpageright')).toHaveClass(styles.contract);
+ });
+
+ test('handles window resize', () => {
+ renderComponent();
+ window.innerWidth = 800;
+ fireEvent(window, new Event('resize'));
+ expect(screen.getByTestId('mainpageright')).toHaveClass(styles.expand);
+ });
+});
diff --git a/src/components/OrganizationScreen/OrganizationScreen.test.tsx b/src/components/OrganizationScreen/OrganizationScreen.test.tsx
deleted file mode 100644
index d31511ea1e..0000000000
--- a/src/components/OrganizationScreen/OrganizationScreen.test.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import React from 'react';
-import { MockedProvider } from '@apollo/react-testing';
-import { fireEvent, render, screen } from '@testing-library/react';
-import { I18nextProvider } from 'react-i18next';
-import 'jest-location-mock';
-import { Provider } from 'react-redux';
-import { BrowserRouter } from 'react-router-dom';
-import { store } from 'state/store';
-import i18nForTest from 'utils/i18nForTest';
-import OrganizationScreen from './OrganizationScreen';
-import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries';
-import { StaticMockLink } from 'utils/StaticMockLink';
-
-let mockID: string | undefined = '123';
-jest.mock('react-router-dom', () => ({
- ...jest.requireActual('react-router-dom'),
- useParams: () => ({ orgId: mockID }),
-}));
-
-const MOCKS = [
- {
- request: {
- query: ORGANIZATIONS_LIST,
- variables: { id: '123' },
- },
- result: {
- data: {
- organizations: [
- {
- _id: '123',
- image: null,
- creator: {
- firstName: 'John',
- lastName: 'Doe',
- email: 'JohnDoe@example.com',
- },
- name: 'Test Organization',
- description: 'Testing this organization',
- address: {
- city: 'Mountain View',
- countryCode: 'US',
- dependentLocality: 'Some Dependent Locality',
- line1: '123 Main Street',
- line2: 'Apt 456',
- postalCode: '94040',
- sortingCode: 'XYZ-789',
- state: 'CA',
- },
- userRegistrationRequired: true,
- visibleInSearch: true,
- members: [],
- admins: [],
- membershipRequests: [],
- blockedUsers: [],
- },
- ],
- },
- },
- },
-];
-const link = new StaticMockLink(MOCKS, true);
-
-const resizeWindow = (width: number): void => {
- window.innerWidth = width;
- fireEvent(window, new Event('resize'));
-};
-
-const clickToggleMenuBtn = (toggleButton: HTMLElement): void => {
- fireEvent.click(toggleButton);
-};
-
-describe('Testing LeftDrawer in OrganizationScreen', () => {
- test('Testing LeftDrawer in page functionality', async () => {
- render(
-
-
-
-
-
-
-
-
- ,
- );
- const toggleButton = screen.getByTestId('closeMenu') as HTMLElement;
- const icon = toggleButton.querySelector('i');
-
- // Resize window to a smaller width
- resizeWindow(800);
- clickToggleMenuBtn(toggleButton);
- expect(icon).toHaveClass('fa fa-angle-double-left');
- // Resize window back to a larger width
-
- resizeWindow(1000);
- clickToggleMenuBtn(toggleButton);
- expect(icon).toHaveClass('fa fa-angle-double-right');
-
- clickToggleMenuBtn(toggleButton);
- expect(icon).toHaveClass('fa fa-angle-double-left');
- });
-
- test('should be redirected to / if orgId is undefined', async () => {
- mockID = undefined;
- render(
-
-
-
-
-
-
-
-
- ,
- );
- expect(window.location.pathname).toEqual('/');
- });
-});
diff --git a/src/components/OrganizationScreen/OrganizationScreen.tsx b/src/components/OrganizationScreen/OrganizationScreen.tsx
index 7bd2119a82..85fb6ee181 100644
--- a/src/components/OrganizationScreen/OrganizationScreen.tsx
+++ b/src/components/OrganizationScreen/OrganizationScreen.tsx
@@ -2,7 +2,13 @@ import LeftDrawerOrg from 'components/LeftDrawerOrg/LeftDrawerOrg';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
-import { Navigate, Outlet, useLocation, useParams } from 'react-router-dom';
+import {
+ Navigate,
+ Outlet,
+ useLocation,
+ useParams,
+ useMatch,
+} from 'react-router-dom';
import { updateTargets } from 'state/action-creators';
import { useAppDispatch } from 'state/hooks';
import type { RootState } from 'state/reducers';
@@ -11,6 +17,12 @@ import styles from './OrganizationScreen.module.css';
import ProfileDropdown from 'components/ProfileDropdown/ProfileDropdown';
import { Button } from 'react-bootstrap';
import type { InterfaceMapType } from 'utils/interfaces';
+import { useQuery } from '@apollo/client';
+import { ORGANIZATION_EVENT_LIST } from 'GraphQl/Queries/Queries';
+interface InterfaceEvent {
+ _id: string;
+ title: string;
+}
/**
* Component for the organization screen
@@ -33,9 +45,13 @@ const OrganizationScreen = (): JSX.Element => {
// Get the organization ID from the URL parameters
const { orgId } = useParams();
+ const [eventName, setEventName] = useState(null);
+
+ const isEventPath = useMatch('/event/:orgId/:eventId');
// If no organization ID is found, navigate back to the home page
if (!orgId) {
+ /*istanbul ignore next*/
return ;
}
@@ -50,7 +66,29 @@ const OrganizationScreen = (): JSX.Element => {
// Update targets whenever the organization ID changes
useEffect(() => {
dispatch(updateTargets(orgId));
- }, [orgId]); // Added orgId to the dependency array
+ }, [orgId]);
+
+ const { data: eventsData } = useQuery(ORGANIZATION_EVENT_LIST, {
+ variables: { id: orgId },
+ });
+
+ useEffect(() => {
+ if (isEventPath?.params.eventId && eventsData?.eventsByOrganization) {
+ const eventId = isEventPath.params.eventId;
+ const event = eventsData.eventsByOrganization.find(
+ (e: InterfaceEvent) => e._id === eventId,
+ );
+ /*istanbul ignore next*/
+ if (!event) {
+ console.warn(`Event with id ${eventId} not found`);
+ setEventName(null);
+ return;
+ }
+ setEventName(event.title);
+ } else {
+ setEventName(null);
+ }
+ }, [isEventPath, eventsData]);
// Handle screen resizing to show/hide the side drawer
const handleResize = (): void => {
@@ -112,6 +150,7 @@ const OrganizationScreen = (): JSX.Element => {
{t('title')}
+ {eventName && {eventName} }
@@ -146,4 +185,5 @@ const map: InterfaceMapType = {
blockuser: 'blockUnblockUser',
orgvenues: 'organizationVenues',
event: 'eventManagement',
+ leaderboard: 'leaderboard',
};
diff --git a/src/components/Pagination/Pagination.test.tsx b/src/components/Pagination/Pagination.spec.tsx
similarity index 75%
rename from src/components/Pagination/Pagination.test.tsx
rename to src/components/Pagination/Pagination.spec.tsx
index b8161c4c84..873f790565 100644
--- a/src/components/Pagination/Pagination.test.tsx
+++ b/src/components/Pagination/Pagination.spec.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { act } from 'react';
import { render, screen } from '@testing-library/react';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
@@ -6,6 +6,7 @@ import { createTheme, ThemeProvider } from '@mui/material/styles';
import Pagination from './Pagination';
import { store } from 'state/store';
import userEvent from '@testing-library/user-event';
+import { describe, it } from 'vitest';
describe('Testing Pagination component', () => {
const props = {
@@ -17,7 +18,7 @@ describe('Testing Pagination component', () => {
},
};
- test('Component should be rendered properly on rtl', () => {
+ it('Component should be rendered properly on rtl', async () => {
render(
@@ -25,9 +26,10 @@ describe('Testing Pagination component', () => {
,
);
-
- userEvent.click(screen.getByTestId(/nextPage/i));
- userEvent.click(screen.getByTestId(/previousPage/i));
+ await act(async () => {
+ userEvent.click(screen.getByTestId(/nextPage/i));
+ userEvent.click(screen.getByTestId(/previousPage/i));
+ });
});
});
@@ -41,7 +43,7 @@ const props = {
theme: { direction: 'rtl' },
};
-test('Component should be rendered properly', () => {
+it('Component should be rendered properly', async () => {
const theme = createTheme({
direction: 'rtl',
});
@@ -56,6 +58,8 @@ test('Component should be rendered properly', () => {
,
);
- userEvent.click(screen.getByTestId(/nextPage/i));
- userEvent.click(screen.getByTestId(/previousPage/i));
+ await act(async () => {
+ userEvent.click(screen.getByTestId(/nextPage/i));
+ userEvent.click(screen.getByTestId(/previousPage/i));
+ });
});
diff --git a/src/components/ProfileDropdown/ProfileDropdown.test.tsx b/src/components/ProfileDropdown/ProfileDropdown.spec.tsx
similarity index 52%
rename from src/components/ProfileDropdown/ProfileDropdown.test.tsx
rename to src/components/ProfileDropdown/ProfileDropdown.spec.tsx
index 785f33ee92..06883768e6 100644
--- a/src/components/ProfileDropdown/ProfileDropdown.test.tsx
+++ b/src/components/ProfileDropdown/ProfileDropdown.spec.tsx
@@ -1,17 +1,29 @@
import React, { act } from 'react';
-import { render, screen } from '@testing-library/react';
+import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { BrowserRouter } from 'react-router-dom';
import ProfileDropdown from './ProfileDropdown';
-import 'jest-localstorage-mock';
import { MockedProvider } from '@apollo/react-testing';
import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
import useLocalStorage from 'utils/useLocalstorage';
import { I18nextProvider } from 'react-i18next';
import i18nForTest from 'utils/i18nForTest';
import { GET_COMMUNITY_SESSION_TIMEOUT_DATA } from 'GraphQl/Queries/Queries';
+import { vi } from 'vitest';
const { setItem } = useLocalStorage();
+
+const mockNavigate = vi.fn();
+
+// Mock useNavigate hook
+vi.mock('react-router-dom', async () => {
+ const actual = await vi.importActual('react-router-dom');
+ return {
+ ...actual,
+ useNavigate: () => mockNavigate,
+ };
+});
+
const MOCKS = [
{
request: {
@@ -38,13 +50,13 @@ const MOCKS = [
},
];
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- warn: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ warn: vi.fn(),
+ error: vi.fn(),
},
- clear: jest.fn(),
+ clear: vi.fn(),
}));
beforeEach(() => {
@@ -60,11 +72,11 @@ beforeEach(() => {
});
afterEach(() => {
- jest.clearAllMocks();
+ vi.clearAllMocks();
localStorage.clear();
});
afterEach(() => {
- jest.clearAllMocks();
+ vi.clearAllMocks();
localStorage.clear();
});
@@ -130,19 +142,124 @@ describe('ProfileDropdown Component', () => {
describe('Member screen routing testing', () => {
test('member screen', async () => {
+ setItem('SuperAdmin', false);
+ setItem('AdminFor', []);
+
render(
-
+
+
+
,
);
+
await act(async () => {
userEvent.click(screen.getByTestId('togDrop'));
});
+ await act(async () => {
+ userEvent.click(screen.getByTestId('profileBtn'));
+ });
+
+ expect(mockNavigate).toHaveBeenCalledWith('/user/settings');
+ });
+ });
+
+ test('handles error when revoking refresh token during logout', async () => {
+ // Mock the revokeRefreshToken mutation to throw an error
+ const MOCKS_WITH_ERROR = [
+ {
+ request: {
+ query: REVOKE_REFRESH_TOKEN,
+ },
+ error: new Error('Failed to revoke refresh token'),
+ },
+ ];
+
+ const consoleErrorMock = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => {});
+
+ render(
+
+
+
+
+ ,
+ );
+
+ // Open the dropdown
+ await act(async () => {
+ userEvent.click(screen.getByTestId('togDrop'));
+ });
+
+ // Click the logout button
+ await act(async () => {
+ userEvent.click(screen.getByTestId('logoutBtn'));
+ });
+
+ // Wait for any pending promises
+ await waitFor(() => {
+ // Assert that console.error was called
+ expect(consoleErrorMock).toHaveBeenCalledWith(
+ 'Error revoking refresh token:',
+ expect.any(Error),
+ );
+ });
+
+ // Cleanup mock
+ consoleErrorMock.mockRestore();
+ });
+
+ test('navigates to /user/settings for a user', async () => {
+ setItem('SuperAdmin', false);
+ setItem('AdminFor', []);
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await act(async () => {
+ userEvent.click(screen.getByTestId('togDrop'));
+ });
+
+ await act(async () => {
userEvent.click(screen.getByTestId('profileBtn'));
- expect(global.window.location.pathname).toBe('/user/settings');
});
+
+ expect(mockNavigate).toHaveBeenCalledWith('/user/settings');
+ });
+
+ test('navigates to /member/:userID for non-user roles', async () => {
+ setItem('SuperAdmin', true); // Set as admin
+ setItem('id', '123');
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await act(async () => {
+ userEvent.click(screen.getByTestId('togDrop'));
+ });
+
+ await act(async () => {
+ userEvent.click(screen.getByTestId('profileBtn'));
+ });
+
+ expect(mockNavigate).toHaveBeenCalledWith('/member/123');
});
});
diff --git a/src/components/RecurrenceOptions/CustomRecurrence.test.tsx b/src/components/RecurrenceOptions/CustomRecurrence.spec.tsx
similarity index 97%
rename from src/components/RecurrenceOptions/CustomRecurrence.test.tsx
rename to src/components/RecurrenceOptions/CustomRecurrence.spec.tsx
index f21553c5af..236e34e855 100644
--- a/src/components/RecurrenceOptions/CustomRecurrence.test.tsx
+++ b/src/components/RecurrenceOptions/CustomRecurrence.spec.tsx
@@ -9,7 +9,6 @@ import {
} from '@testing-library/react';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
-import 'jest-location-mock';
import { I18nextProvider } from 'react-i18next';
import OrganizationEvents from '../../screens/OrganizationEvents/OrganizationEvents';
@@ -23,6 +22,7 @@ import { ThemeProvider } from 'react-bootstrap';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { MOCKS } from '../../screens/OrganizationEvents/OrganizationEventsMocks';
+import { describe, test, expect, vi } from 'vitest';
const theme = createTheme({
palette: {
@@ -48,19 +48,20 @@ const translations = JSON.parse(
),
);
-jest.mock('@mui/x-date-pickers/DateTimePicker', () => {
+vi.mock('@mui/x-date-pickers/DateTimePicker', async () => {
+ const actual = await vi.importActual(
+ '@mui/x-date-pickers/DesktopDateTimePicker',
+ );
return {
- DateTimePicker: jest.requireActual(
- '@mui/x-date-pickers/DesktopDateTimePicker',
- ).DesktopDateTimePicker,
+ DateTimePicker: actual.DesktopDateTimePicker,
};
});
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- warning: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ warning: vi.fn(),
+ error: vi.fn(),
},
}));
@@ -581,7 +582,7 @@ describe('Testing the creaction of recurring events with custom recurrence patte
userEvent.click(screen.getByTestId('createEventBtn'));
await waitFor(() => {
- expect(toast.success).toBeCalledWith(translations.eventCreated);
+ expect(toast.success).toHaveBeenCalledWith(translations.eventCreated);
});
await waitFor(() => {
@@ -709,7 +710,7 @@ describe('Testing the creaction of recurring events with custom recurrence patte
userEvent.click(screen.getByTestId('createEventBtn'));
await waitFor(() => {
- expect(toast.success).toBeCalledWith(translations.eventCreated);
+ expect(toast.success).toHaveBeenCalledWith(translations.eventCreated);
});
await waitFor(() => {
diff --git a/src/components/RecurrenceOptions/RecurrenceOptions.test.tsx b/src/components/RecurrenceOptions/RecurrenceOptions.spec.tsx
similarity index 97%
rename from src/components/RecurrenceOptions/RecurrenceOptions.test.tsx
rename to src/components/RecurrenceOptions/RecurrenceOptions.spec.tsx
index 510f7a04aa..07adf7bd16 100644
--- a/src/components/RecurrenceOptions/RecurrenceOptions.test.tsx
+++ b/src/components/RecurrenceOptions/RecurrenceOptions.spec.tsx
@@ -9,7 +9,6 @@ import {
} from '@testing-library/react';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
-import 'jest-location-mock';
import { I18nextProvider } from 'react-i18next';
import OrganizationEvents from '../../screens/OrganizationEvents/OrganizationEvents';
@@ -23,6 +22,7 @@ import { ThemeProvider } from 'react-bootstrap';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { MOCKS } from '../../screens/OrganizationEvents/OrganizationEventsMocks';
+import { describe, test, expect, vi } from 'vitest';
const theme = createTheme({
palette: {
@@ -52,19 +52,20 @@ const translations = {
...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})),
};
-jest.mock('@mui/x-date-pickers/DateTimePicker', () => {
+vi.mock('@mui/x-date-pickers/DateTimePicker', async () => {
+ const actual = await vi.importActual(
+ '@mui/x-date-pickers/DesktopDateTimePicker',
+ );
return {
- DateTimePicker: jest.requireActual(
- '@mui/x-date-pickers/DesktopDateTimePicker',
- ).DesktopDateTimePicker,
+ DateTimePicker: actual.DesktopDateTimePicker,
};
});
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- warning: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ warning: vi.fn(),
+ error: vi.fn(),
},
}));
@@ -453,7 +454,7 @@ describe('Testing the creaction of recurring events through recurrence options',
userEvent.click(screen.getByTestId('createEventBtn'));
await waitFor(() => {
- expect(toast.success).toBeCalledWith(translations.eventCreated);
+ expect(toast.success).toHaveBeenCalledWith(translations.eventCreated);
});
await waitFor(() => {
@@ -575,7 +576,7 @@ describe('Testing the creaction of recurring events through recurrence options',
userEvent.click(screen.getByTestId('createEventBtn'));
await waitFor(() => {
- expect(toast.success).toBeCalledWith(translations.eventCreated);
+ expect(toast.success).toHaveBeenCalledWith(translations.eventCreated);
});
await waitFor(() => {
diff --git a/src/components/RequestsTableItem/RequestsTableItem.test.tsx b/src/components/RequestsTableItem/RequestsTableItem.spec.tsx
similarity index 62%
rename from src/components/RequestsTableItem/RequestsTableItem.test.tsx
rename to src/components/RequestsTableItem/RequestsTableItem.spec.tsx
index bbd895300b..b5194fcdce 100644
--- a/src/components/RequestsTableItem/RequestsTableItem.test.tsx
+++ b/src/components/RequestsTableItem/RequestsTableItem.spec.tsx
@@ -11,6 +11,7 @@ import { BrowserRouter } from 'react-router-dom';
const link = new StaticMockLink(MOCKS, true);
import useLocalStorage from 'utils/useLocalstorage';
import userEvent from '@testing-library/user-event';
+import { vi } from 'vitest';
const { setItem } = useLocalStorage();
@@ -21,13 +22,13 @@ async function wait(ms = 100): Promise {
});
});
}
-const resetAndRefetchMock = jest.fn();
+const resetAndRefetchMock = vi.fn();
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- error: jest.fn(),
- warning: jest.fn(),
+ success: vi.fn(),
+ error: vi.fn(),
+ warning: vi.fn(),
},
}));
@@ -37,17 +38,17 @@ beforeEach(() => {
afterEach(() => {
localStorage.clear();
- jest.clearAllMocks();
+ vi.clearAllMocks();
});
describe('Testing User Table Item', () => {
- console.error = jest.fn((message) => {
+ console.error = vi.fn((message) => {
if (message.includes('validateDOMNesting')) {
return;
}
console.warn(message);
});
- test('Should render props and text elements test for the page component', async () => {
+ it('Should render props and text elements it for the page component', async () => {
const props: {
request: InterfaceRequestsListItem;
index: number;
@@ -81,7 +82,7 @@ describe('Testing User Table Item', () => {
expect(screen.getByText(/john@example.com/i)).toBeInTheDocument();
});
- test('Accept MembershipRequest Button works properly', async () => {
+ it('Accept MembershipRequest Button works properly', async () => {
const props: {
request: InterfaceRequestsListItem;
index: number;
@@ -113,7 +114,39 @@ describe('Testing User Table Item', () => {
userEvent.click(screen.getByTestId('acceptMembershipRequestBtn123'));
});
- test('Reject MembershipRequest Button works properly', async () => {
+ it('Accept MembershipRequest handles error', async () => {
+ const props: {
+ request: InterfaceRequestsListItem;
+ index: number;
+ resetAndRefetch: () => void;
+ } = {
+ request: {
+ _id: '1',
+ user: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'john@example.com',
+ },
+ },
+ index: 1,
+ resetAndRefetch: resetAndRefetchMock,
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByTestId('acceptMembershipRequestBtn1'));
+ });
+
+ it('Reject MembershipRequest Button works properly', async () => {
const props: {
request: InterfaceRequestsListItem;
index: number;
@@ -144,4 +177,36 @@ describe('Testing User Table Item', () => {
await wait();
userEvent.click(screen.getByTestId('rejectMembershipRequestBtn123'));
});
+
+ it('Reject MembershipRequest handles error', async () => {
+ const props: {
+ request: InterfaceRequestsListItem;
+ index: number;
+ resetAndRefetch: () => void;
+ } = {
+ request: {
+ _id: '1',
+ user: {
+ firstName: 'John',
+ lastName: 'Doe',
+ email: 'john@example.com',
+ },
+ },
+ index: 1,
+ resetAndRefetch: resetAndRefetchMock,
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+ userEvent.click(screen.getByTestId('rejectMembershipRequestBtn1'));
+ });
});
diff --git a/src/components/RequestsTableItem/RequestsTableItemMocks.ts b/src/components/RequestsTableItem/RequestsTableItemMocks.ts
index 22ea245d3a..22bd61d0ac 100644
--- a/src/components/RequestsTableItem/RequestsTableItemMocks.ts
+++ b/src/components/RequestsTableItem/RequestsTableItemMocks.ts
@@ -8,13 +8,13 @@ export const MOCKS = [
request: {
query: ACCEPT_ORGANIZATION_REQUEST_MUTATION,
variables: {
- id: '1',
+ id: '123',
},
},
result: {
data: {
acceptMembershipRequest: {
- _id: '1',
+ _id: '123',
},
},
},
@@ -23,13 +23,13 @@ export const MOCKS = [
request: {
query: REJECT_ORGANIZATION_REQUEST_MUTATION,
variables: {
- id: '1',
+ id: '123',
},
},
result: {
data: {
rejectMembershipRequest: {
- _id: '1',
+ _id: '123',
},
},
},
diff --git a/src/components/SuperAdminScreen/SuperAdminScreen.test.tsx b/src/components/SuperAdminScreen/SuperAdminScreen.spec.tsx
similarity index 97%
rename from src/components/SuperAdminScreen/SuperAdminScreen.test.tsx
rename to src/components/SuperAdminScreen/SuperAdminScreen.spec.tsx
index 84b740ab12..53804a54db 100644
--- a/src/components/SuperAdminScreen/SuperAdminScreen.test.tsx
+++ b/src/components/SuperAdminScreen/SuperAdminScreen.spec.tsx
@@ -1,13 +1,13 @@
import React from 'react';
import { MockedProvider } from '@apollo/react-testing';
import { fireEvent, render, screen } from '@testing-library/react';
-import 'jest-location-mock';
import { I18nextProvider } from 'react-i18next';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { store } from 'state/store';
import i18nForTest from 'utils/i18nForTest';
import SuperAdminScreen from './SuperAdminScreen';
+import { describe, test, expect } from 'vitest';
const resizeWindow = (width: number): void => {
window.innerWidth = width;
diff --git a/src/components/TableLoader/TableLoader.test.tsx b/src/components/TableLoader/TableLoader.test.tsx
index e8400c84ef..a7d334a1c7 100644
--- a/src/components/TableLoader/TableLoader.test.tsx
+++ b/src/components/TableLoader/TableLoader.test.tsx
@@ -73,6 +73,6 @@ describe('Testing Loader component', () => {
,
);
- }).toThrowError();
+ }).toThrow();
});
});
diff --git a/src/components/TagActions/TagActions.module.css b/src/components/TagActions/TagActions.module.css
new file mode 100644
index 0000000000..079dffea65
--- /dev/null
+++ b/src/components/TagActions/TagActions.module.css
@@ -0,0 +1,42 @@
+.errorContainer {
+ min-height: 100vh;
+}
+
+.errorMessage {
+ margin-top: 25%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.errorIcon {
+ transform: scale(1.5);
+ color: var(--bs-danger);
+ margin-bottom: 1rem;
+}
+
+.scrollContainer {
+ height: 100px;
+ overflow-y: auto;
+}
+
+.tagBadge {
+ display: flex;
+ align-items: center;
+ padding: 5px 10px;
+ border-radius: 12px;
+ box-shadow: 0 1px 3px var(--bs-gray-400);
+ max-width: calc(100% - 2rem);
+}
+
+.removeFilterIcon {
+ cursor: pointer;
+}
+
+.loadingDiv {
+ min-height: 300px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/src/components/TagActions/TagActions.test.tsx b/src/components/TagActions/TagActions.test.tsx
new file mode 100644
index 0000000000..d27f177ebe
--- /dev/null
+++ b/src/components/TagActions/TagActions.test.tsx
@@ -0,0 +1,400 @@
+import React from 'react';
+import { MockedProvider } from '@apollo/react-testing';
+import type { RenderResult } from '@testing-library/react';
+import {
+ render,
+ screen,
+ fireEvent,
+ cleanup,
+ waitFor,
+ act,
+} from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { MemoryRouter, Route, Routes } from 'react-router-dom';
+import 'jest-location-mock';
+import { I18nextProvider } from 'react-i18next';
+
+import { store } from 'state/store';
+import userEvent from '@testing-library/user-event';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { toast } from 'react-toastify';
+import type { ApolloLink } from '@apollo/client';
+import type { InterfaceTagActionsProps } from './TagActions';
+import TagActions from './TagActions';
+import i18n from 'utils/i18nForTest';
+import {
+ MOCKS,
+ MOCKS_ERROR_ORGANIZATION_TAGS_QUERY,
+ MOCKS_ERROR_SUBTAGS_QUERY,
+} from './TagActionsMocks';
+import type { TFunction } from 'i18next';
+
+const link = new StaticMockLink(MOCKS, true);
+const link2 = new StaticMockLink(MOCKS_ERROR_ORGANIZATION_TAGS_QUERY, true);
+const link3 = new StaticMockLink(MOCKS_ERROR_SUBTAGS_QUERY, true);
+
+async function wait(ms = 500): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+jest.mock('react-toastify', () => ({
+ toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+}));
+
+const translations = {
+ ...JSON.parse(
+ JSON.stringify(i18n.getDataByLanguage('en')?.translation.manageTag ?? {}),
+ ),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.common ?? {})),
+ ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})),
+};
+
+const props: InterfaceTagActionsProps[] = [
+ {
+ tagActionsModalIsOpen: true,
+ hideTagActionsModal: () => {},
+ tagActionType: 'assignToTags',
+ t: ((key: string) => translations[key]) as TFunction<
+ 'translation',
+ 'manageTag'
+ >,
+ tCommon: ((key: string) => translations[key]) as TFunction<
+ 'common',
+ undefined
+ >,
+ },
+ {
+ tagActionsModalIsOpen: true,
+ hideTagActionsModal: () => {},
+ tagActionType: 'removeFromTags',
+ t: ((key: string) => translations[key]) as TFunction<
+ 'translation',
+ 'manageTag'
+ >,
+ tCommon: ((key: string) => translations[key]) as TFunction<
+ 'common',
+ undefined
+ >,
+ },
+];
+
+const renderTagActionsModal = (
+ props: InterfaceTagActionsProps,
+ link: ApolloLink,
+): RenderResult => {
+ return render(
+
+
+
+
+
+ }
+ />
+
+
+
+
+ ,
+ );
+};
+
+describe('Organisation Tags Page', () => {
+ beforeEach(() => {
+ jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useParams: () => ({ orgId: 'orgId' }),
+ }));
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('Component loads correctly and opens assignToTags modal', async () => {
+ const { getByText } = renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(getByText(translations.assign)).toBeInTheDocument();
+ });
+ });
+
+ test('Component loads correctly and opens removeFromTags modal', async () => {
+ const { getByText } = renderTagActionsModal(props[1], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(getByText(translations.remove)).toBeInTheDocument();
+ });
+ });
+
+ test('Component calls hideTagActionsModal when modal is closed', async () => {
+ const hideTagActionsModalMock = jest.fn();
+
+ const props2: InterfaceTagActionsProps = {
+ tagActionsModalIsOpen: true,
+ hideTagActionsModal: hideTagActionsModalMock,
+ tagActionType: 'assignToTags',
+ t: ((key: string) => translations[key]) as TFunction<
+ 'translation',
+ 'manageTag'
+ >,
+ tCommon: ((key: string) => translations[key]) as TFunction<
+ 'common',
+ undefined
+ >,
+ };
+
+ renderTagActionsModal(props2, link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('closeTagActionsModalBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('closeTagActionsModalBtn'));
+
+ await waitFor(() => {
+ expect(hideTagActionsModalMock).toHaveBeenCalled();
+ });
+ });
+
+ test('Renders error component when when query is unsuccessful', async () => {
+ const { queryByText } = renderTagActionsModal(props[0], link2);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(queryByText(translations.assign)).not.toBeInTheDocument();
+ });
+ });
+
+ test('Renders error component when when subTags query is unsuccessful', async () => {
+ const { getByText } = renderTagActionsModal(props[0], link3);
+
+ await wait();
+
+ // expand tag 1 to list its subtags
+ await waitFor(() => {
+ expect(screen.getByTestId('expandSubTags1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('expandSubTags1'));
+
+ await waitFor(() => {
+ expect(
+ getByText(translations.errorOccurredWhileLoadingSubTags),
+ ).toBeInTheDocument();
+ });
+ });
+
+ test('searchs for tags where the name matches the provided search input', async () => {
+ renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getByPlaceholderText(translations.searchByName),
+ ).toBeInTheDocument();
+ });
+ const input = screen.getByPlaceholderText(translations.searchByName);
+ fireEvent.change(input, { target: { value: 'searchUserTag' } });
+
+ // should render the two searched tags from the mock data
+ // where name starts with "searchUserTag"
+ await waitFor(() => {
+ const tags = screen.getAllByTestId('orgUserTag');
+ expect(tags.length).toEqual(2);
+ });
+ });
+
+ test('Renders more members with infinite scroll', async () => {
+ const { getByText } = renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(getByText(translations.assign)).toBeInTheDocument();
+ });
+
+ // Find the infinite scroll div by test ID or another selector
+ const scrollableDiv = screen.getByTestId('scrollableDiv');
+
+ const initialTagsDataLength = screen.getAllByTestId('orgUserTag').length;
+
+ // Set scroll position to the bottom
+ fireEvent.scroll(scrollableDiv, {
+ target: { scrollY: scrollableDiv.scrollHeight },
+ });
+
+ await waitFor(() => {
+ const finalTagsDataLength = screen.getAllByTestId('orgUserTag').length;
+ expect(finalTagsDataLength).toBeGreaterThan(initialTagsDataLength);
+
+ expect(getByText(translations.assign)).toBeInTheDocument();
+ });
+ });
+
+ test('Selects and deselects tags', async () => {
+ renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag1'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag2'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag1'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('clearSelectedTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('clearSelectedTag2'));
+ });
+
+ test('fetches and lists the child tags and then selects and deselects them', async () => {
+ renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ // expand tag 1 to list its subtags
+ await waitFor(() => {
+ expect(screen.getByTestId('expandSubTags1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('expandSubTags1'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('subTagsScrollableDiv1')).toBeInTheDocument();
+ });
+ // Find the infinite scroll div for subtags by test ID or another selector
+ const subTagsScrollableDiv1 = screen.getByTestId('subTagsScrollableDiv1');
+
+ const initialTagsDataLength =
+ screen.getAllByTestId('orgUserSubTags').length;
+
+ // Set scroll position to the bottom
+ fireEvent.scroll(subTagsScrollableDiv1, {
+ target: { scrollY: subTagsScrollableDiv1.scrollHeight },
+ });
+
+ await waitFor(() => {
+ const finalTagsDataLength =
+ screen.getAllByTestId('orgUserSubTags').length;
+ expect(finalTagsDataLength).toBeGreaterThan(initialTagsDataLength);
+ });
+
+ // select subtags 1 & 2
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTagsubTag1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTagsubTag1'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTagsubTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTagsubTag2'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag1'));
+
+ // deselect subtags 1 & 2
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTagsubTag1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTagsubTag1'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTagsubTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTagsubTag2'));
+
+ // hide subtags of tag 1
+ await waitFor(() => {
+ expect(screen.getByTestId('expandSubTags1')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('expandSubTags1'));
+ });
+
+ test('Toasts error when no tag is selected while assigning', async () => {
+ renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('tagActionSubmitBtn')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('tagActionSubmitBtn'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalledWith(translations.noTagSelected);
+ });
+ });
+
+ test('Successfully assigns to tags', async () => {
+ renderTagActionsModal(props[0], link);
+
+ await wait();
+
+ // select userTags 2 & 3 and assign them
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag2'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag3')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag3'));
+
+ userEvent.click(screen.getByTestId('tagActionSubmitBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.successfullyAssignedToTags,
+ );
+ });
+ });
+
+ test('Successfully removes from tags', async () => {
+ renderTagActionsModal(props[1], link);
+
+ await wait();
+
+ // select userTag 2 and remove people from it
+ await waitFor(() => {
+ expect(screen.getByTestId('checkTag2')).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('checkTag2'));
+
+ userEvent.click(screen.getByTestId('tagActionSubmitBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toHaveBeenCalledWith(
+ translations.successfullyRemovedFromTags,
+ );
+ });
+ });
+});
diff --git a/src/components/TagActions/TagActions.tsx b/src/components/TagActions/TagActions.tsx
new file mode 100644
index 0000000000..083341372f
--- /dev/null
+++ b/src/components/TagActions/TagActions.tsx
@@ -0,0 +1,407 @@
+import { useMutation, useQuery } from '@apollo/client';
+import type { FormEvent } from 'react';
+import React, { useEffect, useState } from 'react';
+import { Modal, Form, Button } from 'react-bootstrap';
+import { useParams } from 'react-router-dom';
+import type {
+ InterfaceQueryOrganizationUserTags,
+ InterfaceTagData,
+} from 'utils/interfaces';
+import styles from './TagActions.module.css';
+import { ORGANIZATION_USER_TAGS_LIST } from 'GraphQl/Queries/OrganizationQueries';
+import {
+ ASSIGN_TO_TAGS,
+ REMOVE_FROM_TAGS,
+} from 'GraphQl/Mutations/TagMutations';
+import { toast } from 'react-toastify';
+import type {
+ InterfaceOrganizationTagsQuery,
+ TagActionType,
+} from 'utils/organizationTagsUtils';
+import { TAGS_QUERY_DATA_CHUNK_SIZE } from 'utils/organizationTagsUtils';
+import InfiniteScroll from 'react-infinite-scroll-component';
+import { WarningAmberRounded } from '@mui/icons-material';
+import TagNode from './TagNode';
+import InfiniteScrollLoader from 'components/InfiniteScrollLoader/InfiniteScrollLoader';
+import type { TFunction } from 'i18next';
+
+interface InterfaceUserTagsAncestorData {
+ _id: string;
+ name: string;
+}
+
+/**
+ * Props for the `AssignToTags` component.
+ */
+export interface InterfaceTagActionsProps {
+ tagActionsModalIsOpen: boolean;
+ hideTagActionsModal: () => void;
+ tagActionType: TagActionType;
+ t: TFunction<'translation', 'manageTag'>;
+ tCommon: TFunction<'common', undefined>;
+}
+
+const TagActions: React.FC = ({
+ tagActionsModalIsOpen,
+ hideTagActionsModal,
+ tagActionType,
+ t,
+ tCommon,
+}) => {
+ const { orgId, tagId: currentTagId } = useParams();
+
+ const [tagSearchName, setTagSearchName] = useState('');
+
+ const {
+ data: orgUserTagsData,
+ loading: orgUserTagsLoading,
+ error: orgUserTagsError,
+ fetchMore: orgUserTagsFetchMore,
+ }: InterfaceOrganizationTagsQuery = useQuery(ORGANIZATION_USER_TAGS_LIST, {
+ variables: {
+ id: orgId,
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: { name: { starts_with: tagSearchName } },
+ },
+ skip: !tagActionsModalIsOpen,
+ });
+
+ const loadMoreUserTags = (): void => {
+ orgUserTagsFetchMore({
+ variables: {
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ after: orgUserTagsData?.organizations[0].userTags.pageInfo.endCursor,
+ },
+ updateQuery: (
+ prevResult: { organizations: InterfaceQueryOrganizationUserTags[] },
+ {
+ fetchMoreResult,
+ }: {
+ fetchMoreResult?: {
+ organizations: InterfaceQueryOrganizationUserTags[];
+ };
+ },
+ ) => {
+ if (!fetchMoreResult) /* istanbul ignore next */ return prevResult;
+
+ return {
+ organizations: [
+ {
+ ...prevResult.organizations[0],
+ userTags: {
+ ...prevResult.organizations[0].userTags,
+ edges: [
+ ...prevResult.organizations[0].userTags.edges,
+ ...fetchMoreResult.organizations[0].userTags.edges,
+ ],
+ pageInfo: fetchMoreResult.organizations[0].userTags.pageInfo,
+ },
+ },
+ ],
+ };
+ },
+ });
+ };
+
+ const userTagsList =
+ orgUserTagsData?.organizations[0]?.userTags.edges.map(
+ (edge) => edge.node,
+ ) ?? /* istanbul ignore next */ [];
+
+ // tags that we have selected to assigned
+ const [selectedTags, setSelectedTags] = useState([]);
+
+ // tags that we have checked, it is there to differentiate between the selected tags and all the checked tags
+ // i.e. selected tags would only be the ones we select, but checked tags will also include the selected tag's ancestors
+ const [checkedTags, setCheckedTags] = useState>(new Set());
+
+ // next 3 states are there to keep track of the ancestor tags of the the tags that we have selected
+ // i.e. when we check a tag, all of it's ancestor tags will be checked too
+ // indicating that the users will be assigned all of the ancestor tags as well
+ const [addAncestorTagsData, setAddAncestorTagsData] = useState<
+ Set
+ >(new Set());
+ const [removeAncestorTagsData, setRemoveAncestorTagsData] = useState<
+ Set
+ >(new Set());
+ const [ancestorTagsDataMap, setAncestorTagsDataMap] = useState(new Map());
+
+ useEffect(() => {
+ const newCheckedTags = new Set(checkedTags);
+ const newAncestorTagsDataMap = new Map(ancestorTagsDataMap);
+ /* istanbul ignore next */
+ addAncestorTagsData.forEach(
+ (ancestorTag: InterfaceUserTagsAncestorData) => {
+ const prevAncestorTagValue = ancestorTagsDataMap.get(ancestorTag._id);
+ newAncestorTagsDataMap.set(
+ ancestorTag._id,
+ prevAncestorTagValue ? prevAncestorTagValue + 1 : 1,
+ );
+ newCheckedTags.add(ancestorTag._id);
+ },
+ );
+
+ setCheckedTags(newCheckedTags);
+ setAncestorTagsDataMap(newAncestorTagsDataMap);
+ }, [addAncestorTagsData]);
+
+ useEffect(() => {
+ const newCheckedTags = new Set(checkedTags);
+ const newAncestorTagsDataMap = new Map(ancestorTagsDataMap);
+ /* istanbul ignore next */
+ removeAncestorTagsData.forEach(
+ (ancestorTag: InterfaceUserTagsAncestorData) => {
+ const prevAncestorTagValue = ancestorTagsDataMap.get(ancestorTag._id);
+ if (prevAncestorTagValue === 1) {
+ newCheckedTags.delete(ancestorTag._id);
+ newAncestorTagsDataMap.delete(ancestorTag._id);
+ } else {
+ newAncestorTagsDataMap.set(ancestorTag._id, prevAncestorTagValue - 1);
+ }
+ },
+ );
+
+ setCheckedTags(newCheckedTags);
+ setAncestorTagsDataMap(newAncestorTagsDataMap);
+ }, [removeAncestorTagsData]);
+
+ const selectTag = (tag: InterfaceTagData): void => {
+ const newCheckedTags = new Set(checkedTags);
+
+ setSelectedTags((selectedTags) => [...selectedTags, tag]);
+ newCheckedTags.add(tag._id);
+
+ setAddAncestorTagsData(new Set(tag.ancestorTags));
+
+ setCheckedTags(newCheckedTags);
+ };
+
+ const deSelectTag = (tag: InterfaceTagData): void => {
+ if (!selectedTags.some((selectedTag) => selectedTag._id === tag._id)) {
+ /* istanbul ignore next */
+ return;
+ }
+
+ const newCheckedTags = new Set(checkedTags);
+
+ setSelectedTags(
+ selectedTags.filter((selectedTag) => selectedTag._id !== tag._id),
+ );
+ newCheckedTags.delete(tag._id);
+
+ setRemoveAncestorTagsData(new Set(tag.ancestorTags));
+
+ setCheckedTags(newCheckedTags);
+ };
+
+ const toggleTagSelection = (
+ tag: InterfaceTagData,
+ isSelected: boolean,
+ ): void => {
+ if (isSelected) {
+ selectTag(tag);
+ } else {
+ deSelectTag(tag);
+ }
+ };
+
+ const [assignToTags] = useMutation(ASSIGN_TO_TAGS);
+ const [removeFromTags] = useMutation(REMOVE_FROM_TAGS);
+
+ const handleTagAction = async (
+ e: FormEvent,
+ ): Promise => {
+ e.preventDefault();
+
+ if (!selectedTags.length) {
+ toast.error(t('noTagSelected'));
+ return;
+ }
+
+ const mutationObject = {
+ variables: {
+ currentTagId,
+ selectedTagIds: selectedTags.map((selectedTag) => selectedTag._id),
+ },
+ };
+
+ try {
+ const { data } =
+ tagActionType === 'assignToTags'
+ ? await assignToTags(mutationObject)
+ : await removeFromTags(mutationObject);
+
+ if (data) {
+ if (tagActionType === 'assignToTags') {
+ toast.success(t('successfullyAssignedToTags'));
+ } else {
+ toast.success(t('successfullyRemovedFromTags'));
+ }
+ hideTagActionsModal();
+ }
+ } catch (error: unknown) {
+ /* istanbul ignore next */
+ if (error instanceof Error) {
+ toast.error(error.message);
+ }
+ }
+ };
+
+ if (orgUserTagsError) {
+ return (
+
+
+
+
+ {t('errorOccurredWhileLoadingOrganizationUserTags')}
+
+
+
+ );
+ }
+
+ return (
+ <>
+
+
+
+ {tagActionType === 'assignToTags'
+ ? t('assignToTags')
+ : t('removeFromTags')}
+
+
+
+
+ >
+ );
+};
+
+export default TagActions;
diff --git a/src/components/TagActions/TagActionsMocks.ts b/src/components/TagActions/TagActionsMocks.ts
new file mode 100644
index 0000000000..f22458fa53
--- /dev/null
+++ b/src/components/TagActions/TagActionsMocks.ts
@@ -0,0 +1,706 @@
+import {
+ ASSIGN_TO_TAGS,
+ REMOVE_FROM_TAGS,
+} from 'GraphQl/Mutations/TagMutations';
+import { ORGANIZATION_USER_TAGS_LIST } from 'GraphQl/Queries/OrganizationQueries';
+import { USER_TAG_SUB_TAGS } from 'GraphQl/Queries/userTagQueries';
+import { TAGS_QUERY_DATA_CHUNK_SIZE } from 'utils/organizationTagsUtils';
+
+export const MOCKS = [
+ {
+ request: {
+ query: ORGANIZATION_USER_TAGS_LIST,
+ variables: {
+ id: '123',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: { name: { starts_with: '' } },
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ userTags: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ name: 'userTag 1',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 11,
+ },
+ ancestorTags: [],
+ },
+ cursor: '1',
+ },
+ {
+ node: {
+ _id: '2',
+ name: 'userTag 2',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [],
+ },
+ cursor: '2',
+ },
+ {
+ node: {
+ _id: '3',
+ name: 'userTag 3',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 0,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [],
+ },
+ cursor: '3',
+ },
+ {
+ node: {
+ _id: '4',
+ name: 'userTag 4',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 0,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [],
+ },
+ cursor: '4',
+ },
+ {
+ node: {
+ _id: '5',
+ name: 'userTag 5',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [],
+ },
+ cursor: '5',
+ },
+ {
+ node: {
+ _id: '6',
+ name: 'userTag 6',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 6,
+ },
+ childTags: {
+ totalCount: 6,
+ },
+ ancestorTags: [],
+ },
+ cursor: '6',
+ },
+ {
+ node: {
+ _id: '7',
+ name: 'userTag 7',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 7,
+ },
+ childTags: {
+ totalCount: 7,
+ },
+ ancestorTags: [],
+ },
+ cursor: '7',
+ },
+ {
+ node: {
+ _id: '8',
+ name: 'userTag 8',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 8,
+ },
+ childTags: {
+ totalCount: 8,
+ },
+ ancestorTags: [],
+ },
+ cursor: '8',
+ },
+ {
+ node: {
+ _id: '9',
+ name: 'userTag 9',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 9,
+ },
+ childTags: {
+ totalCount: 9,
+ },
+ ancestorTags: [],
+ },
+ cursor: '9',
+ },
+ {
+ node: {
+ _id: '10',
+ name: 'userTag 10',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 10,
+ },
+ childTags: {
+ totalCount: 10,
+ },
+ ancestorTags: [],
+ },
+ cursor: '10',
+ },
+ ],
+ pageInfo: {
+ startCursor: '1',
+ endCursor: '10',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ },
+ totalCount: 12,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: ORGANIZATION_USER_TAGS_LIST,
+ variables: {
+ id: '123',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ after: '10',
+ where: { name: { starts_with: '' } },
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ userTags: {
+ edges: [
+ {
+ node: {
+ _id: '11',
+ name: 'userTag 11',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [],
+ },
+ cursor: '11',
+ },
+ {
+ node: {
+ _id: '12',
+ name: 'userTag 12',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [],
+ },
+ cursor: '12',
+ },
+ ],
+ pageInfo: {
+ startCursor: '11',
+ endCursor: '12',
+ hasNextPage: false,
+ hasPreviousPage: true,
+ },
+ totalCount: 12,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: ORGANIZATION_USER_TAGS_LIST,
+ variables: {
+ id: '123',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: { name: { starts_with: 'searchUserTag' } },
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ userTags: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ name: 'searchUserTag 1',
+ parentTag: {
+ _id: '1',
+ },
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: '1',
+ },
+ {
+ node: {
+ _id: '2',
+ name: 'searchUserTag 2',
+ parentTag: {
+ _id: '1',
+ },
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: '2',
+ },
+ ],
+ pageInfo: {
+ startCursor: '1',
+ endCursor: '2',
+ hasNextPage: false,
+ hasPreviousPage: false,
+ },
+ totalCount: 2,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: USER_TAG_SUB_TAGS,
+ variables: {
+ id: '1',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ },
+ },
+ result: {
+ data: {
+ getChildTags: {
+ name: 'userTag 1',
+ childTags: {
+ edges: [
+ {
+ node: {
+ _id: 'subTag1',
+ name: 'subTag 1',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag1',
+ },
+ {
+ node: {
+ _id: 'subTag2',
+ name: 'subTag 2',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag2',
+ },
+ {
+ node: {
+ _id: 'subTag3',
+ name: 'subTag 3',
+ usersAssignedTo: {
+ totalCount: 0,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag3',
+ },
+ {
+ node: {
+ _id: 'subTag4',
+ name: 'subTag 4',
+ usersAssignedTo: {
+ totalCount: 0,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag4',
+ },
+ {
+ node: {
+ _id: 'subTag5',
+ name: 'subTag 5',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag5',
+ },
+ {
+ node: {
+ _id: 'subTag6',
+ name: 'subTag 6',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag6',
+ },
+ {
+ node: {
+ _id: 'subTag7',
+ name: 'subTag 7',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag7',
+ },
+ {
+ node: {
+ _id: 'subTag8',
+ name: 'subTag 8',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag8',
+ },
+ {
+ node: {
+ _id: 'subTag9',
+ name: 'subTag 9',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag9',
+ },
+ {
+ node: {
+ _id: 'subTag10',
+ name: 'subTag 10',
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 5,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag10',
+ },
+ ],
+ pageInfo: {
+ startCursor: 'subTag1',
+ endCursor: 'subTag10',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ },
+ totalCount: 11,
+ },
+ ancestorTags: [],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: USER_TAG_SUB_TAGS,
+ variables: {
+ id: '1',
+ after: 'subTag10',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ },
+ },
+ result: {
+ data: {
+ getChildTags: {
+ name: 'userTag 1',
+ childTags: {
+ edges: [
+ {
+ node: {
+ _id: 'subTag11',
+ name: 'subTag 11',
+ usersAssignedTo: {
+ totalCount: 0,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [
+ {
+ _id: '1',
+ name: 'userTag 1',
+ },
+ ],
+ },
+ cursor: 'subTag11',
+ },
+ ],
+ pageInfo: {
+ startCursor: 'subTag11',
+ endCursor: 'subTag11',
+ hasNextPage: false,
+ hasPreviousPage: true,
+ },
+ totalCount: 11,
+ },
+ ancestorTags: [],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: ASSIGN_TO_TAGS,
+ variables: {
+ currentTagId: '1',
+ selectedTagIds: ['2', '3'],
+ },
+ },
+ result: {
+ data: {
+ assignToUserTags: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: REMOVE_FROM_TAGS,
+ variables: {
+ currentTagId: '1',
+ selectedTagIds: ['2'],
+ },
+ },
+ result: {
+ data: {
+ removeFromUserTags: {
+ _id: '1',
+ },
+ },
+ },
+ },
+];
+
+export const MOCKS_ERROR_ORGANIZATION_TAGS_QUERY = [
+ {
+ request: {
+ query: ORGANIZATION_USER_TAGS_LIST,
+ variables: {
+ id: '123',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: { name: { starts_with: '' } },
+ },
+ },
+ error: new Error('Mock Graphql Error for organization root tags query'),
+ },
+];
+
+export const MOCKS_ERROR_SUBTAGS_QUERY = [
+ {
+ request: {
+ query: ORGANIZATION_USER_TAGS_LIST,
+ variables: {
+ id: '123',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ where: { name: { starts_with: '' } },
+ },
+ },
+ result: {
+ data: {
+ organizations: [
+ {
+ userTags: {
+ edges: [
+ {
+ node: {
+ _id: '1',
+ name: 'userTag 1',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 11,
+ },
+ ancestorTags: [],
+ },
+ cursor: '1',
+ },
+ {
+ node: {
+ _id: '2',
+ name: 'userTag 2',
+ parentTag: null,
+ usersAssignedTo: {
+ totalCount: 5,
+ },
+ childTags: {
+ totalCount: 0,
+ },
+ ancestorTags: [],
+ },
+ cursor: '2',
+ },
+ ],
+ pageInfo: {
+ startCursor: '1',
+ endCursor: '2',
+ hasNextPage: false,
+ hasPreviousPage: false,
+ },
+ totalCount: 2,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: USER_TAG_SUB_TAGS,
+ variables: {
+ id: '1',
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ },
+ },
+ error: new Error('Mock Graphql Error for subTags query'),
+ },
+];
diff --git a/src/components/TagActions/TagNode.tsx b/src/components/TagActions/TagNode.tsx
new file mode 100644
index 0000000000..4a085ecaf9
--- /dev/null
+++ b/src/components/TagActions/TagNode.tsx
@@ -0,0 +1,199 @@
+import { useQuery } from '@apollo/client';
+import { USER_TAG_SUB_TAGS } from 'GraphQl/Queries/userTagQueries';
+import React, { useState } from 'react';
+import type {
+ InterfaceQueryUserTagChildTags,
+ InterfaceTagData,
+} from 'utils/interfaces';
+import type { InterfaceOrganizationSubTagsQuery } from 'utils/organizationTagsUtils';
+import { TAGS_QUERY_DATA_CHUNK_SIZE } from 'utils/organizationTagsUtils';
+import styles from './TagActions.module.css';
+import InfiniteScroll from 'react-infinite-scroll-component';
+import InfiniteScrollLoader from 'components/InfiniteScrollLoader/InfiniteScrollLoader';
+import { WarningAmberRounded } from '@mui/icons-material';
+import type { TFunction } from 'i18next';
+
+/**
+ * Props for the `TagNode` component.
+ */
+interface InterfaceTagNodeProps {
+ tag: InterfaceTagData;
+ checkedTags: Set;
+ toggleTagSelection: (tag: InterfaceTagData, isSelected: boolean) => void;
+ t: TFunction<'translation', 'manageTag'>;
+}
+
+/**
+ * Renders the Tags which can be expanded to list subtags.
+ */
+const TagNode: React.FC = ({
+ tag,
+ checkedTags,
+ toggleTagSelection,
+ t,
+}) => {
+ const [expanded, setExpanded] = useState(false);
+
+ const {
+ data: subTagsData,
+ loading: subTagsLoading,
+ error: subTagsError,
+ fetchMore: fetchMoreSubTags,
+ }: InterfaceOrganizationSubTagsQuery = useQuery(USER_TAG_SUB_TAGS, {
+ variables: {
+ id: tag._id,
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ },
+ skip: !expanded,
+ });
+
+ const loadMoreSubTags = (): void => {
+ fetchMoreSubTags({
+ variables: {
+ first: TAGS_QUERY_DATA_CHUNK_SIZE,
+ after: subTagsData?.getChildTags.childTags.pageInfo.endCursor,
+ },
+ updateQuery: (
+ prevResult: { getChildTags: InterfaceQueryUserTagChildTags },
+ {
+ fetchMoreResult,
+ }: {
+ fetchMoreResult?: { getChildTags: InterfaceQueryUserTagChildTags };
+ },
+ ) => {
+ if (!fetchMoreResult) /* istanbul ignore next */ return prevResult;
+
+ return {
+ getChildTags: {
+ ...fetchMoreResult.getChildTags,
+ childTags: {
+ ...fetchMoreResult.getChildTags.childTags,
+ edges: [
+ ...prevResult.getChildTags.childTags.edges,
+ ...fetchMoreResult.getChildTags.childTags.edges,
+ ],
+ },
+ },
+ };
+ },
+ });
+ };
+
+ if (subTagsError) {
+ return (
+
+
+
+
+ {t('errorOccurredWhileLoadingSubTags')}
+
+
+
+ );
+ }
+
+ const subTagsList =
+ subTagsData?.getChildTags.childTags.edges.map((edge) => edge.node) ??
+ /* istanbul ignore next */ [];
+
+ const handleTagClick = (): void => {
+ setExpanded(!expanded);
+ };
+
+ const handleCheckboxChange = (
+ e: React.ChangeEvent,
+ ): void => {
+ toggleTagSelection(tag, e.target.checked);
+ };
+
+ return (
+
+ );
+};
+
+export default TagNode;
diff --git a/src/components/UpdateSession/UpdateSession.test.tsx b/src/components/UpdateSession/UpdateSession.spec.tsx
similarity index 81%
rename from src/components/UpdateSession/UpdateSession.test.tsx
rename to src/components/UpdateSession/UpdateSession.spec.tsx
index ce0a868820..3453c990c2 100644
--- a/src/components/UpdateSession/UpdateSession.test.tsx
+++ b/src/components/UpdateSession/UpdateSession.spec.tsx
@@ -1,16 +1,7 @@
import React from 'react';
import { MockedProvider } from '@apollo/client/testing';
-import {
- render,
- screen,
- act,
- within,
- fireEvent,
- waitFor,
-} from '@testing-library/react';
+import { render, screen, act, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import 'jest-localstorage-mock';
-import 'jest-location-mock';
import { I18nextProvider } from 'react-i18next';
import { BrowserRouter } from 'react-router-dom';
import { toast } from 'react-toastify';
@@ -19,6 +10,18 @@ import i18n from 'utils/i18nForTest';
import { GET_COMMUNITY_SESSION_TIMEOUT_DATA } from 'GraphQl/Queries/Queries';
import { UPDATE_SESSION_TIMEOUT } from 'GraphQl/Mutations/mutations';
import { errorHandler } from 'utils/errorHandler';
+import { vi } from 'vitest';
+/**
+ * This file contains unit tests for the `UpdateSession` component.
+ *
+ * The tests cover:
+ * - Proper rendering of the component with different scenarios, including mock data, null values, and error states.
+ * - Handling user interactions with the slider, such as setting minimum, maximum, and intermediate values.
+ * - Ensuring callbacks (e.g., `onValueChange`) are triggered correctly based on user input.
+ * - Simulating GraphQL query and mutation operations using mocked data to verify correct behavior in successful and error cases.
+ * - Testing edge cases, including null community data, invalid input values, and API errors, ensuring the component handles them gracefully.
+ * - Verifying proper integration of internationalization, routing, and toast notifications for success or error messages.
+ */
const MOCKS = [
{
@@ -66,31 +69,29 @@ async function wait(ms = 100): Promise {
});
}
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- success: jest.fn(),
- warn: jest.fn(),
- error: jest.fn(),
+ success: vi.fn(),
+ warn: vi.fn(),
+ error: vi.fn(),
},
}));
-jest.mock('utils/errorHandler', () => ({
- errorHandler: jest.fn(),
+vi.mock('utils/errorHandler', () => ({
+ errorHandler: vi.fn(),
}));
describe('Testing UpdateTimeout Component', () => {
- let consoleWarnSpy: jest.SpyInstance;
-
beforeEach(() => {
- consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
+ vi.spyOn(console, 'warn').mockImplementation(() => {});
});
afterEach(() => {
- jest.clearAllMocks();
+ vi.clearAllMocks();
});
- test('Should handle minimum slider value correctly', async () => {
- const mockOnValueChange = jest.fn();
+ it('Should handle minimum slider value correctly', async () => {
+ const mockOnValueChange = vi.fn();
render(
@@ -109,8 +110,8 @@ describe('Testing UpdateTimeout Component', () => {
expect(mockOnValueChange).toHaveBeenCalledWith(15); // Adjust based on slider min value
});
- test('Should handle maximum slider value correctly', async () => {
- const mockOnValueChange = jest.fn();
+ it('Should handle maximum slider value correctly', async () => {
+ const mockOnValueChange = vi.fn();
render(
@@ -129,8 +130,8 @@ describe('Testing UpdateTimeout Component', () => {
expect(mockOnValueChange).toHaveBeenCalledWith(60); // Adjust based on slider max value
});
- test('Should not update value if an invalid value is passed', async () => {
- const mockOnValueChange = jest.fn();
+ it('Should not update value if an invalid value is passed', async () => {
+ const mockOnValueChange = vi.fn();
render(
@@ -150,8 +151,8 @@ describe('Testing UpdateTimeout Component', () => {
expect(mockOnValueChange).not.toHaveBeenCalled();
});
- test('Should update slider value on user interaction', async () => {
- const mockOnValueChange = jest.fn();
+ it('Should update slider value on user interaction', async () => {
+ const mockOnValueChange = vi.fn();
render(
@@ -169,7 +170,7 @@ describe('Testing UpdateTimeout Component', () => {
expect(mockOnValueChange).toHaveBeenCalledWith(expect.any(Number)); // Adjust as needed
});
- test('Components should render properly', async () => {
+ it('Components should render properly', async () => {
render(
@@ -203,7 +204,7 @@ describe('Testing UpdateTimeout Component', () => {
expect(screen.getByRole('button', { name: /Update/i })).toBeInTheDocument();
});
- test('Should update session timeout', async () => {
+ it('Should update session timeout', async () => {
render(
@@ -228,7 +229,7 @@ describe('Testing UpdateTimeout Component', () => {
);
});
- test('Should handle query errors', async () => {
+ it('Should handle query errors', async () => {
const errorMocks = [
{
request: {
@@ -253,7 +254,7 @@ describe('Testing UpdateTimeout Component', () => {
expect(errorHandler).toHaveBeenCalled();
});
- test('Should handle update errors', async () => {
+ it('Should handle update errors', async () => {
const errorMocks = [
{
request: {
@@ -306,7 +307,7 @@ describe('Testing UpdateTimeout Component', () => {
expect(errorHandler).toHaveBeenCalled();
});
- test('Should handle null community object gracefully', async () => {
+ it('Should handle null community object gracefully', async () => {
render(
diff --git a/src/components/UserListCard/UserListCard.test.tsx b/src/components/UserListCard/UserListCard.spec.tsx
similarity index 77%
rename from src/components/UserListCard/UserListCard.test.tsx
rename to src/components/UserListCard/UserListCard.spec.tsx
index e2ad552507..6a3b2d42d8 100644
--- a/src/components/UserListCard/UserListCard.test.tsx
+++ b/src/components/UserListCard/UserListCard.spec.tsx
@@ -1,5 +1,5 @@
-import React, { act } from 'react';
-import { render, screen } from '@testing-library/react';
+import React from 'react';
+import { render, screen, act } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import userEvent from '@testing-library/user-event';
import { I18nextProvider } from 'react-i18next';
@@ -9,6 +9,7 @@ import { ADD_ADMIN_MUTATION } from 'GraphQl/Mutations/mutations';
import i18nForTest from 'utils/i18nForTest';
import { BrowserRouter } from 'react-router-dom';
import { StaticMockLink } from 'utils/StaticMockLink';
+import { vi, describe, it, beforeEach } from 'vitest';
const MOCKS = [
{
@@ -30,17 +31,15 @@ const MOCKS = [
const link = new StaticMockLink(MOCKS, true);
async function wait(ms = 100): Promise {
- await act(() => {
- return new Promise((resolve) => {
- setTimeout(resolve, ms);
- });
- });
+ await act(() => new Promise((resolve) => setTimeout(resolve, ms)));
}
describe('Testing User List Card', () => {
- global.alert = jest.fn();
+ beforeEach(() => {
+ vi.spyOn(global, 'alert').mockImplementation(() => {});
+ });
- test('Should render props and text elements test for the page component', async () => {
+ it('Should render props and text elements test for the page component', async () => {
const props = {
id: '456',
};
@@ -56,11 +55,10 @@ describe('Testing User List Card', () => {
);
await wait();
-
userEvent.click(screen.getByText(/Add Admin/i));
});
- test('Should render text elements when props value is not passed', async () => {
+ it('Should render text elements when props value is not passed', async () => {
const props = {
id: '456',
};
diff --git a/src/components/UserPasswordUpdate/UserPasswordUpdate.test.tsx b/src/components/UserPasswordUpdate/UserPasswordUpdate.spec.tsx
similarity index 58%
rename from src/components/UserPasswordUpdate/UserPasswordUpdate.test.tsx
rename to src/components/UserPasswordUpdate/UserPasswordUpdate.spec.tsx
index 65f5e40f76..0d704eaed8 100644
--- a/src/components/UserPasswordUpdate/UserPasswordUpdate.test.tsx
+++ b/src/components/UserPasswordUpdate/UserPasswordUpdate.spec.tsx
@@ -1,43 +1,22 @@
import React, { act } from 'react';
-import { render, screen } from '@testing-library/react';
+import { render, screen, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import userEvent from '@testing-library/user-event';
import { I18nextProvider } from 'react-i18next';
-import { UPDATE_USER_PASSWORD_MUTATION } from 'GraphQl/Mutations/mutations';
import i18nForTest from 'utils/i18nForTest';
import UserPasswordUpdate from './UserPasswordUpdate';
import { StaticMockLink } from 'utils/StaticMockLink';
import { toast as mockToast } from 'react-toastify';
+import { MOCKS } from './UserPasswordUpdateMocks';
+import { vi } from 'vitest';
-jest.mock('react-toastify', () => ({
+vi.mock('react-toastify', () => ({
toast: {
- error: jest.fn(),
- success: jest.fn(),
+ error: vi.fn(),
+ success: vi.fn(),
},
}));
-const MOCKS = [
- {
- request: {
- query: UPDATE_USER_PASSWORD_MUTATION,
- variables: {
- previousPassword: 'anshgoyal',
- newPassword: 'anshgoyalansh',
- confirmNewPassword: 'anshgoyalansh',
- },
- },
- result: {
- data: {
- users: [
- {
- _id: '1',
- },
- ],
- },
- },
- },
-];
-
const link = new StaticMockLink(MOCKS, true);
async function wait(ms = 5): Promise {
@@ -56,9 +35,9 @@ describe('Testing User Password Update', () => {
confirmNewPassword: 'ThePalisadoesFoundation',
};
- global.alert = jest.fn();
+ global.alert = vi.fn();
- test('should render props and text elements test for the page component', async () => {
+ it('should render props and text elements it for the page component', async () => {
render(
@@ -93,7 +72,7 @@ describe('Testing User Password Update', () => {
).toBeInTheDocument();
});
- test('displays an error when the password field is empty', async () => {
+ it('displays an error when the password field is empty', async () => {
render(
@@ -108,7 +87,7 @@ describe('Testing User Password Update', () => {
expect(mockToast.error).toHaveBeenCalledWith(`Password can't be empty`);
});
- test('displays an error when new and confirm password field does not match', async () => {
+ it('displays an error when new and confirm password field does not match', async () => {
render(
@@ -140,4 +119,67 @@ describe('Testing User Password Update', () => {
'New and Confirm password do not match.',
);
});
+
+ it('Successfully update old password', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Previous Password/i),
+ formData.previousPassword,
+ );
+ userEvent.type(
+ screen.getAllByPlaceholderText(/New Password/i)[0],
+ formData.newPassword,
+ );
+ userEvent.type(
+ screen.getByPlaceholderText(/Confirm New Password/i),
+ formData.confirmNewPassword,
+ );
+
+ userEvent.click(screen.getByText(/Save Changes/i));
+ expect(mockToast.success).toHaveBeenCalledWith(
+ 'Password updated Successfully',
+ );
+ });
+
+ it('wrong old password', async () => {
+ render(
+
+
+
+
+ ,
+ );
+
+ // await wait();
+
+ userEvent.type(
+ screen.getByPlaceholderText(/Previous Password/i),
+ formData.wrongPassword,
+ );
+ userEvent.type(
+ screen.getAllByPlaceholderText(/New Password/i)[0],
+ formData.newPassword,
+ );
+ userEvent.type(
+ screen.getByPlaceholderText(/Confirm New Password/i),
+ formData.confirmNewPassword,
+ );
+
+ userEvent.click(screen.getByText(/Save Changes/i));
+
+ await waitFor(() =>
+ expect(mockToast.error).toHaveBeenCalledWith(
+ 'ApolloError: Invalid previous password',
+ ),
+ );
+ });
});
diff --git a/src/components/UserPasswordUpdate/UserPasswordUpdateMocks.ts b/src/components/UserPasswordUpdate/UserPasswordUpdateMocks.ts
new file mode 100644
index 0000000000..634286f590
--- /dev/null
+++ b/src/components/UserPasswordUpdate/UserPasswordUpdateMocks.ts
@@ -0,0 +1,40 @@
+import { UPDATE_USER_PASSWORD_MUTATION } from 'GraphQl/Mutations/mutations';
+
+export const MOCKS = [
+ {
+ request: {
+ query: UPDATE_USER_PASSWORD_MUTATION,
+ variables: {
+ previousPassword: 'Palisadoes',
+ newPassword: 'ThePalisadoesFoundation',
+ confirmNewPassword: 'ThePalisadoesFoundation',
+ },
+ },
+ result: {
+ data: {
+ users: [
+ {
+ _id: '1',
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_USER_PASSWORD_MUTATION,
+ variables: {
+ previousPassword: 'This is wrong password',
+ newPassword: 'ThePalisadoesFoundation',
+ confirmNewPassword: 'ThePalisadoesFoundation',
+ },
+ },
+ result: {
+ errors: [
+ {
+ message: 'Invalid previous password',
+ },
+ ],
+ },
+ },
+];
diff --git a/src/components/UserPortal/ChatRoom/ChatRoom.module.css b/src/components/UserPortal/ChatRoom/ChatRoom.module.css
index 5fc98351cd..5f89e6122a 100644
--- a/src/components/UserPortal/ChatRoom/ChatRoom.module.css
+++ b/src/components/UserPortal/ChatRoom/ChatRoom.module.css
@@ -4,8 +4,9 @@
/* background-color: rgba(196, 255, 211, 0.3); */
}
-.backgroundWhite {
+.sendMessageInput {
background-color: white;
+ border-radius: 12px 0px 0px 12px;
}
.grey {
@@ -152,7 +153,8 @@
}
.contactImage {
- height: fit-content;
+ height: 45px !important;
+ border-radius: 100%;
}
.senderInfo {
@@ -235,6 +237,38 @@
animation-duration: 1s;
}
+.attachment {
+ padding: 10px;
+ background-color: rgb(219, 219, 219);
+ height: 200px;
+ display: flex;
+ align-items: flex-start;
+ border-radius: 10px;
+}
+
+.attachment img {
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+ border-radius: 8px;
+}
+
+.messageAttachment {
+ height: 300px;
+ width: 300px;
+ border-radius: 10px;
+ object-fit: cover;
+ margin: 10px 0px 10px 10px;
+}
+
+.addAttachmentBtn {
+ border: none;
+ outline: none;
+ margin: 0 10px;
+ background-color: white;
+ font-size: 20px;
+}
+
@keyframes test {
from {
background-color: white;
diff --git a/src/components/UserPortal/ChatRoom/ChatRoom.spec.tsx b/src/components/UserPortal/ChatRoom/ChatRoom.spec.tsx
new file mode 100644
index 0000000000..ef88baaf4c
--- /dev/null
+++ b/src/components/UserPortal/ChatRoom/ChatRoom.spec.tsx
@@ -0,0 +1,5985 @@
+import React from 'react';
+
+import {
+ act,
+ render,
+ screen,
+ fireEvent,
+ waitFor,
+} from '@testing-library/react';
+import { MockedProvider, MockSubscriptionLink } from '@apollo/react-testing';
+import { I18nextProvider } from 'react-i18next';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { store } from 'state/store';
+import i18nForTest from 'utils/i18nForTest';
+import {
+ CHATS_LIST,
+ CHAT_BY_ID,
+ GROUP_CHAT_LIST,
+ UNREAD_CHAT_LIST,
+} from 'GraphQl/Queries/PlugInQueries';
+import {
+ MARK_CHAT_MESSAGES_AS_READ,
+ MESSAGE_SENT_TO_CHAT,
+ SEND_MESSAGE_TO_CHAT,
+} from 'GraphQl/Mutations/OrganizationMutations';
+import ChatRoom from './ChatRoom';
+import { StaticMockLink } from 'utils/StaticMockLink';
+import { vi } from 'vitest';
+import useLocalStorage from 'utils/useLocalstorage';
+
+/**
+ * Unit tests for the ChatRoom component
+ *
+ * Tests cover component rendering, message functionality (sending/replying),
+ * user interactions, GraphQL integration, and provider integrations
+ * (Router, Redux, i18n) for both direct and group chats.
+ */
+
+const { setItem } = useLocalStorage();
+
+async function wait(ms = 100): Promise {
+ await act(() => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+ });
+}
+
+const MESSAGE_SENT_TO_CHAT_MOCK = [
+ {
+ request: {
+ query: MESSAGE_SENT_TO_CHAT,
+ variables: {
+ userId: null,
+ },
+ },
+ result: {
+ data: {
+ messageSentToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ chatMessageBelongsTo: {
+ _id: '1',
+ },
+ messageContent: 'Test ',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MESSAGE_SENT_TO_CHAT,
+ variables: {
+ userId: '2',
+ },
+ },
+ result: {
+ data: {
+ messageSentToChat: {
+ _id: '668ec1f1df364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ media: null,
+ chatMessageBelongsTo: {
+ _id: '1',
+ },
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MESSAGE_SENT_TO_CHAT,
+ variables: {
+ userId: '8',
+ },
+ },
+ result: {
+ data: {
+ messageSentToChat: {
+ _id: '668ec1f1df364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ media: null,
+ chatMessageBelongsTo: {
+ _id: '1',
+ },
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MESSAGE_SENT_TO_CHAT,
+ variables: {
+ userId: '1',
+ },
+ },
+ result: {
+ data: {
+ messageSentToChat: {
+ _id: '668ec1f13603ac4697a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ media: null,
+ chatMessageBelongsTo: {
+ _id: '1',
+ },
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+];
+
+const UNREAD_CHAT_LIST_QUERY_MOCK = [
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {},
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {
+ id: '8',
+ },
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {
+ id: '8',
+ },
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {},
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {},
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {},
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {
+ id: '8',
+ },
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {
+ id: '8',
+ },
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: UNREAD_CHAT_LIST,
+ variables: {
+ id: '8',
+ },
+ },
+ result: {
+ data: {
+ getUnreadChatsByUserId: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ }),
+ },
+ },
+ },
+ },
+];
+
+const GROUP_CHAT_LIST_QUERY_MOCK = [
+ {
+ request: {
+ query: GROUP_CHAT_LIST,
+ variables: {
+ id: '',
+ },
+ },
+ result: {
+ data: {
+ getGroupChatsByUserId: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 1,
+ '2': 1,
+ '3': 1,
+ '4': 1,
+ '5': 1,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: GROUP_CHAT_LIST,
+ variables: {},
+ },
+ result: {
+ data: {
+ getGroupChatsByUserId: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 1,
+ '2': 1,
+ '3': 1,
+ '4': 1,
+ '5': 1,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: GROUP_CHAT_LIST,
+ variables: {},
+ },
+ result: {
+ data: {
+ getGroupChatsByUserId: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 1,
+ '2': 1,
+ '3': 1,
+ '4': 1,
+ '5': 1,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: GROUP_CHAT_LIST,
+ variables: {},
+ },
+ result: {
+ data: {
+ getGroupChatsByUserId: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 1,
+ '2': 1,
+ '3': 1,
+ '4': 1,
+ '5': 1,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: GROUP_CHAT_LIST,
+ variables: {},
+ },
+ result: {
+ data: {
+ getGroupChatsByUserId: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 1,
+ '2': 1,
+ '3': 1,
+ '4': 1,
+ '5': 1,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: GROUP_CHAT_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ getGroupChatsByUserId: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 1,
+ '2': 1,
+ '3': 1,
+ '4': 1,
+ '5': 1,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: GROUP_CHAT_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ getGroupChatsByUserId: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 1,
+ '2': 1,
+ '3': 1,
+ '4': 1,
+ '5': 1,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: GROUP_CHAT_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ getGroupChatsByUserId: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 1,
+ '2': 1,
+ '3': 1,
+ '4': 1,
+ '5': 1,
+ }),
+ },
+ },
+ },
+ },
+];
+
+const CHAT_BY_ID_QUERY_MOCK = [
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '1',
+ createdAt: '2345678903456',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ messages: [
+ {
+ _id: '4',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ },
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '1',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ createdAt: '2345678903456',
+ messages: [
+ {
+ _id: '4',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ },
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '1',
+ isGroup: false,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: null,
+ name: '',
+ createdAt: '2345678903456',
+ messages: [
+ {
+ _id: '4',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ ],
+ admins: [],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ },
+ },
+ },
+ },
+ },
+];
+
+const CHATS_LIST_MOCK = [
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '8',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '8',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '8',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: null,
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '2',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ }),
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ }),
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '2',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '2',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dd40fgh03db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: '65844efc814ddgh4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844ghjefc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: 'ujhgtrdtyuiop',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
+ request: {
+ query: CHATS_LIST,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatsByUserId: [
+ {
+ _id: '65844efc814dhjmkdftyd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ },
+ },
+ {
+ _id: '65844ewsedrffc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ }),
+ },
+ ],
+ },
+ },
+ },
+];
+
+const GROUP_CHAT_BY_ID_QUERY_MOCK = [
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '2',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '345678',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ type: 'STRING',
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ unseenMessagesByUsers: JSON.stringify({
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0,
+ '5': 0,
+ }),
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '1',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: CHAT_BY_ID,
+ variables: {
+ id: '1',
+ },
+ },
+ result: {
+ data: {
+ chatById: {
+ _id: '65844efc814dd4003db811c4',
+ isGroup: true,
+ creator: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: null,
+ email: 'testsuperadmin@example.com',
+ createdAt: '2023-04-13T04:53:17.742Z',
+ __typename: 'User',
+ },
+ organization: {
+ _id: 'pw3ertyuiophgfre45678',
+ name: 'rtyu',
+ },
+ createdAt: '2345678903456',
+ name: 'Test Group Chat',
+ messages: [
+ {
+ _id: '1',
+ createdAt: '345678908765',
+ messageContent: 'Hello',
+ media: '',
+ replyTo: null,
+ sender: {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ },
+ ],
+ users: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ {
+ _id: '2',
+ firstName: 'Test',
+ lastName: 'User',
+ email: 'test@example.com',
+ image: '',
+ },
+ {
+ _id: '3',
+ firstName: 'Test',
+ lastName: 'User1',
+ email: 'test1@example.com',
+ image: '',
+ },
+ {
+ _id: '4',
+ firstName: 'Test',
+ lastName: 'User2',
+ email: 'test2@example.com',
+ image: '',
+ },
+ {
+ _id: '5',
+ firstName: 'Test',
+ lastName: 'User4',
+ email: 'test4@example.com',
+ image: '',
+ },
+ ],
+ admins: [
+ {
+ _id: '1',
+ firstName: 'Disha',
+ lastName: 'Talreja',
+ email: 'disha@example.com',
+ image: '',
+ },
+ ],
+ },
+ },
+ },
+ },
+];
+
+const SEND_MESSAGE_TO_CHAT_MOCK = [
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: '4',
+ messageContent: 'Test reply message',
+ media: 'https://test.com/image.jpg',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: '4',
+ messageContent: 'Test reply message',
+ media: '',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ media: '',
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: '1',
+ messageContent: 'Test reply message',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: undefined,
+ messageContent: 'Hello',
+ media: '',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: '345678',
+ messageContent: 'Test reply message',
+ media: '',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: undefined,
+ messageContent: 'Test message',
+ media: '',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: SEND_MESSAGE_TO_CHAT,
+ variables: {
+ chatId: '1',
+ replyTo: undefined,
+ messageContent: 'Test reply message',
+ media: '',
+ },
+ },
+ result: {
+ data: {
+ sendMessageToChat: {
+ _id: '668ec1f1364e03ac47a151',
+ createdAt: '2024-07-10T17:16:33.248Z',
+ messageContent: 'Test ',
+ media: null,
+ replyTo: null,
+ sender: {
+ _id: '64378abd85008f171cf2990d',
+ firstName: 'Wilt',
+ lastName: 'Shepherd',
+ image: '',
+ },
+ updatedAt: '2024-07-10',
+ },
+ },
+ },
+ },
+];
+
+const MARK_CHAT_MESSAGES_AS_READ_MOCK = [
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: null,
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '8',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: null,
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: null,
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: null,
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: null,
+ chatId: '',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '2',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '2',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '2',
+ chatId: '1',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: null,
+ chatId: '',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '1',
+ chatId: '',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '1',
+ chatId: '',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+ {
+ request: {
+ query: MARK_CHAT_MESSAGES_AS_READ,
+ variables: {
+ userId: '1',
+ chatId: '',
+ },
+ },
+ result: {
+ data: {
+ markChatMessagesAsRead: {
+ _id: '1',
+ },
+ },
+ },
+ },
+];
+
+describe('Testing Chatroom Component [User Portal]', () => {
+ window.HTMLElement.prototype.scrollIntoView = vi.fn();
+ beforeEach(() => {
+ vi.clearAllMocks();
+ vi.resetModules();
+ });
+
+ it('Chat room should display fallback content if no chat is active', async () => {
+ const mocks = [
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...GROUP_CHAT_BY_ID_QUERY_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ...MARK_CHAT_MESSAGES_AS_READ_MOCK,
+ ...GROUP_CHAT_LIST_QUERY_MOCK,
+ ...UNREAD_CHAT_LIST_QUERY_MOCK,
+ ];
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ expect(await screen.findByTestId('noChatSelected')).toBeInTheDocument();
+ });
+
+ it('Selected contact is direct chat', async () => {
+ const link = new MockSubscriptionLink();
+ const mocks = [
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...GROUP_CHAT_BY_ID_QUERY_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ...MARK_CHAT_MESSAGES_AS_READ_MOCK,
+ ...GROUP_CHAT_LIST_QUERY_MOCK,
+ ...UNREAD_CHAT_LIST_QUERY_MOCK,
+ ];
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ });
+
+ it('send message direct chat', async () => {
+ setItem('userId', '2');
+ const mocks = [
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...GROUP_CHAT_BY_ID_QUERY_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ...MARK_CHAT_MESSAGES_AS_READ_MOCK,
+ ...GROUP_CHAT_LIST_QUERY_MOCK,
+ ...UNREAD_CHAT_LIST_QUERY_MOCK,
+ ];
+ const link2 = new StaticMockLink(mocks, true);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+
+ const input = (await screen.findByTestId(
+ 'messageInput',
+ )) as HTMLInputElement;
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Hello' } });
+ });
+ expect(input.value).toBe('Hello');
+
+ const sendBtn = await screen.findByTestId('sendMessage');
+
+ expect(sendBtn).toBeInTheDocument();
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+
+ const messages = await screen.findAllByTestId('message');
+
+ console.log('MESSAGES', messages);
+
+ expect(messages.length).not.toBe(0);
+
+ act(() => {
+ fireEvent.mouseOver(messages[0]);
+ });
+
+ await waitFor(async () => {
+ expect(await screen.findByTestId('moreOptions')).toBeInTheDocument();
+ });
+
+ const moreOptionsBtn = await screen.findByTestId('dropdown');
+ act(() => {
+ fireEvent.click(moreOptionsBtn);
+ });
+
+ const replyBtn = await screen.findByTestId('replyBtn');
+
+ act(() => {
+ fireEvent.click(replyBtn);
+ });
+
+ const replyMsg = await screen.findByTestId('replyMsg');
+
+ await waitFor(() => {
+ expect(replyMsg).toBeInTheDocument();
+ });
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Test reply message' } });
+ });
+ expect(input.value).toBe('Test reply message');
+
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+
+ await wait(400);
+ });
+
+ it('send message direct chat when userId is different', async () => {
+ setItem('userId', '8');
+ const mocks = [
+ ...GROUP_CHAT_BY_ID_QUERY_MOCK,
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ...MARK_CHAT_MESSAGES_AS_READ_MOCK,
+ ...GROUP_CHAT_LIST_QUERY_MOCK,
+ ...UNREAD_CHAT_LIST_QUERY_MOCK,
+ ];
+ const link2 = new StaticMockLink(mocks, true);
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+
+ const input = (await screen.findByTestId(
+ 'messageInput',
+ )) as HTMLInputElement;
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Hello' } });
+ });
+ expect(input.value).toBe('Hello');
+
+ const sendBtn = await screen.findByTestId('sendMessage');
+
+ expect(sendBtn).toBeInTheDocument();
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+
+ const messages = await screen.findAllByTestId('message');
+
+ console.log('MESSAGES', messages);
+
+ expect(messages.length).not.toBe(0);
+
+ act(() => {
+ fireEvent.mouseOver(messages[0]);
+ });
+
+ await waitFor(async () => {
+ expect(await screen.findByTestId('moreOptions')).toBeInTheDocument();
+ });
+
+ const moreOptionsBtn = await screen.findByTestId('dropdown');
+ act(() => {
+ fireEvent.click(moreOptionsBtn);
+ });
+
+ const replyBtn = await screen.findByTestId('replyBtn');
+
+ act(() => {
+ fireEvent.click(replyBtn);
+ });
+
+ const replyMsg = await screen.findByTestId('replyMsg');
+
+ await waitFor(() => {
+ expect(replyMsg).toBeInTheDocument();
+ });
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Test reply message' } });
+ });
+ expect(input.value).toBe('Test reply message');
+
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+
+ await wait(400);
+ });
+
+ it('Selected contact is group chat', async () => {
+ const mocks = [
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ...MARK_CHAT_MESSAGES_AS_READ_MOCK,
+ ...GROUP_CHAT_LIST_QUERY_MOCK,
+ ...UNREAD_CHAT_LIST_QUERY_MOCK,
+ ];
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+ });
+
+ it('send message group chat', async () => {
+ const mocks = [
+ ...MESSAGE_SENT_TO_CHAT_MOCK,
+ ...CHAT_BY_ID_QUERY_MOCK,
+ ...CHATS_LIST_MOCK,
+ ...GROUP_CHAT_BY_ID_QUERY_MOCK,
+ ...SEND_MESSAGE_TO_CHAT_MOCK,
+ ...MARK_CHAT_MESSAGES_AS_READ_MOCK,
+ ...GROUP_CHAT_LIST_QUERY_MOCK,
+ ...UNREAD_CHAT_LIST_QUERY_MOCK,
+ ];
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+ await wait();
+
+ const input = (await screen.findByTestId(
+ 'messageInput',
+ )) as HTMLInputElement;
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Test message' } });
+ });
+ expect(input.value).toBe('Test message');
+
+ const sendBtn = await screen.findByTestId('sendMessage');
+
+ expect(sendBtn).toBeInTheDocument();
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+
+ const messages = await screen.findAllByTestId('message');
+
+ expect(messages.length).not.toBe(0);
+
+ act(() => {
+ fireEvent.mouseOver(messages[0]);
+ });
+
+ expect(await screen.findByTestId('moreOptions')).toBeInTheDocument();
+
+ const moreOptionsBtn = await screen.findByTestId('dropdown');
+ act(() => {
+ fireEvent.click(moreOptionsBtn);
+ });
+
+ act(() => {
+ fireEvent.change(input, { target: { value: 'Test reply message' } });
+ });
+ expect(input.value).toBe('Test reply message');
+
+ act(() => {
+ fireEvent.click(sendBtn);
+ });
+
+ await wait(500);
+ });
+});
diff --git a/src/components/UserPortal/ChatRoom/ChatRoom.test.tsx b/src/components/UserPortal/ChatRoom/ChatRoom.test.tsx
deleted file mode 100644
index c808485132..0000000000
--- a/src/components/UserPortal/ChatRoom/ChatRoom.test.tsx
+++ /dev/null
@@ -1,1586 +0,0 @@
-import React from 'react';
-
-import {
- act,
- render,
- screen,
- fireEvent,
- waitFor,
-} from '@testing-library/react';
-import { MockSubscriptionLink, MockedProvider } from '@apollo/react-testing';
-import { I18nextProvider } from 'react-i18next';
-import { BrowserRouter } from 'react-router-dom';
-import { Provider } from 'react-redux';
-import { store } from 'state/store';
-import i18nForTest from 'utils/i18nForTest';
-import { CHATS_LIST, CHAT_BY_ID } from 'GraphQl/Queries/PlugInQueries';
-import {
- MESSAGE_SENT_TO_CHAT,
- SEND_MESSAGE_TO_CHAT,
-} from 'GraphQl/Mutations/OrganizationMutations';
-import ChatRoom from './ChatRoom';
-import { useLocalStorage } from 'utils/useLocalstorage';
-import { StaticMockLink } from 'utils/StaticMockLink';
-
-const { setItem } = useLocalStorage();
-
-async function wait(ms = 100): Promise {
- await act(() => {
- return new Promise((resolve) => {
- setTimeout(resolve, ms);
- });
- });
-}
-
-const MESSAGE_SENT_TO_CHAT_MOCK = [
- {
- request: {
- query: MESSAGE_SENT_TO_CHAT,
- variables: {
- userId: null,
- },
- },
- result: {
- data: {
- messageSentToChat: {
- _id: '668ec1f1364e03ac47a151',
- createdAt: '2024-07-10T17:16:33.248Z',
- chatMessageBelongsTo: {
- _id: '1',
- },
- messageContent: 'Test ',
- replyTo: null,
- sender: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: '',
- },
- updatedAt: '2024-07-10',
- },
- },
- },
- },
- {
- request: {
- query: MESSAGE_SENT_TO_CHAT,
- variables: {
- userId: '2',
- },
- },
- result: {
- data: {
- messageSentToChat: {
- _id: '668ec1f1df364e03ac47a151',
- createdAt: '2024-07-10T17:16:33.248Z',
- messageContent: 'Test ',
- chatMessageBelongsTo: {
- _id: '1',
- },
- replyTo: null,
- sender: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: '',
- },
- updatedAt: '2024-07-10',
- },
- },
- },
- },
- {
- request: {
- query: MESSAGE_SENT_TO_CHAT,
- variables: {
- userId: '1',
- },
- },
- result: {
- data: {
- messageSentToChat: {
- _id: '668ec1f13603ac4697a151',
- createdAt: '2024-07-10T17:16:33.248Z',
- messageContent: 'Test ',
- chatMessageBelongsTo: {
- _id: '1',
- },
- replyTo: null,
- sender: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: '',
- },
- updatedAt: '2024-07-10',
- },
- },
- },
- },
-];
-
-const CHAT_BY_ID_QUERY_MOCK = [
- {
- request: {
- query: CHAT_BY_ID,
- variables: {
- id: '1',
- },
- },
- result: {
- data: {
- chatById: {
- _id: '1',
- createdAt: '2345678903456',
- isGroup: false,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: null,
- name: '',
- messages: [
- {
- _id: '4',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- ],
- },
- },
- },
- },
- {
- request: {
- query: CHAT_BY_ID,
- variables: {
- id: '1',
- },
- },
- result: {
- data: {
- chatById: {
- _id: '1',
- isGroup: false,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: null,
- name: '',
- createdAt: '2345678903456',
- messages: [
- {
- _id: '4',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- ],
- },
- },
- },
- },
- {
- request: {
- query: CHAT_BY_ID,
- variables: {
- id: '',
- },
- },
- result: {
- data: {
- chatById: {
- _id: '1',
- isGroup: false,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: null,
- name: '',
- createdAt: '2345678903456',
- messages: [
- {
- _id: '4',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- ],
- },
- },
- },
- },
-];
-
-const CHATS_LIST_MOCK = [
- {
- request: {
- query: CHATS_LIST,
- variables: {
- id: null,
- },
- },
- result: {
- data: {
- chatsByUserId: [
- {
- _id: '65844efc814dd40fgh03db811c4',
- isGroup: true,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: {
- _id: 'pw3ertyuiophgfre45678',
- name: 'rtyu',
- },
- createdAt: '2345678903456',
- name: 'Test Group Chat',
- messages: [
- {
- _id: '345678',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- {
- _id: '3',
- firstName: 'Test',
- lastName: 'User1',
- email: 'test1@example.com',
- image: '',
- },
- {
- _id: '4',
- firstName: 'Test',
- lastName: 'User2',
- email: 'test2@example.com',
- image: '',
- },
- {
- _id: '5',
- firstName: 'Test',
- lastName: 'User4',
- email: 'test4@example.com',
- image: '',
- },
- ],
- },
- {
- _id: '65844efc814ddgh4003db811c4',
- isGroup: true,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: {
- _id: 'pw3ertyuiophgfre45678',
- name: 'rtyu',
- },
- createdAt: '2345678903456',
- name: 'Test Group Chat',
- messages: [
- {
- _id: '345678',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- {
- _id: '3',
- firstName: 'Test',
- lastName: 'User1',
- email: 'test1@example.com',
- image: '',
- },
- {
- _id: '4',
- firstName: 'Test',
- lastName: 'User2',
- email: 'test2@example.com',
- image: '',
- },
- {
- _id: '5',
- firstName: 'Test',
- lastName: 'User4',
- email: 'test4@example.com',
- image: '',
- },
- ],
- },
- ],
- },
- },
- },
- {
- request: {
- query: CHATS_LIST,
- variables: {
- id: '',
- },
- },
- result: {
- data: {
- chatsByUserId: [
- {
- _id: '65844ghjefc814dd4003db811c4',
- isGroup: true,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: {
- _id: 'pw3ertyuiophgfre45678',
- name: 'rtyu',
- },
- createdAt: '2345678903456',
- name: 'Test Group Chat',
- messages: [
- {
- _id: '345678',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- {
- _id: '3',
- firstName: 'Test',
- lastName: 'User1',
- email: 'test1@example.com',
- image: '',
- },
- {
- _id: '4',
- firstName: 'Test',
- lastName: 'User2',
- email: 'test2@example.com',
- image: '',
- },
- {
- _id: '5',
- firstName: 'Test',
- lastName: 'User4',
- email: 'test4@example.com',
- image: '',
- },
- ],
- },
- {
- _id: 'ujhgtrdtyuiop',
- isGroup: true,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: {
- _id: 'pw3ertyuiophgfre45678',
- name: 'rtyu',
- },
- createdAt: '2345678903456',
- name: 'Test Group Chat',
- messages: [
- {
- _id: '345678',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- {
- _id: '3',
- firstName: 'Test',
- lastName: 'User1',
- email: 'test1@example.com',
- image: '',
- },
- {
- _id: '4',
- firstName: 'Test',
- lastName: 'User2',
- email: 'test2@example.com',
- image: '',
- },
- {
- _id: '5',
- firstName: 'Test',
- lastName: 'User4',
- email: 'test4@example.com',
- image: '',
- },
- ],
- },
- ],
- },
- },
- },
- {
- request: {
- query: CHATS_LIST,
- variables: {
- id: '1',
- },
- },
- result: {
- data: {
- chatsByUserId: [
- {
- _id: '65844efc814dhjmkdftyd4003db811c4',
- isGroup: true,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: {
- _id: 'pw3ertyuiophgfre45678',
- name: 'rtyu',
- },
- createdAt: '2345678903456',
- name: 'Test Group Chat',
- messages: [
- {
- _id: '345678',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- {
- _id: '3',
- firstName: 'Test',
- lastName: 'User1',
- email: 'test1@example.com',
- image: '',
- },
- {
- _id: '4',
- firstName: 'Test',
- lastName: 'User2',
- email: 'test2@example.com',
- image: '',
- },
- {
- _id: '5',
- firstName: 'Test',
- lastName: 'User4',
- email: 'test4@example.com',
- image: '',
- },
- ],
- },
- {
- _id: '65844ewsedrffc814dd4003db811c4',
- isGroup: true,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: {
- _id: 'pw3ertyuiophgfre45678',
- name: 'rtyu',
- },
- createdAt: '2345678903456',
- name: 'Test Group Chat',
- messages: [
- {
- _id: '345678',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- {
- _id: '3',
- firstName: 'Test',
- lastName: 'User1',
- email: 'test1@example.com',
- image: '',
- },
- {
- _id: '4',
- firstName: 'Test',
- lastName: 'User2',
- email: 'test2@example.com',
- image: '',
- },
- {
- _id: '5',
- firstName: 'Test',
- lastName: 'User4',
- email: 'test4@example.com',
- image: '',
- },
- ],
- },
- ],
- },
- },
- },
-];
-
-const GROUP_CHAT_BY_ID_QUERY_MOCK = [
- {
- request: {
- query: CHAT_BY_ID,
- variables: {
- id: '',
- },
- },
- result: {
- data: {
- chatById: {
- _id: '65844efc814dd4003db811c4',
- isGroup: true,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: {
- _id: 'pw3ertyuiophgfre45678',
- name: 'rtyu',
- },
- createdAt: '2345678903456',
- name: 'Test Group Chat',
- messages: [
- {
- _id: '2',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- {
- _id: '3',
- firstName: 'Test',
- lastName: 'User1',
- email: 'test1@example.com',
- image: '',
- },
- {
- _id: '4',
- firstName: 'Test',
- lastName: 'User2',
- email: 'test2@example.com',
- image: '',
- },
- {
- _id: '5',
- firstName: 'Test',
- lastName: 'User4',
- email: 'test4@example.com',
- image: '',
- },
- ],
- },
- },
- },
- },
- {
- request: {
- query: CHAT_BY_ID,
- variables: {
- id: '1',
- },
- },
- result: {
- data: {
- chatById: {
- _id: '65844efc814dd4003db811c4',
- isGroup: true,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: {
- _id: 'pw3ertyuiophgfre45678',
- name: 'rtyu',
- },
- createdAt: '2345678903456',
- name: 'Test Group Chat',
- messages: [
- {
- _id: '1',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- {
- _id: '3',
- firstName: 'Test',
- lastName: 'User1',
- email: 'test1@example.com',
- image: '',
- },
- {
- _id: '4',
- firstName: 'Test',
- lastName: 'User2',
- email: 'test2@example.com',
- image: '',
- },
- {
- _id: '5',
- firstName: 'Test',
- lastName: 'User4',
- email: 'test4@example.com',
- image: '',
- },
- ],
- },
- },
- },
- },
- {
- request: {
- query: CHAT_BY_ID,
- variables: {
- id: '1',
- },
- },
- result: {
- data: {
- chatById: {
- _id: '65844efc814dd4003db811c4',
- isGroup: true,
- creator: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: null,
- email: 'testsuperadmin@example.com',
- createdAt: '2023-04-13T04:53:17.742Z',
- __typename: 'User',
- },
- organization: {
- _id: 'pw3ertyuiophgfre45678',
- name: 'rtyu',
- },
- createdAt: '2345678903456',
- name: 'Test Group Chat',
- messages: [
- {
- _id: '1',
- createdAt: '345678908765',
- messageContent: 'Hello',
- replyTo: null,
- sender: {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- },
- ],
- users: [
- {
- _id: '1',
- firstName: 'Disha',
- lastName: 'Talreja',
- email: 'disha@example.com',
- image: '',
- },
- {
- _id: '2',
- firstName: 'Test',
- lastName: 'User',
- email: 'test@example.com',
- image: '',
- },
- {
- _id: '3',
- firstName: 'Test',
- lastName: 'User1',
- email: 'test1@example.com',
- image: '',
- },
- {
- _id: '4',
- firstName: 'Test',
- lastName: 'User2',
- email: 'test2@example.com',
- image: '',
- },
- {
- _id: '5',
- firstName: 'Test',
- lastName: 'User4',
- email: 'test4@example.com',
- image: '',
- },
- ],
- },
- },
- },
- },
-];
-
-const SEND_MESSAGE_TO_CHAT_MOCK = [
- {
- request: {
- query: SEND_MESSAGE_TO_CHAT,
- variables: {
- chatId: '1',
- replyTo: '4',
- messageContent: 'Test reply message',
- },
- },
- result: {
- data: {
- sendMessageToChat: {
- _id: '668ec1f1364e03ac47a151',
- createdAt: '2024-07-10T17:16:33.248Z',
- messageContent: 'Test ',
- replyTo: null,
- sender: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: '',
- },
- updatedAt: '2024-07-10',
- },
- },
- },
- },
- {
- request: {
- query: SEND_MESSAGE_TO_CHAT,
- variables: {
- chatId: '1',
- replyTo: '4',
- messageContent: 'Test reply message',
- },
- },
- result: {
- data: {
- sendMessageToChat: {
- _id: '668ec1f1364e03ac47a151',
- createdAt: '2024-07-10T17:16:33.248Z',
- messageContent: 'Test ',
- replyTo: null,
- sender: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: '',
- },
- updatedAt: '2024-07-10',
- },
- },
- },
- },
- {
- request: {
- query: SEND_MESSAGE_TO_CHAT,
- variables: {
- chatId: '1',
- replyTo: '1',
- messageContent: 'Test reply message',
- },
- },
- result: {
- data: {
- sendMessageToChat: {
- _id: '668ec1f1364e03ac47a151',
- createdAt: '2024-07-10T17:16:33.248Z',
- messageContent: 'Test ',
- replyTo: null,
- sender: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: '',
- },
- updatedAt: '2024-07-10',
- },
- },
- },
- },
- {
- request: {
- query: SEND_MESSAGE_TO_CHAT,
- variables: {
- chatId: '1',
- replyTo: undefined,
- messageContent: 'Hello',
- },
- },
- result: {
- data: {
- sendMessageToChat: {
- _id: '668ec1f1364e03ac47a151',
- createdAt: '2024-07-10T17:16:33.248Z',
- messageContent: 'Test ',
- replyTo: null,
- sender: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: '',
- },
- updatedAt: '2024-07-10',
- },
- },
- },
- },
- {
- request: {
- query: SEND_MESSAGE_TO_CHAT,
- variables: {
- chatId: '1',
- replyTo: '345678',
- messageContent: 'Test reply message',
- },
- },
- result: {
- data: {
- sendMessageToChat: {
- _id: '668ec1f1364e03ac47a151',
- createdAt: '2024-07-10T17:16:33.248Z',
- messageContent: 'Test ',
- replyTo: null,
- sender: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: '',
- },
- updatedAt: '2024-07-10',
- },
- },
- },
- },
- {
- request: {
- query: SEND_MESSAGE_TO_CHAT,
- variables: {
- chatId: '1',
- replyTo: undefined,
- messageContent: 'Test message',
- },
- },
- result: {
- data: {
- sendMessageToChat: {
- _id: '668ec1f1364e03ac47a151',
- createdAt: '2024-07-10T17:16:33.248Z',
- messageContent: 'Test ',
- replyTo: null,
- sender: {
- _id: '64378abd85008f171cf2990d',
- firstName: 'Wilt',
- lastName: 'Shepherd',
- image: '',
- },
- updatedAt: '2024-07-10',
- },
- },
- },
- },
-];
-
-describe('Testing Chatroom Component [User Portal]', () => {
- window.HTMLElement.prototype.scrollIntoView = jest.fn();
-
- test('Chat room should display fallback content if no chat is active', async () => {
- const mocks = [
- ...MESSAGE_SENT_TO_CHAT_MOCK,
- ...CHAT_BY_ID_QUERY_MOCK,
- ...CHATS_LIST_MOCK,
- ...SEND_MESSAGE_TO_CHAT_MOCK,
- ];
- render(
-
-
-
-
-
-
-
-
- ,
- );
- await wait();
- expect(await screen.findByTestId('noChatSelected')).toBeInTheDocument();
- });
-
- test('Selected contact is direct chat', async () => {
- const link = new MockSubscriptionLink();
- const mocks = [
- ...MESSAGE_SENT_TO_CHAT_MOCK,
- ...CHAT_BY_ID_QUERY_MOCK,
- ...CHATS_LIST_MOCK,
- ...SEND_MESSAGE_TO_CHAT_MOCK,
- ];
- render(
-
-
-
-
-
-
-
-
- ,
- );
- await wait();
- });
-
- test('send message direct chat', async () => {
- setItem('userId', '2');
- const mocks = [
- ...MESSAGE_SENT_TO_CHAT_MOCK,
- ...CHAT_BY_ID_QUERY_MOCK,
- ...CHATS_LIST_MOCK,
- ...GROUP_CHAT_BY_ID_QUERY_MOCK,
- ...SEND_MESSAGE_TO_CHAT_MOCK,
- ];
- const link2 = new StaticMockLink(mocks, true);
- render(
-
-
-
-
-
-
-
-
- ,
- );
- await wait();
-
- const input = (await screen.findByTestId(
- 'messageInput',
- )) as HTMLInputElement;
-
- act(() => {
- fireEvent.change(input, { target: { value: 'Hello' } });
- });
- expect(input.value).toBe('Hello');
-
- const sendBtn = await screen.findByTestId('sendMessage');
-
- expect(sendBtn).toBeInTheDocument();
- act(() => {
- fireEvent.click(sendBtn);
- });
- await waitFor(() => {
- expect(input.value).toBeFalsy();
- });
-
- const messages = await screen.findAllByTestId('message');
-
- console.log('MESSAGES', messages);
-
- expect(messages.length).not.toBe(0);
-
- act(() => {
- fireEvent.mouseOver(messages[0]);
- });
-
- await waitFor(async () => {
- expect(await screen.findByTestId('moreOptions')).toBeInTheDocument();
- });
-
- const moreOptionsBtn = await screen.findByTestId('dropdown');
- act(() => {
- fireEvent.click(moreOptionsBtn);
- });
-
- const replyBtn = await screen.findByTestId('replyBtn');
-
- act(() => {
- fireEvent.click(replyBtn);
- });
-
- const replyMsg = await screen.findByTestId('replyMsg');
-
- await waitFor(() => {
- expect(replyMsg).toBeInTheDocument();
- });
-
- act(() => {
- fireEvent.change(input, { target: { value: 'Test reply message' } });
- });
- expect(input.value).toBe('Test reply message');
-
- act(() => {
- fireEvent.click(sendBtn);
- });
-
- await wait(400);
- });
-
- test('send message direct chat when userId is different', async () => {
- setItem('userId', '8');
- const mocks = [
- ...GROUP_CHAT_BY_ID_QUERY_MOCK,
- ...MESSAGE_SENT_TO_CHAT_MOCK,
- ...CHAT_BY_ID_QUERY_MOCK,
- ...CHATS_LIST_MOCK,
- ...SEND_MESSAGE_TO_CHAT_MOCK,
- ];
- const link2 = new StaticMockLink(mocks, true);
- render(
-
-
-
-
-
-
-
-
- ,
- );
- await wait();
-
- const input = (await screen.findByTestId(
- 'messageInput',
- )) as HTMLInputElement;
-
- act(() => {
- fireEvent.change(input, { target: { value: 'Hello' } });
- });
- expect(input.value).toBe('Hello');
-
- const sendBtn = await screen.findByTestId('sendMessage');
-
- expect(sendBtn).toBeInTheDocument();
- act(() => {
- fireEvent.click(sendBtn);
- });
- await waitFor(() => {
- expect(input.value).toBeFalsy();
- });
-
- const messages = await screen.findAllByTestId('message');
-
- console.log('MESSAGES', messages);
-
- expect(messages.length).not.toBe(0);
-
- act(() => {
- fireEvent.mouseOver(messages[0]);
- });
-
- await waitFor(async () => {
- expect(await screen.findByTestId('moreOptions')).toBeInTheDocument();
- });
-
- const moreOptionsBtn = await screen.findByTestId('dropdown');
- act(() => {
- fireEvent.click(moreOptionsBtn);
- });
-
- const replyBtn = await screen.findByTestId('replyBtn');
-
- act(() => {
- fireEvent.click(replyBtn);
- });
-
- const replyMsg = await screen.findByTestId('replyMsg');
-
- await waitFor(() => {
- expect(replyMsg).toBeInTheDocument();
- });
-
- act(() => {
- fireEvent.change(input, { target: { value: 'Test reply message' } });
- });
- expect(input.value).toBe('Test reply message');
-
- act(() => {
- fireEvent.click(sendBtn);
- });
-
- await wait(400);
- });
-
- test('Selected contact is group chat', async () => {
- const mocks = [
- ...MESSAGE_SENT_TO_CHAT_MOCK,
- ...CHAT_BY_ID_QUERY_MOCK,
- ...CHATS_LIST_MOCK,
- ...SEND_MESSAGE_TO_CHAT_MOCK,
- ];
- render(
-
-
-
-
-
-
-
-
- ,
- );
- await wait();
- });
-
- test('send message group chat', async () => {
- const mocks = [
- ...MESSAGE_SENT_TO_CHAT_MOCK,
- ...CHAT_BY_ID_QUERY_MOCK,
- ...CHATS_LIST_MOCK,
- ...SEND_MESSAGE_TO_CHAT_MOCK,
- ];
- render(
-
-
-
-
-
-
-
-
- ,
- );
- await wait();
-
- const input = (await screen.findByTestId(
- 'messageInput',
- )) as HTMLInputElement;
-
- act(() => {
- fireEvent.change(input, { target: { value: 'Test message' } });
- });
- expect(input.value).toBe('Test message');
-
- const sendBtn = await screen.findByTestId('sendMessage');
-
- expect(sendBtn).toBeInTheDocument();
- act(() => {
- fireEvent.click(sendBtn);
- });
- await waitFor(() => {
- expect(input.value).toBeFalsy();
- });
-
- const messages = await screen.findAllByTestId('message');
-
- expect(messages.length).not.toBe(0);
-
- act(() => {
- fireEvent.mouseOver(messages[0]);
- });
-
- expect(await screen.findByTestId('moreOptions')).toBeInTheDocument();
-
- const moreOptionsBtn = await screen.findByTestId('dropdown');
- act(() => {
- fireEvent.click(moreOptionsBtn);
- });
-
- const replyBtn = await screen.findByTestId('replyBtn');
-
- act(() => {
- fireEvent.click(replyBtn);
- });
-
- const replyMsg = await screen.findByTestId('replyMsg');
-
- await waitFor(() => {
- expect(replyMsg).toBeInTheDocument();
- });
-
- act(() => {
- fireEvent.change(input, { target: { value: 'Test reply message' } });
- });
- expect(input.value).toBe('Test reply message');
-
- const closeReplyBtn = await screen.findByTestId('closeReply');
-
- expect(closeReplyBtn).toBeInTheDocument();
-
- fireEvent.click(closeReplyBtn);
-
- await wait(500);
- });
-
- test('reply to message', async () => {
- const mocks = [
- ...MESSAGE_SENT_TO_CHAT_MOCK,
- ...CHAT_BY_ID_QUERY_MOCK,
- ...CHATS_LIST_MOCK,
- ...SEND_MESSAGE_TO_CHAT_MOCK,
- ];
- const link2 = new StaticMockLink(mocks, true);
- render(
-
-
-
-
-
-
-
-
- ,
- );
- await wait();
-
- const input = (await screen.findByTestId(
- 'messageInput',
- )) as HTMLInputElement;
-
- act(() => {
- fireEvent.change(input, { target: { value: 'Test message' } });
- });
- expect(input.value).toBe('Test message');
-
- const sendBtn = await screen.findByTestId('sendMessage');
-
- expect(sendBtn).toBeInTheDocument();
- act(() => {
- fireEvent.click(sendBtn);
- });
- await waitFor(() => {
- expect(input.value).toBeFalsy();
- });
-
- const messages = await screen.findAllByTestId('message');
-
- expect(messages.length).not.toBe(0);
-
- act(() => {
- fireEvent.mouseOver(messages[0]);
- });
-
- expect(await screen.findByTestId('moreOptions')).toBeInTheDocument();
-
- const moreOptionsBtn = await screen.findByTestId('dropdown');
- act(() => {
- fireEvent.click(moreOptionsBtn);
- });
-
- const replyBtn = await screen.findByTestId('replyBtn');
-
- act(() => {
- fireEvent.click(replyBtn);
- });
-
- const replyMsg = await screen.findByTestId('replyMsg');
-
- await waitFor(() => {
- expect(replyMsg).toBeInTheDocument();
- });
-
- act(() => {
- fireEvent.change(input, { target: { value: 'Test reply message' } });
- });
- expect(input.value).toBe('Test reply message');
-
- act(() => {
- fireEvent.click(sendBtn);
- });
-
- await wait(400);
- });
-});
diff --git a/src/components/UserPortal/ChatRoom/ChatRoom.tsx b/src/components/UserPortal/ChatRoom/ChatRoom.tsx
index c23244a314..fd06a47c12 100644
--- a/src/components/UserPortal/ChatRoom/ChatRoom.tsx
+++ b/src/components/UserPortal/ChatRoom/ChatRoom.tsx
@@ -5,18 +5,31 @@ import { Button, Dropdown, Form, InputGroup } from 'react-bootstrap';
import styles from './ChatRoom.module.css';
import PermContactCalendarIcon from '@mui/icons-material/PermContactCalendar';
import { useTranslation } from 'react-i18next';
-import { CHAT_BY_ID } from 'GraphQl/Queries/PlugInQueries';
+import { CHAT_BY_ID, UNREAD_CHAT_LIST } from 'GraphQl/Queries/PlugInQueries';
+import type { ApolloQueryResult } from '@apollo/client';
import { useMutation, useQuery, useSubscription } from '@apollo/client';
import {
+ EDIT_CHAT_MESSAGE,
+ MARK_CHAT_MESSAGES_AS_READ,
MESSAGE_SENT_TO_CHAT,
SEND_MESSAGE_TO_CHAT,
} from 'GraphQl/Mutations/OrganizationMutations';
import useLocalStorage from 'utils/useLocalstorage';
import Avatar from 'components/Avatar/Avatar';
import { MoreVert, Close } from '@mui/icons-material';
+import GroupChatDetails from 'components/GroupChatDetails/GroupChatDetails';
+import { GrAttachment } from 'react-icons/gr';
+import convertToBase64 from 'utils/convertToBase64';
interface InterfaceChatRoomProps {
selectedContact: string;
+ chatListRefetch: (
+ variables?:
+ | Partial<{
+ id: string;
+ }>
+ | undefined,
+ ) => Promise>;
}
/**
@@ -59,7 +72,7 @@ type DirectMessage = {
}
| undefined;
messageContent: string;
- type: string;
+ media: string;
};
type Chat = {
@@ -68,12 +81,20 @@ type Chat = {
name?: string;
image?: string;
messages: DirectMessage[];
+ admins: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ email: string;
+ }[];
users: {
_id: string;
firstName: string;
lastName: string;
email: string;
}[];
+ unseenMessagesByUsers: string;
+ description: string;
};
export default function chatRoom(props: InterfaceChatRoomProps): JSX.Element {
@@ -93,10 +114,23 @@ export default function chatRoom(props: InterfaceChatRoomProps): JSX.Element {
const userId = getItem('userId');
const [chatTitle, setChatTitle] = useState('');
const [chatSubtitle, setChatSubtitle] = useState('');
+ const [chatImage, setChatImage] = useState('');
const [newMessage, setNewMessage] = useState('');
const [chat, setChat] = useState();
const [replyToDirectMessage, setReplyToDirectMessage] =
useState(null);
+ const [editMessage, setEditMessage] = useState(null);
+ const [groupChatDetailsModalisOpen, setGroupChatDetailsModalisOpen] =
+ useState(false);
+
+ const [attachment, setAttachment] = useState('');
+
+ const openGroupChatDetails = (): void => {
+ setGroupChatDetailsModalisOpen(true);
+ };
+
+ const toggleGroupChatDetailsModal = (): void =>
+ setGroupChatDetailsModalisOpen(!groupChatDetailsModalisOpen);
/**
* Handles changes to the new message input field.
@@ -114,18 +148,49 @@ export default function chatRoom(props: InterfaceChatRoomProps): JSX.Element {
variables: {
chatId: props.selectedContact,
replyTo: replyToDirectMessage?._id,
+ media: attachment,
messageContent: newMessage,
},
});
+ const [editChatMessage] = useMutation(EDIT_CHAT_MESSAGE, {
+ variables: {
+ messageId: editMessage?._id,
+ messageContent: newMessage,
+ chatId: props.selectedContact,
+ },
+ });
+
+ const [markChatMessagesAsRead] = useMutation(MARK_CHAT_MESSAGES_AS_READ, {
+ variables: {
+ chatId: props.selectedContact,
+ userId: userId,
+ },
+ });
+
const { data: chatData, refetch: chatRefetch } = useQuery(CHAT_BY_ID, {
variables: {
id: props.selectedContact,
},
});
+ // const { refetch: chatListRefetch } = useQuery(CHATS_LIST, {
+ // variables: {
+ // id: userId,
+ // },
+ // });
+
+ const { refetch: unreadChatListRefetch } = useQuery(UNREAD_CHAT_LIST, {
+ variables: {
+ id: userId,
+ },
+ });
+
useEffect(() => {
- chatRefetch();
+ markChatMessagesAsRead().then(() => {
+ props.chatListRefetch();
+ unreadChatListRefetch();
+ });
}, [props.selectedContact]);
useEffect(() => {
@@ -135,6 +200,7 @@ export default function chatRoom(props: InterfaceChatRoomProps): JSX.Element {
if (chat.isGroup) {
setChatTitle(chat.name);
setChatSubtitle(`${chat.users.length} members`);
+ setChatImage(chat.image);
} else {
const otherUser = chat.users.find(
(user: { _id: string }) => user._id !== userId,
@@ -142,35 +208,40 @@ export default function chatRoom(props: InterfaceChatRoomProps): JSX.Element {
if (otherUser) {
setChatTitle(`${otherUser.firstName} ${otherUser.lastName}`);
setChatSubtitle(otherUser.email);
+ setChatImage(otherUser.image);
}
}
}
}, [chatData]);
const sendMessage = async (): Promise => {
- await sendMessageToChat();
+ if (editMessage) {
+ await editChatMessage();
+ } else {
+ await sendMessageToChat();
+ }
await chatRefetch();
setReplyToDirectMessage(null);
setNewMessage('');
+ setAttachment('');
+ await props.chatListRefetch({ id: userId });
};
useSubscription(MESSAGE_SENT_TO_CHAT, {
variables: {
userId: userId,
},
- onData: (messageSubscriptionData) => {
+ onData: async (messageSubscriptionData) => {
if (
messageSubscriptionData?.data.data.messageSentToChat &&
messageSubscriptionData?.data.data.messageSentToChat
.chatMessageBelongsTo['_id'] == props.selectedContact
) {
+ await markChatMessagesAsRead();
chatRefetch();
- } else {
- chatRefetch({
- id: messageSubscriptionData?.data.data.messageSentToChat
- .chatMessageBelongsTo['_id'],
- });
}
+ props.chatListRefetch();
+ unreadChatListRefetch();
},
});
@@ -180,6 +251,22 @@ export default function chatRoom(props: InterfaceChatRoomProps): JSX.Element {
?.lastElementChild?.scrollIntoView({ block: 'end' });
});
+ const fileInputRef = useRef(null);
+
+ const handleAddAttachment = (): void => {
+ fileInputRef?.current?.click();
+ };
+
+ const handleImageChange = async (
+ e: React.ChangeEvent,
+ ): Promise => {
+ const file = e.target.files?.[0];
+ if (file) {
+ const base64 = await convertToBase64(file);
+ setAttachment(base64);
+ }
+ };
+
return (
-
-
+ {chatImage ? (
+
+ ) : (
+
+ )}
+
(chat?.isGroup ? openGroupChatDetails() : null)}
+ className={styles.userDetails}
+ >
{chatTitle}
{chatSubtitle}
@@ -274,6 +372,13 @@ export default function chatRoom(props: InterfaceChatRoomProps): JSX.Element {
)}
+ {message.media && (
+
+ )}
{message.messageContent}
@@ -294,7 +399,16 @@ export default function chatRoom(props: InterfaceChatRoomProps): JSX.Element {
}}
data-testid="replyBtn"
>
- Reply
+ {t('reply')}
+
+ {
+ setEditMessage(message);
+ setNewMessage(message.messageContent);
+ }}
+ data-testid="replyToMessage"
+ >
+ Edit
@@ -317,6 +431,13 @@ export default function chatRoom(props: InterfaceChatRoomProps): JSX.Element {