fix: fix broken/copy-pasta tests

This commit is contained in:
Ben Warzeski
2022-06-13 13:51:27 -04:00
parent 862ff2d0e2
commit 4172f5c4db
12 changed files with 105 additions and 710 deletions

View File

@@ -1,75 +1,41 @@
// import React from 'react';
// import { shallow } from 'enzyme';
import React from 'react';
import { shallow } from 'enzyme';
// import Footer from '@edx/frontend-component-footer';
// import { LearningHeader as Header } from '@edx/frontend-component-header';
import { BrowserRouter } from 'react-router-dom';
// import ListView from 'containers/ListView';
import Footer from '@edx/frontend-component-footer';
// import { App } from './App';
import Dashboard from 'containers/Dashboard';
import { App } from './App';
// jest.mock('data/redux', () => ({
// app: {
// selectors: {
// courseMetadata: (state) => ({ courseMetadata: state }),
// isEnabled: (state) => ({ isEnabled: state }),
// },
// },
// }));
jest.mock('@edx/frontend-component-footer', () => 'Footer');
// jest.mock('@edx/frontend-component-header', () => ({
// LearningHeader: 'Header',
// }));
// jest.mock('@edx/frontend-component-footer', () => 'Footer');
jest.mock('containers/Dashboard', () => 'Dashboard');
jest.mock('containers/LearnerDashboardHeader', () => 'LearnerDashboardHeader');
// jest.mock('containers/DemoWarning', () => 'DemoWarning');
// jest.mock('containers/ListView', () => 'ListView');
const logo = 'fakeLogo.png';
let el;
let router;
// const logo = 'fakeLogo.png';
// let el;
// let router;
// describe('App router component', () => {
// const props = {
// courseMetadata: {
// org: 'course-org',
// number: 'course-number',
// title: 'course-title',
// },
// isEnabled: true,
// };
// test('snapshot: enabled', () => {
// expect(shallow(<App {...props} />)).toMatchSnapshot();
// });
// test('snapshot: disabled (show demo warning)', () => {
// expect(shallow(<App {...props} isEnabled={false} />)).toMatchSnapshot();
// });
// describe('component', () => {
// beforeEach(() => {
// process.env.LOGO_POWERED_BY_OPEN_EDX_URL_SVG = logo;
// el = shallow(<App {...props} />);
// router = el.childAt(0);
// });
// describe('Router', () => {
// test('Routing - ListView is only route', () => {
// expect(router.find('main')).toEqual(shallow(
// <main><ListView /></main>,
// ));
// });
// });
// test('Footer logo drawn from env variable', () => {
// expect(router.find(Footer).props().logo).toEqual(logo);
// });
// test('Header to use courseMetadata props', () => {
// const {
// courseTitle,
// courseNumber,
// courseOrg,
// } = router.find(Header).props();
// expect(courseTitle).toEqual(props.courseMetadata.title);
// expect(courseNumber).toEqual(props.courseMetadata.number);
// expect(courseOrg).toEqual(props.courseMetadata.org);
// });
// });
// });
describe('App router component', () => {
test('snapshot: enabled', () => {
expect(shallow(<App />)).toMatchSnapshot();
});
describe('component', () => {
beforeEach(() => {
process.env.LOGO_POWERED_BY_OPEN_EDX_URL_SVG = logo;
el = shallow(<App />);
router = el.find(BrowserRouter);
});
describe('Router', () => {
test('Routing - ListView is only route', () => {
expect(router.find('main')).toEqual(shallow(
<main><Dashboard /></main>,
));
});
});
test('Footer logo drawn from env variable', () => {
expect(router.find(Footer).props().logo).toEqual(logo);
});
});
});

View File

@@ -0,0 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`App router component snapshot: enabled 1`] = `
<BrowserRouter>
<div>
<LearnerDashboardHeader />
<main>
<Dashboard />
</main>
<Footer
logo="https://edx-cdn.org/v3/stage/open-edx-tag.svg"
/>
</div>
</BrowserRouter>
`;

View File

