diff --git a/src/App.test.jsx b/src/App.test.jsx index 6116b38..775e196 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -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()).toMatchSnapshot(); -// }); -// test('snapshot: disabled (show demo warning)', () => { -// expect(shallow()).toMatchSnapshot(); -// }); -// describe('component', () => { -// beforeEach(() => { -// process.env.LOGO_POWERED_BY_OPEN_EDX_URL_SVG = logo; -// el = shallow(); -// router = el.childAt(0); -// }); -// describe('Router', () => { -// test('Routing - ListView is only route', () => { -// expect(router.find('main')).toEqual(shallow( -//
, -// )); -// }); -// }); -// 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()).toMatchSnapshot(); + }); + describe('component', () => { + beforeEach(() => { + process.env.LOGO_POWERED_BY_OPEN_EDX_URL_SVG = logo; + el = shallow(); + router = el.find(BrowserRouter); + }); + describe('Router', () => { + test('Routing - ListView is only route', () => { + expect(router.find('main')).toEqual(shallow( +
, + )); + }); + }); + test('Footer logo drawn from env variable', () => { + expect(router.find(Footer).props().logo).toEqual(logo); + }); + }); +}); diff --git a/src/__snapshots__/App.test.jsx.snap b/src/__snapshots__/App.test.jsx.snap new file mode 100644 index 0000000..0748d92 --- /dev/null +++ b/src/__snapshots__/App.test.jsx.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`App router component snapshot: enabled 1`] = ` + +
+ +
+ +
+
+
+
+`; diff --git a/src/__snapshots__/index.test.jsx.snap b/src/__snapshots__/index.test.jsx.snap index b7564fc..1e8cd8e 100644 --- a/src/__snapshots__/index.test.jsx.snap +++ b/src/__snapshots__/index.test.jsx.snap @@ -13,11 +13,7 @@ exports[`app registry subscribe: APP_READY. links App to root element 1`] = ` diff --git a/src/containers/UnenrollConfirmModal/components/ReasonPane.jsx b/src/containers/UnenrollConfirmModal/components/ReasonPane.jsx index 425c686..1f40f77 100644 --- a/src/containers/UnenrollConfirmModal/components/ReasonPane.jsx +++ b/src/containers/UnenrollConfirmModal/components/ReasonPane.jsx @@ -58,7 +58,6 @@ ReasonPane.propTypes = { }), selected: PropTypes.string, submit: PropTypes.func, - isSubmited: PropTypes.bool, }).isRequired, }; diff --git a/src/containers/UnenrollConfirmModal/hooks.test.js b/src/containers/UnenrollConfirmModal/hooks.test.js index 3355dac..9315ee2 100644 --- a/src/containers/UnenrollConfirmModal/hooks.test.js +++ b/src/containers/UnenrollConfirmModal/hooks.test.js @@ -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 })), diff --git a/src/data/redux/app/reducer.test.js b/src/data/redux/app/reducer.test.js index 4302fd2..549ad75 100644 --- a/src/data/redux/app/reducer.test.js +++ b/src/data/redux/app/reducer.test.js @@ -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 }); }); }); }); diff --git a/src/data/redux/requests/selectors.test.js b/src/data/redux/requests/selectors.test.js index e5110a4..089dc71 100644 --- a/src/data/redux/requests/selectors.test.js +++ b/src/data/redux/requests/selectors.test.js @@ -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( diff --git a/src/data/redux/thunkActions/app.test.js b/src/data/redux/thunkActions/app.test.js deleted file mode 100644 index 18765bd..0000000 --- a/src/data/redux/thunkActions/app.test.js +++ /dev/null @@ -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()], - ]); - }); - }); -}); diff --git a/src/data/redux/thunkActions/requests.test.js b/src/data/redux/thunkActions/requests.test.js index 72e3262..2e2bfd8 100644 --- a/src/data/redux/thunkActions/requests.test.js +++ b/src/data/redux/thunkActions/requests.test.js @@ -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', () => { }); }); }); diff --git a/src/index.test.jsx b/src/index.test.jsx index 7c099e7..bd6dc20 100644 --- a/src/index.test.jsx +++ b/src/index.test.jsx @@ -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'; diff --git a/src/setupTest.jsx b/src/setupTest.jsx index be5d5b1..2a96946 100755 --- a/src/setupTest.jsx +++ b/src/setupTest.jsx @@ -32,6 +32,7 @@ jest.mock('@edx/frontend-platform/i18n', () => { ), formatDate: jest.fn().mockName('useIntl.formatDate'), }), + IntlProvider: () => 'IntlProvider', defineMessages: m => m, FormattedMessage: () => 'FormattedMessage', }; diff --git a/src/test/app.test.jsx b/src/test/app.test.jsx index bd7021a..04a7a44 100644 --- a/src/test/app.test.jsx +++ b/src/test/app.test.jsx @@ -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: () =>
Document
, - Image: () =>
Image
, - Page: () =>
Page
, - PDFViewer: jest.fn(() => null), - StyleSheet: { create: () => {} }, - Text: () =>
Text
, - View: () =>
View
, - 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(); - }, - ); - }); - }); }); });