-
Notifications
You must be signed in to change notification settings - Fork 322
Expand file tree
/
Copy pathuseExamAccess.test.jsx
More file actions
135 lines (107 loc) · 4.97 KB
/
useExamAccess.test.jsx
File metadata and controls
135 lines (107 loc) · 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { useState } from 'react';
import { logError } from '@edx/frontend-platform/logging';
import { act, renderHook } from '@testing-library/react-hooks';
import { useExamAccessToken, useFetchExamAccessToken, useIsExam } from '@edx/frontend-lib-special-exams';
import { initializeMockApp } from '../../../../../setupTest';
import useExamAccess from './useExamAccess';
jest.mock('@edx/frontend-platform/logging', () => ({
logError: jest.fn(),
}));
jest.mock('@edx/frontend-lib-special-exams', () => ({
useExamAccessToken: jest.fn(),
useFetchExamAccessToken: jest.fn(),
useIsExam: jest.fn(() => false),
}));
const id = 'test-id';
// This object allows us to manipulate the value of the accessToken.
const testAccessToken = { curr: '' };
const mockFetchExamAccessToken = jest.fn().mockImplementation(() => Promise.resolve());
useFetchExamAccessToken.mockReturnValue(mockFetchExamAccessToken);
const mockUseIsExam = (initialState = false) => {
const [isExam, setIsExam] = useState(initialState);
// This setTimeout block is intended to replicate the case where a unit is an exam, but
// the call to fetch exam metadata has not yet completed. That is the value of isExam starts
// as false and transitions to true once the call resolves.
if (!initialState) {
setTimeout(
() => setIsExam(true),
500,
);
}
return isExam;
};
describe('useExamAccess hook', () => {
beforeAll(async () => {
// We need to mock AuthService to implicitly use `getAuthenticatedUser` within `AppContext.Provider`.
await initializeMockApp();
});
beforeEach(() => {
jest.clearAllMocks();
jest.useFakeTimers();
useExamAccessToken.mockReset();
useIsExam.mockImplementation(() => mockUseIsExam());
useExamAccessToken.mockImplementation(() => testAccessToken.curr);
});
describe('behavior', () => {
it('returns accessToken and blockAccess and doesn\'t call token API if not an exam', () => {
const { result } = renderHook(() => useExamAccess({ id }));
const { accessToken, blockAccess } = result.current;
expect(accessToken).toEqual('');
expect(blockAccess).toBe(false);
expect(mockFetchExamAccessToken).not.toHaveBeenCalled();
});
it('returns true for blockAccess if an exam but accessToken not yet fetched', async () => {
useIsExam.mockImplementation(() => mockUseIsExam(true));
const { result, waitForNextUpdate } = renderHook(() => useExamAccess({ id }));
const { accessToken, blockAccess } = result.current;
expect(accessToken).toEqual('');
expect(blockAccess).toBe(true);
expect(mockFetchExamAccessToken).toHaveBeenCalled();
// This is to get rid of the act(...) warning.
await act(async () => {
await waitForNextUpdate();
});
});
it('returns false for blockAccess if an exam and accessToken fetch succeeds', async () => {
useIsExam.mockImplementation(() => mockUseIsExam(true));
const { result, waitForNextUpdate } = renderHook(() => useExamAccess({ id }));
// We wait for the promise to resolve and for updates to state to complete so that blockAccess is updated.
await waitForNextUpdate();
const { accessToken, blockAccess } = result.current;
expect(accessToken).toEqual(testAccessToken.curr);
expect(blockAccess).toBe(false);
expect(mockFetchExamAccessToken).toHaveBeenCalled();
});
it('in progress', async () => {
const { result, waitForNextUpdate } = renderHook(() => useExamAccess({ id }));
let { accessToken, blockAccess } = result.current;
expect(accessToken).toEqual('');
expect(blockAccess).toBe(false);
expect(mockFetchExamAccessToken).not.toHaveBeenCalled();
testAccessToken.curr = 'test-access-token';
// The runAllTimers will update the value of isExam, and the waitForNextUpdate will
// wait for call to setBlockAccess in the finally clause of useEffect hook.
await act(async () => {
jest.runAllTimers();
await waitForNextUpdate();
});
({ accessToken, blockAccess } = result.current);
expect(accessToken).toEqual('test-access-token');
expect(blockAccess).toBe(false);
expect(mockFetchExamAccessToken).toHaveBeenCalled();
});
it('returns false for blockAccess if an exam and accessToken fetch fails', async () => {
useIsExam.mockImplementation(() => mockUseIsExam(true));
const testError = 'test-error';
mockFetchExamAccessToken.mockImplementationOnce(() => Promise.reject(testError));
const { result, waitForNextUpdate } = renderHook(() => useExamAccess({ id }));
// We wait for the promise to resolve and for updates to state to complete so that blockAccess is updated.
await waitForNextUpdate();
const { accessToken, blockAccess } = result.current;
expect(accessToken).toEqual(testAccessToken.curr);
expect(blockAccess).toBe(false);
expect(mockFetchExamAccessToken).toHaveBeenCalled();
expect(logError).toHaveBeenCalledWith(testError);
});
});
});