chore: renderer test coverage (#103)
* chore: renderer test coverage * fix: lint * chore: api tests * chore: tests for app reducer and StartGradeButton * chore: lint * fix: update reducer tests * chore: more test coverage * chore: test coverage * chore: update test for merge conflicts
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const routePath = `${getConfig().PUBLIC_PATH}:courseId`;
|
||||
export const locationId = window.location.pathname.slice(1);
|
||||
|
||||
24
src/data/constants/app.test.js
Normal file
24
src/data/constants/app.test.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import * as platform from '@edx/frontend-platform';
|
||||
import * as constants from './app';
|
||||
|
||||
jest.unmock('./app');
|
||||
|
||||
jest.mock('@edx/frontend-platform', () => {
|
||||
const PUBLIC_PATH = 'test-public-path';
|
||||
return {
|
||||
getConfig: () => ({ PUBLIC_PATH }),
|
||||
PUBLIC_PATH,
|
||||
};
|
||||
});
|
||||
|
||||
describe('app constants', () => {
|
||||
test('route path draws from public path and adds courseId', () => {
|
||||
expect(constants.routePath).toEqual(`${platform.PUBLIC_PATH}:courseId`);
|
||||
});
|
||||
test('locationId returns trimmed pathname', () => {
|
||||
const old = window.location;
|
||||
window.location = { pathName: '/somePath.jpg' };
|
||||
expect(constants.locationId).toEqual(window.location.pathname.slice(1));
|
||||
window.location = old;
|
||||
});
|
||||
});
|
||||
73
src/data/redux/app/reducer.test.js
Normal file
73
src/data/redux/app/reducer.test.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import { initialState, reducer, actions } from './reducer';
|
||||
|
||||
describe('app reducer', () => {
|
||||
describe('initialState', () => {
|
||||
test('populated, but empty course metadata', () => {
|
||||
const data = initialState.courseMetadata;
|
||||
expect(data.name).toEqual('');
|
||||
expect(data.number).toEqual('');
|
||||
expect(data.org).toEqual('');
|
||||
expect(data.courseId).toEqual('');
|
||||
});
|
||||
test('disabled (waffle flag)', () => {
|
||||
expect(initialState.isEnabled).toEqual(false);
|
||||
});
|
||||
test('not grading', () => {
|
||||
expect(initialState.isGrading).toEqual(false);
|
||||
});
|
||||
test('populated, but empty ora metadata', () => {
|
||||
const data = initialState.oraMetadata;
|
||||
expect(data.prompt).toEqual('');
|
||||
expect(data.name).toEqual('');
|
||||
expect(data.type).toEqual('');
|
||||
expect(data.rubricConfig).toEqual(null);
|
||||
});
|
||||
test('not showing review', () => {
|
||||
expect(initialState.showReview).toEqual(false);
|
||||
});
|
||||
test('not showing rubric', () => {
|
||||
expect(initialState.showRubric).toEqual(false);
|
||||
});
|
||||
});
|
||||
describe('reducers', () => {
|
||||
it('returns initial state', () => {
|
||||
expect(reducer(undefined, {})).toEqual(initialState);
|
||||
});
|
||||
const testState = {
|
||||
...initialState,
|
||||
showRubric: true,
|
||||
showReview: true,
|
||||
arbitrary: 'state',
|
||||
};
|
||||
const testValue = 'my-test-value';
|
||||
const testAction = (action, expected) => {
|
||||
expect(reducer(testState, action)).toEqual({
|
||||
...testState,
|
||||
...expected,
|
||||
});
|
||||
};
|
||||
describe('action handlers', () => {
|
||||
test('loadIsEnabled loads isEnabled from payload', () => {
|
||||
testAction(actions.loadIsEnabled(testValue), { isEnabled: testValue });
|
||||
});
|
||||
test('loadCourseMetadata loads courseMetadata from payload', () => {
|
||||
testAction(actions.loadCourseMetadata(testValue), { courseMetadata: testValue });
|
||||
});
|
||||
test('loadOraMetadata loads oraMetadata from payload', () => {
|
||||
testAction(actions.loadOraMetadata(testValue), { oraMetadata: testValue });
|
||||
});
|
||||
describe('setShowReview', () => {
|
||||
it('loads showReview, sets showRubric to false if set to false', () => {
|
||||
testAction(actions.setShowReview(true), { showReview: true });
|
||||
testAction(actions.setShowReview(false), { showReview: false, showRubric: false });
|
||||
});
|
||||
});
|
||||
test('setShowRubric loads showRubric from payload', () => {
|
||||
testAction(actions.setShowRubric(testValue), { showRubric: testValue });
|
||||
});
|
||||
test('toggleShowRubric toggles showRubric value', () => {
|
||||
testAction(actions.toggleShowRubric(), { showRubric: !testState.showRubric });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,7 @@ import { createSlice } from '@reduxjs/toolkit';
|
||||
import { StrictDict } from 'utils';
|
||||
|
||||
import { lockStatuses } from 'data/services/lms/constants';
|
||||
import * as module from './reducer';
|
||||
|
||||
const initialState = {
|
||||
selection: [
|
||||
@@ -60,26 +61,6 @@ const initialState = {
|
||||
next: null, // { response }
|
||||
};
|
||||
|
||||
export const updateGradeData = (state, data) => ({
|
||||
...state,
|
||||
gradeData: {
|
||||
...state.gradeData,
|
||||
[state.current.submissionUUID]: { ...data },
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Updates the given state's gradeData entry for the seleted submission.
|
||||
* @return {object} - new state
|
||||
*/
|
||||
export const loadGradeData = (state, data) => ({
|
||||
...state,
|
||||
gradeData: {
|
||||
...state.gradeData,
|
||||
[state.current.submissionUUID]: { ...data },
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Updates the state's gradingData entry for the seleted submission,
|
||||
* overlaying the passed data on top of the existing data for the that
|
||||
@@ -101,7 +82,7 @@ export const updateGradingData = (state, data) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the given state's localGradeData entry for the seleted submission,
|
||||
* Updates the given state's gradingData entry for the seleted submission,
|
||||
* overlaying the passed data on top of the existing data for the criterion
|
||||
* at the given index (orderNum) for the rubric.
|
||||
* @return {object} - new state
|
||||
@@ -110,7 +91,7 @@ export const updateCriterion = (state, orderNum, data) => {
|
||||
const entry = state.gradingData[state.current.submissionUUID];
|
||||
const criteria = [...entry.criteria];
|
||||
criteria[orderNum] = { ...entry.criteria[orderNum], ...data };
|
||||
return updateGradingData(state, {
|
||||
return module.updateGradingData(state, {
|
||||
...entry,
|
||||
criteria,
|
||||
});
|
||||
@@ -166,16 +147,16 @@ const grading = createSlice({
|
||||
current: { ...state.current, lockStatus: payload.lockStatus },
|
||||
}),
|
||||
setRubricFeedback: (state, { payload }) => (
|
||||
updateGradingData(state, { overallFeedback: payload })
|
||||
module.updateGradingData(state, { overallFeedback: payload })
|
||||
),
|
||||
setCriterionOption: (state, { payload: { orderNum, value } }) => (
|
||||
updateCriterion(state, orderNum, { selectedOption: value })
|
||||
module.updateCriterion(state, orderNum, { selectedOption: value })
|
||||
),
|
||||
setCriterionFeedback: (state, { payload: { orderNum, value } }) => (
|
||||
updateCriterion(state, orderNum, { feedback: value })
|
||||
module.updateCriterion(state, orderNum, { feedback: value })
|
||||
),
|
||||
setShowValidation: (state, { payload }) => (
|
||||
updateGradingData(state, { showValidation: payload })
|
||||
module.updateGradingData(state, { showValidation: payload })
|
||||
),
|
||||
completeGrading: (state, { payload }) => {
|
||||
const gradingData = { ...state.gradingData };
|
||||
@@ -194,39 +175,23 @@ const grading = createSlice({
|
||||
},
|
||||
};
|
||||
},
|
||||
loadStatus: (state, { payload }) => {
|
||||
const gradingData = { ...state.gradingData };
|
||||
delete gradingData[state.current.submissionUUID];
|
||||
return {
|
||||
...state,
|
||||
gradeData: {
|
||||
...state.gradeData,
|
||||
[state.current.submissionUUID]: { ...payload.gradeData },
|
||||
},
|
||||
gradingData,
|
||||
current: {
|
||||
...state.current,
|
||||
gradeStatus: payload.gradeStatus,
|
||||
lockStatus: payload.lockStatus,
|
||||
},
|
||||
};
|
||||
},
|
||||
stopGrading: (state, { payload }) => {
|
||||
const { submissionUUID } = state.current;
|
||||
const localGradeData = { ...state.localGradeData };
|
||||
delete localGradeData[submissionUUID];
|
||||
const gradeData = { ...state.gradeData };
|
||||
let lockStatus = lockStatuses.unlocked;
|
||||
let { gradeStatus } = state.current;
|
||||
if (payload) {
|
||||
const { submissionStatus } = payload;
|
||||
gradeData[submissionUUID] = submissionStatus.gradeData;
|
||||
lockStatus = submissionStatus.lockStatus;
|
||||
gradeStatus = submissionStatus.gradeStatus;
|
||||
}
|
||||
const gradingData = { ...state.gradingData };
|
||||
delete gradingData[submissionUUID];
|
||||
|
||||
const gradeData = {
|
||||
...state.gradeData,
|
||||
...(payload && { [submissionUUID]: payload.submissionStatus.gradeData }),
|
||||
};
|
||||
|
||||
const { gradeStatus } = payload ? payload.submissionStatus : state.current;
|
||||
const lockStatus = payload ? payload.submissionStatus.lockStatus : lockStatuses.unlocked;
|
||||
|
||||
return {
|
||||
...state,
|
||||
localGradeData,
|
||||
gradingData,
|
||||
gradeData,
|
||||
current: {
|
||||
...state.current,
|
||||
lockStatus,
|
||||
|
||||
285
src/data/redux/grading/reducer.test.js
Normal file
285
src/data/redux/grading/reducer.test.js
Normal file
@@ -0,0 +1,285 @@
|
||||
import { keyStore } from 'utils';
|
||||
import { lockStatuses } from 'data/services/lms/constants';
|
||||
|
||||
import * as module from './reducer';
|
||||
|
||||
const {
|
||||
initialState,
|
||||
|
||||
updateGradingData,
|
||||
updateCriterion,
|
||||
|
||||
reducer,
|
||||
actions,
|
||||
} = module;
|
||||
|
||||
const moduleKeys = keyStore(module);
|
||||
|
||||
describe('app reducer', () => {
|
||||
describe('initialState', () => {
|
||||
test('empty selection list', () => {
|
||||
expect(initialState.selection).toEqual([]);
|
||||
});
|
||||
test('empty gradeData object', () => {
|
||||
expect(initialState.gradeData).toEqual({});
|
||||
});
|
||||
test('empty gradingData object', () => {
|
||||
expect(initialState.gradingData).toEqual({});
|
||||
});
|
||||
test('null activeIndex', () => {
|
||||
expect(initialState.activeIndex).toEqual(null);
|
||||
});
|
||||
test('empty current object', () => {
|
||||
expect(initialState.current).toEqual({});
|
||||
});
|
||||
test('null prev pointer', () => {
|
||||
expect(initialState.prev).toEqual(null);
|
||||
});
|
||||
test('null next pointer', () => {
|
||||
expect(initialState.next).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
const submissionUUID = 'test-submission-uuid';
|
||||
const orderNum = 1;
|
||||
const criterion = { unique: 'criterion-data' };
|
||||
const criteria = [{ some: 'test-data' }, criterion, { other: 'fake-data' }];
|
||||
const baseGradingData = { fakeID: { some: 'test-data' } };
|
||||
const gradingData = { unique: 'submission-grading-data', criteria };
|
||||
const lockStatus = 'test-lock-status';
|
||||
const gradeStatus = 'test-grade-status';
|
||||
const testState = {
|
||||
current: { submissionUUID },
|
||||
gradingData: { ...baseGradingData, [submissionUUID]: gradingData },
|
||||
gradeData: { ...baseGradingData, [submissionUUID]: gradingData },
|
||||
activeIndex: 12,
|
||||
};
|
||||
const testValue = 'my-test-value';
|
||||
const testData = { unique: 'test-data' };
|
||||
describe('helpers', () => {
|
||||
describe('updateGradingData', () => {
|
||||
it('returns new state with new grading data for current submission added to model', () => {
|
||||
expect(updateGradingData(testState, testData)).toEqual({
|
||||
...testState,
|
||||
gradingData: {
|
||||
...baseGradingData,
|
||||
[submissionUUID]: { ...gradingData, ...testData },
|
||||
},
|
||||
});
|
||||
expect(
|
||||
updateGradingData({ ...testState, gradingData: baseGradingData }, testData),
|
||||
).toEqual({
|
||||
...testState,
|
||||
gradingData: {
|
||||
...baseGradingData,
|
||||
[submissionUUID]: testData,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('updateCriterion', () => {
|
||||
it('overlays the given data on a given criterion field', () => {
|
||||
const mocks = {
|
||||
updateGradingData: (...args) => ({ updateGradingData: args }),
|
||||
};
|
||||
jest.spyOn(module, moduleKeys.updateGradingData)
|
||||
.mockImplementationOnce(mocks.updateGradingData);
|
||||
expect(updateCriterion(testState, orderNum, testData)).toEqual(
|
||||
mocks.updateGradingData(testState, {
|
||||
...gradingData,
|
||||
criteria: [criteria[0], { ...criterion, ...testData }, criteria[2]],
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('reducers', () => {
|
||||
it('returns initial state', () => {
|
||||
expect(reducer(undefined, {})).toEqual(initialState);
|
||||
});
|
||||
describe('action handlers', () => {
|
||||
describe('loadSubmission', () => {
|
||||
it('loads payload to current and overlays current grade data', () => {
|
||||
const payload = { submissionUUID, gradeData: testData };
|
||||
expect(reducer(testState, actions.loadSubmission(payload))).toEqual({
|
||||
...testState,
|
||||
current: payload,
|
||||
gradeData: {
|
||||
...testState.gradeData,
|
||||
[submissionUUID]: testData,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('loadNext', () => {
|
||||
it('clears current and increments activeIndex', () => {
|
||||
expect(reducer(testState, actions.loadNext())).toEqual({
|
||||
...testState,
|
||||
current: {},
|
||||
activeIndex: testState.activeIndex + 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('loadPrev', () => {
|
||||
it('clears current and decrements activeIndex', () => {
|
||||
expect(reducer(testState, actions.loadPrev())).toEqual({
|
||||
...testState,
|
||||
current: {},
|
||||
activeIndex: testState.activeIndex - 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('updateSelection', () => {
|
||||
it('loads selection from payload and sets activeIndex to 0', () => {
|
||||
expect(reducer(testState, actions.updateSelection(testData))).toEqual({
|
||||
...testState,
|
||||
selection: testData,
|
||||
activeIndex: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('startGrading', () => {
|
||||
describe('resulting state', () => {
|
||||
const action = actions.startGrading({ lockStatus, gradeData: testData });
|
||||
test('loads current lockStatus from payload', () => {
|
||||
expect(reducer(testState, action).current).toEqual({
|
||||
...testState.current,
|
||||
lockStatus,
|
||||
});
|
||||
});
|
||||
test('loads selected gradeData from payload', () => {
|
||||
expect(reducer(testState, action).gradeData).toEqual({
|
||||
...testState.gradeData,
|
||||
[submissionUUID]: testData,
|
||||
});
|
||||
});
|
||||
test('loads gradingData w/ showValidation: false, overlaying on existing data', () => {
|
||||
expect(reducer(testState, action).gradingData).toEqual({
|
||||
...testState.gradingData,
|
||||
[submissionUUID]: {
|
||||
showValidation: false,
|
||||
...testData,
|
||||
...gradingData,
|
||||
},
|
||||
});
|
||||
expect(reducer({ ...testState, gradingData: {} }, action).gradingData).toEqual({
|
||||
[submissionUUID]: { showValidation: false, ...testData },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('failSetLock', () => {
|
||||
it('loads lockStatus from payload', () => {
|
||||
expect(reducer(testState, actions.failSetLock({ lockStatus: testValue }))).toEqual({
|
||||
...testState,
|
||||
current: { ...testState.current, lockStatus: testValue },
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('gradingData updaters', () => {
|
||||
const mocks = {
|
||||
updateGradingData: args => ({ updateGradingData: args }),
|
||||
};
|
||||
beforeEach(() => {
|
||||
jest.spyOn(module, moduleKeys.updateGradingData)
|
||||
.mockImplementationOnce(mocks.updateGradingData);
|
||||
});
|
||||
describe('setRubricFeedback', () => {
|
||||
it('loads overallFeedback from payload', () => {
|
||||
expect(reducer(testState, actions.setRubricFeedback(testValue))).toEqual(
|
||||
mocks.updateGradingData(testState, { overallFeedback: testValue }),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('setShowValidation', () => {
|
||||
it('loads showValidation from payload', () => {
|
||||
expect(reducer(testState, actions.setShowValidation(testValue))).toEqual(
|
||||
mocks.updateGradingData(testState, { showValidation: testValue }),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('criterion updaters', () => {
|
||||
const mocks = {
|
||||
updateCriterion: args => ({ updateCriterion: args }),
|
||||
};
|
||||
beforeEach(() => {
|
||||
jest.spyOn(module, moduleKeys.updateCriterion)
|
||||
.mockImplementationOnce(mocks.updateCriterion);
|
||||
});
|
||||
const args = { orderNum, value: testValue };
|
||||
describe('setCriterionOption', () => {
|
||||
it('loads selectedOption by orderNum', () => {
|
||||
expect(reducer(testState, actions.setCriterionOption(testState, args))).toEqual(
|
||||
mocks.updateCriterion(testState, orderNum, { selectedOption: testValue }),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('setCriterionFeedback', () => {
|
||||
it('loads feedback by orderNum', () => {
|
||||
expect(reducer(testState, actions.setCriterionFeedback(testState, args))).toEqual(
|
||||
mocks.updateCriterion(testState, orderNum, { feecback: testValue }),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('completeGrading', () => {
|
||||
describe('resulting state', () => {
|
||||
const payload = { gradeData: testData, lockStatus, gradeStatus };
|
||||
let output;
|
||||
beforeAll(() => {
|
||||
output = reducer(testState, actions.completeGrading(payload));
|
||||
});
|
||||
test('gradeData: loads gradeData from payload', () => {
|
||||
expect(output.gradeData).toEqual({
|
||||
...testState.gradeData,
|
||||
[submissionUUID]: testData,
|
||||
});
|
||||
});
|
||||
test('gradingData: deletes current data', () => {
|
||||
expect(output.gradingData).toEqual(baseGradingData);
|
||||
});
|
||||
test('current: loads gradeStatus and lockStatus from payload', () => {
|
||||
expect(output.current).toEqual({
|
||||
...testState.current,
|
||||
lockStatus,
|
||||
gradeStatus,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('stopGrading', () => {
|
||||
let output;
|
||||
const args = { submissionStatus: { gradeData: testData, lockStatus, gradeStatus } };
|
||||
describe('resulting state', () => {
|
||||
test('gradingData: deletes current data', () => {
|
||||
output = reducer(testState, actions.stopGrading());
|
||||
expect(output.gradingData).toEqual(baseGradingData);
|
||||
});
|
||||
test('gradeData: appends payload.submissionStatus.gradeData if passed', () => {
|
||||
output = reducer(testState, actions.stopGrading());
|
||||
expect(output.gradeData).toEqual(testState.gradeData);
|
||||
output = reducer(testState, actions.stopGrading(args));
|
||||
expect(output.gradeData).toEqual({
|
||||
...testState.gradeData,
|
||||
[submissionUUID]: testData,
|
||||
});
|
||||
});
|
||||
describe('current: loads lockStatus and gradeStatus', () => {
|
||||
test('defaults to state.current.gradeStatus and unlocked', () => {
|
||||
output = reducer(testState, actions.stopGrading());
|
||||
expect(output.current).toEqual({
|
||||
...testState.current,
|
||||
lockStatus: lockStatuses.unlocked,
|
||||
});
|
||||
});
|
||||
test('loads from payload is passed', () => {
|
||||
output = reducer(testState, actions.stopGrading(args));
|
||||
expect(output.current).toEqual({ ...testState.current, lockStatus, gradeStatus });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
78
src/data/redux/requests/selectors.test.js
Normal file
78
src/data/redux/requests/selectors.test.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import { RequestStates } from 'data/constants/requests';
|
||||
|
||||
import selectors from './selectors';
|
||||
|
||||
jest.mock('reselect', () => ({
|
||||
createSelector: jest.fn((preSelectors, cb) => ({ preSelectors, cb })),
|
||||
}));
|
||||
|
||||
const requestKey = 'my-test-request-key';
|
||||
const requestData = { some: 'request-data' };
|
||||
const inactiveRequest = { status: RequestStates.inactive, some: 'request-data' };
|
||||
const pendingRequest = { status: RequestStates.pending, some: 'request-data' };
|
||||
const completedRequest = { status: RequestStates.completed, some: 'request-data' };
|
||||
const failedRequest = { status: RequestStates.failed, some: 'request-data' };
|
||||
|
||||
const testValue = 'my-test-value';
|
||||
|
||||
const testState = {
|
||||
requests: {
|
||||
[requestKey]: requestData,
|
||||
},
|
||||
};
|
||||
const genRequests = (request) => ({
|
||||
requests: { [requestKey]: request },
|
||||
});
|
||||
const select = (selector, request) => (
|
||||
selector(genRequests(request), { requestKey })
|
||||
);
|
||||
describe('requests selectors unit tests', () => {
|
||||
test('requestStatus returns data associated with given key', () => {
|
||||
expect(selectors.requestStatus(testState, { requestKey })).toEqual(requestData);
|
||||
});
|
||||
describe('allowNavigation', () => {
|
||||
it('returns false if any requests are pending', () => {
|
||||
expect(selectors.allowNavigation(testState)).toEqual(true);
|
||||
expect(selectors.allowNavigation({ requests: { key1: pendingRequest } })).toEqual(false);
|
||||
});
|
||||
});
|
||||
const testStatusSelector = (selector, matchingRequest) => {
|
||||
expect(selector(testState, { requestKey })).toEqual(false);
|
||||
expect(selector(
|
||||
{ requests: { [requestKey]: matchingRequest } },
|
||||
{ requestKey },
|
||||
)).toEqual(true);
|
||||
};
|
||||
test('isInactive returns true iff the given request is inactive', () => {
|
||||
testStatusSelector(selectors.isInactive, inactiveRequest);
|
||||
});
|
||||
test('isPending returns true iff the given request is pending', () => {
|
||||
testStatusSelector(selectors.isPending, pendingRequest);
|
||||
});
|
||||
test('isCompleted returns true iff the given request is completed', () => {
|
||||
testStatusSelector(selectors.isCompleted, completedRequest);
|
||||
});
|
||||
test('isFailed returns true iff the given request is failed', () => {
|
||||
testStatusSelector(selectors.isFailed, failedRequest);
|
||||
});
|
||||
test('error returns the error from the request', () => {
|
||||
expect(select(selectors.error, { error: testValue })).toEqual(testValue);
|
||||
});
|
||||
test('errorStatus returns the error response status', () => {
|
||||
expect(select(selectors.errorStatus, {})).toEqual(undefined);
|
||||
expect(select(selectors.errorStatus, { error: {} })).toEqual(undefined);
|
||||
expect(select(selectors.errorStatus, { error: { response: {} } })).toEqual(undefined);
|
||||
expect(select(selectors.errorStatus, { error: { response: { status: testValue } } }))
|
||||
.toEqual(testValue);
|
||||
});
|
||||
test('errorCode returns the error response data', () => {
|
||||
expect(select(selectors.errorCode, {})).toEqual(undefined);
|
||||
expect(select(selectors.errorCode, { error: {} })).toEqual(undefined);
|
||||
expect(select(selectors.errorCode, { error: { response: {} } })).toEqual(undefined);
|
||||
expect(select(selectors.errorCode, { error: { response: { data: testValue } } }))
|
||||
.toEqual(testValue);
|
||||
});
|
||||
test('data reurns the request data', () => {
|
||||
expect(select(selectors.data, { data: testValue })).toEqual(testValue);
|
||||
});
|
||||
});
|
||||
@@ -1,17 +1,23 @@
|
||||
import { locationId } from 'data/constants/app';
|
||||
|
||||
import { actions } from 'data/redux';
|
||||
import thunkActions from './app';
|
||||
import { selectors, actions } from 'data/redux';
|
||||
import { keyStore } from 'utils';
|
||||
import * as thunkActions from './app';
|
||||
|
||||
jest.mock('./requests', () => ({
|
||||
initializeApp: (args) => ({ initializeApp: args }),
|
||||
batchUnlock: (args) => ({ batchUnlock: args }),
|
||||
}));
|
||||
|
||||
const dispatch = jest.fn((action) => ({ dispatch: action }));
|
||||
const testState = { my: 'test state' };
|
||||
const getState = () => testState;
|
||||
const moduleKeys = keyStore(thunkActions);
|
||||
|
||||
describe('app thunkActions', () => {
|
||||
let dispatch;
|
||||
let dispatchedAction;
|
||||
beforeEach(() => {
|
||||
dispatch = jest.fn((action) => ({ dispatch: action }));
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
describe('initialize', () => {
|
||||
beforeEach(() => {
|
||||
@@ -24,13 +30,13 @@ describe('app thunkActions', () => {
|
||||
});
|
||||
describe('on success', () => {
|
||||
test('loads isEnabled, oraMetadata, courseMetadata and list data', () => {
|
||||
dispatch.mockClear();
|
||||
const response = {
|
||||
courseMetadata: { some: 'course-metadata' },
|
||||
isEnabled: { is: 'enabled?' },
|
||||
oraMetadata: { some: 'ora-metadata' },
|
||||
submissions: { some: 'submissions' },
|
||||
};
|
||||
dispatch.mockClear();
|
||||
dispatchedAction.initializeApp.onSuccess(response);
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.app.loadIsEnabled(response.isEnabled)],
|
||||
@@ -41,4 +47,30 @@ describe('app thunkActions', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('cancelReview', () => {
|
||||
const gradingSelection = (args) => ({ gradingSelection: args });
|
||||
const mockInitialize = (args) => ({ initialize: args });
|
||||
const gradingKeys = keyStore(selectors.grading);
|
||||
beforeEach(() => {
|
||||
jest.spyOn(thunkActions, moduleKeys.initialize)
|
||||
.mockImplementationOnce(mockInitialize);
|
||||
jest.spyOn(selectors.grading, gradingKeys.selection)
|
||||
.mockImplementationOnce(gradingSelection);
|
||||
thunkActions.cancelReview()(dispatch, getState);
|
||||
[[dispatchedAction]] = dispatch.mock.calls;
|
||||
});
|
||||
it('dispatches batchUnlock with submissionUUIDs and onSuccess', () => {
|
||||
expect(dispatchedAction.batchUnlock.submissionUUIDs)
|
||||
.toEqual(gradingSelection(testState));
|
||||
expect(typeof dispatchedAction.batchUnlock.onSuccess).toEqual('function');
|
||||
});
|
||||
it('clears show review state and calls dispatches initialize thunkAction on success', () => {
|
||||
dispatch.mockClear();
|
||||
dispatchedAction.batchUnlock.onSuccess();
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.app.setShowReview(false)],
|
||||
[mockInitialize()],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -107,7 +107,8 @@ export const cancelGrading = () => (dispatch, getState) => {
|
||||
export const submitGrade = () => (dispatch, getState) => {
|
||||
const gradeData = selectors.grading.selected.gradingData(getState());
|
||||
const submissionUUID = selectors.grading.selected.submissionUUID(getState());
|
||||
if (selectors.grading.validation.isValidForSubmit(getState())) {
|
||||
const isValid = selectors.grading.validation.isValidForSubmit(getState());
|
||||
if (isValid) {
|
||||
dispatch(actions.grading.setShowValidation(false));
|
||||
dispatch(requests.submitGrade({
|
||||
submissionUUID,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { actions, selectors } from 'data/redux';
|
||||
import { RequestKeys } from 'data/constants/requests';
|
||||
import { ErrorStatuses, RequestKeys } from 'data/constants/requests';
|
||||
import * as thunkActions from './grading';
|
||||
|
||||
jest.mock('./requests', () => ({
|
||||
@@ -22,27 +22,31 @@ jest.mock('data/redux/grading/selectors', () => ({
|
||||
},
|
||||
selected: {
|
||||
gradeData: jest.fn((state) => ({ gradeData: state })),
|
||||
gradingData: jest.fn((state) => ({ gradingData: state })),
|
||||
isGrading: jest.fn((state) => ({ isGrading: state })),
|
||||
submissionUUID: (state) => ({ selectedsubmissionUUID: state }),
|
||||
lockStatus: (state) => ({ lockStatus: state }),
|
||||
submissionUUID: jest.fn((state) => ({ selectedsubmissionUUID: state })),
|
||||
lockStatus: jest.fn((state) => ({ lockStatus: state })),
|
||||
},
|
||||
validation: {
|
||||
isValidForSubmit: jest.fn((state) => ({ isValidForSubmit: state })),
|
||||
},
|
||||
}));
|
||||
|
||||
const testState = { some: 'testy-state' };
|
||||
const selectedUUID = selectors.grading.selected.submissionUUID(testState);
|
||||
const response = 'test-response';
|
||||
const objResponse = { response };
|
||||
let actionArgs;
|
||||
const dispatch = jest.fn((action) => ({ dispatch: action }));
|
||||
const getState = () => testState;
|
||||
|
||||
const getDispatched = (calledAction) => {
|
||||
calledAction(dispatch, getState);
|
||||
};
|
||||
|
||||
describe('grading thunkActions', () => {
|
||||
const testState = { some: 'testy-state' };
|
||||
const selectedUUID = selectors.grading.selected.submissionUUID(testState);
|
||||
const response = 'test-response';
|
||||
const objResponse = { response };
|
||||
let dispatch;
|
||||
let actionArgs;
|
||||
const getState = () => testState;
|
||||
|
||||
const getDispatched = (calledAction) => {
|
||||
calledAction(dispatch, getState);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
dispatch = jest.fn((action) => ({ dispatch: action }));
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('loadSubmission', () => {
|
||||
@@ -137,10 +141,8 @@ describe('grading thunkActions', () => {
|
||||
describe('onSuccess', () => {
|
||||
const gradeData = { some: 'test grade data' };
|
||||
const startResponse = { other: 'fields', gradeData };
|
||||
beforeEach(() => {
|
||||
dispatch.mockClear();
|
||||
});
|
||||
const fillString = 'selectors.app.fillGradeData based on selected gradeData';
|
||||
beforeEach(() => { dispatch.mockClear(); });
|
||||
test(`dispatches startGrading w/ ${fillString}`, () => {
|
||||
actionArgs.onSuccess(startResponse);
|
||||
expect(dispatch.mock.calls).toContainEqual([
|
||||
@@ -157,6 +159,16 @@ describe('grading thunkActions', () => {
|
||||
expect(dispatch.mock.calls).toContainEqual([actions.app.setShowRubric(true)]);
|
||||
});
|
||||
});
|
||||
describe('onFailure', () => {
|
||||
beforeEach(() => { dispatch.mockClear(); });
|
||||
it('dispatches action to fail setting the lock if error status is Forbidden', () => {
|
||||
const data = { some: 'data' };
|
||||
actionArgs.onFailure({ response: { status: 'arbitrary-status', data } });
|
||||
expect(dispatch).not.toHaveBeenCalled();
|
||||
actionArgs.onFailure({ response: { status: ErrorStatuses.forbidden, data } });
|
||||
expect(dispatch).toHaveBeenCalledWith(actions.grading.failSetLock(data));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancelGrading', () => {
|
||||
@@ -182,5 +194,69 @@ describe('grading thunkActions', () => {
|
||||
expect(dispatch.mock.calls).toContainEqual([actions.grading.stopGrading()]);
|
||||
});
|
||||
});
|
||||
describe('onFailure', () => {
|
||||
beforeEach(() => { dispatch.mockClear(); });
|
||||
it('dispatches action to fail setting the lock if error status is Forbidden', () => {
|
||||
const data = { some: 'data' };
|
||||
actionArgs.onFailure({ response: { status: 'arbitrary-status', data } });
|
||||
expect(dispatch).not.toHaveBeenCalled();
|
||||
actionArgs.onFailure({ response: { status: ErrorStatuses.forbidden, data } });
|
||||
expect(dispatch).toHaveBeenCalledWith(actions.grading.failSetLock(data));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('submitGrade', () => {
|
||||
const mockGradingData = (args) => ({ gradingData: args });
|
||||
const mockSubmissionUUID = (args) => ({ submissionUUID: args });
|
||||
beforeEach(() => {
|
||||
selectors.grading.selected.gradingData.mockImplementationOnce(mockGradingData);
|
||||
selectors.grading.selected.submissionUUID.mockImplementationOnce(mockSubmissionUUID);
|
||||
});
|
||||
describe('if grade data is valid for submission', () => {
|
||||
beforeEach(() => {
|
||||
selectors.grading.validation.isValidForSubmit.mockReturnValueOnce(true);
|
||||
getDispatched(thunkActions.submitGrade());
|
||||
});
|
||||
it('hides validation and submits grade', () => {
|
||||
expect(dispatch.mock.calls[0][0]).toEqual(actions.grading.setShowValidation(false));
|
||||
expect(dispatch.mock.calls[1][0].submitGrade).not.toEqual(undefined);
|
||||
});
|
||||
describe('submitGrade args', () => {
|
||||
let submitGrade;
|
||||
beforeEach(() => {
|
||||
([, [{ submitGrade }]] = dispatch.mock.calls);
|
||||
});
|
||||
it('loads submissionUUID and gradeData from selected submission', () => {
|
||||
expect(submitGrade.submissionUUID).toEqual(mockSubmissionUUID(testState));
|
||||
expect(submitGrade.gradeData).toEqual(mockGradingData(testState));
|
||||
});
|
||||
test('on success, dispatches completeGrading action with response', () => {
|
||||
dispatch.mockClear();
|
||||
submitGrade.onSuccess(response);
|
||||
expect(dispatch.mock.calls).toEqual([[actions.grading.completeGrading(response)]]);
|
||||
});
|
||||
test('on failure, dispatches stopGrading action w/ error if status is Conflict', () => {
|
||||
dispatch.mockClear();
|
||||
submitGrade.onFailure({ response: { status: 'arbitrary', data: response } });
|
||||
expect(dispatch).not.toHaveBeenCalled();
|
||||
submitGrade.onFailure({
|
||||
response: { status: ErrorStatuses.conflict, data: response },
|
||||
});
|
||||
expect(dispatch.mock.calls).toEqual([[actions.grading.stopGrading(response)]]);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('if grade data is invalid for submission', () => {
|
||||
beforeEach(() => {
|
||||
selectors.grading.validation.isValidForSubmit.mockReturnValueOnce(false);
|
||||
getDispatched(thunkActions.submitGrade());
|
||||
});
|
||||
it('sets showValidation to false', () => {
|
||||
expect(dispatch.mock.calls).toEqual([[
|
||||
actions.grading.setShowValidation(true),
|
||||
]]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import api from 'data/services/lms/api';
|
||||
import * as requests from './requests';
|
||||
|
||||
jest.mock('data/services/lms/api', () => ({
|
||||
batchUnlockSubmissions: (submissionUUIDs) => ({ batchUnlockSubmissions: submissionUUIDs }),
|
||||
initializeApp: (locationId) => ({ initializeApp: locationId }),
|
||||
fetchSubmissionStatus: (submissionUUID) => ({ fetchSubmissionStatus: submissionUUID }),
|
||||
fetchSubmission: (submissionUUID) => ({ fetchSubmission: submissionUUID }),
|
||||
@@ -12,66 +13,81 @@ jest.mock('data/services/lms/api', () => ({
|
||||
updateGrade: (submissionUUID, gradeData) => ({ updateGrade: { submissionUUID, gradeData } }),
|
||||
}));
|
||||
|
||||
let dispatch;
|
||||
let onSuccess;
|
||||
let onFailure;
|
||||
const dispatch = jest.fn();
|
||||
const onSuccess = jest.fn();
|
||||
const onFailure = jest.fn();
|
||||
describe('requests thunkActions module', () => {
|
||||
beforeEach(() => {
|
||||
dispatch = jest.fn();
|
||||
onSuccess = jest.fn();
|
||||
onFailure = jest.fn();
|
||||
});
|
||||
|
||||
beforeEach(jest.clearAllMocks);
|
||||
describe('networkRequest', () => {
|
||||
const requestKey = 'test-request';
|
||||
const testData = { some: 'test data' };
|
||||
let resolveFn;
|
||||
let rejectFn;
|
||||
beforeEach(() => {
|
||||
onSuccess = jest.fn();
|
||||
onFailure = jest.fn();
|
||||
requests.networkRequest({
|
||||
requestKey,
|
||||
promise: new Promise((resolve, reject) => {
|
||||
resolveFn = resolve;
|
||||
rejectFn = reject;
|
||||
}),
|
||||
onSuccess,
|
||||
onFailure,
|
||||
})(dispatch);
|
||||
});
|
||||
test('calls startRequest action with requestKey', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([[actions.requests.startRequest(requestKey)]]);
|
||||
});
|
||||
describe('on success', () => {
|
||||
beforeEach(async () => {
|
||||
await resolveFn(testData);
|
||||
describe('with both handlers', () => {
|
||||
beforeEach(() => {
|
||||
requests.networkRequest({
|
||||
requestKey,
|
||||
promise: new Promise((resolve, reject) => {
|
||||
resolveFn = resolve;
|
||||
rejectFn = reject;
|
||||
}),
|
||||
onSuccess,
|
||||
onFailure,
|
||||
})(dispatch);
|
||||
});
|
||||
it('dispatches completeRequest', async () => {
|
||||
test('calls startRequest action with requestKey', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([[actions.requests.startRequest(requestKey)]]);
|
||||
});
|
||||
describe('on success', () => {
|
||||
beforeEach(async () => {
|
||||
await resolveFn(testData);
|
||||
});
|
||||
it('dispatches completeRequest', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.requests.startRequest(requestKey)],
|
||||
[actions.requests.completeRequest({ requestKey, response: testData })],
|
||||
]);
|
||||
});
|
||||
it('calls onSuccess with response', async () => {
|
||||
expect(onSuccess).toHaveBeenCalledWith(testData);
|
||||
expect(onFailure).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe('on failure', () => {
|
||||
beforeEach(async () => {
|
||||
await rejectFn(testData);
|
||||
});
|
||||
test('dispatches completeRequest', async () => {
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.requests.startRequest(requestKey)],
|
||||
[actions.requests.failRequest({ requestKey, error: testData })],
|
||||
]);
|
||||
});
|
||||
test('calls onSuccess with response', async () => {
|
||||
expect(onFailure).toHaveBeenCalledWith(testData);
|
||||
expect(onSuccess).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('without onSuccess and onFailure', () => {
|
||||
test('calls startRequest action with requestKey', async () => {
|
||||
requests.networkRequest({ requestKey, promise: Promise.resolve(testData) })(dispatch);
|
||||
expect(dispatch.mock.calls).toEqual([[actions.requests.startRequest(requestKey)]]);
|
||||
});
|
||||
it('on success dispatches completeRequest', async () => {
|
||||
await requests.networkRequest({ requestKey, promise: Promise.resolve(testData) })(dispatch);
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.requests.startRequest(requestKey)],
|
||||
[actions.requests.completeRequest({ requestKey, response: testData })],
|
||||
]);
|
||||
});
|
||||
it('calls onSuccess with response', async () => {
|
||||
expect(onSuccess).toHaveBeenCalledWith(testData);
|
||||
expect(onFailure).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe('on failure', () => {
|
||||
beforeEach(async () => {
|
||||
await rejectFn(testData);
|
||||
});
|
||||
test('dispatches completeRequest', async () => {
|
||||
it('on failure disaptches completeRequest', async () => {
|
||||
await requests.networkRequest({ requestKey, promise: Promise.reject(testData) })(dispatch);
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
[actions.requests.startRequest(requestKey)],
|
||||
[actions.requests.failRequest({ requestKey, error: testData })],
|
||||
]);
|
||||
});
|
||||
test('calls onSuccess with response', async () => {
|
||||
expect(onFailure).toHaveBeenCalledWith(testData);
|
||||
expect(onSuccess).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -163,6 +179,19 @@ describe('requests thunkActions module', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
describe('batchUnlock', () => {
|
||||
const submissionUUIDs = [1, 2, 3, 4, 5];
|
||||
testNetworkRequestAction({
|
||||
action: requests.batchUnlock,
|
||||
args: { submissionUUIDs, value: false },
|
||||
expectedString: 'with batchUnlock promise',
|
||||
expectedData: {
|
||||
requestKey: RequestKeys.batchUnlock,
|
||||
promise: api.batchUnlockSubmissions(submissionUUIDs),
|
||||
value: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
describe('submitGrade', () => {
|
||||
const gradeData = 'test-grade-data';
|
||||
testNetworkRequestAction({
|
||||
|
||||
140
src/data/services/lms/api.test.js
Normal file
140
src/data/services/lms/api.test.js
Normal file
@@ -0,0 +1,140 @@
|
||||
import { StrictDict, keyStore } from 'utils';
|
||||
import { locationId } from 'data/constants/app';
|
||||
import { paramKeys } from './constants';
|
||||
import urls from './urls';
|
||||
import api from './api';
|
||||
import { stringifyUrl } from './utils';
|
||||
|
||||
jest.mock('./utils', () => ({
|
||||
client: () => ({ delete: (url) => Promise.resolve({ data: { delete: { url } } }) }),
|
||||
get: (url) => Promise.resolve({ data: { get: { url } } }),
|
||||
post: (url, data) => Promise.resolve({ data: { post: { url, data } } }),
|
||||
stringifyUrl: args => ({ stringifyUrl: args }),
|
||||
}));
|
||||
|
||||
jest.mock('data/constants/app', () => ({
|
||||
locationId: 'test-location-id',
|
||||
}));
|
||||
|
||||
const gradeData = 'test-grade-data';
|
||||
const submissionUUID = 'test-submission-uuid';
|
||||
const submissionUUIDs = ['some', 'submission', 'uuid'];
|
||||
|
||||
const methodKeys = StrictDict({
|
||||
get: 'get',
|
||||
post: 'post',
|
||||
delete: 'delete',
|
||||
});
|
||||
|
||||
const urlKeys = keyStore(urls);
|
||||
|
||||
const testAPI = ({
|
||||
promise,
|
||||
method,
|
||||
expected: {
|
||||
urlKey,
|
||||
urlParams,
|
||||
...otherExpected
|
||||
},
|
||||
}) => {
|
||||
it(`returns ${method}(${urlKey}) with correct args and reoslves with response data`, () => (
|
||||
promise.then((data) => {
|
||||
expect(data[method]).toEqual({
|
||||
url: stringifyUrl(urls[urlKey], urlParams),
|
||||
...otherExpected,
|
||||
});
|
||||
})
|
||||
));
|
||||
};
|
||||
|
||||
describe('lms service api methods', () => {
|
||||
describe('initializeApp', () => {
|
||||
testAPI({
|
||||
promise: api.initializeApp(),
|
||||
method: methodKeys.get,
|
||||
expected: {
|
||||
urlKey: urlKeys.oraInitializeUrl,
|
||||
urlParams: { [paramKeys.oraLocation]: locationId },
|
||||
},
|
||||
});
|
||||
});
|
||||
describe('fetchSubmission', () => {
|
||||
testAPI({
|
||||
promise: api.fetchSubmission(submissionUUID),
|
||||
method: methodKeys.get,
|
||||
expected: {
|
||||
urlKey: urlKeys.fetchSubmissionUrl,
|
||||
urlParams: {
|
||||
[paramKeys.oraLocation]: locationId,
|
||||
[paramKeys.submissionUUID]: submissionUUID,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
describe('fetchSubmissionStatus', () => {
|
||||
testAPI({
|
||||
promise: api.fetchSubmissionStatus(submissionUUID),
|
||||
method: methodKeys.get,
|
||||
expected: {
|
||||
urlKey: urlKeys.fetchSubmissionStatusUrl,
|
||||
urlParams: {
|
||||
[paramKeys.oraLocation]: locationId,
|
||||
[paramKeys.submissionUUID]: submissionUUID,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
describe('lockSubmission', () => {
|
||||
testAPI({
|
||||
promise: api.lockSubmission(submissionUUID),
|
||||
method: methodKeys.post,
|
||||
expected: {
|
||||
urlKey: urlKeys.fetchSubmissionLockUrl,
|
||||
urlParams: {
|
||||
[paramKeys.oraLocation]: locationId,
|
||||
[paramKeys.submissionUUID]: submissionUUID,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
describe('unlockSubmission', () => {
|
||||
testAPI({
|
||||
promise: api.unlockSubmission(submissionUUID),
|
||||
method: methodKeys.delete,
|
||||
expected: {
|
||||
urlKey: urlKeys.fetchSubmissionLockUrl,
|
||||
urlParams: {
|
||||
[paramKeys.oraLocation]: locationId,
|
||||
[paramKeys.submissionUUID]: submissionUUID,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
describe('batchUnlockSubmissions', () => {
|
||||
testAPI({
|
||||
promise: api.batchUnlockSubmissions(submissionUUIDs),
|
||||
method: methodKeys.post,
|
||||
expected: {
|
||||
urlKey: urlKeys.batchUnlockSubmissionsUrl,
|
||||
urlParams: {
|
||||
[paramKeys.oraLocation]: locationId,
|
||||
},
|
||||
data: { submissionUUIDs },
|
||||
},
|
||||
});
|
||||
});
|
||||
describe('updateGrade', () => {
|
||||
testAPI({
|
||||
promise: api.updateGrade(submissionUUID, gradeData),
|
||||
method: methodKeys.post,
|
||||
expected: {
|
||||
urlKey: urlKeys.updateSubmissionGradeUrl,
|
||||
urlParams: {
|
||||
[paramKeys.oraLocation]: locationId,
|
||||
[paramKeys.submissionUUID]: submissionUUID,
|
||||
},
|
||||
data: gradeData,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user