@@ -13,11 +13,7 @@ exports[`app registry subscribe: APP_READY. links App to root element 1`] = `
<AppProvider
store={
Object {
"dispatch": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
Symbol(Symbol.observable): [Function],
"redux": "store",
}
}
>

View File

@@ -58,7 +58,6 @@ ReasonPane.propTypes = {
}),
selected: PropTypes.string,
submit: PropTypes.func,
isSubmited: PropTypes.bool,
}).isRequired,
};

View File

@@ -1,8 +1,6 @@
import React from 'react';
import { MockUseState, testCardValues } from 'testUtils';
import * as hooks from './hooks';
import { MockUseState } from 'testUtils';
import { thunkActions } from 'data/redux';
import * as hooks from './hooks';
jest.mock('data/redux/thunkActions/app', () => ({
refreshList: jest.fn((args) => ({ refreshList: args })),

View File

@@ -1,43 +1,16 @@
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',
enrollments: [],
courseData: {
},
entitlements: [],
};
const testValue = 'my-test-value';
const testAction = (action, expected) => {
@@ -47,26 +20,46 @@ describe('app reducer', () => {
});
};
describe('action handlers', () => {
test('loadIsEnabled loads isEnabled from payload', () => {
testAction(actions.loadIsEnabled(testValue), { isEnabled: testValue });
test('loadEntitlements loads entitlements from payload', () => {
testAction(
actions.loadEntitlements(testValue),
{ entitlements: 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 });
describe('loadEnrollments', () => {
const enrollments = [
'course-1',
'course-2',
'course-3',
];
const courseData = {
[enrollments[0]]: {
courseRun: { courseNumber: enrollments[0] },
course: 1,
some: 'data',
},
[enrollments[1]]: {
courseRun: { courseNumber: enrollments[1] },
course: 2,
some: 'other data',
},
[enrollments[2]]: {
courseRun: { courseNumber: enrollments[2] },
course: 3,
some: 'still different data',
},
};
const enrollmentData = enrollments.map(v => courseData[v]);
let out;
beforeEach(() => {
out = reducer(testState, actions.loadEnrollments(enrollmentData));
});
it('loads list of courseRun ids into enrollments field', () => {
expect(out.enrollments).toEqual(enrollments);
});
it('loads object keyed by courseRun ids into courseData field', () => {
expect(out.courseData).toEqual(courseData);
});
});
test('setShowRubric loads showRubric from payload', () => {
testAction(actions.setShowRubric(testValue), { showRubric: testValue });
});
test('toggleShowRubric toggles showRubric value', () => {
testAction(actions.toggleShowRubric(), { showRubric: !testState.showRubric });
});
});
});

View File

@@ -30,12 +30,6 @@ 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(

View File

@@ -1,76 +0,0 @@
import { locationId } from 'data/constants/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 dispatchedAction;
beforeEach(() => {
jest.clearAllMocks();
});
describe('initialize', () => {
beforeEach(() => {
thunkActions.initialize()(dispatch);
[[dispatchedAction]] = dispatch.mock.calls;
});
it('dispatches initializeApp with locationId and onSuccess', () => {
expect(dispatchedAction.initializeApp.locationId).toEqual(locationId);
expect(typeof dispatchedAction.initializeApp.onSuccess).toEqual('function');
});
describe('on success', () => {
test('loads isEnabled, oraMetadata, courseMetadata and list data', () => {
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)],
[actions.app.loadOraMetadata(response.oraMetadata)],
[actions.app.loadCourseMetadata(response.courseMetadata)],
[actions.submissions.loadList(response.submissions)],
]);
});
});
});
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()],
]);
});
});
});

View File

@@ -1,16 +1,9 @@
import { actions } from 'data/redux';
import { RequestKeys } from 'data/constants/requests';
import api from 'data/services/lms/api';
// 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 }),
lockSubmission: ({ submissionUUID }) => ({ lockSubmission: { submissionUUID } }),
unlockSubmission: ({ submissionUUID }) => ({ unlockSubmission: { submissionUUID } }),
updateGrade: (submissionUUID, gradeData) => ({ updateGrade: { submissionUUID, gradeData } }),
// initializeList: (locationId) => ({ initializeList: locationId }),
}));
const dispatch = jest.fn();
@@ -91,6 +84,7 @@ describe('requests thunkActions module', () => {
});
});
/*
const testNetworkRequestAction = ({
action,
args,
@@ -117,92 +111,13 @@ describe('requests thunkActions module', () => {
});
});
};
*/
describe('network request actions', () => {
const submissionUUID = 'test-submission-id';
const locationId = 'test-location-id';
beforeEach(() => {
requests.networkRequest = jest.fn(args => ({ networkRequest: args }));
});
describe('initializeApp', () => {
testNetworkRequestAction({
action: requests.initializeApp,
args: { locationId },
expectedString: 'with initialize key, initializeApp promise',
expectedData: {
requestKey: RequestKeys.initialize,
promise: api.initializeApp(locationId),
},
});
});
describe('fetchSubmissionStatus', () => {
testNetworkRequestAction({
action: requests.fetchSubmissionStatus,
args: { submissionUUID },
expectedString: 'with fetchSubmissionStatus promise',
expectedData: {
requestKey: RequestKeys.fetchSubmissionStatus,
promise: api.fetchSubmissionStatus(submissionUUID),
},
});
});
describe('fetchSubmission', () => {
testNetworkRequestAction({
action: requests.fetchSubmission,
args: { submissionUUID },
expectedString: 'with fetchSubmission promise',
expectedData: {
requestKey: RequestKeys.fetchSubmission,
promise: api.fetchSubmission(submissionUUID),
},
});
});
describe('setLock: true', () => {
testNetworkRequestAction({
action: requests.setLock,
args: { submissionUUID, value: true },
expectedString: 'with setLock promise',
expectedData: {
requestKey: RequestKeys.setLock,
promise: api.lockSubmission(submissionUUID),
},
});
});
describe('setLock: false', () => {
testNetworkRequestAction({
action: requests.setLock,
args: { submissionUUID, value: false },
expectedString: 'with setLock promise',
expectedData: {
requestKey: RequestKeys.setLock,
promise: api.unlockSubmission(submissionUUID),
},
});
});
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({
action: requests.submitGrade,
args: { submissionUUID, gradeData },
expectedString: 'with submitGrade promise',
expectedData: {
requestKey: RequestKeys.submitGrade,
promise: api.updateGrade(submissionUUID, gradeData),
},
});
describe('initializeList', () => {
});
});
});

View File

@@ -35,6 +35,7 @@ jest.mock('@edx/frontend-platform', () => ({
jest.mock('@edx/frontend-component-footer', () => ({
messages: ['some', 'messages'],
}));
jest.mock('data/store', () => ({ redux: 'store' }));
jest.mock('./App', () => 'App');
const testValue = 'my-test-value';

View File

@@ -32,6 +32,7 @@ jest.mock('@edx/frontend-platform/i18n', () => {
),
formatDate: jest.fn().mockName('useIntl.formatDate'),
}),
IntlProvider: () => 'IntlProvider',
defineMessages: m => m,
FormattedMessage: () => 'FormattedMessage',
};

View File

@@ -15,9 +15,7 @@ import thunk from 'redux-thunk';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import urls from 'data/services/lms/urls';
import { ErrorStatuses, RequestKeys, RequestStates } from 'data/constants/requests';
import { gradeStatuses, lockStatuses } from 'data/services/lms/constants';
import fakeData from 'data/services/lms/fakeData';
// import fakeData from 'data/services/lms/fakeData/courses';
import api from 'data/services/lms/api';
import reducers from 'data/redux';
import messages from 'i18n';
@@ -40,21 +38,6 @@ jest.mock('@edx/frontend-platform/auth', () => ({
getLoginRedirectUrl: jest.fn(),
}));
jest.mock('react-pdf', () => ({
Document: () => <div>Document</div>,
Image: () => <div>Image</div>,
Page: () => <div>Page</div>,
PDFViewer: jest.fn(() => null),
StyleSheet: { create: () => {} },
Text: () => <div>Text</div>,
View: () => <div>View</div>,
pdfjs: { GlobalWorkerOptions: {} },
}));
/*
jest.mock('react-pdf/node_modules/pdfjs-dist/build/pdf.worker.entry', () => (
jest.requireActual('react-pdf/dist/umd/entry.jest')
));
*/
const configureStore = () => redux.createStore(
reducers,
redux.compose(redux.applyMiddleware(thunk)),
@@ -66,8 +49,6 @@ let state;
let retryLink;
let inspector;
const { rubricConfig } = fakeData.oraMetadata;
/**
* Simple wrapper for updating the top-level state variable, that also returns the new value
* @return {obj} - current redux store state
@@ -77,16 +58,6 @@ const getState = () => {
return state;
};
/** Fake Data for quick access */
const submissionUUIDs = [
fakeData.ids.submissionUUID(0),
fakeData.ids.submissionUUID(1),
fakeData.ids.submissionUUID(2),
fakeData.ids.submissionUUID(3),
fakeData.ids.submissionUUID(4),
];
const submissions = submissionUUIDs.map(id => fakeData.mockSubmission(id));
/**
* Object to be filled with resolve/reject functions for all controlled network comm channels
*/
@@ -103,58 +74,7 @@ const mockForbiddenError = (reject) => () => reject(new Error({
}));
const mockApi = () => {
api.initializeApp = jest.fn(() => new Promise(
(resolve, reject) => {
resolveFns.init = {
success: () => resolve({
isEnabled: true,
oraMetadata: fakeData.oraMetadata,
courseMetadata: fakeData.courseMetadata,
submissions: fakeData.submissions,
}),
networkError: mockNetworkError(reject),
};
},
));
api.fetchSubmission = jest.fn((submissionUUID) => new Promise(
(resolve, reject) => {
resolveFns.fetch = {
success: () => resolve(fakeData.mockSubmission(submissionUUID)),
networkError: mockNetworkError(reject),
};
},
));
api.fetchSubmissionStatus = jest.fn((submissionUUID) => Promise.resolve(
fakeData.mockSubmissionStatus(submissionUUID)
));
api.lockSubmission = jest.fn(() => new Promise(
(resolve, reject) => {
resolveFns.lock = {
success: () => resolve({ lockStatus: lockStatuses.inProgress }),
networkError: mockForbiddenError(reject),
};
},
));
api.unlockSubmission = jest.fn(() => new Promise(
(resolve, reject) => {
resolveFns.unlock = {
success: () => resolve({ lockStatus: lockStatuses.unlocked }),
networkError: mockNetworkError(reject),
};
},
));
api.updateGrade = jest.fn((uuid, gradeData) => new Promise(
(resolve, reject) => {
resolveFns.updateGrade = {
success: () => resolve({
gradeData,
gradeStatus: gradeStatuses.graded,
lockStatus: lockStatuses.unlocked,
}),
networkError: mockNetworkError(reject),
};
},
));
api.initializeList = jest.fn(() => new Promise());
};
/**
@@ -172,344 +92,17 @@ const renderEl = async () => {
getState();
};
/**
* resolve the initalization promise, and update state object
*/
const initialize = async () => {
resolveFns.init.success();
await inspector.find.listView.viewAllResponsesBtn();
getState();
};
/**
* Select the first 5 entries in the table and click the 'View Selected Responses' button
* Wait for the review page to show and update the top-level state object.
*/
const makeTableSelections = async () => {
[0, 1, 2, 3, 4].forEach(index => userEvent.click(inspector.listView.listCheckbox(index)));
userEvent.click(inspector.listView.selectedBtn(5));
// wait for navigation, which will show while request is pending
try {
await inspector.find.review.prevNav();
} catch (e) {
throw(e);
}
getState();
};
const waitForEqual = async (valFn, expected, key) => waitFor(() => {
expect(valFn(), `${key} is expected to equal ${expected}`).toEqual(expected);
});
const waitForRequestStatus = (key, status) => waitForEqual(
() => getState().requests[key].status,
status,
key,
);
describe('ESG app integration tests', () => {
describe('Learner Dashbpard app integration tests', () => {
beforeEach(async () => {
mockApi();
await renderEl();
inspector = new Inspector(el);
// inspector = new Inspector(el);
});
test('initialization', async (done) => {
const verifyInitialState = async () => {
await waitForRequestStatus(RequestKeys.initialize, RequestStates.pending);
const testInitialState = (key) => expect(
state[key],
`${key} store should have its configured initial state`,
).toEqual(
jest.requireActual(`data/redux/${key}/reducer`).initialState,
);
testInitialState('app');
testInitialState('submissions');
testInitialState('grading');
expect(
inspector.listView.loadingResponses(),
'Loading Responses pending state text should be displayed in the ListView',
).toBeVisible();
}
await verifyInitialState();
// initialization network error
const forceAndVerifyInitNetworkError = async () => {
resolveFns.init.networkError();
await waitForRequestStatus(RequestKeys.initialize, RequestStates.failed);
expect(
await inspector.find.listView.loadErrorHeading(),
'List Error should be available (by heading component)',
).toBeVisible();
const backLink = inspector.listView.backLink();
expect(
backLink.href,
'Back to responses button href should link to urls.openResponse(courseId)',
).toEqual(urls.openResponse(getState().app.courseMetadata.courseId));
};
await forceAndVerifyInitNetworkError();
// initialization retry/pending
retryLink = inspector.listView.reloadBtn();
await userEvent.click(retryLink);
await waitForRequestStatus(RequestKeys.initialize, RequestStates.pending);
// initialization success
const forceAndVerifyInitSuccess = async () => {
await initialize();
await waitForRequestStatus(RequestKeys.initialize, RequestStates.completed);
expect(
state.app.courseMetadata,
'Course metadata in redux should be populated with fake data',
).toEqual(fakeData.courseMetadata);
expect(
state.app.oraMetadata,
'ORA metadata in redux should be populated with fake data',
).toEqual(fakeData.oraMetadata);
expect(
state.submissions.allSubmissions,
'submissions data in redux should be populated with fake data',
).toEqual(fakeData.submissions);
};
await forceAndVerifyInitSuccess();
await makeTableSelections();
await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.pending);
done();
});
describe('initialized', () => {
beforeEach(async () => {
await initialize();
await waitForRequestStatus(RequestKeys.initialize, RequestStates.completed);
await makeTableSelections();
await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.pending);
});
test('initial review state', async (done) => {
// Make table selection and load Review pane
expect(
state.grading.selection,
'submission IDs should be loaded',
).toEqual(submissionUUIDs);
expect(state.app.showReview, 'app store should have showReview: true').toEqual(true);
expect(inspector.review.username(0), 'username should be visible').toBeVisible();
const nextNav = inspector.review.nextNav();
const prevNav = inspector.review.prevNav();
expect(nextNav, 'next nav should be displayed').toBeVisible();
expect(nextNav, 'next nav should be disabled').toHaveAttribute('disabled');
expect(prevNav, 'prev nav should be displayed').toBeVisible();
expect(prevNav, 'prev nav should be disabled').toHaveAttribute('disabled');
expect(
inspector.review.loadingResponse(),
'Loading Responses pending state text should be displayed in the ReviewModal',
).toBeVisible();
done();
});
test('fetch network error and retry', async (done) => {
await resolveFns.fetch.networkError();
await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.failed);
expect(
await inspector.find.review.loadErrorHeading(),
'Load Submission error should be displayed in ReviewModal',
).toBeVisible();
// fetch: retry and succeed
await userEvent.click(inspector.review.retryFetchLink());
await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.pending);
done()
});
test('fetch success and nav chain', async (done) => {
let showRubric = false;
// fetch: success with chained navigation
const verifyFetchSuccess = async (submissionIndex) => {
const submissionString = `for submission ${submissionIndex}`;
const submission = submissions[submissionIndex];
const forceAndVerifyFetchSuccess = async () => {
await resolveFns.fetch.success();
await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.completed);
expect(
inspector.review.gradingStatus(submission),
`Should display current submission grading status ${submissionString}`,
).toBeVisible();
};
await forceAndVerifyFetchSuccess();
showRubric = showRubric || selectors.grading.selected.isGrading(getState());
const verifyRubricVisibility = async () => {
getState();
expect(
state.app.showRubric,
`${showRubric ? 'Should' : 'Should not'} show rubric ${submissionString}`,
).toEqual(showRubric);
if (showRubric) {
expect(
inspector.review.hideRubricBtn(),
`Hide Rubric button should be visible when rubric is shown ${submissionString}`,
).toBeVisible();
} else {
expect(
inspector.review.showRubricBtn(),
`Show Rubric button should be visible when rubric is hidden ${submissionString}`,
).toBeVisible();
}
}
await verifyRubricVisibility();
// loads current submission
const testSubmissionGradingState = () => {
expect(
state.grading.current,
`Redux current grading state should load the current submission ${submissionString}`,
).toEqual({
submissionUUID: submissionUUIDs[submissionIndex],
...submissions[submissionIndex],
});
};
testSubmissionGradingState();
const testNavState = () => {
const expectDisabled = (getNav, name) => (
 expect(getNav(), `${name} should be disabled`).toHaveAttribute('disabled')
);
const expectEnabled = (getNav, name) => (
 expect(getNav(), `${name} should be enabled`).not.toHaveAttribute('disabled')
);
(submissionIndex > 0 ? expectEnabled : expectDisabled)(
inspector.review.prevNav,
'Prev nav',
);
const hasNext = submissionIndex < submissions.length - 1;
(hasNext ? expectEnabled : expectDisabled)(inspector.review.nextNav, 'Next nav');
};
testNavState();
};
await verifyFetchSuccess(0);
for (let i = 1; i < 5; i++) {
await userEvent.click(inspector.review.nextNav());
await verifyFetchSuccess(i);
}
for (let i = 3; i >= 0; i--) {
await userEvent.click(inspector.review.prevNav());
await verifyFetchSuccess(i);
}
done();
});
describe('grading (basic)', () => {
beforeEach(async () => {
await resolveFns.fetch.success();
await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.completed);
await userEvent.click(await inspector.find.review.startGradingBtn());
});
describe('active grading', () => {
beforeEach(async () => {
await resolveFns.lock.success();
});
const selectedOptions = [1, 2];
const feedback = ['feedback 0', 'feedback 1'];
const overallFeedback = 'some overall feedback';
// Set basic grade and feedback
const setGrade = async (done) => {
const {
criterionOption,
criterionFeedback,
feedbackInput,
} = inspector.review.rubric;
const options = [
criterionOption(0, selectedOptions[0]),
criterionOption(1, selectedOptions[1]),
];
await userEvent.click(options[0]);
await userEvent.type(criterionFeedback(0), feedback[0]);
await userEvent.click(options[1]);
await userEvent.type(criterionFeedback(1), feedback[1]);
await userEvent.type(inspector.review.rubric.feedbackInput(), overallFeedback);
return;
};
// Verify active-grading state
const checkGradingState = (submissionUUID=submissionUUIDs[0]) => {
const entry = getState().grading.gradingData[submissionUUID];
const checkCriteria = (index) => {
const criterion = entry.criteria[index];
const selected = rubricConfig.criteria[index].options[selectedOptions[index]].name;
expect(criterion.selectedOption).toEqual(selected);
expect(criterion.feedback).toEqual(feedback[index]);
}
[0, 1].forEach(checkCriteria);
expect(entry.overallFeedback).toEqual(overallFeedback);
}
// Verify after-submission-success grade state
const checkGradeSuccess = () => {
const { gradeData, current } = getState().grading;
const entry = gradeData[submissionUUIDs[0]];
const checkCriteria = (index) => {
const criterion = entry.criteria[index];
const rubricOptions = rubricConfig.criteria[index].options;
expect(criterion.selectedOption).toEqual(rubricOptions[selectedOptions[index]].name);
expect(criterion.feedback).toEqual(feedback[index]);
}
[0, 1].forEach(checkCriteria);
expect(entry.overallFeedback).toEqual(overallFeedback);
expect(current.gradeStatus).toEqual(gradeStatuses.graded);
expect(current.lockStatus).toEqual(lockStatuses.unlocked);
}
const loadNext = async () => {
await userEvent.click(inspector.review.nextNav());
await resolveFns.fetch.success();
};
const loadPrev = async () => {
await userEvent.click(inspector.review.prevNav());
await resolveFns.fetch.success();
}
const startGrading = async () => {
await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.completed);
await userEvent.click(await inspector.find.review.startGradingBtn());
await resolveFns.lock.success();
}
/*
test('submit pending', async (done) => {
done();
});
test('submit failed', async (done) => {
done();
});
*/
test('grade and submit',
async (done) => {
expect(await inspector.find.review.submitGradeBtn()).toBeVisible();
await setGrade();
checkGradingState();
await userEvent.click(inspector.review.rubric.submitGradeBtn());
await resolveFns.updateGrade.success();
checkGradeSuccess();
done();
},
);
test('grade, navigate, and return, maintaining gradingState',
async (done) => {
expect(await inspector.find.review.submitGradeBtn()).toBeVisible();
await setGrade();
checkGradingState();
await loadNext();
await waitForEqual(() => getState().grading.activeIndex, 1, 'activeIndex');
await loadPrev();
await waitForEqual(() => getState().grading.activeIndex, 0, 'activeIndex');
checkGradingState();
done();
},
);
});
});
});
});