-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: Refactor references to examStore to use the useDispatch and…
… useSelector hooks API. (#131) * feat: rename examStore to specialExams in preparation for use in frontend-app-learning This commit renames the examStore to specialExams. This is in preparation for the use of the exam reducer in the frontend-app-learning React application. * refactor: replace use of context with thunks and Redux store in instructions components This commit replaces the use of the ExamStateContext with the use of thunks and the Redux store in the instructions components. The original pattern was to use the withExamStore higher-order component to provide context to the instructions components. This context contained provided the Redux store state and action creators as props by using the connect API. This posed a problem for our need to merge the frontend-app-learning and frontend-lib-special-exams stores, because the special exams store is initialized in this repository and used by the higher-order component. In order to eventually be able to remove the creation of the store in this repository, we have to remove references to the store by interfacing with the Redux more directly by using the useDispatch and useSelector hooks. * test: remove references to ExamStateProvider from tests for instructions components This commit removes references to the ExamStateProvider from tests for instructions components. Because these components have been refactored to no longe rely on the ExamStateProvider, they are not required in the component tree. refactor: replace use of withExamStore higher-order component in TimerServiceProvider (#133) This commit replaces the use of the withExamStore higher-order component with the useDispatch and useSelector hooks in TimerServiceProvider. This commit also refactors components that use the TimerServiceProvider so that they no longer need to pass state and action creators via props. The TimerServiceProvider now gets whatever state it needs directly from the Redux store with a useSelector hook and imports and dispatches thunks directly by importing them from the data directory. The original pattern was to use the withExamStore higher-order component to provide context to the TimerServiceProvider and its children. This context contained provided the Redux store state and action creators as props by using the connect API. This posed a problem for our need to merge the frontend-app-learning and frontend-lib-special-exams stores, because the special exams store is initialized in this repository and used by the higher-order component. In order to eventually be able to remove the creation of the store in this repository, we have to remove references to the store by interfacing with the Redux more directly by using the useDispatch and useSelector hooks. feat: refactor non instruction components (#132) Refactor public API functions to no longer import and use store. (#134) * test: remove asynchronicity from initializing test store The initializeTestStore test utility function used async...await keywords for initializing the test store, which isn't necessary. This commit removes the async...await keywords and refactors any tests that use that function. * test: fix mocking of getExamAttemptsData function * feat: refactor public API to use hooks instead of references to exported store This commit refactors the public API, used by the frontend-app-learning application to interact with the frontend-lib-special-exams state, to export a series of hooks. Originally, the public API imported the frontend-lib-special-exams store directly and operated on it in a series of exported functions. This posed a problem for our need to merge the frontend-app-learning and frontend-lib-special-exams stores, because the special exams store is initialized in this repository and used by public API. In order to eventually be able to remove the creation of the store in this repository, we have to remove references to the store by interfacing with the Redux more directly by using the useDispatch and useSelector hooks. This commit also exports the root reducer from this library. This root reducer will be imported by the frontend-app-learning application and used to configure its store. fix: patch bugs found during bash (#135)
- Loading branch information
1 parent
7f6f442
commit 434bbf2
Showing
47 changed files
with
740 additions
and
980 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,28 @@ | ||
import { examRequiresAccessToken, store } from './data'; | ||
import { useDispatch, useSelector } from 'react-redux'; | ||
import { examRequiresAccessToken } from './data'; | ||
|
||
export const useIsExam = () => { | ||
const { exam } = useSelector(state => state.specialExams); | ||
|
||
export function isExam() { | ||
const { exam } = store.getState().examState; | ||
return !!exam?.id; | ||
} | ||
}; | ||
|
||
export const useExamAccessToken = () => { | ||
const { exam, examAccessToken } = useSelector(state => state.specialExams); | ||
|
||
export function getExamAccess() { | ||
const { exam, examAccessToken } = store.getState().examState; | ||
if (!exam) { | ||
return ''; | ||
} | ||
|
||
return examAccessToken.exam_access_token; | ||
} | ||
}; | ||
|
||
export const useFetchExamAccessToken = () => { | ||
const { exam } = useSelector(state => state.specialExams); | ||
const dispatch = useDispatch(); | ||
|
||
export async function fetchExamAccess() { | ||
const { exam } = store.getState().examState; | ||
const { dispatch } = store; | ||
if (!exam) { | ||
return Promise.resolve(); | ||
} | ||
return dispatch(examRequiresAccessToken()); | ||
} | ||
return () => dispatch(examRequiresAccessToken()); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,80 @@ | ||
import { Factory } from 'rosie'; | ||
|
||
import { isExam, getExamAccess, fetchExamAccess } from './api'; | ||
import { store } from './data'; | ||
import { useExamAccessToken, useFetchExamAccessToken, useIsExam } from './api'; | ||
import { initializeTestStore, render } from './setupTest'; | ||
|
||
/** | ||
* Hooks must be run in the scope of a component. To run the hook, wrap it in a test component whose sole | ||
* responsibility it is to run the hook and assign it to a return value that is returned by the function. | ||
* @param {*} hook: the hook function to run | ||
* @param {*} store: an initial store, passed to the call to render | ||
* @returns: the return value of the hook | ||
*/ | ||
const getHookReturnValue = (hook, store) => { | ||
let returnVal; | ||
const TestComponent = () => { | ||
returnVal = hook(); | ||
return null; | ||
}; | ||
render(<TestComponent />, { store }); | ||
return returnVal; | ||
}; | ||
|
||
describe('External API integration tests', () => { | ||
describe('Test isExam with exam', () => { | ||
describe('Test useIsExam with exam', () => { | ||
let store; | ||
|
||
beforeAll(() => { | ||
jest.mock('./data'); | ||
const mockExam = Factory.build('exam', { attempt: Factory.build('attempt') }); | ||
const mockToken = Factory.build('examAccessToken'); | ||
const mockState = { examState: { exam: mockExam, examAccessToken: mockToken } }; | ||
store.getState = jest.fn().mockReturnValue(mockState); | ||
const mockState = { specialExams: { exam: mockExam, examAccessToken: mockToken } }; | ||
store = initializeTestStore(mockState); | ||
}); | ||
|
||
afterAll(() => { | ||
jest.clearAllMocks(); | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
it('isExam should return true if exam is set', () => { | ||
expect(isExam()).toBe(true); | ||
it('useIsExam should return true if exam is set', () => { | ||
expect(getHookReturnValue(useIsExam, store)).toBe(true); | ||
}); | ||
|
||
it('getExamAccess should return exam access token if access token', () => { | ||
expect(getExamAccess()).toBeTruthy(); | ||
it('useExamAccessToken should return exam access token if access token', () => { | ||
expect(getHookReturnValue(useExamAccessToken, store)).toBeTruthy(); | ||
}); | ||
|
||
it('fetchExamAccess should dispatch get exam access token', () => { | ||
const dispatchReturn = fetchExamAccess(); | ||
expect(dispatchReturn).toBeInstanceOf(Promise); | ||
it('useFetchExamAccessToken should dispatch get exam access token', () => { | ||
// The useFetchExamAccessToken hook returns a function that calls dispatch, so we must call the returned | ||
// value to invoke dispatch. | ||
expect(getHookReturnValue(useFetchExamAccessToken, store)()).toBeInstanceOf(Promise); | ||
}); | ||
}); | ||
|
||
describe('Test isExam without exam', () => { | ||
describe('Test useIsExam without exam', () => { | ||
let store; | ||
|
||
beforeAll(() => { | ||
jest.mock('./data'); | ||
const mockState = { examState: { exam: null, examAccessToken: null } }; | ||
store.getState = jest.fn().mockReturnValue(mockState); | ||
const mockState = { specialExams: { exam: null, examAccessToken: null } }; | ||
store = initializeTestStore(mockState); | ||
}); | ||
|
||
afterAll(() => { | ||
jest.clearAllMocks(); | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
it('isExam should return false if exam is not set', () => { | ||
expect(isExam()).toBe(false); | ||
it('useIsExam should return false if exam is not set', () => { | ||
expect(getHookReturnValue(useIsExam, store)).toBe(false); | ||
}); | ||
|
||
it('getExamAccess should return empty string if exam access token not set', () => { | ||
expect(getExamAccess()).toBeFalsy(); | ||
it('useExamAccessToken should return empty string if exam access token not set', () => { | ||
expect(getHookReturnValue(useExamAccessToken, store)).toBeFalsy(); | ||
}); | ||
|
||
it('fetchExamAccess should not dispatch get exam access token', () => { | ||
const dispatchReturn = fetchExamAccess(); | ||
expect(dispatchReturn).toBeInstanceOf(Promise); | ||
it('useFetchExamAccessToken should not dispatch get exam access token', () => { | ||
expect(getHookReturnValue(useFetchExamAccessToken, store)).toBeInstanceOf(Promise); | ||
}); | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.