Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 44 additions & 45 deletions src/enrollments/EnrollmentsPage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import React from 'react';
import { render, screen, within } from '@testing-library/react';
import { IntlProvider } from '@openedx/frontend-base';
import EnrollmentsPage from './EnrollmentsPage';
import { EnrolledLearner } from './types';
import { screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import messages from './messages';
import EnrollmentsPage from './EnrollmentsPage';
import { EnrolledLearner } from '@src/enrollments/types';
import messages from '@src/enrollments/messages';
import { useEnrollmentByUserId, useEnrollments, useUpdateEnrollments } from '@src/enrollments/data/apiHook';
import { renderWithAlertAndIntl } from '@src/testUtils';

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: () => ({ courseId: 'test-course-id' }),
}));

jest.mock('./data/apiHook', () => ({
useEnrollments: jest.fn(),
useEnrollmentByUserId: jest.fn(),
useUpdateEnrollments: jest.fn(),
}));

jest.mock('./components/EnrollmentsList', () => {
return function MockEnrollmentsList({ onUnenroll }: { onUnenroll: (learner: EnrolledLearner) => void }) {
Expand All @@ -25,55 +36,43 @@ jest.mock('./components/EnrollmentsList', () => {
};
});

jest.mock('./components/EnrollmentStatusModal', () => {
return function MockEnrollmentStatusModal({ isOpen, onClose }: { isOpen: boolean, onClose: () => void }) {
return isOpen ? (
<div role="dialog">
<button onClick={onClose}>Close Modal</button>
</div>
) : null;
};
});

jest.mock('./components/UnenrollModal', () => {
return function MockUnenrollModal({ isOpen, learner, onClose }: { isOpen: boolean, learner: EnrolledLearner | null, onClose: () => void }) {
return isOpen ? (
<div role="dialog">
<span>Unenroll {learner?.fullName}</span>
<button onClick={onClose}>Close Unenroll Modal</button>
</div>
) : null;
};
});

const renderWithIntl = (component: React.ReactElement) => {
return render(
<IntlProvider locale="en">
{component}
</IntlProvider>
);
};

describe('EnrollmentsPage', () => {
beforeEach(() => {
jest.clearAllMocks();
(useEnrollments as jest.Mock).mockReturnValue({
data: { count: 1, numPages: 1, results: [{ username: 'testuser', fullName: 'Test User', email: 'test@example.com', mode: 'audit', isBetaTester: false }] },
isLoading: false,
});
(useEnrollmentByUserId as jest.Mock).mockReturnValue({
data: { enrollmentStatus: 'enrolled' },
refetch: jest.fn(),
});
(useUpdateEnrollments as jest.Mock).mockReturnValue({
mutate: jest.fn(),
isLoading: false,
error: null,
});
});

it('renders the page title', () => {
renderWithIntl(<EnrollmentsPage />);
renderWithAlertAndIntl(<EnrollmentsPage />);
expect(screen.getByRole('heading', { level: 3 })).toBeInTheDocument();
});

it('renders action buttons', () => {
renderWithIntl(<EnrollmentsPage />);
renderWithAlertAndIntl(<EnrollmentsPage />);
expect(screen.getByRole('button', { name: messages.checkEnrollmentStatus.defaultMessage })).toBeInTheDocument();
expect(screen.getByRole('button', { name: new RegExp(messages.addBetaTesters.defaultMessage) })).toBeInTheDocument();
expect(screen.getByRole('button', { name: new RegExp(messages.enrollLearners.defaultMessage) })).toBeInTheDocument();
});

it('renders EnrollmentsList component', () => {
renderWithIntl(<EnrollmentsPage />);
renderWithAlertAndIntl(<EnrollmentsPage />);
expect(screen.getByRole('table')).toBeInTheDocument();
});

it('opens enrollment status modal when more button is clicked', async () => {
renderWithIntl(<EnrollmentsPage />);
renderWithAlertAndIntl(<EnrollmentsPage />);

const moreButton = screen.getByRole('button', { name: messages.checkEnrollmentStatus.defaultMessage });
const user = userEvent.setup();
Expand All @@ -83,20 +82,20 @@ describe('EnrollmentsPage', () => {
});

it('closes enrollment status modal', async () => {
renderWithIntl(<EnrollmentsPage />);
renderWithAlertAndIntl(<EnrollmentsPage />);

const moreButton = screen.getByRole('button', { name: messages.checkEnrollmentStatus.defaultMessage });
const user = userEvent.setup();
await user.click(moreButton);

const closeButton = screen.getByText('Close Modal');
const closeButton = screen.getByText('Close');
await user.click(closeButton);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});

it('opens unenroll modal when unenroll is triggered', async () => {
renderWithIntl(<EnrollmentsPage />);
renderWithAlertAndIntl(<EnrollmentsPage />);

const unenrollButton = screen.getByText('Unenroll Test Learner');
const user = userEvent.setup();
Expand All @@ -109,20 +108,20 @@ describe('EnrollmentsPage', () => {
});

it('closes unenroll modal and clears selected learner', async () => {
renderWithIntl(<EnrollmentsPage />);
renderWithAlertAndIntl(<EnrollmentsPage />);

const unenrollButton = screen.getByText('Unenroll Test Learner');
const user = userEvent.setup();
await user.click(unenrollButton);

const closeUnenrollButton = screen.getByText('Close Unenroll Modal');
const closeUnenrollButton = screen.getByText('Cancel');
await user.click(closeUnenrollButton);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});

it('modals are closed by default', () => {
renderWithIntl(<EnrollmentsPage />);
renderWithAlertAndIntl(<EnrollmentsPage />);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});
Expand Down
25 changes: 18 additions & 7 deletions src/enrollments/EnrollmentsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import { useState } from 'react';
import { useIntl } from '@openedx/frontend-base';
import { ActionRow, Button, IconButton } from '@openedx/paragon';
import { MoreVert } from '@openedx/paragon/icons';
import messages from './messages';
import EnrollmentsList from './components/EnrollmentsList';
import EnrollmentStatusModal from './components/EnrollmentStatusModal';
import UnenrollModal from './components/UnenrollModal';
import { EnrolledLearner } from './types';
import messages from '@src/enrollments/messages';
import EnrollmentsList from '@src/enrollments/components/EnrollmentsList';
import EnrollmentStatusModal from '@src/enrollments/components/EnrollmentStatusModal';
import UnenrollModal from '@src/enrollments/components/UnenrollModal';
import EnrollLearnersModal from '@src/enrollments/components/EnrollLearnersModal';
import { EnrolledLearner } from '@src/enrollments/types';

const EnrollmentsPage = () => {
const intl = useIntl();
const [isEnrollmentStatusModalOpen, setIsEnrollmentStatusModalOpen] = useState(false);
const [isEnrollLearnersModalOpen, setIsEnrollLearnersModalOpen] = useState(false);
const [isUnenrollModalOpen, setIsUnenrollModalOpen] = useState(false);
const [selectedLearner, setSelectedLearner] = useState<EnrolledLearner | null>(null);

Expand All @@ -32,6 +34,14 @@ const EnrollmentsPage = () => {
setIsEnrollmentStatusModalOpen(false);
};

const handleEnrollLearners = () => {
setIsEnrollLearnersModalOpen(true);
};

const handleCloseEnrollLearnersModal = () => {
setIsEnrollLearnersModalOpen(false);
Comment thread
diana-villalvazo-wgu marked this conversation as resolved.
};

return (
<>
<div className="d-flex justify-content-between align-items-center">
Expand All @@ -44,12 +54,13 @@ const EnrollmentsPage = () => {
onClick={handleMoreButton}
/>
<Button variant="outline-primary">+ {intl.formatMessage(messages.addBetaTesters)}</Button>
<Button>+ {intl.formatMessage(messages.enrollLearners)}</Button>
<Button onClick={handleEnrollLearners}>+ {intl.formatMessage(messages.enrollLearners)}</Button>
</ActionRow>
</div>
<EnrollmentsList onUnenroll={handleUnenroll} />
<EnrollmentStatusModal isOpen={isEnrollmentStatusModalOpen} onClose={handleCloseEnrollmentStatusModal} />
{selectedLearner && <UnenrollModal isOpen={isUnenrollModalOpen} learner={selectedLearner} onClose={handleUnenrollModalClose} />}
{selectedLearner && <UnenrollModal isOpen={isUnenrollModalOpen} learner={selectedLearner} onClose={handleUnenrollModalClose} onSuccess={handleUnenrollModalClose} />}
<EnrollLearnersModal isOpen={isEnrollLearnersModalOpen} onClose={handleCloseEnrollLearnersModal} onSuccess={handleCloseEnrollLearnersModal} />
</>
);
};
Expand Down
Loading
Loading