diff --git a/src/data/redux/grading/selectors.js b/src/data/redux/grading/selectors.js index 12f1b00..76d43c1 100644 --- a/src/data/redux/grading/selectors.js +++ b/src/data/redux/grading/selectors.js @@ -71,6 +71,10 @@ selected.response = createSelector( (current) => current.response, ); +export const gradingStatusTransform = ({ gradeStatus, lockStatus }) => ( + lockStatus === lockStatuses.unlocked ? gradeStatus : lockStatus +); + /** * Returns the "grading" status for the selected submission, * which is a combination of the grade and lock statuses. @@ -78,7 +82,7 @@ selected.response = createSelector( */ selected.gradingStatus = createSelector( [module.selected.gradeStatus, module.selected.lockStatus], - (gradeStatus, lockStatus) => (lockStatus === lockStatuses.unlocked ? gradeStatus : lockStatus), + (gradeStatus, lockStatus) => gradingStatusTransform({ gradeStatus, lockStatus }), ); selected.isGrading = createSelector( diff --git a/src/data/services/lms/fakeData/ora.js b/src/data/services/lms/fakeData/ora.js index 2dd0df0..ddab8c7 100644 --- a/src/data/services/lms/fakeData/ora.js +++ b/src/data/services/lms/fakeData/ora.js @@ -57,7 +57,7 @@ const rubricConfig = { { name: 'second criterion', orderNum: 1, - prompt: 'A criterion prompt', + prompt: 'A second criterion prompt', feedback: 'required', options: [ { diff --git a/src/test/app.test.jsx b/src/test/app.test.jsx index 9191b83..e8f6405 100644 --- a/src/test/app.test.jsx +++ b/src/test/app.test.jsx @@ -15,7 +15,7 @@ import { IntlProvider } from '@edx/frontend-platform/i18n'; import urls from 'data/services/lms/urls'; import { ErrorStatuses, RequestKeys, RequestStates } from 'data/constants/requests'; -import { lockStatuses } from 'data/services/lms/constants'; +import { gradeStatuses, lockStatuses } from 'data/services/lms/constants'; import fakeData from 'data/services/lms/fakeData'; import api from 'data/services/lms/api'; import reducers from 'data/redux'; @@ -23,6 +23,7 @@ import messages from 'i18n'; import { selectors } from 'data/redux'; import App from 'App'; +import Inspector from './inspector'; import appMessages from './messages'; jest.unmock('@edx/paragon'); @@ -58,10 +59,9 @@ let el; let store; let state; let retryLink; -let find; -let get; - +let inspector; +const { rubricConfig } = fakeData.oraMetadata; /** * Simple wrapper for updating the top-level state variable, that also returns the new value @@ -82,6 +82,9 @@ const submissionUUIDs = [ ]; const submissions = submissionUUIDs.map(id => fakeData.mockSubmission(id)); +/** + * Object to be filled with resolve/reject functions for all controlled network comm channels + */ const resolveFns = {}; /** * Mock the api with jest functions that can be tested against. @@ -89,6 +92,7 @@ const resolveFns = {}; const mockNetworkError = (reject) => () => reject(new Error({ response: { status: ErrorStatuses.badRequest }, })); + const mockApi = () => { api.initializeApp = jest.fn(() => new Promise( (resolve, reject) => { @@ -132,6 +136,18 @@ const mockApi = () => { }; }, )); + api.updateGrade = jest.fn((uuid, gradeData) => new Promise( + (resolve, reject) => { + resolveFns.updateGrade = { + success: () => resolve({ + gradeData, + gradeStatus: gradeStatuses.graded, + lockStatus: lockStatuses.unlocked, + }), + networkError: mockNetworkError(reject), + }; + }, + )); }; /** @@ -149,40 +165,12 @@ const renderEl = async () => { getState(); }; -class Inspector { - listTable = () => el.getByRole('table'); - modal = () => el.getByRole('dialog'); - modalEl = () => within(this.modal()); - - listTable = () => el.getByRole('table'); - listTableRows = () => this.listTable().querySelectorAll('tbody tr'); - listCheckbox = (index) => within(this.listTableRows().item(index)).getByTitle('Toggle Row Selected'); - listViewSelectedBtn = () => el.getByText('View selected responses (5)'); - nextNav = () => el.getByLabelText(appMessages.ReviewActionsComponents.loadNext); - prevNav = () => el.getByLabelText(appMessages.ReviewActionsComponents.loadPrevious); - reviewUsername = (index) => this.modalEl().getByText(fakeData.ids.username(index)); - reviewLoadingResponse = () => this.modalEl().getByText(appMessages.ReviewModal.loadingResponse); - reviewRetryLink = () => ( - el.getByText(appMessages.ReviewErrors.reloadSubmission).closest('button') - ); - reviewGradingStatus = (submission) => ( - this.modalEl().getByText(appMessages.lms[gradingStatus(submission)]) - ); -} -class Finder { - listViewAllResponsesBtn = () => el.findByText(appMessages.ListView.viewAllResponses); - listLoadErrorHeading = () => el.findByText(appMessages.ListView.loadErrorHeading); - prevNav = () => get.modalEl().findByLabelText(appMessages.ReviewActionsComponents.loadPrevious); - reviewLoadErrorHeading = () => el.findByText(appMessages.ReviewErrors.loadErrorHeading); -} - - /** * resolve the initalization promise, and update state object */ const initialize = async () => { resolveFns.init.success(); - await find.listViewAllResponsesBtn(); + await inspector.find.listView.viewAllResponsesBtn(); getState(); }; @@ -191,23 +179,17 @@ const initialize = async () => { * 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(get.listCheckbox(index))); - userEvent.click(get.listViewSelectedBtn()); + [0, 1, 2, 3, 4].forEach(index => userEvent.click(inspector.listView.listCheckbox(index))); + userEvent.click(inspector.listView.selectedBtn()); // wait for navigation, which will show while request is pending try { - await find.prevNav(); + await inspector.find.review.prevNav(); } catch (e) { throw(e); } getState(); }; -// Click the 'next' button in review modal -const clickNext = async () => { userEvent.click(get.nextNav()); }; - -// Click the 'next' button in review modal -const clickPrev = async () => { userEvent.click(get.prevNav()); }; - const waitForEqual = async (valFn, expected, key) => waitFor(() => { expect(valFn(), `${key} is expected to equal ${expected}`).toEqual(expected); }); @@ -217,194 +199,276 @@ const waitForRequestStatus = (key, status) => waitForEqual( key, ); -const gradingStatus = ({ lockStatus, gradeStatus }) => ( - lockStatus === lockStatuses.unlocked ? gradeStatus : lockStatus -); describe('ESG app integration tests', () => { - test('initialState', async () => { + beforeEach(async () => { mockApi(); await renderEl(); - get = new Inspector(); - find = new Finder(); - 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( - el.getByText(appMessages.ListView.loadingResponses), - 'Loading Responses pending state text should be displayed in the ListView', - ).toBeVisible(); + 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 - resolveFns.init.networkError(); - await waitForRequestStatus(RequestKeys.initialize, RequestStates.failed); - expect( - await find.listLoadErrorHeading(), - 'List Error should be available (by heading component)', - ).toBeVisible(); - const backLink = el.getByText(appMessages.ListView.backToResponsesLowercase).closest('a'); - expect( - backLink.href, - 'Back to responses button href should link to urls.openResponse(courseId)', - ).toEqual(urls.openResponse(getState().app.courseMetadata.courseId)); - retryLink = el.getByText(appMessages.ListView.reloadSubmissions).closest('button'); + 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 - 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); - - // Table selection - await makeTableSelections(); - - // Review pending - await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.pending); - expect( - state.grading.selected, - 'submission IDs should be loaded', - ).toEqual(submissionUUIDs); - // expect(get.modal(), 'ReviewModal should be visible').toBeVisible(); - expect(state.app.showReview, 'app store should have showReview: true').toEqual(true); - expect(get.reviewUsername(0), 'username should be visible').toBeVisible(); - expect(get.nextNav(), 'next nav should be displayed').toBeVisible(); - expect(get.nextNav(), 'next nav should be disabled').toHaveAttribute('disabled'); - expect(get.prevNav(), 'prev nav should be displayed').toBeVisible(); - expect(get.prevNav(), 'prev nav should be disabled').toHaveAttribute('disabled'); - expect( - get.reviewLoadingResponse(), - 'Loading Responses pending state text should be displayed in the ReviewModal', - ).toBeVisible(); - - // Review: Fetch - Network Error - await resolveFns.fetch.networkError(); - await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.failed); - expect( - await find.reviewLoadErrorHeading(), - 'Load Submission error should be displayed in ReviewModal', - ).toBeVisible(); - - // fetch: retry and succeed - await userEvent.click(get.reviewRetryLink()); - await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.pending); - - let showRubric = false; - // fetch: success - const verifyFetchSuccess = async (submissionIndex) => { - const submissionString = `for submission ${submissionIndex}`; - const submission = submissions[submissionIndex]; - await resolveFns.fetch.success(); - - await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.completed); + const forceAndVerifyInitSuccess = async () => { + await initialize(); + await waitForRequestStatus(RequestKeys.initialize, RequestStates.completed); expect( - get.reviewGradingStatus(submission), - `Should display current submission grading status ${submissionString}`, - ).toBeVisible(); - - showRubric = showRubric || selectors.grading.selected.isGrading(getState()); - getState(); + state.app.courseMetadata, + 'Course metadata in redux should be populated with fake data', + ).toEqual(fakeData.courseMetadata); expect( - state.app.showRubric, - `${showRubric ? 'Should' : 'Should not'} show rubric ${submissionString}`, - ).toEqual(showRubric); - if (showRubric) { - expect( - el.getByText(appMessages.ReviewActions.hideRubric), - `Hide Rubric button should be visible when rubric is shown ${submissionString}`, - ).toBeVisible(); - - } else { - expect( - el.getByText(appMessages.ReviewActions.showRubric), - `Show Rubric button should be visible when rubric is hidden ${submissionString}`, - ).toBeVisible(); - } - - // loads current submission + state.app.oraMetadata, + 'ORA metadata in redux should be populated with fake data', + ).toEqual(fakeData.oraMetadata); expect( - state.grading.current, - `Redux current grading state should load the current submission ${submissionString}`, - ).toEqual({ - submissionUUID: submissionUUIDs[submissionIndex], - ...submissions[submissionIndex], - }); - expect(get.nextNav(), `Next nav should be visible ${submissionString}`).toBeVisible(); - expect(get.prevNav(), `Prev nav should be visible ${submissionString}`).toBeVisible(); - - if (submissionIndex > 0) { - expect( - get.prevNav(), - `Prev nav should be enabled ${submissionString}`, - ).not.toHaveAttribute('disabled'); - } else { - expect( - get.prevNav(), - `Prev nav should be disabled ${submissionString}`, - ).toHaveAttribute('disabled'); - } - if (submissionIndex < submissions.length - 1) { - expect( - get.nextNav(), - `Next nav should be enabled ${submissionString}`, - ).not.toHaveAttribute('disabled'); - } else { - expect( - get.nextNav(), - `Next nav should be disabled ${submissionString}`, - ).toHaveAttribute('disabled'); - } + state.submissions.allSubmissions, + 'submissions data in redux should be populated with fake data', + ).toEqual(fakeData.submissions); }; - await verifyFetchSuccess(0); + await forceAndVerifyInitSuccess(); - await clickNext(); - await verifyFetchSuccess(1); + await makeTableSelections(); + await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.pending); + done(); + }); - await clickNext(); - await verifyFetchSuccess(2); + describe('initialized', () => { + beforeEach(async () => { + await initialize(); + await waitForRequestStatus(RequestKeys.initialize, RequestStates.completed); + await makeTableSelections(); + await waitForRequestStatus(RequestKeys.fetchSubmission, RequestStates.pending); + }); - await clickNext(); - await verifyFetchSuccess(3); + test('initial review state', async (done) => { + // Make table selection and load Review pane + expect( + state.grading.selected, + '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(); + }); - await clickNext(); - await verifyFetchSuccess(4); + 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() + }); - await clickPrev(); - await verifyFetchSuccess(3); + 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(); - await clickNext(); - await verifyFetchSuccess(4); + showRubric = showRubric || selectors.grading.selected.isGrading(getState()); - await clickPrev(); - await verifyFetchSuccess(3); + 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(); - await clickPrev(); - await verifyFetchSuccess(2, true); + } else { + expect( + inspector.review.showRubricBtn(), + `Show Rubric button should be visible when rubric is hidden ${submissionString}`, + ).toBeVisible(); + } + } + await verifyRubricVisibility(); - await clickPrev(); - await verifyFetchSuccess(1); + // 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(); - await clickPrev(); - await verifyFetchSuccess(0); - ///done(); + 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()); + }); + /* + test('pending', async (done) => { + done(); + }); + test('error', async (done) => { + done(); + }); + */ + 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 () => { + for (let i of [0, 1]) { + await userEvent.click(inspector.review.rubric.criterionOption(i, selectedOptions[i])); + await userEvent.type(inspector.review.rubric.criterionFeedback(i), feedback[i]); + } + await userEvent.type(inspector.review.rubric.feedbackInput(), overallFeedback); + }; + + // Verify active-grading state + const checkGradingState = () => { + const { gradingData } = getState().grading; + const entry = gradingData[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); + } + + // 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); + } + /* + test('submit pending', async (done) => { + done(); + }); + test('submit failed', async (done) => { + done(); + }); + */ + test('submit grade (success)', 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(); + }); + }); + }); }); }); diff --git a/src/test/inspector.js b/src/test/inspector.js new file mode 100644 index 0000000..71c267b --- /dev/null +++ b/src/test/inspector.js @@ -0,0 +1,109 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { within } from '@testing-library/react'; + +import fakeData from 'data/services/lms/fakeData'; +import { gradingStatusTransform } from 'data/redux/grading/selectors'; + +import appMessages from './messages'; + +const { rubricConfig } = fakeData.oraMetadata; + +/** + * App inspector class providing methods to return elements from within + * the virtual DOM + * @props {Root Node} el - Root app render node. + */ +class Inspector { + constructor(el) { + this.el = el; + this.getByRole = this.el.getByRole; + this.getByText = this.el.getByText; + this.getByLabelText = this.el.getByLabelText; + this.findByText = this.el.findByText; + this.findByLabelText = this.el.findByLabelText; + } + + /** + * Returns listView elements (immediate return methods) + */ + get listView() { + const table = () => this.getByRole('table'); + const tableRows = () => table().querySelectorAll('tbody tr'); + return { + table, + tableRows, + selectedBtn: () => this.getByText('View selected responses (5)'), + loadingResponses: () => this.getByText(appMessages.ListView.loadingResponses), + listCheckbox: (index) => ( + within(tableRows().item(index)).getByTitle('Toggle Row Selected') + ), + backLink: () => this.getByText(appMessages.ListView.backToResponsesLowercase).closest('a'), + reloadBtn: () => this.getByText(appMessages.ListView.reloadSubmissions).closest('button'), + }; + } + + /** + * Returns Review Modal elements (immediate return methods) + */ + get review() { + const modal = this.getByRole('dialog'); + const modalEl = within(modal); + const { getByLabelText, getByText } = modalEl; + const rubricContent = () => modalEl.getByText(appMessages.Rubric.rubric).closest('div'); + const rubricFeedback = () => ( + within(rubricContent()).getByText(appMessages.Rubric.overallComments).closest('div') + ); + const rubricCriterion = (index) => ( + within(rubricContent()).getByText(rubricConfig.criteria[index].prompt).closest('div') + ); + return { + modal: () => modal, + modalEl: () => modalEl, + nextNav: () => getByLabelText(appMessages.ReviewActionsComponents.loadNext), + prevNav: () => getByLabelText(appMessages.ReviewActionsComponents.loadPrevious), + hideRubricBtn: () => getByText(appMessages.ReviewActions.hideRubric), + showRubricBtn: () => getByText(appMessages.ReviewActions.showRubric), + loadingResponse: () => getByText(appMessages.ReviewModal.loadingResponse), + retryFetchLink: () => ( + getByText(appMessages.ReviewErrors.reloadSubmission).closest('button') + ), + username: (index) => getByText(fakeData.ids.username(index)), + gradingStatus: (submission) => ( + getByText(appMessages.lms[gradingStatusTransform(submission)]) + ), + rubric: { + criteria: () => modal.querySelectorAll('.rubric-criteria'), + feedbackInput: () => within(rubricFeedback()).getByRole('textbox'), + submitGradeBtn: () => modalEl.getByText(appMessages.Rubric.submitGrade), + criterion: rubricCriterion, + criterionOption: (criterionIndex, optionIndex) => ( + within(rubricCriterion(criterionIndex)) + .getByText(rubricConfig.criteria[criterionIndex].options[optionIndex].label) + ), + criterionFeedback: (index) => within(rubricCriterion(index)).getByRole('textbox'), + }, + }; + } + + /** + * Returns promises for attempting to find elements within the DOM + */ + get find() { + return { + listView: { + viewAllResponsesBtn: () => this.findByText(appMessages.ListView.viewAllResponses), + loadErrorHeading: () => this.findByText(appMessages.ListView.loadErrorHeading), + }, + review: { + prevNav: () => ( + this.review.modalEl().findByLabelText(appMessages.ReviewActionsComponents.loadPrevious) + ), + loadErrorHeading: () => this.findByText(appMessages.ReviewErrors.loadErrorHeading), + startGradingBtn: () => this.findByText(appMessages.ReviewActionsComponents.startGrading), + submitGradeBtn: () => this.findByText(appMessages.Rubric.submitGrade), + }, + }; + } +} + +export default Inspector;