Skip to content

Commit c0b3752

Browse files
test: increase coverage
1 parent 02997f8 commit c0b3752

1 file changed

Lines changed: 300 additions & 0 deletions

File tree

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
import { renderHook, waitFor } from '@testing-library/react';
2+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3+
import { useEnrollments, useEnrollmentByUserId } from './apiHook';
4+
import { getEnrollments, getEnrollmentStatus, PaginationParams } from './api';
5+
6+
jest.mock('./api');
7+
8+
const mockGetEnrollments = getEnrollments as jest.MockedFunction<typeof getEnrollments>;
9+
const mockGetEnrollmentStatus = getEnrollmentStatus as jest.MockedFunction<typeof getEnrollmentStatus>;
10+
11+
const mockEnrollmentsData = {
12+
count: 2,
13+
results: [
14+
{
15+
id: '1',
16+
username: 'student1',
17+
fullName: 'Student One',
18+
email: 'student1@example.com',
19+
track: 'verified',
20+
betaTester: false,
21+
},
22+
{
23+
id: '2',
24+
username: 'student2',
25+
fullName: 'Student Two',
26+
email: 'student2@example.com',
27+
track: 'audit',
28+
betaTester: true,
29+
},
30+
],
31+
};
32+
33+
const mockEnrollmentStatusData = {
34+
status: 'enrolled',
35+
};
36+
37+
const createWrapper = () => {
38+
const queryClient = new QueryClient({
39+
defaultOptions: {
40+
queries: { retry: false },
41+
mutations: { retry: false },
42+
},
43+
});
44+
45+
const Wrapper = ({ children }: { children: React.ReactNode }) => (
46+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
47+
);
48+
49+
Wrapper.displayName = 'TestWrapper';
50+
return Wrapper;
51+
};
52+
53+
describe('enrollments api hooks', () => {
54+
beforeEach(() => {
55+
jest.clearAllMocks();
56+
});
57+
58+
describe('useEnrollments', () => {
59+
const courseId = 'course-v1:edX+Test+2023';
60+
const pagination: PaginationParams = { page: 1, pageSize: 20 };
61+
62+
it('fetches enrollments successfully', async () => {
63+
mockGetEnrollments.mockResolvedValue(mockEnrollmentsData);
64+
65+
const { result } = renderHook(() => useEnrollments(courseId, pagination), {
66+
wrapper: createWrapper(),
67+
});
68+
69+
expect(result.current.isLoading).toBe(true);
70+
71+
await waitFor(() => {
72+
expect(result.current.isSuccess).toBe(true);
73+
});
74+
75+
expect(mockGetEnrollments).toHaveBeenCalledWith(courseId, pagination);
76+
expect(result.current.data).toBe(mockEnrollmentsData);
77+
expect(result.current.error).toBe(null);
78+
});
79+
80+
it('handles API error', async () => {
81+
const mockError = new Error('Network error');
82+
mockGetEnrollments.mockRejectedValue(mockError);
83+
84+
const { result } = renderHook(() => useEnrollments(courseId, pagination), {
85+
wrapper: createWrapper(),
86+
});
87+
88+
await waitFor(() => {
89+
expect(result.current.isError).toBe(true);
90+
});
91+
92+
expect(mockGetEnrollments).toHaveBeenCalledWith(courseId, pagination);
93+
expect(result.current.error).toBe(mockError);
94+
expect(result.current.data).toBe(undefined);
95+
});
96+
97+
it('handles different pagination parameters', async () => {
98+
const customPagination: PaginationParams = { page: 3, pageSize: 50 };
99+
mockGetEnrollments.mockResolvedValue(mockEnrollmentsData);
100+
101+
const { result } = renderHook(() => useEnrollments(courseId, customPagination), {
102+
wrapper: createWrapper(),
103+
});
104+
105+
await waitFor(() => {
106+
expect(result.current.isSuccess).toBe(true);
107+
});
108+
109+
expect(mockGetEnrollments).toHaveBeenCalledWith(courseId, customPagination);
110+
expect(result.current.data).toBe(mockEnrollmentsData);
111+
});
112+
113+
it('handles empty results', async () => {
114+
const emptyResults = { count: 0, results: [] };
115+
mockGetEnrollments.mockResolvedValue(emptyResults);
116+
117+
const { result } = renderHook(() => useEnrollments(courseId, pagination), {
118+
wrapper: createWrapper(),
119+
});
120+
121+
await waitFor(() => {
122+
expect(result.current.isSuccess).toBe(true);
123+
});
124+
125+
expect(result.current.data).toBe(emptyResults);
126+
expect(result.current.data?.count).toBe(0);
127+
expect(result.current.data?.results).toHaveLength(0);
128+
});
129+
130+
it('handles HTTP error responses', async () => {
131+
const httpError = {
132+
response: {
133+
status: 404,
134+
data: { error: 'Course not found' },
135+
},
136+
};
137+
mockGetEnrollments.mockRejectedValue(httpError);
138+
139+
const { result } = renderHook(() => useEnrollments(courseId, pagination), {
140+
wrapper: createWrapper(),
141+
});
142+
143+
await waitFor(() => {
144+
expect(result.current.isError).toBe(true);
145+
});
146+
147+
expect(result.current.error).toBe(httpError);
148+
});
149+
});
150+
151+
describe('useEnrollmentByUserId', () => {
152+
const courseId = 'course-v1:edX+Test+2023';
153+
const userIdentifier = 'student@example.com';
154+
155+
it('fetches enrollment status successfully when enabled', async () => {
156+
mockGetEnrollmentStatus.mockResolvedValue(mockEnrollmentStatusData);
157+
158+
const { result } = renderHook(() => useEnrollmentByUserId(courseId, userIdentifier), {
159+
wrapper: createWrapper(),
160+
});
161+
162+
// Initially should not fetch because enabled is false
163+
expect(result.current.isLoading).toBe(false);
164+
expect(result.current.data).toBe(undefined);
165+
166+
// Manually trigger the query
167+
result.current.refetch();
168+
169+
await waitFor(() => {
170+
expect(result.current.isSuccess).toBe(true);
171+
});
172+
173+
expect(mockGetEnrollmentStatus).toHaveBeenCalledWith(courseId, userIdentifier);
174+
expect(result.current.data).toBe(mockEnrollmentStatusData);
175+
expect(result.current.error).toBe(null);
176+
});
177+
178+
it('is disabled by default', async () => {
179+
mockGetEnrollmentStatus.mockResolvedValue(mockEnrollmentStatusData);
180+
181+
const { result } = renderHook(() => useEnrollmentByUserId(courseId, userIdentifier), {
182+
wrapper: createWrapper(),
183+
});
184+
185+
// Should not automatically fetch
186+
expect(result.current.isLoading).toBe(false);
187+
expect(result.current.data).toBe(undefined);
188+
expect(result.current.isFetched).toBe(false);
189+
190+
// API should not have been called
191+
expect(mockGetEnrollmentStatus).not.toHaveBeenCalled();
192+
});
193+
194+
it('handles API error when manually triggered', async () => {
195+
const mockError = new Error('User not found');
196+
mockGetEnrollmentStatus.mockRejectedValue(mockError);
197+
198+
const { result } = renderHook(() => useEnrollmentByUserId(courseId, userIdentifier), {
199+
wrapper: createWrapper(),
200+
});
201+
202+
// Manually trigger the query
203+
result.current.refetch();
204+
205+
await waitFor(() => {
206+
expect(result.current.isError).toBe(true);
207+
});
208+
209+
expect(mockGetEnrollmentStatus).toHaveBeenCalledWith(courseId, userIdentifier);
210+
expect(result.current.error).toBe(mockError);
211+
expect(result.current.data).toBe(undefined);
212+
});
213+
214+
it('uses correct query key', async () => {
215+
mockGetEnrollmentStatus.mockResolvedValue(mockEnrollmentStatusData);
216+
217+
const { result } = renderHook(() => useEnrollmentByUserId(courseId, userIdentifier), {
218+
wrapper: createWrapper(),
219+
});
220+
221+
// Manually trigger the query
222+
result.current.refetch();
223+
224+
await waitFor(() => {
225+
expect(result.current.isSuccess).toBe(true);
226+
});
227+
228+
// Verify the query was called with correct parameters
229+
expect(mockGetEnrollmentStatus).toHaveBeenCalledWith(courseId, userIdentifier);
230+
expect(result.current.data).toBe(mockEnrollmentStatusData);
231+
});
232+
233+
it('handles different user identifiers', async () => {
234+
const username = 'student123';
235+
mockGetEnrollmentStatus.mockResolvedValue(mockEnrollmentStatusData);
236+
237+
const { result } = renderHook(() => useEnrollmentByUserId(courseId, username), {
238+
wrapper: createWrapper(),
239+
});
240+
241+
// Manually trigger the query
242+
result.current.refetch();
243+
244+
await waitFor(() => {
245+
expect(result.current.isSuccess).toBe(true);
246+
});
247+
248+
expect(mockGetEnrollmentStatus).toHaveBeenCalledWith(courseId, username);
249+
expect(result.current.data).toBe(mockEnrollmentStatusData);
250+
});
251+
252+
it('handles different enrollment statuses', async () => {
253+
const statuses = ['enrolled', 'unenrolled', 'pending'];
254+
255+
for (const status of statuses) {
256+
const statusData = { status };
257+
mockGetEnrollmentStatus.mockResolvedValue(statusData);
258+
259+
const { result } = renderHook(() => useEnrollmentByUserId(courseId, userIdentifier), {
260+
wrapper: createWrapper(),
261+
});
262+
263+
// Manually trigger the query
264+
result.current.refetch();
265+
266+
await waitFor(() => {
267+
expect(result.current.isSuccess).toBe(true);
268+
});
269+
270+
expect(result.current.data?.status).toBe(status);
271+
272+
// Clear mock for next iteration
273+
jest.clearAllMocks();
274+
}
275+
});
276+
277+
it('handles HTTP error responses', async () => {
278+
const httpError = {
279+
response: {
280+
status: 404,
281+
data: { error: 'User not found in course' },
282+
},
283+
};
284+
mockGetEnrollmentStatus.mockRejectedValue(httpError);
285+
286+
const { result } = renderHook(() => useEnrollmentByUserId(courseId, userIdentifier), {
287+
wrapper: createWrapper(),
288+
});
289+
290+
// Manually trigger the query
291+
result.current.refetch();
292+
293+
await waitFor(() => {
294+
expect(result.current.isError).toBe(true);
295+
});
296+
297+
expect(result.current.error).toBe(httpError);
298+
});
299+
});
300+
});

0 commit comments

Comments
 (0)