Skip to content

Commit

Permalink
added registrants test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
duplixx committed Nov 10, 2024
1 parent 85c2008 commit f83f61e
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 83 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

112 changes: 90 additions & 22 deletions src/components/EventRegistrantsModal/EventRegistrantsModal.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import React from 'react';
import {
fireEvent,
queryByLabelText,
render,
waitFor,
} from '@testing-library/react';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { EventRegistrantsModal } from './EventRegistrantsModal';
import { EVENT_ATTENDEES, MEMBERS_LIST } from 'GraphQl/Queries/Queries';
Expand Down Expand Up @@ -46,7 +41,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',
},
},
],
},
},
},
Expand All @@ -69,9 +76,9 @@ const queryMockOrgMembers = [
_id: 'user1',
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
image: '',
createdAt: '12/12/22',
image: null,
email: '[email protected]',
createdAt: '2023-01-01',
organizationsBlockedBy: [],
},
],
Expand All @@ -81,6 +88,24 @@ const queryMockOrgMembers = [
},
},
];
const queryMockWithoutOrgMembers = [
{
request: {
query: MEMBERS_LIST,
variables: { id: 'org123' },
},
result: {
data: {
organizations: [
{
_id: 'org123',
members: [],
},
],
},
},
},
];

const successfulAddRegistrantMock = [
{
Expand Down Expand Up @@ -180,19 +205,18 @@ describe('Testing Event Registrants Modal', () => {

await waitFor(() =>
expect(
queryByText('Please choose a user to add first!'),
queryByText('Please choose an user to add first!'),
).toBeInTheDocument(),
);

// Choose a user to add as an attendee
const attendeeInput = queryByLabelText('Add a Registrant');
if (attendeeInput) {
fireEvent.change(attendeeInput, {
target: { value: 'John Doe' },
});
fireEvent.keyDown(attendeeInput, { key: 'ArrowDown' });
fireEvent.keyDown(attendeeInput, { key: 'Enter' });
}
const attendeeInput = queryByLabelText('Add an Registrant');
fireEvent.change(attendeeInput as Element, {
target: { value: 'John Doe' },
});
fireEvent.keyDown(attendeeInput as HTMLElement, { key: 'ArrowDown' });
fireEvent.keyDown(attendeeInput as HTMLElement, { key: 'Enter' });

fireEvent.click(queryByText('Add Registrant') as Element);

await waitFor(() =>
Expand Down Expand Up @@ -234,7 +258,7 @@ describe('Testing Event Registrants Modal', () => {
);

// Choose a user to add as an attendee
const attendeeInput = queryByLabelText('Add a Registrant');
const attendeeInput = queryByLabelText('Add an Registrant');
fireEvent.change(attendeeInput as Element, {
target: { value: 'John Doe' },
});
Expand Down Expand Up @@ -293,7 +317,7 @@ describe('Testing Event Registrants Modal', () => {
});

test('Delete attendee mutation must fail properly', async () => {
const { queryByText, getByLabelText } = render(
const { queryByText, getByTestId } = render(
<MockedProvider
addTypename={false}
mocks={[
Expand Down Expand Up @@ -321,7 +345,7 @@ describe('Testing Event Registrants Modal', () => {

await waitFor(() => expect(queryByText('John Doe')).toBeInTheDocument());

const deleteButton = getByLabelText('Delete');
const deleteButton = getByTestId('CancelIcon');
fireEvent.click(deleteButton);

await waitFor(() =>
Expand All @@ -332,4 +356,48 @@ describe('Testing Event Registrants Modal', () => {
expect(queryByText('Error removing attendee')).toBeInTheDocument(),
);
});
test('Autocomplete functionality works correctly', async () => {
const { getByTitle, getByText, getByPlaceholderText } = render(
<MockedProvider
addTypename={false}
mocks={[...queryMockWithoutRegistrant, ...queryMockWithoutOrgMembers]}
>
<BrowserRouter>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<Provider store={store}>
<I18nextProvider i18n={i18nForTest}>
<ToastContainer />
<EventRegistrantsModal {...props} />
</I18nextProvider>
</Provider>
</LocalizationProvider>
</BrowserRouter>
</MockedProvider>,
);

// 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);
});
});
85 changes: 28 additions & 57 deletions src/components/EventRegistrantsModal/EventRegistrantsModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import { Modal, Button } from 'react-bootstrap';
import { toast } from 'react-toastify';
import { useMutation, useQuery } from '@apollo/client';
Expand All @@ -13,8 +13,8 @@ import Chip from '@mui/material/Chip';
import Stack from '@mui/material/Stack';
import TextField from '@mui/material/TextField';
import Autocomplete from '@mui/material/Autocomplete';
import AddOnSpotAttendee from './AddOnSpotAttendee';
import { useTranslation } from 'react-i18next';
import AddOnSpotAttendee from './AddOnSpotAttendee';

// Props for the EventRegistrantsModal component
type ModalPropType = {
Expand All @@ -30,6 +30,7 @@ interface InterfaceUser {
firstName: string;
lastName: string;
}

/**
* Modal component for managing event registrants.
* Allows adding and removing attendees from an event.
Expand All @@ -43,16 +44,10 @@ interface InterfaceUser {
export const EventRegistrantsModal = (props: ModalPropType): JSX.Element => {
const { eventId, orgId, handleClose, show } = props;
const [member, setMember] = useState<InterfaceUser | null>(null);
const [isAdding, setIsAdding] = useState(false);
const [addRegistrantMutation] = useMutation(ADD_EVENT_ATTENDEE, {
refetchQueries: [
{ query: EVENT_ATTENDEES, variables: { id: eventId } },
{ query: MEMBERS_LIST, variables: { id: orgId } },
],
});
const [open, setOpen] = useState(false);

// Hooks for mutation operations
const [addRegistrantMutation] = useMutation(ADD_EVENT_ATTENDEE);
const [removeRegistrantMutation] = useMutation(REMOVE_EVENT_ATTENDEE);

// Translation hooks
Expand All @@ -70,22 +65,16 @@ export const EventRegistrantsModal = (props: ModalPropType): JSX.Element => {
variables: { id: eventId },
});

const {
data: memberData,
loading: memberLoading,
refetch: memberRefetch,
} = useQuery(MEMBERS_LIST, {
const { data: memberData, loading: memberLoading } = useQuery(MEMBERS_LIST, {
variables: { id: orgId },
pollInterval: 5000,
});

// Function to add a new registrant to the event
const addRegistrant = (): void => {
if (member == null) {
toast.warning('Please choose a user to add first!');
toast.warning('Please choose an user to add first!');
return;
}
setIsAdding(true);
toast.warn('Adding the attendee...');
addRegistrantMutation({
variables: {
Expand All @@ -102,9 +91,6 @@ export const EventRegistrantsModal = (props: ModalPropType): JSX.Element => {
.catch((err) => {
toast.error(t('errorAddingAttendee') as string);
toast.error(err.message);
})
.finally(() => {
setIsAdding(false); // Set loading state to false
});
};

Expand All @@ -129,26 +115,11 @@ export const EventRegistrantsModal = (props: ModalPropType): JSX.Element => {
});
};

useEffect(() => {
if (show) {
const refetchInterval = setInterval(() => {
Promise.all([
attendeesRefetch().catch((err) =>
toast.error(`Failed to refresh attendees list: ${err.message}`),
),
memberRefetch().catch((err) =>
toast.error(`Failed to refresh members list: ${err.message}`),
),
]);
}, 5000);

return () => clearInterval(refetchInterval);
}
}, [show, attendeesRefetch, memberRefetch]);
// Show a loading screen if data is still being fetched
if (attendeesLoading || memberLoading) {
return (
<>
<div className={styles.loader} data-testid="loader"></div>
<div className={styles.loader}></div>
</>
);
}
Expand All @@ -158,39 +129,43 @@ export const EventRegistrantsModal = (props: ModalPropType): JSX.Element => {
<Modal show={show} onHide={handleClose} backdrop="static" centered>
<AddOnSpotAttendee
show={open}
handleClose={() => setOpen(false)}
reloadMembers={() => {
memberRefetch();
attendeesRefetch();
}}
handleClose={
/*istanbul ignore next */
() => setOpen(false)
}
reloadMembers={
/*istanbul ignore next */
() => {
attendeesRefetch();
}
}
/>
<Modal.Header closeButton className="bg-primary">
<Modal.Title className="text-white">Event Registrants</Modal.Title>
</Modal.Header>
<Modal.Body>
<h5 className="mb-2"> Registered Registrants </h5>
{attendeesData.event.attendees.length === 0
? 'There are no registered attendees for this event.'
{attendeesData.event.attendees.length == 0
? `There are no registered attendees for this event.`
: null}
<Stack direction="row" className="flex-wrap gap-2">
{attendeesData?.event?.attendees.map((attendee: InterfaceUser) => (
{attendeesData.event.attendees.map((attendee: InterfaceUser) => (
<Chip
avatar={
<Avatar>{`${attendee.firstName[0]}${attendee.lastName[0]}`}</Avatar>
}
label={`${attendee.firstName} ${attendee.lastName}`}
variant="outlined"
key={attendee._id}
onDelete={() => deleteRegistrant(attendee._id)}
deleteIcon={<span aria-label="Delete">×</span>}
onDelete={(): void => deleteRegistrant(attendee._id)}
/>
))}
</Stack>
<br />

<Autocomplete
id="addRegistrant"
onChange={(_, newMember) => {
onChange={(_, newMember): void => {
setMember(newMember);
}}
noOptionsText={
Expand All @@ -210,24 +185,20 @@ export const EventRegistrantsModal = (props: ModalPropType): JSX.Element => {
getOptionLabel={(member: InterfaceUser): string =>
`${member.firstName} ${member.lastName}`
}
renderInput={(params) => (
renderInput={(params): React.ReactNode => (
<TextField
{...params}
label="Add a Registrant"
data-testid="autocomplete"
label="Add an Registrant"
placeholder="Choose the user that you want to add"
/>
)}
/>
<br />
</Modal.Body>
<Modal.Footer>
<Button
variant="success"
onClick={addRegistrant}
disabled={isAdding}
data-testid="add-registrant-button"
>
{isAdding ? 'Adding...' : 'Add Registrant'}
<Button variant="success" onClick={addRegistrant}>
Add Registrant
</Button>
</Modal.Footer>
</Modal>
Expand Down
5 changes: 1 addition & 4 deletions src/screens/EventManagement/EventManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import VolunteerContainer from 'screens/EventVolunteers/VolunteerContainer';
import EventAgendaItems from 'components/EventManagement/EventAgendaItems/EventAgendaItems';
import useLocalStorage from 'utils/useLocalstorage';
import EventAttendance from 'components/EventManagement/EventAttendance/EventAttendance';
import { EventRegistrantsWrapper } from 'components/EventRegistrantsModal/EventRegistrantsWrapper';
/**
* List of tabs for the event dashboard.
*
Expand Down Expand Up @@ -232,9 +231,7 @@ const EventManagement = (): JSX.Element => {
);
case 'registrants':
return (
<div data-testid="eventRegistrantsTab">
<EventRegistrantsWrapper eventId={eventId} orgId={orgId} />
</div>
<div data-testid="eventRegistrantsTab">Event Registrants</div>
);
case 'attendance':
return (
Expand Down

0 comments on commit f83f61e

Please sign in to comment.