Skip to content

Commit 169d610

Browse files
feat: add/unenroll learners
1 parent 82c29e5 commit 169d610

File tree

10 files changed

+717
-57
lines changed

10 files changed

+717
-57
lines changed

src/enrollments/EnrollmentsPage.test.tsx

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1-
import React from 'react';
2-
import { render, screen, within } from '@testing-library/react';
3-
import { IntlProvider } from '@openedx/frontend-base';
1+
import { screen, within } from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
43
import EnrollmentsPage from './EnrollmentsPage';
54
import { Learner } from './types';
6-
import userEvent from '@testing-library/user-event';
75
import messages from './messages';
6+
import { useEnrollmentByUserId, useEnrollments, useEnrollLearners, useUnenrollLearners } from './data/apiHook';
7+
import { renderWithAlertAndIntl } from '@src/testUtils';
8+
9+
jest.mock('react-router-dom', () => ({
10+
...jest.requireActual('react-router-dom'),
11+
useParams: () => ({ courseId: 'test-course-id' }),
12+
}));
13+
14+
jest.mock('./data/apiHook', () => ({
15+
useEnrollments: jest.fn(),
16+
useEnrollmentByUserId: jest.fn(),
17+
useEnrollLearners: jest.fn(),
18+
useUnenrollLearners: jest.fn(),
19+
}));
820

