diff --git a/package.json b/package.json index 1d7fd4c..dedef87 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@edx/frontend-app-gradebook", - "version": "1.4.34", + "version": "1.4.35", "description": "edx editable gradebook-ui to manipulate grade overrides on subsections", "repository": { "type": "git", diff --git a/src/components/GradesTab/EditModal/HistoryHeader.jsx b/src/components/GradesTab/EditModal/HistoryHeader.jsx new file mode 100644 index 0000000..e0413f8 --- /dev/null +++ b/src/components/GradesTab/EditModal/HistoryHeader.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +/** + * HistoryHeader + * simple display container for an individual history table header + * @param {string} id - header id + * @param {string} label - header label + * @param {string} value - header value + */ +const HistoryHeader = ({ id, label, value }) => ( +
+
{label}:
+
{value}
+
+); +HistoryHeader.defaultProps = { + value: null, +}; +HistoryHeader.propTypes = { + id: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +}; + +export default HistoryHeader; diff --git a/src/components/GradesTab/EditModal/HistoryHeader.test.jsx b/src/components/GradesTab/EditModal/HistoryHeader.test.jsx new file mode 100644 index 0000000..89e7079 --- /dev/null +++ b/src/components/GradesTab/EditModal/HistoryHeader.test.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import HistoryHeader from './HistoryHeader'; + +describe('HistoryHeader', () => { + const props = { + id: 'water', + label: 'Brita', + value: 'hydration', + }; + describe('Component', () => { + test('snapshot', () => { + expect(shallow()).toMatchSnapshot(); + }); + }); +}); diff --git a/src/components/GradesTab/EditModal/ModalHeaders.jsx b/src/components/GradesTab/EditModal/ModalHeaders.jsx index 9065ce2..870f274 100644 --- a/src/components/GradesTab/EditModal/ModalHeaders.jsx +++ b/src/components/GradesTab/EditModal/ModalHeaders.jsx @@ -3,22 +3,13 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import selectors from 'data/selectors'; +import HistoryHeader from './HistoryHeader'; -const HistoryHeader = ({ id, label, value }) => ( -
-
{label}:
-
{value}
-
-); -HistoryHeader.defaultProps = { - value: null, -}; -HistoryHeader.propTypes = { - id: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), -}; - +/** + * + * Provides a list of HistoryHeaders for the student name, assignment, + * original grade, and current override grade. + */ export const ModalHeaders = ({ modalState, originalGrade, @@ -62,7 +53,6 @@ ModalHeaders.propTypes = { }; export const mapStateToProps = (state) => ({ - editUpdateData: selectors.app.editUpdateData(state), modalState: { assignmentName: selectors.app.modalState.assignmentName(state), updateUserName: selectors.app.modalState.updateUserName(state), diff --git a/src/components/GradesTab/EditModal/ModalHeaders.test.jsx b/src/components/GradesTab/EditModal/ModalHeaders.test.jsx new file mode 100644 index 0000000..57c2361 --- /dev/null +++ b/src/components/GradesTab/EditModal/ModalHeaders.test.jsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import selectors from 'data/selectors'; + +import { + ModalHeaders, + mapStateToProps, +} from './ModalHeaders'; + +jest.mock('./HistoryHeader', () => 'HistoryHeader'); + +jest.mock('data/selectors', () => ({ + __esModule: true, + default: { + app: { + editUpdateData: jest.fn(state => ({ editUpdateData: state })), + modalState: { + assignmentName: jest.fn(state => ({ assignmentName: state })), + updateUserName: jest.fn(state => ({ updateUserName: state })), + }, + }, + grades: { + gradeOverrideCurrentEarnedGradedOverride: jest.fn(state => ({ currentGrade: state })), + gradeOriginalEarnedGraded: jest.fn(state => ({ originalGrade: state })), + }, + }, +})); +describe('ModalHeaders', () => { + let el; + const props = { + currentGrade: 2, + originalGrade: 20, + modalState: { + assignmentName: 'Qwerty', + updateUserName: 'Uiop', + }, + }; + + describe('Component', () => { + describe('snapshots', () => { + beforeEach(() => { + }); + describe('gradeOverrideHistoryError is and empty and open is true', () => { + test('modal open and StatusAlert showing', () => { + el = shallow(); + expect(el).toMatchSnapshot(); + }); + }); + describe('gradeOverrideHistoryError is empty and open is false', () => { + test('modal closed and StatusAlert closed', () => { + el = shallow( + , + ); + expect(el).toMatchSnapshot(); + }); + }); + }); + }); + + describe('mapStateToProps', () => { + const testState = { he: 'lives in a', pineapple: 'under the sea' }; + let mapped; + beforeEach(() => { + mapped = mapStateToProps(testState); + }); + describe('modalState', () => { + test('assignmentName from app.modalState.assignmentName', () => { + expect( + mapped.modalState.assignmentName, + ).toEqual(selectors.app.modalState.assignmentName(testState)); + }); + test('updateUserName from app.modalState.updateUserName', () => { + expect( + mapped.modalState.updateUserName, + ).toEqual(selectors.app.modalState.updateUserName(testState)); + }); + }); + describe('originalGrade', () => { + test('from grades.gradeOverrideCurrentEarnedGradedOverride', () => { + expect(mapped.currentGrade).toEqual( + selectors.grades.gradeOverrideCurrentEarnedGradedOverride(testState), + ); + }); + }); + describe('originalGrade', () => { + test('from grades.gradeOriginalEarnedGrades', () => { + expect(mapped.originalGrade).toEqual( + selectors.grades.gradeOriginalEarnedGraded(testState), + ); + }); + }); + }); +}); diff --git a/src/components/GradesTab/EditModal/OverrideTable/AdjustedGradeInput.jsx b/src/components/GradesTab/EditModal/OverrideTable/AdjustedGradeInput.jsx index fbc4acc..d33a103 100644 --- a/src/components/GradesTab/EditModal/OverrideTable/AdjustedGradeInput.jsx +++ b/src/components/GradesTab/EditModal/OverrideTable/AdjustedGradeInput.jsx @@ -8,6 +8,11 @@ import { Form } from '@edx/paragon'; import selectors from 'data/selectors'; import actions from 'data/actions'; +/** + * + * Input control for adjusting the grade of a unit + * displays an "/ ${possibleGrade} if there is one in the data model. + */ export class AdjustedGradeInput extends React.Component { constructor(props) { super(props); diff --git a/src/components/GradesTab/EditModal/OverrideTable/AdjustedGradeInput.test.jsx b/src/components/GradesTab/EditModal/OverrideTable/AdjustedGradeInput.test.jsx new file mode 100644 index 0000000..895fc00 --- /dev/null +++ b/src/components/GradesTab/EditModal/OverrideTable/AdjustedGradeInput.test.jsx @@ -0,0 +1,92 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import actions from 'data/actions'; +import selectors from 'data/selectors'; + +import { + AdjustedGradeInput, + mapStateToProps, + mapDispatchToProps, +} from './AdjustedGradeInput'; + +jest.mock('@edx/paragon', () => ({ + Form: { Control: () => 'Form.Control' }, +})); +jest.mock('data/selectors', () => ({ + __esModule: true, + default: { + root: { + editModalPossibleGrade: jest.fn(state => ({ updateUserName: state })), + }, + app: { + modalState: { adjustedGradeValue: jest.fn(state => ({ adjustedGradeValue: state })) }, + }, + }, +})); +jest.mock('data/actions', () => ({ + __esModule: true, + default: { + app: { setModalState: jest.fn() }, + }, +})); +describe('AdjustedGradeInput', () => { + let el; + let props = { + value: 1, + possibleGrade: 5, + }; + beforeEach(() => { + props = { + ...props, + setModalState: jest.fn(), + }; + }); + describe('Component', () => { + beforeEach(() => { + el = shallow(); + }); + describe('snapshots', () => { + test('displays input control and "out of possible grade" label', () => { + el.instance().onChange = jest.fn().mockName('this.onChange'); + expect(el.instance().render()).toMatchSnapshot(); + }); + }); + describe('behavior', () => { + describe('onChange', () => { + it('calls props.setModalState event target value', () => { + const value = 42; + el.instance().onChange({ target: { value } }); + expect(props.setModalState).toHaveBeenCalledWith({ + adjustedGradeValue: value, + }); + }); + }); + }); + }); + + describe('mapStateToProps', () => { + const testState = { like: 'no one', ever: 'was' }; + let mapped; + beforeEach(() => { + mapped = mapStateToProps(testState); + }); + describe('modalState', () => { + test('possibleGrade from root.editModalPossibleGrade', () => { + expect( + mapped.possibleGrade, + ).toEqual(selectors.root.editModalPossibleGrade(testState)); + }); + test('updateUserName from app.modalState.updateUserName', () => { + expect( + mapped.value, + ).toEqual(selectors.app.modalState.adjustedGradeValue(testState)); + }); + }); + }); + describe('mapDispatchToProps', () => { + test('setModalState from actions.app.setModalState', () => { + expect(mapDispatchToProps.setModalState).toEqual(actions.app.setModalState); + }); + }); +}); diff --git a/src/components/GradesTab/EditModal/OverrideTable/ReasonInput.jsx b/src/components/GradesTab/EditModal/OverrideTable/ReasonInput.jsx index d183e9d..a9e1c18 100644 --- a/src/components/GradesTab/EditModal/OverrideTable/ReasonInput.jsx +++ b/src/components/GradesTab/EditModal/OverrideTable/ReasonInput.jsx @@ -7,6 +7,10 @@ import { Form } from '@edx/paragon'; import selectors from 'data/selectors'; import actions from 'data/actions'; +/** + * + * Input control for the "reason for change" field in the Edit modal. + */ export class ReasonInput extends React.Component { constructor(props) { super(props); diff --git a/src/components/GradesTab/EditModal/OverrideTable/ReasonInput.test.jsx b/src/components/GradesTab/EditModal/OverrideTable/ReasonInput.test.jsx new file mode 100644 index 0000000..5f9c311 --- /dev/null +++ b/src/components/GradesTab/EditModal/OverrideTable/ReasonInput.test.jsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import actions from 'data/actions'; +import selectors from 'data/selectors'; + +import { + ReasonInput, + mapStateToProps, + mapDispatchToProps, +} from './ReasonInput'; + +jest.mock('@edx/paragon', () => ({ + Form: { Control: () => 'Form.Control' }, +})); +jest.mock('data/selectors', () => ({ + __esModule: true, + default: { + app: { + modalState: { reasonForChange: jest.fn(state => ({ reasonForChange: state })) }, + }, + }, +})); +jest.mock('data/actions', () => ({ + __esModule: true, + default: { + app: { setModalState: jest.fn() }, + }, +})); +describe('ReasonInput', () => { + let el; + let props = { + value: 'did not answer the question', + }; + beforeEach(() => { + props = { + ...props, + setModalState: jest.fn(), + }; + }); + describe('Component', () => { + beforeEach(() => { + el = shallow(, { disableLifecycleMethods: true }); + }); + describe('snapshots', () => { + test('displays reason for change input control', () => { + el.instance().onChange = jest.fn().mockName('this.onChange'); + expect(el.instance().render()).toMatchSnapshot(); + }); + }); + describe('behavior', () => { + describe('onChange', () => { + it('calls props.setModalState event target value', () => { + const value = 42; + el.instance().onChange({ target: { value } }); + expect(props.setModalState).toHaveBeenCalledWith({ + reasonForChange: value, + }); + }); + }); + describe('componentDidMount', () => { + it('focuses the input ref', () => { + const focus = jest.fn(); + expect(el.instance().ref).toEqual({ current: null }); + el.instance().ref.current = { focus }; + el.instance().componentDidMount(); + expect(el.instance().ref.current.focus).toHaveBeenCalledWith(); + }); + }); + }); + }); + + describe('mapStateToProps', () => { + const testState = { to: { catchThem: 'my real test', trainThem: 'my cause!' } }; + let mapped; + beforeEach(() => { + mapped = mapStateToProps(testState); + }); + describe('modalState', () => { + test('value from app.modalState.reasonForChange', () => { + expect(mapped.value).toEqual(selectors.app.modalState.reasonForChange(testState)); + }); + }); + }); + describe('mapDispatchToProps', () => { + test('setModalState from actions.app.setModalState', () => { + expect(mapDispatchToProps.setModalState).toEqual(actions.app.setModalState); + }); + }); +}); diff --git a/src/components/GradesTab/EditModal/OverrideTable/__snapshots__/AdjustedGradeInput.test.jsx.snap b/src/components/GradesTab/EditModal/OverrideTable/__snapshots__/AdjustedGradeInput.test.jsx.snap new file mode 100644 index 0000000..93a0024 --- /dev/null +++ b/src/components/GradesTab/EditModal/OverrideTable/__snapshots__/AdjustedGradeInput.test.jsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AdjustedGradeInput Component snapshots displays input control and "out of possible grade" label 1`] = ` + + + / 5 + +`; diff --git a/src/components/GradesTab/EditModal/OverrideTable/__snapshots__/ReasonInput.test.jsx.snap b/src/components/GradesTab/EditModal/OverrideTable/__snapshots__/ReasonInput.test.jsx.snap new file mode 100644 index 0000000..5931cf9 --- /dev/null +++ b/src/components/GradesTab/EditModal/OverrideTable/__snapshots__/ReasonInput.test.jsx.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ReasonInput Component snapshots displays reason for change input control 1`] = ` + +`; diff --git a/src/components/GradesTab/EditModal/OverrideTable/__snapshots__/test.jsx.snap b/src/components/GradesTab/EditModal/OverrideTable/__snapshots__/test.jsx.snap new file mode 100644 index 0000000..58b0090 --- /dev/null +++ b/src/components/GradesTab/EditModal/OverrideTable/__snapshots__/test.jsx.snap @@ -0,0 +1,47 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`OverrideTable Component snapshots basic snapshot shows a row for each entry and one editable row 1`] = ` +, + "date": "todaaaaaay", + "reason": , + }, + ] + } +/> +`; diff --git a/src/components/GradesTab/EditModal/OverrideTable/index.jsx b/src/components/GradesTab/EditModal/OverrideTable/index.jsx index d1e12dc..0e58bdc 100644 --- a/src/components/GradesTab/EditModal/OverrideTable/index.jsx +++ b/src/components/GradesTab/EditModal/OverrideTable/index.jsx @@ -16,6 +16,11 @@ const GRADE_OVERRIDE_HISTORY_COLUMNS = [ { label: 'Adjusted grade', key: 'adjustedGrade' }, ]; +/** + * + * Table containing previous grade override entries, and an "edit" row + * with todays date, an AdjustedGradeInput and a ReasonInput + */ export const OverrideTable = ({ hide, gradeOverrides, @@ -42,6 +47,7 @@ OverrideTable.defaultProps = { gradeOverrides: [], }; OverrideTable.propTypes = { + // redux gradeOverrides: PropTypes.arrayOf(PropTypes.shape({ date: PropTypes.string, grader: PropTypes.string, diff --git a/src/components/GradesTab/EditModal/OverrideTable/test.jsx b/src/components/GradesTab/EditModal/OverrideTable/test.jsx new file mode 100644 index 0000000..c459440 --- /dev/null +++ b/src/components/GradesTab/EditModal/OverrideTable/test.jsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import selectors from 'data/selectors'; + +import { + OverrideTable, + mapStateToProps, +} from '.'; + +jest.mock('@edx/paragon', () => ({ Table: () => 'Table' })); +jest.mock('./ReasonInput', () => 'ReasonInput'); +jest.mock('./AdjustedGradeInput', () => 'AdjustedGradeInput'); + +jest.mock('data/selectors', () => ({ + __esModule: true, + default: { + app: { + modalState: { + todaysDate: jest.fn(state => ({ todaysDate: state })), + }, + }, + grades: { + hasOverrideErrors: jest.fn(state => ({ hasOverrideErrors: state })), + gradeOverrides: jest.fn(state => ({ gradeOverrides: state })), + }, + }, +})); + +describe('OverrideTable', () => { + const props = { + gradeOverrides: [ + { + date: 'yesterday', + grader: 'me', + reason: 'you ate my sandwich', + adjustedGrade: 0, + }, + { + date: 'today', + grader: 'me', + reason: 'you brought me a new sandwich', + adjustedGrade: 20, + }, + ], + hide: false, + todaysDate: 'todaaaaaay', + }; + + describe('Component', () => { + describe('snapshots', () => { + it('returns null if hide is true', () => { + expect(shallow()).toEqual({}); + }); + describe('basic snapshot', () => { + test('shows a row for each entry and one editable row', () => { + expect(shallow()).toMatchSnapshot(); + }); + }); + }); + }); + + describe('mapStateToProps', () => { + const testState = { I: 'wanna', be: 'the', very: 'best' }; + let mapped; + beforeEach(() => { + mapped = mapStateToProps(testState); + }); + describe('modalState', () => { + test('hide from grades.hasOverrideErrors', () => { + expect(mapped.hide).toEqual(selectors.grades.hasOverrideErrors(testState)); + }); + test('gradeOverrides from grades.gradeOverrides', () => { + expect(mapped.gradeOverrides).toEqual(selectors.grades.gradeOverrides(testState)); + }); + test('todaysData from app.modalState.todaysDate', () => { + expect(mapped.todaysDate).toEqual(selectors.app.modalState.todaysDate(testState)); + }); + }); + }); +}); diff --git a/src/components/GradesTab/EditModal/__snapshots__/HistoryHeader.test.jsx.snap b/src/components/GradesTab/EditModal/__snapshots__/HistoryHeader.test.jsx.snap new file mode 100644 index 0000000..f006b2c --- /dev/null +++ b/src/components/GradesTab/EditModal/__snapshots__/HistoryHeader.test.jsx.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`HistoryHeader Component snapshot 1`] = ` +
+
+ Brita + : +
+
+ hydration +
+
+`; diff --git a/src/components/GradesTab/EditModal/__snapshots__/ModalHeaders.test.jsx.snap b/src/components/GradesTab/EditModal/__snapshots__/ModalHeaders.test.jsx.snap new file mode 100644 index 0000000..8efe704 --- /dev/null +++ b/src/components/GradesTab/EditModal/__snapshots__/ModalHeaders.test.jsx.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is and empty and open is true modal open and StatusAlert showing 1`] = ` +
+ + + + +
+`; + +exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is empty and open is false modal closed and StatusAlert closed 1`] = ` +
+ + + + +
+`; diff --git a/src/components/GradesTab/EditModal/__snapshots__/test.jsx.snap b/src/components/GradesTab/EditModal/__snapshots__/test.jsx.snap new file mode 100644 index 0000000..a5fc671 --- /dev/null +++ b/src/components/GradesTab/EditModal/__snapshots__/test.jsx.snap @@ -0,0 +1,75 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EditMoal Component snapshots gradeOverrideHistoryError is and empty and open is true modal open and StatusAlert showing 1`] = ` + + + + +
+ Showing most recent actions (max 5). To see more, please contact support. +
+
+ Note: Once you save, your changes will be visible to students. +
+ + } + buttons={ + Array [ + , + ] + } + closeText="Cancel" + onClose={[MockFunction this.closeAssignmentModal]} + open={true} + title="Edit Grades" +/> +`; + +exports[`EditMoal Component snapshots gradeOverrideHistoryError is empty and open is false modal closed and StatusAlert closed 1`] = ` + + + + +
+ Showing most recent actions (max 5). To see more, please contact support. +
+
+ Note: Once you save, your changes will be visible to students. +
+ + } + buttons={ + Array [ + , + ] + } + closeText="Cancel" + onClose={[MockFunction this.closeAssignmentModal]} + open={false} + title="Edit Grades" +/> +`; diff --git a/src/components/GradesTab/EditModal/index.jsx b/src/components/GradesTab/EditModal/index.jsx index c96c18b..85e281e 100644 --- a/src/components/GradesTab/EditModal/index.jsx +++ b/src/components/GradesTab/EditModal/index.jsx @@ -16,6 +16,15 @@ import thunkActions from 'data/thunkActions'; import OverrideTable from './OverrideTable'; import ModalHeaders from './ModalHeaders'; +/** + * + * Wrapper component for the modal that allows editing the grade for an individual + * unit, for a given student. + * Provides a StatusAlert with override fetch errors if any are found, an OverrideTable + * (with appropriate headers) for managing the actual override, and a submit button for + * adjusting the grade. + * (also provides a close button that clears the modal state) + */ export class EditModal extends React.Component { constructor(props) { super(props); diff --git a/src/components/GradesTab/EditModal/test.jsx b/src/components/GradesTab/EditModal/test.jsx new file mode 100644 index 0000000..465ab66 --- /dev/null +++ b/src/components/GradesTab/EditModal/test.jsx @@ -0,0 +1,132 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import actions from 'data/actions'; +import selectors from 'data/selectors'; +import thunkActions from 'data/thunkActions'; + +import { + EditModal, + mapDispatchToProps, + mapStateToProps, +} from '.'; + +jest.mock('./OverrideTable', () => 'OverrideTable'); +jest.mock('./ModalHeaders', () => 'ModalHeaders'); +jest.mock('@edx/paragon', () => ({ + Button: () => 'Button', + Modal: () => 'Modal', + StatusAlert: () => 'StatusAlert', +})); +jest.mock('data/actions', () => ({ + __esModule: true, + default: { + app: { closeModal: jest.fn() }, + grades: { doneViewingAssignment: jest.fn() }, + }, +})); +jest.mock('data/thunkActions', () => ({ + __esModule: true, + default: { + grades: { updateGrades: jest.fn() }, + }, +})); +jest.mock('data/selectors', () => ({ + __esModule: true, + default: { + app: { + modalState: { + open: jest.fn(state => ({ isModalOpen: state })), + }, + }, + grades: { + gradeOverrideHistoryError: jest.fn(state => ({ overrideHistoryError: state })), + }, + }, +})); +describe('EditMoal', () => { + let props; + beforeEach(() => { + props = { + gradeOverrideHistoryError: 'Weve been trying to contact you regarding...', + open: true, + closeModal: jest.fn(), + doneViewingAssignment: jest.fn(), + updateGrades: jest.fn(), + }; + }); + + describe('Component', () => { + describe('behavior', () => { + let el; + beforeEach(() => { + el = shallow(); + }); + describe('closeAssignmentModal', () => { + it('calls props.doneViewingAssignment and props.closeModal', () => { + el.instance().closeAssignmentModal(); + expect(props.doneViewingAssignment).toHaveBeenCalledWith(); + expect(props.closeModal).toHaveBeenCalledWith(); + }); + }); + describe('handleAdjustedGradeClick', () => { + it('calls props.updateGardes and this.closeAssignmentModal', () => { + el.instance().closeAssignmentModal = jest.fn(); + el.instance().handleAdjustedGradeClick(); + expect(props.updateGrades).toHaveBeenCalledWith(); + expect(el.instance().closeAssignmentModal).toHaveBeenCalledWith(); + }); + }); + }); + describe('snapshots', () => { + let el; + beforeEach(() => { + el = shallow(); + el.instance().closeAssignmentModal = jest.fn().mockName('this.closeAssignmentModal'); + el.instance().handleAdjustedGradeClick = jest.fn().mockName( + 'this.handleAdjustedGradeClick', + ); + }); + describe('gradeOverrideHistoryError is and empty and open is true', () => { + test('modal open and StatusAlert showing', () => { + expect(el.instance().render()).toMatchSnapshot(); + }); + }); + describe('gradeOverrideHistoryError is empty and open is false', () => { + test('modal closed and StatusAlert closed', () => { + el.setProps({ open: false, gradeOverrideHistoryError: '' }); + expect(el.instance().render()).toMatchSnapshot(); + }); + }); + }); + }); + + describe('mapStateToProps', () => { + const testState = { martha: 'why did you say that name?!' }; + let mapped; + beforeEach(() => { + mapped = mapStateToProps(testState); + }); + test('gradeOverrideHistoryError from grades.gradeOverrideHistoryError', () => { + expect( + mapped.gradeOverrideHistoryError, + ).toEqual(selectors.grades.gradeOverrideHistoryError(testState)); + }); + test('open from app.modalState.open', () => { + expect(mapped.open).toEqual(selectors.app.modalState.open(testState)); + }); + }); + describe('mapDispatchToProps', () => { + test('closeModal from actions.app.closeModal', () => { + expect(mapDispatchToProps.closeModal).toEqual(actions.app.closeModal); + }); + test('doneViewingAssignemtn from actions.grades.doneViewingAssignment', () => { + expect( + mapDispatchToProps.doneViewingAssignment, + ).toEqual(actions.grades.doneViewingAssignment); + }); + test('updateGrades from thunkActions.grades.updateGrades', () => { + expect(mapDispatchToProps.updateGrades).toEqual(thunkActions.grades.updateGrades); + }); + }); +}); diff --git a/src/data/selectors/app.js b/src/data/selectors/app.js index 186b89d..347fa28 100644 --- a/src/data/selectors/app.js +++ b/src/data/selectors/app.js @@ -109,6 +109,8 @@ export default StrictDict({ courseGradeFilterValidity, courseGradeLimits, editUpdateData, + isFilterMenuClosed, + isFilterMenuOpening, ...simpleSelectors, modalState: StrictDict(modalSelectors), filterMenu: StrictDict({