921
// Mock the child components
1022
jest.mock('./components/EnrollmentsList', () => {
@@ -26,55 +38,46 @@ jest.mock('./components/EnrollmentsList', () => {
2638
};
2739
});
2840

29-
jest.mock('./components/EnrollmentStatusModal', () => {
30-
return function MockEnrollmentStatusModal({ isOpen, onClose }: { isOpen: boolean, onClose: () => void }) {
31-
return isOpen ? (
32-
<div role="dialog">
33-
<button onClick={onClose}>Close Modal</button>
34-
</div>
35-
) : null;
36-
};
37-
});
38-
39-
jest.mock('./components/UnenrollModal', () => {
40-
return function MockUnenrollModal({ isOpen, learner, onClose }: { isOpen: boolean, learner: Learner | null, onClose: () => void }) {
41-
return isOpen ? (
42-
<div role="dialog">
43-
<span>Unenroll {learner?.fullName}</span>
44-
<button onClick={onClose}>Close Unenroll Modal</button>
45-
</div>
46-
) : null;
47-
};
48-
});
49-
50-
const renderWithIntl = (component: React.ReactElement) => {
51-
return render(
52-
<IntlProvider locale="en">
53-
{component}
54-
</IntlProvider>
55-
);
56-
};
57-
5841
describe('EnrollmentsPage', () => {
42+
beforeAll(() => {
43+
(useEnrollments as jest.Mock).mockReturnValue({
44+
data: { count: 1, numPages: 1, results: [{ username: 'testuser', fullName: 'Test User', email: 'test@example.com', mode: 'audit', isBetaTester: false }] },
45+
isLoading: false,
46+
});
47+
(useEnrollmentByUserId as jest.Mock).mockReturnValue({
48+
data: { enrollmentStatus: 'enrolled' },
49+
refetch: jest.fn(),
50+
});
51+
(useEnrollLearners as jest.Mock).mockReturnValue({
52+
mutate: jest.fn(),
53+
isLoading: false,
54+
error: null,
55+
});
56+
(useUnenrollLearners as jest.Mock).mockReturnValue({
57+
mutate: jest.fn(),
58+
isPending: false,
59+
});
60+
});
61+
5962
it('renders the page title', () => {
60-
renderWithIntl(<EnrollmentsPage />);
63+
renderWithAlertAndIntl(<EnrollmentsPage />);
6164
expect(screen.getByRole('heading', { level: 3 })).toBeInTheDocument();
6265
});
6366

6467
it('renders action buttons', () => {
65-
renderWithIntl(<EnrollmentsPage />);
68+
renderWithAlertAndIntl(<EnrollmentsPage />);
6669
expect(screen.getByRole('button', { name: messages.checkEnrollmentStatus.defaultMessage })).toBeInTheDocument();
6770
expect(screen.getByRole('button', { name: new RegExp(messages.addBetaTesters.defaultMessage) })).toBeInTheDocument();
6871
expect(screen.getByRole('button', { name: new RegExp(messages.enrollLearners.defaultMessage) })).toBeInTheDocument();
6972
});
7073

7174
it('renders EnrollmentsList component', () => {
72-
renderWithIntl(<EnrollmentsPage />);
75+
renderWithAlertAndIntl(<EnrollmentsPage />);
7376
expect(screen.getByRole('table')).toBeInTheDocument();
7477
});
7578

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

7982
const moreButton = screen.getByRole('button', { name: messages.checkEnrollmentStatus.defaultMessage });
8083
const user = userEvent.setup();
@@ -84,20 +87,20 @@ describe('EnrollmentsPage', () => {
8487
});
8588

8689
it('closes enrollment status modal', async () => {
87-
renderWithIntl(<EnrollmentsPage />);
90+
renderWithAlertAndIntl(<EnrollmentsPage />);
8891

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

93-
const closeButton = screen.getByText('Close Modal');
96+
const closeButton = screen.getByText('Close');
9497
await user.click(closeButton);
9598

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

99102
it('opens unenroll modal when unenroll is triggered', async () => {
100-
renderWithIntl(<EnrollmentsPage />);
103+
renderWithAlertAndIntl(<EnrollmentsPage />);
101104

102105
const unenrollButton = screen.getByText('Unenroll Test Learner');
103106
const user = userEvent.setup();
@@ -110,20 +113,20 @@ describe('EnrollmentsPage', () => {
110113
});
111114

112115
it('closes unenroll modal and clears selected learner', async () => {
113-
renderWithIntl(<EnrollmentsPage />);
116+
renderWithAlertAndIntl(<EnrollmentsPage />);
114117

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

119-
const closeUnenrollButton = screen.getByText('Close Unenroll Modal');
122+
const closeUnenrollButton = screen.getByText('Cancel');
120123
await user.click(closeUnenrollButton);
121124

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

125128
it('modals are closed by default', () => {
126-
renderWithIntl(<EnrollmentsPage />);
129+
renderWithAlertAndIntl(<EnrollmentsPage />);
127130

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

src/enrollments/EnrollmentsPage.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import messages from './messages';
66
import EnrollmentsList from './components/EnrollmentsList';
77
import EnrollmentStatusModal from './components/EnrollmentStatusModal';
88
import UnenrollModal from './components/UnenrollModal';
9+
import EnrollLearnersModal from './components/EnrollLearnersModal';
910
import { Learner } from './types';
1011

1112
const EnrollmentsPage = () => {
1213
const intl = useIntl();
1314
const [isEnrollmentStatusModalOpen, setIsEnrollmentStatusModalOpen] = useState(false);
15+
const [isEnrollLearnersModalOpen, setIsEnrollLearnersModalOpen] = useState(false);
1416
const [isUnenrollModalOpen, setIsUnenrollModalOpen] = useState(false);
1517
const [selectedLearner, setSelectedLearner] = useState<Learner | null>(null);
1618

@@ -32,6 +34,10 @@ const EnrollmentsPage = () => {
3234
setIsEnrollmentStatusModalOpen(false);
3335
};
3436

37+
const handleEnrollLearners = () => {
38+
setIsEnrollLearnersModalOpen(true);
39+
};
40+
3541
return (
3642
<>
3743
<div className="d-flex justify-content-between align-items-center">
@@ -44,12 +50,13 @@ const EnrollmentsPage = () => {
4450
onClick={handleMoreButton}
4551
/>
4652
<Button variant="outline-primary">+ {intl.formatMessage(messages.addBetaTesters)}</Button>
47-
<Button>+ {intl.formatMessage(messages.enrollLearners)}</Button>
53+
<Button onClick={handleEnrollLearners}>+ {intl.formatMessage(messages.enrollLearners)}</Button>
4854
</ActionRow>
4955
</div>
5056
<EnrollmentsList onUnenroll={handleUnenroll} />
5157
<EnrollmentStatusModal isOpen={isEnrollmentStatusModalOpen} onClose={handleCloseEnrollmentStatusModal} />
52-
{selectedLearner && <UnenrollModal isOpen={isUnenrollModalOpen} learner={selectedLearner} onClose={handleUnenrollModalClose} />}
58+
{selectedLearner && <UnenrollModal isOpen={isUnenrollModalOpen} learner={selectedLearner} onClose={handleUnenrollModalClose} onSuccess={() => {}} />}
59+
<EnrollLearnersModal isOpen={isEnrollLearnersModalOpen} onClose={() => setIsEnrollLearnersModalOpen(false)} onSuccess={() => {}} />
5360
</>
5461
);
5562
};

0 commit comments

Comments
 (0)